引言
如题,用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;
功能基本实现,待完善