首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

HarmonyOS Next开发学习手册——拖拽事件

  • 25-02-22 07:00
  • 2482
  • 10643
blog.csdn.net

概述

拖拽框架提供了一种通过鼠标或手势触屏的方式传递数据,即从一个组件位置拖出数据,并拖入到另一个组件位置上进行响应,拖出一方提供数据,拖入一方接收和处理数据。该操作可以让用户方便地移动、复制或删除指定内容。

  • 拖拽操作:在某个能够响应拖出的组件上长按并滑动触发的拖拽行为,当用户释放时,拖拽操作结束;
  • 拖拽背景(背板):用户所拖动数据的形象化表示,开发者可通过 onDragStart 的 CustomerBuilder 或 DragItemInfo 设置,也可以通过 dragPreview 通用属性设置;
  • 拖拽内容:拖动的数据,使用UDMF统一API UnifiedData  进行封装;
  • 拖出对象:触发拖拽操作并提供数据的组件;
  • 拖入目标:可接收并处理拖动数据的组件;
  • 拖拽点:鼠标或手指等与屏幕的接触位置,是否进入组件范围的判定是以接触点是否进入范围进行判断。

拖拽流程

手势拖拽

对于手势长按触发拖拽的场景,发起拖拽前框架侧会对当前组件是否可拖拽进行校验,针对默认可拖出的组件( Search 、 TextInput 、 TextArea 、 RichEditor 、 Text 、 Image 、 Hyperlink )需要判断是否设置了 draggable 属性为true(若系统使能分层参数,则draggable默认为true),其他组件需要额外判断是否设置了onDragStart回调函数,在满足上述可拖拽条件下,长按大于等于500ms可触发拖拽,长按800ms开始做预览图的浮起动效。

手势拖拽(手指/手写笔)触发拖拽流程:

鼠标拖拽

鼠标拖拽属于即拖即走,只要鼠标左键在可拖拽的组件上按下并移动大于1vp就可触发拖拽。

当前支持应用内和跨应用拖拽,提供了多个回调事件供开发者感知拖拽状态并干预系统默认拖拽行为,具体如下:

回调事件说明
onDragStart支持拖出的组件产生拖出动作时触发。该回调可以感知拖拽行为的发起,开发者可通过在 onDragStart 方法中设置拖拽所传递的数据以及自定义拖拽背板图。推荐开发者使用pixelmap的方式返回背板图,不推荐使用customBuilder的方式,会有额外的性能开销。
onDragEnter当拖拽活动的拖拽点进入组件范围内时触发,只有该组件监听了 onDrop 事件时,此回调才会被触发。
onDragMove拖拽点在组件范围内移动时触发;只有该组件监听了onDrop事件时,此回调才会被触发。
在此过程中可通过 DragEvent 中的setResult方法影响系统部分场景下的外观
1. 设置DragResult.DROP_ENABLED;
2. 设置DragResult.DROP_DISABLED。
onDragLeave拖拽点离开组件范围时触发;只有该组件监听了onDrop事件时,此回调才会被触发。
针对以下两种情况默认不会发送onDragLeave事件:
1. 父组件移动到子组件;
2. 目标组件与当前组件布局有重叠;
API version 12开始可通过 UIContext 中的 setDragEventStrictReportingEnabled 方法严格触发onDragLeave事件。
onDrop当用户在组件范围内释放时触发,需在此回调中通过DragEvent中的setResult方法设置拖拽结果,否则在拖出方组件的onDragEnd方法中通过getRresult方法只能拿到默认的处理结果DragResult.DRAG_FAILED。
该回调也是开发者干预系统默认拖入处理行为的地方,系统会优先执行开发者的onDrop回调,通过在回调中执行setResult方法来告知系统该如何处理所拖拽的数据;
1. 设置 DragResult.DRAG_SUCCESSFUL,数据完全由开发者自己处理,系统不进行处理;
2. 设置DragResult.DRAG_FAILED,数据不再由系统继续处理;
3. 设置DragResult.DRAG_CANCELED,系统也不需要进行数据处理;
4. 设置DragResult.DROP_ENABLED或DragResult.DROP_DISABLED会被忽略,同设置DragResult.DRAG_FAILED;
onDragEnd当用户释放拖拽时,拖拽活动结束,发起拖出动作的组件会触发该回调。
onPreDrag绑定此事件的组件,当触发拖拽发起前的不同阶段时,触发回调。
开发者可以使用该方法监听 PreDragStatus 中的枚举在发起拖拽前的不同阶段准备数据。
1. ACTION_DETECTING_STATUS:拖拽手势启动阶段。(按下50ms时触发);
2. READY_TO_TRIGGER_DRAG_ACTION:拖拽准备完成,可发起拖拽阶段。(按下500ms时触发);
3. PREVIEW_LIFT_STARTED:拖拽浮起动效发起阶段。(按下800ms时触发);
4. PREVIEW_LIFT_FINISHED:拖拽浮起动效结束阶段。(浮起动效完全结束时触发);
5. PREVIEW_LANDING_STARTED:拖拽落回动效发起阶段。(落回动效发起时触发);
6. PREVIEW_LANDING_FINISHED:拖拽落回动效结束阶段。(落回动效结束时触发);
7. ACTION_CANCELED_BEFORE_DRAG:拖拽浮起落位动效中断。(已满足READY_TO_TRIGGER_DRAG_ACTION状态后,未达到动效阶段,手指抬手时触发)。

拖拽背板图

拖拽移动过程中显示的拖拽背板图,并非是组件本身,其是用户拖动数据的表示,开发者可以将其设置为任意可显示的图像。其中onDragStart 回调返回的customBuilder或pixelmap可以设置拖拽移动过程中的背板图,浮起图默认使用组件本身的截图;dragpreview属性设置的customBuilder或pixelmap可以设置浮起和拖拽过程的背板图;如果开发者没有配置背板图,则系统会默认取组件本身的截图作为浮起及拖拽过程中的背板图。

拖拽背板图当前支持设置透明度、圆角、阴影和模糊,具体用法见: 拖拽控制

约束:

  • 对于容器组件,如果内部内容通过position,offset等手段使得绘制区域超出了容器组件范围,则系统截图无法截取到范围之外的内容,此种情况下,如果一定要浮起及拖拽背板能够包含范围之外的内容,则可考虑通过扩大容器范围或自定义方式进行;
  • 不管是使用自定义builder或是系统默认截图方式,截图都暂时无法应用 scale 、 rotate 等图形变换效果。

开发步骤

通用拖拽适配

如下以 Image 组件为例,介绍组件拖拽开发的基本步骤,以及开发中需要注意的事项。

  1. 组件使能拖拽
  • 设置draggable属性为true,并设置onDragStart回调,回调中可以通过UDMF设置拖拽的数据,并返回自定义拖拽背板图;
import UDC from '@ohos.data.unifiedDataChannel';
import UTD from '@ohos.data.uniformTypeDescriptor';

Image($r('app.media.app_icon'))
    .width(100)
    .height(100)
    .draggable(true)
    .onDragStart((event) => {
        let data: UDC.Image = new UDC.Image();
        data.imageUri = 'common/pic/img.png';
        let unifiedData = new UDC.UnifiedData(data);
        event.setData(unifiedData);

        let dragItemInfo: DragItemInfo = {
        pixelMap: this.pixmap,
        extraInfo: "this is extraInfo",
        };
        // onDragStart回调函数中返回自定义拖拽背板图
        return dragItemInfo;
    })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 手势场景触发拖拽抵赖底层绑定的长按手势,若开发者在被拖拽组件上也绑定长按手势,则会与底层的长按手势发生竞争,导致拖拽失败。可以用并行手势解决解决此类问题,如下:
.parallelGesture(LongPressGesture().onAction(() => {
   promptAction.showToast({ duration: 100, message: 'Long press gesture trigger' });
}))
  • 1
  • 2
  • 3
  1. 自定义拖拽背板图
  • 自定义拖拽背板图的pixmap可以通过设置onPreDrag函数在长按50ms时触发的回调中前提准备;
.onPreDrag((status: PreDragStatus) => {
    if (preDragStatus == PreDragStatus.ACTION_DETECTING_STATUS) {
        this.getComponentSnapshot();
    }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 具体pixmap的生成可以调用 componentSnapshot 函数;
@Builder
pixelMapBuilder() {
    Column() {
      Image($r('app.media.startIcon'))
        .width(120)
        .height(120)
        .backgroundColor(Color.Yellow)
    }
  }
  private getComponentSnapshot(): void {
  componentSnapshot.createFromBuilder(()=>{this.pixelMapBuilder()},
  (error: Error, pixmap: image.PixelMap) => {
      if(error){
        console.log("error: " + JSON.stringify(error))
        return;
      }
      this.pixmap = pixmap;
  })
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  1. 如果开发者要严格触发 onDragLeave 事件,可以通过 setDragEventStrictReportingEnabled 方法设置。
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
import { UIContext } from '@ohos.arkui.UIContext';

export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/Index', (err, data) => {
      if (err.code) {
        return;
      }
      windowStage.getMainWindow((err, data) => {
        if (err.code) {
          return;
        }
        let windowClass: window.Window = data;
        let uiContext: UIContext = windowClass.getUIContext();
        uiContext.getDragController().setDragEventStrictReportingEnabled(true);
      });
    });
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  1. 拖拽过程显示角标样式
  • 可以通过设置 allowDrop 定义接收的数据类型影响角标显示,当拖拽数据是定义允许落入的数据类型时,显示COPY角标;当拖拽数据不在定义允许落入的数据类型范围时,显示FORBIDDEN角标;未设置allowDrop时,显示MOVE角标。如下代码表示只接收UnifiedData中定义的HYPERLINK和PLAIN_TEXT类型的数据,其他数据类型将禁止落入;
.allowDrop([UTD.UniformDataType.HYPERLINK, UTD.UniformDataType.PLAIN_TEXT])
  • 1
  • 此外在实现onDrop回调的情况下还可以通过在onDragMove中设置 DragResult 为DROP_ENABLED,并设置 DragBehavior 为COPY或MOVE控制角标显示。如下代码将移动时的角标强制设置为MOVE;
.onDragMove((event) => {
    event.setResult(DragResult.DROP_ENABLED);
    event.dragBehavior = DragBehavior.MOVE;
})
  • 1
  • 2
  • 3
  • 4
  1. 拖拽数据的接收
  • 需要设置onDrop回调,并在回调中处理拖拽数据,显示设置拖拽结果
.onDrop((dragEvent?: DragEvent) => {
    // 获取拖拽数据
    this.getDataFromUdmf((dragEvent as DragEvent), (event: DragEvent) => {
    let records: Array = event.getData().getRecords();
    let rect: Rectangle = event.getPreviewRect();
    this.imageWidth = Number(rect.width);
    this.imageHeight = Number(rect.height);
    this.targetImage = (records[0] as UDC.Image).imageUri;
    this.imgState = Visibility.None;
    // 显式设置result为successful,则将该值传递给拖出方的onDragEnd
    event.setResult(DragResult.DRAG_SUCCESSFUL);
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 数据的传递是通过UDMF实现的,在数据较大时可能存在时延,因此在首次获取数据失败时建议加1500ms的延迟重试机制:
getDataFromUdmfRetry(event: DragEvent, callback: (data: DragEvent) => void) {
  try {
    let data: UnifiedData = event.getData();
    if (!data) {
      return false;
    }
    let records: Array = data.getRecords();
    if (!records || records.length <= 0) {
      return false;
    }
    callback(event);
    return true;
  } catch (e) {
    console.log("getData failed, code: " + (e as BusinessError).code + ", message: " + (e as BusinessError).message);
    return false;
  }
}

getDataFromUdmf(event: DragEvent, callback: (data: DragEvent) => void) {
 if (this.getDataFromUdmfRetry(event, callback)) {
   return;
 }
 setTimeout(() => {
   this.getDataFromUdmfRetry(event, callback);
 }, 1500);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  1. 拖拽发起方可以通过设置onDragEnd回调感知拖拽结果
import promptAction from '@ohos.promptAction';

.onDragEnd((event) => {
    // onDragEnd里取到的result值在接收方onDrop设置
  if (event.getResult() === DragResult.DRAG_SUCCESSFUL) {
    promptAction.showToast({ duration: 100, message: 'Drag Success' });
  } else if (event.getResult() === DragResult.DRAG_FAILED) {
    promptAction.showToast({ duration: 100, message: 'Drag failed' });
  }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

多选拖拽适配

API version 12开始 Grid 组件和 List 组件中的GridItem和ListItem组件支持多选拖拽,当前只支持onDragStart的方式。如下以Grid为例,介绍多选拖拽的基本步骤,以及开发中的注意事项。

  1. 组件多选拖拽使能
  • 创建GridItem子组件并绑定onDragStart函数。同时设置GridItem组件的状态是可选中的;
Grid() {
  ForEach(this.numbers, (idx: number) => {
    GridItem() {
      Column()
        .backgroundColor(this.colors[idx % 9])
        .width(50)
        .height(50)
        .opacity(1.0)
        .id('grid'+idx)
    }
    .onDragStart(()=>{})
    .selectable(true)
  }, (idx: string) => idx)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 多选拖拽功能默认为关闭状态,使用多选拖拽需要在 dragPreviewOptions 接口中的DragInteractionOptions参数中设置isMultiSelectionEnabled为true,表示当前组件是否多选。DragInteractionOptions也有支持组件浮起前默认效果的参数defaultAnimationBeforeLifting,设置该参数为true后组件在浮起前会有一个默认缩小动效。
.dragPreviewOptions({isMultiSelectionEnabled:true,defaultAnimationBeforeLifting:true})
  • 1
  • 为了保证选中状态,需要设置GridItem子组件selected状态为true。例如,可以通过 onClick 的调用去设置特定的组件为选中的状态。
.selected(this.isSelectedGrid[idx])
.onClick(()=>{
    this.isSelectedGrid[idx] = !this.isSelectedGrid[idx]
})
  • 1
  • 2
  • 3
  • 4
  1. 多选拖拽性能优化
  • 多选拖拽情况下,多选会有聚拢的动画效果,聚拢效果触发时,会给当前屏幕内显示的选中组件截图,当选中组件过多,会导致很高的性能开销。多选拖拽支持从dragPreview中获取截图来作为聚拢动效的截图,以节省性能。
.dragPreview({
    pixelMap:this.pixmap
})
  • 1
  • 2
  • 3
  • 截图的获取可以在选中组件时通过调用componentSnapshot中的get方法获取。如下通过获取组件对应id的方法进行截图。
@State previewData: DragItemInfo[] = []
@State isSelectedGrid: boolean[] = []
.onClick(()=>{
    this.isSelectedGrid[idx] = !this.isSelectedGrid[idx]
    if (this.isSelectedGrid[idx]) {
        let gridItemName = 'grid' + idx
        componentSnapshot.get(gridItemName, (error: Error, pixmap: image.PixelMap)=>{
            this.pixmap = pixmap
            this.previewData[idx] = {
                pixelMap:this.pixmap
            }
        })
    }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  1. 多选显示效果

    通过stateStyles可以设置选中态和非选中态的显示效果,方便区分。

@Styles
normalStyles(): void{
  .opacity(1.0)
}

@Styles
selectStyles(): void{
  .opacity(0.4)
}

.stateStyles({
  normal : this.normalStyles,
  selected: this.selectStyles
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  1. 数量角标适配

    多选拖拽的数量角标当前需要应用使用dragPreviewOptions中的numberBadge参数设置,开发者需要根据当前选中的节点数量来设置数量角标。

@State numberBadge: number = 0;

.onClick(()=>{
    this.isSelectedGrid[idx] = !this.isSelectedGrid[idx]
    if (this.isSelectedGrid[idx]) {
      this.numberBadge++;
    } else {
      this.numberBadge--;
  }
})
// 多选场景右上角数量角标需要应用设置numberBadge参数
.dragPreviewOptions({numberBadge: this.numberBadge})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

完整示例

通用拖拽适配案例

import UDC from '@ohos.data.unifiedDataChannel';
import UTD from '@ohos.data.uniformTypeDescriptor';
import promptAction from '@ohos.promptAction';
import { BusinessError } from '@ohos.base';
import image from '@ohos.multimedia.image'
import componentSnapshot from '@ohos.arkui.componentSnapshot'

@Entry
@Component
struct Index {
  @State targetImage: string = '';
  @State imageWidth: number = 100;
  @State imageHeight: number = 100;
  @State imgState: Visibility = Visibility.Visible;
  @State pixmap: image.PixelMap|undefined = undefined

  @Builder
  pixelMapBuilder() {
    Column() {
      Image($r('app.media.startIcon'))
        .width(120)
        .height(120)
        .backgroundColor(Color.Yellow)
    }
  }

  getDataFromUdmfRetry(event: DragEvent, callback: (data: DragEvent) => void) {
    try {
      let data: UnifiedData = event.getData();
      if (!data) {
        return false;
      }
      let records: Array = data.getRecords();
      if (!records || records.length <= 0) {
        return false;
      }
      callback(event);
      return true;
    } catch (e) {
      console.log("getData failed, code: " + (e as BusinessError).code + ", message: " + (e as BusinessError).message);
      return false;
    }
  }
  // 获取UDMF数据,首次获取失败后添加1500ms延迟重试机制
  getDataFromUdmf(event: DragEvent, callback: (data: DragEvent) => void) {
    if (this.getDataFromUdmfRetry(event, callback)) {
      return;
    }
    setTimeout(() => {
      this.getDataFromUdmfRetry(event, callback);
    }, 1500);
  }
  // 调用componentSnapshot中的createFromBuilder接口截取自定义builder的截图
  private getComponentSnapshot(): void {
    componentSnapshot.createFromBuilder(()=>{this.pixelMapBuilder()},
      (error: Error, pixmap: image.PixelMap) => {
        if(error){
          console.log("error: " + JSON.stringify(error))
          return;
        }
        this.pixmap = pixmap;
      })
  }
  // 长按50ms时提前准备自定义截图的pixmap
  private PreDragChange(preDragStatus: PreDragStatus): void {
    if (preDragStatus == PreDragStatus.ACTION_DETECTING_STATUS) {
      this.getComponentSnapshot();
    }
  }

  build() {
    Row() {
      Column() {
        Text('start Drag')
          .fontSize(18)
          .width('100%')
          .height(40)
          .margin(10)
          .backgroundColor('#008888')
        Row() {
          Image($r('app.media.app_icon'))
            .width(100)
            .height(100)
            .draggable(true)
            .margin({ left: 15 })
            .visibility(this.imgState)
            // 绑定平行手势,可同时触发应用自定义长按手势
            .parallelGesture(LongPressGesture().onAction(() => {
              promptAction.showToast({ duration: 100, message: 'Long press gesture trigger' });
            }))
            .onDragStart((event) => {
              let data: UDC.Image = new UDC.Image();
              data.imageUri = 'common/pic/img.png';
              let unifiedData = new UDC.UnifiedData(data);
              event.setData(unifiedData);

              let dragItemInfo: DragItemInfo = {
                pixelMap: this.pixmap,
                extraInfo: "this is extraInfo",
              };
              return dragItemInfo;
            })
              // 提前准备拖拽自定义背板图
            .onPreDrag((status: PreDragStatus) => {
              this.PreDragChange(status);
            })
            .onDragEnd((event) => {
              // onDragEnd里取到的result值在接收方onDrop设置
              if (event.getResult() === DragResult.DRAG_SUCCESSFUL) {
                promptAction.showToast({ duration: 100, message: 'Drag Success' });
              } else if (event.getResult() === DragResult.DRAG_FAILED) {
                promptAction.showToast({ duration: 100, message: 'Drag failed' });
              }
            })
        }

        Text('Drag Target Area')
          .fontSize(20)
          .width('100%')
          .height(40)
          .margin(10)
          .backgroundColor('#008888')
        Row() {
          Image(this.targetImage)
            .width(this.imageWidth)
            .height(this.imageHeight)
            .draggable(true)
            .margin({ left: 15 })
            .border({ color: Color.Black, width: 1 })
            // 控制角标显示类型为MOVE,即不显示角标
            .onDragMove((event) => {
              event.setResult(DragResult.DROP_ENABLED)
              event.dragBehavior = DragBehavior.MOVE
            })
            .allowDrop([UTD.UniformDataType.IMAGE])
            .onDrop((dragEvent?: DragEvent) => {
              // 获取拖拽数据
              this.getDataFromUdmf((dragEvent as DragEvent), (event: DragEvent) => {
                let records: Array = event.getData().getRecords();
                let rect: Rectangle = event.getPreviewRect();
                this.imageWidth = Number(rect.width);
                this.imageHeight = Number(rect.height);
                this.targetImage = (records[0] as UDC.Image).imageUri;
                this.imgState = Visibility.None;
                // 显式设置result为successful,则将该值传递给拖出方的onDragEnd
                event.setResult(DragResult.DRAG_SUCCESSFUL);
              })
            })
        }
      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156

多选拖拽适配案例

import componentSnapshot from "@ohos.arkui.componentSnapshot";
import image from '@ohos.multimedia.image'

@Entry
@Component
struct GridEts {
  @State pixmap: image.PixelMap|undefined = undefined
  @State numbers: number[] = []
  @State isSelectedGrid: boolean[] = []
  @State previewData: DragItemInfo[] = []
  @State colors: Color[] = [Color.Red, Color.Blue, Color.Brown, Color.Gray, Color.Green, Color.Grey, Color.Orange,Color.Pink ,Color.Yellow]
  @State numberBadge: number = 0;

  @Styles
  normalStyles(): void{
    .opacity(1.0)
  }

  @Styles
  selectStyles(): void{
    .opacity(0.4)
  }

  onPageShow(): void {
    let i: number = 0
    for(i=0;i<100;i++){
      this.numbers.push(i)
      this.isSelectedGrid.push(false)
      this.previewData.push({})
    }
  }

  @Builder
  RandomBuilder(idx: number) {
    Column()
      .backgroundColor(this.colors[idx % 9])
      .width(50)
      .height(50)
      .opacity(1.0)
  }

  build() {
    Column({ space: 5 }) {
      Grid() {
        ForEach(this.numbers, (idx: number) => {
          GridItem() {
            Column()
              .backgroundColor(this.colors[idx % 9])
              .width(50)
              .height(50)
              .opacity(1.0)
              .id('grid'+idx)
          }
          .dragPreview(this.previewData[idx])
          .selectable(true)
          .selected(this.isSelectedGrid[idx])
          // 设置多选显示效果
          .stateStyles({
            normal : this.normalStyles,
            selected: this.selectStyles
          })
          .onClick(()=>{
            this.isSelectedGrid[idx] = !this.isSelectedGrid[idx]
            if (this.isSelectedGrid[idx]) {
              this.numberBadge++;
              let gridItemName = 'grid' + idx
              // 选中状态下提前调用componentSnapshot中的get接口获取pixmap
              componentSnapshot.get(gridItemName, (error: Error, pixmap: image.PixelMap)=>{
                this.pixmap = pixmap
                this.previewData[idx] = {
                  pixelMap:this.pixmap
                }
              })
            } else {
              this.numberBadge--;
            }
          })
          // 使能多选拖拽,右上角数量角标需要应用设置numberBadge参数
          .dragPreviewOptions({numberBadge: this.numberBadge},{isMultiSelectionEnabled:true,defaultAnimationBeforeLifting:true})
          .onDragStart(()=>{
          })
        }, (idx: string) => idx)
      }
      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
      .columnsGap(5)
      .rowsGap(10)
      .backgroundColor(0xFAEEE0)
    }.width('100%').margin({ top: 5 })
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90

鸿蒙全栈开发全新学习指南

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以要有一份实用的鸿蒙(HarmonyOS NEXT)学习路线与学习文档用来跟着学习是非常有必要的。

针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。

本路线共分为四个阶段:

第一阶段:鸿蒙初中级开发必备技能

在这里插入图片描述

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:gitee.com/MNxiaona/733GH

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

鸿蒙入门教学视频:

美团APP实战开发教学:gitee.com/MNxiaona/733GH

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing?,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH

鸿蒙开发学习资料领取!!!
微信名片
注:本文转载自blog.csdn.net的OpenHarmony_小贾的文章"https://blog.csdn.net/maniuT/article/details/140218166"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top