上一篇我们写到了《FlutterFlutter原理篇:事件机制传播与响应机制与HitTestBehavior的介绍》忘记了的同学可以回过头看一看点击事件是怎么回事的,今天我们来讲讲Flutter手势GestureDetector到底是怎么回事,以及来分析一下他背后的运行原理
在我们讨论他的运行原理之前,我们先来看看他是怎么使用的
import 'package:flutter/material.dart';
class TestGestureDetector extends StatelessWidget{
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapUp: (x)=>print("1"),
child: Container(
width:200,
height: 200,
color: Colors.red,
),
);
}
}
看起来还是蛮简单的嘛,好像与Listener使用差不多啊,确实是差不多,而且他的原理里面就用到了Listener,要不我们去看看他是怎么一回事
其实他继承于StatelessWidget,那么我们直接看他的build方法看看:
@override
Widget build(BuildContext context) {
final Map gestures = {};
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(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,
);
}
大家要知道一点的就是一个GestureDetector可能对应好多个手势识别器Recognizer,因为我们的代码里面只传递了onTapUp事件所以我们就只看TapGestureRecognizer即可,首先第一行初始化了Map
GestureRecognizerFactory
@optionalTypeArgs
abstract class GestureRecognizerFactory {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const GestureRecognizerFactory();
/// Must return an instance of T.
T constructor();
/// Must configure the given instance (which will have been created by
/// `constructor`).
///
/// This normally means setting the callbacks.
void initializer(T instance);
bool _debugAssertTypeMatches(Type type) {
assert(type == T, 'GestureRecognizerFactory of type $T was used where type $type was specified.');
return true;
}
}
class GestureRecognizerFactoryWithHandlers extends GestureRecognizerFactory {
/// Creates a gesture recognizer factory with the given callbacks.
///
/// The arguments must not be null.
const GestureRecognizerFactoryWithHandlers(this._constructor, this._initializer)
: assert(_constructor != null),
assert(_initializer != null);
final GestureRecognizerFactoryConstructor _constructor;
final GestureRecognizerFactoryInitializer _initializer;
@override
T constructor() => _constructor();
@override
void initializer(T instance) => _initializer(instance);
}
_constructor与_initializer对应的类型如下:
/// Signature for closures that implement [GestureRecognizerFactory.constructor].
typedef GestureRecognizerFactoryConstructor = T Function();
/// Signature for closures that implement [GestureRecognizerFactory.initializer].,
typedef GestureRecognizerFactoryInitializer = void Function(T instance);
代码很简单不赘述了,我们需要知道的gestures[TapGestureRecognizer]存放的是TapGestureRecognizer的构造函数与初始化函数即可,其中我们写的onTapUp函数就在里面,我们再来看看函数返回的是一个RawGestureDetector,我们去看看他,其实他是一个StatefullWidget,他在初始化State的时候会调用上面提到的构造器以及初始化函数去生成Map
RawGestureDetectorState.initState
@override
void initState() {
super.initState();
_semantics = widget.semantics ?? _DefaultSemanticsGestureDelegate(this);
_syncAll(widget.gestures);
}
RawGestureDetectorState._syncAll
void _syncAll(Map gestures) {
assert(_recognizers != null);
final Map oldRecognizers = _recognizers!;
_recognizers = {};
for (final Type type in gestures.keys) {
assert(gestures[type] != null);
assert(gestures[type]!._debugAssertTypeMatches(type));
assert(!_recognizers!.containsKey(type));
_recognizers![type] = oldRecognizers[type] ?? gestures[type]!.constructor();
assert(_recognizers![type].runtimeType == type, 'GestureRecognizerFactory of type $type created a GestureRecognizer of type ${_recognizers![type].runtimeType}. The GestureRecognizerFactory must be specialized with the type of the class that it returns from its constructor method.');
gestures[type]!.initializer(_recognizers![type]!);
}
for (final Type type in oldRecognizers.keys) {
if (!_recognizers!.containsKey(type))
oldRecognizers[type]!.dispose();
}
}
主要是:
_recognizers![type] = oldRecognizers[type] ?? gestures[type]!.constructor();
gestures[type]!.initializer(_recognizers![type]!);
这两行构造和初始化对象
我们接着看他对应的build函数:
RawGestureDetectorState.build
@override
Widget build(BuildContext context) {
Widget result = Listener(
onPointerDown: _handlePointerDown,
behavior: widget.behavior ?? _defaultBehavior,
child: widget.child,
);
if (!widget.excludeFromSemantics) {
result = _GestureSemantics(
behavior: widget.behavior ?? _defaultBehavior,
assignSemantics: _updateSemanticsForRenderObject,
child: result,
);
}
return result;
}
OK,这里直接是使用到了Listener来构造的Widget,并且指定了onPointerDown函数,由于上一篇我们说到了Listener的运行原理,在被命中的情况下添加进队列,再在事件分发的时候执行handleEvent方法然后继而会执行我们添加的onPointerDown方法,我们再来看看这个方法:
void _handlePointerDown(PointerDownEvent event) {
assert(_recognizers != null);
for (final GestureRecognizer recognizer in _recognizers!.values)
recognizer.addPointer(event);
}
这里貌似是把他加进了recognizer这个里面,我们看看这个addPointer方法
GestureRecognizer.addPointer
void addPointer(PointerDownEvent event) {
_pointerToKind[event.pointer] = event.kind;
if (isPointerAllowed(event)) {
addAllowedPointer(event);
} else {
handleNonAllowedPointer(event);
}
}
BaseTapGestureRecognizer.addAllowedPointer
@override
void addAllowedPointer(PointerDownEvent event) {
assert(event != null);
if (state == GestureRecognizerState.ready) {
// If there is no result in the previous gesture arena,
// we ignore them and prepare to accept a new pointer.
if (_down != null && _up != null) {
assert(_down!.pointer == _up!.pointer);
_reset();
}
assert(_down == null && _up == null);
// `_down` must be assigned in this method instead of `handlePrimaryPointer`,
// because `acceptGesture` might be called before `handlePrimaryPointer`,
// which relies on `_down` to call `handleTapDown`.
_down = event;
}
if (_down != null) {
// This happens when this tap gesture has been rejected while the pointer
// is down (i.e. due to movement), when another allowed pointer is added,
// in which case all pointers are simply ignored. The `_down` being null
// means that _reset() has been called, since it is always set at the
// first allowed down event and will not be cleared except for reset(),
super.addAllowedPointer(event);
}
}
_reset 做了一些重置的操作
PrimaryPointerGestureRecognizer.addAllowedPointer
@override
void addAllowedPointer(PointerDownEvent event) {
super.addAllowedPointer(event);
if (state == GestureRecognizerState.ready) {
_state = GestureRecognizerState.possible;
_primaryPointer = event.pointer;
_initialPosition = OffsetPair(local: event.localPosition, global: event.position);
if (deadline != null)
_timer = Timer(deadline!, () => didExceedDeadlineWithEvent(event));
}
}
第一句是调用了父类的方法,下面是记录了一些offset作为后面点击判断使用的
OneSequenceGestureRecognizer.addAllowedPointer
@override
@protected
void addAllowedPointer(PointerDownEvent event) {
startTrackingPointer(event.pointer, event.transform);
}
TapGestureRecognizer间接继承于OneSequenceGestureRecognizer
OneSequenceGestureRecognizer.startTrackingPointer
@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);
}
这里最重要的是第一句
PointerRouter.addRoute
void addRoute(int pointer, PointerRoute route, [Matrix4? transform]) {
final Map routes = _routeMap.putIfAbsent(
pointer,
() => {},
);
assert(!routes.containsKey(route));
routes[route] = transform;
}
这里面生成了一个Map对象routes他的key为pointer(也就是pointer指针),他的value为一个类型为
PrimaryPointerGestureRecognizer.handleEvent
@override
void handleEvent(PointerEvent event) {
assert(state != GestureRecognizerState.ready);
if (state == GestureRecognizerState.possible && event.pointer == primaryPointer) {
final bool isPreAcceptSlopPastTolerance =
!_gestureAccepted &&
preAcceptSlopTolerance != null &&
_getGlobalDistance(event) > preAcceptSlopTolerance!;
final bool isPostAcceptSlopPastTolerance =
_gestureAccepted &&
postAcceptSlopTolerance != null &&
_getGlobalDistance(event) > postAcceptSlopTolerance!;
if (event is PointerMoveEvent && (isPreAcceptSlopPastTolerance || isPostAcceptSlopPastTolerance)) {
resolve(GestureDisposition.rejected);
stopTrackingPointer(primaryPointer!);
} else {
handlePrimaryPointer(event);
}
}
stopTrackingIfPointerNoLongerDown(event);
}
OK初始化完了,这里经过了很多步骤只是routes[route] = transform把他存起来了而已,那么怎么去调用呢,这个就要说到了事件传递的时候,大家还记得我们在事件测试命中的时候那个函数吗?
RendererBinding.hitTest
@override
void hitTest(HitTestResult result, Offset position) {
assert(renderView != null);
assert(result != null);
assert(position != null);
renderView.hitTest(result, position: position);
super.hitTest(result, position); //RendererBinding继承于GestureBinding
}
RendererBinding继承于GestureBinding,最后一句调用的是GestureBinding.hitTest
GestureBinding.hitTest
@override // from HitTestable
void hitTest(HitTestResult result, Offset position) {
result.add(HitTestEntry(this));
}
这里把GestureBinding对象加入到了命中测试队列,等待事件传递的时候就会执行他的handleEvent方法:
GestureBinding.handleEvent
@override // from HitTestTarget
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(event);,这里面我们留一个彩蛋,因为这里事件类型为PointerDownEvent的时候不会直接进行我们的函数回调,我们要等到PointerDownUp的时候才会正确执行,但是我们先往下看,因为我们先忽略一些细节只看回调的处理
void route(PointerEvent event) {
final Map? routes = _routeMap[event.pointer];
final Map copiedGlobalRoutes = Map.from(_globalRoutes);
if (routes != null) {
_dispatchEventToRoutes(
event,
routes,
Map.from(routes),
);
}
_dispatchEventToRoutes(event, _globalRoutes, copiedGlobalRoutes);
}
第一句话就是把_routeMap的value拿出来,key为pointer,
/// Unique identifier for the pointer, not reused. Changes for each new
/// pointer down event.
final int pointer;
注意这个pointer是唯一的,不可重用的,但是每次pointer down的时候会改变,也就是说一次事件的down,move,up其实这个是一样的,所以我在down注册的key可以在up的时候取出来
最后面回到这里来:
PointerRouter._dispatch
@pragma('vm:notify-debugger-on-exception')
void _dispatch(PointerEvent event, PointerRoute route, Matrix4? transform) {
try {
event = event.transformed(transform);
route(event);
} catch (exception, stack) {
InformationCollector? collector;
assert(() {
collector = () sync* {
yield DiagnosticsProperty('router', this, level: DiagnosticLevel.debug);
yield DiagnosticsProperty('route', route, level: DiagnosticLevel.debug);
yield DiagnosticsProperty('event', event, level: DiagnosticLevel.debug);
};
return true;
}());
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'gesture library',
context: ErrorDescription('while routing a pointer event'),
informationCollector: collector,
));
}
}
route(event);这一句话就是直接调用了上面那个函数,我们再看一遍:
PrimaryPointerGestureRecognizer.handleEvent
@override
void handleEvent(PointerEvent event) {
assert(state != GestureRecognizerState.ready);
if (state == GestureRecognizerState.possible && event.pointer == primaryPointer) {
final bool isPreAcceptSlopPastTolerance =
!_gestureAccepted &&
preAcceptSlopTolerance != null &&
_getGlobalDistance(event) > preAcceptSlopTolerance!;
final bool isPostAcceptSlopPastTolerance =
_gestureAccepted &&
postAcceptSlopTolerance != null &&
_getGlobalDistance(event) > postAcceptSlopTolerance!;
if (event is PointerMoveEvent && (isPreAcceptSlopPastTolerance || isPostAcceptSlopPastTolerance)) {
resolve(GestureDisposition.rejected);
stopTrackingPointer(primaryPointer!);
} else {
handlePrimaryPointer(event);
}
}
stopTrackingIfPointerNoLongerDown(event);
}
这里最主要的就是handlePrimaryPointer:
BaseTapGestureRecognizer.handlePrimaryPointer
@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!);
}
}
假设我们的是PointerUpEvent事件,这里直接调用_checkUp函数:
void _checkUp() {
if (!_wonArenaForPrimaryPointer || _up == null) {
return;
}
assert(_up!.pointer == _down!.pointer);
handleTapUp(down: _down!, up: _up!);
_reset();
}
因为这个函数里面_wonArenaForPrimaryPointer这个变量是为false,_up变量为null,但是在上面的GestureBinding.handleEvent(Event为PointerDownEvent)的gestureArena.close(event.pointer);中会把_wonArenaForPrimaryPointer变量设置为true,我们简单的看一下:
GestureArenaManager.close
/// Prevents new members from entering the arena.
///
/// Called after the framework has finished dispatching the pointer down event.
void close(int pointer) {
final _GestureArena? state = _arenas[pointer];
if (state == null)
return; // This arena either never existed or has been resolved.
state.isOpen = false;
assert(_debugLogDiagnostic(pointer, 'Closing', state));
_tryToResolveArena(pointer, state);
}
void _tryToResolveArena(int pointer, _GestureArena state) {
assert(_arenas[pointer] == state);
assert(!state.isOpen);
if (state.members.length == 1) {
scheduleMicrotask(() => _resolveByDefault(pointer, state));
} else if (state.members.isEmpty) {
_arenas.remove(pointer);
assert(_debugLogDiagnostic(pointer, 'Arena empty.'));
} else if (state.eagerWinner != null) {
assert(_debugLogDiagnostic(pointer, 'Eager winner: ${state.eagerWinner}'));
_resolveInFavorOf(pointer, state, state.eagerWinner!);
}
}
这里面使用了这个微队列任务在做这个置位操作:scheduleMicrotask(() => _resolveByDefault(pointer, state)); 老实说至于为什么使用微队列来做这个操作我一下还没想通,直接调用不行?
void _resolveInFavorOf(int pointer, _GestureArena state, GestureArenaMember member) {
assert(state == _arenas[pointer]);
assert(state != null);
assert(state.eagerWinner == null || state.eagerWinner == member);
assert(!state.isOpen);
_arenas.remove(pointer);
for (final GestureArenaMember rejectedMember in state.members) {
if (rejectedMember != member)
rejectedMember.rejectGesture(pointer);
}
member.acceptGesture(pointer);
}
BaseTapGestureRecognizer.acceptGesture
@override
void acceptGesture(int pointer) {
super.acceptGesture(pointer);
if (pointer == primaryPointer) {
_checkDown();
_wonArenaForPrimaryPointer = true; //注意这一句
_checkUp(); //这里_checkUp里面的_up为null不会直接往下执行
}
}
但是到Event为PointerUpEvent的时候再回到handlePrimaryPointer这个函数的时候_wonArenaForPrimaryPointer已经为true,_up变量也会有值,最后还是会回到这个_checkUp函数里面调用handleTapUp:
TapGestureRecognizer.handleTapUp
@override
void handleTapUp({ required PointerDownEvent down, required PointerUpEvent up}) {
final TapUpDetails details = TapUpDetails(
kind: up.kind,
globalPosition: up.position,
localPosition: up.localPosition,
);
switch (down.buttons) {
case kPrimaryButton:
if (onTapUp != null)
invokeCallback('onTapUp', () => onTapUp!(details));
if (onTap != null)
invokeCallback('onTap', onTap!);
break;
case kSecondaryButton:
if (onSecondaryTapUp != null)
invokeCallback('onSecondaryTapUp', () => onSecondaryTapUp!(details));
if (onSecondaryTap != null)
invokeCallback('onSecondaryTap', () => onSecondaryTap!());
break;
case kTertiaryButton:
if (onTertiaryTapUp != null)
invokeCallback('onTertiaryTapUp', () => onTertiaryTapUp!(details));
break;
default:
}
}
这里主要调用的是invokeCallback
也就是运行的是TapGestureRecognizer的onTapUp方法也就是运行的是我们再DEMO中添加的回调方法
好了,讲到这里你已经对于手势的基本运行原理已经很清楚了,其实他就是基于Listener作为监听,利用了事件传播机制作为回调而已(因为默认他就是可以命中的),理解了源码以后就是这么简单
下面我们再来说第二个话题:
手势的冲突以及它背后的原理什么什么,让我们先来看看一个例子:
import 'package:flutter/material.dart';
class TestGestureDetector2 extends StatelessWidget{
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapUp: (x)=>print("2"),
child: Container(
width:200,
height: 200,
color: Colors.red,
alignment: Alignment.center,
child: GestureDetector(
onTapUp: (x)=>print("1"),
child: Container(
width: 100,
height: 100,
color: Colors.grey,
),
),
),
);
}
}
上面的例子是两个GestureDetector手势,嵌套组成父子组件,这个时候我们点击子组件的时候你会发现打印log里面只会打印1,父组件的手势好像没有起作用,这个就是我们今天要说的手势之间的冲突,为什么会有这个冲突呢,让我再回转头来看看上面事件传递机制的时候还有一个细节我们忽略了:
@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);
}
第一行在命中的时候把handleEvent加入到了routes里面这个我们上面已经分析过了,我们接着看下面一句,_entries[pointer] = _addPointerToArena(pointer);
OneSequenceGestureRecognizer._addPointerToArena
GestureArenaEntry _addPointerToArena(int pointer) {
if (_team != null)
return _team!.add(pointer, this);
return GestureBinding.instance!.gestureArena.add(pointer, this);
}
继续看最后一行:
GestureArenaManager.add
/// Adds a new member (e.g., gesture recognizer) to the arena.
GestureArenaEntry add(int pointer, GestureArenaMember member) {
final _GestureArena state = _arenas.putIfAbsent(pointer, () {
assert(_debugLogDiagnostic(pointer, '★ Opening new gesture arena.'));
return _GestureArena();
});
state.add(member);
assert(_debugLogDiagnostic(pointer, 'Adding: $member'));
return GestureArenaEntry._(this, pointer, member);
}
_GestureArena.add
void add(GestureArenaMember member) {
assert(isOpen);
members.add(member);
}
貌似多了几个我们没见过的对象,我来给大家解释一下:
GestureArenaManager :这个是整个手势冲突的管理类,他里面有一个final Map
_arenas Map字段专门存放的是key为pointer(上面我们解释过了pointer,也就是每次一个完整事件的唯一标识),value为_GestureArena的对象_GestureArena:这里面是专门存放了一个字段 final List
members = ,管理所有的GestureArenaMember[]; GestureArenaMember:有两个抽象方法(这两个方法关系到了到底是接纳这个手势,还是拒绝这个手势),很多类直接或者间接继承了他,比如我上面DEMO的TapGestureRecognizer:
abstract class GestureRecognizer extends GestureArenaMember with DiagnosticableTreeMixin {}
abstract class OneSequenceGestureRecognizer extends GestureRecognizer {}
abstract class PrimaryPointerGestureRecognizer extends OneSequenceGestureRecognizer {}
abstract class BaseTapGestureRecognizer extends PrimaryPointerGestureRecognizer {}
class TapGestureRecognizer extends BaseTapGestureRecognizer {}
abstract class GestureArenaMember {
/// Called when this member wins the arena for the given pointer id.
void acceptGesture(int pointer);
/// Called when this member loses the arena for the given pointer id.
void rejectGesture(int pointer);
}
说道这里我们明白了就是把我们手势识别器对象TapGestureRecognizer存了起来,简单来说就是TapGestureRecognizer存在_GestureArena里面,_GestureArena存在_arenas里面
大家再回过头来看看我们上面提到的彩蛋,我们再来看看里面的细节,首先是这里:
GestureBinding.handleEvent
@override // from HitTestTarget
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(event);事件一开始是PointerDownEvent的时候并不会直接进行调用,因为上面提到的_wonArenaForPrimaryPointer变量为false,但是也不会在 gestureArena.close(event.pointer); 这个里面进行置位,因为这个时候我们有两个长度的members单位(也就是两个TapGestureRecognizer),不满足判断 state.members.length == 1 为false
void _tryToResolveArena(int pointer, _GestureArena state) {
assert(_arenas[pointer] == state);
assert(!state.isOpen);
if (state.members.length == 1) { //这一句为false
scheduleMicrotask(() => _resolveByDefault(pointer, state));
} else if (state.members.isEmpty) {
_arenas.remove(pointer);
assert(_debugLogDiagnostic(pointer, 'Arena empty.'));
} else if (state.eagerWinner != null) {
assert(_debugLogDiagnostic(pointer, 'Eager winner: ${state.eagerWinner}'));
_resolveInFavorOf(pointer, state, state.eagerWinner!);
}
}
我们往下看:gestureArena.sweep(event.pointer); 这一句
void sweep(int pointer) {
final _GestureArena? state = _arenas[pointer];
if (state == null)
return; // This arena either never existed or has been resolved.
assert(!state.isOpen);
if (state.isHeld) {
state.hasPendingSweep = true;
assert(_debugLogDiagnostic(pointer, 'Delaying sweep', state));
return; // This arena is being held for a long-lived member.
}
assert(_debugLogDiagnostic(pointer, 'Sweeping', state));
_arenas.remove(pointer); //把pointer里面所有的识别器全部移除
if (state.members.isNotEmpty) {
// First member wins.
assert(_debugLogDiagnostic(pointer, 'Winner: ${state.members.first}'));
state.members.first.acceptGesture(pointer);
// Give all the other members the bad news.
for (int i = 1; i < state.members.length; i++)
state.members[i].rejectGesture(pointer);
}
}
第一句话先把pointer对应的_GestureArena拿出来,然后 _arenas.remove(pointer); 把pointer里面所有的识别器全部移除,然后 state.members.first.acceptGesture(pointer); 这一句取出第一个识别器调用它的acceptGesture方法,再把调用rejectGesture方法里面的拒绝其他识别器的响应,也就是竞争失败了
@override
void rejectGesture(int pointer) {
super.rejectGesture(pointer);
if (pointer == primaryPointer) {
// Another gesture won the arena.
assert(state != GestureRecognizerState.possible);
if (_sentTapDown)
_checkCancel(null, 'forced');
_reset();
}
}
那么问题就来了,谁才是第一个members里面的TapGestureRecognizer对象呢,这个就要提到了我们原原来说过的事件命中机制的原理,child优先添加进入命中队列,优先得到事件分发,这么的话我们的子节点的识别器TapGestureRecognizer会优先调用_handlePointerDown也就是优先调用_addPointerToArena方法加入到了members里面,所以我们最后看看子节点调用的acceptGesture函数:
@override
void acceptGesture(int pointer) {
super.acceptGesture(pointer);
if (pointer == primaryPointer) {
_checkDown();
_wonArenaForPrimaryPointer = true;
_checkUp();
}
}
_wonArenaForPrimaryPointer = true; 先置位,然后调用_checkUp函数去执行handleTapUp继而执行我们TapGestureRecognizer里面的onTapUp函数,所以打印就只会输出子组件打印1
好了,我们总算明白了如果是父子控件都注册了GestureDetector的话,那么只有子组件的会得到响应,原因就是上面提到的这一套竞争机制,不过老实说我总觉得这套机制还可以再完善一下,总感觉不够那么完美,这里我们介绍了一下两个onTapUp事件的冲突,其实多个识别器拖动之间也会有冲突,这里就不详解了,相信大家根据这套机制应该可以自己去弄明白
最后说一说如果真遇到了父子控件都需要注册up回调的时候怎么去解决呢,就是要打破这套竞争机制,最简单的办法就是利用Listener替代父GestureDetector,因为直接使用Listener一开始就不会有这套竞争的规则了
import 'package:flutter/material.dart';
class TestGestureDetector3 extends StatelessWidget{
@override
Widget build(BuildContext context) {
return Scaffold(
body: Listener(
onPointerUp: (x)=>print("2") ,
child: Container(
width:200,
height: 200,
color: Colors.red,
alignment: Alignment.center,
child: GestureDetector( //GestureDetector1
onTapUp: (x)=>print("1"),
child: Container(
width: 50,
height: 50,
color: Colors.grey,
),
),
),
),
);
}
}
好了今天就到这里了,我们讲解手势运行原理以及冲突的原理已经到了尾声了,如果有喜欢的小伙伴欢迎给我点赞加留言,你的点赞加关注是我写作持续的动力,谢谢···