鸿蒙开发: 案例集合List:ListItem侧滑(删除、收藏)

🎯 案例集合List:ListItem侧滑(删除、收藏)

🌍 案例集合List

🚪 最近开启了学员班级点我欢迎加入(学习相关知识可获得定制礼盒)

🏷️ 效果图

🏷 原生

🏷 自定义

📖 参考

🧩 拆解

  • 原生ListItem组件自带侧滑
javascript 复制代码
/**
 * 用户类
 */
@ObservedV2
class UserInfo {
  @Trace label: string = ''
  @Trace collect: boolean = false
  id: number = 0

  changeCollect() {
    this.collect = !this.collect
  }
}

/**
 * 模拟数据
 */
const listData: string[] = ['购物', '体育', '服装', '军事', '电商', '娱乐', '科技']

@ComponentV2
export struct ListSlideDelete {
  /**
   * 模拟数据
   */
  @Local mockData: UserInfo[] = []

  aboutToAppear(): void {
    this.InitData()
  }

  /**
   * 初始化数据
   */
  InitData() {
    for (let i = 0; i < listData.length; i++) {
      let myInfo: UserInfo = new UserInfo()
      myInfo.label = listData[i]
      myInfo.collect = false
      myInfo.id = i
      this.mockData.push(myInfo)
    }
  }

  /**
   * 删除列表数据项
   * @param idx
   */
  delListItem(idx: number) {
    this.mockData.splice(idx, 1)
  }

  @Builder
  listItemEnd(item: UserInfo, idx: number) {
    Row({ space: 5 }) {
      Text('收藏')
        .width(50)
        .aspectRatio(1)
        .fontSize(20)
        .fontWeight(FontWeight.Regular)
        .borderRadius(50)
        .backgroundColor(item.collect ? Color.Orange : Color.Gray)
        .textAlign(TextAlign.Center)
        .onClick(() => item.changeCollect())
      Text('删除')
        .width(50)
        .aspectRatio(1)
        .fontSize(20)
        .fontWeight(FontWeight.Regular)
        .borderRadius(50)
        .backgroundColor('#80ff8b8b')
        .textAlign(TextAlign.Center)
        .onClick(() => this.delListItem(idx))
    }
    .padding(5)
    .justifyContent(FlexAlign.Center)
  }

  build() {
    Column() {
      List({ space: 20 }) {
        ForEach(this.mockData, (item: UserInfo, idx: number) => {
          ListItem() {
            Text(item.label)
              .width('100%')
              .height(100)
              .fontSize(20)
              .borderRadius(20)
              .textAlign(TextAlign.Center)
              .backgroundColor('#4dfdddcb')
          }
          .swipeAction(
            {
              end: {
                builder: () => this.listItemEnd(item, idx),
                // onAction: () => this.delListItem(idx), TODO:放开:侧滑到最左侧也可删除
              }
            }
          )
        }, (item: UserInfo) => item.id.toString())
      }
      .layoutWeight(1)
      .scrollBar(BarState.Off) // 关闭滚动条
      .edgeEffect(EdgeEffect.None) // 关闭滚动动效
    }
    .width('100%')
    .height('100%')
    .padding(15)
  }
}
  • 自定义ListItem组件自带侧滑
javascript 复制代码
/**
 * 用户类
 */
@ObservedV2
class UserInfo {
  @Trace label: string = ''
  @Trace collect: boolean = false
  @Trace translate: number = 0
  id: number = 0

  changeCollect() {
    this.collect = !this.collect
  }
}

/**
 * 模拟数据
 */
const listData: string[] = ['购物', '体育', '服装', '军事', '电商', '娱乐', '科技']

@ComponentV2
export struct CustomListSlideDelete {
  /**
   * 模拟数据
   */
  @Local mockData: UserInfo[] = []
  /**
   * 最大滑动距离(listItemEnd组件宽度)
   */
  private maxTranslate: number = -110
  /**
   * 滑动距离的一半
   */
  private halfOfTheDistance: number = -65
  /**
   * 最小滑动距离
   */
  private defTranslate: number = 0

  aboutToAppear(): void {
    this.InitData()
  }

  /**
   * 初始化数据
   */
  InitData() {
    for (let i = 0; i < listData.length; i++) {
      let myInfo: UserInfo = new UserInfo()
      myInfo.label = listData[i]
      myInfo.collect = false
      myInfo.id = i
      myInfo.translate = 0
      this.mockData.push(myInfo)
    }
  }
  @Builder
  listItemEnd(item: UserInfo, idx: number) {
    Row({ space: 5 }) {
      Text('收藏')
        .width(50)
        .aspectRatio(1)
        .fontSize(20)
        .fontWeight(FontWeight.Regular)
        .borderRadius(50)
        .backgroundColor(item.collect ? Color.Orange : Color.Gray)
        .textAlign(TextAlign.Center)
        .onClick(() => item.changeCollect())
      Text('删除')
        .width(50)
        .aspectRatio(1)
        .fontSize(20)
        .fontWeight(FontWeight.Regular)
        .borderRadius(50)
        .backgroundColor('#80ff8b8b')
        .textAlign(TextAlign.Center)
        .onClick(() =>  this.mockData.splice(idx, 1))
    }
    .padding({ left: 5 })
    .width(110)
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  build() {
    Column() {
      List({ space: 20 }) {
        ForEach(this.mockData, (item: UserInfo, idx: number) => {
          ListItem() {
            Row() {
              Row() {
                Text(item.label)
                  .fontSize(20)
              }
              .width('100%')
              .height('100%')
              .justifyContent(FlexAlign.Center)

              this.listItemEnd(item, idx)
            }
            .width('100%')
            .height(100)
            .borderRadius(20)
            .backgroundColor('#4dfdddcb')
            .translate({ x: item.translate })
            .animation({ duration: 200, curve: Curve.EaseOut })
            // 支持手动滑动手势
            .gesture(
              PanGesture({ direction: PanDirection.Horizontal })
                .onActionUpdate((event: GestureEvent) => {
                  // 左负数, 右正数字
                  const offsetX = event.offsetX
                  // 当前listItem的滑动
                  const curTranslate = item.translate
                  // 默认滑动值
                  const defTranslate = this.defTranslate
                  // 最大滑动值
                  const maxTranslate = this.maxTranslate

                  // 规避左右滑动的时候符合最小 最大滑动值,其余情况不滑动
                  if (curTranslate === defTranslate && offsetX > 0 || curTranslate === maxTranslate && offsetX < 0) {
                    return
                  }

                  // 只允许在0 ~ -110滑动
                  if (curTranslate <= defTranslate && curTranslate >= maxTranslate) {
                    // 小于0 认为在向左滑动
                    if (offsetX < 0) {
                      if (-(Math.abs(curTranslate) + Math.abs(event.offsetX)) <= -110) {
                        // 小于-110的时候给-110
                        this.mockData[idx].translate = maxTranslate
                        return
                      }
                      this.mockData[idx].translate = -(Math.abs(curTranslate) + Math.abs(event.offsetX))
                    }
                    // 认为在向右滑动
                    else {
                      if (curTranslate + event.offsetX >= 0) {
                        // 大于0的时候给0
                        this.mockData[idx].translate = defTranslate
                        return
                      }
                      this.mockData[idx].translate = curTranslate + event.offsetX
                    }
                  }
                })
                .onActionEnd(() => {
                  // 滑动结束后自动调整状态(超过一半则完全展开,否则收起)
                  if (this.mockData[idx].translate < this.halfOfTheDistance) {
                    this.mockData[idx].translate = this.maxTranslate
                    return
                  }
                  this.mockData[idx].translate = this.defTranslate
                })
            )
          }
        }, (item: UserInfo) => item.id.toString())
      }
      .layoutWeight(1)
      .scrollBar(BarState.Off) // 关闭滚动条
      .edgeEffect(EdgeEffect.None) // 关闭滚动动效
    }
    .width('100%')
    .height('100%')
    .padding(15)
  }
}

🌸🌼🌺

相关推荐
春卷同学1 小时前
拼图游戏 - Electron for 鸿蒙PC项目实战案例
javascript·electron·harmonyos
盐焗西兰花1 小时前
鸿蒙学习实战之路 - 轮播图组件实现
学习·华为·harmonyos
ChinaDragon1 小时前
HarmonyOS:转场动画
harmonyos
ChinaDragon2 小时前
HarmonyOS:转场动画-模态转场
harmonyos
青云交2 小时前
ShellCheck命令行工具适配开源鸿蒙PC实战指南
华为·开源·makefile·harmonyos·shellcheck·预编译二进制·hnp 打包
RisunJan2 小时前
【HarmonyOS】鸿蒙开发语言的选择
开发语言·华为·harmonyos
春卷同学2 小时前
Electron for 鸿蒙PC开发的经典推箱子游戏
游戏·electron·harmonyos
春卷同学3 小时前
电子蛇对战 - Electron for 鸿蒙PC项目实战案例
javascript·electron·harmonyos
想你依然心痛3 小时前
[鸿蒙2025领航者闯关]从「单端」到「多端共生」:星盾安全架构下的金融 APP 重构实录
金融·harmonyos·安全架构·鸿蒙2025领航者闯关·鸿蒙6实战·开发者年度总结