本案例通过List组件、组合手势GestureGroup、swipeAction属性以及attributeModifier属性等实现了列表项的交换和删除。
效果图预览
使用说明:
- 进入页面,长按列表项,执行拖拽操作,当拖拽长度大于列表项所占高度一半的时候,列表项进行交换。
- 列表项左滑,显示删除按钮,点击删除按钮,此列表项被删除。
实现思路
首先创建一个数组modifier来添加自定义属性对象,根据组合手势GestureGroup来控制自定义属性的值并通过attributeModifier绑定自定义属性对象来动态加载属性。 然后通过swipeAction属性绑定删除组件,左滑显示此删除组件,点击实现列表项的删除。
- 声明一个数组,添加自定义属性对象,每个自定义属性对象对应一个列表项,源码参考AttributeModifier.ets和ListExchangeCtrl.ets。
constructor(deductionData: Array<T>) {
this.deductionData = deductionData;
this.modifier = new Array<ListItemModifier>();
deductionData.forEach(() => {
this.modifier.push(new ListItemModifier());
})
}
/**
* 通过实现AttributeModifier接口,自定义属性修改器
* 将拖拽排序相关样式封装成属性修改器,可以方便移植
*/
export class ListItemModifier implements AttributeModifier<ListItemAttribute> {
public hasShadow: boolean = false;
public isDeleted: boolean = false;
public scale: number = 1;
public offsetY: number = 0;
public offsetX: number = 0;
public opacity: number = 1;
public static instance: ListItemModifier | null = null;
public static getInstance(): ListItemModifier {
if (!ListItemModifier.instance) {
ListItemModifier.instance = new ListItemModifier();
}
return ListItemModifier.instance;
}
/**
* 定义组件普通状态时的样式
* @param instance
*/
applyNormalAttribute(instance: ListItemAttribute): void {
if (this.hasShadow) {
instance.shadow({ radius: $r('app.integer.list_exchange_shadow_radius'), color: $r('app.color.box_shadow') });
instance.zIndex(1);
instance.opacity(0.5);
} else {
instance.opacity(this.opacity);
}
instance.translate({ x: this.offsetX, y: this.offsetY });
instance.scale({ x: this.scale, y: this.scale });
}
}
- 绑定attributeModifier属性以及组合手势GestureGroup,attributeModifier属性的值为对应的自定义属性对象。源码参考ListExchangeView.ets。
List() {
ForEach(this.appInfoList, (item: AppInfo) => {
ListItem() {
DeductionView({ app: item })
}
.zIndex(this.currentData === item ? 2 : 1)
.swipeAction({ end: this.deleteBuilder(item) })
.transition(TransitionEffect.OPACITY)
.attributeModifier(this.listExchangeCtrl.getModifier(item)) //动态设置组件的属性方法, 参数为属性修改器
.gesture(
// 以下组合手势为顺序识别,当长按手势事件未正常触发时,则不会出发拖动手势事件
GestureGroup(GestureMode.Sequence,
LongPressGesture()
.onAction((event: GestureEvent) => {
this.currentData = item;
this.isLongPress = true;
this.listExchangeCtrl.onLongPress(item);
}),
PanGesture()
.onActionUpdate((event: GestureEvent) => {
this.listExchangeCtrl.onMove(item, event.offsetY);
})
.onActionEnd((event: GestureEvent) => {
this.listExchangeCtrl.onDrop(item);
})
).onCancel(() => {
if (!this.isLongPress) {
return;
}
this.listExchangeCtrl.onDrop(item);
})
)
}, (item: AppInfo) => JSON.stringify(item))
}
.divider({ strokeWidth: '1px', color: 0xeaf0ef })
.scrollBar(BarState.Off)
.padding({ left: 10, right: 10 })
.backgroundColor(Color.White)
.width('100%')
- 长按列表项,通过LongPressGesture识别长按手势,执行onLongPress函数方法更改此列表项的scale、shadow、zIndex和opacity等属性,并通过animateTo来实现动画效果,源码参考ListExchangeCtrl.ets。
onLongPress(item: T) {
const index: number = this.deductionData.indexOf(item);
this.dragRefOffset = 0;
// TODO:知识点:长按当前列表项透明度和放大动画
animateTo({ curve: Curve.Friction, duration: ANIMATE_DURATION }, () => {
this.state = OperationStatus.PRESSING;
this.modifier[index].hasShadow = true;
this.modifier[index].scale = 1.04; // 放大比例为1.04
})
}
- 交换列表项,通过PanGesture手势的onActionUpdate方法监听拖动的纵轴移动长度,然后执行onMove方法,根据移动长度的大小来判断是否执行列表项交换方法changeItem,源码参考ListExchangeCtrl.ets。
onMove(item: T, offsetY: number) {
const index: number = this.deductionData.indexOf(item);
this.offsetY = offsetY - this.dragRefOffset;
this.modifier[index].offsetY = this.offsetY;
const direction: number = this.offsetY > 0 ? 1 : -1;
// 触发拖动时,被覆盖子组件缩小与恢复的动画
const curveValue: ICurve = curves.initCurve(Curve.Sharp);
const value: number = curveValue.interpolate(Math.abs(this.offsetY) / ITEM_HEIGHT);
const shrinkScale: number = 1 - value / 10; // 计算缩放比例,value值缩小10倍
if (index < this.modifier.length - 1) { // 当拖拽的时候,被交换的对象会缩放
this.modifier[index + 1].scale = direction > 0 ? shrinkScale : 1;
}
if (index > 0) {
this.modifier[index - 1].scale = direction > 0 ? 1 : shrinkScale;
}
// TODO:知识点:处理列表项的切换操作
if (Math.abs(this.offsetY) > ITEM_HEIGHT / 2) {
animateTo({ curve: Curve.Friction, duration: ANIMATE_DURATION }, () => {
this.offsetY -= direction * ITEM_HEIGHT;
this.dragRefOffset += direction * ITEM_HEIGHT;
this.modifier[index].offsetY = this.offsetY;
this.changeItem(index, index + direction);
})
}
}
changeItem(index: number, newIndex: number): void {
const tmp: Array<T> = this.deductionData.splice(index, 1);
this.deductionData.splice(newIndex, 0, tmp[0]);
const tmp2: Array<ListItemModifier> = this.modifier.splice(index, 1);
this.modifier.splice(newIndex, 0, tmp2[0]);
}
- 通过swipeAction属性绑定删除按钮组件,列表项左滑显示删除组件,点击删除按钮,列表项删除。源码参考ListExchangeCtrl.ets。
deleteItem(item: T): void {
const index = this.deductionData.indexOf(item);
this.dragRefOffset = 0;
// TODO:知识点:左偏移以及透明度动画
animateTo({
curve: Curve.Friction, onFinish: () => {
// TODO:知识点:列表项删除动画
animateTo({
curve: Curve.Friction, onFinish: () => {
this.state = OperationStatus.IDLE;
}
}, () => {
this.modifier.splice(index, 1);
this.deductionData.splice(index, 1);
})
}
}, () => {
this.state = OperationStatus.DELETE;
this.modifier[index].offsetX = 150; // 列表项左偏移150
this.modifier[index].opacity = 0; // 列表项透明度为0
})
}
高性能知识点
不涉及。
工程结构&模块类型
listexchange // har类型
|---model
| |---ListInfo.ets // 列表项信息
| |---AttributeModifier.ets // 属性对象
| |---ListExchangeCtrl.ets // 列表项交换
| |---MockData.ets // 模拟数据
|---view
| |---ListExchangeView.ets // 视图层-应用主页面
模块依赖
本实例依赖common模块来实现日志的打印、资源 的调用、依赖动态路由模块来实现页面的动态加载。
最后
有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。
这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。
希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!
如果你是一名有经验的资深Android移动开发、Java开发、前端开发、对鸿蒙感兴趣以及转行人员,可以直接领取这份资料
获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
鸿蒙(HarmonyOS NEXT)最新学习路线
-
HarmonOS基础技能
- HarmonOS就业必备技能
- HarmonOS多媒体技术
- 鸿蒙NaPi组件进阶
- HarmonOS高级技能
- 初识HarmonOS内核
- 实战就业级设备开发
有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
《鸿蒙 (OpenHarmony)开发入门教学视频》
《鸿蒙生态应用开发V2.0白皮书》
《鸿蒙 (OpenHarmony)开发基础到实战手册》
OpenHarmony北向、南向开发环境搭建
《鸿蒙开发基础》
- ArkTS语言
- 安装DevEco Studio
- 运用你的第一个ArkTS应用
- ArkUI声明式UI开发
- .……
《鸿蒙开发进阶》
- Stage模型入门
- 网络管理
- 数据管理
- 电话服务
- 分布式应用开发
- 通知与窗口管理
- 多媒体技术
- 安全技能
- 任务管理
- WebGL
- 国际化开发
- 应用测试
- DFX面向未来设计
- 鸿蒙系统移植和裁剪定制
- ……
《鸿蒙进阶实战》
- ArkTS实践
- UIAbility应用
- 网络案例
- ……
获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料
总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。