DEV Community

Cover image for HarmonyOS development: to achieve a super simple grid drag and drop
程序员一鸣
程序员一鸣

Posted on

HarmonyOS development: to achieve a super simple grid drag and drop

Foreword

this article is based on Api12 

grid drag and drop, this function is very common, generally used for channel editing or item sequence arrangement. In the development of Hongmeng, the system also gives relevant Api for grid editing, which can be easily implemented through itemdragstart and itemdrop. Itemdragstart is used to set the display during drag and drop, and itemdrop is used for data exchange logic processing. 

According to the official provision, we casually implemented a simple drag and drop effect:

 

@Entry
  @Component
  struct Index {
    @State numbers: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    @Builder
    itemLayout(text: string) {
      Text(text)
        .fontSize(16)
        .backgroundColor(Color.Pink)
        .width(80)
        .height(80)
        .textAlign(TextAlign.Center)
    }

    changeIndex(index1: number, index2: number) { 
      let temp = this.numbers[index1];
      this.numbers[index1] = this.numbers[index2];
      this.numbers[index2] = temp;
    }

    build() {
      Column() {
        Grid() {
          ForEach(this.numbers, (n: number) => {
            GridItem() {
              this.itemLayout(n.toString())
            }
          })
        }
        .columnsTemplate('1fr 1fr 1fr')
          .columnsGap(10)
          .rowsGap(10)
          .editMode(true) 
          .onItemDragStart((event: ItemDragInfo, itemIndex: number) => { //第一次拖拽此事件绑定的组件时,触发回调。
            return this.itemLayout(this.numbers[itemIndex].toString()) //设置拖拽过程中显示
          })
          .onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number,
                       isSuccess: boolean) => {
                         if (!isSuccess || insertIndex >= this.numbers.length) {
                           return
                         }
                         this.changeIndex(itemIndex, insertIndex)
                       })
      }.width('100%')
        .height("100%")
    }
  }
Enter fullscreen mode Exit fullscreen mode

Image description

in the above code, we simply implemented a grid drag and drop. After the actual operation, the long press did drag and drop, and the corresponding item was also exchanged, but there was no process of mobile exchange, because there was no setting. Drag animation method, if you want to drag the process of moving animation, just need to set supportAnimation is set to true. In addition, there is another very important attribute, that is editMode, true is edit mode, false is not editable. 

The drag-and-drop code is very simple. After all, the government has also given relevant cases. Two of them are not given, that is, the prohibition of drag-and-drop and the prohibition of exchange with them. 

For example, the first and second entries cannot be dragged and dropped. How do you set them? For example, no entry can be exchanged with the first one. How should it be set? 

Drag and Drop Prohibited

It is also the above code. For example, it is very simple to prohibit the first entry from being dragged. In the onItemDragStart method, if the specified index is touched, it is not allowed to be executed.

.onItemDragStart((event: ItemDragInfo, itemIndex: number) => { 

  if (itemIndex == 0) {

    return
  }
  return this.itemLayout(this.numbers[itemIndex].toString()) //设置拖拽过程中显示
})
Enter fullscreen mode Exit fullscreen mode

It should be noted that the above code only prohibits dragging. However, there is a potential problem that other entries can be exchanged with it. After Exchange, due to the change of its index, it becomes draggable. If you want to realize real, you cannot drag even after being exchanged, then you cannot judge the index and can judge by the unique value, for example, item data is replaced with an object, and a unique value is defined in the object. 

Prohibition of Exchange

in actual development, in addition to the prohibition of dragging and dropping of an entry, there is also logic that cannot be exchanged with it. For example, the first entry is fixed, which not only prohibits dragging and dropping, but also cannot be exchanged with it. How can this be implemented?

 

onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number,
            isSuccess: boolean) => {

              if (insertIndex <= 0) {
                return
              }
              if (!isSuccess || insertIndex >= this.numbers.length) {
                return
              }
              this.changeIndex(itemIndex, insertIndex)
            })
Enter fullscreen mode Exit fullscreen mode

It is very simple. If it is the first one, it is enough not to execute it directly. Although the above code implements the prohibition of exchange, there is a problem, that is, the actual animation has been executed, but the final exchange has not been executed. There are still big visual problems. 

The effect we want is to keep the animation of moving and dragging, but when dragging to the first one, the animation will not be executed, and the others will be executed as they are executed. 

Then, when setting supportAnimation animation animation, it cannot be set to true directly, but after moving to the position of item one, it is set to fasle, and the others are set to true. 

How to get the coordinates of the first item? In fact, each component has a method, onAreaChange. Through this attribute, we can get the width and height of any component and the coordinates of xy. Of course, you can also calculate. After all, you know the width and height of the screen and the width and height of the entry. 

The coordinates of the gesture movement can be obtained by the onTouch method, and judged in the Move Event. If the movement is within the range of item one, the animation is canceled, otherwise the animation is executed.

Simple implementation

at present, the grid drag-and-drop function has been encapsulated and put into the refresh Library. If you want to use it directly, you can rely on this library:

 

"dependencies": { "@abner/refresh": "^1.3.6"}
Enter fullscreen mode Exit fullscreen mode

the simple case is as follows:

 

import { GridDropView } from '@abner/refresh'

@Entry
  @Component
  struct Index {
    @State numbers: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    @Builder
    itemLayout(item: Object, _: number) {
      Text(item.toString())
        .fontSize(16)
        .backgroundColor(Color.Pink)
        .width(80)
        .height(80)
        .textAlign(TextAlign.Center)
    }



    build() {
      Column() {
        GridDropView({
          columnsTemplate:"1fr 1fr 1fr",
          items: this.numbers,
          itemLayout: this.itemLayout,
          dropLayout: this.itemLayout,
          rowsGap: 10,
          columnsGap: 10,
          isEditMode: true,
          prohibitDrop: [0, 1], 
          prohibitMaxSwap: 0,
          onDropData: (items) => {
            console.log("END:" + JSON.stringify(items))
          }
        })
      }.width('100%')
        .height("100%")

    }
  }
Enter fullscreen mode Exit fullscreen mode

precautions

the three most important ways to implement drag and drop are to open the editing State editMode, implement the onItemDragStart and onItemDrop, set the drag and drop Movement animation and exchange data, and if you want to open the complementary animation, you still need to implement it. The supportAnimation method.

Top comments (0)