HarmonyOS鸿蒙Next中List组件拖拽时动画属性不生效如何解决
HarmonyOS鸿蒙Next中List组件拖拽时动画属性不生效如何解决
【问题现象】
在List列表里进行组件拖拽交换,组件交换过程中想实现动画效果,但是设置的supportAnimation动画属性没生效。
【背景知识】
- 拖拽事件指组件被长按后拖拽时触发的事件。
- 组合手势由多种单一手势组合而成,通过在GestureGroup中使用不同GestureMod来声明该组合手势的类型,支持连续识别、并行识别和互斥识别三种类型。
- 手势处理分为:长按、单击、双击、多次点击、拖动、捏合、旋转、滑动。
- Grid支持supportAnimation动画,List组件不支持,可以通过animateTo显式动画接口来指定由于闭包代码导致的状态变化插入过渡动效。
- attributeModifier:动态设置组件的属性,支持开发者在属性设置时使用if/else语法,且根据需要使用多态样式设置属性。
【定位思路】
参考官方文档,比对Grid和List属性(结果如下表)可知,Grid支持supportAnimation动画,List组件不支持,也无相关属性可替代,需要结合拖拽事件、组合手势、动画效果来进行原生开发。
参数名 | List是否支持 | Grid是否支持 | 参数描述 |
---|---|---|---|
supportAnimation | × | √ | 是否支持动画。当前支持GridItem拖拽动画。默认值:false |
【解决方案】
顺序识别手势触发List组件拖拽交换,利用[@ohos.curves](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-curve)动画插值曲线功能,在List组件拖拽交换过程中绑定动画效果,具体实现步骤如下:
-
导入动画插值曲线功能模块,对当前拖拽块的阴影设置以及偏移量应用。代码示例如下:
import curves from '[@ohos](/user/ohos).curves'; class ListItemModify implements AttributeModifier<ListItemAttribute> { public hasShadow: boolean = false public scale: number = 1 public offsetY: number = 0 applyNormalAttribute(instance: ListItemAttribute): void { if (this.hasShadow) { instance.shadow({ radius: 70, color: '# 15000000', offsetX: 0, offsetY: 0 }) instance.zIndex(1) } instance.scale({ x: this.scale, y: this.scale }) instance.translate({ y: this.offsetY }) } } enum DragSortState { IDLE, PRESSING, MOVING, DROPPING, }
-
组件拖拽交换过程中绑定动画效果。代码示例如下:
// 与下/上一个元素交换,offsetY(实时偏移量)需要 减/加 ITEM_INTV(交换偏移量) // 与下/上一个元素交换,dragRefOffset(初始偏移值) 按照交换后的位置重新计算,加/减 ITEM_INTV(交换偏移量) // 向下/上拖动距离超过一半时,与下/上一个元素交换位置 if (this.offsetY > this.ITEM_INTV / 2) { animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => { this.offsetY -= this.ITEM_INTV this.dragRefOffset += this.ITEM_INTV this.modify[index].offsetY = this.offsetY this.itemMove(index, index + 1) }) } else if (this.offsetY < -this.ITEM_INTV / 2) { animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => { this.offsetY += this.ITEM_INTV this.dragRefOffset -= this.ITEM_INTV this.modify[index].offsetY = this.offsetY this.itemMove(index, index - 1) }) }
-
顺序识别手势触发组件拖拽交换,代码示例如下:
[@Entry](/user/Entry) [@Component](/user/Component) struct ListItemExample { @State private arr: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] @State dragSortCtrl: DragSortCtrl<number> = new DragSortCtrl<number>(this.arr, 120) build() { Stack() { List({ space: 20, initialIndex: 0 }) { ForEach(this.arr, (item: number) => { ListItem() { Text('' + item) .width('100%') .height(100) .fontSize(16) .textAlign(TextAlign.Center) .backgroundColor(0xFFFFFF) } .clip(true) .attributeModifier(this.dragSortCtrl.getModify(item)) .borderRadius(10) .margin({ left: 12, right: 12 }) .gesture( // 以下组合手势为顺序识别,当长按手势事件未正常触发时则不会触发拖动手势事件 // Sequence代表顺序识别,按照手势的注册顺序识别手势,直到所有手势识别成功。 GestureGroup(GestureMode.Sequence, // 用于触发长按手势事件,触发长按手势的最少手指数为1,最短长按时间为500毫秒 LongPressGesture({ repeat: false }) .onAction((event?: GestureEvent) => { this.dragSortCtrl.onLongPress(item) }), // 用于触发拖动手势事件,滑动的最小距离为5vp时拖动手势识别成功。 PanGesture({ fingers: 1, direction: null, distance: 0 }) .onActionUpdate((event: GestureEvent) => { this.dragSortCtrl.onMove(item, event.offsetY) }) .onActionEnd((event: GestureEvent) => { this.dragSortCtrl.onDrop(item) }) ) .onCancel(() => { this.dragSortCtrl.onDrop(item) }) ) }, (item: number) => item.toString()) } }.width('100%').height('100%').backgroundColor(0xDCDCDC).padding({ top: 5 }) } }
【总结】
对列表的列表项进行拖动时,实现其他列表项自动补位和动态排列的效果可采用如下方法:
- 为列表或宫格项(item)添加拖拽能力,使能 draggable,并注册onDragStart;
- 在 onDragStart 回调中将所拖条目设置visibility为 HIDDEN 状态;
- 在列表或宫格项(item)上注册 onDragMove 监听拖起的移动事件;
- 拖动过程中,通过 onDragMove 的 event 参数获取到拖拽跟手点坐标;
- 计算跟手点坐标与item中线的距离关系,当重合时,启动挤位动效;
- Item布局信息可通过 componentUtils API获取到;
- 挤位动效通过animateTo来改变datasource里的index,触发list的排序动效;
- 落位动效可通过自定义动效完成。
更多关于HarmonyOS鸿蒙Next中List组件拖拽时动画属性不生效如何解决的实战教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于HarmonyOS鸿蒙Next中List组件拖拽时动画属性不生效如何解决的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
List组件不支持supportAnimation
动画属性
List组件不支持supportAnimation
动画属性,需通过animateTo
显式动画接口实现拖拽动画效果。步骤如下:
- 导入动画插值曲线模块,设置拖拽块的阴影和偏移量。
- 在拖拽交换过程中绑定动画效果,使用
animateTo
接口。 - 通过顺序识别手势触发拖拽交换,使用
GestureGroup
和LongPressGesture
、PanGesture
组合手势。
代码示例
import curves from '@ohos.curves';
class ListItemModify implements AttributeModifier<ListItemAttribute> {
public hasShadow: boolean = false;
public scale: number = 1;
public offsetY: number = 0;
applyNormalAttribute(instance: ListItemAttribute): void {
if (this.hasShadow) {
instance.shadow({ radius: 70, color: '#15000000', offsetX: 0, offsetY: 0 });
instance.zIndex(1);
}
instance.scale({ x: this.scale, y: this.scale });
instance.translate({ y: this.offsetY });
}
}
enum DragSortState {
IDLE,
PRESSING,
MOVING,
DROPPING,
}
if (this.offsetY > this.ITEM_INTV / 2) {
animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
this.offsetY -= this.ITEM_INTV;
this.dragRefOffset += this.ITEM_INTV;
this.modify[index].offsetY = this.offsetY;
this.itemMove(index, index + 1);
});
} else if (this.offsetY < -this.ITEM_INTV / 2) {
animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
this.offsetY += this.ITEM_INTV;
this.dragRefOffset -= this.ITEM_INTV;
this.modify[index].offsetY = this.offsetY;
this.itemMove(index, index - 1);
});
}
@Entry
@Component
struct ListItemExample {
@State private arr: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
@State dragSortCtrl: DragSortCtrl<number> = new DragSortCtrl<number>(this.arr, 120);
build() {
Stack() {
List({ space: 20, initialIndex: 0 }) {
ForEach(this.arr, (item: number) => {
ListItem() {
Text('' + item)
.width('100%')
.height(100)
.fontSize(16)
.textAlign(TextAlign.Center)
.backgroundColor(0xFFFFFF);
}
.clip(true)
.attributeModifier(this.dragSortCtrl.getModify(item))
.borderRadius(10)
.margin({ left: 12, right: 12 })
.gesture(
GestureGroup(GestureMode.Sequence,
LongPressGesture({ repeat: false })
.onAction((event?: GestureEvent) => {
this.dragSortCtrl.onLongPress(item);
}),
PanGesture({ fingers: 1, direction: null, distance: 0 })
.onActionUpdate((event: GestureEvent) => {
this.dragSortCtrl.onMove(item, event.offsetY);
})
.onActionEnd((event: GestureEvent) => {
this.dragSortCtrl.onDrop(item);
})
)
.onCancel(() => {
this.dragSortCtrl.onDrop(item);
})
);
}, (item: number) => item.toString());
}
}.width('100%').height('100%').backgroundColor(0xDCDCDC).padding({ top: 5 });
}
}