🎯 案例集合List:多列表(List)相互拖拽(位置交换)
🌍 案例集合List
🚪 最近开启了学员班级点我欢迎加入(学习相关知识可获得定制礼盒)
🏷️ 效果图

❌ 待实现(预计这两天)
- 组件检测左右偏移动效(用于多列表的添加/删除)
✅ 已实现
- 不同列表交换
- 同列表交换
- 组件边缘滑动 (原生自带)
- 拖出/插入过渡动画
📖 参考
🧩 拆解
javascript
/**
* 组件在拖拽中需要用到的额外信息
*/
interface extraParamsObj {
insertIndex: number // 插入项下标
selectedIndex: number // 选中项下标
}
/**
* mockData
*/
class ItemData {
id: number = 0
content: string = ''
}
/**
* 组件标识
*/
enum CompType {
LeftList = 'LeftList',
RightList = 'RightList',
}
@Component
export struct MultiListDragAndDrop {
/**
* 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" }
]
/**
* 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" },
]
/**
* 当前拖拽下标
*/
@State curDragIdx: number = -1
/**
* 当前悬停在那个组件上方的下标
*/
@State hoverIdx: number = -1
/**
* 默认组件类型组别
*/
@State dragType: CompType | undefined = undefined
/**
* 当前悬停在那个组件上方
*/
@State hoverComp: CompType | undefined = undefined
/**
* 拖拽项视图
* @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)
}
/**
* 拖拽开始统一方法
* @param compType
* @param extraParams
*/
private itemDragAndDropToStart(compType: CompType, extraParams: string) {
if (this.dragType === undefined) {
// 保持只要拖拽开始就标记组件唯一标识
this.dragType = compType
}
this.curDragIdx = (JSON.parse(extraParams) as extraParamsObj)?.selectedIndex
}
/**
* 拖拽释放统一方法
* @param compType
* @param extraParams
*/
private itemDragAndDropEnds(compType: CompType, extraParams: string) {
const insertIdx = (JSON.parse(extraParams) as extraParamsObj)?.insertIndex
if (insertIdx >= this.listDataType(compType).length) {
return
}
// 交换逻辑
this.exchangeItem(this.curDragIdx, insertIdx, compType)
// 重置当前悬停在那个组件上方的下标
this.hoverIdx = -1
// 重置组件标识
this.dragType = this.hoverComp = undefined
}
/**
* 悬停组件过渡
* @param compType
* @param extraParams
*/
private itemDragMove(compType: CompType, extraParams: string) {
const insertIdx = (JSON.parse(extraParams) as extraParamsObj)?.insertIndex
if (insertIdx >= this.listDataType(compType).length) {
return
}
this.hoverIdx = insertIdx
this.hoverComp = compType
this.getUIContext()
.getPromptAction()
.showToast({ message: `当前组件:${compType}---准备替换的目标下标${insertIdx}` })
}
/**
* 交换选项
* @param curDrag_idx 当前拖拽item下标
* @param insert_idx 当前插入项下标
* @param dragType 拖拽组件的类型(标识)
*/
private exchangeItem(curDrag_idx: number, insert_idx: number, dragType: CompType) {
/**
* 判断为同一列表交换
*/
if (this.dragType === dragType) {
this.sameListExchange(curDrag_idx, insert_idx, dragType)
return
}
/**
* 不同列表交换
*/
switch (this.dragType) {
case CompType.LeftList:
this.differentListExchange(this.leftList, this.rightList, curDrag_idx, insert_idx)
break
case CompType.RightList:
this.differentListExchange(this.rightList, this.leftList, curDrag_idx, insert_idx)
break
}
}
/**
* 相同列表item交换
* @param curDrag_idx 当前拖拽item下标
* @param insert_idx 当前插入项下标
* @param dragType
*/
private sameListExchange(curDrag_idx: number, insert_idx: number, dragType: CompType) {
const exchangeItem: ItemData = this.listDataType(dragType)[curDrag_idx]
this.listDataType(dragType)[curDrag_idx] = this.listDataType(dragType)[insert_idx]
this.listDataType(dragType)[insert_idx] = exchangeItem
}
/**
* 不同列表item交换
* @param list_1
* @param list_2
* @param curDrag_idx 当前拖拽item下标
* @param insert_idx 当前插入项下标
*/
private differentListExchange(
list_1: ItemData[],
list_2: ItemData[],
curDrag_idx: number,
insert_idx: number
) {
const exchangeItem: ItemData = list_1[curDrag_idx]
list_1[curDrag_idx] = list_2[insert_idx]
list_2[insert_idx] = exchangeItem
}
/**
* 数据类型
* @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) => {
ListItem() {
ListItemCase({
item,
itemIdx: idx,
hoverIdx: this.hoverIdx,
defCompType: CompType.LeftList,
curCompType: this.hoverComp,
})
}
.onDragStart((event: DragEvent, extraParams: string) => {
this.itemDragAndDropToStart(CompType.LeftList, extraParams)
return this.dragBuilder(item)
})
}, (item: ItemData) => item.id.toString())
}
.onDragMove((event: DragEvent, extraParams: string) => {
this.itemDragMove(CompType.LeftList, extraParams)
})
.onDrop((event: DragEvent, extraParams: string) => {
this.itemDragAndDropEnds(CompType.LeftList, extraParams)
})
.width(180)
.height(230)
.scrollBar(BarState.On)
.edgeEffect(EdgeEffect.None)
// 右列表
List({ space: 10 }) {
ForEach(this.rightList, (item: ItemData, idx: number) => {
ListItem() {
ListItemCase({
item,
itemIdx: idx,
hoverIdx: this.hoverIdx,
defCompType: CompType.RightList,
curCompType: this.hoverComp
})
}
.onDragStart((event: DragEvent, extraParams: string) => {
this.itemDragAndDropToStart(CompType.RightList, extraParams)
return this.dragBuilder(item)
})
}, (item: ItemData) => item.id.toString())
}
.onDragMove((event: DragEvent, extraParams: string) => {
this.itemDragMove(CompType.RightList, extraParams)
})
.onDrop((event: DragEvent, extraParams: string) => {
this.itemDragAndDropEnds(CompType.RightList, extraParams)
})
.width(180)
.height(230)
.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
/**
*
* @returns
*/
private onScale() {
return this.hoverIdx === this.itemIdx ? 0.75 : 1
}
build() {
Text(this.item.content)
.width(150)
.height(50)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.backgroundColor(Color.Orange)
.textAlign(TextAlign.Center)
.borderRadius(20)
.scale(
this.curCompType === this.defCompType ?
{ x: this.onScale(), y: this.onScale() } :
undefined
)
.animation({ curve: Curve.Smooth, duration: 200 })
}
}
🌸🌼🌺