鸿蒙开发:案例集合List:多列表相互拖拽(删除/插入,偏移动效)(微暇)

🎯 案例集合List:多列表相互拖拽(删除/插入,偏移动效)(微暇)

🌍 案例集合List

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

🏷️ 效果图

已实现

  • 不同列表交换
  • 同列表交换
  • 组件边缘滑动 (原生自带)
  • 组件检测偏移动效
  • 拖出/插入过渡动画

📖 参考

🧩 拆解

javascript 复制代码
import { curves } from "@kit.ArkUI"

/**
 * 组件在拖拽中需要用到的额外信息
 */
interface extraParamsObj {
  insertIndex: number // 插入项下标
  selectedIndex: number // 选中项下标
}

/**
 * mockData
 */
class ItemData {
  id: number = 0
  content: string = ''
}

/**
 * 组件标识
 */
enum CompType {
  LeftList = 'LeftList',
  RightList = 'RightList',
}

@Component
export struct MultipleListsForMutualDelAndIns {
  /**
   * mock数据1
   */
  @State leftList: ItemData[] =
    [
      { id: 1, content: "A" },
      { id: 2, content: "B" },
      { id: 3, content: "C" },
      { id: 4, content: "D" },
      { id: 5, content: "E" },
      { id: 6, content: "F" },
      { id: 111, content: "" },// 用于拖拽到底部无法位移
    ]
  /**
   * mock数据2
   */
  @State rightList: ItemData[] =
    [
      { id: 7, content: "G" },
      { id: 8, content: "H" },
      { id: 9, content: "I" },
      { id: 10, content: "j" },
      { id: 11, content: "K" },
      { id: 12, content: "L" },
      { id: 222, content: "" },// 用于拖拽到底部无法位移
    ]
  /**
   * 当前悬停在那个组件上方的下标
   */
  @State hoverIdx: number = -1
  /**
   * 当前悬停在那个组件上方
   */
  @State hoverComp: CompType | undefined = undefined
  /**
   * 拖拽的item
   */
  @State scaleItem: ItemData = new ItemData()

  /**
   * 拖拽项视图
   * @param item
   */
  @Builder
  private dragBuilder(item: ItemData) {
    Text(item.content)
      .width(150)
      .height(50)
      .fontSize(20)
      .fontWeight(FontWeight.Bold)
      .backgroundColor(Color.Orange)
      .textAlign(TextAlign.Center)
      .borderRadius(20)
      .scale({ x: 1.15, y: 1.15 })
  }

  /**
   * 拖拽开始统一方法
   * @param compType
   * @param extraParams
   */
  private itemDragAndDropToStart(compType: CompType, idx: number, item: ItemData) {
    this.scaleItem = item
    this.hoverComp = compType
    this.hoverIdx = idx
    this.listDataType(compType).splice(idx, 1)
  }

  /**
   * 拖拽释放统一方法
   * @param compType
   * @param extraParams
   */
  private itemDragAndDropEnds(compType: CompType) {
    this.listDataType(compType).splice(this.hoverIdx, 0, this.scaleItem)

    // 重置当前悬停在那个组件上方的下标
    this.hoverIdx = -1
    // 重置组件标识
    this.hoverComp = undefined
  }

  /**
   * 悬停组件过渡
   * @param compType
   * @param extraParams
   */
  private itemDragMove(compType: CompType, extraParams: string) {
    this.hoverComp = compType
    this.hoverIdx = (JSON.parse(extraParams) as extraParamsObj)?.insertIndex

    this.getUIContext()
      .getPromptAction()
      .showToast({ message: `当前组件:${this.hoverComp}---准备替换的目标下标${this.hoverIdx}` })
  }

  /**
   * 数据类型
   * @param dragType
   * @returns
   */
  private listDataType(dragType: CompType) {
    switch (dragType) {
      case CompType.LeftList:
        return this.leftList

      case CompType.RightList:
        return this.rightList
    }
  }

  build() {
    Row({ space: 10 }) {
      // 左列表
      List({ space: 10 }) {
        ForEach(this.leftList, (item: ItemData, idx: number) => {
          if (this.leftList.length - 1 !== idx) {
            ListItem() {
              ListItemCase({
                item,
                itemIdx: idx,
                hoverIdx: this.hoverIdx,
                defCompType: CompType.LeftList,
                curCompType: this.hoverComp,
              })
            }
            .onDragStart((event: DragEvent, extraParams: string) => {
              this.itemDragAndDropToStart(CompType.LeftList, idx, item)
              return this.dragBuilder(item)
            })
          } else {
            // 处理拖拽到最后一个元素无法换位置
            ListItem()
              .margin({
                top: this.leftList.length - 1 === this.hoverIdx && this.hoverComp === CompType.LeftList ? 60 : 0
              })
              .animation({ curve: curves.interpolatingSpring(0, 1, 400, 38), duration: 100 })
          }
        }, (item: ItemData) => item.id.toString())
      }
      .onDragMove((event: DragEvent, extraParams: string) => {
        this.itemDragMove(CompType.LeftList, extraParams)
      })
      .onDrop((event: DragEvent, extraParams: string) => {
        this.itemDragAndDropEnds(CompType.LeftList)
        event.useCustomDropAnimation = true // 落入没有动效
      })
      .width(180)
      .height(250)
      .scrollBar(BarState.On)
      .edgeEffect(EdgeEffect.None)

      // 右列表
      List({ space: 10 }) {
        ForEach(this.rightList, (item: ItemData, idx: number) => {
          if (this.rightList.length - 1 !== idx) {
            ListItem() {
              ListItemCase({
                item,
                itemIdx: idx,
                hoverIdx: this.hoverIdx,
                defCompType: CompType.RightList,
                curCompType: this.hoverComp
              })
            }
            .onDragStart((event: DragEvent, extraParams: string) => {
              this.itemDragAndDropToStart(CompType.RightList, idx, item)
              return this.dragBuilder(item)
            })
          } else {
            // 处理拖拽到最后一个元素无法换位置
            ListItem()
              .margin({
                top: this.rightList.length - 1 === this.hoverIdx && this.hoverComp === CompType.RightList ? 60 : 0
              })
              .animation({ curve: curves.interpolatingSpring(0, 1, 400, 38), duration: 100 })
          }
        }, (item: ItemData) => item.id.toString())
      }
      .onDragMove((event: DragEvent, extraParams: string) => {
        this.itemDragMove(CompType.RightList, extraParams)
      })
      .onDrop((event: DragEvent, extraParams: string) => {
        this.itemDragAndDropEnds(CompType.RightList)
        event.useCustomDropAnimation = true // 落入没有动效
      })
      .width(180)
      .height(250)
      .scrollBar(BarState.On)
      .edgeEffect(EdgeEffect.None)
    }
    .width('100%')
    .height('100%')
    .padding({ left: 10, right: 10 })
    .justifyContent(FlexAlign.Center)
  }
}

@Component
export struct ListItemCase {
  /**
   * 当前项
   */
  @Prop item: ItemData
  /**
   * 当前项下标
   */
  @Prop itemIdx: number
  /**
   * 当前悬停在某个项的下标
   */
  @Prop hoverIdx: number
  /**
   * 默认组件类型 (标识)
   */
  @Prop defCompType: CompType
  /**
   * 当前组件类型
   */
  @Prop curCompType: CompType

  /**
   * 添加外边距top
   * @returns
   */
  private itemMarTop() {
    return this.hoverIdx === this.itemIdx && this.defCompType === this.curCompType ? 60 : 0
  }

  build() {
    Text(this.item.content)
      .width(150)
      .height(50)
      .fontSize(20)
      .fontWeight(FontWeight.Bold)
      .backgroundColor(Color.Orange)
      .textAlign(TextAlign.Center)
      .borderRadius(20)
      .margin({ top: this.itemMarTop() })
      .animation({ curve: curves.interpolatingSpring(0, 1, 400, 38), duration: 100 })
  }
}

🌸🌼🌺

相关推荐
Huang兄1 小时前
鸿蒙-List和Grid拖拽排序:仿微信小程序删除效果
harmonyos·arkts·arkui
anyup19 小时前
🔥2026最推荐的跨平台方案:H5/小程序/App/鸿蒙,一套代码搞定
前端·uni-app·harmonyos
Ranger09291 天前
鸿蒙开发新范式:Gpui
rust·harmonyos
Huang兄1 天前
鸿蒙-深色模式适配
harmonyos·arkts·arkui
SummerKaze3 天前
为鸿蒙开发者写一个 nvm:hmvm 的设计与实现
harmonyos
在人间耕耘5 天前
HarmonyOS Vision Kit 视觉AI实战:把官方 Demo 改造成一套能长期复用的组件库
人工智能·深度学习·harmonyos
王码码20355 天前
Flutter for OpenHarmony:socket_io_client 实时通信的事实标准(Node.js 后端的最佳拍档) 深度解析与鸿蒙适配指南
android·flutter·ui·华为·node.js·harmonyos
HarmonyOS_SDK5 天前
【FAQ】HarmonyOS SDK 闭源开放能力 — Ads Kit
harmonyos
Swift社区5 天前
如何利用 ArkUI 框架优化鸿蒙应用的渲染性能
华为·harmonyos
特立独行的猫a5 天前
uni-app x跨平台开发实战:开发鸿蒙HarmonyOS影视票房榜组件完整实现过程
华为·uni-app·harmonyos·轮播图·uniapp-x