1## 如何在网格Grid中通过拖拽交换子组件位置 2 3### 场景说明 4 5在使用网格Grid的应用中,可以通过拖拽子组件GridItem的方式,交换子组件的显示位置。 6 7### 效果呈现 8 9本示例在模拟器中显示的最终效果如下(预览器中显示效果略有差异): 10 11 12 13### 运行环境 14 15- IDE:DevEco Studio 3.1 Beta2 16- SDK:Ohos_sdk_public 3.2.11.9 (API Version 9 Release) 17 18### 实现原理 19 201. 设置Grid的editMode属性为true,使Grid进入编辑模式,从而可以拖拽Grid组件内部GridItem。 21 222. 在Grid的相关拖拽事件中进行拖拽逻辑处理: 23 24 1. 在onItemDragStart事件中显示拖拽过程中的图片,即被拖拽的GridItem。 25 26 2. 在onItemDrop事件中根据拖拽前后的位置,完成两个GridItem位置交换的逻辑。 27 28### 开发步骤 29 301. 构建Grid组件及子组件GridItem,开启Grid组件的editMode属性。 31 32 ```ts 33 build() { 34 Column({ space: 5 }) { 35 Grid(this.scroller) { 36 ForEach(this.numbers, (day: string) => { 37 GridItem() { 38 ... 39 } 40 }) 41 } 42 .editMode(true) 43 ... 44 45 }.width('100%').margin({ top: 5 }) 46 } 47 ``` 48 492. 当长按GridItem时触发onItemDragStart事件,在该事件中提供被拖拽GridItem的显示逻辑。 50 51 ```ts 52 .onItemDragStart((event: ItemDragInfo, itemIndex: number) => { 53 return this.pixelMapBuilder() 54 }) 55 ``` 56 57 其中,pixelMapBuilder构造拖拽过程中显示的图片,即被拖拽的GridItem。 58 59 ```ts 60 @State text: string = 'drag' 61 62 @Builder pixelMapBuilder() { 63 Column() { 64 Text(this.text) 65 .fontSize(16) 66 .backgroundColor(0xF9CF93) 67 .width(80) 68 .height(80) 69 .textAlign(TextAlign.Center) 70 } 71 } 72 ``` 73 74 拖拽过程中GridItem显示的内容,在触摸事件发生时进行传递。 75 76 ```ts 77 ForEach(this.numbers, (day: string) => { 78 GridItem() { 79 Text(day) 80 ... 81 .onTouch((event: TouchEvent) => { 82 if (event.type === TouchType.Down) { 83 this.text = day 84 } 85 }) 86 } 87 }) 88 ``` 89 903. 停止拖拽时触发onItemDrop事件,在该事件中完成两个GridItem位置交换的逻辑。 91 92 为了防止GridItem被拖拽到空白的区域,在交换之前判断拖拽插入的位置是否超出当前已有内容的范围: 93 94 ```ts 95 .onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => { 96 if(insertIndex < this.numbers.length){ 97 this.changeIndex(itemIndex, insertIndex) 98 } 99 }) 100 ``` 101 102 其中,changeIndex为具体交换数组元素位置的逻辑: 103 104 ```ts 105 changeIndex(index1: number, index2: number) { 106 [this.numbers[index1], this.numbers[index2]] = [this.numbers[index2], this.numbers[index1]]; 107 } 108 ``` 109 110### 完整代码 111 112通过上述步骤可以完成整个示例的开发,完整代码如下: 113 114```ts 115@Entry 116@Component 117struct Index { 118 @State numbers: String[] = [] 119 scroller: Scroller = new Scroller() 120 @State text: string = 'drag' 121 122 //拖拽过程中展示的样式 123 @Builder pixelMapBuilder() { 124 Column() { 125 Text(this.text) 126 .fontSize(16) 127 .backgroundColor(0xF9CF93) 128 .width(80) 129 .height(80) 130 .textAlign(TextAlign.Center) 131 } 132 } 133 134 aboutToAppear() { 135 for (let i = 1;i <= 15; i++) { 136 this.numbers.push(i + '') 137 } 138 } 139 140 //交换数组中元素位置 141 changeIndex(index1: number, index2: number) { 142 [this.numbers[index1], this.numbers[index2]] = [this.numbers[index2], this.numbers[index1]]; 143 } 144 145 build() { 146 Column({ space: 5 }) { 147 Grid(this.scroller) { 148 ForEach(this.numbers, (day: string) => { 149 GridItem() { 150 Text(day) 151 .fontSize(16) 152 .backgroundColor(0xF9CF93) 153 .width(80) 154 .height(80) 155 .textAlign(TextAlign.Center) 156 .onTouch((event: TouchEvent) => { 157 if (event.type === TouchType.Down) { 158 this.text = day 159 } 160 }) 161 } 162 }) 163 } 164 .columnsTemplate('1fr 1fr 1fr') 165 .columnsGap(10) 166 .rowsGap(10) 167 .onScrollIndex((first: number) => { 168 console.info(first.toString()) 169 }) 170 .width('90%') 171 .backgroundColor(0xFAEEE0) 172 .height('100%') 173 //设置Grid是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部GridItem 174 .editMode(true) 175 //第一次拖拽此事件绑定的组件时,触发回调 176 .onItemDragStart((event: ItemDragInfo, itemIndex: number) => { 177 //设置拖拽过程中显示的图片 178 return this.pixelMapBuilder() 179 }) 180 //绑定此事件的组件可作为拖拽释放目标,当在本组件范围内停止拖拽行为时,触发回调 181 //itemIndex为拖拽起始位置,insertIndex为拖拽插入位置 182 .onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => { 183 //不支持拖拽到已有内容以外的位置 184 if(insertIndex < this.numbers.length){ 185 this.changeIndex(itemIndex, insertIndex) 186 } 187 }) 188 }.width('100%').margin({ top: 5 }) 189 } 190} 191```