鸿蒙ACE-手势事件处理

583 阅读8分钟

事件响应链收集

  1. 触摸事件onTouch事件)是用户与设备交互的原始事件,是所有手势事件组成的基础,有Down,Move,Up,Cancel四种触摸事件的类型手势均由触摸事件组成

  2. 触摸事件的分发由触摸测试(TouchTest)结果决定,其结果会直接决定哪些控件的事件加入事件响应链(事件响应成员组成的链表),并最终按照响应链顺序判定是否消费

有几个因素可以影响触摸测试的结果

  • TouchTest:触摸测试入口方法,此方法无外部接口
    • TouchTest的触发时机由每次点按的按下动作发起,默认由组件树的根节点的TouchTest方法作为入口
    • hitTestBehavior可以由InterceptTouch事件变更
    • 触摸热区/禁用控制等不满足组件事件交互诉求,会导致立即返回父节点

image.png

  • hitTestBehavior:触摸测试控制

    • 命中:触摸测试成功收集到当前组件/子组件的事件。
    • 子组件对父组件触摸测试的影响,取决于最后一个没有被阻塞触摸测试的子组件
    • HitTestMode.Default:默认不配hitTestBehavior属性的效果,自身如果命中会阻塞兄弟组件,但是不阻塞子组件。

    • HitTestMode.None:自身不接收事件,但不会阻塞兄弟组件/子组件继续做触摸测试。

    • HitTestMode.Block:阻塞子组件的触摸测试,如果自身触摸测试命中,会阻塞兄弟组件及父组件的触摸测试。

    • HitTestMode.Transparent:自身进行触摸测试,同时不阻塞兄弟组件及父组件。

  • interceptTouch:事件自定义拦截

    • 自定义事件拦截在按下触发时,可以根据业务状态 动态改变组件的hitTestBehavior属性
  • responseRegion:触摸热区设置

    • 触摸热区设置会影响触屏/鼠标类的触摸测试,如果设置为0,或不可触控区域,则事件直接返回父节点继续触摸测试
  • enabled:禁用控制

    • 设置禁用控制的组件,包括其子组件不会发起触摸测试过程,会直接返回父节点继续触摸测试
  • 安全组件

  • 其他属性设置:透明度/组件下线

点击查看 具体怎么影响触摸测试结果

  1. ArkUI事件响应链收集,根据右子树(按组件布局的先后层级)优先的后序遍历流程

例如 用户触摸的动作如果发生在组件E上,事件响应链收集的流程如下

  • 根据右子树优先的后序遍历流程,所以事件会从右边树的D节点开始往上传。
  • 虽然触摸点在组件D和组件B的交集上,但组件D的hitTestBehavior属性默认为HitTestMode.Default,D组件收集到事件后会阻塞兄弟节点(组件B)
  • 所以没有收集组件A的左子树,最终收集到的响应链是E->D->A。

ArkUI在处理触屏事件时,会在触屏事件触发前进行按压点和组件区域的触摸测试,来收集需要响应触屏事件的组件

再基于触摸测试结果分发相应的触屏事件

手势

手势分为系统手势和自定义手势,系统手势的优先级更高,会被优先响应

除了触摸事件(onTouch事件)外的所有手势与事件,均是通过基础手势或者组合手势实现的。

除非显式声明允许多个手势同时成功,否则同一时间只会有一个手势响应。

  1. 当父子组件均绑定同一类手势时,子组件优先于父组件触发。
  2. 当同一个组件同时绑定多个手势时,先达到手势触发条件的手势优先触发。
  3. 当同一个组件绑定相同事件类型的系统手势和自定义手势时,系统手势会优先响应

手势响应优先级(从左至右,优先级由高到低) image.png

手势绑定

通过绑定手势方法绑定不同的手势,可以影响对手势响应的控制

手势绑定支持 常规手势绑定方法(gesture)、带优先级手势绑定方法(priorityGesture)、并行手势绑定方法(parallelGesture)

绑定手势方法功能规格配参1配参2约束
gesture绑定手势事件,父子组件交叠区域均绑定,响应子组件GestureTypeGestureMask与通用事件抢占
priorityGesture当父组件配置priorityGesture时,优先识别父组件priorityGesture绑定的手势。GestureTypeGestureMask与通用事件抢占
parallelGesture父组件绑定parallelGesture时,父子组件相同的手势事件都可以触发GestureTypeGestureMask

加入priorityGesture和parallelGesture绑定方法后,手势的响应顺序 image.png

GestureMask枚举说明

名称描述
Normal不屏蔽子组件的手势,按照默认手势识别顺序进行识别。
IgnoreInternal屏蔽子组件的手势,包括子组件上的系统内置的手势,如子组件为List组件时,内置的滑动手势同样会被屏蔽。 若父子组件区域存在部分重叠,则只会屏蔽父子组件重叠的部分。

不同手势绑定配参方案规格

父手势子手势GestureMask(父)交叠区域相同事件响应方交叠区域不同事件响应方
gesturegesturedefault子组件各自响应
gesturegestureIgnoreInternal父组件父组件
priorityGesturegesturedefault父组件各自响应
priorityGesturegestureIgnoreInternal父组件父组件
parallelGesturegesturedefault各自响应各自响应
parallelGesturegestureIgnoreInternal父组件父组件

组合手势(GestureGroup)

手势组合是指多种手势组合为复合手势,通过GestureGroup属性,可以给同一个组件添加多个手势,支持连续识别、并行识别和互斥识别模式

组合手势属于手势的一种,也会影响手势响应的控制

接口可选模式描述注册事件
GestureGroupSequence手势顺序队列,需要按预定的手势组顺序执行,有一个失败则全部失败onCancel
GestureGroupParallel手势组合,直到所有已识别的手势执行完
GestureGroupExclusive互斥识别,成功完成一个手势,则完成手势任务

独占事件控制

通过monopolizeEvents属性设置组件是否独占事件,事件范围包括组件自带的事件和开发者自定义的点击、触摸、手势事件。先响应事件的控件作为第一响应者,在手指离开屏幕前其他组件不会响应任何事件

在一个窗口内,设置了独占控制的组件上的事件如果首先响应,则本次交互只允许此组件上设置的事件响应,窗口内其他组件上的事件不会响应。

如果开发者通过parallelGesture绑定了与子组件同时触发的手势,如PanGesture,子组件设置了独占控制且首个响应事件,则父组件的手势不会响应。

自定义手势判定

为组件提供自定义手势判定能力。开发者可根据需要,在手势识别期间,根据自己的业务逻辑来决定是否响应手势。使用onGestureJudgeBegin方法对手势进行判定,开发者可以根据自身业务逻辑,选择是否响应自定义手势。

手势拦截增强

通过shouldBuiltInRecognizerParallelWith将系统内置手势和比其优先级高的手势做并行化处理,可以动态控制手势事件的触发(这里主要是系统的Pan手势)

responseRegionhitTestBehavior

影响触摸测试的因素同样也可能会影响到手势的响应流程。例如responseRegion属性和hitTestBehavior属性可以控制Touch事件的分发,从而可以影响到onTouch事件和手势的响应。而绑定手势方法属性可以控制手势的竞争从而影响手势的响应,但不会影响到onTouch事件。

ArkUI组件自身的属性控制手势响应

ArkUI组件自身的属性,也可以对手势事件的响应做出控制。例如Grid、List、Scroll、Swiper、WaterFlow等滚动容器组件提供了nestedScroll属性,来解决和父组件的嵌套滚动的冲突问题;例如Swiper组件的disableSwipe可以设置禁用组件滑动切换的功能;又例如List组件可以通过设置enableScrollInteraction属性来设置是否支持手势滚动列表。

其它影响触摸测试的API

  1. 在父节点,通过onChildTouchTest决定如何让子节点去做触摸测试,影响子组件的触摸测试,最终影响后续的触屏事件分发
  2. 在组件节点自身,通过onTouchIntercept来根据事件在控件上按下时发生的位置、输入源等事件信息决定控件上的HitTestMode属性

源码处理流程

我们可以在 arkcompiler_ets_runtime源码中找到TouchTest的处理流程 gitee.com/openharmony…

HitTestResult FrameNode::TouchTest(const PointF& globalPoint, const PointF& parentLocalPoint, const PointF& parentRevertPoint, TouchRestrict& touchRestrict, TouchTestResult& result, int32_t touchId, ResponseLinkResult& responseLinkResult, bool isDispatch)

梳理后的流程图大概如下 Untitled.png

onGestureRecognizerJudgeBegin和onGestureJudgeBeginCallback互斥,并且onGestureRecognizerJudgeBegin的优先级更高 image.png

参考资料

  1. 自定义事件拦截 onTouchIntercept developer.huawei.com/consumer/cn…
  2. 自定义事件分发 onChildTouchTest developer.huawei.com/consumer/cn…
  3. 自定义手势判定 onGestureJudgeBegin developer.huawei.com/consumer/cn…
  4. 手势拦截增强 shouldBuiltInRecognizerParallelWith developer.huawei.com/consumer/cn…
  5. 触摸测试 developer.huawei.com/consumer/cn…
  6. 事件独占控制 monopolizeEvents developer.huawei.com/consumer/cn…
  7. 触摸热区设置 responseRegion developer.huawei.com/consumer/cn…
  8. 触摸测试控制 hitTestBehavior developer.huawei.com/consumer/cn…
  9. 手势事件冲突解决方案 developer.huawei.com/consumer/cn…
  10. ArkTS运行时源码 gitee.com/openharmony…