从源码看flutter(五):GestureDetector篇

从源码看flutter系列集合

开篇

flutter的接触事情涉及到的东西比较多,本篇文章将会从 GestureDetector 作为切入点,来对接触事情的完结做一个全面的了解

GestureDetector

class GestureDetecti n 8 s $or extends StatelessWidget {
...
@override
Widget build(BuildContext context) {
final Map< } a M !;Type, GestureRecognizerFactory> gestk ? T u 7 1 n  Hures = <Type, GesturS l 1 k % ! JeRecognizerFactory>! . w W f ( $ N 8{};
...
red M h  p Gturn RawGestureDetecto. z Cr(
gestures: gestures,
behavior: behavior,
excludeFromSemantics: e= I .xcludeFromSemantics,
child: child,
);& 1 5 J : S
}
}

这儿,会将所有在 GestureDetector 设置的各种点击事情,放w , [ w ] ; q 7 }在各个 GestureRecognizer 中,然后经过 GestureRecognizerFactoryT e E % : } 对其进行封装,存入 gestures 中,这儿 GestureRecognizerFactory 的完结仍是蛮有意思的,感兴趣的小伙伴能够去看一下它的源码

这儿,咱们先简略了M _ 0 r _ #解一下 GestureRM o r yecognize( ? o Pr

GestureRecognizer

abstr^ N : d z 7 f Fact class GestureRecognizer extends GestureArenaMember with DiagnosticableTreeMixin {
...
void addPointer(PointerDownEvent event) {
...
if (isPJ z C y p &  JointerAllowed(event)) {
addAllowedPointer(event);
} elv F 0 ` 7 zse {
handleNonAllowedPointer(event);
}
}
@protected
void addAllowedPointer(PointerDownEvenG O o ^ g & *t event) { }
...
}

GestS z : | , 6 M ~ureRecognizer 首要供给了addPointer(downEvent) 用于接6 T t r B 0 n f WPointerDownEvent 目标,它会调用 addAllowedPointer(event) 办法,由子类去详细完结

GestureRecognizer 承继于 GestureArenaMember,它只供给了两个办法

abstract class GestureArenaMember {
void9 ` + q acceptGj ^ Qesture(int pointer);
void rejectGesture(int pointer);
}

分别表示手势竞技场成员取胜与失利时会调用的办法,接下来咱们来看一下 RawGestureDetector

RawGestureDetector

class RawGa j v N ^ 0 m 0 GestureDetector extends StatefulWiJ 4 + jdget {
...
}
class RawGestureDetectorState exth E & * % 8 v bends State<RawGesU ; h 6 G _ r itureDetector> {
...
void _handlePointerDow^ L % & 1 (n(PointerDownEvent event) {
assert(_recognizers != null);
for (final GestureRecognizer recognizer in4 a c M H E c x _recognizers.values)
recognizer.addP| q } jointer(event);
}
。。。
@override
Widget build(BuildContext context) {
Widget[ d U ! result = Listener(
onPointerDown: _handlePointerDown,
behavior: widget.behavior ?? _defat Q uultBehavior,
child: widget.child,
);
if (!widget.excludeFromSemantics)
result = _u L ] a 2 Q ! v yGestureSemantics(...);
return result;
}
...
}

RawGestureDetector 是一个 StatefulWi* ? # s l o 5dget ,它默许回来的是 Listener 目标。其间还有一个 _GestureSemantics 它用于完结一些具有语义化的手势,比方长按展示 tooltip等,这儿咱们不用重视它。

这儿将之前经过 GestureDetector 传入的 gestures 转换成了 _recognizers,而且将他们放在了 _handlePointerDof ( g h M 8 T K 1wn(eventG P & X 4 M k L [) 办法里经过 onPoin$ N e 5 u terDown 传给了 L ( @istener 目标

这儿还需要注意, _handlePointerDown(event) 中对 GestureRecognizer 目标进行了遍历,并调用了他们的 addPointer(event) 办法

接下来咱们看 – : [ $ – v s一下 Listener

Listener

class Listener extends StatelessWidget {
...
@p e [ i ( ioverride
Widget build(BuildContext context) {
Widget result = _child;
if (onPointerEnter != nullY * & B p l M ||
onPointerExit != null ||
onPointerHover != null) {
rec H 4sult = MouseRegion(...)/ v d . E;
}
result = _PointerListener(
onPointerDown: onPointerDown,
onPointerUp: onPointerUp,
onPointerMove: onPointerMove,
onPointerCancel: onPointerCancel,
onPointerSignal: onPointerSignal,
behavior: behavior,
child: result,
);
return result;
}
}

Listener 是一个 StatelessX v CWidget ,当传入v 1 D Y z )的部分事情q q } I v u 4 w $不为 null 时,比方悬停事情 ona + 5 x k R 1 C ?PointerHover,回来的便是 MouseReg6 8 hion,它用来处理鼠标的输入,默许回来 _PointerListener,这儿咱们只需要重视这个目标

从前面咱们能够知道,咱们只对 Listener 传入了 onPointerDown ,所以这儿传递给 _PointerListenerm [ 2 0 b s S G其他手势回调都是 null

_PointerListener

class _PointerL# X D A A f p Ai8 Q , ^ g 0 *stener extends SingleChildRenderObjectWh L B X w C * Didget {
...
@override
RenderPointerListener createRenderObject(Build@  ; D w + aContext context)9 7 K Y W K q 2 D {
return RenderPointerListener(
onPointerDown: onPointerDown,
...
);
}
...
}

_PointerListene{ f J J ( H ;r 是一个 SingleChildRenderObjectWidget ,它对应的 RenderObjectRenderP9 + %ointerListener ,咱们来看一下这个目标

RenderPointerListener

class RenderPointerListener extends RenderProxyBoxWV 5 P T 6 A ;ithHB ? hitTestBehav` 2 L +  q P 5 tior {
...
@override
void handlP + ,eEvent(PointerEvent event, HitTestEntry[ 8 J ] a entry) {
...
if (onPointerDown != null &# O 5;& event is PointerDownEvent)
return onPointerDown(event);
...
}
}

之前咱们分析 RenderObject 时就知道, Re] W WnderObject 目标有一个 HitTestTarget 接口

abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixiO 4 G ?n implements HitTestTarget { ... }
abstract class HitTestTarget {
factory HitTestTarget._() => null;
void handleEvent(PointerEvent event, HitTestEntry entry);
}

这个接口供给了 han0 2 - s vdleEvent(...) 办法供 RenderObject 完结去处理各种手势事情,终究基本上都是交给子类去完结这个办法。这儿的 RenderPointerListener 便是如此

这儿关于 GestureDetectorF $ 9全体结构有了一个开端的了解,而且无法再往下深入了,接下来咱们A + 7 3将从别的一个切入点来看手势事情。那便是手势分发的起点

手势分发流程

起点其实不难找,之前咱们就知道过了,runApp(...) 办法作为flutter的入口,会对 WidgetsFlutterBinding 进行初始化,WidgetsFlutterBinding 混入了多个 Binding 目标,其D i s w % a间就有专门处理手势的 GestureBinding,咱们看一下就知道了

手势分发起点:[ ? D c GestureBinding

GestureBindinginitIN h 8nstancH x F L kes() 中能够看到如下内容

mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatY l ) wcher, HitTestTq J D warget {
@override
voiB e R Q E J i U cd initInstances() {
super.initInstances();
_inL y # } } f S u estance = this;
window.onPointerDataPacket = _handlePoiS 6 ^ 1 o A 0nterDataPacket;
}
..= | S x.
}

这儿的 _haN 6 % Z 6 TndlePointerDataPacket 便是接触事情的起点,它会处理设备输入的信息,将其转换为flutg C : r X + w 8ter中的手势事情

_handlePointerDataPacket(…)

  finalt o ` Queue<PointeM c y x # w  ]rEvent> _pendingPointerEvents = Queue<PointerEvent>();
void _handlePointerD. 1 o f G SataPacket(ui.PointerDataPacket packet) {
_pendingPointerEvents.addAll(PointerEventConverter.Z 2 B L L k *expand(packet.data, window.devicePixelRatio));
if (!locked)
_flushPoY E r { JinterEventQueue();
}

手势事情都被存放在了一个队列中,之后会调用 _flushPointerEventQueue() 来进行手势分发

_flushPointerEventQueue(…)

  void _flushPointerEventQueue() {
assert(!locked);
while (_pendingPoin I 8 RterEvents.isNotEmpty)
_handlePointerEvent(_pendingPointerEvents.removeFirst());
}

这儿经过遍` D o C % X历队列,调用 _handlePointerEvent(event) 对各个event进X d k t行处理

_handlePointerEvent(event)

  void _handlePointerR l N x 0 F / PEvm h v Gent(PointerEvent event) {
...
HitTestResult hitTestResui V R =lt;
if (event is PointerDownEvent || event is PointerSignalEvent) {
..} g 0.
} else if (event is PointerUpEvent || e) 1 N O a h S G xvent is PointerCancelEvent5 t H) {
.7 s , G w..
} else if (event.down) {
...
}
...
if (hitTestResult != null ||
event is PointerHoverEvent ||
event is Point$ ^ w 4erAddedE+ @ z Z 5 I ; Zven i l J s  B Ft ||
event is PointerRemovedEvent) {
dispatchEvent(event, hitTestResult);
}
}

当你在在屏幕上点击一下,触发的事情流程如下:

PointerHoverEvent -> PointerDownEvent -> PointerUpEvent

在屏幕上滑动时,流程如下

PointerHoverEvent -> PointerDownEvent –D L R Q x k u (> …PointerMoveEvent…0 d % 0 g F h ~ -> PointerUpEvent

Pointeo j ^ c ] @ C S DrHoverEvent 首要用于 flutter_web 中的鼠标悬停事情,这儿咱们不重视它,咱们能够看一下,s M D y ^ Z当触发 PointerDown? c v +Event 时,做了些什么

  finalc = B C $ X $ Map<int, HitTestResult> _hitTests = <int, HitTestResult>{};
...
if (e$ } K ~ a c k (vent is PointerDownEvent || event is PointerSignalEvent) {
assert(!_hitTests.containsKey(event.pointer));
hitTestRy ~ z [ s . z ^ Mesult = HR N ]itTestResult();
hitTest(hitTestResult, event.position);
if (event is PointerDownEvent) {
_hitTests[event.poinq Z L K x X *ter] = hitTestResult;
}
...
}
...
@overrid{ e R z Ie // from HitTestable
void hitTest(HitTestResult result, Offset position) {
result.add(HitTestEntry(this));
}

这儿的 hitTest(...) 由接口 HitTestable 供给,值得注意的是 GestureBindingRendererBindd c u ing 都完结了这个接口,能够看一下在 RendererBinding 中的完结

  ///RendererBinding
@override
void hitTest(HitTestResult result, Offset position) {
assert(renderView != null);
renderView.hitTest(result, position: position);
supe# | x W K h 1 r.h/  y 4 g l ? # 9itTest(result, position);
}

这儿调用了 RenderViewh@ ( 5 2 U _ FiI o /tTest(...) 办法,咱们现已知道过了a R ! W,它是根 RenderObject 目标,进入它的 hitTee r z b { 4st(..B T D ? _ @.) 看一下

  ///RenderView
bool hitTest(HitTestResult result, { Offset position }) {
if (child !=^ 3 4 Q K q ? Y P null)
child.hY 3 : 9 LitTest(BoxHitTestResult.wrap(result), position: position);
result.add(HitTestEntry(this));
return true;
}

这儿? V CchildRR X S f 0 G | wende# i DrBox 目标,在 RendeT : MrBox 中默许供给了 hitTestt o ` s S u(...) 这个办法

  ///RenderBox
bool hitTest(BoxHi| r i y Z tTestResult result, { @required Offset position }) {
...
if (_size.coB X @ Kntains(position)) {
ifW H P f (hitTestChildren(result, position: position) || hitTestSelf(position)) {
resW * a 1 Sult.add(BoxHitTestEntry(this, position));
retV Y + Furn true;
}
}
return false;
}

所以看到这儿你就能知道,当触发了o , g Poinq L P h @teru r z ^ ;DownEvent 事情时,会调用所有 RenderBoxhitTest(...) 办法,将符合条件的目标放入到 BoxHitTestEntry(...) 中,再存入 HitTestResult 保护的 List<HitTestEntry> _path

HitT2 s , r 2 ? ~ p bestEntry 接受的目标是 HitTestTarget ,上面咱们也说到过了,RenderObject n 7 Q u G p ct 是完结了这个接口的。所以终究这些 RenderBox 会被存入 BoxHitTestEntry 先放入 List<HitTestEntry> 中,其次是存入了 HitTestE# K GntryRenderView ,终究才是 GestureBM M N Binding 目标

至于为什么要把+ / l +这些目标搜集起来放入 HitTQ C &estResult 呢?后边会逐步阐明

HitTestResult 被创建后,会被存入GestureBindingz Y P X @ @ B 保护的 Map<int, HitTestResult> _hitTests 中,keyevent.pointerZ b I s X I每触发一次事情,pointer 的值都会+1,不会重复

接下来,会进入= o n m g dispatchEvent(event, hitTestResult) 办法,进行分发事情

dispatchEvent(…)

  @over- K J I v hride // from HitTA F W zestDispatcher
void dil 0 & [ PspatchEvent(PointerEvent event, HitTestK P @ Z L x = w fResult hitTestResult) {
...
if (hitTestResult == nulh T b 4l) {
assert(event is PointerHoveF 2 F &rEvent || event is PointerA~ j : x R EddedEvent || event is PointerRemovedEvent);
t: _ L : n bry {
pointerRouter.route(event);
} catch (exceptk ] q q ) - q w bion, stack) {
...
return;
}
for (final HitTestEntryp ~ V z b & J g entry in hitTestResult.path) {
try {
entrk x q } ; v G = Sy.target.handleEvent(event.transformed(entry.transform), entry);
}
...
}
}

咱们首要重视 entry.target.handleEvent(...) 办法,这儿对之前9 H ( h + S 5 YhitTest(...) 中增加的各个完结了 HitTestTarget 接口的目标,调用其 handleEvent(...) 办法。而 hitTestResult.path 的次序咱们现d a * k A q 8 F已说过了,大致是W / A b u P 1下面这样:

… -> RenderPo% V 8 biJ P } n [ K } H 0nterListener -> … -> RenderView -> Geso 7 R tureBinding

这儿会顺次调用他们完结的 handleEvent(...) 。而事情的分发,便是经过这样完结的!

m C – ! # R } i端手势分发

F w + ]们再次进入 RenderPointerListe= o wnerhandl, ` @ * j {eEvent(...) 办法

RenderPointerListener

  @over. ? c & .  ridX r l f ( M O @ Pe
void handleEvent(PointerEvent event, HitTestEO l 8 $ N fntry entry) {
...
if (onPointerDown != null && event is PointerDownEvent)
return onPointerDown(event);
...
}

这儿会调用 onPointerDown(event) ,经过前面的的了解,咱们知y 5 I q道这个办法便是 RawGestureDetectorState 传入的 _handlePointP E 2 7erDown(...),再来看一遍

  void _handlePointerDown(PointerDownEv t T } ( 3 ]ent event) {
for (final GestureRecognizer recognizer in _recognizers.valueu s e G t 2 Zs)
recognizer.ad[ u L &dPointer(event);
}

addPointer(event) 的内容之前也说过了,终究都是调用 GestureRecognizerj t R - $ 供给的 addAlloa 8 .wedPointer(...) 办法。假如咱们在 GestureDetector 中设置了 onTapDown() 或者其他点击事情,这儿就会调用 TapGestureRecognizeraddPointer(...) 办法。咱们就先以它为` q , Z ) P例,来看一下都做了些什么

TapGestureRecognizer

咱们先简略的看一下 TapGestureRecognizer 的承继结构

TapGestureRecognizer -&g2 1 ot; BaseTapGestureRecognizer –+ r * %&gt@ g ? b G; PrimaryPointerGestureRecognizer -&k g q i !gt; OneSequenceGestureRecognizer -> Gesturx = b [eRecognizer

这些类中,只要 GestureRecognizer 完结了 addPoint7 F t T O U ver(...) 办法,| V V y 7 D ~只要 BaseTapGestureRecognizerPrimaryPointerGestureRecognizer 完结了 addAlloW p 5 g |wedPoin~ 6 Xter(...) 办法

能够先来看一下 BaseTapGestureRecognizeraddAllowedPo| R N q $ = w /inter(...M C E q 9 c %)

BaseTapq S W @GestureRecognizer

  PointerDownd Q ( F y f v (Event _dow) ( Z % e i C sn;
...
@override
void addAllowedPointer(PointerDownEvent event) {
assert(event != null);
if (state == GestureRecognizerState.rep 5 aa H ddy) {
_down = event;
}
i6 - ^ x S k ~ If (_down != null) {
super.addAllowedQ q G w E (Pointer(event);
}
}

这儿只B p ( 2是做了一个简略的赋值,保存传递的 Po( ^ ) einterDownEvent ,它会在 _reset() 中被置空

接下来,进入 PrimaryPointerGestureRen x b = w U # T 7cognizeraddAllowedPointer(...)

PrimaryPointerGestureRecognizer

  @override
void addAllowedPointer(PointerDownEvent event) {
staf N G k !rtTrackingPointerU ( 5 O N @ #(event.pointer, event.transform);
if (state == GestureRecognizerState.ready) {
state = GestureRecognizerState.p+ 6 J S ^ - f lossible;
primaryPointer = event.pointer;
initial4 V = RPosition = OffsetPair(local: event.localPosition, global: event.position);
if (deadline != null)
_timer = Timer(deadlY 1 ! ` d ( /ine, () => didExceedDeadlineWithEv0 Z h - u . I oent(event));
}
}

这儿首要重视 startTrackingPointer(...) 办法,它在 Ons M 5 - { F H j 7eSequenceGestureRecognizer 中完结

OneSequenceGestureRecognizer

  @protected
void startTrackingPointer(int pointer, [Matrix4 transform]) {
Gest2 / - K } g EureBinding.instance.pointerRouter.addRoute(pointer,7 l _ . U K handleEvent, tran_ _ )sform);
_trackedPointers.add(pointer);
assert(!_entries.containsValue(pointer));
_entries[pointer] = _addPointerToArena(pointer);
}

上面有两个比较要害的当地,先看第一个 addRoute(...)

  ///PointerRouter
final Map<int, Map<PointerRoute, Matrix4>&5 h ! q q | _ = ]gt; _routeMap = <int, Map<PointerRoutZ , B ~ W ; Ve, Matrix4>>{};
...
void addRoute(int pointer, PointerRoute route, [Matrix4 tranP x Q Z 2 zsform]) {
final Map<PointerRoute, MaS ^ utrix4> routes = _routeMap.putIfAbsent(
pointer,
() => <Po. o / } ! ( L }interRoute, MatriF } J S = 4 = 1x4>{},
);
assert(!routes.containsKey(route));
routes[rod c i ~ute] =. ~ _ d D # A | d traE ] } H 5 - u l {nsform;
}

在这儿把 handleEvent(..z F ] V + ) e q.) 办法作为 PointerRoute 传入到了 PointerRouter 中,它的作用,咱们后边就知道了。接下来看另一个非常要害的当地:_entries[pointer] = _addPointerToArena(pointd G | A T ^er)

  GestureArenaTeam _team;
...
GestureArenaEntry _addPointerToArena(int pointer) {
...
return GestureBinding.instance.gestureArena.add(pointer, this);
}

能够看到,这儿将当时的 OneSequenceGestureRecognizer 作为 GesturU $ -eArenaMember 目标,传入了 GestureBind4 ; : d !ing 中保护的 GestureArenaManager 内。也c F + * w w T便是将需要竞技的手势成员,放入了手势竞技场内。

那么到这儿,RenderPointerListenerhandleEvent(...) 就履行结束了,接下来会履行 RenderVi! m n 7 k S 6ewhandleEvent(...),不过由于它并没有重写这个办法,所以咱们会直接来到 GestureBindinghandleEvent(...)

GestureBinding

  final PointerRoutea H $ M ~ 8 ; _r pointerRouter = PointerRouter();
...
@override // from HitTestTarget
void handleEvent(PointerEvent event, HitTestEr p f Y c  ?ntry entry) {
poinb Z JterRouter.route(event);
if (event is PointerDownEvent) {
gestureArena.close(event.pointer);
} else if (event is PointerUpEvent) {
ge/ h b F 7 % WstureArena.sweep(event.pointer);
} else if (event is PointerSignalEvent) {
pointerSignalResolver.rI 4 { Q # E Wesolve(eveZ X q ; I I I _ Unt);
}
}

这儿就要. m z 4 m z K ?进入这个非常重要的 route(event) 办法了

PointerRouter

  final Map<int, Map<PointerRout, $ B & C U Ze, MatG @ = w K } )rix4>> _routeMap = <int, Map<PointerRoute, Matrix4>>{};
final Map<PointerRoute, Matrix4> _globalRoutes = <PointerRoute, Matrix4>{};
void route(PointerEvent event) {
final Map<c p [ 8 H;PointerRoute, Matrix4> routes = _routeMB _ ? l / .ap[event.pointer];
final Map<PointerRoute, Matrix4> copiedGlobalRoutes = Map<PointerRoute- s 6 B { 3 M, Matrix4>.from(_globalRoutes);
if (routes != null) {
_dispatchEventToRoutes(
event,
routes,
Map<PointerRK S soute, Matrix4>.from(routes),
)m | c @ E _ $ d;
}
_dispatchEventToRoutes(event, _globalRoutes, copiedGlobalRouA x E ! e l  ctes);? S z f } 4 H Y
}

这儿有两个 _dispat; /ch] c s = . DEventToRoutes(...) 办法,后者履行的是 _globalRoutes 中的 PointerRoute,而 _globalRoutes 中存放的是其他的全局手势,比方用于躲藏 ToolTipc 3 4 J A e R / Ws 的手势等,现在还不太了解它的其他详细作用。

不过前面的 routes 便是咱们之前在 OneSequenceGestureRecognizerst4 f 0 ; s + H XartTrackingPG b u e $ h +ointer(...)p p Z a c B增加的各个 handleEvent(...) 办法

能够看一下 _dispatchEventToRoutes(...)

  vo5 } C T W i x aid _dispatchEventToRoutes(
PointerEvent even7 u Z 2 Tt,
Map<PointerRoute, Matrix4> referenceRoutes,
Map<PointerRoute, Matrix4> copiedRoutes,
) {
copiedRoutes.forEach((PointerRoute routH c D ! 9e, M9 k naC l # { D 8 D 3trix4 transform) {
if (referenceRoutes.containsKey(route)) {
_dispatch(even4 g a ~  ` ) at, route, transform);1 2 / O
}
});
}
...
void _dispatch(PointerEvent event, Pointer=  ! WRoute route, Matr= G 2 , = hix4 transform) {
try {
event =R V g ~ event.transformed(transform);
route(event);
}
...
}

便是遍历然后履行所有的 PointerRoute 办法,这儿的 PointerRoute 便是之前的各个 OneSequenceGestureRecognizer 中的 handleEvent(...)

这儿要注意不要把 OneSequenceGestureRecognizerhandleEvent(...)RenderPointerListenerhandleEvent(...) 混杂了

OnC = : R v ieSequenceGestureRecognizer 供给了 handleE W j WEvent(...) ,交由子类去完结,咱们接着之前的点击事情,完结它的点击子类是 Primaryo % 3 [ D Y Y SPointerGestureRecognizer ;假如是拖拽事情的话,完结的子类便是 DragGestureRecognizer

PrimaryPointerGestureRecognizer

  @override
void handleEvent(Poinl = zterEvent event)T 7 r ^ O ( | T F {
assert(state != GestureRecognizerState.ready);
if (state == GestureRecognizerState.possible && event.pointer == primaryPointer) {
final bool isPreAcceptSlopPastTolerance = ...;
final bool isPostAc6 T P q # Y F zceptSlopPastTolerance = ...;
if (event is PointerMoveEvent && (isPreAcceptSlopPastTolerance || isPostAcceptSlopPastTolerance)) {
resolve(Ges6 o i l ~ } q 4tureDisposition.rejected);z O A
stopTrackingPointer(primaryPointH G A e e + e ? er);
} else {
handlePrimaryPointer(event);
}
}
stopTrackingIfPointerNoLe ( u D m HongeI 9 v 6 K PrDown(event);
}
@protected
void handlePrimaryPointer(PointerEvent event);

当时事情是 PointerO o HMoveEvent 时,会做一个判别,假如必定时间内滑动的间隔超过 18px 那么就会进入第一个判h n I w J别中,该手势会被回绝掉,也便是从竞技场中会被移除,此刻会触发 onTapCancel

咱们首要关心手势有用时的办法 handlePrimaryPointer(event),能够看到这个办法是交由子类去完结的,完结它的子类自然便是 BasE I 2 N L ^ , ! heTapGestureRecognizer

BaseTapGestureRecogniz@ c 8 z qer

  @M 4 R ooverride
void handlePrimaryPointer(PointerEvent event) {
if (event is PointerUpEvent) {
_up = event;
_checkUp();
} else if (event is Pointex  Q 6 k + krCancelEvent) {( U K , q O V f C
resolve(GestureDisposition.rejected)$ 1 ] 8 F ! 1;
...
_reset();
} else if (event.buttons != _down.buttons) {
resolve(GestureDisposition.; 8 4 * x K ]reje N ! z 4 J v M Zcted);H l N b -
stopTracF ; 6 ( V V q ] ikingPointer(primaryPointer);
}
}

能够看到,在这儿只对 PointerUpEventPointerCanc$ S u # u f XelEvent 进行了处理,并没有处理 PointerDownEvent ,这儿很自然的就能够知道, PointerDownEvent 肯定被放在了 GestureBi8 6 3 R $ hndij L GnH c H q ( i F ;ghandleEvent(...) 的后边部分进行处理

不过咱们这儿仍是能够先看一下 PointerUpE) z mvent 是怎么b S X 0 处理的,进入 _checkUp() 办法

  @protected
void handleTapUp({ PointerDownEvent down, PointerUpEvent up });
...
void _checkUp() {
...
handleTapUp(down: _down, up: _{ . Y 1 z C oup);
_reset();
}

_checkUp() 中调用了 handleT} L z s =apUp(...) 办法,它是一个交由子类完结的办法,而 BaseTapGestureRecognizer 的子类便是 TapGestureRecogni) d = ? - a + E 5zer

TapGestureRecognizer

  @protected
T in( S T 0vokeCallback<T>(String name, Recognizer0 T A ~ I (Callback<T> callback, { String debugReport() }) {
...
result = callback();
...
retu3 | j l 2rn result;
}
@protectZ $ 4 w . q D ( 6ed
@override
void handleTaG o ) y VpUp({PointerDownEve x PeY m h D  !nt down, PointerUpEvent up}) {
final TapUpDetails details = TapUpDetails(...);
switch (down.buttons) {
case kPrimaryButton:
if (oN d y j % U ^ V 7nTapUp != null)
invokeCallback<void>('onTapUp', (e Z ? + ! m b | /) => onTapUp(details));4 B # q M f
if (onTap != null)
invokeCallback<void>('onTap'9 I , onTap);
break;
...
default:
}
}

能够看到,终究经过 invokeCallback(...) 办法履行了传入的办法,包括 onTapUponTap ,从这儿咱们就知道了,咱们最最常用的点击事情,便是在 TapGestureRE ~ WecognizerhandleTapUp(...) 中履行的。

TapGes; w ? 3 u CtureRecognizer 还有 handleTapDown(...) 用于履行 onTapDown ,它则是经过 BaseTapGestureRecogn! t y 5 i ~ Z K 6izer_checkDown() 调用F _ M

接下来咱们能够回到 GestureBinding ,看看 PointerDownEvent 到底是怎么处理的

  void handleEvent(PointerEve M 1 u n - knt event, HitTestEnt a : s # 0ry entry) {
pointerRouter.route(event);
if (evenO @ F t f R ^ 4 jt is PointerDowq $ & y ? 9 ~ ? 1nEvent) {
gestureArena.close(event.i h P H T L 9pointer);
}
...
}

GestureArenaManager -&gK ; } 1 d xt; close(…)

  void close(int pox h ( x . Rinter) {
final _GesturM  @ E xeArena state = _arenas[pointer];
...
stateX q K !.isOpen = false;
...
_tryToResolveArena(pointer, state);
}
void _v M F X GtryToResolveArena(int pointer, _GestureArena state) {
assert(_arenas[pointer] == state);
assert(!state.isOpen);
if (state.membersc H X p y s.length == 1) {
scheduleMicrotask(() =>G / $ ~ B . $ D X _resolveByDefault(pointen X e &r, state));
} else if (statw W 7 O ye.members.isEmpty) {
_ah t H G Q renas.remove(pointer);
assert(_debugLogDiagnostic(pointer, 'Arena empty.'));
} else if (state.eagerWinner != null) {
assert(_debugLogDiagnostic(pointer, 'Eager winneS C n I T 8r: ${state.eageX ? Q 1 } / crWinner}'));
_resol{  C I 6veInFavorOf(pointer, state, state.eagerWinner);
}
}

close(...) 调用了 _tryTX I n {oResoL p k Y d ?lveArena(...) 办法,在这个办法中,处理了三种情况

  • 假如竞技场成员只要一个
  • 假如竞技场没有任何成员
  • 假如存在竞技场的取胜者

假如成员只要一个,那么事情理所应当交给它处理;假如没有成员就不说了;假如存在胜利者,交给胜利者处理也是正常的。显然,close(...) 中并没有对竞技场的成员做一个竞争的处理,它% k H m B !只担任没有点击抵触的时分^ [ o,也便是只要一个点击目标。这种情况终究会经过调用它的 acceptGesture(...) 来触发 onTapDown

咱们持续看 close(...) 后边的办法

  @overri, 9 g p l b o Zde // from HitTestTarget
void handleEvent(PointerEvent event, HitTestEz v S 6 T q 0 h =ntry entry) {
pointerRouter.route(event);
if (event is PointerDownEvent) {
gestureArena.close(event.0 H V ;pointer);
} else if (event is PointerUpEvent) {
gestureAt y B drena.sweep(event.pointer);
}
...
}

Ge_ % g l 6stureArenaManager -> sweep(…)

  void sweep(int pointer) {
...
if (state.members.is - J  t c 9NotEmpty) {
...
state.members.first.acceptGesture(pointe( g ~ 5 O Vr);
...
for (int i = 1; i < state.members.length; i++)
state.members[i].rejectGesg d Z ) R ` stud ; + c 9 T G 2 ure5 a T f t A(pointer);
}
}

看看这个办法,多么简略粗暴,直接确认竞技场成员中的第一个为取胜者!依据前面咱们说过的 hintTest(...) 的增加次序,能够知道,RenderB* & = 5ox数最基层的目标是最早被增加到列表中的

所以这儿的第一个成员,便是最基层的目标,在屏幕的显现中,它便是最里层的元素,所以假如像下面这样,为两个色彩块设置点击事情的话,只要红色的会收效

从源码看flutter(五):GestureDetector篇

咱们能够简略的看一下 a8 S , / ` & * ecceptGesture(...) 做了些什么,这儿会进入 BaseTapGestureRecognizerN G { { BacceptGesture(...)

  ///BaseTapGestureRecognizer
@override
void acceptGesture(int pointerT D o P) {
super.acceptGesture(pointer);
if (pointer == primaryPointer) {
_checkDown();X 6 V . U O
_wonArenaForPrimaryPointer = true] X l v V;
_checkUp();
}
}

能够看到,这_ A Y j (儿的 _checkDown() 中就处理了 onTapDown 事情,后边跟着了一个 _checkUp() 用于对 onL p / mTapUponTap 的处理,& : Q o r & } *假如竞技场成员只要一个,这儿的 _checkUp() 不会收效

到这儿,关于点击事情的整个流程咱们都清楚了,能够分为下面两种情况

  • 竞技场只要一个O t 5 Z y N H成员时:在 Gesture) D y w ?ArenaManager. G c O ] M vclose(...) 中完O F !onTapDown 的调用,此刻事情为_ ` U Z PointDown= o } 3 c # d 5 ;Evenl | V T $ w 2 2t; 在 PointerRo0 s Mute; @ C irroute(...) 办法完结对 onTJ w p o Y sa) [ S M B 7pUponTap 的调用,此刻事情为 PointU4 * T l ? ) 6pEvent
  • 竞技场有多个成员时:在 Ges- d 9 + y B 0ture. % s ! YAren! ? l j [ ^ + % waManagersweep(...) 办法先完结对 onTapDown 的调用,后完结对 o# S 9 { ]nTapUponTap 的调用,此刻事情为! _ p F W PointUpEvent

接下来或许你会发生疑问了,假如上面两个色彩块监听的是相同的滑动事情,在竞技场中他B o ( – c yK Y T c ] 8 N 7又是怎么处a D = # o ( 0 l理的呢?

下面就来简略的看一下,以 PanGestureRecognizer9 v - O m } g 为例

PanGestureRecognizer

简略的看一下它的结构

PanGestureRecognizer -> DragGestureRecognizer -> OneSequenceGestureRecognizer -> GestureRecogn9 _ ( r *izer

基本上和拖拽相关的中心逻辑都在 DragGestH A R . : , j R pureRecognizer 中了

从之前咱们的流程就知g 3 E V e y c s道,会先走 a~ F 4 i } ! ^ddAllowedPointer(...) 办法,之后经过在 GestureBindinghandleEvent(...)[ p X ? j ) $ x履行 PointerRouterroute(.A w ! (..) 来走 GestureRecognizerhandleEvent(...) 办法

所以咱们先看 DragGestureRecognizeraddAllowedPointer(...)

  @override
void addAllowedPointer(PointerEvent event) {
startTrackingPol s D i W I 3 r 3iv f f 5 t p l D Wnter(event.pointer, event.transform);
_V E * velocityTrackers[event.pointer] = Vet 0 g B I ~locityTracker();
if (_state == _DragState.ready) {
_state = _DragStatel s a ?.possible;
...
_checkDown();
} else if (_state == _DragState.accepted) {
resolve(GestureDispositio @ p s ? E v 1n.accepted);( M R ^
}
}

仍是会先在 st: 7 I Y artTrackingPointer(...)h- p .andleEvent(...) 加入 PointerRouter ,然后把当时目标加入竞技场。

接着经过 _checkDown() 履行了 onDown 办法,关于 DragGestureRecognizer 它便是 onPanDown

以上是收到 onPointDownEvent 时的事情,由于是拖拽,接下来会收到 onPU j F V ] o bointMoveEvent 事情

再看它的 handleEvent(...)

  @override
void handleEvent(PointerEvent event) {
...
if (event is PointerMoveEvent) {
...
if (_state == _DraI $ y b sgState.aB `   T ^ & ] Vccepted) {
...
} else {
...
if (_hasSufficientGlobalDistanceToAccept)
resolve(GestureDisposition.accepted);
}
}
...
}

其间 _hasSufficientGl, : n ; [ KobalDistanceToAccept 是交由子类去完结的办法,用于判别滑动间隔是否有用,默许大于36个像素就有用;假如有用,就会进入 resolvex O - j(...) 办法,它终究会调用到 GestureArenaManager_resolveInFavorOf(...)

而这个办法,关于传入的 GestureArenaMember 目标,直接_ D g L W A Z .判定其v K ; t P l Y为胜出者,并将其他竞技场成员清除去。而这儿传入的+ 2 # G 0目标和咱们之前再点击事情中的相同,都是树结构中最基层的目标,也便是屏幕上最里层的元素3 Z d G 8 * ;

所以这儿的滑动事情在竞技N @ F b . 1 Z场中的处理便是这样了。而咱M 8 K们本篇的内容也即将结束

总结

r W h /势的分发流程大致如下:

  • 触发手势: flutter接受到由底层传来的接触事情告诉,它会触发 GestureBindia o i = Fng_handlePointerDataPacketE o _ %(...) 办法,flutter再这个办法中对传来的数据进行转换,变成flutter中适用的格局
  • HitTestTarget目标搜集: 经过 hitTest(...) 办法,将K L – u RenderObject树中符合条件的& T d . RenderBox 目标增加到 HitTestResult 中,增加次序是由底至上的,最上层的两个目标分别是 GestureBindingRenderView ,这些目标都完结了 HitTestTarget 接口,也便是说他们都具有 handlerEvent(...) 办法
  • 事情分发: 在 dispatchEve} 3 R e | @ Q .nt(...) 中,经过遍历之前增加的目标t F L &,调用他们的 handlerEvent(...) 办法来进行事情的分发
  • GestureRecognizer目标搜集: 咱们的 GestureDete# W 6 C s - Wctor 对应的 RenderPointerListener 会进行事情处理,在收到 PointDownEvent 事情时,会将所有 GestureDetector 中注册的 GestureRecognizer 目标的 handlerEvent 办法作为 PointerRoute 传入 PointerRouter ,并将该目标t I o – ^ 0 i = 放入 GestureArenaManager 保护的竞技场
  • 事情处理: 履行到最外层,也便是 Gesd l G . :tureBindinghandleE$ $ X Gvent 中,在这儿从竞技场选出终究处理手势的 GestureRecognizer 目标,然后进行手势处理

ps:以上分析都是根据z | s ^ I Y ]单点接触事情,尚未对多点接触事情进行分析

终究,阐明一下,单点接触事情的中心并不是P ` ~ ( { v 7所谓的手势竞技场,由于根本就没有一个真正的竞技过程。终究J ` i . T = 9 0都是直接挑选最基层的 GestureDetector 作为O ( : + m ^ r手势的处理者,单点接触事情的中心其实是这些L H 7 H E e竞技场成员被增加到竞技场中的次序——是由底至上的次序

评论

发表回复