当前位置:

ReactNative 滑动字母选择城市 以和搜索功能

访客 2024-04-23 1061 0

先上效果图

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

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

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

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

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

哪一个字母的item

下面上代码

/***Createdbyliumlon2017/10/5.*/importReact,{Component}from'react';import{View,Image,TouchableOpacity,Modal,Text,StatusBar,ListView,Platform,Dimensions,StyleSheet,RefreshControl,Alert,TextInput,PanResponder}from'react-native';import_from'lodash';importNavigationBarfrom'./compont/NavigationBar'const{width,height}=Dimensions.get('window')constSECTIONHEIGHT=30,ROWHEIGHT=40//这是利用lodash的range和数组的map画出26个英文字母varUtil=require('./util/util');//工具类varScreenUtil=require('./util/ScreenUtil');//工具类letcity=[];//城市的数组里面放的是对象vartotalheight=[];//每个字母对应的城市和字母的总高度比如所有a字母中数据的高度varlettersItemheight=[];//每个字母的y坐标varmyLetters=[];//我的字母数组varmyDataBlob={};//获取到的数据varlettersBottom=10;//字母列表距离底部高度varmySectionIDs=[];//组idvarmyRowIDs=[];//组内varcityData=[];//获取到的数据vartotalNumber=10;//总条数的数据varsearchHeight=35;//搜索框高度varsearchHeightMargin=2;//搜索框marginvarlettersHeight;//字母列表高度varthat;exportdefaultclassCityListextendsComponent{constructor(props){super(props);//获取组中数据vargetSectionData=(myDataBlob,mySectionIDs)=>{//console.log("组idmySectionIDs="mySectionIDs);//console.log(`组中数据=${myDataBlob[mySectionIDs]}`);returnmyDataBlob[mySectionIDs];};//获取行中的数据vargetRowData=(myDataBlob,mySectionIDs,myRowIDs)=>{//console.log(`行中数据=${myDataBlob[myRowIDs]}`);returnmyDataBlob[myRowIDs];};this.state={dataSource:newListView.DataSource({getSectionHeaderData:getSectionData,getRowData:getRowData,rowHasChanged:(row1,row2)=>row1!==row2,sectionHeaderHasChanged:(s1,s2)=>s1!==s2,}),isLoading:true,lettersShow:true}that=this;}//加载数据loadData=()=>{//varthiz=this;Util.post('http://ovji4jgcd.bkt.clouddn.com/Mycity.json',{},(ret)=>{//console.log(ret);if(ret.resCode==1&&ret.data.length>0){cityData=ret.data;//console.log(cityData);totalNumber=ret.totalNumber;//一系列的操作遍历数组console.log("正确的总数据:"cityData);this.setData(cityData);}});}//设置数据setData=(cityData)=>{for(leti=0;i<cityData.length;i){varmysectionName='Section_'i;letcityMode=cityData[i].data;letzimu=cityData[i].zimu;mySectionIDs.push(mysectionName)myRowIDs[i]=[];varinnerLoop=cityData[i].data;//内循环中的城市myDataBlob[mysectionName]=zimu;//把字母放入总数据myLetters.push(zimu)//把字母放入用于右边的字母列表for(letjj=0;jj<innerLoop.length;jj){letrowName=i'_'jj;myRowIDs[i].push(rowName);myDataBlob[rowName]=innerLoop[jj];}//组的高度上行的高度*有多少行vareachheight=SECTIONHEIGHTROWHEIGHT*cityMode.lengthtotalheight.push(eachheight)}letsize=myLetters.length;//console.log("字母数量"size);//console.log("lettersHeight="lettersHeight);//关闭对话框设置数据源//console.log("打印setstate===================")this.setState({dataSource:this.state.dataSource.cloneWithRowsAndSections(myDataBlob,mySectionIDs,myRowIDs),isLoading:false,lettersShow:true})}//更新数据updateData=(cityData)=>{console.log("更新的数据:"cityData);letmyDataBlob=[],mySectionIDs=[],myRowIDs=[];for(leti=0;i<cityData.length;i){letmysectionName='Section_'i;//letcityMode=cityData[i].data;letzimu=cityData[i].zimu;mySectionIDs.push(mysectionName)myRowIDs[i]=[];letinnerLoop=cityData[i].data;//内循环中的城市myDataBlob[mysectionName]=zimu;//把字母放入总数据//myLetters.push(zimu)//把字母放入用于右边的字母列表for(letjj=0;jj<innerLoop.length;jj){letrowName=i'_'jj;myRowIDs[i].push(rowName);myDataBlob[rowName]=innerLoop[jj];}}//console.log("打印setstate===================")this.setState({dataSource:this.state.dataSource.cloneWithRowsAndSections(myDataBlob,mySectionIDs,myRowIDs),isLoading:false,lettersShow:false})}//返回箭头handleBack=()=>{//把任务栈顶部的任务清除this.props.navigation.goBack();}//左边的箭头getNavLeftBtn=()=>{return<Viewstyle={{flexDirection:'row',alignItems:'center'}}><TouchableOpacityactiveOpacity={0.7}onPress={this.handleBack}><Imagesource={require('../res/image/ic_arrow_back_white_36pt.png')}style={{width:24,height:24}}/></TouchableOpacity></View>;}//页面渲染加载完调用加载数据componentDidMount(){this.loadData();}//设置行renderRow(rowData,rowId){return(<TouchableOpacitykey={rowId}style={{height:ROWHEIGHT,justifyContent:'center',paddingLeft:20,paddingRight:30}}onPress={()=>{that.changedata(rowData)}}><Viewstyle={styles.rowdata}><Textstyle={styles.rowdatatext}>{rowData}</Text></View></TouchableOpacity>)}//设置组renderSectionHeader=(sectionData,sectionID)=>{return(<Viewstyle={{height:SECTIONHEIGHT,justifyContent:'center',paddingLeft:5,backgroundColor:'gray'}}><Textstyle={{color:'black',fontWeight:'bold',marginLeft:10}}>{sectionData}{/*{console.log(`sectionData=${sectionData}`)}*/}</Text></View>)}//renderringhtindexLetters右边的字母//onLayout测量字母renderLetters(letter,index){return(<TouchableOpacityonLayout={({nativeEvent:e})=>this.oneLetterLayout(e)}key={index}activeOpacity={0.7}onPressIn={()=>{this.scrollTo(index)}}><Viewstyle={styles.letter}><Textstyle={styles.letterText}>{letter}</Text></View></TouchableOpacity>)}//回调改变显示的城市changedata=(cityname)=>{const{navigation}=this.props;const{state,goBack}=navigation;console.log(state);console.log(cityname);state.params.callback(cityname)goBack();}//touchrightindexLetters,scrolltheleftscrollTo=(index)=>{letposition=0;for(leti=0;i<index;i){position=totalheight[i]}this._listView.scrollTo({y:position,animated:true})}//搜索框高度searchLayout=(e)=>{//console.log('searchLayout高度'e.layout.height);}//字母高度lettersLayout=(e)=>{//console.log('lettersLayout高度'e.layout.height);//console.log('lettersLayouty坐标'e.layout.y);lettersHeight=height-searchHeight*2-searchHeightMargin*2;//console.log('字母列表高度='lettersHeight);//console.log('height='height);}//每个字母高度oneLetterLayout=(e)=>{//console.log('lettersLayout高度'e.layout.height);//console.log('每个字母高度y坐标'e.layout.y);//if(lettersItemheight.length>=0){//lettersItemheight=[];//}if(lettersItemheight.length!=myLetters.length){lettersItemheight.push(e.layout.y);}}//文本改变changeText=(text)=>{//console.log("改变的文本:"text);text=text.trim();if(text!=""){if(/^[\u4e00-\u9fa5]/.test(text)){//是否有中文console.log("===有中文===")letmCityData=[];console.log("原始数据:"cityData);letk=0;for(leti=0;i<cityData.length;i){letdata=[];data=cityData[i].data;//console.log("data="data);//console.log("data长度="data.length);letisHas=false;//是否存在varitemData=[];//这里是匹配的城市数据for(letj=0;j<data.length;j){//console.log("字符串是否相等"data[j].includes(text));//console.log("输入框内:"text);//console.log("数据内:"data[j]);//itemData=cityData[i];if(data[j].includes(text)){console.log("正确的itmdata="cityData[i].data);itemData.push(data[j]);//mCityData.push(data);isHas=true;//}}//内层循环结束console.log("itemData="itemData);if(isHas){letobj;obj={'zimu':cityData[i].zimu,'data':itemData,'id':cityData[i].id};console.log("添加的数据"obj.zimu""obj.data);console.log("添加的数据"obj);mCityData[k]=obj;k;}}//console.log("过滤后的数据:"mCityData[0].data);//console.log("过滤后的数据:"mCityData[1].data);//console.log("过滤后的数据长度:"mCityData.length);//console.log("过滤后的数据:"mCityData);//mCityData.map((item,i)=>{//console.log("\n"item.data);//})this.updateData(mCityData);}else{//否则是英文for(leti=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){letmCityData=[];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,mySectionIDs,myRowIDs),isLoading:false,lettersShow:true})}}componentWillMount(){this._panGesture=PanResponder.create({//要求成为响应者:onStartShouldSetPanResponder:(evt,gestureState)=>true,onStartShouldSetPanResponderCapture:(evt,gestureState)=>true,onMoveShouldSetPanResponder:(evt,gestureState)=>true,onMoveShouldSetPanResponderCapture:(evt,gestureState)=>true,onPanResponderTerminationRequest:(evt,gestureState)=>true,onPanResponderGrant:(evt,gestureState)=>{//console.log('触摸当响应器产生时的屏幕坐标\nx:'gestureState.x0',y:'gestureState.y0);letvalue=gestureState.y0-searchHeight*2-lettersBottom1;//console.log("点击的点:"value);for(leti=0;i<lettersItemheight.length;i){if(value<0){this.scrollTo(0);}elseif(value>lettersItemheight[i]){this.scrollTo(i);}}},onPanResponderMove:(evt,gestureState)=>{//console.log('移动最近一次移动时的屏幕坐标\nmoveX:'gestureState.moveX',moveY:'gestureState.moveY);//console.log('移动当响应器产生时的屏幕坐标\nx0:'gestureState.x0',y0:'gestureState.y0);//console.log('移动从触摸操作开始时的累计纵向路程\ndx:'gestureState.dx',dy:'gestureState.dy);letvalue=gestureState.moveY-searchHeight*2-lettersBottom1;//console.log("移动的点"value);for(leti=0;i<lettersItemheight.length;i){if(value<0){this.scrollTo(0);}elseif(value>lettersItemheight[i]){this.scrollTo(i);}}//console.log(this.mul(sub,myLetters.length));},onResponderTerminationRequest:(evt,gestureState)=>true,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=[];//组idmyRowIDs=[];//组内cityData=[];//获取到的数据lettersItemheight=[];}//渲染render(){return(<Viewstyle={styles.container}><NavigationBaronLayout={({nativeEvent:e1})=>this.navigationLayout(e1)}ref={(ref)=>this.myNavigationBar=ref}title="选择城市"leftButton={this.getNavLeftBtn()}></NavigationBar><ViewonLayout={({nativeEvent:e})=>this.searchLayout(e)}style={styles.searchBox}><Imagesource={require('../res/image/search_bar_icon_normal.png')}style={styles.searchIcon}/><TextInputstyle={styles.inputText}onChangeText={(text)=>this.changeText(text)}underlineColorAndroid='transparent'//设置下划线背景色透明达到去掉下划线的效果placeholder='请输入城市名称或首字母'/></View><ListViewcontentContainerStyle={styles.contentContainer}ref={listView=>this._listView=listView}dataSource={this.state.dataSource}renderRow={this.renderRow}renderSectionHeader={this.renderSectionHeader}enableEmptySections={true}initialListSize={totalNumber}refreshControl={<RefreshControlrefreshing={this.state.isLoading}tintColor="#63B8FF"title="正在加载..."titleColor="#63B8FF"colors={['#63B8FF']}/>}/>{//判断是否显示右边字母列表this.state.lettersShow==false?(null):(<Viewref="ref_letters"{...this._panGesture.panHandlers}onLayout={({nativeEvent:e})=>this.lettersLayout(e)}style={styles.letters}>{myLetters.map((letter,index)=>this.renderLetters(letter,index))}</View>)}</View>);}};conststyles=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:searchHeightsearchHeight4,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',alignItems:'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',alignItems:'center',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,paddingBottom:0,paddingTop:0,flex:1,},})

Github地址

右上角点击一下star就是对我最好的支持也是我的动力谢谢

github.com/liudao01/ci…

发表评论

  • 评论列表
还没有人评论,快来抢沙发吧~