react natve 使用 ScrollView 完成城市列表

最近项目中需要用到城市列表这一功能,试过了 SectionList、FlatList、以及 ScrollView,对比之下发现 ScrollView 最简单,其余两者因为数据太多,不会一次性全部渲染出来

 

使用 ScrollView 的 scrollTo() 方法,即可实现;

 

滚动的距离 = 城市的高度和 + 字母的高度和 + header的高度;

滚动这是滚动 ScrollView 的距离

城市 city.json 文件

{ 
    "city":[	 
	 {
	     "title":"A",
	     "data":[
	 		"阿坝","阿拉善","阿里","安康","安庆","鞍山","安顺","安阳","澳门"
	 	    ]
	 },
	 {
	     "title":"B",
	     "data":["北京","白银","保定","宝鸡","保山","包头","巴中","北海","蚌埠","本溪","毕节","滨州","百色","亳州"
	 	    ]
	 },
	 {
	     "title":"C",
	     "data":["重庆","成都","长沙","长春","沧州","常德","昌都","长治","常州","巢湖","潮州","承德", "郴州","赤峰","池州","崇左","楚雄","滁州","朝阳"
	 	    ]
	 },
	 {
	     "title":"D",
	     "data":["大连","东莞","大理","丹东","大庆","大同","大兴安岭","德宏","德阳","德州","定西","迪庆","东营"
	 	    ]
	 },
	 {
	     "title":"E",
	     "data":["鄂尔多斯","恩施","鄂州"]
	 },
	 {
	     "title":"F",
	     "data":["福州","防城港","佛山","抚顺","抚州","阜新","阜阳"
	 	    ]
	 		
	 },
	 {
	    "title":"G",
	     "data":["广州","桂林","贵阳","甘南","赣州","甘孜","广安","广元","贵港","果洛"]
			
	 },
	 {
	    "title":"H",
	     "data":["杭州","哈尔滨","合肥","海口","呼和浩特","海北","海东","海南","海西","邯郸","汉中","鹤壁","河池","鹤岗","黑河","衡水","衡阳","河源","贺州","红河","淮安","淮北","怀化","淮南","黄冈","黄南","黄山","黄石","惠州","葫芦岛","呼伦贝尔","湖州","菏泽"
		    ]
	 },
	 {
	    "title":"J",
	     "data":["济南","佳木斯","吉安","江门","焦作","嘉兴","嘉峪关","揭阳","吉林","金昌","晋城","景德镇","荆门","荆州","金华","济宁","晋中","锦州","九江",
            "酒泉"
                    ]
            
	 },
	 {
	    "title":"K",
	     "data":["昆明","开封"]
 
	 },
	 {
	    "title":"L",
	     "data":["兰州","拉萨","来宾","莱芜","廊坊","乐山","凉山","连云港","聊城","辽阳","辽源","丽江","临沧","临汾","临夏","临沂","林芝","丽水","六安","六盘水",
            "柳州","陇南","龙岩","娄底","漯河","洛阳","泸州","吕梁"
                    ]
 
	 },
	 {
 	    "title":"M",
	     "data":["马鞍山","茂名","眉山","梅州","绵阳","牡丹江"]
 			
	 },
	 {
	    "title":"N",
	     "data":["南京","南昌","南宁","宁波","南充","南平","南通","南阳","那曲","内江", "宁德","怒江"
                     ]
	 },
	 {
	    "title":"P",
	     "data":["盘锦","攀枝花","平顶山","平凉","萍乡","莆田","濮阳"]
 
	 },
	 {
	    "title":"Q",
	     "data":["青岛","黔东南","黔南","黔西南","庆阳","清远","秦皇岛","钦州","齐齐哈尔","泉州","曲靖","衢州"]
			
	 },
	 {
	    "title":"R",
	     "data":["日喀则","日照"]
	 },
	 {
	    "title":"S",
	     "data":["上海","深圳","苏州","沈阳","石家庄","三门峡","三明","三亚","商洛","商丘","上饶","山南","汕头","汕尾","韶关","绍兴","邵阳","十堰","朔州","四平","绥化","遂宁","随州","宿迁","宿州"
		    ]
            
	 },
	 {
	    "title":"T",
	     "data":["天津","太原","泰安","泰州","台州","唐山","天水","铁岭","铜川","通化","通辽","铜陵","铜仁","台湾"]
		    
	 },
	 {
		"title":"W",
		"data":["武汉","乌鲁木齐","无锡","威海","潍坊","文山","温州","乌海","芜湖","乌兰察布","武威","梧州"]
	 },
	 {
	    "title":"X",
	     "data":["厦门","西安","西宁","襄樊","湘潭","湘西","咸宁","咸阳","孝感","邢台","新乡","信阳","新余","忻州","西双版纳","宣城","许昌","徐州","香港","锡林郭勒","兴安"
		    ]
            
	 },
	 {
	    "title":"Y",
	     "data":["银川","雅安","延安","延边","盐城","阳江","阳泉","扬州","烟台","宜宾","宜昌","宜春","营口","益阳","永州","岳阳","榆林","运城","云浮","玉树","玉溪","玉林"
		    ]
			
	 },
	 {
	     "title":"Z",
	     "data":["杂多县","赞皇县","枣强县","枣阳市","枣庄","泽库县","增城市","曾都区","泽普县","泽州县","札达县","扎赉特旗","扎兰屯市","扎鲁特旗","扎囊县","张北县","张店区","章贡区","张家港","张家界","张家口","漳平市","漳浦县","章丘市","樟树市","张湾区","彰武县","漳县","张掖","漳州","长子县","湛河区","湛江","站前区","沾益县","诏安县","召陵区","昭平县","肇庆","昭通","赵县","昭阳区","招远市","肇源县","肇州县","柞水县","柘城县","浙江","镇安县","振安区","镇巴县","正安县","正定县","正定新区","正蓝旗","正宁县","蒸湘区","正镶白旗","正阳县","郑州","镇海区","镇江","浈江区","镇康县","镇赉县","镇平县","振兴区","镇雄县","镇原县","志丹县","治多县","芝罘区","枝江市","芷江侗族自治县","织金县","中方县","中江县","钟楼区","中牟县","中宁县","中山","中山区","钟山区","钟山县","中卫","钟祥市","中阳县","中原区","周村区","周口","周宁县","舟曲县","舟山","周至县","庄河市","诸城市","珠海","珠晖区","诸暨市","驻马店","准格尔旗","涿鹿县","卓尼","涿州市","卓资县","珠山区","竹山县","竹溪县","株洲","株洲县","淄博","子长县","淄川区","自贡","秭归县","紫金县","自流井区","资溪县","资兴市","资阳"
		    ]
	 }
    ]
}

字母

const letters = ['A','B','C','D','E','F','G','H','J','K','L','M','N','P','Q','R','S','T','W','X','Y','Z']

 

App.js

import React, {Component} from 'react';
import { StyleSheet, ScrollView, Dimensions } from 'react-native';
import {
  Container,
  Input,
  ListItem,
  Icon,
  H3,
  View,
  Text,
} from 'native-base'
import HeaderNav from './HeaderNav'

import city from '../util/city'
const data = city.city;
const letters = ['A','B','C','D','E','F','G','H','J','K','L','M','N','P','Q','R','S','T','W','X','Y','Z']

const { height } = Dimensions.get('window')
const LETTER_HEIGHT = 40; //字母高度
const ITEM_HEIGHT = 45; //每一个 item 高度

export default class HeaderSearchNav extends Component {
  constructor(props) {
    super(props);
    this.state = {
      currentCity: '',
      historyCity: [],
      search: false,
      searchData: [],
    }
  }
  render() {
    return (
      <Container>
        <HeaderNav 
          leftIcon={'angle-left'}
          leftType={'FontAwesome'}
          title={'选择城市'}
          goBack={()=>this.props.navigation.goBack()}
        />
        <ListItem noIndent style={styles.search}>
          <Icon name={'search'} style={{fontSize:24,color:'#666'}}/>
          <Input placeholder={'请输入城市名或首字母'} 
            placeholderTextColor={'#999'}
            onChangeText={this._search}
            style={styles.input}
          />
        </ListItem>
        
        {
          this.state.search ? 
          <ScrollView showsVerticalScrollIndicator={false}>
            {
              this.state.searchData.map((val,i) => {
                return (
                  <ListItem key={i} style={{height:ITEM_HEIGHT}}>
                    <Text  style={styles.text}>{val}</Text>
                  </ListItem>
                )
              })
            }
          </ScrollView>
          :
          <ScrollView ref={'scroll'} showsVerticalScrollIndicator={false}>
            <ListItem noIndent style={styles.list}>
              <Text style={{fontSize:16,color:'#666'}}>当前城市: </Text>
              <Text style={{fontSize:14,color:'#999'}}>东莞</Text>
            </ListItem>
            {
              this.state.historyCity.length == 0 ? null 
              :
              <ListItem noIndent style={styles.list}>
                <Text style={{fontSize:16,color:'#666'}}>历史城市: </Text>
                {
                  this.state.historyCity.map((val,i)=>{
                    return (
                      <Text key={i} style={{fontSize:14,color:'#999',marginRight:10}}>{val}</Text>
                    )
                  })
                }
              </ListItem>
            }
            
            {
              data.map((val,i) => {
                return (
                  <View key={i}>
                    <ListItem noIndent style={styles.letter}>
                      <H3 >{val.title}</H3>
                    </ListItem>
                    {
                      val.data.map((val,i)=>{
                        return (
                          <ListItem key={i} style={{height:ITEM_HEIGHT}}>
                            <Text  style={styles.text} onPress={()=>this._selectedCity(val)}>{val}</Text>
                          </ListItem>
                        )
                      })
                    }
                  </View>
                )
              })
            }
          </ScrollView>
        }

        {
          this.state.search ? null
          :
          <View style={styles.position}>
            {
              letters.map((val,i)=>{
                return (
                  <Text key={i} style={styles.positionText} onPress={()=>this._scroll(i)}>{val}</Text>
                )
              })
            }
          </View>
        }
        
      </Container>
    );
  }
  // 搜索
  _search = (text) => {
    let searchData = this.state.searchData;
    if(text) {
      this.setState({search: true})
    }else {
      this.setState({search: false})
    }
    data.forEach((val) => {
      if(val.title == text.toUpperCase()) {
        searchData = val.data;
      }
    })
    this.setState({search:text,searchData});
    console.log(searchData)
  }
  // 点击右侧字母,左侧滑动到指定位置
  _scroll(i) {
    let newArr = [];
    data.forEach((val,index)=>{
      if(index < i) {
        val.data.forEach(item=>{
          newArr.push(item)
        })
      }
    })
    let letterHeight = LETTER_HEIGHT * i;
    let itemHeight = ITEM_HEIGHT * newArr.length;
    let topHeight = ITEM_HEIGHT * 2;
    if(this.state.historyCity.length == 0) {
      topHeight = ITEM_HEIGHT;
    }
    this.refs.scroll.scrollTo(
      {
        y: letterHeight + itemHeight + topHeight,
        animated: true,
      }
    )
  }
  // 选择城市
  _selectedCity(val) {
    let historyCity = this.state.historyCity;
    
    if(historyCity.indexOf(val) > -1) {
      return 
    }
    historyCity.unshift(val);
    if(historyCity.length > 3) {
      historyCity = historyCity.slice(0,3)
    }
    this.setState({historyCity})
  }
}

const styles = StyleSheet.create({
  search: {
    height: ITEM_HEIGHT,
    borderRadius: 10,
    marginTop: 5,
    marginHorizontal: 15,
    backgroundColor: '#eee',
    borderBottomColor: 'transparent',
  },
  input: {
    fontSize: 14,
    color: '#999'
  },
  list: {
    borderBottomColor: 'transparent',
    height: ITEM_HEIGHT,
  },
  letter: {
    height: LETTER_HEIGHT,
    backgroundColor: '#eee',
    borderBottomColor: 'transparent',
  },
  text: {
    paddingVertical: 5,
    color: '#666',
    fontSize: 14,
  },
  position: {
    position: 'absolute',
    top: height * 0.2,
    right: 0,
    width: 30,
    height,
    alignItems: 'center',
    backgroundColor: '#fff'
  },
  positionText: {
    fontSize: 13,
    color: 'rgb(82,174,82)',
    paddingVertical: 3,
  }
});

最终效果:

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值