写一个地区选择组件备用(ReactNative)

组件效果以下

IOS Android

使用组件

<AreaPicker
    onFinished={this.onFinished.bind(this)}
    modalHeight={800}
    visible={visible} 
/>
复制代码
  • visible: 类型 boolean, 控制是否显示组件node

  • modalHeight:类型 number, 设置内容窗口的高度react

  • onFinished:类型 func, 选取完整地区后回调, 返回值是一个数组[{label: 'xx', value: 'xx'}]git

    onFinished(list) {
        this.setState({
            selected: (list.map(o => o.label)).join('')
        })
    }
    复制代码

实现逻辑

动画
  • 使用react-native-animatable, 须要先安装。github

    yarn add react-native-animatable
    复制代码
  • 遮罩层的淡入效果npm

    <Animatable.View style={[styles.container, !visible && {opacity: 0, transform:[{scale: 0}]}]} ref = {ref => this.containerRef = ref}>
    
    </Animatable.View>
    复制代码

    初始化组件的时候,componentWillReceiveProps 不会执行,当props发生变化时执行,能够在这个函数里调用this.setState()去更新组件内部属性,其中也能够经过this.props去获取到旧的属性值,完成一些对比逻辑。react-native

    componentWillReceiveProps(nextProps){
        const { visible } = nextProps
        !!this.containerRef && this.containerRef.animate('fadeIn', ANIMATE_DURATION).then(() => {
            // todo...
        })
    }
    复制代码
  • 选项卡的滚动线条数组

    线条元素绝对定位,宽度动态获取,移动的位置经过 translateX 设置bash

    <Animatable.View
        animation={{
          0: {
            translateX: startTranslateValue
          },
          1: {
            translateX: endTtranslateValue
          }
        }}
        delay={0}
        duration={200}
        easing="ease-in-out"
        // iterationCount="infinite"
        useNativeDrive
        style={[styles.lineStyle, {width: lineWidth}]}
    >
    </Animatable.View>
    复制代码

    其中 startTranslateValue 是始点, endTtranslateValue 是终点。 若是一开始从0移动到100,再接下来多是从100移动200函数

    有一个属性 currentActiveIdx 指向当前Tab索引,当切换Tab时,currentActiveIdx 跟着变化,再发现 currentActiveIdx 发生变化的时候, 想办法调整线条宽度和移动的位置动画

    componentWillReceiveProps(nextProps) {
        const newVal = nextProps.currentActiveIdx
        const oldVal = this.props.currentActiveIdx
        if (newVal == oldVal) return
        // todo 调整线条样式
    }
    复制代码

    具体todo了什么?

    • 想办法获取组件宽高位置信息。

    • 获取到上一次oldVal的组件的宽高位置, 记录。

    • 获取到这一次newVal的组件的宽高位置, 记录。

    • 始点startTranslateValue 设置为这一次的终点。

    • 终点 endTtranslateValue 设置为这一次的x坐标值(nodeProps.x - scaleSize(30) 减去容器的左边距内填充值)。

    • 定时器设置,确保组件完成渲染,宽高已经撑开。

    setTimeout(() => {
            getNodePropsByRef(this['item' + oldVal]).then(nodeProps => {
                this.map[oldVal] = {
                    w: nodeProps.width,
                    x: nodeProps.x
                }
                getNodePropsByRef(this['item' + newVal]).then(nodeProps => {
                    this.map[newVal] = {
                        w: nodeProps.width,
                        x: nodeProps.x - scaleSize(30)
                    }
                    this.setState({
                        startTranslateValue: this.state.endTtranslateValue,
                        endTtranslateValue: this.map[newVal].x,
                        lineWidth: parseFloat(this.map[newVal].w)
                    })
                })
            })
    }, 100)
    复制代码

    getNodePropsByRef 方法以下:

    xy 是相对于整个屏幕,左上角坐标

    import {findNodeHandle, UIManager} from 'react-native'
    export const getNodePropsByRef = ref => {
    const handle = findNodeHandle(ref);
        return new Promise((resolve) => {
          UIManager.measure(handle, (x, y, width, height, pageX, pageY) => {
            resolve({
              x,
              y,
              width,
              height,
              pageX,
              pageY
            })
          })
        })
      } 
    复制代码
省市区数据及其处理
  • 数据用的是 area-data

    pca['86'] // 等同于 AreaData['86']
    // 全部省份:{'110000': '北京市', '120000': '天津市', '130000': '河北省', ...}
    
    pcaa['130000'] // 等同于 AreaData['130000']
    // 对应省份的全部城市:{'130100': '石家庄市', '130200': '唐山市', '130300': '秦皇岛市', ...}
    
    pcaa['130200'] // 等同于 AreaData['130200']
    // 对应市的全部县区:{'130201': '市辖区', '130202': '路南区', '130203': '路北区', ...}
    复制代码
  • 初始时配置的数据

    selectedRows: [
        {
            label: '请选择',
            value: null,
            values: pca['86']
        }
    ]
    复制代码

    当选中其中一个数据时,向selectedRows添加第二组数据

    selectedRows.push({
        label: '请选择',
        value: null,
        values: pcaa[item.key]
    })
    复制代码

    当已经有三组数据,并选中第一组的某个值时, 去掉第三组数据

    selectedRows.length == 3 && selectedRows.splice(2, 1)
    复制代码

    一样的思路处理第二组,第三组数据的选取,最后判断选完后,调用函数返回数据

    onFinished(selectedRows.map(o => ({label: o.label, value: o.value})))
    复制代码
  • 列表组件 FlatList

    <FlatList
        ref={getFlatListRef}
        showsVerticalScrollIndicator={false}
        onContentSizeChange={onFlatListContentSizeChange}
        data={values}
        keyExtractor={(item, index) => item.key + ''}
        renderItem={ItemView}
    />
    复制代码

    每次更换列表数据时,须要滚动到顶部

    onFlatListContentSizeChange(){
        if (!!this.flatListRef) {
           this.flatListRef.scrollToIndex({index: 0, animated: false})
        }
    }
    复制代码
  • GITHUB - 博客 欢迎start ~ !

  • GITHUB - 组件 欢迎start ~ !

相关文章
相关标签/搜索