React native城市列表组件

城市列表选择是很多app共有的功能,比如典型的美图app。那么对于React Native怎么实现呢?

要实现上面的效果,首先需要对界面的组成简单分析,界面的数据主要由当前城市,历史访问城市和热门城市组成,所以我们在提供Json数据的时候就需要将数据分为至少3部分。

const ALL_CITY_LIST = DATA_JSON.allCityList;
const HOT_CITY_LIST = DATA_JSON.hotCityList;
const LAST_VISIT_CITY_LIST = DATA_JSON.lastVisitCityList;

而要实现字母索引功能,我们需要自定义一个控件,实现和数据的绑定关系,自定义组件代码如下: CityIndexListView.js

'use strict';
import React, {Component} from 'react';
import {
    StyleSheet,
    View,
    Text,
    TouchableOpacity,
    ListView,
    Dimensions,
} from 'react-native';

import Toast, {DURATION} from './ToastUtil'

const SECTIONHEIGHT = 30;
const ROWHEIGHT = 40;
const ROWHEIGHT_BOX = 40;
var totalheight = []; //每个字母对应的城市和字母的总高度

const {width, height} = Dimensions.get('window');

var that;

const key_now = '当前';
const key_last_visit = '最近';
const key_hot = '热门';

export default class CityIndexListView extends Component {

    constructor(props) {
        super(props);

        var getSectionData = (dataBlob, sectionID) => {
            return sectionID;
        };
        var getRowData = (dataBlob, sectionID, rowID) => {
            return dataBlob[sectionID][rowID];
        };

        let ALL_CITY_LIST = this.props.allCityList;
        let CURRENT_CITY_LIST = this.props.nowCityList;
        let LAST_VISIT_CITY_LIST = this.props.lastVisitCityList;
        let HOT_CITY_LIST = this.props.hotCityList;

        let letterList = this._getSortLetters(ALL_CITY_LIST);

        let dataBlob = {};
        dataBlob[key_now] = CURRENT_CITY_LIST;
        dataBlob[key_last_visit] = LAST_VISIT_CITY_LIST;
        dataBlob[key_hot] = HOT_CITY_LIST;

        ALL_CITY_LIST.map(cityJson => {
            let key = cityJson.sortLetters.toUpperCase();

            if (dataBlob[key]) {
                let subList = dataBlob[key];
                subList.push(cityJson);
            } else {
                let subList = [];
                subList.push(cityJson);
                dataBlob[key] = subList;
            }
        });

        let sectionIDs = Object.keys(dataBlob);
        let rowIDs = sectionIDs.map(sectionID => {
            let thisRow = [];
            let count = dataBlob[sectionID].length;
            for (let ii = 0; ii < count; ii++) {
                thisRow.push(ii);
            }

            let eachheight = SECTIONHEIGHT + ROWHEIGHT * thisRow.length;
            if (sectionID === key_hot || sectionID === key_now || sectionID === key_last_visit) {
                let rowNum = (thisRow.length % 3 === 0)
                    ? (thisRow.length / 3)
                    : parseInt(thisRow.length / 3) + 1;

                console.log('sectionIDs===>' + sectionIDs + ", rowNum=====>" + rowNum);

                eachheight = SECTIONHEIGHT + ROWHEIGHT_BOX * rowNum;
            }

            totalheight.push(eachheight);

            return thisRow;
        });


        let ds = new ListView.DataSource({
            getRowData: getRowData,
            getSectionHeaderData: getSectionData,
            rowHasChanged: (row1, row2) => row1 !== row2,
            sectionHeaderHasChanged: (s1, s2) => s1 !== s2
        });

        this.state = {
            dataSource: ds.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs),
            letters: sectionIDs
        };

        that = this;
    }

    _getSortLetters(dataList) {
        let list = [];

        for (let j = 0; j < dataList.length; j++) {
            let sortLetters = dataList[j].sortLetters.toUpperCase();

            let exist = false;
            for (let xx = 0; xx < list.length; xx++) {
                if (list[xx] === sortLetters) {
                    exist = true;
                }
                if (exist) {
                    break;
                }
            }
            if (!exist) {
                list.push(sortLetters);
            }
        }

        return list;
    }

    _cityNameClick(cityJson) {
        // alert('选择了城市====》' + cityJson.id + '#####' + cityJson.name);
        this.props.onSelectCity(cityJson);
    }

    _scrollTo(index, letter) {
        this.refs.toast.close();
        let position = 0;
        for (let i = 0; i < index; i++) {
            position += totalheight[i]
        }
        this._listView.scrollTo({y: position});
        this.refs.toast.show(letter, DURATION.LENGTH_SHORT);
    }

    _renderRightLetters(letter, index) {
        return (
            <TouchableOpacity key={'letter_idx_' + index} activeOpacity={0.6} onPress={() => {
                this._scrollTo(index, letter)
            }}>
                <View style={styles.letter}>
                    <Text style={styles.letterText}>{letter}</Text>
                </View>
            </TouchableOpacity>
        );
    }

    _renderListBox(cityJson, rowId) {
        return (
            <TouchableOpacity key={'list_item_' + cityJson.id} style={styles.rowViewBox} onPress={() => {
                that._cityNameClick(cityJson)
            }}>
                <View style={styles.rowdataBox}>
                    <Text style={styles.rowDataTextBox}>{cityJson.name}</Text>
                </View>
            </TouchableOpacity>
        );
    }

    _renderListRow(cityJson, rowId) {
        console.log('rowId===>' + rowId + ", cityJson====>" + JSON.stringify(cityJson));
        if (rowId === key_now || rowId === key_hot || rowId === key_last_visit) {
            return that._renderListBox(cityJson, rowId);
        }

        return (
            <TouchableOpacity key={'list_item_' + cityJson.id} style={styles.rowView} onPress={() => {
                that._cityNameClick(cityJson)
            }}>
                <View style={styles.rowdata}>
                    <Text style={styles.rowdatatext}>{cityJson.name}</Text>
                </View>
            </TouchableOpacity>
        )
    }

    _renderListSectionHeader(sectionData, sectionID) {
        return (
            <View style={styles.sectionView}>
                <Text style={styles.sectionText}>
                    {sectionData}
                </Text>
            </View>
        );
    }

    render() {
        return (
            <View style={styles.container}>
                <View style={styles.listContainner}>
                    <ListView ref={listView => this._listView = listView}
                              contentContainerStyle={styles.contentContainer} dataSource={this.state.dataSource}
                              renderRow={this._renderListRow} renderSectionHeader={this._renderListSectionHeader}
                              enableEmptySections={true} initialListSize={500}/>
                    <View style={styles.letters}>
                        {this.state.letters.map((letter, index) => this._renderRightLetters(letter, index))}
                    </View>
                </View>
                <Toast ref="toast" position='top' positionValue={200} fadeInDuration={750} fadeOutDuration={1000}
                       opacity={0.8}/>
            </View>
        )
    }
}

const styles = StyleSheet.create({
    container: {
        // paddingTop: 50,
        flex: 1,
        flexDirection: 'column',
        backgroundColor: '#F4F4F4',
    },
    listContainner: {
        height: Dimensions.get('window').height,
        marginBottom: 10
    },
    contentContainer: {
        flexDirection: 'row',
        width: width,
        backgroundColor: 'white',
        justifyContent: 'flex-start',
        flexWrap: 'wrap'
    },
    letters: {
        position: 'absolute',
        height: height,
        top: 0,
        bottom: 0,
        right: 10,
        backgroundColor: 'transparent',
        // justifyContent: 'flex-start',
        // alignItems: 'flex-start'
        alignItems: 'center',
        justifyContent: 'center'
    },
    letter: {
        height: height * 4 / 100,
        width: width * 4 / 50,
        justifyContent: 'center',
        alignItems: 'center'
    },
    letterText: {
        textAlign: 'center',
        fontSize: height * 1.1 / 50,
        color: '#e75404'
    },
    sectionView: {
        paddingTop: 5,
        paddingBottom: 5,
        height: 30,
        paddingLeft: 10,
        width: width,
        backgroundColor: '#F4F4F4'
    },
    sectionText: {
        color: '#e75404',
        fontWeight: 'bold'
    },
    rowView: {
        height: ROWHEIGHT,
        paddingLeft: 10,
        paddingRight: 10,
        borderBottomColor: '#F4F4F4',
        borderBottomWidth: 0.5
    },
    rowdata: {
        paddingTop: 10,
        paddingBottom: 2
    },

    rowdatatext: {
        color: 'gray',
        width: width
    },

    rowViewBox: {
        height: ROWHEIGHT_BOX,
        width: (width - 30) / 3,
        flexDirection: 'row',
        backgroundColor: '#ffffff'
    },
    rowdataBox: {
        borderWidth: 1,
        borderColor: '#DBDBDB',
        marginTop: 5,
        marginBottom: 5,
        paddingBottom: 2,
        marginLeft: 10,
        marginRight: 10,
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center'
    },
    rowDataTextBox: {
        marginTop: 5,
        flex: 1,
        height: 20
    }

});

然后在头部还需要实现一个搜索框。 SearchBox.js

'use strict';
import React, {Component} from 'react';
import {
    View,
    TextInput,
    StyleSheet,
    Platform,
} from 'react-native';

export default class SearchBox extends Component {
    constructor(props) {
        super(props);
        this.state = {
            value: ''
        };

    }

    onEndEditingKeyword(vv) {
        console.log(vv);
    }

    onChanegeTextKeyword(vv) {
        console.log('onChanegeTextKeyword', vv);

        this.setState({value: vv});
        this.props.onChanegeTextKeyword(vv);
    }

    render() {
        return (
            <View style={styles.container}>
                <View style={styles.inputBox}>
                    <View style={styles.inputIcon}>
                    </View>
                    <TextInput ref="keyword" autoCapitalize="none" value={this.props.keyword}
                               onChangeText={this.onChanegeTextKeyword.bind(this)} returnKeyType="search" maxLength={20}
                               style={styles.inputText} underlineColorAndroid="transparent"
                               placeholder={'输入城市名或拼音查询'}/>
                </View>
            </View>
        )
    }
}

const styles = StyleSheet.create({
    container: {
        marginTop: 5,
        marginBottom: 5,
        backgroundColor: '#ffffff',
        flexDirection: 'row',
        height: Platform.OS === 'ios'
            ? 35
            : 45,
        borderBottomWidth: StyleSheet.hairlineWidth,
        borderBottomColor: '#cdcdcd',
        paddingBottom: 5
    },
    inputBox: {
        height: Platform.OS === 'ios'
            ? 30
            : 40,
        marginLeft: 5,
        marginRight: 5,
        flex: 1,
        flexDirection: 'row',
        backgroundColor: '#E6E7E8'
    },
    inputIcon: {
        margin: Platform.OS === 'ios'
            ? 5
            : 10
    },
    inputText: {
        alignSelf: 'flex-end',
        marginTop: Platform.OS === 'ios'
            ? 0
            : 0,
        flex: 1,
        height: Platform.OS === 'ios'
            ? 30
            : 40,
        marginLeft: 2,
        marginRight: 5,
        fontSize: 12,
        lineHeight: 30,
        textAlignVertical: 'bottom',
        textDecorationLine: 'none'
    }
});

最终效果:

最后是界面的绘制,这里就不多说了,大家可以下载源码自行查看。源码地址:http://download.csdn.net/detail/xiangzhihong8/9905924

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏游戏杂谈

CURLcode的定义

http://curl.haxx.se/libcurl/c/libcurl-errors.html

2652
来自专栏菩提树下的杨过

Silverlight Telerik控件学习:TreeView数据绑定并初始化选中状态、PanelBar的Accordion效果、TabPanel、Frame基本使用

实际开发中控件的数据源肯定是动态绑定的,不可能在xaml里写死item项。既然要绑定,就先来几个实体类: ? 上面是类图,各类的代码如下:  Business...

2958
来自专栏c#开发者

在DataGrid中选择,确认,删除多行复选框列表

在DataGrid中选择,确认,删除多行复选框列表 Selecting, Confirming & Deleting Mul...

3747
来自专栏linux驱动个人学习

高通msm8909耳机调试

1、DTS相应修改: DTS相关代码:kernel/arch/arm/boot/dts/qcom/msm8909-qrd-skuc.dtsi: 1 s...

9735
来自专栏从零开始的linux

linux基础命令6

tr命令 # tr 'a-z' 'A-Z' < /etc/fstab # # /ETC/FSTAB # CREATED BY ANACONDA ON THU ...

2837
来自专栏MelonTeam专栏

Bitmap 源码阅读笔记

导语: Android 系统上的图片的处理,跟Bitmap 这个类脱不了关系,我们有必要去深入阅读里面的源码,以便在工作中能更好的处理Bitmap相关的问题...

2918
来自专栏肖蕾的博客

Java代码动态修改 ConstraintLayout 内控件布局的辅助类

1113
来自专栏SAP最佳业务实践

SAP S/4 HANA新变化-MM-IM物料帐:物料评估

Material Ledger Obligatory for Material Valuation 物料帐强制启用 Description This simpl...

4646
来自专栏MasiMaro 的技术博文

遍历系统中加载的驱动程序以及通过设备对象指针获取设备对象名称

遍历系统中加载的驱动可以在R3层完成,通过几个未导出的函数:ZwOpenDirectoryObject、ZwQueryDirectoryObject,下面是具体...

1162
来自专栏lonelydawn的前端猿区

前端验证码绘制(canvas)

一切尽在代码中 js文件 /** * canvas绘制动画浮动验证码 * code by lonelydawn 2017-04-10 */ var c...

3187

扫码关注云+社区