harmony 鸿蒙Multi-level Gesture Events
Multi-level Gesture Events
Multi-level gesture events occur when both parent and child components receive a gesture or event bound to them. Handling such events can be tricky: The gesture and event detection is affected by a plurality of factors, with much transmission and competition involved, and an unexpected response easily occurs.
This topic describes the default response sequence of multi-level gesture events and how to set related attributes to affect the response sequence of multi-level gesture events.
Default Multi-level Gesture Events
Touch Event
The touch event (onTouch) is the foundation of all gestures, comprising four types: Down, Move, Up, and Cancel. Gestures are built from the touch events. For example, a tap consists of a Down event followed by an Up event, while a swipe involves a Down event, a sequence of Move events, and finally an Up event. Touch events have unique characteristics:
Components that have registered for onTouch events receive callbacks for these events when touched, influenced by touch targets and touch control settings.
The onTouch event callbacks follow a closed-loop pattern. If a component receives a Down event with a specific finger ID (such as 0), it will also receive subsequent Move and Up events for that same finger ID.
The onTouch event callbacks maintain consistency. If a component receives a Down event for finger ID 0 but not for finger ID 1, it will only receive touch events for finger ID 0 and will not receive any subsequent touch events for finger ID 1.
For common container components (such as Column), onTouch events can be received by parent and child components at the same time, and how they are received by sibling components is subject to the layout.
ComponentA() {
ComponentB().onTouch(() => {})
ComponentC().onTouch(() => {})
}.onTouch(() => {})
If components B and C are children of component A, then touching component B or component C also touches component A. The onTouch callback can be invoked by multiple components at the same time. Therefore, when component B is touched, the onTouch callback is invoked by both component A and component B, but not by component C; when component C is touched, the onTouch callback is invoked by both component A and component C, but not by component B.
For special container components, such as Stack, onTouch events can be received by parent and child components at the same time, but how they are received by child components depends on the stacking relationship.
Stack A() {
ComponentB().onTouch(() => {})
ComponentC().onTouch(() => {})
}.onTouch(() => {})
Assume that components B and C are children of Stack A, and component C is stacked on component B. Then touching component B or component C also touches Stack A. The onTouch callback can be invoked by multiple components at the same time. Therefore, when the overlapping area of components B and C is touched, the onTouch callback is invoked by both Stack A and component C, but not by component B (which is stacked by component C).
Gestures and Events
All gestures and events except the touch event (onTouch) are implemented using basic or combined gestures. For example, the drag event is a sequence of a long press gesture and a swipe gesture.
If no explicit declaration is made, only one gesture in a gesture group can be recognized for a single finger at the same time, which means that only one set callback can be invoked.
Therefore, unless it is explicitly declared that multiple gestures can be recognized at the same time, only one gesture is handled at once.
The response to gestures complies with the following rules:
When the parent and child components are bound to the same type of gesture, the child component responds prior to the parent component.
When a component is bound to multiple gestures, the gesture that first meets triggering conditions is preferentially triggered.
ComponentA() {
ComponentB().gesture(TapGesture({count: 1}))
}.gesture(TapGesture({count: 1}))
When both the parent and child components are bound to a tap gesture, the child component responds prior to the parent component. Therefore, when the user touches component B, the callback of TapGesture bound to component B is invoked, but the callback bound to component A is not.
ComponentA()
.gesture(
GestureGroup(
GestureMode.Exclusive,
TapGesture({count: 1}),
PanGesture({distance: 5})
)
)
If the tap gesture and the swipe gesture are bound to a component in exclusive recognition mode, the gesture that first meets triggering conditions is preferentially triggered. If the user performs a tap operation, the callback corresponding to the tap is invoked. If the user performs a swipe operation and the swipe distance reaches the threshold, the callback corresponding to the swipe is invoked.
Handling Multi-level Gesture Events with Custom Logic
You can set attributes to control the multi-level gesture event competition process.
Specifically, use the responseRegion and hitTestBehavior attributes to control dispatching of touch events, thereby affecting the response to the onTouch events and gestures. You can also call gesture binding methods to control gesture competition and affect gesture response, but this approach does not affect triggering of onTouch events.
Using responseRegion
The responseRegion attribute sets the touch target of a component, which can be larger or smaller than the layout scope of the component.
ComponentA() {
ComponentB()
.onTouch(() => {})
.gesture(TapGesture({count: 1}))
.responseRegion({Rect1, Rect2, Rect3})
}
.onTouch(() => {})
.gesture(TapGesture({count: 1}))
.responseRegion({Rect4})
In the preceding example, .responseRegion({Rect4}) is set for component A, and as such, all touch events and gestures that fall within the Rect4 region can be received by the callback corresponding to component A.
Similarly, with .responseRegion({Rect1, Rect2, Rect3}) set for component B, all touch events and gestures that fall within the Rect1, Rect2, and Rect3 regions can be received by the callback corresponding to component B.
When responseRegion is set for a component, the component responds to all gestures and events that occur within the designated regions, instead of those in the layout area. This may lead to no response to gestures and events in the layout-related area.
The responseRegion attribute accepts an array consisting of multiple Rect values.
Using hitTestBehavior
The hitTestBehavior attribute sets which components can respond to specific gestures and events. It is especially useful under complex multi-level event scenarios.
ComponentA() {
ComponentB()
.onTouch(() => {})
.gesture(TapGesture({count: 1}))
ComponentC() {
ComponentD()
.onTouch(() => {})
.gesture(TapGesture({count: 1}))
}
.onTouch(() => {})
.gesture(TapGesture({count: 1}))
.hitTestBehavior(HitTestMode.Block)
}
.onTouch(() => {})
.gesture(TapGesture({count: 1}))
With HitTestMode.Block, the node responds to the hit test of a touch event, but its child node and sibling node are blocked from the hit test; as a result, neither the child node nor sibling node can receive the onTouch events and gestures.
When hitTestBehavior is not set for component C, a touch in the target touch of component D triggers the onTouch events of components A, C, and D, as well as the tap gesture of component D.
When hitTestBehavior is set to HitTestMode.Block for component C, a touch in the target touch of component D triggers the onTouch events of components A and C, but not the onTouch event of component D. In addition, because component D is blocked and its tap gesture of the component D cannot be triggered, the tap gesture of component C is triggered.
Stack A() {
ComponentB()
.onTouch(() => {})
.gesture(TapGesture({count: 1}))
ComponentC()
.onTouch(() => {})
.gesture(TapGesture({count: 1}))
.hitTestBehavior(HitTestMode.Transparent)
}
.onTouch(() => {})
.gesture(TapGesture({count: 1}))
With HitTestMode.Transparent, both the node and its child node respond to the hit test of a touch event, and its sibling node is also considered during the hit test.
If hitTestBehavior is not set for component C, when the overlapping area of component B and component C is touched, the onTouch events of Stack A and component C are triggered, the touch event of component C is triggered, and neither the onTouch event nor tap gesture of component B is triggered.
If hitTestBehavior is set to HitTestMode.Transparent for component C, when the overlapping area of components B and C is touched, the onTouch events of Stack A and component C are still triggered, and the touch event of component C is also triggered; yet, because component B can receive the touch event in this case, its onTouch event and tap gesture are triggered.
ComponentA() {
ComponentB()
.onTouch(() => {})
.gesture(TapGesture({count: 1}))
}
.onTouch(() => {})
.gesture(TapGesture({count: 1}))
.hitTestBehavior(HitTestMode.None)
With HitTestMode.None, the node neither receives touch events nor interferes with the touch testing of its sibling components or child components.
If hitTestBehavior is not set for component A, a touch in the target touch of component B triggers the onTouch events of components A and B, as well as the tap gesture of component B.
When hitTestBehavior is set to HitTestMode.None for component A, a touch in the target touch of component B triggers the onTouch event and tap gesture of component B, but not the onTouch event of component A.
Under simple scenarios, you are advised to set hitTestBehavior for each single component. Under complex scenarios, you are advised to set different hitTestBehavior values to multiple components to control the dispatching of touch events.
Calling Gesture Binding Methods
When binding a parent component and a child component to a same gesture, you can assign different response priorities to them by using different gesture binding methods.
When .gesture is used for gesture binding, the child component responds prior to the parent component.
ComponentA() {
ComponentB()
.gesture(TapGesture({count: 1}))
}
.gesture(TapGesture({count: 1}))
In the preceding example, both the parent and child components are bound to the tap gesture, and the child component responds prior to the parent component. In this case, when component B is touched, the tap gesture of component B is triggered, but that of component A is not.
To enable the parent component to respond prior to the child component, use the .priorityGesture method.
ComponentA() {
ComponentB()
.gesture(TapGesture({count: 1}))
}
.priorityGesture(TapGesture({count: 1}))
In the preceding example, the .priorityGesture method is used to bind the parent component to the tap gesture, and the parent component responds prior to the child component. In this case, when component B is touched, the tap gesture of component A is triggered, but that of component B is not.
To enable both the parent and child components to respond to a same gesture, use the .parallelGesture method in the parent component.
ComponentA() {
ComponentB()
.gesture(TapGesture({count: 1}))
}
.parallelGesture(TapGesture({count: 1}))
In the preceding example, the .parallelGesture method is used to bind the parent component to the tap gesture, and both the parent and child components can respond to the bound gesture. In this case, when component B is touched, both the tap gestures of components A and B are triggered.
你可能感兴趣的鸿蒙文章
harmony 鸿蒙Atomic Service Full Screen Launch Component (FullScreenLaunchComponent)
harmony 鸿蒙Arc Button (ArcButton)
harmony 鸿蒙Frame Animation (ohos.animator)
harmony 鸿蒙Implementing Property Animation
- 所属分类: 后端技术
- 本文标签:
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦