Skip to main content

moregeek program

#打卡不停更# openharmony-arkui(ts)声明式开发之列表拖动排列_鸿蒙社区的博客-多极客编程

作者:梁青松


项目介绍


本项目基于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


©著作权归作者所有:来自51CTO博客作者开源基础软件社区官方的原创作品,请联系作者获取转载授权,否则将追究法律责任

《thinking in java》作者:不要使用并发!_qq6315732798102的博客-多极客编程

前言今天纯粹就是带你们来读读书的~ 最近除了工作,特地买回了自己很喜欢的作者新发售的一本书《On Java》,作者是我的老朋友布鲁斯·埃克尔,在Java领域很有名,你可能没听过他的名字,但极有可能听过他的另一本书《Thinking In Java》,我想很多Java工程师都读过这本书,可以说是Java编程思想的良心之作。 虽然布鲁斯是我的老朋友,但我不得不吐槽一下,大概通读了一遍《On Jav

#yyds干货盘点#java中拼接string的几种方式_文本、的博客-多极客编程

Java 提供了拼接 String 字符串的多种方式,不过有时候如果我们不注意 ​​null​​​ 字符串的话,可能会把 ​​null​​ 拼接到结果当中,很明显这不是我们想要的。在这篇文章中,将介绍一些在拼接 String 时避免 ​​null​​ 值的几种方式。问题复现如果我们想要拼接 String 数组,可以简单的使用 ​​+​​ 运算符进行拼接,但是可能会遇到 ​​null​​ 值。Str

python 时间与日期模块总结_ni_cue~的博客-多极客编程

python标准库中关于日期和时间的库主要有三个:calendar、time和datetime。 1.calemdar模块 calendar模块在日历的获取、显示以及年份是否为闰年等方面有诸多函数支持 1.1打印日历 import calendar # 输出2022年9月份的日历 print(calendar.month(2022, 9)) 输出结果 September 2022

数组找出单身狗经典问题_mb6318b1fa06863的博客-多极客编程

前言单身狗问题是大厂近几年的一个热门考点,所以我们就一起来探讨一下吧!摘要单身狗的问题解法有很多种,今天我带给大家两种经典解法,一、数组比较法,二、异或法,这两种解法我会分开来讲。思路我放在具体板块讲解一、数组比较法(两只狗)首先呢,我们要用数组比较,就需要把元素两两比较,当一个数出现两次,使之相减,结果为0的话不是单身狗,反之,这就是我们判断出单身狗的条件,但是如果数组乱序的话相邻元素比较可能为

matlab爬虫获取王者荣耀英雄皮肤_domi+1的博客-多极客编程

前言:周末闲来无事,玩了几局王者荣耀,突发奇想怎么获取到王者荣耀里面的英雄皮肤,本期分享一下如何通过matlab爬虫批量提取王者荣耀的英雄皮肤关键字:王者荣耀、爬虫、Matlab首先在度娘上找到王者荣耀的英雄主页​​https://pvp.qq.com/web201605/herolist.shtml​​通过chrome浏览器的F12,找到获取英雄的调用方法。 双击这个herolist.json,

基础知识(5) --matlab中特殊符号使用总结_domi+1的博客-多极客编程

前言:上篇文章分享了Matlab经常会遇到(),[],与{}三种符号,下面接着捋一捋其他的特殊符号使用方法,主要有:冒号'分号&   &&与|      || 或~非.点1、:冒号冒号的主要用途是用来表示数据从开始位置取到/生成到截止位置,例如:>> x = 1:4 % 生成1到4的数据x = 1 2 3 4>> x

#打卡不停更#编译效率快三倍,使用wsl2编译openharmony嘎嘎得劲!!_鸿蒙社区的博客-多极客编程

笔者发现使用wsl(Windows Subsystem for Linux,适用于 Linux 的 Windows 子系统)编译openharmony镜像比传统虚拟机快3倍以上。编译环境换成wsl后,编译ohos只需要传统虚拟机上三分之一的时间。下面分享使用经验如下: (目录) 为什么使用WSL2比传统虚拟机编译OpenHarmony快? 以下是微软官方给的解释:https://learn.mi

【ffh】openharmony设备开发(四)-wifi_ap开发_鸿蒙社区的博客-多极客编程

前言 我们在第一篇文章中讲到了WIFI的STA连接,本文章主要讲轻量化系统的WIFI的AP连接,即是打开WIFI的热点。本文适用于OpenHarmony3.1的轻量化系统设备. 设计流程 wifiAPTask主线程函数 注册wifi事件的回调函数RegisterWifiEvent(WifiEvent* event) 初始化wifi热点相关配置SetHotspotConfig(const H

harmonyos助力构建“食用菌智慧农场”_harmonyos开发者社区的博客-多极客编程

HarmonyOS助力构建“食用菌智慧农场”​【开发者说】栏目是为HarmonyOS开发者提供的展示和分享平台,在这里,大家可以发表自己的技术洞察和见解,也可以展示自己的开发心得和成果。欢迎大家积极投稿,公众号后台回复【投稿】,即可获得投稿渠道。期待你们的分享~本期我们给大家带来的是来自冰城哈尔滨的风驰电掣团队王丹的分享,希望能给你的HarmonyOS开发之旅带来启发~在第二届HarmonyOS开

#冲刺创作新星#一起学做鸿蒙“羊了个羊”_鸿蒙社区的博客-多极客编程

简介 最近大火了一个小游戏火遍朋友圈,我们就一起看看如何能用OpenHarmony学习做个”羊了个羊“。本文中引用的图片资源均来自:https://github.com/Jetereting/ylgy。 开发 1. HAP应用建立 《#跟着小白一起学鸿蒙#[六]如何编写一个hap应用》里我们介绍了简单的Hap应用的开发以及基础控件的介绍,这里我们就不赘述Hap项目的建立过程,以下就是基础的Ha

#打卡不停更#家庭健康管理平台_鸿蒙社区的博客-多极客编程

0. 项目简介 身体健康是一切生产生活的硬性基础。健康是福,一切安好,未来才可期。为什么经常跑步体重缺还在往上飘?突发紧急情况怎么处理?在数字时代,如何更好的为人们提供健康福祉、普及健康知识?如何进一步驱动个人健康管理是的值得研究的方向。为此,我们团队打造了一个健康管理平台——家庭健康管理平台。概览如下图所示: 家庭健康助理是集健康数据测量与管理、急救设备及使用指导、疫情防控实况、日常生活建议

#打卡不停更#智能喂食器_鸿蒙社区的博客-多极客编程

一、介绍 ​ 随着人们生活方式的不断改变,宠物猫在许多家庭中占有重要的地位,其凭借独立的个性和易于打理的饲养方式,成为当下上班族喜欢的宠物之一,人们更是把宠物猫和狗作为家庭的重要成员。有铲屎官表示,每月在宠物身上的基础花销是用来购买宠物粮,为了“小主”们的饮食可以说操碎了心,比起让它们吃好吃饱他们更关心狗粮猫粮的食品安全、生活舒适度、身心健康等。这也让宠物智能设备成为刚需产品,随着