React Native实现应用中心图标拖动功能
React Native (简称RN)是Facebook于2015年4月开源的跨平台移动应用开发框架,是Facebook早先开源的UI框架 React 在原生移动应用平台的衍生产物,目前支持iOS和安卓两大平台。RN使用Javascript语言,类似于HTML的JSX,以及CSS来开发移动应用,因此熟悉Web前端开发的技术人员只需很少的学习就可以进入移动应用开发领域。(百度百科)
小编有幸参与了公司APP的开发,该APP采用混合式开发,本人负责RN的几个功能的开发,其中最复杂的就是应用中心的功能,如下图所示。模块分为上下两部分,下面是所有应用,上面是下方勾选的应用,可以上下左右滑动(图中只有一行,后面改成了两行,故可以上下或者斜着滑动,偷懒就不截图了)
所以一共要实现接收数据、选择应用、调整顺序、提交这个几个功能,下面重点讲述调整顺序的代码,其他的代码可能会掠过。
1、获取数据
定一个几个变量,用于保存我的应用和所有应用,获取数据调用了原生的方法,大家用RN的获取数据的方法是一样的。
在构造器里面的变量 this.myapps = []; this.application= [];
//获取数据 dsfectchData(){ RNBridgeManager.queryAppList((error, result) => { if(error){ console.log("---q--->error:" + error); }else{ var myapp = []; for(var i=0;i<result.length;i++){ if(result[i].myAppList == 1){ myapp.push(result[i]); console.log( i + " : " + result[i].appId); } } this.myapps = myapp; //保存我的应用数据 console.log("this.myapps" + this.myapps.length); this.application = result; // 保存所有数据 } }); }
2、展示数据
重点来看我的应用的数据展示方式,如下图第一个红色标记的是将数据放入到一个数组中,这个数据记录
顺序;第二个红色标记用于和上一个标记中的order数组一起,用于排序和重新展示;第三个红色标记的
部分是每个可拖动的图标的定位,当然首先要将定位设定为:position = 'absolute'。绿色底标注
的为可拖动的核心,即触摸操作。
<View style={{flexDirection: 'row',height:25,backgroundColor: '#fff',alignItems: 'center',paddingTop:10,marginTop:px2pt(10)}}>
<Image source={{uri:this.state.navbar}} style={{width:3,height:15,marginLeft:px2pt(10)}} /> <Text style={styles.itemTitle}>我的应用</Text> <Text style={styles.itemTitleDes}>(按住拖动调整顺序)</Text> </View> <View style={styles.containerMy}> {this.myapps.map((item, i)=>{ this.order.push(item); return ( <View {...this._panResponder.panHandlers} ref={(ref) => this.items[i] = ref} key={i} style={[styles.item, {top: (parseInt(i/4))*90 + 10,left:(i%4)*this.widthSreen}]}> <Image source={{uri:this.state.shortcut_icon_bg}} style={styles.thumbnail} /> <Image source={this._getApplicationIcons(item.appId)} style={styles.thumbnailInside} /> <TextActivity text={item.appName} width={containerWidth}/> <TouchableWithoutFeedback onPress={this._pressIcon.bind(this,this.myapps,i,1)} style={styles.buttonRightTop}> <Image source={{uri:this.state.shortcut_icon_del}} style={styles.imageRightTop} /> </TouchableWithoutFeedback> </View> ); })} </View>
3、滑动操作部分,此部分依次为 1)点击时判定是哪个图标(根据坐标) 2)判定移动位置是否已经和其他的图标有重合,如果有,则交换两个图标的位置 3)释放后的显示结果 componentWillMount(){ this._panResponder = PanResponder.create({ onStartShouldSetPanResponder: (evt, gestureState) => true, onMoveShouldSetPanResponder: (evt, gestureState) => true, onPanResponderGrant: (evt, gestureState) => { const {pageX,pageY, locationX,locationY} = evt.nativeEvent; console.log("--eric-->:pageX:" + pageX + "--pageY:" + pageY + "--locationX:" + locationX + "--locationY:" + locationY); this.index = this._getIdByPosition(pageX,pageY); //逻辑应该是点击的一瞬间,判定是第几个 (parseInt(i/4))*90,left:(i%4)*this.widthSreen} this.preX = (this.index%4)*this.widthSreen; this.preY = (parseInt(this.index/4))*90 + 10; //get the taped item and highlight it let item = this.items[this.index]; if(item!=null){ item.setNativeProps({ style: { shadowColor: "#000", shadowOpacity: 0.3, shadowRadius: 5, shadowOffset: {height: 0, width: 2}, elevation: 5 } }); } }, // 移动判定是否进入了某个图标的领域,如果进入则交换 onPanResponderMove: (evt, gestureState) => { let left = this.preX + gestureState.dx; let top = this.preY + gestureState.dy; console.log("--eric-->top:" + top + ":" + gestureState.dy); let item = this.items[this.index]; if(item!=null) { item.setNativeProps({ style: {left: left,top: top} }); } let collideIndex = this._getIdByPosition(evt.nativeEvent.pageX,evt.nativeEvent.pageY); if(collideIndex !== this.index && collideIndex !== -1) { let collideItem = this.items[collideIndex]; if(collideItem!=null){ collideItem.setNativeProps({ style: {left: this._getLeftValueYById(this.index),top:this._getTopValueYById(this.index)} }); //swap two values [this.items[this.index], this.items[collideIndex]] = [this.items[collideIndex], this.items[this.index]]; [this.order[this.index], this.order[collideIndex]] = [this.order[collideIndex], this.order[this.index]]; this.index = collideIndex; } } }, // 释放后的显示结果 onPanResponderTerminationRequest: (evt, gestureState) => true, onPanResponderRelease: (evt, gestureState) => { const shadowStyle = { shadowColor: "#000", shadowOpacity: 0, shadowRadius: 0, shadowOffset: {height: 0, width: 0,}, elevation: 0 }; let item = this.items[this.index]; //go back the correct position if(item!=null){ item.setNativeProps({ style: {...shadowStyle, left: this._getLeftValueYById(this.index),top: this._getTopValueYById(this.index)} }); } console.log(this.order); }, onPanResponderTerminate: (evt, gestureState) => { // Another component has become the responder, so this gesture // should be cancelled } }); }