用React-native写一个卡的要死的日历选择器

需求背景:有一天,设计给了一个比较漂亮的日历选择页面,要用RN实现。个人感觉是用不了什么巧妙的方法了,只能是用FlatList去一个个的实现。实现出来后呢,发现很卡(在Android上基本不能跑,想control + C的同学看到这里可以撤了,因为这个用不了)。

先上个别人家的效果图:


合成效果图.gif

然后是自己家的效果图:


日历选择器.gif

我拍胸脯保证,别人家的一定是用原生的实现的......


实现思路

需求:实现一个自当日起,往前一年的日期选择器,显示12个月的日期。选择完毕后,返回两个日期,按照时间早晚的顺序装入数组中返回。

思路逻辑:
1、需要计算出月份的范围。(当前月回溯12个月)
2、需要计算出每个月有几周的跨度。

  • 7 * 4 的情况
  • 7 * 5 的情况(最常见)
  • 7 * 6 的情况

3、 将计算好的数据,放入到一个以周为单位的二维数组中,即日历表为一个7 * N的矩阵。(为了方便期间,我们以月为单位,每个月为一个矩阵数组,然后将月放入到年的数组里)

布局思路:
1、利用FlatList进行布局。
2、日期的头部悬浮单独制作。


思路完了我们来撸代码

首先,创建数据管理类

Calendar.js

定义数据:


// 定义“日”的结构
type DateDay = {
    dateValue: number,
    weekDay : number,
    isToday : boolean,
    notCurrentMonth: boolean,
}

// 定义日历的结构
/**
 * 定义日历结构接口
 */
type Calendar = {
    year: number,
    month: number,
    totalWeeks: number,
    firstWeek: [],
    secondWeek: [],
    thirdWeek: [],
    forthWeek: [],
    fifthWeek: [], // 只有在二月份28天且二月1号在周日的情况下,没有第五周
    sixthWeek: [], // 极少数情况会有第六周
}

定义类:


class CalendarManager {
    static weeks = ['日','一','二','三','四','五','六'];// 静态的日历角标
    year: number; // 年
    month: number; // 月
    date: number; // 日(当前日期)
    timeSp: Date; // 当前日期对象
    weekDay: number; // 当前的星期数
    // readonly currentMonthData: Calendar; // 当前月份的数据,暂时没有用到
    constructor(){
        this.timeSp = new Date();
        this.year = this.timeSp.getFullYear();
        this.month = this.timeSp.getMonth() + 1;
        this.date = this.timeSp.getDate();
        this.weekDay = this.timeSp.getDay();
        // this.currentMonthData = this.calculateCalendarData();
    }
}

在初始化的时候,就确定当前的年月日,然后计算的时候,按照当前的年月日来走。

接下来,就开始给CalendarManager这个类里面,添加方法。

首先,我们需要一个根据当前月份计算日期的方法


 // 需要一个根据当前月份计算日期的方法
    // 日历表为7*5矩阵,即为一个二维数组,每次调整当前类的数据时,自动计算当月的日历(有7*4和7*6的情况)
    calculateCalendarData = (year, month) => {
        if (!year) {
            year = this.year;
        }
        if (!month) {
            month = this.month - 1;
        } else {
            month = month - 1;
        }
        const tempDate = new Date(year, month, 0); // 首先找出上个月的最后一天
        const day = tempDate.getDay();// 上个月最后一天的week数
        const lastMonthLastDayDate = tempDate.getDate();
        const isFeb = month == 1;// 判断是否是二月
        const isFirstDaySat = day == 6;
// 初始化接口类型的数据,默认weeks数为5
        let result: Calendar = {
            year: year,
            month: month + 1,
            totalWeeks: 5,
            firstWeek: [],
            secondWeek: [],
            thirdWeek: [],
            forthWeek: []
        };
// 开始填充数据
        for (let weeksNum = 0; weeksNum < 6; weeksNum += 1) {
            let endDate = new Date(year, month + 1, 0).getDate();
            // 第一周的情况
            if (weeksNum == 0) {
                let startDate = 1;
                if (day !== 6) {
                    // 填充第一周的空白天数
                    for (let spaceDays = 0; spaceDays <= day; spaceDays += 1) {
                        const last_date = lastMonthLastDayDate - (day - spaceDays); // 计算日期
                        const dayData: DateDay = {dateValue: last_date, weekDay: day - spaceDays, notCurrentMonth: true}
                        result.firstWeek.push(dayData);
                    }
                }
                // 填充第一周的剩余天数
                for (let startDay = result.firstWeek.length; startDay < 7; startDay += 1) {
                    const dayData: DateDay = {dateValue: startDate, weekDay: startDay, notCurrentMonth: false}
                    if (this.date == startDate && this.month == month && this.year == year) {
                        dayData.isToday = true;
                    }
                    result.firstWeek.push(dayData);
                    startDate += 1;
                }
            }
            // 二到四的情况
            if (weeksNum !== 0 && weeksNum !== 4) {
                let everyDate = result.firstWeek[6].dateValue + 1;
                if (weeksNum == 2) {
                    everyDate = result.secondWeek[6].dateValue + 1;
                }
                if (weeksNum == 3) {
                    everyDate = result.thirdWeek[6].dateValue + 1;

                }
                for (let startDay = 0; startDay < 7; startDay += 1) {
                    const dayData: DateDay = {dateValue: everyDate, weekDay: startDay, notCurrentMonth: false};
                    if (this.date == everyDate && this.month == month && this.year == year) {
                        dayData.isToday = true;
                    }
                    switch (weeksNum) {
                        case 1:
                            result.secondWeek.push(dayData);
                            break;
                        case 2:
                            result.thirdWeek.push(dayData);
                            break;
                        case 3:
                            result.forthWeek.push(dayData);
                    }
                    everyDate += 1;
                }
                result.totalWeeks = 4;
            }
            // 第五周的情况
            if (weeksNum === 4 && !(isFeb && isFirstDaySat && endDate === 28)) {
                let everyDate = result.forthWeek[6].dateValue + 1;
                let lastDayNum = endDate - result.forthWeek[6].dateValue;
                result.fifthWeek = [];
                if (lastDayNum <= 7) {
                    for (let startDay = 0; startDay < lastDayNum; startDay += 1) {
                        const dayData: DateDay = {dateValue: everyDate, weekDay: startDay, notCurrentMonth: false};
                        if (this.date == everyDate && this.month == month && this.year == year) {
                            dayData.isToday = true;
                        }
                        result.fifthWeek.push(dayData);
                        everyDate += 1;
                    }
                    for (let nextDay = 0; nextDay < 7 - lastDayNum; nextDay += 1) {
                        const dayData: DateDay = {
                            dateValue: nextDay + 1,
                            weekDay: lastDayNum + nextDay + 1,
                            notCurrentMonth: true
                        };
                        result.fifthWeek.push(dayData);
                    }
                    result.totalWeeks = 5;
                    break; // 打断循环即可
                } else {
                    for (let startDay = 0; startDay < 7; startDay += 1) {
                        const dayData: DateDay = {dateValue: everyDate, weekDay: startDay, notCurrentMonth: false};
                        if (this.date == everyDate && this.month == month && this.year == year) {
                            dayData.isToday = true;
                        }
                        result.fifthWeek.push(dayData);
                        everyDate += 1;
                    }
                }
            }
            if (weeksNum === 5 && result.fifthWeek) {
                let everyDate = result.fifthWeek[6].dateValue + 1;
                let lastDayNum = endDate - result.fifthWeek[6].dateValue;
                result.sixthWeek = [];
                for (let startDay = 0; startDay < lastDayNum; startDay += 1) {
                    const dayData: DateDay = {dateValue: everyDate, weekDay: startDay, notCurrentMonth: false};
                    if (this.date == everyDate && this.month == month && this.year == year) {
                        dayData.isToday = true;
                    }
                    result.sixthWeek.push(dayData);
                    everyDate += 1;
                }
                for (let nextDay = 0; nextDay < 7 - lastDayNum; nextDay += 1) {
                    const dayData: DateDay = {
                        dateValue: nextDay + 1,
                        weekDay: lastDayNum + nextDay + 1,
                        notCurrentMonth: true
                    };
                    result.sixthWeek.push(dayData);
                }
                result.totalWeeks = 6;
            }
        }
        return result;
    }

其次,我们...也没有什么需要的了(皮这一下我很开心)

接下来,开始撸Item(月份的显示),创建一个CalendarItemView.js

开始撸界面:
先导入该导入的,


import React, { Component } from 'react';
import PropType from 'prop-types';
import {
    View,
    Text,
    TouchableOpacity,
    StyleSheet,
    ART,
} from 'react-native';
import { observer } from 'mobx-react/native';
import { observable, runInAction } from 'mobx';
import CalenderManager from './Calendar';
import D, { W } from './Demissions';

const { Surface, Shape, Path } = ART;

写常量:


const _MarginHor = 15;
const _titleHeight = W(60);
const _weekItemHeight = W(136);
const _itemBackground = '#f5f5f5';
const _itemRadius = W(35);

// row index text相关内容常量
const contentWidth = D.ScreenW - (2 * _MarginHor);
const dateView_B_Width = (_itemRadius * 2) + ((contentWidth - 15 - (14 * _itemRadius)) / 6);
const tempWidth = (_itemRadius * 2) + ((contentWidth - 15 - (14 * _itemRadius)) / 12); // 间隙的一半
const selectedStyle = { borderRadius: _itemRadius, backgroundColor: '#0084bf' };
const inSelectedStyle = { backgroundColor: '#cce6f2' }; 

写样式:


const CalenderItemStyle = StyleSheet.create({
    container: {
        flex: 0,
        width: contentWidth,
        marginHorizontal: _MarginHor,
        minHeight: (_weekItemHeight * 4) + _titleHeight,
        // maxHeight: (_weekItemHeight * 5) + _titleHeight,
        // height: (_weekItemHeight * 5) + _titleHeight,
        backgroundColor: _itemBackground,
    },
    itemTitleView: {
        flex: 0,
        height: _titleHeight,
        backgroundColor: _itemBackground,
        justifyContent: 'center',
        alignItems: 'center',
    },
    weekItemView: {
        flex: 0,
        height: W(136),
        width: contentWidth,
        paddingHorizontal: 7.5, // 记得计算多余的宽度
        backgroundColor: '#fff',
        // borderTopColor: '#f0f0f0',
        // borderTopWidth: W('minPix') * 2,
        justifyContent: 'space-between',
        alignItems: 'center',
        flexDirection: 'row',
    },
    weekDayBackGroundView: {
        flex: 0,
        height: _itemRadius * 2,
        width: dateView_B_Width,
        backgroundColor: '#fff',
        // paddingHorizontal: 5,
        // width: _itemRadius * 2,
        justifyContent: 'center',
        alignItems: 'center',
    },
    weekDayView: {
        flex: 0,
        // marginHorizontal: 5,
        width: _itemRadius * 2,
        height: _itemRadius * 2,
        justifyContent: 'center',
        alignItems: 'center',
    },
    weekDayText: {
        textAlign: 'center',
        fontSize: 14,
    },
    SAndEBackView: {
        position: 'absolute',
        flex: 0,
        height: _itemRadius * 2,
        width: dateView_B_Width / 2,
        top: 0,
        backgroundColor: '#cce6f2',
    },
});

撸界面:


@observer
class CalendarItemView extends Component {
    static propTypes = {
        year: PropType.number.isRequired,
        month: PropType.number.isRequired,
        startDate: PropType.number,
        endDate: PropType.number,
        allSelect: PropType.bool,
        onRowItemPress: PropType.func,
        selectedOk: PropType.bool,
    }
    static defaultProps = {
        startDate: -1,
        endDate: -1,
        allSelect: false,
        onRowItemPress: (dateInfo) => {
            // console.log(`点击了${dateInfo.year}/${dateInfo.month}/${dateInfo.date}`);
        },
        selectedOk: false,
    }
    constructor(props) {
        super(props);
        this.calenderManager = new CalenderManager();
        // this.calenderData = this.calenderManager.calculateCalendarData(this.props.year, this.props.month);
        this.selectedOK = this.props.selectedOk;
    }
    componentWillReceiveProps(nextProps) {
        // if (nextProps.selectedOk !== this.props.selectedOk || nextProps.startDate !== this.props.startDate || nextProps.endDate !== this.props.endDate || nextProps.allSelect !== this.props.allSelect) {
        //     runInAction(() => {
        //         this.calenderData = this.calenderManager.calculateCalendarData(nextProps.year, nextProps.month);
        //         this.selectedOK = this.props.selectedOk;
        //     });
        // }
        runInAction(() => {
            this.calenderData = this.calenderManager.calculateCalendarData(nextProps.year, nextProps.month);
            this.selectedOK = this.props.selectedOk;
        });
    }

    shouldComponentUpdate(nextProps, nextState) {
        return (nextProps.selectedOk !== this.props.selectedOk || nextProps.startDate !== this.props.startDate || nextProps.endDate !== this.props.endDate || nextProps.allSelect !== this.props.allSelect);
    }
    calenderManager = null;
    @observable calenderData = null;
    @observable selectedOK = false;
    pressDateRowItem = (dateValue: number) => {

        if (this.props.onRowItemPress) {
            const dateInfo = {
                year: this.props.year,
                month: this.props.month,
                date: dateValue,
            };
            /**
             * 传回选择数据
             */
            this.props.onRowItemPress(dateInfo);
        }
    }

    renderTitle = () => {
        const year = this.calenderData ? this.calenderData.year : this.props.year;
        const month = this.calenderData ? this.calenderData.month : this.props.month;
        const title = `${year}-${month}`;
        return (
            <View style={CalenderItemStyle.itemTitleView}>
                <Text>{title}</Text>
            </View>
        );
    }
    /**
     * 计算每一个月份怎么显示
     * @param obj: 每一个月份的信息
     * @param index 当前周的星期数
     * @returns {*}
     */
    renderRowText = (obj, index) => {
        // 计算边界的宽度布局
        let frontierStyle = {};
        if (index === 0) {
            frontierStyle = {
                borderTopLeftRadius: _itemRadius,
                borderBottomLeftRadius: _itemRadius,
                width: tempWidth,
                alignItems: 'flex-start',
            };
        } else if (index === 6) {
            frontierStyle = {
                borderBottomRightRadius: _itemRadius,
                borderTopRightRadius: _itemRadius,
                width: tempWidth,
                alignItems: 'flex-end',
            };
        }
        // 如果不是当月的日子,直接返回空视图。
        if (obj.notCurrentMonth) {
            return (
                <View
                    key={`other_date@${this.props.month}-${obj.dateValue}`}
                    style={[CalenderItemStyle.weekDayBackGroundView, frontierStyle]}
                />
            );
        }
        let needFrontierBackView = false;
        let EAndSStyle = {};
        let _style = {};
        let _backgroundStyle = {};
        if (this.props.startDate === obj.dateValue || this.props.endDate === obj.dateValue) {
            _style = selectedStyle;
        }
        const isStartExist = this.props.startDate !== -1;
        const isEndExist = this.props.endDate !== -1;
        const isGreaterThanStart = (isStartExist && this.props.startDate < obj.dateValue);
        const isLessThanEnd = (isEndExist && this.props.endDate > obj.dateValue);
        if (this.props.allSelect) {
            _backgroundStyle = inSelectedStyle;
        } else if (!isEndExist && isGreaterThanStart) {
            _backgroundStyle = inSelectedStyle;
        } else if (!isStartExist && isLessThanEnd) {
            _backgroundStyle = inSelectedStyle;
        } else if (isGreaterThanStart && isLessThanEnd) {
            _backgroundStyle = inSelectedStyle;
        }
        if (obj.dateValue === this.props.startDate) {
            EAndSStyle = {
                right: 0,
            };
            needFrontierBackView = true;
        } else if (obj.dateValue === this.props.endDate) {
            EAndSStyle = {
                left: 0,
            };
            needFrontierBackView = true;
        }
        return (
            <TouchableOpacity
                key={`date@${this.props.month}-${obj.dateValue}`}
                style={[CalenderItemStyle.weekDayBackGroundView, this.selectedOK ? _backgroundStyle : null, frontierStyle]}
                onPress={() => { this.pressDateRowItem(obj.dateValue); }}
                activeOpacity={0.80}
            >
                {needFrontierBackView && this.selectedOK ? <View style={[CalenderItemStyle.SAndEBackView, EAndSStyle]} /> : null}
                <View style={[CalenderItemStyle.weekDayView, _style]}>
                    <Text style={CalenderItemStyle.weekDayText}>{obj.notCurrentMonth ? '' : obj.dateValue}</Text>
                </View>
            </TouchableOpacity>
        );
    }
    renderRow = (weekData: Array) => {
        if (typeof weekData === 'undefined' || weekData.length === 0) {
            return <View />;
        }
        const listCom = [];
        weekData.forEach((obj, index) => {
            listCom.push(this.renderRowText(obj, index));
        });
        const _width = D.ScreenW - (2 * _MarginHor);
        const path = new Path().moveTo(15, 0).lineTo(_width - 15, 0);
        return (
            <View style={CalenderItemStyle.weekItemView}>
                <Surface width={_width} height={1} style={{ position: 'absolute', top:0, left: 0 }}>
                    <Shape d={path} strokeWidth={1.5} stroke={'#f0f0f0'} />
                </Surface>
                {listCom}
            </View>
        );
    };
    renderCalendarContent = () => {
        if (!this.calenderData) { return <View/> }
        const firstWeek = this.renderRow(this.calenderData.firstWeek);
        const secondWeek = this.renderRow(this.calenderData.secondWeek);
        const thirdWeek = this.renderRow(this.calenderData.thirdWeek);
        const forthWeek = this.renderRow(this.calenderData.forthWeek);
        const fifthWeek = this.renderRow(this.calenderData.fifthWeek);
        const sixthWeek = this.renderRow(this.calenderData.sixthWeek);
        return (
            <View style={{ flex:1, backgroundColor: '#fff', alignItems: 'center' }}>
                {firstWeek}
                {secondWeek}
                {thirdWeek}
                {forthWeek}
                {fifthWeek}
                {sixthWeek}
            </View>
        );
    };
    render() {
        return (
            <View style={CalenderItemStyle.container}>
                {this.renderTitle()}
                {this.renderCalendarContent()}
            </View>
        );
    }
}

敲黑板,划重点了

这里实现item渲染的思路是,每个item都有一个startdata和enddata的检测机制,渲染startdata和enddata的之间的item样式。都在,都渲染,在区间外,则不渲染。

然后把放到FlatList即可。


import React, { Component } from 'react';
import {
    View,
    Text,
    StyleSheet,
    TouchableOpacity,
    FlatList,
} from 'react-native';
import { persist } from 'mobx-persist';
import { observer } from 'mobx-react/native';
import { observable, action, runInAction, toJS, autoRun, computed } from 'mobx';
import D, { W } from './Demissions';
import CalendarItemView from './CalendarItemView';
import CalendarManager from './Calendar';
import CalendarDataStore from './CalendarDataStore';

const _MarginHor = 15;
const _MarginTop = 22.5;
const _ContainerBackgroundColor = '#f5f5f5';

const SelectedDateStyle = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'flex-start',
        alignItems: 'center',
        backgroundColor: _ContainerBackgroundColor,
    },
    headerStyle: {
        flex: 0,
        marginHorizontal: _MarginHor,
        marginTop: _MarginTop,
        height: W(150),
        width: D.ScreenW - (2 * _MarginHor),
        backgroundColor: '#fff',
        // alignItems: 'center',
    },
    selectTitleViewStyle: {
        flex: 0,
        height: W(90),
        width: D.ScreenW - (2 * _MarginHor),
        justifyContent: 'center',
        alignItems: 'center',
        paddingHorizontal: 15,
    },
    selectTitle: {
        flex: 0,
        fontSize: 16,
        fontWeight: '500',
        color: '#000',
    },
    dayTitleViewStyle: {
        flex: 0,
        width: D.ScreenW - (4 * _MarginHor),
        height: W(60),
        flexDirection: 'row',
        justifyContent: 'space-between',
        // paddingHorizontal: _MarginHor,
        alignItems: 'center',
        borderTopWidth: W('minPix') * 2,
        borderTopColor: '#f0f0f0',
        alignSelf: 'center',
    },
    cancelBtnStyle: {
        flex: 0,
        height: W(90),
        minWidth: W(90),
        position: 'absolute',
        top: 0,
        left: 0,
        justifyContent: 'center',
        alignItems: 'center',
        paddingHorizontal: _MarginHor,
    },
    cancelText: {
        fontSize: 16,
        color: '#000',
    },
    flatListStyle: {
        flex: 1,
    },
});

/**
 * 处理选择页面的数据逻辑
 */
class SelectedDateModel {
    constructor() {
        this.calendarManager = new CalendarManager();
    }
    calendarManager: CalendarManager = null;
    @observable years;
    @observable calendarDataArr = [];
    @observable selectDateArr = [];
    @computed get selectedOK() {
        return this.selectDateArr >= 2;
    }
    @action createData = () => {
        if (CalendarDataStore.calendarDataArr.length !== 0 && CalendarDataStore.calendarDataArr[11].month === this.calendarManager.month) {
            this.originDataArr = toJS(CalendarDataStore.calendarDataArr);
            this.calendarDataArr = toJS(CalendarDataStore.calendarDataArr);
            return;
        }
        // 生成当年的数据
        for (let i = 11; i >= 0; i -= 1) {
            // 年、月的计算,push顺序
            let _month = this.calendarManager.month - i;
            if (_month <= 0) {
                _month = 12 - _month;
            }
            const tempObj = {
                month: _month,
                year: this.calendarManager.month - i - 1 < 0 ? this.calendarManager.year - 1 : this.calendarManager.year,
                isSelectedAll: false,
                startDate: -1,
                endDate: -1,
            };
            this.calendarDataArr.push(tempObj);
        }
        this.originDataArr = toJS(this.calendarDataArr);
        CalendarDataStore.calendarDataArr = this.calendarDataArr;
    }
    originDataArr = [];
    @action onItemPress = (dateInfo) => {
        this.selectedDate(dateInfo.year, dateInfo.month, dateInfo.date);
    }
    @action selectedDate = (year, month, day) => {
        const time = new Date(year, month - 1, day);
        if (this.selectDateArr.length >= 2) {
            this.pressCancel();
        }
        if (this.selectDateArr.length === 0) {
            this.selectDateArr.push(time.valueOf());
        } else if (time.valueOf() !== this.selectDateArr[0]) {
            this.selectDateArr.push(time.valueOf());
            // console.log(toJS(this.selectDateArr));
            this.selectDateArr = toJS(this.selectDateArr).sort(function (a, b) {
                return a - b;
            }); // 排序
            // console.log(toJS(this.selectDateArr));
        }
        this.changeUI();
    }
    @action changeUI = () => {
        if (this.calendarDataArr.length === 0) return;
        let tepStart = null;
        let startMonth = -1;
        let startYear = -1;
        let startDay = -1;
        if (this.selectDateArr.length === 1) {
            tepStart = this.selectDateArr[0];
            if (tepStart) {
                const startDate = new Date(tepStart);
                startMonth = startDate.getMonth() + 1;
                startYear = startDate.getFullYear();
                startDay = startDate.getDate();
                const tempObj = {
                    month: startMonth,
                    year: startYear,
                    isSelectedAll: false,
                    startDate: startDay,
                    endDate: -1,
                };
                const monthNum = this.calendarManager.month - startMonth;
                const tempArr = toJS(this.calendarDataArr);
                if (monthNum >= 0) {
                    tempArr[11 - monthNum] = tempObj;
                } else {
                    tempArr[-monthNum - 1] = tempObj;
                }
                this.calendarDataArr = toJS(tempArr);
            }
        } else if (this.selectDateArr.length === 2) {
            let tepStart = this.selectDateArr[0];
            let startMonth = -1;
            let startYear = -1;
            let startDay = -1;
            let tepEnd = this.selectDateArr[1];
            let endMonth = -1;
            let endYear = -1;
            let endDay = -1;
            if (tepStart) {
                const startDate = new Date(tepStart);
                startMonth = startDate.getMonth() + 1;
                startYear = startDate.getFullYear();
                startDay = startDate.getDate();
            }
            if (tepEnd) {
                const endDate = new Date(tepEnd);
                endMonth = endDate.getMonth() + 1;
                endDay = endDate.getDate();
                endYear = endDate.getFullYear();
            }
            if (endMonth === startMonth) {
                const tempObj = {
                    month: startMonth,
                    year: startYear,
                    isSelectedAll: false,
                    startDate: startDay,
                    endDate: endDay,
                };
                const monthNum = this.calendarManager.month - startMonth;
                const tempArr = toJS(this.calendarDataArr);
                const month_index = monthNum >= 0 ? 11 - monthNum : -monthNum - 1;
                tempArr[month_index] = tempObj;
                this.calendarDataArr = toJS(tempArr);
            } else {
                const tempObj_start = {
                    month: startMonth,
                    year: startYear,
                    isSelectedAll: false,
                    startDate: startDay,
                    endDate: -1,
                };
                const tempObj_end = {
                    month: endMonth,
                    year: endYear,
                    isSelectedAll: false,
                    startDate: -1,
                    endDate: endDay,
                };
                const start_monthNum = this.calendarManager.month - startMonth;
                const end_monthNum = this.calendarManager.month - endMonth;
                const tempArr = toJS(this.calendarDataArr);
                const start_index = start_monthNum >= 0 ? 11 - start_monthNum : -start_monthNum - 1;
                const end_index = end_monthNum >= 0 ? 11 - end_monthNum : -end_monthNum - 1;
                tempArr[start_index] = tempObj_start;
                tempArr[end_index] = tempObj_end;
                if (Math.abs(start_monthNum - end_monthNum) > 1) {
                    for (let i = start_index + 1; i < end_index; i += 1) {
                        tempArr[i] = {
                            month: tempArr[i].month,
                            year: tempArr[i].year,
                            isSelectedAll: true,
                            startDate: -1,
                            endDate: -1,
                        };
                    }
                }
                this.calendarDataArr = toJS(tempArr);
            }
        }
    }
    @action pressCancel = () => {
        this.selectDateArr = [];
        if (this.originDataArr.length !== 0) {
            this.calendarDataArr = this.originDataArr;
        }
    }
}

@observer
class SelectedDatePage extends Component {
    static navigatorStyle = {
        tabBarHidden: true,
    };
    model: ?SelectedDateModel = null;
    @observable showFlatList = false;
    constructor(props) {
        super(props);
        this.model = new SelectedDateModel();
    }
    componentDidMount() {
        // 延迟创建,(Android端的渲染有点问题,需要进一步处理下)
        this.model.createData();
        runInAction(() => {
            this.showFlatList = true;
        });
    }
    keyExtractor = (item, index) => {
        return `calendarKey@${index}`;
    }
    /**
     * 返回ListItem
     */
    renderItem = (row) => {
        const item = row.item;
        return (
            <CalendarItemView
                year={item.year}
                month={item.month}
                startDate={item.startDate}
                endDate={item.endDate}
                allSelect={item.isSelectedAll}
                selectedOk={this.model.selectedOK}
                onRowItemPress={this.model.onItemPress}
            />
        );
    }
    /**
     * 返回日期头部视图
     * @returns {*}
     */
    renderHeaderView = () => {
        const titleArr = ['日', '一', '二', '三', '四', '五', '六'];
        const listCom = [];
        titleArr.forEach((obj, index) => {
            listCom.push(
                <Text
                    key={`key@${obj}`}
                    style={{ flex: 0, textAlign: 'center', width: W(40), fontSize: 14 }}
                >
                    {obj}
                </Text>
            );
        });
        return (
            <View style={SelectedDateStyle.headerStyle}>
                <View style={SelectedDateStyle.selectTitleViewStyle}>
                    <TouchableOpacity style={SelectedDateStyle.cancelBtnStyle} onPress={this.model.pressCancel}>
                        <Text style={SelectedDateStyle.cancelText}>取消</Text>
                    </TouchableOpacity>
                    <Text style={SelectedDateStyle.selectTitle}>选择日期</Text>
                </View>
                <View style={SelectedDateStyle.dayTitleViewStyle}>
                    {listCom}
                </View>
            </View>
        );
    }

    renderFlatList = () => {
        return (
            <FlatList
                style={SelectedDateStyle.flatListStyle}
                data={toJS(this.model.calendarDataArr)}
                renderItem={this.renderItem}
                keyExtractor={this.keyExtractor}
                extraData={[this.model.selectDateArr, this.model.selectedOK]}
            />
        );
    }
    render() {
        return (
            <View style={SelectedDateStyle.container}>
                {this.renderHeaderView()}
                {this.renderFlatList()}
            </View>
        );
    }
}

OK, 这样就完成了一个卡的要死的日历。
关于为什么在Android上会卡,我的初步推测是,每次选择日历,都要遍历所有item进行渲染,一年有365天,就有365个item的样式需要去重绘。
但是总觉的对于计算机的运行速度而言,365这个数量级,也是个毫秒级的处理。但为什么在Android上会卡呢??
我把锅甩给了RN,各位读者有什么高见的话,欢迎在评论取留言回复。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容