持续创作,加速成长!这是我参与「日新计划 6 月更文挑战」的第1天,点击查看活动详情
前面我们介绍了 runApp
、BaseBinding
,知道了 Flutter
的初始化就是一堆 Binding
的初始化和服务注册,GestureBinding
是第一个初始化的 Binding
,见名知意,这个 Binding
是专门用来处理手势的,这一篇就看看 Flutter
是怎么处理手势的。

往期精彩:
Flutter 必知必会系列 —— runApp 做了啥
Flutter 必知必会系列 —— mixin 和 BindingBase 的巧妙配合
Flutter 的手势从哪里来
Flutter
仅仅是一个 UI 框架,是没有从屏幕读取手势功能的,它只能被动的响应。我们以 Android 为例,来找一下手势是怎么传递的。
首先看 Flutter
侧的手势起点。
/// GestureBinding mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget { @override void initInstances() { super.initInstances(); _instance = this; window.onPointerDataPacket = _handlePointerDataPacket; } }
GestureBinding
仅仅是给 onPointerDataPacket
赋值,所以我们看赋值的动作干了啥。
class SingletonFlutterWindow extends FlutterWindow { PointerDataPacketCallback? get onPointerDataPacket => platformDispatcher.onPointerDataPacket; set onPointerDataPacket(PointerDataPacketCallback? callback) { platformDispatcher.onPointerDataPacket = callback; //第一处 } }
我们看第一处的代码,仅仅是给 platformDispatcher
赋值,我们继续追。
platformDispatcher
是 PlatformDispatcher 的实例,并且 PlatformDispatcher 是单例的,所以说一个 Native 的 FlutterView
对应一个 Flutter 的 PlatformDispatcher
实例。(这里大家需要理解一点,Native 的 FlutterView 对应的就是 Flutter 程序,容器是承载 FlutterView 的 Activity 或者 Fragment)
见名知意,这个类就是 Native 消息的分发器。我们看赋值代码。
/// PlatformDispatcher class PlatformDispatcher { PointerDataPacketCallback? get onPointerDataPacket => _onPointerDataPacket; PointerDataPacketCallback? _onPointerDataPacket; Zone _onPointerDataPacketZone = Zone.root; set onPointerDataPacket(PointerDataPacketCallback? callback) { _onPointerDataPacket = callback; _onPointerDataPacketZone = Zone.current; } }
所以,GestureBinding
中的 window
赋值语句,其实是给 PlatformDispatcher
的 _onPointerDataPacket
赋值,那么调用 _onPointerDataPacket
的地方,就是发起 Flutter 手势的地方。
class PlatformDispatcher {
// Called from the engine, via hooks.dart
void _dispatchPointerDataPacket(ByteData packet) {
if (onPointerDataPacket != null) {
_invoke1<PointerDataPacket>(
onPointerDataPacket,
_onPointerDataPacketZone,
_unpackPointerDataPacket(packet),
);
}
}
}
就是 PlatformDispatcher 的 _dispatchPointerDataPacket
方法,而调用 _dispatchPointerDataPacket
的是

我们看到调用的方法是被 @pragma(‘vm:entry-point’) 注解的,也就是从 Native 调用的,而非 Flutter 层。
所以,就是我们前面得到的结论: Flutter 层只是被动的响应来自 Native 的调用,调用的入口是 _dispatchPointerDataPacket。
既然是 Native 层,那就是 FlutterView 了,大概率是重写 onTouch
方法。我们看 Native 的处理逻辑,既然承载 Flutter
的是 FlutterView
,我们看其手势处理方法。
/// FlutterView public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseCursorViewDelegate { @Override public boolean onTouchEvent(@NonNull MotionEvent event) { if (!isAttachedToFlutterEngine()) { return super.onTouchEvent(event); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { requestUnbufferedDispatch(event); } return androidTouchProcessor.onTouchEvent(event); } }
基本就是讲手势透传给了 androidTouchProcessor
, androidTouchProcessor 是谁呢?AndroidTouchProcessor
描述如下:
Sends touch information from Android to Flutter in a format that Flutter understands.
把手势的信息从 Android 层传给 Flutter,关键是对信息进行了格式转换。
我们看它的逻辑:
/// AndroidTouchProcessor public class AndroidTouchProcessor { public boolean onTouchEvent(@NonNull MotionEvent event) { return onTouchEvent(event, IDENTITY_TRANSFORM); } public boolean onTouchEvent(@NonNull MotionEvent event, Matrix transformMatrix) { int pointerCount = event.getPointerCount(); // 准备数据包 ByteBuffer packet = ByteBuffer.allocateDirect(pointerCount * POINTER_DATA_FIELD_COUNT * BYTES_PER_FIELD); packet.order(ByteOrder.LITTLE_ENDIAN); int maskedAction = event.getActionMasked(); int pointerChange = getPointerChangeForAction(event.getActionMasked()); if (updateForSinglePointer) { //添加数据包 DOWN 事件 addPointerForIndex(event, event.getActionIndex(), pointerChange, 0, transformMatrix, packet); } else if (updateForMultiplePointers) { //添加数据包 UP 事件 addPointerForIndex(event, event.getActionIndex(), pointerChange, 0, transformMatrix, packet); } else { for (int p = 0; p < pointerCount; p++) { // 添加数据包 MOVE 事件 addPointerForIndex(event, p, pointerChange, 0, transformMatrix, packet); } } // 数据包发送给 Flutter renderer.dispatchPointerDataPacket(packet, packet.position()); return true; } }
onTouchEvent
的处理:
准备数据包,将数据封装成 Flutter
认识的格式,添加到数据包中,renderer
发送给 Flutter。
我们先看封装的格式,在 addPointerForIndex
中:
//代码片段 packet.putLong(motionEventId); // motionEventId packet.putLong(timeStamp); // time_stamp packet.putLong(pointerChange); // change packet.putLong(pointerKind); // kind packet.putLong(signalKind); // signal_kind packet.putLong(event.getPointerId(pointerIndex)); // device packet.putLong(0); if (signalKind == PointerSignalKind.SCROLL) { packet.putDouble(-event.getAxisValue(MotionEvent.AXIS_HSCROLL)); // scroll_delta_x packet.putDouble(-event.getAxisValue(MotionEvent.AXIS_VSCROLL)); // scroll_delta_y } else { packet.putDouble(0.0); // scroll_delta_x packet.putDouble(0.0); // scroll_delta_x }
这一段代码是不是和 Flutter
侧的代码很像呀:

我们看 render : FlutterRenderer 是怎么把数据包发送过去的。
/// FlutterRenderer public void dispatchPointerDataPacket(@NonNull ByteBuffer buffer, int position) { flutterJNI.dispatchPointerDataPacket(buffer, position); } /// FlutterJNI @UiThread public void dispatchPointerDataPacket(@NonNull ByteBuffer buffer, int position) { ensureRunningOnMainThread(); ensureAttachedToNative(); nativeDispatchPointerDataPacket(nativeShellHolderId, buffer, position); } private native void nativeDispatchPointerDataPacket( long nativeShellHolderId, @NonNull ByteBuffer buffer, int position);
调用的就是 JNI
的代码,JNI
调用到了 Engine
底层的代码,尽而调用到了 Flutter
层,如下图中 engine
的代码。

下个系列详细介绍 C++ 层是怎么和 Flutter 交互的,这个系列暂且只需要知道 Flutter 层设置了一个手势处理方法 PlatformDispatcher
的 _dispatchPointerDataPacket
,当识别到手势交互时,Android 的 FlutterView 对原始的手势进行了格式的封装,然后通过 JNI
调用到了上面的 _dispatchPointerDataPacket
即可。
现在知道了手势怎么来的,下面我们来看怎么处理手势的。
Flutter 是怎么处理手势的
上面讲的都是 Flutter
和 Native
交互的东西,对于我们使用者来说,我们的起点就是 GestureBinding
的 _handlePointerDataPacket
方法,只要知道它的逻辑,就知道 Flutter 的手势逻辑了。
// GestureBinding void _handlePointerDataPacket(ui.PointerDataPacket packet) { _pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, window.devicePixelRatio)); //第一处 if (!locked) _flushPointerEventQueue(); } void _flushPointerEventQueue() { while (_pendingPointerEvents.isNotEmpty) handlePointerEvent(_pendingPointerEvents.removeFirst()); //第二处 }
Native 调用 Flutter 的时候是一个数据包,所以需要将数据包进行转换,转换的结果就是 _pendingPointerEvents : Queue 队列,然后 flush 队列。
flush
就是从前往后(先进先出)的边移除边处理每一个事件。
比如:队列中有三个事件:A、B、C。
那么:会依次调用 handlePointerEvent(A)、handlePointerEvent(B)、handlePointerEvent(C)。
那我们继续看 handlePointerEvent
是怎么处理的。
/// GestureBinding void handlePointerEvent(PointerEvent event) { ///... 省略代码 _handlePointerEventImmediately(event); } void _handlePointerEventImmediately(PointerEvent event) { HitTestResult? hitTestResult; if (event is PointerDownEvent || event is PointerSignalEvent || event is PointerHoverEvent) { 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) { hitTestResult = _hitTests[event.pointer]; } if (hitTestResult != null || event is PointerAddedEvent || event is PointerRemovedEvent) { dispatchEvent(event, hitTestResult); //第三处 } }
handlePointerEvent
就是直接调用了 _handlePointerEventImmediately
方法,在方法内部进行了事件的处理。和我们预想的是不是很像,根据事件类型处理不同的事情。
一般情况下: Down 事件用来记录,Up 事件用来结束,其他的事件做分发。就是对应了第一处、第二处和第三处。
我们先看 PointerDownEvent 事件是怎么记录事件开始的。
Down 事件记录开始
/// GestureBinding void _handlePointerEventImmediately(PointerEvent event) { HitTestResult? hitTestResult; if (event is PointerDownEvent || event is PointerSignalEvent || event is PointerHoverEvent) { hitTestResult = HitTestResult(); //第一处 hitTest(hitTestResult, event.position); //第二处 if (event is PointerDownEvent) { _hitTests[event.pointer] = hitTestResult; } } /// 代码省略... if (hitTestResult != null || event is PointerAddedEvent || event is PointerRemovedEvent) { dispatchEvent(event, hitTestResult); //第三处 } }
就做了下面件事:
-
① 声明
HitTestResult
,我们可以认为HitTestResult
是一个集合,集合中存放的就是可以接受处理这个落点的所有对象。 -
② 发起点击检测,就是看落点是不是在某个
RenderObject
上 -
③
_hitTests
的Map
中添加第一处声明的HitTestResult
,Map 的Key
是手势的 id,_hitTests
是 GestureBinding 的成员变量,所以每一个事件序列都会以 Map 的形式保存下来。
下面我们详细看。
第一处是声明了 hitTestResult
,并在第二处直接调用了 hitTest
。

注意看,hitTest 是有被重写的表示的,重写的地方就是 RenderBinding
,方法如下:
/// RenderBinding @override void hitTest(HitTestResult result, Offset position) { renderView.hitTest(result, position: position); super.hitTest(result, position); }
注意看,调用了根 RenderView
的点击检测,然后再调用上面 GestureBinding
的点击检测。
根 RenderView
的点击检测做了啥呢?
/// RenderView bool hitTest(HitTestResult result, { required Offset position }) { if (child != null) child!.hitTest(BoxHitTestResult.wrap(result), position: position); result.add(HitTestEntry(this)); return true; } /// RenderObject bool hitTest(BoxHitTestResult result, { required Offset position }) { if (_size!.contains(position)) { if (hitTestChildren(result, position: position) || hitTestSelf(position)) { result.add(BoxHitTestEntry(this, position)); return true; } } return false; }
就做了两件事:
-
① 发起子节点的落点检测,这个过程是一个递归的过程,会将所有宽高包含的子孙节点都添加到集合中
-
② 将
根View
添加到集合中
这就是 根View
的落点检测过程,现在回过头看:
/// RenderBinding @override void hitTest(HitTestResult result, Offset position) { renderView.hitTest(result, position: position); super.hitTest(result, position); }
renderView.hitTest
就是添加了所有的符合条件的 RenderObject
,super
是什么呢?super
是 GestureBinding
的 hitTest,如果不熟悉可以看前面的 Flutter 必知必会系列 —— mixin 和 BindingBase 的巧妙配合,如下:

把 GestureBinding 本身添加到了集合中。我们举个例子:

落点落在了 B 上,B 是 A 的子节点,那么形成的 hitTestResult 中会维护上图中黑色的列表,最巧妙的地方是 HitTestResult 维护的列表中最后一个元素是 Binding。这样就形成了:Binding 是手势的起点和终点,至于手势上的有哪些节点,它并不关心,它只负责处理框架层的东西。
那么框架层的东西是啥呢?
就是 down 事件之后的 dispatchEvent
。
dispatchEvent 分发处理事件
/// GestureBinding void _handlePointerEventImmediately(PointerEvent event) { HitTestResult? hitTestResult; if (event is PointerDownEvent || event is PointerSignalEvent || event is PointerHoverEvent) { 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) { hitTestResult = _hitTests[event.pointer]; } if (hitTestResult != null || event is PointerAddedEvent || event is PointerRemovedEvent) { dispatchEvent(event, hitTestResult); //第一处 } }
我们再看 _handlePointerEventImmediately
方法,在判断完具体的事件类型之后,就走到了第一处代码处。
见名知意,这个就是分发。分发就是将事件分给应该处理的节点,比如上图中的 A 和 B 等等。
/// GestureBinding void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) { ///... 代码省略 for (final HitTestEntry entry in hitTestResult.path) { try { entry.target.handleEvent(event.transformed(entry.transform), entry); } catch (exception, stack) { } } }
注意一点: 在循环遍历中,最后一个元素是 Binding
,其他的元素都是RenderObject
,我们暂且不看,只看框架层的东西。
/// GestureBinding @override void handleEvent(PointerEvent event, HitTestEntry entry) { pointerRouter.route(event); //第一处 if (event is PointerDownEvent) { //第二处 gestureArena.close(event.pointer); } else if (event is PointerUpEvent) { gestureArena.sweep(event.pointer); } else if (event is PointerSignalEvent) { pointerSignalResolver.resolve(event); } }
第一处就是发起手势的处理,pointerRouter
是路由器的意思,route
方法 就是路由到具体的处理。
第二处就是针对不同的事件框架应该干啥事,这里涉及到了手势竞技场的概念。
我们具体来看,先看路由的处理。
///PointerRouter void route(PointerEvent event) { final Map<PointerRoute, Matrix4?>? routes = _routeMap[event.pointer]; final Map<PointerRoute, Matrix4?> copiedGlobalRoutes = Map<PointerRoute, Matrix4?>.from(_globalRoutes); if (routes != null) { _dispatchEventToRoutes( event, routes, Map<PointerRoute, Matrix4?>.from(routes), ); } _dispatchEventToRoutes(event, _globalRoutes, copiedGlobalRoutes); } void _dispatchEventToRoutes( PointerEvent event, Map<PointerRoute, Matrix4?> referenceRoutes, Map<PointerRoute, Matrix4?> copiedRoutes, ) { copiedRoutes.forEach((PointerRoute route, Matrix4? transform) { if (referenceRoutes.containsKey(route)) { _dispatch(event, route, transform); } }); } @pragma('vm:notify-debugger-on-exception') void _dispatch(PointerEvent event, PointerRoute route, Matrix4? transform) { try { event = event.transformed(transform); route(event); //第一处 } catch (exception, stack) { } }
_routeMap
是一个 Map ,Map 的 key 就是事件序列的 id ,value 是 PointerRoute
是事件的处理回调。
_dispatchEventToRoutes
就是分发,具体走到了 _dispatch
,然后走到了第一处的 route(event)
。
这里注意一点: PointerRoute
是一个函数签名,就是 _routeMap
中的 value,也就是说 谁注册了,它就路由到谁。
那么谁注册了呢?
就是调用 addRoute
的地方,具体就是手势收到落点的地方。
/// GestureRecognizer void startTrackingPointer(int pointer, [Matrix4? transform]) { GestureBinding.instance!.pointerRouter.addRoute(pointer, handleEvent, transform); //第一处 _trackedPointers.add(pointer); _entries[pointer] = _addPointerToArena(pointer); }
在手势需要追踪的时候,就调用到了 第一处的代码,第一处就是具体的手势注册代码,注册的 PointerRoute
就是手势的 handleEvent
。
所以流程就是: 具体的手势注册自己的事件处理方法,然后框架层负责调用。
我们再看手势竞技场干了啥。
/// GestureRecognizer void handleEvent(PointerEvent event, HitTestEntry entry) { pointerRouter.route(event); if (event is PointerDownEvent) { gestureArena.close(event.pointer); } else if (event is PointerUpEvent) { gestureArena.sweep(event.pointer); } else if (event is PointerSignalEvent) { pointerSignalResolver.resolve(event); } }
如果是 PointerDownEvent
事件,那么 gestureArena
就关闭掉,关闭的意思是:
-
如果竞技场中只有一个元素,那么该元素就获胜,获得获得手势处理权,就开始处理手势。
-
如果竞技场中没有元素,那么没有元素获胜。
-
如果竞技场中有特别渴望胜利的,那么特别渴望胜利的就获得手势处理权,比如 EagerGestureRecognizer,这个机会很少,因为会打破默认的处理。
如果是 PointerUpEvent
事件,那么就需要竞争了 sweep
。竞争的结果就是第一个获得手势处理权,其他的手势拒绝手势。
上面就是框架层的手势处理,下面我们看看具体的手势组件。
手势组件源码解读
手势处理组件我们常用的就是 Listener
、GestureDetector
等等。GestureDetector
包裹的就是 Listener
,下面我们看看它俩的源码。
Listener 源码
class Listener extends SingleChildRenderObjectWidget { const Listener({ Key? key, this.onPointerDown, this.onPointerMove, this.onPointerUp, this.onPointerHover, this.onPointerCancel, this.onPointerSignal, this.behavior = HitTestBehavior.deferToChild, Widget? child, }) : assert(behavior != null), super(key: key, child: child); @override RenderPointerListener createRenderObject(BuildContext context) { return RenderPointerListener( onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerHover: onPointerHover, onPointerCancel: onPointerCancel, onPointerSignal: onPointerSignal, behavior: behavior, ); } }
Listener
是 SingleChildRenderObjectWidget
类型的组件, 构造方法中的 onPointerDown
中的就是 Down
事件的回调。具体的逻辑在 RenderPointerListener
中。
/// RenderPointerListener class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior { @override void handleEvent(PointerEvent event, HitTestEntry entry) { if (event is PointerDownEvent) return onPointerDown?.call(event); if (event is PointerMoveEvent) return onPointerMove?.call(event); if (event is PointerUpEvent) return onPointerUp?.call(event); if (event is PointerHoverEvent) return onPointerHover?.call(event); if (event is PointerCancelEvent) return onPointerCancel?.call(event); if (event is PointerSignalEvent) return onPointerSignal?.call(event); } }
我们看到了 handleEvent
方法,针对每一种类型处理了不同的回调。
还记得 handleEvent
是谁调用的吗? handleEvent
是从 RenderObject
继承的方法。
// GestureBinding void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) { for (final HitTestEntry entry in hitTestResult.path) { try { entry.target.handleEvent(event.transformed(entry.transform), entry); } catch (exception, stack) { } } }
就是这里的 handleEvent
方法,hitTestResult
的 path
就是我们的RenderObject
,也就是 RenderPointerListener
,所以 entry.target.handleEvent
会走到 handleEvent
中。
注意一点: 如果我们代码只是单纯的使用了 Listener ,并不会走到框架的手势处理,只会走到 entry.target.handleEvent 就结束了。
下面我们看手势的组件处理。
GestureDetector 源码
GestureDetector
是 StatelessWidget
,为每一种手势都封装了一个回调,比如点击的就是 onTapDown
,其逻辑都在 build 方法中。
/// GestureDetector
@override
Widget build(BuildContext context) {
final Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{};
if (onTapDown != null ||
onTapUp != null ||
onTap != null ||
onTapCancel != null ||
onSecondaryTap != null ||
onSecondaryTapDown != null ||
onSecondaryTapUp != null ||
onSecondaryTapCancel != null||
onTertiaryTapDown != null ||
onTertiaryTapUp != null ||
onTertiaryTapCancel != null
) {
gestures[TapGestureRecognizer] = GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
() => TapGestureRecognizer(debugOwner: this),
(TapGestureRecognizer instance) {
instance
..onTapDown = onTapDown
..onTapUp = onTapUp
..onTap = onTap
..onTapCancel = onTapCancel
..onSecondaryTap = onSecondaryTap
..onSecondaryTapDown = onSecondaryTapDown
..onSecondaryTapUp = onSecondaryTapUp
..onSecondaryTapCancel = onSecondaryTapCancel
..onTertiaryTapDown = onTertiaryTapDown
..onTertiaryTapUp = onTertiaryTapUp
..onTertiaryTapCancel = onTertiaryTapCancel;
},
);
}
/// 代码省略
return RawGestureDetector(
gestures: gestures,
behavior: behavior,
excludeFromSemantics: excludeFromSemantics,
child: child,
);
}
逻辑比较简单:根据我们设置的回调生成手势,比如 onTapDown 就是 TapGestureRecognizer 手势,然后将手势封装到了 gestures
中,并在最后返回了 RawGestureDetector
组件。
RawGestureDetector
是 StatefulWidget 类型的组件,其逻辑在 RawGestureDetectorState
的 initState 和 build 方法中。
/// RawGestureDetectorState void initState() { super.initState(); _semantics = widget.semantics ?? _DefaultSemanticsGestureDelegate(this); _syncAll(widget.gestures); } void _syncAll(Map<Type, GestureRecognizerFactory> gestures) { final Map<Type, GestureRecognizer> oldRecognizers = _recognizers!; _recognizers = <Type, GestureRecognizer>{}; for (final Type type in gestures.keys) { _recognizers![type] = oldRecognizers[type] ?? gestures[type]!.constructor(); gestures[type]!.initializer(_recognizers![type]!); //第一处 } }
初始化的过程就是 gestures
中手势初始化的过程,这个初始化就是手势中回调的赋值。比如 TapGestureRecognizer 的 onTapDown 赋值为我们开发者设置的自定义回调,重点是第一处的代码。这里用到了工厂模式大家可以细细品味一下。
build 的逻辑如下:
/// RawGestureDetectorState @override Widget build(BuildContext context) { Widget result = Listener( onPointerDown: _handlePointerDown, behavior: widget.behavior ?? _defaultBehavior, child: widget.child, ); return result; }
我们看,就是返回了 Listener
组件,然后给 Listener
的 Down
事件赋值为 _handlePointerDown
回调,我们知道 Listener
是不处理手势的,只负责接受 Down
等事件的回调,所以 Down 事件的时候,会调用到_handlePointerDown
方法中。
/// RawGestureDetectorState void _handlePointerDown(PointerDownEvent event) { for (final GestureRecognizer recognizer in _recognizers!.values) recognizer.addPointer(event); }
我们看代码比较简单,就是将手势注册到了路由中。具体如下:
/// GestureRecognizer void addPointer(PointerDownEvent event) { _pointerToKind[event.pointer] = event.kind; if (isPointerAllowed(event)) { addAllowedPointer(event); } else { handleNonAllowedPointer(event); } } @override @protected void addAllowedPointer(PointerDownEvent event) { startTrackingPointer(event.pointer, event.transform); } @protected void startTrackingPointer(int pointer, [Matrix4? transform]) { GestureBinding.instance!.pointerRouter.addRoute(pointer, handleEvent, transform);//第一处 _trackedPointers.add(pointer); assert(!_entries.containsValue(pointer)); _entries[pointer] = _addPointerToArena(pointer); }
我们看最终调用到了手势的 startTrackingPointer
中,我们看第一处的代码,第一处就是我们前面介绍的注册 handleEvent 的地方。
那么 handleEvent
是啥呢?
/// PrimaryPointerGestureRecognizer @override void handleEvent(PointerEvent event) { if (state == GestureRecognizerState.possible && event.pointer == primaryPointer) { /// 代码省略 if (event is PointerMoveEvent && (isPreAcceptSlopPastTolerance || isPostAcceptSlopPastTolerance)) { resolve(GestureDisposition.rejected); stopTrackingPointer(primaryPointer!); } else { handlePrimaryPointer(event); //第一处 } } stopTrackingIfPointerNoLongerDown(event); }
我们看第一处代码,第一处代码就是手势接收到 Down
事件的处理。
/// PrimaryPointerGestureRecognizer @override void handlePrimaryPointer(PointerEvent event) { if (event is PointerUpEvent) { _up = event; _checkUp(); } else if (event is PointerCancelEvent) { resolve(GestureDisposition.rejected); if (_sentTapDown) { _checkCancel(event, ''); } _reset(); } else if (event.buttons != _down!.buttons) { resolve(GestureDisposition.rejected); stopTrackingPointer(primaryPointer!); } }
上面方法中,我们发现没有关于 Down
事件的处理,所以 HitTestResult
的 path 中走到了 Binding 的处理。
/// GestureBinding @override void handleEvent(PointerEvent event, HitTestEntry entry) { pointerRouter.route(event); if (event is PointerDownEvent) { gestureArena.close(event.pointer); } else if (event is PointerUpEvent) { gestureArena.sweep(event.pointer); } else if (event is PointerSignalEvent) { pointerSignalResolver.resolve(event); } }
也就是 gestureArena.close(event.pointer) 的代码,根据我们上面的介绍,如果我们只设置一个 onTapDown 的话,直接关闭竞技场:
void _tryToResolveArena(int pointer, _GestureArena state) {
if (state.members.length == 1) {
scheduleMicrotask(() => _resolveByDefault(pointer, state)); //第一处
} else if (state.members.isEmpty) {
_arenas.remove(pointer);
} else if (state.eagerWinner != null) {
_resolveInFavorOf(pointer, state, state.eagerWinner!);
}
}
void _resolveByDefault(int pointer, _GestureArena state) {
if (!_arenas.containsKey(pointer))
return; // Already resolved earlier.
final List<GestureArenaMember> members = state.members;
_arenas.remove(pointer);
state.members.first.acceptGesture(pointer); //第二处
}
也就是第一处代码的地方。点击手势直接获胜。走到第二处代码,也就是手势的 acceptGesture 方法进行调用。
@override
void acceptGesture(int pointer) {
super.acceptGesture(pointer);
if (pointer == primaryPointer) {
_checkDown();
_wonArenaForPrimaryPointer = true;
_checkUp();
}
}
void _checkDown() {
if (_sentTapDown) {
return;
}
handleTapDown(down: _down!);
_sentTapDown = true;
}
@protected
@override
void handleTapDown({required PointerDownEvent down}) {
final TapDownDetails details = TapDownDetails(
globalPosition: down.position,
localPosition: down.localPosition,
kind: getKindForPointer(down.pointer),
);
switch (down.buttons) {
case kPrimaryButton:
if (onTapDown != null)
invokeCallback<void>('onTapDown', () => onTapDown!(details)); //第一处
break;
case kSecondaryButton:
if (onSecondaryTapDown != null)
invokeCallback<void>('onSecondaryTapDown', () => onSecondaryTapDown!(details));
break;
case kTertiaryButton:
if (onTertiaryTapDown != null)
invokeCallback<void>('onTertiaryTapDown', () => onTertiaryTapDown!(details));
break;
default:
}
}
最终会调用到第一处代码,也就是调用到了我们的自定义的 onTapDown 回调中。
上面就是手势组件的处理过程。
总结
上面我们就从宏观上介绍了手势的处理过程,从 Native 中接受到事件的数据序列,GestureBinding 接受事件序列,然后进行格式转换,从框架层进行处理。如果没有手势参与的话,就直接调用了具体的 handle 事件。如果有手势参与的话,那么就通过手势竞技场来调用注册好的手势回调。
更加细节的手势处理,可以在此基础上进行深究,比如多个手势怎么具体的进行获胜判断的等等。
评论(0)