DEV Community

HarmonyOS
HarmonyOS

Posted on

Gesture Events in HarmonyOS ArkTS

Read the original article:Gesture Events in HarmonyOS ArkTS

Requirement Description

Gesture events in HarmonyOS ArkTS enable developers to handle user interactions such as taps, long presses, pans, pinches, and swipes.

These events are essential for creating responsive and intuitive UI components. This document provides a detailed guide on implementing gesture binding, single gestures, combined gestures, multi-level gesture events, and gesture judgment logic.

Background Knowledge

HarmonyOS ArkTS provides a flexible gesture system that allows developers to bind gestures to UI components, prioritize gestures, and handle complex interactions. Key concepts include:

  • Gesture Binding: Associating gestures with components using .gesture(), .priorityGesture(), and .parallelGesture().
  • Single Gestures: Individual interactions like taps, long presses, pans, pinches, and swipes.
  • Combined Gestures: Layered or simultaneous interactions using GestureGroup with modes like Sequence, Parallel, and Exclusive.
  • Multi-level Gesture Events: Handling gestures in nested UI hierarchies with responseRegion and hitTestBehavior.
  • Gesture Judgment: Resolving conflicts and prioritizing gestures using APIs like onGestureJudgeBegin and shouldBuiltInRecognizerParallelWith.

Implementation Steps

Gesture Binding

  • .gesture(): Bind a single gesture to a component.

Example: Tap, LongPress, Pan, Pinch, Rotation, or Swipe gestures.

  • .priorityGesture(): Prioritize parent gestures over child components.

Example: Ensure parent components respond to gestures even if child components are overlapping.

  • .parallelGesture(): Enable both parent and child components to respond to the same gesture.

Example: Simultaneous pan gestures on nested components.

Single Gesture

  • Define the gesture type: Use TapGesture, LongPressGesture, PanGesture, etc.
  • Bind the gesture: Use .gesture() to associate the gesture with a component.
  • Implement action logic: Use .onAction() to define the behavior triggered by the gesture.

Combined Gestures

  • Use GestureGroup: Combine multiple gestures with GestureMode (Sequence, Parallel, Exclusive).
  • Define parameters: Adjust count, distance, speed, fingers, etc., to customize gesture behavior.
  • Handle recognition: Use .onAction() or .onActionUpdate() to process gesture events.

Multi-level Gesture Events

  • Use responseRegion: Control the touch target area of a component.
  • Set hitTestBehavior: Define how components interact with touch events (Block, Transparent, None).
  • Manage priority: Use .priorityGesture() or .parallelGesture() to control gesture priority.

Gesture Judgment

  • Use onGestureJudgeBegin: Dynamically decide whether to trigger a gesture based on component state.
  • Use shouldBuiltInRecognizerParallelWith: Control parallel gesture recognition between nested components.
  • Adjust recognizer states: Enable or disable gesture recognizers based on boundary.

Code Snippet / Configuration

Single Gesture

Tap Gesture

Text("Tap Me")  
  .gesture(TapGesture({ count: 2 })  
    .onAction(() => {  
      console.info("Double tap detected");  
    })  
  )
Enter fullscreen mode Exit fullscreen mode

Long Press Gesture

Text("LongPress OnAction:" + this.count)  
  .gesture(LongPressGesture({ repeat: true })  
    .onAction(() => {  
      this.count++;  
    })  
  )
Enter fullscreen mode Exit fullscreen mode

Pan Gesture

 Text("PanGesture Offset:\nX: " + this.offsetX + "\nY: " + this.offsetY)  
  .gesture(PanGesture()  
    .onActionUpdate((event) => {  
      this.offsetX = event.offsetX;  
      this.offsetY = event.offsetY;  
    })  
  )
Enter fullscreen mode Exit fullscreen mode

Pinch Gesture

 Column()  
  .scale({ x: this.scaleValue, y: this.scaleValue })  
  .gesture(PinchGesture({ fingers: 3 })  
    .onActionUpdate((event) => {  
      this.scaleValue = event.scale;  
    })  
  )
Enter fullscreen mode Exit fullscreen mode

Rotation Gesture

Text("RotationGesture angle:" + this.angle)  
  .rotate({ angle: this.angle })  
  .gesture(RotationGesture()  
    .onActionUpdate((event) => {  
      this.angle = event.angle;  
    })  
  )
Enter fullscreen mode Exit fullscreen mode

Swipe Gesture

Column()  
  .rotate({ angle: this.rotateAngle })  
  .gesture(SwipeGesture({ direction: SwipeDirection.Vertical })  
    .onAction((event) => {  
      this.rotateAngle = event.angle;  
    })  
  )
Enter fullscreen mode Exit fullscreen mode

Combined Gestures Examples

Parallel Recognition

gesture(GestureGroup(GestureMode.Parallel,  
  TapGesture({ count: 1 })  
    .onAction(() => { this.count1++; }),  
  TapGesture({ count: 2 })  
    .onAction(() => { this.count2++; })  
))
Enter fullscreen mode Exit fullscreen mode

Exclusive Recognition

gesture(GestureGroup(GestureMode.Exclusive,  
  TapGesture({ count: 1 })  
    .onAction(() => { this.count1++; }),  
  TapGesture({ count: 2 })  
    .onAction(() => { this.count2++; })  
))
Enter fullscreen mode Exit fullscreen mode

Gesture Judgment Examples

shouldBuiltInRecognizerParallelWith

.shouldBuiltInRecognizerParallelWith((current, others) => {  
  for (let i = 0; i < others.length; i++) {  
    let target = others[i].getEventTargetInfo();  
    if (target.getId() == "inner" && others[i].isBuiltIn() && others[i].getType() == GestureControl.GestureType.PAN_GESTURE) {  
      return others[i];  
    }  
  }  
  return undefined;  
})
Enter fullscreen mode Exit fullscreen mode

onGestureRecognizerJudgeBegin

.onGestureRecognizerJudgeBegin((event, current, others) => {  
  let target = current.getEventTargetInfo();  
  if (target.getId() == "outer" && current.isBuiltIn() && current.getType() == GestureControl.GestureType.PAN_GESTURE) {  
    for (let i = 0; i < others.length; i++) {  
      let target = others[i].getEventTargetInfo() as ScrollableTargetInfo;  
      if (target instanceof ScrollableTargetInfo && target.getId() == "inner") {  
        let panEvent = event as PanGestureEvent;  
        this.childRecognizer.setEnabled(true);  
        this.currentRecognizer.setEnabled(false);  
        if (target.isEnd()) {  
          if (panEvent && panEvent.offsetY < 0) {  
            this.childRecognizer.setEnabled(false);  
            this.currentRecognizer.setEnabled(true);  
          }  
        }  
      }  
    }  
  }  
  return GestureJudgeResult.CONTINUE;  
})
Enter fullscreen mode Exit fullscreen mode

Test Results

Single Gestures:

  • Tap, LongPress, Pan, Pinch, Rotation, and Swipe gestures are recognized correctly.
  • Parameters like count, distance, speed, and fingers affect gesture behavior.

Combined Gestures:

  • Sequence Mode: Gestures are recognized in order; if one fails, others are ignored.
  • Parallel Mode: Gestures are recognized simultaneously (e.g., Tap and Double-Tap).
  • Exclusive Mode: Only the first recognized gesture is processed (e.g., Tap overrides Double-Tap if bound first).

Multi-level Gesture Events:

  • hitTestBehavior controls whether child components receive touch events.
  • responseRegion defines the area where gestures are recognized.

Gesture Judgment:

  • onGestureJudgeBegin dynamically decides whether to trigger gestures based on component state.
  • shouldBuiltInRecognizerParallelWith enables parallel recognition between nested components.

Limitations or Considerations

Conflict Resolution:

  • Use responseRegion and hitTestBehavior to avoid unintended behavior in nested hierarchies.
  • Test thoroughly to resolve conflicts between parent and child components.

Performance:

  • Optimize gesture parameters (e.g., distance, speed, count) for smooth interactions.
  • Avoid overusing gestures in complex UIs to prevent performance issues.

User Experience:

  • Ensure gestures align with user expectations (e.g., long press feedback).
  • Provide visual or auditory feedback for gestures like taps and swipes.

Testing:

  • Always test on real devices to ensure expected behavior in nested hierarchies.
  • Validate gesture recognition under different conditions (e.g., overlapping components, touch targets).

Related Documents or Links

Written by Emine INAN

Top comments (0)