reactnative日历_reactnative 日历选择 - CSDN
  • ReactNative日历组件

    2020-06-12 23:30:05
    该模块包含各种可自定义的React Native 日历组件
  • 项目地址: react-native-slideable-calendar-strip 演示地址: Calendar-Strip.mp4 ...一般的甲方都会在一个页面上拖动拖动, 看到一个日历, 就想滑动切换上下周, 由于react-native-calendar-strip没有滑动特...

    项目地址: react-native-slideable-calendar-strip

    演示地址: Calendar-Strip.mp4

    为何要再实现一个日历控件

    已经有了react-native-calendar-strip为何还需要我这个日历控件?

    一般的甲方都会在一个页面上拖动拖动, 看到一个日历, 就想滑动切换上下周, 由于react-native-calendar-strip没有滑动特性, 并且在这个issue上讨论了好久, 并没有可行的方案. 于是就萌发自己写一个日历插件的冲动.

    控件需要有何特性

    • 左右滑动
    • 农历展示
    • 选中日期
    • 事件标识
    • 下滑手势
    • 回到今日

    开发过程

    要开发一个日历控件, 最大的问题就是日期的转换, 虽然Moment.js被很多人使用, 但是Moment使用大量的面向对象的API, 严重影响性能, 这也是在我尝试了Moment之后发现的, 于是就换上了datefns, 轻量级js日期控件, 完全的函数式风格, 在日历控件中只需保存Date数据, 其他的日期比较/转换等操作都交给datefns.

    其次最头疼的问题是使用FlatList展示数据时候, 如何动态生成新的数据.

    在日历控件首次加载时候, 会生成5个周的日期, 将FlatList滚动到中间一页(今天所在的周, 第2页, 从0开始). 当用户滑动到最后一页, 就需要再次生成2个周的数据拼接到尾部, 当用户滑动到第一页, 就需要生成2个周的数据拼接到数组首部, 并且这时候今天所在的页数也会变化, 所以要将今天所在的周的页数+2, 拼接到首部会影响FlatList数据展示, 会展示第一页数据, 此时的第一页数据是最新生成的日期, 所以要滚动到第二页(从0页开始).

      loadPreviousTwoWeek(originalDates) {
        const originalFirstDate = originalDates[0];
        const originalLastDate = originalDates[originalDates.length-1];
        const firstDayOfPrevious2Week = subDays(originalFirstDate, 7 * 2);
        // 生成两周之前的第一天到原始数据最后一天的日期
        const eachDays = eachDay(firstDayOfPrevious2Week, originalLastDate);
        this.setState(prevState => ({
          datas: eachDays,
          currentPage: prevState.currentPage+2,
          pageOfToday: prevState.pageOfToday+2,
        }), () => {
          // 悄无声息滚动
          this.scrollToPage(2, false);
        });
      }
    复制代码

    滑动到最后一页需要加载下两周日期:

    //  onEndReached={() => { this.onEndReached(); } }
    //  onEndReachedThreshold={0.01}
      onEndReached() {
        // console.log('onEndReached');
        this.loadNextTwoWeek(this.state.datas);
      }
      loadNextTwoWeek(originalDates) {
        const originalFirstDate = originalDates[0];
        const originalLastDate = originalDates[originalDates.length-1];
        const lastDayOfNext2Week = addDays(originalLastDate, 7 * 2);
        const eachDays = eachDay(originalFirstDate, lastDayOfNext2Week);
        this.setState({ datas: eachDays });
      }
    复制代码

    ScrollViewonMomentumScrollEnd属性监听页数变化, 记录今天所在周的页数和当前展示的页数

    // onMomentumScrollEnd={this.momentumEnd}
    // scrollEventThrottle={500}
      momentumEnd = (event) => {
        const firstDayInCalendar = this.state.datas ? this.state.datas[0] : new Date();
        // 从第一天到今天一共多少天
        const daysBeforeToday = differenceInDays(firstDayInCalendar, new Date());
        // ~~向下取整, 第一天到今天一共几周, 也就是今天所在周所在的页数
        const pageOfToday = ~~(Math.abs(daysBeforeToday / 7));
        const screenWidth = event.nativeEvent.layoutMeasurement.width;
        // 通过offset来获取当前所在页数
        const currentPage = event.nativeEvent.contentOffset.x / screenWidth;
        // 记录今天所在周页数, 当前展示周的页数, 今天所在周是否被展示
        this.setState({
          pageOfToday,
          currentPage,
          isTodayVisible: currentPage === pageOfToday,
        });
    
        // 如果滑动到第一页了就需要加载之前两周数据
        if (event.nativeEvent.contentOffset.x < width) {
          this.loadPreviousTwoWeek(this.state.datas);
        }
      }
    复制代码

    最棘手的问题是用户点击了日历之外的一个button, 跳转到日历上指定的一天.

    1. 指定日期正好在当前展示的一个周内
      currentPageDatesIncludes = (date) => {
        const { currentPage } = this.state;
        const currentPageDates = this.state.datas.slice(7*currentPage, 7*(currentPage+1));
        // dont use currentPageDates.includes(date); because can't compare Date in it
        return !!currentPageDates.find(d => isSameDay(d, date));
      }
    复制代码

    直接设置选中日期为指定日期.

    1. 指定日期不在当前展示周内, 但是当前控件日期数据包含指定日期
        const sameDay = (d) => isSameDay(d, nextSelectedDate);
          if (this.state.datas.find(sameDay)) {
            let selectedIndex = this.state.datas.findIndex(sameDay);
            if (selectedIndex === -1) selectedIndex = this.state.pageOfToday; // in case not find
            const selectedPage = ~~(selectedIndex / 7);
            this.scrollToPage(selectedPage);
          }
    复制代码

    找到指定日期所在周的页数, 滚动过去.

    1. 指定日期不在当前展示周内, 并且当前控件日期数据不包含指定日期
    if (isFuture(nextSelectedDate)) {
      const head = this.state.datas[0];
      const tail = endOfWeek(nextSelectedDate);
      const days = eachDay(head, tail);
      this.setState({
        datas: days,
        isTodayVisible: false,
      }, () => {
        const page = ~~(days.length/7 - 1);
        // to last page
        this.scrollToPage(page);
      });
    } else {
      const head = startOfWeek(nextSelectedDate);
      const tail = this.state.datas[this.state.datas.length - 1];
      const days = eachDay(head, tail);
      this.setState({
        datas: days,
        isTodayVisible: false,
      }, () => {
        // to first page
        this.scrollToPage(0);
      });
    }
    复制代码

    如果是未来某一天, 那么生成那天所在周的周六到当前日期控件所有日期的第一天之间的所有日期, 找到最后一页, 滚动过去.

    如果是之前某一天, 那么生成那天所在周的周日(第一天)到当前日期控件所有日期的最后一天之间的所有日期, 滚动到第一页.

    关于 pageOfTodaycurrentPage 交给 momentumEnd() 自动处理.

    滚动到页方法是利用 FlatListscrollToIndex 实现:

      scrollToPage = (page, animated=true) => {
        this._calendar.scrollToIndex({ animated, index: 7 * page });
      }
    复制代码

    下滑手势:

      componentWillMount() {
        const touchThreshold = 50;
        const speedThreshold = 0.2;
        this._panResponder = PanResponder.create({
          onStartShouldSetPanResponder: () => false,
          onMoveShouldSetPanResponder: (evt, gestureState) => {
            const { dy, vy } = gestureState;
            // 滑动距离大雨50, 并且滑动速度大于0.2, 有效下滑
            if (dy > touchThreshold && vy > speedThreshold) {
              const { onSwipeDown } = this.props;
              onSwipeDown && onSwipeDown();
            }
            return false;
          },
          onPanResponderRelease: () => {},
        });
      }
      
      // 最外层 <View {...this._panResponder.panHandlers}>
    复制代码

    其他:

    • 使用 ChineseLunar 来转换中国农历.
    • isTodayVisible 为false时在日历Header上展示一个 button
    • 点击 跳转到今天所在周的页数
    • 最终整个控件的 state 只有 :
    this.state = {
      datas: this.getInitialDates(), // 保存所有日期,
      isTodayVisible: true, // 今天所在周是否在展示
      pageOfToday: 2, // 今天在日历的第几页,  从0开始
      currentPage: 2, // 当前是日历的第几页,  从0开始
    };
    复制代码
    • 所有保存的日期都是 Date格式, 并且是0点 Wed May 16 2018 00:00:00 GMT+0800 (CST)
    • 控件所需要的props:
    CalendarStrip.propTypes = {
      selectedDate: PropTypes.object.isRequired,
      onPressDate: PropTypes.func,
      onPressGoToday: PropTypes.func,
      markedDate: PropTypes.array,
      onSwipeDown: PropTypes.func,
    };
    
    复制代码

    PS. 使用datefns另一个好处是, 当传给控件

    markedDate = ['2018-01-01', '2018-05-01', '2018-06-01']
    复制代码

    也是支持的, 不必须传一个Date格式的日期.

    如何开源

    1. 托管到GitHub

    2. 发布到npmjs

    3. travis持续集成(jest测试)

    展开全文
  • 这次介绍的这个React Native日历日程组件名叫react-native-calendars,是纯JS开发,可以适配IOS和安卓双平台。使用方便,功能强大,可以通过配置自定义样式和主题,更重要的是它支持日程显示。下面我们来看看这个...

    这次介绍的这个React Native日历日程组件名叫react-native-calendars,是纯JS开发,可以适配IOS和安卓双平台。使用方便,功能强大,可以通过配置自定义样式和主题,更重要的是它支持日程显示。下面我们来看看这个组件的使用方法。

    http://mp.weixin.qq.com/s/zS4VbGJh4WVxkP9xMIN3iw

    安装

    1. npm install --save react-native-calendars

    因为是纯JS的,所以不需要link,执行完就可以了。

    使用

    react-native-calendars主要包含三种子组件,分别是 Calendar(日历), CalendarList(日历列表), Agenda(日程),可以根据实际需要选择使用。下面我们分别介绍这三种组件的使用示例。

    Calendar(日历)

    示例代码

    1. <Calendar

    2.  // Initially visible month. Default = Date()

    3.  current={'2012-03-01'}

    4.  // Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined

    5.  minDate={'2012-05-10'}

    6.  // Maximum date that can be selected, dates after maxDate will be grayed out. Default = undefined

    7.  maxDate={'2012-05-30'}

    8.  // Handler which gets executed on day press. Default = undefined

    9.  onDayPress={(day) => {console.log('selected day', day)}}

    10.  // Month format in calendar title. Formatting values: http://arshaw.com/xdate/#Formatting

    11.  monthFormat={'yyyy MM'}

    12.  // Handler which gets executed when visible month changes in calendar. Default = undefined

    13.  onMonthChange={(month) => {console.log('month changed', month)}}

    14.  // Hide month navigation arrows. Default = false

    15.  hideArrows={true}

    16.  // Replace default arrows with custom ones (direction can be 'left' or 'right')

    17.  renderArrow={(direction) => (<Arrow />)}

    18.  // Do not show days of other months in month page. Default = false

    19.  hideExtraDays={true}

    20.  // If hideArrows=false and hideExtraDays=false do not switch month when tapping on greyed out

    21.  // day from another month that is visible in calendar page. Default = false

    22.  disableMonthChange={true}

    23.  // If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.

    24.  firstDay={1}

    25.  // Hide day names. Default = false

    26.  hideDayNames={true}

    27. />

    CalendarList(日历列表)

    示例代码

    1. <CalendarList

    2.  // Callback which gets executed when visible months change in scroll view. Default = undefined

    3.  onVisibleMonthsChange={(months) => {console.log('now these months are visible', months);}}

    4.  // Max amount of months allowed to scroll to the past. Default = 50

    5.  pastScrollRange={50}

    6.  // Max amount of months allowed to scroll to the future. Default = 50

    7.  futureScrollRange={50}

    8.  // Enable or disable scrolling of calendar list

    9.  scrollEnabled={true}

    10.  ...calendarParams

    11. />

    Agenda(日程)

    示例代码

    1. <Agenda

    2.  // the list of items that have to be displayed in agenda. If you want to render item as empty date

    3.  // the value of date key kas to be an empty array []. If there exists no value for date key it is

    4.  // considered that the date in question is not yet loaded

    5.  items={

    6.    {'2012-05-22': [{text: 'item 1 - any js object'}],

    7.     '2012-05-23': [{text: 'item 2 - any js object'}],

    8.     '2012-05-24': [],

    9.     '2012-05-25': [{text: 'item 3 - any js object'},{text: 'any js object'}],

    10.    }}

    11.  // callback that gets called when items for a certain month should be loaded (month became visible)

    12.  loadItemsForMonth={(month) => {console.log('trigger items loading')}}

    13.  // callback that gets called on day press

    14.  onDayPress={(day)=>{console.log('day pressed')}}

    15.  // callback that gets called when day changes while scrolling agenda list

    16.  onDayChange={(day)=>{console.log('day changed')}}

    17.  // initially selected day

    18.  selected={'2012-05-16'}

    19.  // Minimum date that can be selected, dates before minDate will be grayed out. Default = undefined

    20.  minDate={'2012-05-10'}

    21.  // Maximum date that can be selected, dates after maxDate will be grayed out. Default = undefined

    22.  maxDate={'2012-05-30'}

    23.  // Max amount of months allowed to scroll to the past. Default = 50

    24.  pastScrollRange={50}

    25.  // Max amount of months allowed to scroll to the future. Default = 50

    26.  futureScrollRange={50}

    27.  // specify how each item should be rendered in agenda

    28.  renderItem={(item, firstItemInDay) => {return (<View />);}}

    29.  // specify how each date should be rendered. day can be undefined if the item is not first in that day.

    30.  renderDay={(day, item) => {return (<View />);}}

    31.  // specify how empty date content with no items should be rendered

    32.  renderEmptyDate={() => {return (<View />);}}

    33.  // specify how agenda knob should look like

    34.  renderKnob={() => {return (<View />);}}

    35.  // specify your item comparison function for increased performance

    36.  rowHasChanged={(r1, r2) => {return r1.text !== r2.text}}

    37.  // Hide knob button. Default = false

    38.  hideKnob={true}

    39.  // By default, agenda dates are marked if they have at least one item, but you can override this if needed

    40.  markedDates={{

    41.    '2012-05-16': {selected: true, marked: true},

    42.    '2012-05-17': {marked: true},

    43.    '2012-05-18': {disabled: true}

    44.  }}

    45.  // agenda theme

    46.  theme={{

    47.    ...calendarTheme,

    48.    agendaDayTextColor: 'yellow',

    49.    agendaDayNumColor: 'green',

    50.    agendaTodayColor: 'red',

    51.    agendaKnobColor: 'blue'

    52.  }}

    53.  // agenda container style

    54.  style={{}}

    55. />

    说明

    react-native-calendars组件的GitHub 地址:https://github.com/wix/react-native-calendars,更多的配置和使用方法请点击查看原文查看GitHub上的文档以及示例代码。

    展开全文
  • 支持滑动的ReactNative日历控件
  • 日历控件。先上图吧 image.png ======================js文件========================== import React from 'react'; import { View, StyleSheet, Text, Modal, TouchableOpacity, Dimensions, I...
        

    日历控件。先上图吧


    1426137-c5183cc1c61522b5.png
    image.png

    ======================js文件==========================

    import React from 'react';

    import {
    View,
    StyleSheet,
    Text,
    Modal,
    TouchableOpacity,
    Dimensions,
    Image,
    FlatList,
    } from 'react-native'
    import LogUtil from "../../support/utils/LogUtil";
    const {width,height} =Dimensions.get('window')
    // import DatePicker from 'rt-datepicker';
    import moment from 'moment';

    var toYear = (moment(new Date()).format('YYYY'))
    var toMonth = (moment(new Date()).format('MM'))

    export default class RNCalendar_CL extends React.Component {

    constructor(props){
        super(props)
        this.state = {
            modalVisible:false,
            selectedDate:'',
            dayData: [],
            month: toMonth,
            year: toYear,
            color:[],
        }
    }
    
    showModal(){
        LogUtil.log('calender:'+this.props.defaultDate);
        this.setState({modalVisible:true})
    }
    
    cancelModal(){
        this.setState({modalVisible:false})
    }
    
    dateChanged(date){
        this.setState({modalVisible:false});
        this.props.dateChanged(date);
    }
    
    // ==============================日历相关======================================
    
    componentDidMount() {
        var dayCount = this.getDaysOfMonth(toYear, toMonth)
        var dayIn = this.getFirstDay(toYear, toMonth)
        var temp = []
        var color = []
        for (var i = 1; i < dayIn; i++) {
            temp.push(' ')
            color.push(0)
        }
        for (var i = 1; i <= dayCount; i++) {
            temp.push(i)
            color.push(0)
        }
        this.setState({
            dayData: temp,
            color:color,
        })
    
    }
    
    
    
    createHeaderBar = () =>{
        return(
            <View style={{
                height: 50,
                width: width,
                backgroundColor: '#FFFFFF',
                flexDirection: 'row',
                justifyContent: 'space-between',
                alignItems: 'center'
            }}>
                <TouchableOpacity
                    activeOpacity={1}
                    style={{marginLeft: 10}}
                    onPress={this.clickPrevious}>
                    {/*<Image*/}
                    {/*style={{*/}
                    {/*height: 30,*/}
                    {/*width: 30*/}
                    {/*}}*/}
                    {/*source={require('../../../resource/images/waitTransferOwner/add.png')}*/}
                    {/*resizeMode={'contain'}/>*/}
                    <Text style={{fontSize:14,
                        color:'#9EA3AD',
                    }}>上个月</Text>
                </TouchableOpacity>
                <Text style={{
                    fontSize: 16,
                    color:'#243047'
                }}>
                    {this.state.year + '年' + this.state.month + '月'}
                </Text>
                <TouchableOpacity
                    activeOpacity={1}
                    style={{marginRight: 10}}
                    onPress={this.clickNext}>
                    {/*<Image*/}
                    {/*style={{*/}
                    {/*height: 30,*/}
                    {/*width: 30*/}
                    {/*}}*/}
                    {/*source={require('../../../resource/images/waitTransferOwner/add.png')}*/}
                    {/*resizeMode={'contain'}/>*/}
                    <Text style={{fontSize:14,
                        color:'#9EA3AD',
                    }}>下个月</Text>
                </TouchableOpacity>
            </View>
        )
    }
    
    createDayBar = () =>{
        return(
            <View style={{
                height: 40,
                width: width,
                alignItems: 'center',
                flexDirection: 'row',
            }}>
                {this.createLab()}
            </View>
        )
    }
    
    createLab = () => {
        var dateArray = ['一', '二', '三', '四', '五', '六', '七']
        var array = []
        for (var i = 1; i < 8; i++) {
            array.push(
                <View
                    key={I}
                    style={{
                        width: width / 7,
                        height: 40,
                        justifyContent: 'center',
                        alignItems: 'center',
                        backgroundColor: '#FFFFFF'
                    }}>
                    <Text style={{
                        color: '#243047',
                        fontSize: 16
                    }}>
                        {dateArray[i - 1]}
                    </Text>
                </View>
            )
        }
        return array
    }
    creatContent = () =>{
        return(
            <FlatList
                data={this.state.dayData}
                numColumns={7}
                horizontal={false}
                extraData={this.state}
                renderItem={this.renderItem}
                keyExtractor={this.keyExtractor}/>
        )
    }
    clickItem = (item, index) => {
        if (item == ' ') {
            return
        }
        var temp = this.state.color
        if (temp[index] == 1) {
            temp[index] = 0
        }
        else if (temp[index] == 0) {
            temp[index] = 1
        }
        this.setState({
            selectedDate:(toYear+'-'+toMonth+'-'+item),
            color:temp
        },()=>{
            LogUtil.log('我选择的日期是'+this.state.selectedDate);
            this.dateChanged(this.state.selectedDate)
        })
    
    }
    
    
    
    renderItem = ({item,index}) => {
        return (
            <TouchableOpacity
                activeOpacity={1}
                onPress={this.clickItem.bind(this, item, index)}>
                <View
                    style={{
                        width: width / 7,
                        height: 40,
                        justifyContent: 'center',
                        alignItems: 'center',
                        borderRadius: 5,  //圆角5
                        borderWidth: 1,
                        borderColor:'white',
                        backgroundColor:(toYear+'-'+toMonth+'-'+item)===this.state.selectedDate ? '#3576F0' : 'white',
    
                    }}>
                    <Text
                        style={{color: (toYear+'-'+toMonth+'-'+item)===this.state.selectedDate ? 'white' : '#243047'}}>{item}</Text>
                </View>
            </TouchableOpacity>
        )
    }
    
    keyExtractor = (item, index) => 'Zdate' + index
    
    getDaysOfMonth = (year, month) => {
        var day = new Date(year, month, 0)
        var dayCount = day.getDate()
        return dayCount
    }
    
    getFirstDay = (year, month) => {
        var day = new Date(year, month - 1)
        var dayCount = day.getDay()
        if (dayCount == 0) {
            dayCount = 7
        }
        return dayCount
    }
    clickNext = () => {
        toMonth++
        if (toMonth > 12) {
            toMonth = 1
            toYear++
        }
        this.setState({
            month: toMonth,
            year: toYear
        })
    
        var dayCount = this.getDaysOfMonth(toYear, toMonth)
        var dayIn = this.getFirstDay(toYear, toMonth)
        var temp = []
        var color = []
        for (var i = 1; i < dayIn; i++) {
            temp.push(' ')
            color.push(0)
        }
        for (var i = 1; i <= dayCount; i++) {
            temp.push(i)
            color.push(0)
        }
        this.setState({
            dayData: temp,
            color:color,
        })
    }
    
    clickPrevious = () => {
        toMonth--
        if (toMonth < 1) {
            toMonth = 12
            toYear--
        }
        this.setState({
            month: toMonth,
            year: toYear
        })
        var dayCount = this.getDaysOfMonth(toYear, toMonth)
        var dayIn = this.getFirstDay(toYear, toMonth)
        var temp = []
        for (var i = 1; i < dayIn; i++) {
            temp.push(' ')
        }
        for (var i = 1; i <= dayCount; i++) {
            temp.push(i)
        }
        this.setState({
            dayData: temp
        })
    }
    

    // ==============================日历相关======================================

    render(){
        return <Modal animationType="fade"  //slide
                                    visible={this.state.modalVisible}
                                    transparent={true}
                                    onRequestClose={()=>this.setState({modalVisible:false})}
        >
            <View style={styles.modalStyle}>
                <View style={styles.subView}>
                    <View style={styles.canlendarStyle}>
                        {this.createHeaderBar()}
                        <View style={{
                            width: '100%',
                            height: 0.5,
                            alignSelf: 'center',
                            borderBottomWidth: 1,
                            borderBottomColor: '#dddddd',
                        }}/>
                        {this.createDayBar()}
                        {this.creatContent()}
    
    
                    </View>
    
                    {/*取消按钮*/}
                    <TouchableOpacity
                        style={[styles.actionItem,this.props.itemStyle,{borderTopWidth:0,height:50,marginBottom:-5}]}
                        onPress={()=>this.setState({modalVisible:false})}>
                        <Text style={styles.actionItemTitle}>取消</Text>
                    </TouchableOpacity>
                </View>
            </View>
        </Modal>
    }
    

    }

    const styles = StyleSheet.create({
    modalStyle:{
    justifyContent:'flex-end',
    alignItems:'center',
    flex:1,
    backgroundColor:'rgba(0,0,0,0.2)'
    },
    subView:{
    justifyContent:'flex-end',
    flexDirection: 'column',
    alignItems:'center',
    alignSelf:'stretch',
    width:width,
    backgroundColor:'#fff'

    },
    canlendarStyle:{
        width:width,
        height:320,
        alignItems:'center',
        justifyContent:'center',
        borderTopColor:'#cccccc',
        borderTopWidth:0.5,
        backgroundColor: 'white',
    },
    actionItemTitle:{
        fontSize:18,
        color:'blue',
        textAlign:'center',
    },
    

    })

    ======================js文件==========================
    用的时候 都在同一个界面

    1. import RNCalendar_CL from "../../RNCalendar_CL";
    2.  <RNCalendar_CL
                   ref="RNCalendar_CL"
                   dateChanged={(date) => this.dateChanged(date)}
               />
      
    3. 写一个按钮 呼出日历 :
      this.refs.RNCalendar_CL.showModal();

    4.当点击确定的时候:
    //从日历选择日期结果回调
    dateChanged = (date) => {
    };
    嗯 就酱

    展开全文
  • 在本系列中,您将学习如何使用React Native创建移动应用程序中常用的页面布局。 您将要创建的布局将不起作用-相反,本系列的主要重点是让您动手以布局React Native应用程序中的内容。 为了跟随本系列的进行,在您...

    在本系列中,您将学习如何使用React Native创建移动应用程序中常用的页面布局。 您将要创建的布局将不起作用-相反,本系列的主要重点是让您动手以布局React Native应用程序中的内容。

    为了跟随本系列的进行,在您阅读本教程中的分步说明之前,我建议您先尝试自己重新创建每个屏幕。 仅仅阅读本教程,您将不会真正从中受益! 首先尝试,然后再在此处查找答案。 如果成功使它看起来像原始屏幕,则将您的实现与我的实现进行比较。 然后自己决定哪个更好!

    在本系列的第二部分中,您将创建以下日历页面:

    日历页面

    日历应用程序用于跟踪用户添加的事件和约会。 您会在野外找到不同的变体,但是其中大多数将具有与物理日历相同的元素:当前月份和年份,月份中的日期以及用户添加的事件或约会。

    这是这种布局的几个示例:

    谷歌日历
    android日历

    项目设置

    当然,第一步是建立一个新的React Native项目:

    react-native init react-native-common-screens

    设置项目后,打开index.android.js文件,并将默认代码替换为以下代码:

    import React, { Component } from 'react';
    import {
      AppRegistry
    } from 'react-native';
    
    import Calendar from './src/pages/Calendar';
    
    export default class ReactNativeCommonScreens extends Component {
    
      render() {
        return (
          <Calendar />
        );
      }
    
    }
    
    AppRegistry.registerComponent('ReactNativeCommonScreens', () => ReactNativeCommonScreens);

    创建一个src/pages文件夹,并在其中创建Calendar.js文件。

    您还需要react-native-vector-icons包。 这专门用于导航图标以及页面中将需要的其他图标。

    npm install --save react-native-vector-icons

    打开android/app/build.gradle文件并添加对该包的引用:

    dependencies {
        //rest of the dependencies are here at the top
        compile project(':react-native-vector-icons') //add this
    }

    通过在底部添加以下内容,对android/settings.gradle文件执行相同的操作:

    include ':react-native-vector-icons'
    project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')

    打开android/app/src/main/java/com/react-native-common-screens/MainApplication.java并导入软件包:

    import java.util.Arrays;
    import java.util.List;
    
    import com.oblador.vectoricons.VectorIconsPackage; //add this

    最后,初始化程序包:

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
          new VectorIconsPackage() //add this
      );
    }

    创建日历页面

    好的,既然您已尝试自己编写布局代码(不作弊,对吗?),我将向您展示如何构建实现。

    起初,我以为这将是最难实现的,但是请相信我,只要您已经了解了基础知识,它实际上并没有那么复杂。 这里有很多机会可以使用JavaScript代码来进行渲染。

    首先包括所有需要的组件和软件包:

    import React, { Component } from 'react';
    
    import {
      StyleSheet,
      Text,
      View,
      ScrollView
    } from 'react-native';
    
    import Icon from 'react-native-vector-icons/FontAwesome';
    import { range } from 'lodash';
    import Button from '../components/Button';

    这次有一个尚未安装的新软件包,即lodash 您实际上并不需要整个lodash库,只需要range函数。 这用于根据特定范围生成数字数组。 您可以通过在终端上执行npm install --save lodash.range来仅安装此功能。

    添加用于创建页面的样板代码:

    export default class Calendar extends Component {
        render() {
        	return (
    			<ScrollView style={styles.container}>
    			    ...
    			</ScrollView>
    		);
    	}
    }
    
    const styles = StyleSheet.create({
    	container: {
    		flex: 1
    	}
    });

    页眉中包含三个元素:用于返回上一页的按钮,当前页的标题以及显示当前所选日期的人性化表示形式的文本。

    <View style={styles.header}>
        <Button 
    		noDefaultStyles={true}
    		onPress={this.press.bind(this)} 
    		styles={{button: styles.header_item}}
    	>
            <View style={styles.header_button}>
            	<Icon name="chevron-left" size={30} color="#FFF" />
            	<Text style={[styles.header_text]}> Menu</Text>
            </View>
        </Button>
        <View style={styles.header_item}>
        	<Text style={[styles.header_text, styles.text_center, styles.bold_text]}>Calendar</Text>
        </View>
    	<View style={styles.header_item}>
        	<Text style={[styles.header_text, styles.text_right]}>Today</Text>
        </View>
    </View>
    日历页面初步外观

    header具有rowflexDirection ,因此每个header_item均水平堆叠。 为其分配了相同的flex值,因此它们消耗相等的空间。 text_centertext_right用于将那些header_item的文本与中心和右边对齐。 这样做是因为默认情况下,它们在容器的最左侧对齐。

    header: {
        backgroundColor: '#329BCB',
    	flexDirection: 'row',
    	padding: 20
    },
    header_item: {
    	flex: 1
    },
    header_button: {
    	flexDirection: 'row'
    },
    text_center: {
    	textAlign: 'center'
    },
    text_right: {
    	textAlign: 'right'
    },
    header_text: {
    	color: '#fff',
    	fontSize: 20
    },
    bold_text: {
    	fontWeight: 'bold'
    },

    添加样式后,它现在应如下所示:

    日历页面样式的标题

    接下来是实际日历,分为三部分:标题,星期几和日历天:

    <View>
        <View style={styles.calendar_header}>
            ...
        </View>
        <View style={styles.calendar_weekdays}>
            ...
        </View>
        <View style={styles.calendar_days}>
            ...
        </View>
    </View>

    日历标题允许用户更改年份和月份。

    至少有两种方法可以实现。 第一种方法是将每个元素视为单个项目,然后将justifyContent: 'space-between'应用于其容器。 第二种方法是将与年份有关的所有元素分组,对与月份有关的元素进行分组。

    第二种方法是下面应用的方法。 从语义上讲,这更有意义,因为用于回溯年份的按钮,年份本身以及用于向前导航的按钮都是相关的,因此您可以将它们放在同一个容器中,将它们视为一件事情。 月份控件也是如此。

    <View style={styles.calendar_header}>
        <View style={styles.calendar_header_item}>
            <Button 
        		noDefaultStyles={true}
        		onPress={this.press.bind(this)}
        	>
                <Icon name="chevron-left" size={18} color="#333" />
            </Button>
        	<Text style={styles.calendar_header_text}>2013</Text>
        	<Button 
        		noDefaultStyles={true}
        		onPress={this.press.bind(this)}
        	>
                <Icon name="chevron-right" size={18} color="#333" />
            </Button>
        </View>
        
        <View style={styles.calendar_header_item}>
        	<Button 
        		noDefaultStyles={true}
        		onPress={this.press.bind(this)}
        	>
                <Icon name="chevron-left" size={18} color="#333" />
            </Button>
        	<Text style={styles.calendar_header_text}>November</Text>
        	<Button 
        		noDefaultStyles={true}
        		onPress={this.press.bind(this)}
        	>
                <Icon name="chevron-right" size={18} color="#333" />
            </Button>
        </View>
    </View>
    日历页面已添加日历标题

    从那里,您可以对同一行中的这两组组件应用相同的技术。 要在两个按钮(后退和前进)和标签之间添加空格,我们使用justifyContent: 'space-between' 我们使用alignItems: 'center'将其中的所有元素向中心微移。 最后,我们添加左右填充以在两组之间添加更多空间。

    calendar_header: {
        flexDirection: 'row'
    },
    calendar_header_item: {
    	flex: 1,
    	flexDirection: 'row',
    	justifyContent: 'space-between',
    	alignItems: 'center',
    	paddingTop: 20,
    	paddingRight: 40,
    	paddingLeft: 40
    },
    calendar_header_text: {
    	fontWeight: 'bold',
    	fontSize: 20
    },
    日历页面添加了日历标题样式

    接下来是工作日。 我们使用一个函数来渲染这些元素,因为最好使用一些JavaScript代码来渲染所有元素。

    <View style={styles.calendar_weekdays}>
        { this.renderWeekDays() }
    </View>

    因此,您可以拥有一个包含一周中各天的数组,而不是让一周中的每一天呈现七个ViewText组件。 然后,您可以使用Array.map()函数迭代那些日子。 对于每次迭代,渲染一个显示日期的Text组件。

    renderWeekDays() {
        let weekdays = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
    	return weekdays.map((day) => {
    		return (
    			<Text key={day} style={styles.calendar_weekdays_text}>{day.toUpperCase()}</Text>
    		);
    	});
    }

    请注意,在上面的代码中, toUpperCase()函数用于将每天的所有字母都转换为大写。 React Native不带有text-transform CSS属性,因此,这是除使用大写字符串手动之外还实现大写字母的唯一方法。

    日历页面已添加日历工作日

    这是日历标题的样式:

    calendar_weekdays_text: {
        flex: 1,
    	color: '#C0C0C0',
    	textAlign: 'center'
    },
    日历页面样式的日历工作日

    日历天还使用一个函数来渲染日期:

    <View style={styles.calendar_days}>
        { this.renderWeeks() }
    </View>

    renderWeeks()函数在lodash中使用range()函数来生成一个数组,其中包含上个月的日期和当前月份的日期。 然后将这两个数组合并在一起。

    但是,您不能直接将结果数组用作日历天的数据源。 这是因为,如果您简单地遍历项目并每天输出Text组件,则每周之间不会有任何区别。 您已经知道要使每个日历日内联,您需要将flexDirection: 'row'应用于其容器。 因此,将其应用于单个容器将导致所有日历天都放在一行中。

    这意味着您每周需要有一个单独的容器。 问题是如何。 同样,至少有两种方法可以实现此目的。

    第一种方法是为具有可变店多天目前如何输出,然后添加一个条件语句,将呈现一个开口<View>每一个变量包含时间0和结束</View>每次有时间7 一旦为7 ,将其重置为0 这是最直接的方法。

    但是我将在这里使用其他方法。 下面,使用getWeeksArray()函数来实现它。 此函数接受天数数组并将其分组为每个包含7天的数组。 从那里,您可以遍历这些数组中的每个数组以呈现周容器。 然后,对于每次迭代,您都再次遍历一周中的几天以呈现这些天。 这就是renderDays()函数的作用。

    renderWeeks() {
        let past_month_days = range(27, 31);
    	let this_month_days = range(1, 30);
    
    	let days = past_month_days.concat(past_month_days, this_month_days);
    	let grouped_days = this.getWeeksArray(days);
    
    	return grouped_days.map((week_days, index) => {
    		return (
    			<View key={index} style={styles.week_days}>
    				{ this.renderDays(week_days) }				
    			</View>
    		);
    	});
    }

    这是getWeeksArray()函数:

    getWeeksArray(days) {
        var weeks_r = [];
    	var seven_days = [];
    	var count = 0;
    	days.forEach((day) => {
    	  count += 1;
    	  seven_days.push(day);
    	  if(count == 7){
    	    weeks_r.push(seven_days)
    	    count = 0;
    	    seven_days = [];
    	  }
    	});
    	return weeks_r;
    }

    这是renderDays()函数:

    renderDays(week_days) {
        return week_days.map((day, index) => {
    		return (
    			<Button 
    				label={day}
    				key={index} 
    				onPress={this.press.bind(this)} 
    				styles={{button: styles.day, label: styles.day_text}}
    				noDefaultStyles={true}
    			/>	
    		);
    	});
    }
    日历页面已添加日历天

    添加每个星期( week_days )和一天( day and day_text )的day_text

    week_days: {
        flexDirection: 'row'
    },
    day: {
    	flex: 1,
    	backgroundColor: '#F5F5F5',
    	padding: 17,
    	margin: 2
    },
    day_text: {
    	textAlign: 'center',
    	color: '#A9A9A9',
    	fontSize: 25
    },
    日历页面添加日历天样式

    接下来是用户为当前选定的日期以及选定的日期和时间添加的注释。 同样,最好根据元素的用途对元素进行分组,而不是根据元素在页面中的放置方式进行分组。 当然所有这些元素都是相关的,因此我们将它们放在同一容器中。 但是仔细观察,您会发现可以将它们进一步分组:实际注释和所选日期。 考虑到这一点,您将得到以下标记:

    <View style={styles.notes}>
        <View style={styles.notes_notes}>
    		<Text style={styles.notes_text}>Riding my bike around the neighborhood.</Text>
    	</View>
    	<View style={[styles.notes_selected_date]}>
    		<Text style={styles.small_text}>8:23 PM</Text>
    		<Text style={styles.big_text}>14</Text>
    		<View style={styles.inline}>
    			<Icon name="bicycle" size={20} color="#CCC" />
    			<Text style={styles.small_text}> THURSDAY</Text>
    		</View>
    	</View>
    </View>
    日历页面添加笔记

    所选日期所占用的空间少于注释,因此您必须对注释应用更大的flex值。 在这种情况下,使用flex: 3flex: 1 ,这意味着注释占用了3/4的可用空间,而所选日期消耗了1/4。 如果对您更有意义,也可以使用小数( 0.750.25 )。 重要的是选择一个标准并坚持下去。 alignItems: 'flex-end'用于notes_selected_date以便其所有子代都向右对齐。 这是必需的,因为默认情况下它们在左侧对齐。

    notes: {
        marginTop: 10,
    	padding: 20,
    	borderColor: '#F5F5F5',
    	borderTopWidth: 1,
    	borderBottomWidth: 1,
    	flexDirection: 'row',
    	backgroundColor: '#FAFAFA'
    },
    notes_notes: {
    	flex: 3
    },
    notes_text: {
    	fontSize: 18
    },
    notes_selected_date: {
    	flex: 1,
    	alignItems: 'flex-end',
    	flexDirection: 'column'
    },
    small_text: {
    	fontSize: 15
    },
    big_text: {
    	fontSize: 50,
    	fontWeight: 'bold'
    },
    inline: {
    	flexDirection: 'row'
    },
    日历页面在日志中添加了样式

    最后,我们添加了日志,这些日志与上一教程中的日志非常相似,因此我将留给您了解如何实现布局!

    <View style={styles.logs}>
        <View>
    		<Text style={styles.log_text}>Create New Entry</Text>
    		<Text style={styles.log_subtext}>On Thursday, November 14</Text>
    	</View>
    	<Button 
    		noDefaultStyles={true}
    		onPress={this.press.bind(this)}
    	>
    		<Icon name="chevron-right" size={30} color="#CCC" />
    	</Button>
    </View>

    以下是样式:

    logs: {
        flexDirection: 'row',
    	justifyContent: 'space-between',
    	alignItems: 'center',
    	padding: 20,
    	borderColor: '#F5F5F5',
    	borderBottomWidth: 1
    },
    log_text: {
    	fontSize: 25
    },
    log_subtext: {
    	fontSize: 18
    }

    结论

    而已! 在本教程中,您创建了一个日历页面。 我们为应用程序设计了一个不错的日历布局,并且向您展示了如何使用JavaScript代码来补偿Flexbox的某些局限性。

    如您所见,我们需要一种将连续天数限制为仅7天的方法。 Flexbox没有指定此方法的方式,因此我们使用JavaScript重构了原始的天数组,从而将它们分为每个包含7天的组。 从那里开始,我们要做的就是将每个组包装在一个View ,然后应用flexDirection: 'row'使每个组在各自的行中呈现。

    翻译自: https://code.tutsplus.com/tutorials/common-react-native-app-layouts-calendar-page--cms-27641

    展开全文
  • React Native Calendars ✨
  • react-native-whc-calendar跨平台个性化日历组件.zip,A react native module to show calendar, it works on iOS and Android. 跨平台日历组件支持iOS、Android
  • React Native CalendarView RN 日历控件
  • React Native 日期选择器

    2019-05-25 14:41:09
    React Native 中ios 有DatePickerIOS,android 有DatePickerAndroid,本人只是试了下ios,设置node= date,显示出来的效果是: render() { return ( <View style={styles.container}> <...

    项目中要用到日期选择器,类似这样:

    在React Native 中ios 有DatePickerIOS,android 有DatePickerAndroid,本人只是试了下ios,设置node= date,显示出来的效果是:

      render() {
        return (
          <View style={styles.container}>
            <DatePickerIOS
              date={this.state.chosenDate}
              onDateChange={this.setDate}
              mode = 'date'
              //format="YYYY-MM-DD"
            />
          </View>
        )
      }
    }

    达不到我要显示的效果,为啥是英文的,于是开始搜网站,发现两个比较好的组件:

     //集成了ios 和android ,两个个平台组件长得不一样,但都和原生一样。
    (1)https://www.npmjs.com/package/react-native-datepicker 
    
     //类似ios,两个平台长得差不多,这个后期可能会用到。
    (2)https://github.com/henninghall/react-native-date-picker
    
     //这个网站有很多封装好的组件,需要学习一下。
    (3)https://github.com/forrest23/ReactNativeComponents 
    
     //和1类似,应该是同一个
    (4)https://mp.weixin.qq.com/s/PAcIUkUSCStubGz2a-LNOg
    
     //这是另外一种日期选择器
     https://swift.ctolib.com/article/wiki/111090
    
    //这个日期选择器可以进行各种类型的选择 城市 民族,应该都可以实现。
    https://blog.csdn.net/xukongjing1/article/details/85018902

    后来问Q友,告诉我国际化,奥,有个属性是locale,我没有理解,原来是通过国际化显示中国的显示形式,于是改代码为:

      render() {
        return (
          <View style={styles.container}>
            <DatePickerIOS
              date={this.state.chosenDate}
              onDateChange={this.setDate}
              mode = 'date'
              format="YYYY-MM-DD"
              //ormat="YYYY年MM月DD日"
              locale = 'zh-Hans'
            />
          </View>
        )
      }
    }

    结果出来了,开心!!!

     

     

    展开全文
  • react native自带的时间处理方法在实际项目使用过程中有一定的局限性,所以找了个三方的工具类moment,使用起来还是比较方便的,记录一下:1.安装:npm install moment --save 2.引入:import moment from 'moment';...
  • ReactNative工程根目录中 终端运行如下命令 npm install react-native-action-button --save 相关第三方组件资源 第一部分 react-native-carousel 轮播图 react-native-countdown 倒计时 react-native-device-...
  • 本教程的实现效果如下: 为了实现其淡入/淡出的覆盖效果, 还有取消按钮...三方组件的地址:https://github.com/eyaleizenberg/react-native-custom-action-sheet (可以看看,也可以直接按我的步骤走) 1. 在termi...
  • 一直想写一下我在React Native原生模块封装方面的一些经验和心得,来分享给大家,但实在抽不开身,今天看了一下日历发现2018年马上就结束了,所以就赶年底将这篇博文写好并发布(其实是两篇:要看iOS篇的点这里...
  • 对课程有什么问题或者有什么好的建议都可以联系Hank老师QQ:471926977 ...新颖、实用、详尽的ReactNative初级课程,本 教程涵盖ReactNative官方的新组件和实践,甚 至包括尚未正式发布的新特性 时”!
  • 移动端应用高速发展, 本教程便是使用ReactNative开发的高性能基于原生应用跨Android与iOS两大平台的大型综合App。 本实战项目使用react native 开发招聘,房产,点餐,商城,二手车,本地商务的大型综合门户性...
  • react-native-linear-gradient 颜色渐变处理 react-native-login 视频界面登录 react-native-keyboard-aware-scroll-view 键盘显示处理 react-native-popup-dialog 弹窗 react-native-dropdownalert 一种非常漂亮的...
  • React Native开发工具

    2020-06-23 11:06:35
    React Native也不例外。 在本文中,我将向您介绍一些最佳的UI框架,库,组件,开发工具和Web服务,它们将使您成为一个更快乐,更高效的React Native开发人员。 文字编辑器和IDE Visual Studio Code是一个文本编辑...
  • React Native 项目常用第三方组件汇总: react-native-animatable 动画 react-native-carousel 轮播 react-native-countdown 倒计时 react-native-device-info 设备信息 react-native-fileupload ...
  • react-native获取农历日期和二十四节气 这是做react-native开发以来写的第一篇文章,记录一下开始,以后还会有更多的文章发出来! 问题描述 开发中有这样的需求,要把当前的日期转化为农历日期或者根据当前的时间...
1 2 3 4 5 ... 20
收藏数 1,445
精华内容 578
关键字:

reactnative日历