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

组件效果如下
IOS Android
使用组件
<AreaPicker
    onFinished={this.onFinished.bind(this)}
    modalHeight={800}
    visible={visible} 
/>
复制代码
  • visible: 类型 boolean, 控制是否显示组件

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

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

    onFinished(list) {
        this.setState({
            selected: (list.map(o => o.label)).join('')
        })
    }
    复制代码
实现逻辑
动画
  • 使用react-native-animatable, 需要先安装。

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

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

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

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

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

    <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 ~ !

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 React Native 组件,可以实现拍照上传多张图片的功能: ```javascript import React, { useState } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Image, ScrollView } from 'react-native'; import * as ImagePicker from 'expo-image-picker'; import Constants from 'expo-constants'; const ImagePickerComponent = () => { const [selectedImages, setSelectedImages] = useState([]); const pickImage = async () => { let result = await ImagePicker.launchCameraAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: true, aspect: [4, 3], quality: 1, }); if (!result.cancelled) { setSelectedImages([...selectedImages, { uri: result.uri }]); } }; return ( <View style={styles.container}> <TouchableOpacity style={styles.button} onPress={pickImage}> <Text style={styles.buttonText}>Take a photo</Text> </TouchableOpacity> <ScrollView horizontal={true}> {selectedImages.map((image, index) => ( <View key={index}> <Image source={{ uri: image.uri }} style={styles.image} /> </View> ))} </ScrollView> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', paddingTop: Constants.statusBarHeight, backgroundColor: '#ecf0f1', padding: 8, }, button: { alignItems: 'center', backgroundColor: '#DDDDDD', padding: 10, marginBottom: 10, }, buttonText: { fontSize: 20, }, image: { width: 100, height: 100, margin: 5, }, }); export default ImagePickerComponent; ``` 这个组件使用了 `expo-image-picker` 库来打开相机拍照,并将拍摄的图片加入到一个数组中。这个数组中的每一个元素都是一个对象,包含了图片的 URI。然后,使用 `ScrollView` 组件将所有的图片展示出来。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值