diff --git a/zh-cn/third-party-cases/Readme-CN.md b/zh-cn/third-party-cases/Readme-CN.md index 3d7375963afee604adcb2c052acff1047f8e57f5..5b0607e8dd9cdc52713c8eeec6fe863c25d34aba 100644 --- a/zh-cn/third-party-cases/Readme-CN.md +++ b/zh-cn/third-party-cases/Readme-CN.md @@ -11,7 +11,8 @@ - [如何实现列表项的新增和删除](how-to-add-delete-listitems.md) - [如何通过显示动画实现书籍翻页动效](book-flip-animation.md) - [如何为同一组件在不同场景下绑定不同的业务逻辑](different-operations-for-one-component.md) -- [如何实现列表项ListItem滑动显示快捷菜单](listitem-slide-to-display-menu.md) +- [如何实现列表项滑动显示快捷菜单](listitem-slide-to-display-menu.md) +- [如何在网格组件中通过拖拽交换子组件的位置](griditem-drag-and-drop.md) ### 网络管理 - [如何请求并加载网络图片](how-to-load-images-from-internet.md) diff --git a/zh-cn/third-party-cases/figures/griditem-drag.gif b/zh-cn/third-party-cases/figures/griditem-drag.gif new file mode 100644 index 0000000000000000000000000000000000000000..3320c46b64e057b752088258cd93a1d960f13c99 Binary files /dev/null and b/zh-cn/third-party-cases/figures/griditem-drag.gif differ diff --git a/zh-cn/third-party-cases/griditem-drag-and-drop.md b/zh-cn/third-party-cases/griditem-drag-and-drop.md new file mode 100644 index 0000000000000000000000000000000000000000..97a3b2636cb6ea34f56de02a67febaef19230dc3 --- /dev/null +++ b/zh-cn/third-party-cases/griditem-drag-and-drop.md @@ -0,0 +1,191 @@ +## 如何在网格Grid中通过拖拽交换子组件位置 + +### 场景说明 + +在使用网格Grid的应用中,可以通过拖拽子组件GridItem的方式,交换子组件的显示位置。 + +### 效果呈现 + +本示例在模拟器中显示的最终效果如下(预览器中显示效果略有差异): + +![listitem-slide](figures/griditem-drag.gif) + +### 运行环境 + +- IDE:DevEco Studio 3.1 Beta2 +- SDK:Ohos_sdk_public 3.2.11.5 (API Version 9 Release) + +### 实现原理 + +1. 设置Grid的editMode属性为true,使Grid进入编辑模式,从而可以拖拽Grid组件内部GridItem。 + +2. 在Grid的相关拖拽事件中进行拖拽逻辑处理: + + 1. 在onItemDragStart事件中显示拖拽过程中的图片,即被拖拽的GridItem。 + + 2. 在onItemDrop事件中根据拖拽前后的位置,完成两个GridItem位置交换的逻辑。 + +### 开发步骤 + +1. 构建Grid组件及子组件GridItem,开启Grid组件的editMode属性。 + + ```ts + build() { + Column({ space: 5 }) { + Grid(this.scroller) { + ForEach(this.numbers, (day: string) => { + GridItem() { + ... + } + }) + } + .editMode(true) + ... + + }.width('100%').margin({ top: 5 }) + } + ``` + +2. 当长按GridItem时触发onItemDragStart事件,在该事件中提供被拖拽GridItem的显示逻辑。 + + ```ts + .onItemDragStart((event: ItemDragInfo, itemIndex: number) => { + return this.pixelMapBuilder() + }) + ``` + + 其中,pixelMapBuilder构造拖拽过程中显示的图片,即被拖拽的GridItem。 + + ```ts + @State text: string = 'drag' + + @Builder pixelMapBuilder() { + Column() { + Text(this.text) + .fontSize(16) + .backgroundColor(0xF9CF93) + .width(80) + .height(80) + .textAlign(TextAlign.Center) + } + } + ``` + + 拖拽过程中GridItem显示的内容,在触摸事件发生时进行传递。 + + ```ts + ForEach(this.numbers, (day: string) => { + GridItem() { + Text(day) + ... + .onTouch((event: TouchEvent) => { + if (event.type === TouchType.Down) { + this.text = day + } + }) + } + }) + ``` + +3. 停止拖拽时触发onItemDrop事件,在该事件中完成两个GridItem位置交换的逻辑。 + + 为了防止GridItem被拖拽到空白的区域,在交换之前判断拖拽插入的位置是否超出当前已有内容的范围: + + ```ts + .onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => { + if(insertIndex < this.numbers.length){ + this.changeIndex(itemIndex, insertIndex) + } + }) + ``` + + 其中,changeIndex为具体交换数组元素位置的逻辑: + + ```ts + changeIndex(index1: number, index2: number) { + [this.numbers[index1], this.numbers[index2]] = [this.numbers[index2], this.numbers[index1]]; + } + ``` + +### 完整代码 + +通过上述步骤可以完成整个示例的开发,完整代码如下: + +```ts +@Entry +@Component +struct Index { + @State numbers: String[] = [] + scroller: Scroller = new Scroller() + @State text: string = 'drag' + + //拖拽过程中展示的样式 + @Builder pixelMapBuilder() { + Column() { + Text(this.text) + .fontSize(16) + .backgroundColor(0xF9CF93) + .width(80) + .height(80) + .textAlign(TextAlign.Center) + } + } + + aboutToAppear() { + for (let i = 1;i <= 15; i++) { + this.numbers.push(i + '') + } + } + + //交换数组中元素位置 + changeIndex(index1: number, index2: number) { + [this.numbers[index1], this.numbers[index2]] = [this.numbers[index2], this.numbers[index1]]; + } + + build() { + Column({ space: 5 }) { + Grid(this.scroller) { + ForEach(this.numbers, (day: string) => { + GridItem() { + Text(day) + .fontSize(16) + .backgroundColor(0xF9CF93) + .width(80) + .height(80) + .textAlign(TextAlign.Center) + .onTouch((event: TouchEvent) => { + if (event.type === TouchType.Down) { + this.text = day + } + }) + } + }) + } + .columnsTemplate('1fr 1fr 1fr') + .columnsGap(10) + .rowsGap(10) + .onScrollIndex((first: number) => { + console.info(first.toString()) + }) + .width('90%') + .backgroundColor(0xFAEEE0) + .height('100%') + //设置Grid是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部GridItem + .editMode(true) + //第一次拖拽此事件绑定的组件时,触发回调 + .onItemDragStart((event: ItemDragInfo, itemIndex: number) => { + //设置拖拽过程中显示的图片 + return this.pixelMapBuilder() + }) + //绑定此事件的组件可作为拖拽释放目标,当在本组件范围内停止拖拽行为时,触发回调 + //itemIndex为拖拽起始位置,insertIndex为拖拽插入位置 + .onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => { + //不支持拖拽到已有内容以外的位置 + if(insertIndex < this.numbers.length){ + this.changeIndex(itemIndex, insertIndex) + } + }) + }.width('100%').margin({ top: 5 }) + } +} +``` \ No newline at end of file