ReactNative 城市选择 搜索 滑动字母选择

前端之家收集整理的这篇文章主要介绍了ReactNative 城市选择 搜索 滑动字母选择前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

先上效果

遇到的问题

  • 右侧字母选择器 高度问题,
  • 右侧字母选择器 如何使用手势检测panresponse
  • 右侧字母选择器 计算高度如何判断是触摸到那个字母上的(思考 如果是==native应用是如何做的==…刚看过 native应用的城市列表也是通过计算每个字母的高度来检测的)动态创建的控件

右边滑动的原理: 通过onlayout计算每个字母高度

,然后加入数组,手指触摸字母列表时 知道 触摸的y坐标

这样再减去列表距离顶部的距离 就是字母列表的初始坐标.根据

y坐标和刚才通过onlayout计算出的数组进行比对在那个区间则是

哪一个字母的item

下面上代码

  1. /**
  2. * Created by liuml on 2017/10/5.
  3. */
  4.  
  5. import React,{Component} from 'react';
  6. import {
  7. View,Image,TouchableOpacity,Modal,Text,StatusBar,ListView,Platform,Dimensions,StyleSheet,RefreshControl,Alert,TextInput,PanResponder
  8. } from 'react-native';
  9. import _ from 'lodash';
  10. import NavigationBar from './compont/NavigationBar'
  11.  
  12. const {width,height} = Dimensions.get('window')
  13. const SECTIONHEIGHT = 30,ROWHEIGHT = 40
  14.  
  15. //这是利用lodash的range和数组的map画出26个英文字
  16.  
  17.  
  18. var Util = require('./util/util');//工具类
  19. var ScreenUtil = require('./util/ScreenUtil');//工具类
  20. let city = [];//城市的数组 里面放的是对象
  21. var totalheight = [];//每个字母对应的城市和字母的总高度 比如所有a字母中数据的高度
  22. var lettersItemheight = [];//每个字母的y坐标
  23. var myLetters = [];//我的字母数组
  24. var myDataBlob = {};//获取到的数据
  25. var lettersBottom = 10;//字母列表距离底部高度
  26. var mySectionIDs = []; //组id
  27. var myRowIDs = [];//组内
  28. var cityData = [];//获取到的数据
  29. var totalNumber = 10;//总条数的数据
  30. var searchHeight = 35;//搜索框高度
  31. var searchHeightMargin = 2;//搜索框margin
  32. var lettersHeight;//字母列表高度
  33. var that ;
  34. export default class CityList extends Component {
  35.  
  36.  
  37. constructor(props) {
  38. super(props);
  39. // 获取组中数据
  40. var getSectionData = (myDataBlob,mySectionIDs) => {
  41. // console.log("组id mySectionIDs = " + mySectionIDs);
  42. // console.log(`组中数据 = ${myDataBlob[mySectionIDs]}`);
  43. return myDataBlob[mySectionIDs];
  44. };
  45. // 获取行中的数据
  46. var getRowData = (myDataBlob,mySectionIDs,myRowIDs) => {
  47. // console.log(`行中数据 = ${myDataBlob[myRowIDs]}`);
  48. return myDataBlob[myRowIDs];
  49. };
  50. this.state = {
  51. dataSource: new ListView.DataSource({
  52. getSectionHeaderData: getSectionData,getRowData: getRowData,rowHasChanged: (row1,row2) => row1 !== row2,sectionHeaderHasChanged: (s1,s2) => s1 !== s2,}),isLoading: true,letteRSShow: true
  53. }
  54. that = this;
  55. }
  56.  
  57. //加载数据
  58. loadData = () => {
  59.  
  60. // var thiz = this;
  61. Util.post('http://ovji4jgcd.bkt.clouddn.com/Mycity.json',{},(ret) => {
  62. // console.log(ret);
  63. if (ret.resCode == 1 && ret.data.length > 0) {
  64.  
  65. cityData = ret.data;
  66. // console.log(cityData);
  67. totalNumber = ret.totalNumber;
  68. //一系列的操作 遍历数组
  69. console.log("正确的总数据: " + cityData);
  70. this.setData(cityData);
  71. }
  72. });
  73.  
  74.  
  75. }
  76.  
  77. //设置数据
  78. setData = (cityData) => {
  79. for (let i = 0; i < cityData.length; i++) {
  80. var mysectionName = 'Section_' + i;
  81. let cityMode = cityData[i].data;
  82. let zimu = cityData[i].zimu;
  83. mySectionIDs.push(mysectionName)
  84. myRowIDs[i] = [];
  85. var innerLoop = cityData[i].data; //内循环中的城市
  86. myDataBlob[mysectionName] = zimu;//把字母放入总数据
  87. myLetters.push(zimu)//把字母放入用于右边的字母列表
  88. for (let jj = 0; jj < innerLoop.length; jj++) {
  89. let rowName = i + '_' + jj;
  90. myRowIDs[i].push(rowName);
  91. myDataBlob[rowName] = innerLoop[jj];
  92. }
  93. //组的高度 + 上行的高度 * 有多少行
  94. var eachheight = SECTIONHEIGHT + ROWHEIGHT * cityMode.length
  95. totalheight.push(eachheight)
  96. }
  97. let size = myLetters.length;
  98. // console.log("字母数量" + size);
  99. // console.log("lettersHeight = " + lettersHeight);
  100. //关闭对话框 设置数据源
  101. // console.log("打印setstate===================")
  102. this.setState({
  103. dataSource: this.state.dataSource.cloneWithRowsAndSections(myDataBlob,myRowIDs),isLoading: false,letteRSShow: true
  104. })
  105. }
  106. //更新数据
  107. updateData = (cityData) => {
  108. console.log("更新的数据: " + cityData);
  109. let myDataBlob = [],mySectionIDs = [],myRowIDs = [];
  110. for (let i = 0; i < cityData.length; i++) {
  111. let mysectionName = 'Section_' + i;
  112. // let cityMode = cityData[i].data;
  113. let zimu = cityData[i].zimu;
  114. mySectionIDs.push(mysectionName)
  115. myRowIDs[i] = [];
  116. let innerLoop = cityData[i].data; //内循环中的城市
  117. myDataBlob[mysectionName] = zimu;//把字母放入总数据
  118. // myLetters.push(zimu)//把字母放入用于右边的字母列表
  119.  
  120. for (let jj = 0; jj < innerLoop.length; jj++) {
  121. let rowName = i + '_' + jj;
  122. myRowIDs[i].push(rowName);
  123. myDataBlob[rowName] = innerLoop[jj];
  124. }
  125. }
  126.  
  127. // console.log("打印setstate===================")
  128. this.setState({
  129. dataSource: this.state.dataSource.cloneWithRowsAndSections(myDataBlob,letteRSShow: false
  130. })
  131. }
  132.  
  133. //返回箭头
  134. handleBack = () => {
  135. //把任务栈顶部的任务清除
  136. this.props.navigation.goBack();
  137. }
  138.  
  139.  
  140. //左边的箭头
  141. getNavLeftBtn = () => {
  142. return <View style={{flexDirection: 'row',alignItems: 'center'}}>
  143. <TouchableOpacity
  144. activeOpacity={0.7}
  145. onPress={this.handleBack}>
  146. <Image source={require('../res/image/ic_arrow_back_white_36pt.png')}
  147. style={{width: 24,height: 24}}/>
  148. </TouchableOpacity>
  149. </View>;
  150. }
  151.  
  152.  
  153. //页面渲染加载完调用加载数据
  154. componentDidMount() {
  155. this.loadData();
  156. }
  157.  
  158. //设置行
  159. renderRow(rowData,rowId) {
  160. return (
  161. <TouchableOpacity
  162. key={rowId}
  163. style={{height: ROWHEIGHT,justifyContent: 'center',paddingLeft: 20,paddingRight: 30}}
  164. onPress={() => {
  165. that.changedata(rowData)
  166. }}>
  167. <View style={styles.rowdata}><Text style={styles.rowdatatext}>{rowData}</Text></View>
  168. </TouchableOpacity>
  169. )
  170. }
  171.  
  172. //设置组
  173. renderSectionHeader = (sectionData,sectionID) => {
  174. return (
  175. <View style={{height: SECTIONHEIGHT,paddingLeft: 5,backgroundColor: 'gray'}}>
  176. <Text style={{color: 'black',fontWeight: 'bold',marginLeft: 10}}>
  177. {sectionData}
  178. {/*{console.log(`sectionData = ${sectionData}`)}*/}
  179. </Text>
  180. </View>
  181. )
  182. }
  183. // render ringht index Letters 右边的字母
  184. // onLayout 测量字母
  185. renderLetters(letter,index) {
  186. return (
  187. <TouchableOpacity
  188. onLayout={({nativeEvent: e}) => this.oneLetterLayout(e)}
  189. key={index} activeOpacity={0.7}
  190. onPressIn={() => {
  191. this.scrollTo(index)
  192. }}>
  193. <View
  194. style={styles.letter}>
  195. <Text style={styles.letterText}>{letter}</Text>
  196. </View>
  197. </TouchableOpacity>
  198. )
  199. }
  200.  
  201.  
  202. //回调改变显示的城市
  203. changedata = (cityname) => {
  204. const {navigation} = this.props;
  205. const {state,goBack} = navigation;
  206. console.log(state);
  207. console.log(cityname);
  208. state.params.callback(cityname)
  209. goBack();
  210. }
  211.  
  212. //touch right indexLetters,scroll the left
  213. scrollTo = (index) => {
  214. let position = 0;
  215.  
  216. for (let i = 0; i < index; i++) {
  217. position += totalheight[i]
  218. }
  219. this._listView.scrollTo({
  220. y: position,animated: true
  221. })
  222. }
  223.  
  224.  
  225. //搜索框高度
  226. searchLayout = (e) => {
  227. // console.log('searchLayout 高度' + e.layout.height);
  228. }
  229. //字母高度
  230. lettersLayout = (e) => {
  231. // console.log('lettersLayout 高度' + e.layout.height);
  232. // console.log('lettersLayout y坐标' + e.layout.y);
  233. lettersHeight = height - searchHeight * 2 - searchHeightMargin * 2;
  234. // console.log('字母列表高度 = ' + lettersHeight);
  235. // console.log('height = ' + height);
  236. }
  237. //每个字母高度
  238. oneLetterLayout = (e) => {
  239. // console.log('lettersLayout 高度' + e.layout.height);
  240. // console.log('每个字母高度 y坐标' + e.layout.y);
  241. // if (lettersItemheight.length >= 0) {
  242. // lettersItemheight = [];
  243. // }
  244. if (lettersItemheight.length != myLetters.length) {
  245. lettersItemheight.push(e.layout.y);
  246. }
  247. }
  248. //文本改变
  249. changeText = (text) => {
  250. // console.log("改变的文本: " + text);
  251. text = text.trim();
  252. if (text != "") {
  253. if (/^[\u4e00-\u9fa5]/.test(text)) {//是否有中文
  254. console.log("===有中文===")
  255. let mCityData = [];
  256. console.log("原始数据: " + cityData);
  257. let k = 0;
  258. for (let i = 0; i < cityData.length; i++) {
  259. let data = [];
  260. data = cityData[i].data;
  261. // console.log("data = " + data);
  262. // console.log("data 长度 = " + data.length);
  263. let isHas = false;//是否存在
  264.  
  265. var itemData = [];//这里是匹配的城市数据
  266. for (let j = 0; j < data.length; j++) {
  267. // console.log("字符串是否相等"+data[j].includes(text));
  268. // console.log("输入框内: "+text);
  269. // console.log("数据内: "+data[j]);
  270.  
  271. // itemData = cityData[i];
  272. if (data[j].includes(text)) {
  273. console.log("正确的itm data = " + cityData[i].data);
  274. itemData.push(data[j]);
  275. // mCityData.push(data);
  276. isHas = true;//
  277. }
  278. }
  279. //内层循环结束
  280. console.log("itemData = " + itemData);
  281. if (isHas) {
  282. let obj;
  283. obj = {'zimu': cityData[i].zimu,'data': itemData,'id': cityData[i].id};
  284. console.log("添加的数据 " + obj.zimu + " " + obj.data);
  285. console.log("添加的数据 " + obj);
  286. mCityData[k] = obj;
  287. k++;
  288. }
  289.  
  290.  
  291.  
  292. }
  293. // console.log("过滤后的数据: " + mCityData[0].data);
  294. // console.log("过滤后的数据: " + mCityData[1].data);
  295. // console.log("过滤后的数据长度: " + mCityData.length);
  296. // console.log("过滤后的数据: "+mCityData );
  297. // mCityData.map((item,i) => { // console.log("\n" + item.data); // }) this.updateData(mCityData); } else {//否则是英文 for (let i = 0; i < cityData.length; i++) { console.log("===英文===") // console.log("打印字母 " + cityData[i].zimu); // console.log("打印改变的文字 " + text.toLowerCase()); if (cityData[i].zimu == text.toUpperCase()) { // if (cityData[i].zimu.indexOf(text) != -1) { let mCityData = []; mCityData.push(cityData[i]); this.updateData(mCityData); return; } } } } else { // myDataBlob.map((item,i)=>{ // console.log(item); // }) // console.log(myDataBlob); // console.log(mySectionIDs); // console.log(myRowIDs); // // console.log("打印setstate===================") console.log("===数据为空刷新===") this.setState({ dataSource: this.state.dataSource.cloneWithRowsAndSections(myDataBlob,isLoading: false,letteRSShow: true }) } } componentWillMount() { this._panGesture = PanResponder.create({ //要求成为响应者: onStartShouldSetPanResponder: (evt,gestureState) => true,onStartShouldSetPanResponderCapture: (evt,onMoveShouldSetPanResponder: (evt,onMoveShouldSetPanResponderCapture: (evt,onPanResponderTerminationRequest: (evt,onPanResponderGrant: (evt,gestureState) => { // console.log('触摸 当响应器产生时的屏幕坐标 \n x:' + gestureState.x0 + ',y:' + gestureState.y0); let value = gestureState.y0 - searchHeight * 2 - lettersBottom + 1; // console.log("点击的点 : " + value); for (let i = 0; i < lettersItemheight.length; i++) { if (value < 0) { this.scrollTo(0); } else if (value > lettersItemheight[i]) { this.scrollTo(i); } } },onPanResponderMove: (evt,gestureState) => { // console.log('移动 最近一次移动时的屏幕坐标\n moveX:' + gestureState.moveX + ',moveY:' + gestureState.moveY); // console.log('移动 当响应器产生时的屏幕坐标\n x0:' + gestureState.x0 + ',y0:' + gestureState.y0); // console.log('移动 从触摸操作开始时的累计纵向路程\n dx:' + gestureState.dx + ',dy :' + gestureState.dy); let value = gestureState.moveY - searchHeight * 2 - lettersBottom + 1; // console.log("移动的点 " + value); for (let i = 0; i < lettersItemheight.length; i++) { if (value < 0) { this.scrollTo(0); } else if (value > lettersItemheight[i]) { this.scrollTo(i); } } // console.log(this.mul(sub,myLetters.length)); },onResponderTerminationRequest: (evt,onPanResponderRelease: (evt,gestureState) => { // console.log('抬手 x:' + gestureState.moveX + ',y:' + gestureState.moveY); },onPanResponderTerminate: (evt,gestureState) => { // console.log(`结束 = evt.identifier = ${evt.identifier} gestureState = ${gestureState}`); },}); } //做一些清除操作 避免再次进入会有数据异常 componentWillUnmount() { myLetters = []; myDataBlob = {};//获取到的数据 mySectionIDs = []; //组id myRowIDs = [];//组内 cityData = [];//获取到的数据 lettersItemheight = []; } //渲染 render() { return ( <View style={styles.container}> <NavigationBar onLayout={({nativeEvent: e1}) => this.navigationLayout(e1)} ref={(ref) => this.myNavigationBar = ref} title="选择城市" leftButton={this.getNavLeftBtn()} ></NavigationBar> <View onLayout={({nativeEvent: e}) => this.searchLayout(e)} style={styles.searchBox}> <Image source={require('../res/image/search_bar_icon_normal.png')} style={styles.searchIcon}/> <TextInput style={styles.inputText} onChangeText={(text) => this.changeText(text)} underlineColorAndroid='transparent' //设置下划线背景色透明 达到去掉下划线的效果 placeholder='请输入城市名称或首字母'/> </View> <ListView contentContainerStyle={styles.contentContainer} ref={listView => this._listView = listView} dataSource={this.state.dataSource} renderRow={this.renderRow} renderSectionHeader={this.renderSectionHeader} enableEmptySections={true} initialListSize={totalNumber} refreshControl={ <RefreshControl refreshing={this.state.isLoading} tintColor="#63B8FF" title="正在加载..." titleColor="#63B8FF" colors={['#63B8FF']} /> } /> { //判断是否显示右边字母列表 this.state.letteRSShow == false ? (null) : ( <View ref="ref_letters" {...this._panGesture.panHandlers} onLayout={({nativeEvent: e}) => this.lettersLayout(e)} style={styles.letters}> {myLetters.map((letter,index) => this.renderLetters(letter,index))} </View> ) } </View> ); } }; const styles = StyleSheet.create({ container: { flex: 1,flexDirection: 'column' },contentContainer: { width: width,paddingBottom: 20,backgroundColor: 'white',},//字母列表的样式 letters: { flexDirection: 'column',position: 'absolute',height: height - searchHeight - searchHeight - lettersBottom - StatusBar.currentHeight,top: searchHeight + searchHeight + 4,bottom: lettersBottom,right: 10,backgroundColor: 'transparent',justifyContent: 'space-between',alignItems: 'center',// height 字母的高度间距 // width 字母的宽度 letter: { height: height * 3.3 / 100,width: width * 3 / 50,justifyContent: 'center',letterText: {//右边list字母的样式 textAlign: 'center',fontSize: height * 1.1 / 50,color: 'black' },rowdata: {//下划线的样式 borderBottomColor: '#faf0e6',borderBottomWidth: 0.5 },rowdatatext: { color: 'gray',searchBox: {//最外层搜索框包裹 height: searchHeight,borderColor: 'black',flexDirection: 'row',// 水平排布 borderRadius: 10,// 设置圆角边 backgroundColor: '#FFF',borderWidth: 0.8,borderRadius: 10,borderColor: 'gray',marginLeft: 8,paddingTop: 0,marginTop: searchHeightMargin,marginBottom: searchHeightMargin,paddingBottom: 0,marginRight: 8,searchIcon: {//搜索图标 height: 20,width: 20,marginLeft: 5,resizeMode: 'stretch' },inputText: {//搜索框 backgroundColor: 'transparent',fontSize: 13,flex: 1,})

Github 地址

https://github.com/liudao01/cityList

猜你在找的React相关文章