最近因为业务需求须要实现一个功能须要实现图片的上传和排序和删除,在网上搜索了几款发现都须要固定列数,感受不太友好,因此本身实现了一个能够 不须要设定列数的排序,并且 布局高度实现自适应。
![[图片上传中...(iphone.jpg-9f7224-1533711885416-0)]
](https://user-gold-cdn.xitu.io...react
其实拖拽排序在大多数编程语言里已经有不少中三方插件可使用,实现方法都差很少,并且例如Android和iOS或者如今的React-Native他们逻辑几乎是能够共用,你会写一个语言的拖拽排序,其余的都差很少。
tip: 滑动逻辑,例如当你把index=1拖到index=3,不是将1和3替换(0,3,2,1,4),而是(0,3,1,2,4)这才是拖拽后结果,只将被拖拽的一个替换到要去的位置,其余的向前和向后移动
// 触摸事件的监听 this._panResponder = PanResponder.create({ onStartShouldSetPanResponder: (evt, gestureState) => this.props.sortable, onStartShouldSetPanResponderCapture: (evt, gestureState) => { this.isMovePanResponder = false return false }, // 接管触摸加滑动事件 onMoveShouldSetPanResponder: (evt, gestureState) => this.isMovePanResponder, onMoveShouldSetPanResponderCapture: (evt, gestureState) => this.isMovePanResponder, onPanResponderGrant: (evt, gestureState) => {}, onPanResponderMove: (evt, gestureState) => this.moveTouch(evt,gestureState), onPanResponderRelease: (evt, gestureState) => this.endTouch(evt), onPanResponderTerminationRequest: (evt, gestureState) => false, onShouldBlockNativeResponder: (evt, gestureState) => false, }) //这里使用长按触发开发拖拽事件,其实开始是使用触摸必定时间后触发事件的,但和View的单机事件有冲突很差解决,因此选择了长按触发事件 startTouch(touchIndex) { // 接管滑动 this.isMovePanResponder = true //if (this.measureTimeOut) clearTimeout(this.measureTimeOut) if (sortRefs.has(touchIndex)) { if (this.props.onDragStart) { this.props.onDragStart(touchIndex) } //变大和加透明 Animated.timing( this.state.dataSource[touchIndex].scaleValue, { toValue: maxScale, duration: scaleDuration, } ).start(()=>{ // 备份被触摸的事件 this.touchCurItem = { ref: sortRefs.get(touchIndex), index: touchIndex, // 记录以前的位置 originLeft: this.state.dataSource[touchIndex].originLeft, originTop: this.state.dataSource[touchIndex].originTop, moveToIndex: touchIndex, } }) } } //滑动 moveTouch (nativeEvent,gestureState) { if (this.touchCurItem) { let dx = gestureState.dx let dy = gestureState.dy const rowNum = parseInt(this.props.parentWidth/this.itemWidth); const maxWidth = this.props.parentWidth-this.itemWidth const maxHeight = this.itemHeight*Math.ceil(this.state.dataSource.length/rowNum) - this.itemHeight //出界后取最大或最小值防止出界 if (this.touchCurItem.originLeft + dx < 0) { dx = -this.touchCurItem.originLeft } else if (this.touchCurItem.originLeft + dx > maxWidth) { dx = maxWidth - this.touchCurItem.originLeft } if (this.touchCurItem.originTop + dy < 0) { dy = -this.touchCurItem.originTop } else if (this.touchCurItem.originTop + dy > maxHeight) { dy = maxHeight - this.touchCurItem.originTop } let left = this.touchCurItem.originLeft + dx let top = this.touchCurItem.originTop + dy //置于最上层 this.touchCurItem.ref.setNativeProps({ style: { zIndex: touchZIndex, } }) //滑动时刷新布局,这里直接刷新Animated的数字就能够进行局部刷新了 this.state.dataSource[this.touchCurItem.index].position.setValue({ x: left, y: top, }) let moveToIndex = 0 let moveXNum = dx/this.itemWidth let moveYNum = dy/this.itemHeight if (moveXNum > 0) { moveXNum = parseInt(moveXNum+0.5) } else if (moveXNum < 0) { moveXNum = parseInt(moveXNum-0.5) } if (moveYNum > 0) { moveYNum = parseInt(moveYNum+0.5) } else if (moveYNum < 0) { moveYNum = parseInt(moveYNum-0.5) } moveToIndex = this.touchCurItem.index+moveXNum+moveYNum*rowNum if (moveToIndex > this.state.dataSource.length-1) moveToIndex = this.state.dataSource.length-1 // 其余item向左和向右滑动 if (this.touchCurItem.moveToIndex != moveToIndex ) { this.touchCurItem.moveToIndex = moveToIndex this.state.dataSource.forEach((item,index)=>{ let nextItem = null if (index > this.touchCurItem.index && index <= moveToIndex) { nextItem = this.state.dataSource[index-1] } else if (index >= moveToIndex && index < this.touchCurItem.index) { nextItem = this.state.dataSource[index+1] } else if (index != this.touchCurItem.index && (item.position.x._value != item.originLeft || item.position.y._value != item.originTop)) { nextItem = this.state.dataSource[index] //有时前一个或者后一个数据有个动画差的缘由没法回到正确位置,这里进行矫正 } else if ((this.touchCurItem.index-moveToIndex > 0 && moveToIndex == index+1) || (this.touchCurItem.index-moveToIndex < 0 && moveToIndex == index-1)) { nextItem = this.state.dataSource[index] } //须要滑动的就进行滑动动画 if (nextItem != null) { Animated.timing( item.position, { toValue: {x: parseInt(nextItem.originLeft+0.5),y: parseInt(nextItem.originTop+0.5)}, duration: slideDuration, easing: Easing.out(Easing.quad), } ).start() } }) } } } //触摸事件 endTouch (nativeEvent) { //clear if (this.measureTimeOut) clearTimeout(this.measureTimeOut) if (this.touchCurItem) { if (this.props.onDragEnd) { this.props.onDragEnd(this.touchCurItem.index,this.touchCurItem.moveToIndex) } //this.state.dataSource[this.touchCurItem.index].scaleValue.setValue(1) Animated.timing( this.state.dataSource[this.touchCurItem.index].scaleValue, { toValue: 1, duration: scaleDuration, } ).start() this.touchCurItem.ref.setNativeProps({ style: { zIndex: defaultZIndex, } }) this.changePosition(this.touchCurItem.index,this.touchCurItem.moveToIndex) this.touchCurItem = null } } //刷新数据 changePosition(startIndex,endIndex) { if (startIndex == endIndex) { const curItem = this.state.dataSource[startIndex] this.state.dataSource[startIndex].position.setValue({ x: parseInt(curItem.originLeft+0.5), y: parseInt(curItem.originTop+0.5), }) return; } let isCommon = true if (startIndex > endIndex) { isCommon = false let tempIndex = startIndex startIndex = endIndex endIndex = tempIndex } const newDataSource = [...this.state.dataSource].map((item,index)=>{ let newIndex = null if (isCommon) { if (endIndex > index && index >= startIndex) { newIndex = index+1 } else if (endIndex == index) { newIndex = startIndex } } else { if (endIndex >= index && index > startIndex) { newIndex = index-1 } else if (startIndex == index) { newIndex = endIndex } } if (newIndex != null) { const newItem = {...this.state.dataSource[newIndex]} newItem.originLeft = item.originLeft newItem.originTop = item.originTop newItem.position = new Animated.ValueXY({ x: parseInt(item.originLeft+0.5), y: parseInt(item.originTop+0.5), }) item = newItem } return item }) this.setState({ dataSource: newDataSource },()=>{ if (this.props.onDataChange) { this.props.onDataChange(this.getOriginalData()) } //防止RN不绘制开头和结尾 const startItem = this.state.dataSource[startIndex] this.state.dataSource[startIndex].position.setValue({ x: parseInt(startItem.originLeft+0.5), y: parseInt(startItem.originTop+0.5), }) const endItem = this.state.dataSource[endIndex] this.state.dataSource[endIndex].position.setValue({ x: parseInt(endItem.originLeft+0.5), y: parseInt(endItem.originTop+0.5), }) }) }
一个完整小巧的Redux全家桶项目github
多功能React-Native-Toast组件react-native