鸿蒙开发:实现一个超简单的网格拖拽

程序员一鸣
• 阅读 1

前言

网格拖拽,此功能很是常见,一般用于频道的编辑或者条目顺序的排列,在鸿蒙的开发中,针对网格的编辑,系统也给出了相关的Api,通过onItemDragStart和在onItemDrop即可轻松实现,onItemDragStart用于设置拖拽过程中的显示,onItemDrop是进行数据交换逻辑处理。

根据官方提供,我们随便实现了一个简单的拖拽效果:

@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) //设置Grid是否进入编辑模式
      .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%")
  }
}

鸿蒙开发:实现一个超简单的网格拖拽

以上代码我们就很简单的实现了一个网格拖拽,实际运行之后,长按确实发生了拖拽,和对应的item也进行了交换,但是并没有一个移动交换的过程,这是因为没有设置拖拽动画方法,如果想要拖拽过程中有移动动画,只需要设置supportAnimation为true即可,除此之外,还有一个非常重要的属性,那就是editMode,为true即为编辑模式,false即不可编辑。

拖拽的代码很是简单,毕竟官方也给出了相关案例,其中有两个点是未给出的,那就是禁止拖拽,和禁止与其交换。

比如,第一个和第二个条目,不能拖拽,如何进行设置呢?再比如,任何条目都不能和第一个进行交换,又该如何设置呢?

禁止拖拽

还是上述的代码,例如,把第一个条目禁止,不让其执行拖拽,实现起来很是简单,在onItemDragStart方法里,如果触摸的是指定索引,不让其执行即可。

.onItemDragStart((event: ItemDragInfo, itemIndex: number) => { 
       //第一次拖拽此事件绑定的组件时,触发回调。
       if (itemIndex == 0) {
         //禁止拖拽
         return
       }
       return this.itemLayout(this.numbers[itemIndex].toString()) //设置拖拽过程中显示
     })

需要注意的是,以上的代码仅仅是做到了禁止拖拽,但是,有一个潜在的问题是,别的条目是可以和它进行交换的,交换过之后,由于它的索引发生了变化,就变得可以拖拽了,如果想实现真正的,即便被交换后也不能拖拽,那就不能判断索引了,可以以唯一值进行判断,比如item数据换成对象,在对象里定义唯一值。

禁止交换

实际的开发中,除了某个条目禁止拖拽之外,也有不能和它进行交换的逻辑,比如第一个条目,就是固定的,不仅仅禁止拖拽,也不能和其进行交换,这种情况下如何进行实现呢?

onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number,
           isSuccess: boolean) => {
           //第一个禁止交换
           if (insertIndex <= 0) {
             return
           }
           if (!isSuccess || insertIndex >= this.numbers.length) {
             return
           }
           this.changeIndex(itemIndex, insertIndex)
         })

很是简单,如果是第一个,直接不执行即可,以上的代码虽然实现了禁止交换,但是有一个问题,那就是实际的动画已经执行了,只是最后的交换没有执行而已,在视觉上仍然存在很大的问题。

鸿蒙开发:实现一个超简单的网格拖拽

我们想要的效果是,保留移动拖拽的动画,只是拖拽到第一个时,动画不执行,其他的该怎么执行就怎么执行。

那么,在设置supportAnimation动画的时候,就不能直接设置为true了,而是移动到条目一的位置后,设置为fasle,其他再设置为true。

如何拿到第一个item的坐标呢,其实每一个组件都有一个方法,onAreaChange,通过这个属性,我们就可以拿到任何一个组件的宽高还有,xy的坐标,当然,你也可以通过计算的方式,毕竟,屏幕的宽高,还有条目的宽高你都知道。

手势移动的坐标可以通过onTouch方法进行获取,在Move事件中进行判断,如果移动到了条目一的范围之内,取消动画,否则就执行动画。

鸿蒙开发:实现一个超简单的网格拖拽

简单实现

目前呢这个网格拖拽功能已经做了一层封装,放到了refresh库中,大家如果想直接使用,可以依赖这个库:

"dependencies": { "@abner/refresh": "^1.3.6"}

简单案例如下:

 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("拖拽结束:" + JSON.stringify(items))
            }
          })
        }.width('100%')
        .height("100%")

      }
    }

注意事项

实现拖拽,最重要的三个方法就是,打开编辑状态editMode,实现onItemDragStart和onItemDrop,设置拖拽移动动画和交换数据,如果想到开启补位动画,还需要实现supportAnimation方法。

点赞
收藏
评论区
推荐文章
徐小夕 徐小夕
4年前
基于React+Koa实现一个h5页面可视化编辑器
前言前段时间笔者一直忙于数据可视化方面的工作,比如如何实现拖拽式生成可视化大屏,如何定制可视化图表交互和数据导入方案等,这块需求在B端企业中应用非常大,所以非常有探索价值。本篇文章并非和数据可视化相关,而是通过抽象技术底层,将其应用于H5页面可视化搭建上,通过技术的手段实现拖拽式生成H5页面。这块也有非常多的应用场景,比如我们需要开发一个移动端网站,一
落落落洛克 落落落洛克
4年前
JavaScript进阶之实现拖拽(上)
我的前端学习笔记📒最近花了点时间把笔记整理到语雀上了,方便童鞋们阅读我的前端学习笔记📒(https://www.yuque.com/wanggangfeng
Alex799 Alex799
4年前
Vue进阶(幺柒幺):前端用户体验提升(五)Flex实现弹性布局
需求背景在实现组件拖拽设计过程中,发现组件样式中设置了display:flex属性信息,导致组件生成后无法实现拖拽效果,网上查阅资料后发现Flex布局大有天地。Flex来源
低代码开发平台 | 低代码的衍生历程、优势及未来趋势
通过简单的拖拉拽操作,而不用编写复杂的代码,实现少写代码或者不写代码,就能快速高效完成业务目标。低代码平台演进1.低代码概念低代码是无需编码(0代码)或通过少量代码就可以快速生成应用程序的开发平台。通过可视化进行应用程序开发的方法,具有不同经验水平的开发人员可以通过图形化的用户界面,使用拖拽组件和模型驱动的逻辑来创建网页和移动应用程序。2.低代码衍生历
徐小夕 徐小夕
4年前
可视化搭建平台的参考网格线设计
最近一工作一直很忙,H5Dooring也在持续更新迭代中,接下来笔者将带大家介绍一下H5Dooring的新功能,并介绍网格参考线的实现方案,内容很短,实现方案也很简单,欢迎大家提出更好的方案和实现思路.H5Dooring更新日志添加画布网格参考线以及快捷切换方式添加文字跑马灯组件画布操作控件支持拖拽Doori
徐小夕 徐小夕
4年前
轻松教你搞定组件的拖拽, 缩放, 多控制点伸缩和拖拽数据上报
最近笔者专注于研究可视化搭建平台的解决方案,分析了很多拖拽组件和页面动态化技术,也在H5Dooring(https://github.com/MrXujiang/h5Dooring)项目中做了很多技术实现,包括:搭建平台的组件设计和分类模式拖拽生成页面方案动态表单编辑器设计页面实时预览编译技术自定义组件和自定
Wesley13 Wesley13
3年前
Unity 实现物体拖拽
Unity实现拖拽:!(http://ddlovegg.me/wpcontent/uploads/2017/10/ezgif.comvideotogif300x169.gif)也可以继承UnityEventSystem中的接口实现。当鼠标按下的时候以左键为例:UsingSystem.Collections;
Wesley13 Wesley13
3年前
Unity 2D角色动画状态切换
一,Idle状态  1,选中角色,打开Animation动画面板;  2,新建一个动画面板Idle;  3,拖动相关角色状态图片,实现动画二,run状态  1,新建一个动画面板run;  2,拖动相关角色状态图片,实现动画三,jump状态  1,新建一个动画面板jump;  2,拖动相关角色状态图片,实现动画四,打开角
Stella981 Stella981
3年前
Echarts:实现拖拽效果
1<!DOCTYPEhtml2<html3<head4<metacharset"utf8"5<scriptsrc"./echarts.js"</script6</head7<body8<div
Wesley13 Wesley13
3年前
Unity 2D游戏开发教程之使用脚本实现游戏逻辑
Unity2D游戏开发教程之使用脚本实现游戏逻辑使用脚本实现游戏逻辑通过上一节的操作,我们不仅创建了精灵的动画,还设置了动画的过渡条件,最终使得精灵得以按照我们的意愿,进入我们所指定的动画状态。但是这其中还有一些问题。例如,我们无法使用键盘控制精灵当前要进入的动画状态,而且精