了解更多开源请访问:开源基础软件社区https://ost.51cto.com项目介绍本项目基于OpenHarmony的ArkUI框架:TS扩展的声明式开发范例。语法和概念可以参考官网官方文档地址:基于TS扩展的声明式开发范式,因为OpenHarmony的API相对HarmonyOS的API功能比较完善和成熟,一些新技术也早早暴露出来,所以本项目直接使用OpenHarmonySDK开发。工具版本:DevEcoStudio3.0ReleaseSDK版本:3.1.7.7(APIVersion8Release)效果演示实现思路首先记录每个索引对应的item的y轴坐标;使用属性方法:position()来设置item的位置;然后使用onTouch事件移动选中的item,完成与其他item的位置交换。1.页面布局@Entry@ComponentstructIndex{//列表数据@Statearray:Array=['1','2','3','4','5','6','7','8','9']build(){Column(){ForEach(this.array,(item,index)=>{Text('content'+item).width('100%').height(50).fontSize(18).fontColor(Color.White).borderRadius(10).margin({bottom:10}).textAlign(TextAlign.Center).backgroundColor('#18BF74')},item=>项目)}.width('100%').height('100%').padding(10)}}2.记录y轴坐标并设置位置新增三个变量mapOffsetY:将每个索引存储到y-轴位置。moveIndex:要移动的索引。moveOffsetY:移动的y轴偏移量。使用onAreaChange方法记录index对应的y轴位置,使用position方法设置item的位置。@Entry@ComponentstructIndex{//列表数据@Statearray:Array=['1','2','3','4','5','6','7','8','9']//key:index,value:y轴位置privatemapOffsetY:Map=newMap()//移动索引@StatemoveIndex:number=-2//移动偏移数量@StatemoveOffsetY:number=0build(){Column(){ForEach(this.array,(item,index)=>{Text('content'+item).width('100%').height(50).fontSize(18).fontColor(Color.White).borderRadius(10).margin({bottom:10}).textAlign(TextAlign.Center).backgroundColor('#18BF74').position({x:this.moveIndex===index?5:0,y:this.moveIndex===index?this.moveOffsetY:this.mapOffsetY.get(index)}).onAreaChange((oldValue:Area,newValue:Area)=>{if(this.mapOffsetY.size!==this.array.length){//记录每个itemconso的y坐标le.info(`index=${index}${JSON.stringify(newValue)}`)constheight=Number.parseInt(newValue.height.toString())this.mapOffsetY.set(index,10+(index*10)+index*height)//更新页面,使位置可以工作this.moveIndex=-1}})},item=>item)}.width('100%').height('100%').padding(10)}3.移动选中的itemzIndex(移动时浮在其他item之上)backgroundColor(移动时改变移动item的背景色)。onTouch(触摸事件,当手指移动时,改变选中项的位置)。@Entry@ComponentstructIndex{......//按下时自身顶点的y轴位置privatedownSelfY=0//按下时距离屏幕的y轴位置privatedownScreenY=0build(){Column(){ForEach(this.array,(item,index)=>{Text('content'+item).......zIndex(this.moveIndex===index?1:0).backgroundColor(this.moveIndex===index?'#14a063':'#18BF74').onTouch((event:TouchEvent)=>this.onTouchEvent(event,index))},item=>item)}.width('100%').height('100%').padding(10)}onTouchEvent(event:TouchEvent,index:number){switch(event.type){caseTouchType.Down://手指按下{//更新当前索引这。moveIndex=index//按下时自身顶点的y轴位置this.downSelfY=event.touches[0].y//按下时距离屏幕的y轴位置this.downScreenY=event.touches[0].screenY//改变偏移量this.moveOffsetY=this.downScreenY-this.downSelfY-5}breakcaseTouchType.Move://手指移动{//与屏幕y坐标的距离constscreenY=event.touches[0].screenY//改变偏移量this.moveOffsetY=screenY-this.downSelfY-5}breakcaseTouchType.Up://手指向上抬起this.moveIndex=-1breakdefault:break;}}}4.位置交换位置交换只是视觉上的变化。列表的索引index还是从0到7,其实改变是为了满足交换两个item内容(列表中的数据)的主要逻辑在下面的代码中:向下拖,向上拖这个部分......onTouchEvent(event:TouchEvent,index:number){switch(event.type){caseTouchType.Down://fingerpressed{......}breakcaseTouchType.Move://Fingermovement{//从屏幕y坐标的距离constscreenY=event.touches[0].screenY//改变偏移量this.moveOffsetY=screenY-this.downSelfY-5......//向下拖动if(screenY-this.downScreenY>25){//交换满足条件的两个item的内容consttempOffsetY=this.array[this.moveIndex+1]this.array[this.moveIndex+1]=this.array[this.moveIndex]this.array[this.moveIndex]=tempOffsetY//更新按下的y坐标this.downScreenY+=60//更新移动索引,触发页面更新this.moveIndex++}//向上拖动if(screenY-this.downScreenY<-35){consttempOffsetY=this.array[this.moveIndex-1]this.array[this.moveIndex-1]=this.array[this.mveIndex]this.array[this.moveIndex]=tempOffsetYthis.downScreenY-=60this.moveIndex--}}breakcaseTouchType.Up://手指向上this.moveIndex=-1breakdefault:break;}}完成代码在上面代码的基础上添加属性动画动画,让位置交换看起来不那么生硬@Entry@ComponentstructIndex{//列表数据@Statearray:Array=['1','2','3','4','5','6','7','8','9']//key:index,value:y轴位置privatemapOffsetY:Map=newMap()//移动索引@StatemoveIndex:number=-2//移动偏移数量@StatemoveOffsetY:number=0//按下时自身顶点的y轴位置privatedownSelfY=0//按下时距屏幕的y轴位置privatedownScreenY=0build(){Column(){ForEach(this.array,(item,index)=>{Text('content'+item).width('100%').height(50).fontSize(18).fontColor(Color.White).borderRadius(10).margin({bottom:10}).textAlign(TextAlign.Center).zIndex(this.moveIndex===index?1:0).position({x:this.moveIndex===index?5:0,y:this.moveIndex===index?this.moveOffsetY:this.mapOffsetY.get(index)}).animation({duration:this.moveIndex===index?0:100}).backgroundColor(this.moveIndex===index?'#14a063':'#18BF74').onTouch((event:TouchEvent)=>this.onTouchEvent(event,index)).onAreaChange((oldValue:Area,newValue:Area)=>{if(this.mapOffsetY.size!==this.array.length){//记录每一项的y坐标console.info(`index=${index}${JSON.stringify(newValue)}`)constheight=Number.parseInt(newValue.height.toString())this.mapOffsetY.set(index,10+(index*10)+index*height)//更新页面以使位置有效this.moveIndex=-1}})},item=>item)}.width('100%').height('100%').padding(10)}onTouchEvent(event:TouchEvent,index:number){switch(event.type){caseTouchType.Down://手指按下{//更新当前移动的索引this.moveIndex=index//按下时自身顶点的y轴位置this.downSelfY=event.touches[0].y//按下时屏幕上的y轴位置this.downScreenY=event.touches[0].screenY//改变偏移量this.moveOffsetY=this.downScreenY-this.downSelfY-5}breakcaseTouchType.Move://手指移动{//距离屏幕y坐标的距离constscreenY=event.touches[0].screenY//改变偏移量this.moveOffsetY=screenY-this.downSelfY-5//第一位,不能向上移动if(this.moveIndex===0&&this.moveOffsetY<0){this.moveOffsetY=0return}//最后一位不能向下移动if(this.moveIndex===this.array.length-1&&this.moveOffsetY>this.mapOffsetY.get(this.moveIndex)){this.moveOffsetY=this.mapOffsetY.get(this.moveIndex)return}//向下拖动if(screenY-this.downScreenY>25){//交换满足条件consttempOffsetY=this.array[this.moveIndex+1]this.array[this.moveIndex+1]=this.array[this.moveIndex]this.array[this.moveIndex]=tempOffsetY//更新按下的y坐标this.downScreenY+=60//更新移动索引,触发页面更新this.moveIndex++}//向上拖动if(screenY-this.downScreenY<-35){consttempOffsetY=this.array[this.moveIndex-1]this.array[this.moveIndex-1]=this.array[this.moveIndex]this.array[this.moveIndex]=tempOffsetYthis.downScreenY-=60this.moveIndex--}}breakcaseTouchType.Up://抬起你的手指this.moveIndex=-1breakdefault:break;}}}总结这个项目的难点是positionexchange那一块:index的顺序没有变,只是改变了list里面的数据,移动了中间的index和onAreaChange方法。如果没有设置方法position,可以在方法中获取每个item的y坐标,设置position后,y坐标错误,需要在onAreaChange中计算item的y坐标,然后更新页面.这样就可以显示列表了。每天进步一点点,需要付出一点点努力。了解更多开源知识,请访问:开源基础软件社区https://ost.51cto.com。