城市列表选择是很多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'
}
});
最终效果:
最后是界面的绘制,这里就不多说了,大家可以下载源码自行查看。源码地址:react-native-city_jb51.rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# react
# native城市列表
# native
# 带索引城市列表
# 原生+React实现懒加载(无限滚动)列表方式
# react无限滚动组件的实现示例
# React实现数字滚动组件numbers-scroll的示例详解
# React列表栏及购物车组件使用详解
# React Native中ScrollView组件轮播图与ListView渲染列表组件用法实例分析
# react实现列表滚动组件功能
# 自定义
# 说了
# 不多
# 还需要
# 美图
# 绑定
# 可以下载
# 而要
# 大家多多
# 选择了
# ii
# eachheight
# lt
# CURRENT_CITY_LIST
# length
# getRowData
# rowID
# log
# ds
# sectionIDs
相关文章:
山东网站制作公司有哪些,山东大源集团官网?
如何快速搭建高效香港服务器网站?
郑州企业网站制作公司,郑州招聘网站有哪些?
如何获取免费开源的自助建站系统源码?
如何在Golang中使用replace替换模块_指定本地或远程路径
如何在万网开始建站?分步指南解析
常州自助建站工具推荐:低成本搭建与模板选择技巧
网页制作模板网站推荐,网页设计海报之类的素材哪里好?
在线ppt制作网站有哪些,请推荐几个好的课件下载的网站?
已有域名和空间,如何快速搭建网站?
威客平台建站流程解析:高效搭建教程与设计优化方案
如何在腾讯云服务器上快速搭建个人网站?
小米网站链接制作教程,请问miui新增网页链接调用服务有什么用啊?
建站一年半SEO优化实战指南:核心词挖掘与长尾流量提升策略
如何通过虚拟主机快速搭建个人网站?
义乌企业网站制作公司,请问义乌比较好的批发小商品的网站是什么?
深圳网站制作费用多少钱,读秀,深圳文献港这样的网站很多只提供网上试读,但有些人只要提供试读的文章就能全篇下载,这个是怎么弄的?
建站主机选哪种环境更利于SEO优化?
北京营销型网站制作公司,可以用python做一个营销推广网站吗?
网站设计制作公司地址,网站建设比较好的公司都有哪些?
建站10G流量真的够用吗?如何应对访问高峰?
小捣蛋自助建站系统:数据分析与安全设置双核驱动网站优化
如何配置支付宝与微信支付功能?
如何在万网主机上快速搭建网站?
XML的“混合内容”是什么 怎么用DTD或XSD定义
如何在云主机上快速搭建多站点网站?
c++怎么实现高并发下的无锁队列_c++ std::atomic原子变量与CAS操作【详解】
如何快速搭建高效服务器建站系统?
c# Task.ConfigureAwait(true) 在什么场景下是必须的
深入理解Android中的xmlns:tools属性
海南网站制作公司有哪些,海口网是哪家的?
,制作一个手机app网站要多少钱?
电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?
seo网站制作优化,网站SEO优化步骤有哪些?
c# 在高并发场景下,委托和接口调用的性能对比
图片制作网站免费软件,有没有免费的网站或软件可以将图片批量转为A4大小的pdf?
免费的流程图制作网站有哪些,2025年教师初级职称申报网上流程?
如何挑选优质建站一级代理提升网站排名?
建站之星备案流程有哪些注意事项?
上海制作企业网站有哪些,上海有哪些网站可以让企业免费发布招聘信息?
C++ static_cast和dynamic_cast区别_C++静态转换与动态类型安全转换
惠州网站建设制作推广,惠州市华视达文化传媒有限公司怎么样?
如何在IIS中新建站点并配置端口与IP地址?
如何快速上传自定义模板至建站之星?
建站之星2.7模板快速切换与批量管理功能操作指南
建站之星安装提示数据库无法连接如何解决?
建站之星北京办公室:智能建站系统与小程序生成方案解析
如何高效生成建站之星成品网站源码?
PHP正则匹配日期和时间(时间戳转换)的实例代码
*请认真填写需求信息,我们会在24小时内与您取得联系。