用户3096723
用户3096723
  • 发布:2026-07-14 09:54
  • 更新:2026-07-14 09:54
  • 阅读:10

如何实现不同分组间元素拖拽切换效果

分类:鸿蒙Next

问题现象
如下图所示,需求是现在有A、B两个组,A、B两组中的元素可以拖动,并且A组中的元素可以拖动到B组,B组的元素同样可以拖动到A组,请问如何实现这种多组之间相互拖拽的效果?

点击放大

背景知识
使用Grid组件构建网格元素布局,启动editMode编辑模式,进入编辑模式可以拖拽Grid组件内部GridItem。
onItemDragStart和onItemDrop事件分别在开始拖拽网格元素时触发和停止拖拽时触发,通过事件的组合完成交换数组位置的逻辑。
解决方案
使用Grid布局构建界面。其中,columnsTemplate可设置当前网格布局列的数量、固定列宽或最小列宽值;columnsGap可设置列与列的间距;rowsGap可设置行与行的间距。
Grid(this.scroller) {
GridItem() {
Text('标题' + this.numbers.length)
.fontSize(16)
.width('100%')
.height(30)
.padding({ left: 10 })
.textAlign(TextAlign.Start);
}
.onClick(() => {
this.isShowGroup1 = !this.isShowGroup1;
});

if (this.isShowGroup1) {
ForEach(this.numbers, (day: string) => {
GridItem() {
Text(day)
.fontSize(16)
.backgroundColor('#f1f3f5')
.width('100%')
.height(30)
.textAlign(TextAlign.Center);
};
});
}
GridItem() {
Text('标题' + this.numbers2.length)
.fontSize(16)
.width('100%')
.height(30)
.padding({ left: 10 })
.textAlign(TextAlign.Start);
}
.onClick(() => {
this.isShowGroup2 = !this.isShowGroup2;
});

if (this.isShowGroup2) {
ForEach(this.numbers2, (day: string) => {
GridItem() {
Text(day)
.fontSize(16)
.backgroundColor('#f1f3f5')
.width('100%')
.height(30)
.textAlign(TextAlign.Center);
};
});
}
}
.columnsTemplate('1fr')
.columnsGap(2)
.rowsGap(2)
.scrollBar(BarState.Off)
给Grid组件设置editMode为true,即Grid进入编辑模式,进入编辑模式可以拖拽Grid组件内部GridItem。设置supportAnimation为true,即Grid拖拽元素时支持动画。
.supportAnimation(true)
.editMode(true) // 设置Grid是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部GridItem
定义GridItem被拖拽时元素的样式,元素被拖拽时展示浮动内容。
@Builder
pixelMapBuilder() { // 拖拽过程样式
Column() {
Text('浮动内容' + this.text)
.fontSize(16)
.backgroundColor('#f1f3f5')
.width('90%')
.height(30)
.textAlign(TextAlign.Center);
};
}
判断当前移动的是不是标题,如果是标题禁止拖动。
// 判断是否是标题
isTitle(index: number) {
if (index === 0) {
return true;
}
if (!this.isShowGroup1 && index === 1) {
return true;
}
if (this.isShowGroup1 && index === this.numbers.length + 1) {
return true;
}
return false;
}
定义拖拽过程中的数组交换逻辑。
moveToIndex(index1: number, index2: number) { // 将index1位置的元素移动至 index2位置
let temp = '';
let num1Length = 1 + (this.numbers.length - 1) * Number(this.isShowGroup1);
if (index1 <= num1Length) {
temp = this.numbers.splice(index1 - 1, 1)[0];
} else {
index1 = index1 - this.numbers.length - 1;
temp = this.numbers2.splice(index1 - 1, 1)[0];
}
if (index2 <= num1Length) {
this.numbers.splice(index2 - 1, 0, temp);
} else {
index2 = index2 - this.numbers.length - 1;
this.numbers2.splice(index2 - 1, 0, temp);
}
}
给Grid组件绑定onItemDragStart和onItemDrop事件,在onItemDragStart回调中设置拖拽过程中显示的图片,并在onItemDrop中完成交换数组位置的逻辑。
.onItemDragStart((event: ItemDragInfo, itemIndex: number) => { // 第一次拖拽此事件绑定的组件时,触发回调。
if (this.isTitle(itemIndex)) {
return;
}
if (itemIndex > this.numbers.length) {
itemIndex = itemIndex - this.numbers.length - 1;
this.text = this.numbers2[itemIndex - 1];
} else {
this.text = this.numbers[itemIndex - 1];
}
return this.pixelMapBuilder(); // 设置拖拽过程中显示的图片。
})
.onItemDrop((event: ItemDragInfo, itemIndex: number,
insertIndex: number) => { // 绑定此事件的组件可作为拖拽释放目标,当在本组件范围内停止拖拽行为时,触发回调。
console.info(eventX: ${event.x});
this.moveToIndex(itemIndex, insertIndex);
});
完整示例如下:

@Entry
@Component
struct GridDemo {
@State numbers: string[] = [];
@State numbers2: string[] = [];
@State isShowGroup1: boolean = true;
@State isShowGroup2: boolean = true;
@State text: string = 'drag';
scroller: Scroller = new Scroller();

@Builder
pixelMapBuilder() { // 拖拽过程样式
Column() {
Text('浮动内容' + this.text)
.fontSize(16)
.backgroundColor('#f1f3f5')
.width('90%')
.height(30)
.textAlign(TextAlign.Center);
};
}

aboutToAppear() {
for (let i = 1; i <= 15; i++) {
this.numbers.push('组' + i);
this.numbers2.push('组' + i);
}
}

moveToIndex(index1: number, index2: number) { // 将index1位置的元素移动至 index2位置
let temp = '';
let num1Length = 1 + (this.numbers.length - 1) * Number(this.isShowGroup1);
if (index1 <= num1Length) {
temp = this.numbers.splice(index1 - 1, 1)[0];
} else {
index1 = index1 - this.numbers.length - 1;
temp = this.numbers2.splice(index1 - 1, 1)[0];
}
if (index2 <= num1Length) {
this.numbers.splice(index2 - 1, 0, temp);
} else {
index2 = index2 - this.numbers.length - 1;
this.numbers2.splice(index2 - 1, 0, temp);
}
}

// 判断是否是标题
isTitle(index: number) {
if (index === 0) {
return true;
}
if (!this.isShowGroup1 && index === 1) {
return true;
}
if (this.isShowGroup1 && index === this.numbers.length + 1) {
return true;
}
return false;
}

build() {
Column({ space: 5 }) {
Column() {
Grid(this.scroller) {
GridItem() {
Text('标题' + this.numbers.length)
.fontSize(16)
.width('100%')
.height(30)
.padding({ left: 10 })
.textAlign(TextAlign.Start);
}
.onClick(() => {
this.isShowGroup1 = !this.isShowGroup1;
});

      if (this.isShowGroup1) {  
        ForEach(this.numbers, (day: string) => {  
          GridItem() {  
            Text(day)  
              .fontSize(16)  
              .backgroundColor('#f1f3f5')  
              .width('100%')  
              .height(30)  
              .textAlign(TextAlign.Center);  
          };  
        });  
      }  
      GridItem() {  
        Text('标题' + this.numbers2.length)  
          .fontSize(16)  
          .width('100%')  
          .height(30)  
          .padding({ left: 10 })  
          .textAlign(TextAlign.Start);  
      }  
      .onClick(() => {  
        this.isShowGroup2 = !this.isShowGroup2;  
      });  

      if (this.isShowGroup2) {  
        ForEach(this.numbers2, (day: string) => {  
          GridItem() {  
            Text(day)  
              .fontSize(16)  
              .backgroundColor('#f1f3f5')  
              .width('100%')  
              .height(30)  
              .textAlign(TextAlign.Center);  
          };  
        });  
      }  
    }  
    .columnsTemplate('1fr')  
    .columnsGap(2)  
    .rowsGap(2)  
    .scrollBar(BarState.Off)  
    .onScrollIndex((first: number) => {  
      console.info(first.toString());  
    })  
    .width('90%')  
    .supportAnimation(true)  
    .editMode(true) // 设置Grid是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部GridItem  
    .onItemDragStart((event: ItemDragInfo, itemIndex: number) => { // 第一次拖拽此事件绑定的组件时,触发回调。  
      if (this.isTitle(itemIndex)) {  
        return;  
      }  
      if (itemIndex > this.numbers.length) {  
        itemIndex = itemIndex - this.numbers.length - 1;  
        this.text = this.numbers2[itemIndex - 1];  
      } else {  
        this.text = this.numbers[itemIndex - 1];  
      }  
      return this.pixelMapBuilder(); // 设置拖拽过程中显示的图片。  
    })  
    .onItemDrop((event: ItemDragInfo, itemIndex: number,  
      insertIndex: number) => { // 绑定此事件的组件可作为拖拽释放目标,当在本组件范围内停止拖拽行为时,触发回调。  
      console.info(`eventX: ${event.x}`);  
      this.moveToIndex(itemIndex, insertIndex);  
    });  

  };  
}.width('100%').margin({ top: 5 });  

}
}
https://pastebin.com/ALZ1heJ8
https://pastebin.com/NUA8cAMq
https://pastebin.com/DU7BW7SP
https://pastebin.com/Sh2K4277
https://pastebin.com/MKNCS5xM
https://pastebin.com/jBqWTCsQ
https://pastebin.com/TNhaEKd7
https://pastebin.com/pAcyuLWk
https://pastebin.com/BccwcGVK
https://pastebin.com/ETXeKqiW
https://pastebin.com/DRxMx41K
https://pastebin.com/L15SkLc9
https://pastebin.com/Pyz2h7xc
https://pastebin.com/LWtxbvUn
https://pastebin.com/S7wbsPSF
https://pastebin.com/XmPzcDSQ
https://pastebin.com/zJ2kJ7ZA
https://pastebin.com/wknFnVct
https://pastebin.com/Jx9edSj2
https://pastebin.com/s7w3uv0a
https://pastebin.com/JHwj1NaL
https://pastebin.com/RzdjWfNv
https://pastebin.com/9G3P7TeA
https://pastebin.com/sG5wxN1u
https://pastebin.com/hJ2c716v
https://pastebin.com/4exdhVkn
https://pastebin.com/hUVhcvMP
https://pastebin.com/A4LnBDtM
https://pastebin.com/L6FwB3N9
https://pastebin.com/pt0KRF9P
https://pastebin.com/93GGDNJv
https://pastebin.com/DHdskaAf
https://pastebin.com/1QH5dm9R
https://pastebin.com/P30Qqeif
https://pastebin.com/ngQjFN3W
https://pastebin.com/BRAv0z5d
https://pastebin.com/fdYZAvMX
https://pastebin.com/ciXgk5F6
https://pastebin.com/RjC4wjc9
https://pastebin.com/kEaUKCTq
https://pastebin.com/C3JuxCrQ
https://pastebin.com/y0KUAU4u
https://pastebin.com/nW5AFBcM
https://pastebin.com/yR9yyNZ5
https://pastebin.com/zvH6TX45
https://pastebin.com/RsL54ULk
https://pastebin.com/TRTYXt4k
https://pastebin.com/zCGW0vif
https://pastebin.com/6drbFd0j
https://pastebin.com/D7urfsq6
https://pastebin.com/119esj24
https://pastebin.com/ndHUJCr4
https://pastebin.com/ciSWy0ut
https://pastebin.com/iKf50her
https://pastebin.com/RkE5yyn8
https://pastebin.com/jj99Wa7u
https://pastebin.com/iwJ5us6j
https://pastebin.com/hSuvYeqx
https://pastebin.com/6YQNbNZ6
https://pastebin.com/SsUF39yN
https://pastebin.com/xLFVFWSd
https://pastebin.com/8C1YCkb7
https://pastebin.com/PBRiQwEE
https://pastebin.com/CZw00CFD
https://pastebin.com/MhC3vvEH
https://pastebin.com/NGucrr8R
https://pastebin.com/5inLc8eS
https://pastebin.com/rnNKvRei
https://pastebin.com/LDXYYnhG
https://pastebin.com/5YpXtQeT
https://pastebin.com/mb5JFUs5
https://pastebin.com/HDzxgFHY
https://pastebin.com/Ssx5zf6i
https://pastebin.com/qMCRpKuL
https://pastebin.com/YMyidNzi
https://pastebin.com/vSwjFPu0
https://pastebin.com/yFPJgYtk
https://pastebin.com/5FLbpDXJ
https://pastebin.com/vRj3ezdr
https://pastebin.com/mYUwfwNk
https://pastebin.com/dTx5DQSb
https://pastebin.com/e7jXBCMc
https://pastebin.com/dxgE2bcC
https://pastebin.com/STW6QhxS
https://pastebin.com/bZhrAr9R
https://pastebin.com/nfYTP7Au
https://pastebin.com/aWunSSVB
https://pastebin.com/zh4pAmyd
https://pastebin.com/3pqzBPyc
https://pastebin.com/ddwcDZHC
https://pastebin.com/2ZqNLikt
https://pastebin.com/56U4rWjG
https://pastebin.com/w24SbjAy
https://pastebin.com/by3Jn4Pq
https://pastebin.com/9UkiAZMK
https://pastebin.com/MxhA05RB
https://pastebin.com/4Jshmpfb
https://pastebin.com/L5sHYH9N
https://pastebin.com/0bJWTV7h
https://pastebin.com/AwM6iDiL
https://pastebin.com/Lsr4Cxkg
https://pastebin.com/LAfLC3a3
https://pastebin.com/f7wTnKEC
https://pastebin.com/HSEbHCyh
https://pastebin.com/uv6Hqjs4
https://pastebin.com/i2V8QUcy
https://pastebin.com/ZAPYfBNk
https://pastebin.com/waMXDnj3
https://pastebin.com/GMUKBZUs
https://pastebin.com/pezF3Vbk
https://pastebin.com/n7DYUQ5v
https://pastebin.com/cyT65uSE
https://pastebin.com/sx2iJqcn
https://pastebin.com/ZPqkWKaR
https://pastebin.com/HEyb77U9
https://pastebin.com/4FZufveK
https://pastebin.com/uExv5h76
https://pastebin.com/Ac7CHJc7
https://pastebin.com/M4s8ZR79
https://pastebin.com/nys7xPvA
https://pastebin.com/PehqarxQ
https://pastebin.com/RbdWT272
https://pastebin.com/UfFk8r7t
https://pastebin.com/y1XYanaB
https://pastebin.com/VmifaeiF
https://pastebin.com/2dDAckiN
https://pastebin.com/FAfELYhc
https://pastebin.com/QCFiEVnQ
https://pastebin.com/QbgF4PzX
https://pastebin.com/X4yVdKxp
https://pastebin.com/cuqhnADr
https://pastebin.com/8iUfUnL8
https://pastebin.com/xhFdDU3m
https://pastebin.com/5Yt1eLAQ
https://pastebin.com/XNDHeeM8
https://pastebin.com/kJXfXZ8R
https://pastebin.com/hxwe46wW
https://pastebin.com/B4mB17YB
https://pastebin.com/GQbTpcbY
https://pastebin.com/DpYauvxs
https://pastebin.com/1gsWBrLW
https://pastebin.com/g9WsSEzm
https://pastebin.com/hzqj45RD
https://pastebin.com/vWKXzBJ9
https://pastebin.com/GTajTfJB
https://pastebin.com/d52vdzg9
https://pastebin.com/w1xQpURf
https://pastebin.com/GVzvf2jD
https://pastebin.com/U5DNXZR5
https://pastebin.com/bBDcpTNV
https://pastebin.com/kaQ390Yd
https://pastebin.com/RHfrn44V
https://pastebin.com/ycM6hx0X
https://pastebin.com/sSUD6yBD
https://pastebin.com/vYZwLY68
https://pastebin.com/hsu9R9jj
https://pastebin.com/G6HZUJ4J
https://pastebin.com/tr8ae7XA
https://pastebin.com/j8cttQZk
https://pastebin.com/YeKMiByw
https://pastebin.com/j6Nnu9Qe
https://pastebin.com/LVGaaVQa
https://pastebin.com/JJWVEiS5
https://pastebin.com/GEFCqsT7
https://pastebin.com/3ZjJhLsT
https://pastebin.com/eRuatTXc
https://pastebin.com/6dLRhyiJ
https://pastebin.com/VS65myh8
https://pastebin.com/NK7i1SnE
https://pastebin.com/6Akg12TS
https://pastebin.com/Qs3xpNS5
https://pastebin.com/RjrrPqe0
https://pastebin.com/urvjTD8V
https://pastebin.com/63sbx7mm
https://pastebin.com/CxznX5Js
https://pastebin.com/yL6vTZKF
https://pastebin.com/ExT07Ygf
https://pastebin.com/QeQZdYcJ
https://pastebin.com/BY7bpMhj
https://pastebin.com/yAYUGpkf
https://pastebin.com/YXCzrDjU
https://pastebin.com/S10HgVGn
https://pastebin.com/LY6WLkTu
https://pastebin.com/3pfYGW1q
https://pastebin.com/3pV0G6nk
https://pastebin.com/CW4g7TJv
https://pastebin.com/M8J73bkP
https://pastebin.com/L9vcxB0W
https://pastebin.com/buUr51Ls

0 关注 分享

要回复文章请先登录注册