事件响应链收集
-
触摸事件(onTouch事件)是用户与设备交互的原始事件,是所有手势事件组成的基础,有Down,Move,Up,Cancel四种触摸事件的类型。 手势均由触摸事件组成
-
触摸事件的分发由触摸测试(TouchTest)结果决定,其结果会直接决定哪些控件的事件加入事件响应链(事件响应成员组成的链表),并最终按照响应链顺序判定是否消费
有几个因素可以影响触摸测试的结果
- TouchTest:触摸测试入口方法,此方法无外部接口
- TouchTest的触发时机由每次点按的按下动作发起,默认由组件树的根节点的TouchTest方法作为入口
- hitTestBehavior可以由InterceptTouch事件变更
- 触摸热区/禁用控制等不满足组件事件交互诉求,会导致立即返回父节点
-
hitTestBehavior:触摸测试控制
- 命中:触摸测试成功收集到当前组件/子组件的事件。
- 子组件对父组件触摸测试的影响,取决于最后一个没有被阻塞触摸测试的子组件
- HitTestMode.Default:默认不配hitTestBehavior属性的效果,自身如果命中会阻塞兄弟组件,但是不阻塞子组件。
- HitTestMode.None:自身不接收事件,但不会阻塞兄弟组件/子组件继续做触摸测试。
- HitTestMode.Block:阻塞子组件的触摸测试,如果自身触摸测试命中,会阻塞兄弟组件及父组件的触摸测试。
- HitTestMode.Transparent:自身进行触摸测试,同时不阻塞兄弟组件及父组件。
-
interceptTouch:事件自定义拦截
- 自定义事件拦截在按下触发时,可以根据业务状态 动态改变组件的hitTestBehavior属性
-
responseRegion:触摸热区设置
- 触摸热区设置会影响触屏/鼠标类的触摸测试,如果设置为0,或不可触控区域,则事件直接返回父节点继续触摸测试
-
enabled:禁用控制
- 设置禁用控制的组件,包括其子组件不会发起触摸测试过程,会直接返回父节点继续触摸测试
-
安全组件
-
其他属性设置:透明度/组件下线
- ArkUI事件响应链收集,根据右子树(按组件布局的先后层级)优先的后序遍历流程
例如 用户触摸的动作如果发生在组件E上,事件响应链收集的流程如下
- 根据右子树优先的后序遍历流程,所以事件会从右边树的D节点开始往上传。
- 虽然触摸点在组件D和组件B的交集上,但组件D的hitTestBehavior属性默认为HitTestMode.Default,D组件收集到事件后会阻塞兄弟节点(组件B)
- 所以没有收集组件A的左子树,最终收集到的响应链是E->D->A。
ArkUI在处理触屏事件时,会在触屏事件触发前进行按压点和组件区域的触摸测试,来收集需要响应触屏事件的组件
再基于触摸测试结果分发相应的触屏事件
手势
手势分为系统手势和自定义手势,系统手势的优先级更高,会被优先响应
除了触摸事件(onTouch事件)外的所有手势与事件,均是通过基础手势或者组合手势实现的。
除非显式声明允许多个手势同时成功,否则同一时间只会有一个手势响应。
- 当父子组件均绑定同一类手势时,子组件优先于父组件触发。
- 当同一个组件同时绑定多个手势时,先达到手势触发条件的手势优先触发。
- 当同一个组件绑定相同事件类型的系统手势和自定义手势时,系统手势会优先响应
手势响应优先级(从左至右,优先级由高到低)
手势绑定
通过绑定手势方法绑定不同的手势,可以影响对手势响应的控制
手势绑定支持 常规手势绑定方法(gesture)、带优先级手势绑定方法(priorityGesture)、并行手势绑定方法(parallelGesture)
绑定手势方法 | 功能规格 | 配参1 | 配参2 | 约束 |
---|---|---|---|---|
gesture | 绑定手势事件,父子组件交叠区域均绑定,响应子组件 | GestureType | GestureMask | 与通用事件抢占 |
priorityGesture | 当父组件配置priorityGesture时,优先识别父组件priorityGesture绑定的手势。 | GestureType | GestureMask | 与通用事件抢占 |
parallelGesture | 父组件绑定parallelGesture时,父子组件相同的手势事件都可以触发 | GestureType | GestureMask | 无 |
加入priorityGesture和parallelGesture绑定方法后,手势的响应顺序
GestureMask枚举说明
名称 | 描述 |
---|---|
Normal | 不屏蔽子组件的手势,按照默认手势识别顺序进行识别。 |
IgnoreInternal | 屏蔽子组件的手势,包括子组件上的系统内置的手势,如子组件为List组件时,内置的滑动手势同样会被屏蔽。 若父子组件区域存在部分重叠,则只会屏蔽父子组件重叠的部分。 |
不同手势绑定配参方案规格
父手势 | 子手势 | GestureMask(父) | 交叠区域相同事件响应方 | 交叠区域不同事件响应方 |
---|---|---|---|---|
gesture | gesture | default | 子组件 | 各自响应 |
gesture | gesture | IgnoreInternal | 父组件 | 父组件 |
priorityGesture | gesture | default | 父组件 | 各自响应 |
priorityGesture | gesture | IgnoreInternal | 父组件 | 父组件 |
parallelGesture | gesture | default | 各自响应 | 各自响应 |
parallelGesture | gesture | IgnoreInternal | 父组件 | 父组件 |
组合手势(GestureGroup)
手势组合是指多种手势组合为复合手势,通过GestureGroup属性,可以给同一个组件添加多个手势,支持连续识别、并行识别和互斥识别模式
组合手势属于手势的一种,也会影响手势响应的控制
接口 | 可选模式 | 描述 | 注册事件 |
---|---|---|---|
GestureGroup | Sequence | 手势顺序队列,需要按预定的手势组顺序执行,有一个失败则全部失败 | onCancel |
GestureGroup | Parallel | 手势组合,直到所有已识别的手势执行完 | 无 |
GestureGroup | Exclusive | 互斥识别,成功完成一个手势,则完成手势任务 | 无 |
独占事件控制
通过monopolizeEvents属性设置组件是否独占事件,事件范围包括组件自带的事件和开发者自定义的点击、触摸、手势事件。先响应事件的控件作为第一响应者,在手指离开屏幕前其他组件不会响应任何事件。
在一个窗口内,设置了独占控制的组件上的事件如果首先响应,则本次交互只允许此组件上设置的事件响应,窗口内其他组件上的事件不会响应。
如果开发者通过parallelGesture绑定了与子组件同时触发的手势,如PanGesture,子组件设置了独占控制且首个响应事件,则父组件的手势不会响应。
自定义手势判定
为组件提供自定义手势判定能力。开发者可根据需要,在手势识别期间,根据自己的业务逻辑来决定是否响应手势。使用onGestureJudgeBegin方法对手势进行判定,开发者可以根据自身业务逻辑,选择是否响应自定义手势。
手势拦截增强
通过shouldBuiltInRecognizerParallelWith将系统内置手势和比其优先级高的手势做并行化处理,可以动态控制手势事件的触发(这里主要是系统的Pan手势)
responseRegion和hitTestBehavior
影响触摸测试的因素同样也可能会影响到手势的响应流程。例如responseRegion属性和hitTestBehavior属性可以控制Touch事件的分发,从而可以影响到onTouch事件和手势的响应。而绑定手势方法属性可以控制手势的竞争从而影响手势的响应,但不会影响到onTouch事件。
ArkUI组件自身的属性控制手势响应
ArkUI组件自身的属性,也可以对手势事件的响应做出控制。例如Grid、List、Scroll、Swiper、WaterFlow等滚动容器组件提供了nestedScroll属性,来解决和父组件的嵌套滚动的冲突问题;例如Swiper组件的disableSwipe可以设置禁用组件滑动切换的功能;又例如List组件可以通过设置enableScrollInteraction属性来设置是否支持手势滚动列表。
其它影响触摸测试的API
- 在父节点,通过onChildTouchTest决定如何让子节点去做触摸测试,影响子组件的触摸测试,最终影响后续的触屏事件分发
- 在组件节点自身,通过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)
梳理后的流程图大概如下
onGestureRecognizerJudgeBegin和onGestureJudgeBeginCallback互斥,并且onGestureRecognizerJudgeBegin的优先级更高
参考资料
- 自定义事件拦截 onTouchIntercept developer.huawei.com/consumer/cn…
- 自定义事件分发 onChildTouchTest developer.huawei.com/consumer/cn…
- 自定义手势判定 onGestureJudgeBegin developer.huawei.com/consumer/cn…
- 手势拦截增强 shouldBuiltInRecognizerParallelWith developer.huawei.com/consumer/cn…
- 触摸测试 developer.huawei.com/consumer/cn…
- 事件独占控制 monopolizeEvents developer.huawei.com/consumer/cn…
- 触摸热区设置 responseRegion developer.huawei.com/consumer/cn…
- 触摸测试控制 hitTestBehavior developer.huawei.com/consumer/cn…
- 手势事件冲突解决方案 developer.huawei.com/consumer/cn…
- ArkTS运行时源码 gitee.com/openharmony…
评论记录:
回复评论: