zoukankan      html  css  js  c++  java
  • React Native 实现城市选择组件

    前言

    很多 App 都有城市选择的功能,今天带大家编写一个城市选择组件。下面是这个组件的效果图。

    功能分析

    从上图中可以看出,我们将所有城市按照字母区分块,右边是字母索引。通过点击右边的字母可以跳转到相对应的块,同样的,在移动左边的列表的时候,右边也会跟随移动来显示不同的高亮。

    这个组件中,我们通过 react Native 提供的 FlatList 来实现。最开始我的实现是通过自己计算高度,在阅读文档的时候,发现 FlatList 组件提供了几个很好用的特性:

    onViewableItemsChanged
    scrollToIndex

    思路

    城市选择组件最重要是需要城市数据的来源,可以通过网络获取,但是由于数据量过大,网络的性能不太理想。我提前准备了一个 json 文件放到项目中。数据的结构如下:

    {
        "data": [
            {
                "key": "A",
                "cities": [
                    {
                        "key": "152900",
                        "city": "阿拉善盟"
                    },
                    ...
                ]
            },
            {
                "key": "B",
                "cities": [
                    {
                        "key": "130600",
                        "city": "保定市"
                    },
                    ...
                ]
            },
            ...
        ]
    }

    在渲染的时候,通过 FlatList 将每个字母渲染出来,每个字母中的城市通过遍历来渲染出来。在每次可见项目变化时,右边的字母列表通过判断是否等于当前可见项目来判断高亮状态。点击右边的字母时,则跳转到指定的 index 上。

    广州品牌设计公司https://www.houdianzi.com PPT模板下载大全https://redbox.wode007.com

    实现

    先准备好城市数据放到 src/assets 目录中:

    接下来在 src 目录中新建一个 ChoseCity.js 文件作为组件。在组件中现将文件内容导入进来,用作 FlatList 组件的数据。

    import react, { Component } from 'react';
    import { FlatList, View, StyleSheet } from 'react-native';
    
    export default class ChoseCity extends Component {
        constructor(props) {
            super(props);
    
            this.state = {
                data: [],               // 用于存放所有的城市数据
                right: [],              // 右边的字母导航数据
                currentLetter: 'A'      // 当前选中的城市
            }
        }
    
        async componentDidMount() {
            const { data } = await require('./assets/cities.json');
            console.log(data);
        }
    
        render() {
            return (
                <View>
    
                </View>
            );
        }
    }

    在调试信息中我们可以看到 data 的数据结构:

    接下来我们将 data 中的数据存放到 state 中:

    async componentDidMount() {
        const { data } = await require('./assets/cities.json');
        let cityInfo = [];
        let right = [];
    
        // 这里的保证了城市数据和右边的字母导航同步
        data.map((item, index) => {
            cityInfo[index] = { key: item.key, data: item.cities };
            right[index] = item.key;
        });
    
        this.setState({ data: cityInfo, right: right });
    }

    接下来我们就开始渲染 FlatList 中的数据:

    renderItem = ({ item, index }) => (
        <View style={styles.cityPiece}>
            <Text style={styles.keyText}>{item.key}</Text>
            <View style={styles.cities}>
                {item.data.map(({ city }, index) => (
                    <TouchableOpacity key={index} style={styles.cityItem}>
                        <Text>{city}</Text>
                    </TouchableOpacity>
                ))}
            </View>
        </View>
    );
    
    render() {
        return (
            <View>
                <FlatList
                    data={this.state.data}
                    renderItem={this.renderItem}
                    keyExtractor={item => item.key}
                />
            </View>
        );
    }

    上面代码中,我使用了 map 来遍历每个字母中包含的城市。效果如下:

    接下来我们就来实现右边的导航,在 render 方法的根 View 组件中添加下面代码:

    <View style={styles.right}>
        {/* 由于数据不多,也直接使用 map 来遍历 */}
        {this.state.right.map((item, index) => (
            <TouchableOpacity key={index}>
                <Text style={[this.state.currentLetter === item && { color: '#FD7700' }]}>{item}</Text>
            </TouchableOpacity>
        ))}
    </View>
    }

    到这里,外观部分已经全部实现了,这里是样式代码:

    const styles = StyleSheet.create({
        keyText: {
            fontSize: 16,
            fontWeight: 'bold',
        },
        cityPiece: {
            marginTop: 6,
            backgroundColor: '#FFF',
            paddingLeft: 21,
            paddingRight: 21,
            paddingTop: 15,
            paddingBottom: 15
        },
        cities: {
            flexWrap: 'wrap',
            flexDirection: 'row',
        },
        cityItem: {
            flex: 0,
            backgroundColor: '#F6F5F5',
            paddingLeft: 22,
            paddingRight: 22,
            paddingTop: 11,
            paddingBottom: 11,
            borderRadius: 18,
            marginTop: 14,
            marginRight: 10,
        },
        right: {
            position: 'absolute',
            top: 0,
            right: 0,
            bottom: 0,
            paddingRight: 5,
            paddingTop: 5,
            paddingBottom: 5,
            justifyContent: 'space-between',
            backgroundColor: '#F6F5F5',
            paddingLeft: 10,
        },
    })

    接下来我们先实现右边导航跟随数据的滚动来改变高亮。这里需要用到 onViewableItemsChanged 。这里回调函数给了我们两个参数: viewableItems 和 changed ,我们来看看它们的结构。

    从这里可以看出,viewableItems 数组中第一个元素是当前可见的第一项,所以我们只需要第一个可见元素作为当前项即可。

    onViewableItemsChanged = ({ viewableItems, changed }) => {
        // 将第一个可见的元素,作为当前元素
        this.setState({
            currentLetter: viewableItems[0].key
        });
    }
    
    render() {
        return (
            <View>
                <FlatList
                    data={this.state.data}
                    showsVerticalScrollIndicator={false}
                    renderItem={this.renderItem}
                    keyExtractor={item => item.key}
                    onViewableItemsChanged={this.handleViewableItemsChanged}
                />
            </View>
        );
    }

    到这里已经实现了跟随高亮,怎么样很简单吧,是不是比想象中更简单,接下来我们来实现点击字母跳转到指定字母块,这里要用到 scrollToIndex ,代码如下:

    scrollTo = (index) => {
        this.list.scrollToIndex({ viewOffset: -6, viewPosition: 0, index, animated: true });
    }
    
    render() {
        return (
            <View style={{backgroundColor: '#F6F5F5'}}>
                <View style={styles.right}>
                    {/* 由于数据不多,也直接使用 map 来遍历 */}
                    {this.state.right.map((item, index) => (
                        {/* 利用 index 来进行跳转 */}
                        <TouchableOpacity key={index} onPress={() => {this.scrollTo(index)}}>
                            <Text style={[this.state.currentLetter === item && { color: '#FD7700' }]}>{item}</Text>
                        </TouchableOpacity>
                    ))}
                </View>
    
                <FlatList
                    style={{marginRight: 30}}
                    data={this.state.data}
                    ref={flatList => this.list = flatList}
                    showsVerticalScrollIndicator={false}
                    renderItem={this.renderItem}
                    keyExtractor={item => item.key}
                    onViewableItemsChanged={this.handleViewableItemsChanged}
                />
            </View>
        );
    }

    总结

    这个组件并不是一个完善的组件,还有一个小 bug,当滚动到 z 的时候,右边 z 并不会高亮,这里可以判断是否已经到底,如果到底,则高亮字母 z。如果还需要其它功能,大家自己扩展即可。

  • 相关阅读:
    STL Allocator
    Several NeedToKnow(assert/stdin/stdout/CString/Standard C++ Library)
    VS Project Property Sheet
    进度总结(3)
    进度总结(2)
    进度总结(4)
    进度总结(7)
    进度总结(1)
    进度总结(5)
    进度总结(6)
  • 原文地址:https://www.cnblogs.com/qianxiaox/p/14102550.html
Copyright © 2011-2022 走看看