DEV Community

HarmonyOS
HarmonyOS

Posted on

Multi-level Layout: How to Pass Gesture Events from Upper Layers to Lower-level Controls

Read the original article:Multi-level Layout: How to Pass Gesture Events from Upper Layers to Lower-level Controls

equirement Description

Due to the need to implement video playback control gestures, the following code cannot control volume, brightness, and progress by swiping on the screen.

 Stack() {
   Column() {
     // Component B
   }
   .gesture(PanGesture()) // Expected to receive the passed gesture here
   Column() {
     // Component C, containing subcomponents D, E, and F; expected to pass the gesture event upward to the component above
     Blank()
       .parallelGesture(null, GestureMask.IgnoreInternal) 
     this.bottom()
   }
   .parallelGesture(null, GestureMask.IgnoreInternal)
 }
Enter fullscreen mode Exit fullscreen mode

The layered layout is shown below: Component B represents lower-level elements such as volume and brightness controls, while D/E/F represent upper-layer components.

cke_4586.png

Expected result:

Gesture operations on components D, E, and F should be passed through to component B, enabling control of volume, brightness, and progress, without triggering gesture operations in C, D, E, or F.

Actual issue:

Gestures only work on the upper-layer components D, E, and F and cannot be passed down to the target lower-layer component B.

Background Knowledge

You can control the multi-layer gesture event competition process using the hitTestBehavior property.

  • HitTestMode.Transparent: Responds to touch tests itself, but does not block sibling nodes’ touch tests.
  • HitTestMode.Block: Responds to touch tests itself and blocks touch tests for child and sibling nodes, preventing their onTouch events and gestures from being triggered.
  • HitTestMode.None: Does not respond to touch tests and does not block touch controls of child or sibling nodes.

Implementation Steps

To meet the business requirements, organize the UI hierarchy so that the upper-layer components do not handle gesture responses but instead pass them to the lower-layer components for processing.

According to the hitTestBehavior definition, selecting HitTestMode.None ensures that upper layers do not respond, allowing gesture events to pass through to child nodes.

Code Snippet / Configuration

@Entry
@Component
struct Index {
  build() {
    Stack() {
      Column() {
        Column() {
          Text('ColumnB')
        }
        .onTouch(() => {
          console.info('Executing a gesture:ComponentB');
        })
        .gesture(TapGesture({ count: 1 }))
        .backgroundColor(Color.Green)
        .width('90%')
        .height('90%')
      }
      // Component C uses .hitTestBehavior(HitTestMode.None), so it does not respond to touch tests
      ComponentC()
        .onTouch(() => {
          console.info('Executing a gesture:ComponentC');
        })
        .gesture(TapGesture({ count: 1 }))
        .backgroundColor(Color.Blue)
        .width('80%')
        .height('80%')
        .hitTestBehavior(HitTestMode.None)
    }
    .onTouch(() => {
      console.info('Executing a gesture:StackA');
    })
    .gesture(TapGesture({ count: 1 }))
    .backgroundColor(Color.Gray)
    .width('100%')
    .height('100%')
    .hitTestBehavior(HitTestMode.None)
  }
}

@Component
struct ComponentC {
  build() {
    Flex({ direction: FlexDirection.Column }) {
      Column() {
        Text('ColumnD')
      }
      .onTouch(() => {
        console.info('Executing a gesture:ComponentD');
      })
      .gesture(TapGesture({ count: 1 }))
      .backgroundColor(Color.Yellow)
      .height('30%')
      .width('100%')

      // Component E uses .hitTestBehavior(HitTestMode.None), so it does not respond to touch tests
      Column() {
        Text('ColumnE')
      }
      .onTouch(() => {
        console.info('Executing a gesture:ComponentE');
      })
      .gesture(TapGesture({ count: 1 }))
      .backgroundColor(Color.Pink)
      .height('30%')
      .width('100%')
      .hitTestBehavior(HitTestMode.None)

      Column() {
        Text('ColumnF')
      }
      .onTouch(() => {
        console.info('Executing a gesture:ComponentF');
      })
      .gesture(TapGesture({ count: 1 }))
      .backgroundColor(Color.Red)
      .height('30%')
      .width('100%')
    }.width('100%').height('100%')
    .hitTestBehavior(HitTestMode.None)
  }
}
Enter fullscreen mode Exit fullscreen mode

Test Results

Gesture propagation results when touching different colored areas:

Touching area D:
 Executing a gesture:ComponentD
 Executing a gesture:ComponentB

Touching area E:
 Executing a gesture:ComponentB

Touching area F:
 Executing a gesture:ComponentF
 Executing a gesture:ComponentB

Touching area C:
 Executing a gesture:ComponentB

Touching area B:
 Executing a gesture:ComponentB

Touching area A:
 // No related log output
Enter fullscreen mode Exit fullscreen mode

cke_3197.png

Limitations or Considerations

This example supports API Version 19 Release and above.

This example supports HarmonyOS 5.1.1 Release SDK and above.

Compilation and execution require DevEco Studio 5.1.1 Release or later.

Written by Bunyamin Akcay

Top comments (0)