ArkUI List 拖动排序最佳实践
在 ArkUI 中实现列表拖动排序,主要依赖 List 组件的 onMove 回调。结合 @Local 的响应式更新,几行代码就能实现功能。
数据层
typescript
@Local imageUris: Array<string> = [];
渲染层
typescript
List() {
ForEach(this.imageUris, (uri: string, index: number) => {
ListItem() {
Stack() {
Image(uri)
.width('100%')
.height(150)
.objectFit(ImageFit.Cover)
.borderRadius(10)
Stack()
.width('100%')
.height(150)
}
}
.margin(10)
.borderRadius(10)
.backgroundColor('#FFFFFFFF')
}, (uri: string) => uri)
.onMove((from: number, to: number) => {
let tmp = this.imageUris.splice(from, 1);
this.imageUris.splice(to, 0, tmp[0]);
})
}
onMove 回调
onMove 是整个拖动排序的核心。当手指长按某个 ListItem 并拖动到新位置时,系统会触发这个回调,并传入两个索引:被拖动项的原位置 from,和目标位置 to。
这里的实现利用了数组的 splice 方法两步走:
splice(from, 1)--- 从原位置切出被拖动的元素splice(to, 0, ...)--- 将其插入到目标位置
由于数组引用没有变化(splice 是 in-place 操作),@Local 的响应式驱动需要依赖 ArkUI 的代理对象机制。在 ForEach 中使用稳定的 key 配合 splice 操作,框架能正确追踪数组变化并触发最小粒度的 UI 更新。
如果需要保留排序结果到持久化存储,比如 UserInfo 或数据库,可以在 onMove 回调末尾追加对应的保存逻辑。
图片选择
配合系统 PhotoViewPicker,最多选 20 张:
typescript
async selectImages(): Promise<void> {
const photoPicker = new photoAccessHelper.PhotoViewPicker();
const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
photoSelectOptions.maxSelectNumber = 20;
const photoSelectResult = await photoPicker.select(photoSelectOptions);
if (photoSelectResult?.photoUris?.length > 0) {
this.imageUris = [...this.imageUris, ...photoSelectResult.photoUris];
}
}
组件层级
scss
List (onMove 接收拖动事件)
├── ListItem (长按触发拖动)
│ └── Stack (布局容器)
│ ├── Image (图片展示)
│ └── Stack (透明覆盖层,透传手势)
└── ListItem ...
注意事项
- 透明覆盖层 --- 当 ListItem 内只包含 Image 时,Image 组件会优先处理手势,导致
onMove无法触发。在 Image 上方添加一个透明 Stack 铺满父容器尺寸,将手势透传给 ListItem。Stack 默认裁剪超出内容,圆角设置不受影响。 - ForEach 需要稳定的 key --- 使用
(uri: string) => uri作为唯一标识,避免排序时列表项渲染错乱。