HarmomyOS 触屏事件(点击、触摸、拖拽)

本文详细介绍了如何在HarmonyOS中实现单击事件,包括定义组件、分配ID、绑定事件、重写onClick方法和四种常见事件配置方式:箭头函数、匿名函数、成员函数和声明的箭头函数。
摘要由CSDN通过智能技术生成

1. HarmomyOS 触屏事件

  触屏事件指当手指/手写笔在组件上按下、滑动、抬起时触发的回调事件。包括点击事件、触摸事件和拖拽事件。
在这里插入图片描述

1.1. 点击事件

  击事件是指通过手指或手写笔做出一次完整的按下和抬起动作。当发生点击事件时,会触发以下回调函数:单击事件是我们平时操作过程中触发的最多的事件。事件方法以“.”链式调用的方式配置系统组件支持的事件,建议每个事件方法单独写一行。
  event参数提供点击事件相对于窗口或组件的坐标位置,以及发生点击的事件源。

onClick(event: (event?: ClickEvent) => void)

1.1.1. 实现步骤

  实现HarmonyOS(鸿蒙)的单击事件主要分为四个步骤:
(1)定义组件,给组件分配唯一ID,之后通过ID定位组件
(2)给定义的组件绑定单击事件
(3)实现ClickedListener接口并重写onClick方法
(4)实现onClick方法中的具体逻辑,以此完成点击事件的相关业务操作

1.1.2. 实现方法

(1)使用箭头函数配置组件的事件方法。

Button('Click me')
  .onClick(() => {
    this.myText = 'ArkUI';
  })

(2)使用匿名函数表达式配置组件的事件方法,要求使用bind,以确保函数体中的this指向当前组件。

Button('add counter')
  .onClick(function(){
    this.counter += 2;
  }.bind(this))

(3)使用组件的成员函数配置组件的事件方法。

myClickHandler(): void {
  this.counter += 2;
}
Button('add counter')
  .onClick(this.myClickHandler.bind(this))

(4)使用声明的箭头函数,可以直接调用,不需要bind this

fn = () => {
  console.info(`counter: ${this.counter}`)
  this.counter++
}
Button('add counter')
  .onClick(this.fn)

1.1.3. 完整代码

import { promptAction, router } from '@kit.ArkUI'
import { RouterParams } from '@zzsKit/zzsLib'
import { TitleBar } from '../../../components/common/TitleBar'

@Extend(Button)
function buttonItem() {
  .stateEffect(true)
  .type(ButtonType.Normal)
  .borderRadius(8)
  .fontSize(17)
  .backgroundColor($r('app.color.primary_green'))
  .padding({
    top: 8,
    bottom: 8,
    left: 70,
    right: 70
  })
  .margin({
    top: 15,
    bottom: 15
  })
}

@Entry
@Component
struct ClickPage {
  @State pageTitle: string = "点击事件"
  @State text: string = '点击事件'

  aboutToAppear() {
    try {
      this.pageTitle = (router
        .getParams() as RouterParams).title
    } catch (e) {
    }
  }

  private myClick() {
    promptAction.showToast(
      { message: "点击" })
    this.text = "点击"
  }

  private myClick5(event: ClickEvent) {
    promptAction.showToast(
      { message: "点击" })
    this.text = "点击" + JSON.stringify(event)
  }

  build() {
    Column() {
      TitleBar({ pageTitle: $pageTitle })
      Button('点击1')
        .buttonItem()
        .onClick((event: ClickEvent) => {
          this.text = "点击1"
          promptAction.showToast(
            { message: "点击1" })
        })
      Button('点击3')
        .buttonItem()
        .onClick(() => this.myClick?.())
      Button('点击4')
        .buttonItem()
        .onClick(() => this.myClick())
      Button('点击5')
        .buttonItem()
        .onClick((event: ClickEvent) => this.myClick5(event))
      Button('点击6')
        .buttonItem()
        .onClick(() => {
          promptAction.showToast(
            { message: "event" })
        })
      Button('Click').width(100).height(40)
        .onClick((event?: ClickEvent) => {
          if (event) {
            this.text = 'Click Point:' + '\n  windowX:' + event.windowX + '\n  windowY:' + event.windowY
              + '\n  x:' + event.x + '\n  y:' + event.y + '\ntarget:' + '\n  component globalPos:('
              + event.target.area.globalPosition.x + ',' + event.target.area.globalPosition.y + ')\n  width:'
              + event.target.area.width + '\n  height:' + event.target.area.height + '\ntimestamp' + event.timestamp;
          }
        })
      Text(this.text).margin(15)
    }
    .width('100%')
    .height('100%')
    .alignItems(HorizontalAlign.Center)
  }
}

在这里插入图片描述

1.2. 触摸事件

  当手指或手写笔在组件上触碰时,会触发不同动作所对应的事件响应,包括按下(Down)、滑动(Move)、抬起(Up)事件:

onTouch(event: (event?: TouchEvent) => void)

(1)event.type为TouchType.Down:表示手指按下。
(2)event.type为TouchType.Up:表示手指抬起。
(3)event.type为TouchType.Move:表示手指按住移动。
(4)event.type为TouchType.Cancel:表示打断取消当前手指操作。
  触摸事件可以同时多指触发,通过event参数可获取触发的手指位置、手指唯一标志、当前发生变化的手指和输入的设备源等信息。

import { router } from '@kit.ArkUI';
import { RouterParams } from '@zzsKit/zzsLib';
import { TitleBar } from '../../../components/common/TitleBar';

@Entry
@Component
struct TouchPage {
  @State text: string = '';
  @State eventType: string = '';
  @State pageTitle: string = "点击事件"
  aboutToAppear() {
    try {
      this.pageTitle = (router
        .getParams() as RouterParams).title
    } catch (e) {
    }
  }
  build() {
    Column() {
      TitleBar({ pageTitle: $pageTitle })
      Button('Touch').height(40).width(100)
        .onTouch((event?: TouchEvent) => {
          if (event) {
            if (event.type === TouchType.Down) {
              //按下
              this.eventType = '按下';
            }
            if (event.type === TouchType.Up) {
              //抬起
              this.eventType = '抬起';
            }
            if (event.type === TouchType.Move) {
              //移动
              this.eventType = '移动';
            }
            this.text = '触摸类型:' + this.eventType
              + '\n触摸点与触摸之间的距离:\n'
              + 'x: ' + event.touches[0].x + '\n'
              + 'y: ' + event.touches[0].y
              + '\nComponent globalPos:('
              + event.target.area.globalPosition.x + ','
              + event.target.area.globalPosition.y + ')\n'
              + 'width:' + event.target.area.width + '\n'
              + 'height:' + event.target.area.height
          }
        })
      Text(this.text)
    }
  }
}

在这里插入图片描述

1.3. 拖拽事件

  拖拽框架提供了一种通过鼠标或手势触屏的方式传递数据,即从一个组件位置拖出数据,并拖入到另一个组件位置上进行响应,拖出一方提供数据,拖入一方接收和处理数据。该操作可以让用户方便地移动、复制或删除指定内容。
(1)拖拽操作:在某个能够响应拖出的组件上长按并滑动触发的拖拽行为,当用户释放时,拖拽操作结束;
(2)拖拽背景(背板):用户所拖动数据的形象化表示,开发者可通过onDragStart的CustomerBuilder或DragItemInfo设置,也可以通过dragPreview通用属性设置;
(3)拖拽内容:拖动的数据,使用UDMF统一API UnifiedData 进行封装;
(4)拖出对象:触发拖拽操作并提供数据的组件;
(5)拖入目标:可接收并处理拖动数据的组件;
(6)拖拽点:鼠标或手指等与屏幕的接触位置,是否进入组件范围的判定是以接触点是否进入范围进行判断。

1.3.1. 通用拖拽适配

import { unifiedDataChannel, uniformTypeDescriptor } from '@kit.ArkData';
import { promptAction, componentSnapshot } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
import { image } from '@kit.ImageKit';

@Entry
@Component
struct DragPage {
  @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<unifiedDataChannel.UnifiedRecord> = 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: unifiedDataChannel.Image = new unifiedDataChannel.Image();
              data.imageUri = 'common/pic/img.png';
              let unifiedData = new unifiedDataChannel.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([uniformTypeDescriptor.UniformDataType.IMAGE])
            .onDrop((dragEvent?: DragEvent) => {
              // 获取拖拽数据
              this.getDataFromUdmf((dragEvent as DragEvent), (event: DragEvent) => {
                let records: Array<unifiedDataChannel.UnifiedRecord> = event.getData().getRecords();
                let rect: Rectangle = event.getPreviewRect();
                this.imageWidth = Number(rect.width);
                this.imageHeight = Number(rect.height);
                this.targetImage = (records[0] as unifiedDataChannel.Image).imageUri;
                this.imgState = Visibility.None;
                // 显式设置result为successful,则将该值传递给拖出方的onDragEnd
                event.setResult(DragResult.DRAG_SUCCESSFUL);
              })
            })
        }
      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
  }
}

在这里插入图片描述

1.3.2. 多选拖拽适配

import { componentSnapshot } from '@kit.ArkUI';
import { image } from '@kit.ImageKit';

@Entry
@Component
struct Drag2Page {
  @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 })
  }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值