(一)react native 城市选择功能实现----FlatList

引言

如题,用Flatlist制作一个简单的城市选择功能

效果

1,当前定位城市
2,热门城市
3,搜索及取消
4.点击移动到具体位置
实现的效果图如下:
在这里插入图片描述

实现思路

1.准备城市文件
在这里插入图片描述

2.初始化数据

componentWillMount() {
		//初始化数据
		let cityContent2 = cityContent.data;
		let letterList = [];
		let cityArray = [];
		let sections = [];
		this.city2number = 0;
		this.descNumber = 0;
		this.titleCityArray = [0];
		//设置不同的type 在 FlatList 中的 renderItem 中用于区分,实现不同的样式
		// push定位城市
		sections.push({
			name: '定位城市',
			type: 'desc',
		}, {
			name: '北京',
			type: 'location',
		}
		)
		// push常用城市
		sections.push({
			name: '常用城市',
			type: 'desc',
		}, {
			name: '珠海,广州',
			type: 'city2',
		}
		)
		// push热门城市
		sections.push({
			name: '热门城市',
			type: 'desc',
		}, {
			name: '珠海,广州,杭州',
			type: 'city2',
		}
		)
		sections.push({
			name: '上海,西安,北京',
			type: 'city2',
		}
		)
		letterList.splice(0, 0, '定位', '常用', '热门');
		this.letterDescNumber = letterList.length;
		sections.forEach(element => {
			if (element.type != 'desc') {
				this.city2number++;
			} else {
				this.descNumber++;
			}
		});
		let i = 0;
		cityContent2.forEach(element => {
			sections[sections.length] = {
				name: element.title,
				type: 'letter',
			};
			element.city.forEach(element => {
				if (element.city_child == element.city_parent) {
					sections[sections.length] = {
						name: element.city_child,
						type: 'city',
					}
					i++;
				}
			});
			this.titleCityArray[this.titleCityArray.length] = i;
			letterList[letterList.length] = element.title;
		});

		// 查找时使用的数据
		let key = 0;
		cityArray = [];
		cityContent2.forEach(element => {
			element.city.forEach(element => {
				if (element.city_child == element.city_parent) {
					cityArray[cityArray.length] = {
						'name': element.city_child,
						'name_en': element.city_child_en,
						'key': key++,
					}
				}
			});
		});
		this.setState({
			sections: sections,
			listData: letterList,
			cityBase: cityArray,
			searchArray: cityArray,
		});
	}

3.全部代码

import React, { Component } from 'react';
import {
	StyleSheet,
	View,
	Text,
	TextInput,
	TouchableOpacity,
	FlatList,
	Image,
	SafeAreaView,
	ScrollView,
	RefreshControl,
	AsyncStorage
} from 'react-native';
import Utils from '../../components/Utils'
import { images } from '../../image/index'
import cityContent from '../../assets/city.json'
import { observer, inject } from "mobx-react"
const CITYHEIGHT = 30;
const TITLEHEIGHT = 40;
const DESCHEIGHT = 25;
const CITYHEIGHT2 = 30;
//mobx管理器
@inject('UpDate')
@observer
class CityChoose extends Component {
	constructor(props) {
		super(props);
		this.UpDate = this.props.UpDate
		this.state = {
			sections: [],
			cityArray: [],
			cityBase: [],
			showFlag: true,
			searchArray: [],
			searchText: '',
			chosedCity: '',
		}
		// 特殊栏目的数量 (定位,热门,常用)
		descNumber = 0;
		// 和上面对应的item 的数量。三个的总数
		city2number = 0;
		// 每个栏目前面城市的数量
		titleCityArray = [0],
			// 右侧导航中 特殊栏目的数量。
			this.letterDescNumber = 0;
	}
	static navigationOptions = {
		headerShown: false
	}
	//查找
	onChangeText = (text) => {
		this.setState({
			searchText: text
		})
		let Chinese = new RegExp('^[\u4e00-\u9fa5]+$');
		let English = new RegExp('^[a-zA-Z]+$');
		if (Chinese.test(text)) {
			let temp = [];
			this.state.cityBase.forEach(city => {
				if (city.name.indexOf(text) == 0) {
					// temp[temp.length] = city;
					temp.push(city)
				}
			});
			this.setState({
				searchArray: temp,
				showFlag: false,
			})
		} else if (English.test(text)) {
			text = text.toLowerCase();
			let temp = [];
			this.state.cityBase.forEach(city => {
				if (city.name_en.indexOf(text) == 0) {
					temp.push(city)
				}
			});
			this.setState({
				searchArray: temp,
				showFlag: false,
			})
		} else if (text.length == 0) {
			this.setState({
				searchArray: this.state.cityBase,
				showFlag: true
			});
		} else {
			this.setState({
				searchArray: [],
				showFlag: false
			});
		}
	}
	
	renderItem = (info) => {
		var txt = info.item.name;
		switch (info.item.type) {
			case 'city': {
				return (
					<TouchableOpacity
						onPress={() => {
						//保存点击的城市到mobx里
							this.UpDate.UpCityInfo(txt)
				this.props.navigation.navigate('Home')
						}}>
						<Text
							style={{
								height: scaleSizeH(30),
								width: Utils.size.width - 70,
								textAlignVertical: 'center',
								paddingLeft: scaleSizeH(5),
								color: '#5C5C5C',
								fontSize: scaleSizeH(15),
								borderBottomColor: 'rgb(161,161,161)',
								borderBottomWidth: scaleSizeH(1),
								// backgroundColor: "#0f0",
							}}
						>
							{txt}
						</Text>
					</TouchableOpacity>
				)
			}
			case 'letter': {
				return (
					<Text
						style={{
							height: scaleSizeH(40),
							width: '100%',
							textAlignVertical: 'center',
							//backgroundColor: "#0f0",
							paddingLeft: scaleSizeH(5),
							color: '#5C5C5C',
							fontSize: 15,
							borderBottomColor: 'rgb(161,161,161)',
							borderBottomWidth: 1,
						}}
					>
						{txt}
					</Text>
				)
			}
			case 'desc': {
				return (
					<Text
						style={{
							height: scaleSizeH(25),
							width: '100%',
							textAlignVertical: 'center',
							// backgroundColor: "#0f0",
							color: '#5C5C5C',
							fontSize: setSpText(15),
						}}
					>
						{txt}
					</Text>
				)
			}
			case 'city2': {
				txt = txt.split(',');
				return (
					<View style={{
						flexDirection: 'row',
					}}>
						{
							txt.map((element, index) => {
								return (
									<TouchableOpacity
										onPress={() => {
											
//保存点击的城市到mobx里											this.UpDate.UpCityInfo(element)										this.props.navigation.navigate('Home')
										
										}}>
										<Text
											key={'info' + info.index + 'index' + index}
											style={{
												width: scaleSizeH(94),
												height: scaleSizeH(30),
												fontSize: setSpText(15),
												lineHeight: scaleSizeH(30),
												textAlign: 'center',
												alignItems: 'center',
												backgroundColor: '#F4F4F4',
												borderWidth: Utils.pixel,
												borderColor: 'rgba(220,220,220,0.5)',
												marginRight: scaleSizeH(14),
												marginBottom: scaleSizeH(10)
											}}

										>
											{element}
										</Text>
									</TouchableOpacity>
								)
							})
						}
					</View>
				)
			}
			case 'location': {
				return (
					<Text
						style={{
							width: scaleSizeH(94),
							height: scaleSizeH(30),
							fontSize: setSpText(15),
							lineHeight: scaleSizeH(30),
							textAlign: 'center',
							alignItems: 'center',
							backgroundColor: '#F4F4F4',
							borderWidth: Utils.pixel,
							borderColor: 'rgba(220,220,220,0.5)',
							marginRight: scaleSizeH(14),
							marginBottom: scaleSizeH(10)
						}}
						onPress={() => {
							this.setState({
								chosedCity: txt
							})
						}}
					>
						{txt}
					</Text>
				)
			}
		}
	}
	_getItemLayout = (data, index) => {
		if (data[index].type == 'letter' || data[index].type == 'city') {
			let i;
			for (i = index; i > 0; i--) {
				if (data[i].type == 'letter') {
					break;
				}
			}
			let offset = this.state.listData.indexOf(data[i]['name']) - this.letterDescNumber;
			return {
				index,
				offset: CITYHEIGHT * (this.titleCityArray[offset] + index - i) + (offset) * (TITLEHEIGHT)
					+ this.city2number * CITYHEIGHT2 + this.descNumber * (DESCHEIGHT),
				length: i == index ? TITLEHEIGHT : CITYHEIGHT,
			}
		} else {
			let i;
			for (i = index; i > 0; i--) {
				if (data[i].type == 'desc') {
					break;
				}
			}
			let offset = this.state.listData.indexOf(data[i]['name'].slice(0, 2));
			return {
				index,
				offset: CITYHEIGHT2 * index + offset * (DESCHEIGHT - CITYHEIGHT2),
				length: i == index ? DESCHEIGHT : CITYHEIGHT2,
			}
		}

	}


//搜索之后的渲染
	_searchRenderItem = (info) => {
		return (
			<View style={{
				flexDirection: 'row', width: '100%',
				height: CITYHEIGHT, alignItems: 'center'
			}}>
				<TouchableOpacity
					onPress={() => {
						this.UpDate.UpCityInfo(info.item.name)
						this.props.navigation.navigate('Home')
					}}
					style={{
						width: '100%',
						height: CITYHEIGHT,
					}}
				>
					<Text
						style={{
							width: '100%',
							height: CITYHEIGHT,
							fontSize: setSpText(15)
						}}
					>{info.item.name}</Text>
				</TouchableOpacity>
			</View>
		)
	}

	_flatRenderItem = (info) => {
		return (
			<TouchableOpacity onPress={() => this._moveAction(info)} style={{ width: CITYHEIGHT, alignItems: 'center', height: 20 }}>
				<Text>{info.item}</Text>
			</TouchableOpacity>
		)
	}

	_moveAction = (info) => {
		let item = info.item.length == 1 ? info.item : info.item + '城市';
		for (let i = 0; i < this.state.sections.length; i++) {
			if (this.state.sections[i].name == item) {
				this.refs.FlatList.scrollToIndex({ animated: false, index: i });
				break;
			}
		}
	}


	componentWillMount() {
		//初始化数据
		let cityContent2 = cityContent.data;
		let letterList = [];
		let cityArray = [];
		let sections = [];
		this.city2number = 0;
		this.descNumber = 0;
		this.titleCityArray = [0];
		//设置不同的type 在 FlatList 中的 renderItem 中用于区分,实现不同的样式
		// push定位城市
		sections.push({
			name: '定位城市',
			type: 'desc',
		}, {
			name: '北京',
			type: 'location',
		}
		)
		// push常用城市
		sections.push({
			name: '常用城市',
			type: 'desc',
		}, {
			name: '珠海,广州',
			type: 'city2',
		}
		)
		// push热门城市
		sections.push({
			name: '热门城市',
			type: 'desc',
		}, {
			name: '珠海,广州,杭州',
			type: 'city2',
		}
		)
		sections.push({
			name: '上海,西安,北京',
			type: 'city2',
		}
		)
		letterList.splice(0, 0, '定位', '常用', '热门');
		this.letterDescNumber = letterList.length;
		sections.forEach(element => {
			if (element.type != 'desc') {
				this.city2number++;
			} else {
				this.descNumber++;
			}
		});
		let i = 0;
		cityContent2.forEach(element => {
			sections[sections.length] = {
				name: element.title,
				type: 'letter',
			};
			element.city.forEach(element => {
				if (element.city_child == element.city_parent) {
					sections[sections.length] = {
						name: element.city_child,
						type: 'city',
					}
					i++;
				}
			});
			this.titleCityArray[this.titleCityArray.length] = i;
			letterList[letterList.length] = element.title;
		});

		// 查找时使用的数据
		let key = 0;
		cityArray = [];
		cityContent2.forEach(element => {
			element.city.forEach(element => {
				if (element.city_child == element.city_parent) {
					cityArray[cityArray.length] = {
						'name': element.city_child,
						'name_en': element.city_child_en,
						'key': key++,
					}
				}
			});
		});
		this.setState({
			sections: sections,
			listData: letterList,
			cityBase: cityArray,
			searchArray: cityArray,
		});
	}
	componentDidMount() {

	}
	render() {
		return (
			<SafeAreaView style={{ flex: 1, backgroundColor: '#FFFFFF' }} >
				<View
					style={{
						flex: 1,
						alignItems: 'center',
						// backgroundColor: '#FFFFFF',
						// backgroundColor: 'pink',
						paddingLeft: scaleSizeH(15),
						paddingRight: scaleSizeH(15)
					}}
				>
					<View style={{
						width: '100%',
						height: scaleSizeH(90),
						flexDirection: 'row',
						alignItems: 'center',
						justifyContent: 'space-between',
						// backgroundColor: 'green'
					}}>
						<TouchableOpacity onPress={() => {
							// this.props.navigation.navigate('Home')
							this.props.navigation.goBack()
						}}
							style={{
								width: scaleSizeH(20),
								height: scaleSizeH(20),
							}}>
							<Image source={images.back_black} style={{
								width: scaleSizeH(10),
								height: scaleSizeH(20),
							}} />
						</TouchableOpacity>
						<TextInput
							style={{
								width: scaleSizeH(261),
								height: scaleSizeH(36),
								borderRadius: scaleSizeH(18),
								backgroundColor: '#F4F4F4',
								paddingLeft: scaleSizeH(15)
							}}
							value={this.state.searchText}
							// 	onChangeText={(text) => {
							// 		this.setState({ username: text })
							// 	}}
							onChangeText={(text) => this.onChangeText(text)}
							underlineColorAndroid='transparent'
							placeholder={'输入城市名字或首字母查询'}
						/>
						<TouchableOpacity
							onPress={() => {
								this.setState({
									searchText: '',
									searchArray: this.state.cityBase,
									showFlag: true
								})
							}}
						>
							<Text
								style={{
									fontSize: setSpText(16),
									color: '#333333',
								}}>cancel</Text>
						</TouchableOpacity>
					</View>
					{this.state.showFlag == true ? (
						<View
							style={{
								width: '100%'
							}}
						>
							<FlatList
								ref={'FlatList'}
								style={{
									width: '100%',
								}}
								data={this.state.sections}
								showsVerticalScrollIndicator={false}
								renderItem={this.renderItem}
								initialNumToRender={50}
								getItemLayout={(data, index) => this._getItemLayout(data, index)}
								keyExtractor={(item, index) => {
									return (index.toString());
								}}
							>
							</FlatList>
							<View
								style={{
									position: 'absolute',
									height: Utils.size.height - 93 + Utils.statusBarHeight,
									alignItems: 'center',
									justifyContent: 'center',
									right: 0,
								}}
							>
								<View style={{ height: 25 * 20 }}>
									<FlatList
										initialNumToRender={25}
										data={this.state.listData}
										renderItem={this._flatRenderItem}
										keyExtractor={(item, index) => { return item + index }}
									>
									</FlatList>
								</View>
							</View>
						</View>
					) : (
							<FlatList
								style={{
									width: '100%',

								}}
								data={this.state.searchArray}
								renderItem={this._searchRenderItem}
								keyExtractor={(item, index) => {
									return (index.toString());
								}}
							/>
						)}
				</View>

			</SafeAreaView>
		)
	}
}
export default CityChoose;

功能基本实现,待完善

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值