一、Flutter事件原理介绍
在移动端,一次完整的时间分为三个阶段:手指按下,手指移动,和手指抬起,而更高级的手势(如点击、双击、拖动等)都是基于这些原始事件。
当手指按下的时候,Flutter会对应用程序执行命中测试(Hit Test),以确定手指与屏幕接触的位置存在哪些组件(widget),手指按下事件(以及该指针的后续事件)然后被分发到由命中测试发现的最内部的组件,然后从那里开始,事件会在组件树向上冒泡,这些事件会从最内部的组件被分发到组件树根路径上的所有组件。只有通过命中测试的组件才能触发事件。
Flutter中可以使用Listener来监听原始触摸事件,Listener的构造如下:
const Listener({
Key? key,
this.onPointerDown,//手指按下回调
this.onPointerMove, //手指移动回调
this.onPointerUp,//手指抬起回调
this.onPointerHover,
this.onPointerCancel,//触摸事件取消回调
this.onPointerSignal,
this.behavior = HitTestBehavior.deferToChild,
Widget? child,
})
PointerDownEvent、 PointerMoveEvent、 PointerUpEvent 都是PointerEvent的子类,PointerEvent类中包括当前手指的一些信息。
position:它是指针相对于当对于全局坐标的偏移。
localPosition: 它是指针相对于当对于全局坐标的偏移
delta:两次指针移动事件(PointerMoveEvent)的距离。
pressure:按压力度,如果手机屏幕支持压力传感器(如iPhone的3D Touch),此属性会更有意义,如果手机不支持,则始终为1。
orientation:指针移动方向,是一个角度值。
Flutter中封装了GestureDetetor和GestureRecognizer识别和处理手势冲突,具体使用并不难,可以自己去学习。
二、Flutter 事件处理流程
Flutter 事件处理流程主要分为两步,为了聚焦核心流程,我们以用户触摸事件为列:
1.命中测试:当手指按下时,触发PointDownEvent事件,按照深度优先遍历当前渲染树,对每一个渲染对象进行命中测试(hit test),如果命中测试通过,则该渲染对象会被添加到一个HitTestResult列表中。
2.事件分发:命中测试完毕后,会遍历HitTestResult列表,调用每一个渲染对象的事件处理方法(handleEvent)来处理PointerDownEvent事件,改过程为"事件分发"(event dispatch)。随后当手指移动的时,便会分发PointerMoveEvent事件。
3.事件清洗:当手指抬起(PointerUpEvent)或事件取消时(PointerCancelEvent),会先对响应的事件分发,分发完毕后会清空HitTestResult列表。
可以从源码的层面来看整个事件处理流程
// 触发新事件时,flutter 会调用此方法
void _handlePointerEventImmediately(PointerEvent event) {
HitTestResult? hitTestResult;
if (event is PointerDownEvent ) {
hitTestResult = HitTestResult();
// 发起命中测试
hitTest(hitTestResult, event.position);
if (event is PointerDownEvent) {
_hitTests[event.pointer] = hitTestResult;
}
} else if (event is PointerUpEvent || event is PointerCancelEvent) {
//获取命中测试的结果,然后移除它
hitTestResult = _hitTests.remove(event.pointer);
} else if (event.down) { // PointerMoveEvent
//直接获取命中测试的结果
hitTestResult = _hitTests[event.pointer];
}
// 事件分发
if (hitTestResult != null) {
dispatchEvent(event, hitTestResult);
}
}
三、命中测试
一个对象是否可以响应事件,取决于在其对命中测试过程中是否被添加到了HitTestResult列表,如果没有被添加进去,则后续的时间分发将不会分发给自己。命中测试分两步:
1.renderView是RenderView对应的RenderObject对象,RederObject对象的hitTest方法主要功能是:从该节点出发,按照深度优先的顺序递归遍历子树(渲染树)上的每一个节点并对他进行命中测试。这个过程称为“渲染树命中测试”。
2.渲染树命中测试完毕后,会调用GestureBinding的HitTest方法,该方法主要用于处理手势,我们会在后面介绍。
总结原理
会按照rederObjective树的去深度遍历每个子节点,如果hittest返回true,就是命中测试了,会终止遍历。然后会把从他开始的向上到定点的节点都添加到HitResult里面。然后会遍历HitResult执行他们的handleEvent来处理事件。只有Listener和他的一些子类会执行handleEvent。
网友评论