首页 > 其他分享 >#打卡不停更# OpenHarmony-ArkUI(TS)声明式开发之列表拖动排列

#打卡不停更# OpenHarmony-ArkUI(TS)声明式开发之列表拖动排列

时间:2022-09-28 16:36:08浏览次数:93  
标签:OpenHarmony index TS event item moveIndex 打卡 screenY array

作者:梁青松

项目介绍

本项目基于OpenHarmony的ArkUI框架:TS扩展的声明式开发范式,关于语法和概念直接看官网官方文档地址:基于TS扩展的声明式开发范式,因为OpenHarmony的API相对于HarmonyOS的API,功能上比较完善和成熟的,有些新的技术也早早接触到,所以本项目直接使用OpenHarmony SDK开发。

工具版本: DevEco Studio 3.0 Release

SDK版本: 3.1.7.7(API Version 8 Release)

效果演示

demo.gif

实现思路

先记录每个index对应的item的y轴坐标;使用属性方法:position() 来设置item的位置;再使用onTouch事件移动选中的item并完成与其他item的位置交换。

1. 页面布局

@Entry
@Component
struct Index {
  // 列表数据
  @State array: Array<string> = ['1', '2', '3', '4', '5', '6', '7', '8', '9']

  build() {
    Column() {
      ForEach(this.array, (item, index) => {
        Text('内容' + item)
          .width('100%')
          .height(50)
          .fontSize(18)
          .fontColor(Color.White)
          .borderRadius(10)
          .margin({ bottom: 10 })
          .textAlign(TextAlign.Center)
          .backgroundColor('#18BF74')
      }, item => item)
    }.width('100%')
    .height('100%')
    .padding(10)
  }
}

2. 记录y轴坐标并设置位置

新增三个变量

  1. mapOffsetY:存入每一个index对y轴位置
  2. moveIndex:移动的索引
  3. moveOffsetY:移动的y轴偏移量

使用onAreaChange方法记录index对应的y轴位置,使用position方法设置item的位置

@Entry
@Component
struct Index {
  // 列表数据
  @State array: Array<string> = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
  // key:索引,value:y轴位置
  private mapOffsetY: Map<number, number> = new Map()
  // 移动的index
  @State moveIndex: number = -2
  // 移动的偏移量
  @State moveOffsetY: number = 0

  build() {
    Column() {
      ForEach(this.array, (item, index) => {
        Text('内容' + 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) {
              // 记录每个item的y坐标
              console.info(`index = ${index} ${JSON.stringify(newValue)}`)
              const height = Number.parseInt(newValue.height.toString())
              this.mapOffsetY.set(index, 10 + (index * 10) + index * height)
              // 更新页面,才能让position起作用
              this.moveIndex = -1
            }
          })
      }, item => item)
    }.width('100%')
    .height('100%')
    .padding(10)
  }

3. 移动选中的item

3.1 zIndex(移动时浮在其他item之上)

3.2 backgroundColor(移动时改变移动中的item背景颜色)

3.3 onTouch(触摸事件,手指移动时,更改选中的item位置)

demo1.png

@Entry
@Component
struct Index {
  ......
  // 按下时自身顶点y轴位置
  private downSelfY = 0
  // 按下时距屏幕的y轴位置
  private downScreenY = 0

  build() {
    Column() {
      ForEach(this.array, (item, index) => {
        Text('内容' + 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) {
      case TouchType.Down: // 手指按下
        {
          // 更新当前移动的index
          this.moveIndex = index
          // 按下时自身顶点y轴位置
          this.downSelfY = event.touches[0].y
          // 按下时距屏幕的y轴位置
          this.downScreenY = event.touches[0].screenY
          // 更改偏移量
          this.moveOffsetY = this.downScreenY - this.downSelfY - 5
        }
        break
      case TouchType.Move: // 手指移动
        {
          // 距离屏幕y坐标
          const screenY = event.touches[0].screenY
          // 更改偏移量
          this.moveOffsetY = screenY - this.downSelfY - 5
        }
        break
      case TouchType.Up: // 手指抬起
        this.moveIndex = -1
        break
      default:
        break;
    }
  }
}

4. 位置交换

位置交换只是视觉上面的改变,列表的索引index还是从0到7,其实改变的是满足交换的两个item内容(列表中数据)主要的逻辑是在以下代码:向下拖动、向上拖动这一部分。

......
onTouchEvent(event: TouchEvent, index: number) {
    switch (event.type) {
      case TouchType.Down: // 手指按下
        {
			......
        }
        break
      case TouchType.Move: // 手指移动
        {
          // 距离屏幕y坐标
          const screenY = event.touches[0].screenY
          // 更改偏移量
          this.moveOffsetY = screenY - this.downSelfY - 5
          ......
          // 向下拖动
          if (screenY - this.downScreenY > 25) {
            // 交换满足条件的两个item内容  
            const tempOffsetY = 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) {
            const tempOffsetY = this.array[this.moveIndex-1]
            this.array[this.moveIndex-1] = this.array[this.moveIndex]
            this.array[this.moveIndex] = tempOffsetY
            this.downScreenY -= 60
            this.moveIndex--
          }
        }
        break
      case TouchType.Up: // 手指抬起
        this.moveIndex = -1
        break
      default:
        break;
    }
  }

完整代码

在上面的代码的基础上加了属性动画animation,让位置交换看起来没有那么生硬。

@Entry
@Component
struct Index {
  // 列表数据
  @State array: Array<string> = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
  // key:索引,value:y轴位置
  private mapOffsetY: Map<number, number> = new Map()
  // 移动的index
  @State moveIndex: number = -2
  // 移动的偏移量
  @State moveOffsetY: number = 0
  // 按下时自身顶点y轴位置
  private downSelfY = 0
  // 按下时距屏幕的y轴位置
  private downScreenY = 0

  build() {
    Column() {
      ForEach(this.array, (item, index) => {
        Text('内容' + 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) {
              // 记录每个item的y坐标
              console.info(`index = ${index} ${JSON.stringify(newValue)}`)
              const height = Number.parseInt(newValue.height.toString())
              this.mapOffsetY.set(index, 10 + (index * 10) + index * height)
              // 更新页面,才能让position起作用
              this.moveIndex = -1
            }
          })
      }, item => item)
    }.width('100%')
    .height('100%')
    .padding(10)
  }

  onTouchEvent(event: TouchEvent, index: number) {
    switch (event.type) {
      case TouchType.Down: // 手指按下
        {
          // 更新当前移动的index
          this.moveIndex = index
          // 按下时自身顶点y轴位置
          this.downSelfY = event.touches[0].y
          // 按下时距屏幕的y轴位置
          this.downScreenY = event.touches[0].screenY
          // 更改偏移量
          this.moveOffsetY = this.downScreenY - this.downSelfY - 5
        }
        break
      case TouchType.Move: // 手指移动
        {
          // 距离屏幕y坐标
          const screenY = event.touches[0].screenY
          // 更改偏移量
          this.moveOffsetY = screenY - this.downSelfY - 5
          // 第一位,不能上移
          if (this.moveIndex === 0 && this.moveOffsetY < 0) {
            this.moveOffsetY = 0
            return
          }
          // 最后一位,不能下移
          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) {
            // 交换满足条件的两个item内容
            const tempOffsetY = 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) {
            const tempOffsetY = this.array[this.moveIndex-1]
            this.array[this.moveIndex-1] = this.array[this.moveIndex]
            this.array[this.moveIndex] = tempOffsetY
            this.downScreenY -= 60
            this.moveIndex--
          }
        }
        break
      case TouchType.Up: // 手指抬起
        this.moveIndex = -1
        break
      default:
        break;
    }
  }
}

总结

本项目的难点就是位置交换那块:index的顺序并没有改变,只是更改列表中的数据和移动的中索引。还有就是onAreaChange这个方法,如果没有设置方法position,在其方法内是能拿到每个item的y坐标,设置position后,y坐标是错误的,需要在onAreaChange计算一下item的y坐标,然后再更新页面。这样列表才能展示出来。

每天进步一点点、需要付出努力亿点点。

更多原创内容请关注:中软国际 HarmonyOS 技术团队

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com/#bkwz

标签:OpenHarmony,index,TS,event,item,moveIndex,打卡,screenY,array
From: https://blog.51cto.com/harmonyos/5716480

相关文章

  • ts报错解决-//@ts-ignore
    项目中最近切换了TypeScript,运行时没有问题的,但是打包的时候会报很多的错,最后我们是在报错的文件上方加上了 '//@ts-ignore'隐藏了ts文件的报错,建议我们在使用的时候在这......
  • Echarts销毁方法
    在调取Echarts实例时判断是否存在,存在就销毁if(this.launchEchart!=null&&this.launchEchart!=""&&this.launchEchart!=undefined){  this.launchEchar......
  • echarts文字颜色自定义修改
    1、改变坐标轴文字颜色:在xAxis,yAxis中添加以下代码即可 axisLabel:{      show:true,      textStyle:{         color......
  • vue3 ts 类式写法的mixins
    vue-property-decorator混入(mixins)//mixins.tsimport{Vue}from'vue-property-decorator'classMixinsextendsVue{publicname='混入'publicsay......
  • postgresql uuid模糊搜索 uuid string 类型转换 SQL Error [42883] explicit type cas
    问题描述在postgresql的使用过程中,你可能有以下几种需求:要对uuid类型的列进行过滤,但是需要手动输入整个uuid太麻烦容易出错,如果uuid列也能像字符串一样模糊匹配就......
  • 28、Python使用pyecharts绘制动态图
    基本思想:使用Python的pyecharts包进行中国疫情的动态图绘制;链接:https://pan.baidu.com/s/15oaBrsSGL_YNikRlwwaZZw 提取码:c460 复制这段内容后打开百度网盘手机App,操作......
  • vue3脚手架中ts无法识别引入的vue文件,提示找不到xxx.vue模块的解决
    使用vite搭建vue3脚手架的时候,发现main.ts中引入App.vue编辑器会报错,但是不影响代码运行。报错信息:TS2307:Cannotfindmodule'./App.vue'oritscorrespondingtyped......
  • 25、Jetson Xavier Nx 使用deepstream6.0进行目标检测和推流处理
    基本思想:客户的开发板,搞一个deepstream开发板上进行推流检测并将视频推到手机上进行实时显示,如果开发板的python环境有问题的话,可以在pc端进行模型转换,不用计较pc端的驱动......
  • 38、记录使用华为的ModelArts去调用npu训练yolov5模型和推理
    基本思想:有机会使用华为ModelArts云服务,做一下尝试,逐记录一下第一步:登录帐号,查看一下服务配置,镜像自己选择和缴费就行[ma-user~]$npu-smiinfo+--------------------------......
  • Vue源码解读之InitState
    前面我们讲到了_init函数的执行流程,简单回顾下:初始化生命周期-initLifecycle初始化事件-initEvents初始化渲染函数-initRender调用钩子函数-beforeCreate初始化依赖注......