从源码看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 W收 PointerDownEvent
目标,它会调用 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
,所以这儿传递给 _PointerListener
的m [ 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
,它对应的 RenderObject
是 RenderP9 + %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
便是如此
这儿关于 GestureDetector
的F $ 9全体结构有了一个开端的了解,而且无法再往下深入了,接下来咱们A + 7 3将从别的一个切入点来看手势事情。那便是手势分发的起点
手势分发流程
起点其实不难找,之前咱们就知道过了,runApp(...)
办法作为flutter的入口,会对 WidgetsFlutterBinding
进行初始化,WidgetsFlutterBinding
混入了多个 Binding
目标,其D i s w % a间就有专门处理手势的 GestureBinding
,咱们看一下就知道了
手势分发起点:[ ? D c GestureBinding
在 GestureBinding
的 initIN 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
供给,值得注意的是 GestureBinding
与 RendererBindd 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);
}
这儿调用了 RenderView
的 h@ ( 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 C的 child
是 RR 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
事情时,会调用所有 RenderBox
的 hitTest(...)
办法,将符合条件的目标放入到 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 Gntry
的 RenderView
,终究才是 GestureBM M N Binding
目标
至于为什么要把+ / l +这些目标搜集起来放入 HitTQ C &estResult
呢?后边会逐步阐明
当 HitTestResult
被创建后,会被存入GestureBindingz Y P X @ @ B
保护的 Map<int, HitTestResult> _hitTests
中,key 是 event.pointer
,Z 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 Y在 hitTest(...)
中增加的各个完结了 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 wner
的 handl, ` @ * 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()
或者其他点击事情,这儿就会调用 TapGestureRecognizer
的 addPointer(...)
办法。咱们就先以它为` q , Z ) P例,来看一下都做了些什么
TapGestureRecognizer
咱们先简略的看一下 TapGestureRecognizer
的承继结构
TapGestureRecognizer -&g2 1 ot; BaseTapGestureRecognizer –+ r * %>@ 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 ~只要 BaseTapGestureRecognizer
和 PrimaryPointerGestureRecognizer
完结了 addAlloW p 5 g |wedPoin~ 6 Xter(...)
办法
能够先来看一下 BaseTapGestureRecognizer
的 addAllowedPo| 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 7cognizer
的 addAllowedPointer(...)
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便是将需要竞技的手势成员,放入了手势竞技场内。
那么到这儿,RenderPointerListener
的 handleEvent(...)
就履行结束了,接下来会履行 RenderVi! m n 7 k S 6ew
的 handleEvent(...)
,不过由于它并没有重写这个办法,所以咱们会直接来到 GestureBinding
的 handleEvent(...)
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
便是咱们之前在 OneSequenceGestureRecognizer
的 st4 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(...)
这儿要注意不要把
OneSequenceGestureRecognizer
的handleEvent(...)
和RenderPointerListener
的handleEvent(...)
混杂了
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);
}
}
能够看到,在这儿只对 PointerUpEvent
与 PointerCanc$ S u # u f XelEvent
进行了处理,并没有处理 PointerDownEvent
,这儿很自然的就能够知道, PointerDownEvent
肯定被放在了 GestureBi8 6 3 R $ hndij L GnH c H q ( i F ;g
的 handleEvent(...)
的后边部分进行处理
不过咱们这儿仍是能够先看一下 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(...)
办法履行了传入的办法,包括 onTapUp
和 onTap
,从这儿咱们就知道了,咱们最最常用的点击事情,便是在 TapGestureRE ~ Wecognizer
的 handleTapUp(...)
中履行的。
而 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
数最基层的目标是最早被增加到列表中的
所以这儿的第一个成员,便是最基层的目标,在屏幕的显现中,它便是最里层的元素,所以假如像下面这样,为两个色彩块设置点击事情的话,只要红色的会收效
咱们能够简略的看一下 a8 S , / ` & * ecceptGesture(...)
做了些什么,这儿会进入 BaseTapGestureRecognizerN G { { B
的 acceptGesture(...)
///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 / mTapUp
和 onTap
的处理,& : Q o r & } *假如竞技场成员只要一个,这儿的 _checkUp()
不会收效
到这儿,关于点击事情的整个流程咱们都清楚了,能够分为下面两种情况
- 竞技场只要一个O t 5 Z y N H成员时:在
Gesture) D y w ?ArenaManager. G c O ] M v
的close(...)
中完O F !结onTapDown
的调用,此刻事情为_ ` U ZPointDown= o } 3 c # d 5 ;Evenl | V T $ w 2 2t
; 在PointerRo0 s Mute; @ C ir
的route(...)
办法完结对onTJ w p o Y sa) [ S M B 7pUp
和onTap
的调用,此刻事情为PointU4 * T l ? ) 6pEvent
- 竞技场有多个成员时:在
Ges- d 9 + y B 0ture. % s ! YAren! ? l j [ ^ + % waManager
的sweep(...)
办法先完结对onTapDown
的调用,后完结对o# S 9 { ]nTapUp
与onTap
的调用,此刻事情为! _ p F WPointUpEvent
接下来或许你会发生疑问了,假如上面两个色彩块监听的是相同的滑动事情,在竞技场中他B o ( – c y们K 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(...)
办法,之后经过在 GestureBinding
的 handleEvent(...)
中[ p X ? j ) $ x履行 PointerRouter
的 route(.A w ! (..)
来走 GestureRecognizer
的 handleEvent(...)
办法
所以咱们先看 DragGestureRecognizer
的 addAllowedPointer(...)
@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 – uRenderObject
树中符合条件的& T d .RenderBox
目标增加到HitTestResult
中,增加次序是由底至上的,最上层的两个目标分别是GestureBinding
与RenderView
,这些目标都完结了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 . :tureBinding
的handleE$ $ 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竞技场成员被增加到竞技场中的次序——是由底至上的次序
发表回复
要发表评论,您必须先登录。