ReactNative 自定义机场选择列表 AirportListView
来源:互联网 发布:断点续传java 编辑:程序博客网 时间:2024/04/30 19:21
自定义机场选择列表 AirportListView
一、内容简介
对于拥有国外市场或者通讯功能的APP来说,通过列表展示选择自己需要的数据是很常见的功能模块,例如:通讯录,选择国家等等。这类组件的实现原理基本一致,本篇文章带大家实现一个机场的列表选择模块。
目前只实现了最基础的首字母定位查找功能,首字母分组功能,后期会加上模糊查询,热门城市推荐。
技术点总结
- ReactNative 原生ListView组件
- 查询并存储所有选项的首字母,并且去重
- 将城市名称按照首字母分组
- 记录每个分组起始的高度
- 异步读取本地数据
二、代码实现
1、数据源数据结构
数据片段
{ "allAirportList":[ { "airport": "伊尔施", "city": "阿尔山", "enAirport": "", "match": "阿尔山 伊尔施|aershanyiershi|YIE", "pinyin": "aershanyiershi", "tcode": "YIE", "sortLetters": "a" }, { "airport": "阿克苏", "city": "阿克苏", "enAirport": "", "match": "阿克苏 阿克苏|akesu|AKU", "pinyin": "akesu", "tcode": "AKU", "sortLetters": "a" }, { "airport": "阿勒泰", "city": "阿勒泰", "enAirport": "", "match": "阿勒泰 阿勒泰|aletai|AAT", "pinyin": "aletai", "tcode": "AAT", "sortLetters": "a" }, { "airport": "昆莎", "city": "阿里", "enAirport": "", "match": "阿里 昆莎|alikunsha|NGQ", "pinyin": "alikunsha", "tcode": "NGQ", "sortLetters": "a" }, ]}
2、实现ListView基础功能
- 创建AirportListView类
- 初始化ListView,这里用到了ListView的Section功能,详情可查看官网Demo
- 定义状态机
import React, {Component,PropTypes}from 'react';import { StyleSheet, View, Text, Platform, TouchableOpacity, ListView, Dimensions,} from 'react-native';export default class AirportListView extends Component { constructor(props) { super(props); this.totalHeight = []; // 存放每个分组的起始高度 var getSectionData = (dataBlob, sectionID) => { return sectionID; }; var getRowData = (dataBlob, sectionID, rowID) => { return dataBlob[sectionID][rowID]; }; let ds = new ListView.DataSource({ getRowData: getRowData, getSectionHeaderData: getSectionData, rowHasChanged: (row1, row2) => row1 !== row2, sectionHeaderHasChanged: (s1, s2) => s1 !== s2, }); this.state = { citiesData: ds.cloneWithRowsAndSections([]) ,letters: [] }; }}
3、数据处理
- 获取源数据
- 分拣出涉及到的首字母
- 将城市数据按首字母索引分组
- 计算每个分组的高度并存储
- 异步处理数据
(1) 获取数据源
数据源由调用该组件的页面传递
let data = this.props.dataSource;
(2) 获取字母索引数组
let letterList = this._getSortLetters(data);
- for循环的大意:拿到数据列表中每个对象的sortLetter字段,首先小写转化成大写,其次遍历字母索引数组,遍历过程中拿前一步获取到字段进行比较,如果有相等的项,说明此字母已出现过,不必做存储,中断循环,跳入下一次的遍历,如果不存在相等项,则将该字母存入索引数组,最后返回字母索引数组
_getSortLetters(dataList) { let list = []; // 存放数据数组 for (let i = 0; i < dataList.length; i++) { let sortLetters = dataList[i].sortLetters.toUpperCase(); let exist = false; for (let j = 0; j < list.length; j++) { if (list[j] === sortLetters) { exist = true; } if (exist) { break; } } if (!exist) { list.push(sortLetters); } } return list; }
(3)将城市数据按首字母索引分组
- dataBlob中存放的是按照首字母分好组的城市数据
let dataBlob = {}; data.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; } });
(4) 计算每个分组的高度并存储
- 通过Object.keys(dataBlog)获取到所有的key值,也就是所有分组的header
- 提炼出每个组所包含的子元素总数
- 计算出每个section的总高度:header高度 + row高度 * row每个组的总个数
let sectionIDs = Object.keys(dataBlob); let rowIDs = sectionIDs.map(sectionID => { let thisRow = []; let count = dataBlob[sectionID].length; for (let i = 0; i < count; i++) { thisRow.push(i); } let eachheight = SECTION_HEIGHT + ROW_HEIGHT * thisRow.length; this.totalHeight.push(eachheight); return thisRow; });
(5) 添加异步功能
groupingData() { return new Promise((resolve, reject) => { let data = this.props.dataSource; let letterList = this._getSortLetters(data); let dataBlob = {}; // 将城市按字母索引分组 data.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 i = 0; i < count; i++) { thisRow.push(i); } let eachheight = SECTION_HEIGHT + ROW_HEIGHT * thisRow.length; this.totalHeight.push(eachheight); return thisRow; }); let result = { dataBlob: dataBlob ,sectionIDs: sectionIDs ,rowIDs: rowIDs } resolve(result); }); }
4、布局显示
- 主布局
- 字母索引布局
- ListView定位方法
- 点击响应方法
主布局
render() { return ( <View style={{flexDirection: 'column',backgroundColor: '#F4F4F4'}}> <View style={{height: Dimensions.get('window').height,marginBottom: 10}}> <ListView ref={listView => this._listView = listView} contentContainerStyle={{flexDirection: 'row',width: width,backgroundColor: 'white',justifyContent: 'space-around',flexWrap: 'wrap',}} dataSource={this.state.citiesData} renderRow={this._renderListRow} renderSectionHeader={this._renderListSectionHeader} enableEmptySections={true} initialListSize={1000} /> <View style={styles.letters}> {this.renderLetters()} </View> </View> </View> ) }
sectionheader布局
_renderListSectionHeader(sectionData, sectionID) { return ( <View style={{paddingTop: 5,paddingBottom: 5,height: 30,paddingLeft: 10,width: width,backgroundColor: '#F4F4F4',}}> <Text style={{color: '#333333',fontWeight: 'bold'}}> {sectionData} </Text> </View> ); }
row布局
- 构造方法中获取this
constructor(props) { super(props); that = this;}
_renderListRow(cityJson, rowId) { return ( <TouchableOpacity key={'list_item_' + cityJson.tcode} style={{height: ROW_HEIGHT,paddingLeft: 10,paddingRight: 10,borderBottomColor: '#F4F4F4',borderBottomWidth: 1,}} onPress={()=> { that._cityNameClick(cityJson) }}> <View style={{ paddingTop: 10, paddingBottom: 2 }}> <Text style={{color: '#333333',width: width}}>{cityJson.city} ({cityJson.tcode})</Text> </View> </TouchableOpacity> ) }
字母索引布局
- 这里用到了第三方数据操作框架underscorejs
renderLetters() { if(!_.isEmpty(this.state.letters)) { return( <View> {this.state.letters.map((letter, index) => this._renderRightLetters(letter, index))} </View> ) } }
索引定位
// 根据字母索引定位城市列表 _scrollTo(index, letter) { let position = 0; for (let i = 0; i < index; i++) { position += this.totalHeight[i] } this._listView.scrollTo({ y: position }); }
点击响应方法callback
- 类中定义回调函数类型
static propTypes = { onConfirm: PropTypes.func.isRequired, };// 选择城市后的callback_cityNameClick(cityJson) { this.props.onConfirm(cityJson);}
样式
const styles = StyleSheet.create({ letters: { position: 'absolute', height: height, top: 0, bottom: 0, right: 10, backgroundColor: 'transparent', justifyContent: 'flex-start', alignItems: 'flex-start', }, letter: { height: height * 3.3 / 100, width: width * 3 / 50+10, justifyContent: 'center', alignItems: 'center', }});
- 注:核心思路来源于简书上的一篇文章,在此基础之上进行封装改造
- 文章链接:http://www.jianshu.com/p/cec3bf7ed7b1
阅读全文
0 0
- ReactNative 自定义机场选择列表 AirportListView
- ReactNative列表ListView
- ReactNative 图片列表组件
- 自定义View----点击滑动选择字母列表
- android 使用activity自定义下拉选择列表
- Android 自定义View实现城市选择列表
- Android 自定义View,圆形随机选择列表
- ReactNative自定义控件。
- [ReactNative]自定义alert对话框
- ReactNative 自定义导航栏
- 微信小程序----全国机场索引列表(MUI索引列表)
- ReactNative自定义控件之 RefreshLayout
- Android-局部刷新ListView数据信息的自定义选择列表
- 自定义列表选择Dialog,适用网络请求数据
- 自定义列表编辑状态左侧选择按钮图片
- 【后台】菜单项选择列表增加自定义类型
- 列表选择
- ReactNative ListView改变数据源,列表不刷新
- 在使用android:layout_weight属性时,控件不能对齐的解决方案
- Activity状态转换和Activity栈
- csdn博客被封,解决方法
- MyBatis学习笔记(一)
- liux笔记本合盖继续运行
- ReactNative 自定义机场选择列表 AirportListView
- SteamVR Unity Tool Kit(二):激光与移动
- MySQL中关于JSON你不得不知道的那些事!
- Python语法备忘-常用函数
- glances – 系统监控工具
- 数组排序方法的性能比较(上):注意事项及试验
- word批量删除中英文间的多个空格
- RobotFramework自动化测试框架系统关键字之断言
- 使用UIActivityViewController分享时,限制分享