鸿蒙实战开辟(HarmonyOS)列表项互换和删除场景案例

打印 上一主题 下一主题

主题 1029|帖子 1029|积分 3087

  鸿蒙NEXT开辟实战往期必看文章:
一分钟了解”纯血版!鸿蒙HarmonyOS Next应用开辟!
“非常详细的” 鸿蒙HarmonyOS Next应用开辟学习蹊径!(从零基础入门到醒目)
HarmonyOS NEXT应用开辟案例实践总联合(连续更新......)
HarmonyOS NEXT应用开辟性能优化实践总结(连续更新......)

介绍

本案例通过List组件、组合手势GestureGroup、swipeAction属性以及attributeModifier属性等实现了列表项的互换和删除。
效果图预览



利用说明

  • 进入页面,长按列表项,执行拖拽操作,当拖拽长度大于列表项所占高度一半的时候,列表项举行互换。
  • 列表项左滑,显示删除按钮,点击删除按钮,此列表项被删除。
下载安装

1.模块oh-package.json5文件中引入依赖。
  1. "dependencies": {
  2.   "listexchange": "har包地址"
  3. }
复制代码
2.ets文件import自界说视图实现列表视图。
  1. import { ListExchange } from 'listexchange';
复制代码
快速利用

本章节告急介绍了怎样快速上手自界说视图实现列表切换效果组件。

  • 设置列表项元素的类。开辟者可以根据自身业务列表项的需求场景自行变更或者拓展属性,但是需要自界说列表项元素视图。
  1. class ListInfo {
  2.   icon: ResourceStr = '';
  3.   name: ResourceStr = '';
  4.   constructor(icon: ResourceStr = '', name: ResourceStr = '') {
  5.     this.icon = icon;
  6.     this.name = name;
  7.    }
  8. }
复制代码

  • 数据准备。首先构建一个ListInfo类型的数组,然后向其中传入对应的内容数据。
  1. const MEMO_DATA: ListInfo[] = [
  2.       new ListInfo($r("app.media.list_exchange_ic_public_cards_filled"), '账户余额'),
  3.       new ListInfo($r("app.media.list_exchange_ic_public_cards_filled2"), 'xx银行储蓄卡(1234)'),
  4.       new ListInfo($r("app.media.list_exchange_ic_public_cards_filled3"), 'xx银行储蓄卡(1238)'),
  5.       new ListInfo($r("app.media.list_exchange_ic_public_cards_filled4"), 'xx银行储蓄卡(1236)')];
  6. @State appInfoList: ListInfo[] = MEMO_DATA;
复制代码

  • 声明管理列表项互换的类ListExchangeCtrl。通过ListExchangeCtrl来实现列表项与列表项之间的位置互换。详细实现请看后续的实现步骤章节。
  1. // 列表项交换类
  2. @State listExchangeCtrl: ListExchangeCtrl<ListInfo> = new ListExchangeCtrl();
复制代码

  • 自界说列表项组件。开辟者可以自界说列表项的UI。
  1. // 列表项数据信息
  2. @Builder deductionView(listItemInfo: ListInfo) {...}
复制代码

  • 构建自界说列表视图。在代码符合的位置利用ListExchange组件并传入对应的参数。
  1. /**
  2. * 列表交换视图
  3. * appInfoList: 数据源
  4. * listExchangeCtrl: 列表项交换类
  5. * deductionView: 自定义列表项元素视图
  6. */
  7. ListExchange({
  8.   appInfoList: this.appInfoList,
  9.   listExchangeCtrl: this.listExchangeCtrl,
  10.   deductionView: (listItemInfo: Object) => {
  11.     this.deductionView(listItemInfo as Listinfo)
  12.   }
  13. })
复制代码
属性(接口)说明

ListInfo类属性(开辟者可以自行拓展或者更改列表的属性元素)
属性类型释义默认值iconResourceStr列表图片-nameResourceStr列表名称- ListExchange组件属性
属性类型释义默认值appInfoListListInfo[]列表数据源-listExchangeCtrlListExchangeCtrl列表项元素互换类-deductionViewvoid自界说列表项元素视图- 实现思绪

首先创建一个数组modifier来添加自界说属性对象,根据组合手势GestureGroup来控制自界说属性的值并通过attributeModifier绑定自界说属性对象来动态加载属性。 然后通过swipeAction属性绑定删除组件,左滑显示此删除组件,点击实现列表项的删除。

  • 声明一个数组,添加自界说属性对象,每个自界说属性对象对应一个列表项,源码参考AttributeModifier.ets和ListExchangeCtrl.ets。
  1. initData(deductionData: Array<T>) {
  2.   this.deductionData = deductionData;
  3.   deductionData.forEach(() => {
  4.     this.modifier.push(new ListItemModifier());
  5.   })
  6. }
  7. /**
  8.   * 通过实现AttributeModifier接口,自定义属性修改器
  9.   * 将拖拽排序相关样式封装成属性修改器,可以方便移植
  10.   */
  11. export class ListItemModifier implements AttributeModifier<ListItemAttribute> {
  12.   // 阴影
  13.   public hasShadow: boolean = false;
  14.   // 缩放
  15.   public scale: number = 1;
  16.   // 纵轴偏移量
  17.   public offsetY: number = 0;
  18.   // 横轴偏移量
  19.   public offsetX: number = 0;
  20.   // 透明度
  21.   public opacity: number = 1;
  22.   // 是否被删除
  23.   public static getInstance(): ListItemModifier {
  24.     if (!ListItemModifier.instance) {
  25.       ListItemModifier.instance = new ListItemModifier();
  26.     }
  27.     return ListItemModifier.instance;
  28.   }
  29.   /**
  30.    * 定义组件普通状态时的样式
  31.    * @param instance: ListItem属性
  32.    */
  33.   applyNormalAttribute(instance: ListItemAttribute): void {
  34.     if (this.hasShadow) {
  35.       instance.shadow({ radius: $r('app.integer.list_exchange_shadow_radius'), color: $r('app.color.box_shadow') });
  36.       instance.zIndex(1);
  37.       instance.opacity(0.5);
  38.     } else {
  39.       instance.opacity(this.opacity);
  40.     }
  41.     instance.translate({ x: this.offsetX, y: this.offsetY });
  42.     instance.scale({ x: this.scale, y: this.scale });
  43.   }
  44. }
复制代码

  • 绑定attributeModifier属性以及组合手势GestureGroup,attributeModifier属性的值为对应的自界说属性对象。源码参考ListExchangeView.ets。
  1. // 列表区域
  2. List() {
  3.   ForEach(this.appInfoList, (item: Object) => {
  4.     ListItem() {
  5.       this.deductionView(item)
  6.     }
  7.     .zIndex(this.currentListItem === item ? 2 : 1) // 层级属性
  8.     .swipeAction({ end: this.defaultDeleteBuilder(item) }) // 用于设置ListItem的划出组件
  9.     .transition(TransitionEffect.OPACITY)
  10.     .attributeModifier(this.listExchangeCtrl.getModifier(item)) //动态设置组件的属性方法, 参数为属性修改器
  11.     .gesture(
  12.       // 以下组合手势为顺序识别,当长按手势事件未正常触发时,则不会出发拖动手势事件
  13.       GestureGroup(GestureMode.Sequence,
  14.         // 长按
  15.         LongPressGesture()
  16.           .onAction((event: GestureEvent) => {
  17.             this.currentListItem = item;
  18.             this.isLongPress = true;
  19.             this.listExchangeCtrl.onLongPress(item);
  20.           }),
  21.         // 拖动
  22.         PanGesture()
  23.           .onActionUpdate((event: GestureEvent) => {
  24.             this.listExchangeCtrl.onMove(item, event.offsetY);
  25.           })
  26.           .onActionEnd((event: GestureEvent) => {
  27.             this.listExchangeCtrl.onDrop(item);
  28.             this.isLongPress = false;
  29.           })
  30.       ).onCancel(() => {
  31.         if (!this.isLongPress) {
  32.           return;
  33.         }
  34.         this.listExchangeCtrl.onDrop(item);
  35.       }))
  36.   }, (item: Object) => JSON.stringify(item))
  37. }
  38. .divider({ strokeWidth: '1px', color: 0xeaf0ef })
  39. .scrollBar(BarState.Off)
  40. .border({
  41.   radius: {
  42.     bottomLeft: $r('app.string.ohos_id_corner_radius_default_l'),
  43.     bottomRight: $r('app.string.ohos_id_corner_radius_default_l')
  44.   }
  45. })
  46. .backgroundColor(Color.White)
  47. .width('100%')
复制代码

  • 长按列表项,通过LongPressGesture辨认长按手势,执行onLongPress函数方法更改此列表项的scale、shadow、zIndex和opacity等属性,并通过animateTo来实现动画效果,源码参考ListExchangeCtrl.ets。
  1. onLongPress(item: T) {
  2.    const index: number = this.deductionData.indexOf(item);
  3.    this.dragRefOffset = 0;
  4.    // TODO:知识点:长按当前列表项透明度和放大动画
  5.    animateTo({ curve: Curve.Friction, duration: ANIMATE_DURATION }, () => {
  6.      this.state = OperationStatus.PRESSING;
  7.      this.modifier[index].hasShadow = true;
  8.      this.modifier[index].scale = 1.04; // 放大比例为1.04
  9.    })
  10. }
复制代码

  • 互换列表项,通过PanGesture手势的onActionUpdate方法监听拖动的纵轴移动长度,然后执行onMove方法,根据移动长度的大小来判断是否执行列表项互换方法changeItem,源码参考ListExchangeCtrl.ets。
  1. onMove(item: T, offsetY: number) {
  2.    const index: number = this.deductionData.indexOf(item);
  3.    this.offsetY = offsetY - this.dragRefOffset;
  4.    this.modifier[index].offsetY = this.offsetY;
  5.    const direction: number = this.offsetY > 0 ? 1 : -1;
  6.    // 触发拖动时,被覆盖子组件缩小与恢复的动画
  7.    const curveValue: ICurve = curves.initCurve(Curve.Sharp);
  8.    const value: number = curveValue.interpolate(Math.abs(this.offsetY) / ITEM_HEIGHT);
  9.    const shrinkScale: number = 1 - value / 10; // 计算缩放比例,value值缩小10倍
  10.    if (index < this.modifier.length - 1) { // 当拖拽的时候,被交换的对象会缩放
  11.      this.modifier[index + 1].scale = direction > 0 ? shrinkScale : 1;
  12.    }
  13.    if (index > 0) {
  14.      this.modifier[index - 1].scale = direction > 0 ? 1 : shrinkScale;
  15.    }
  16.    // TODO:知识点:处理列表项的切换操作
  17.    if (Math.abs(this.offsetY) > ITEM_HEIGHT / 2) {
  18.      animateTo({ curve: Curve.Friction, duration: commonConstants.ANIMATE_DURATION }, () => {
  19.        this.offsetY -= direction * ITEM_HEIGHT;
  20.        this.dragRefOffset += direction * ITEM_HEIGHT;
  21.        this.modifier[index].offsetY = this.offsetY;
  22.        this.changeItem(index, index + direction);
  23.      })
  24.    }
  25. }
  26. changeItem(index: number, newIndex: number): void {
  27.    const tmp: Array<T> = this.deductionData.splice(index, 1);
  28.    this.deductionData.splice(newIndex, 0, tmp[0]);
  29.    const tmp2: Array<ListItemModifier> = this.modifier.splice(index, 1);
  30.    this.modifier.splice(newIndex, 0, tmp2[0]);
  31. }
复制代码

  • 通过swipeAction属性绑定删除按钮组件,列表项左滑显示删除组件,点击删除按钮,列表项删除。源码参考ListExchangeCtrl.ets。
  1. deleteItem(item: T): void {
  2.   const index = this.deductionData.indexOf(item);
  3.   this.dragRefOffset = 0;
  4.   // TODO:知识点:左偏移以及透明度动画
  5.   animateTo({
  6.     curve: Curve.Friction, onFinish: () => {
  7.       // TODO:知识点:列表项删除动画
  8.       animateTo({
  9.         curve: Curve.Friction, onFinish: () => {
  10.           this.state = OperationStatus.IDLE;
  11.         }
  12.       }, () => {
  13.         this.modifier.splice(index, 1);
  14.         this.deductionData.splice(index, 1);
  15.       })
  16.     }
  17.   }, () => {
  18.     this.state = OperationStatus.DELETE;
  19.     this.modifier[index].offsetX = 150; // 列表项左偏移150
  20.     this.modifier[index].opacity = 0; // 列表项透明度为0
  21.   })
  22. }
复制代码
高性能知识点

不涉及。
工程结构&模块类型

  1. listexchange                                 // har类型
  2. |---common
  3. |   |---commonConstants.ets                  // 常量
  4. |---model
  5. |   |---AttributeModifier.ets                // 属性对象
  6. |   |---ListExchangeCtrl.ets                 // 列表项交换
  7. |   |---ListInfo.ets                         // 列表项信息
  8. |   |---MockData.ets                         // 模拟数据
  9. |---util
  10. |   |---ListExchange.ets                     // 自定义列表视图
  11. |   |---Logger.ets                           // 日志
  12. |---view
  13. |   |---ListExchangeView.ets                 // 视图层-应用主页面
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

麻花痒

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表