Flutter事件之GestureBinding

Flutter在启动时(runApp)会进行一些浇水类的"粘合",WidgetsFlutterBinding作为主类,需要粘合一系列的Binding,其中GestureBinding就是事件处理类;

GestureBinding是Flutter中管理手势事件的Binding,是Flutter Framework层处理事件的最起点;

GestureBinding实现了HitTestable, HitTestDispatcher, HitTestTarget,分别具有以下功能

  • hitTest命中测试
  • dispatchEvent事件分发
  • handleEvent处理事件()

成员变量:

//触点路由,由手势识别器注册,会把手势识别器的pointer和handleEvent存入
//以便在GestureBinding.handleEvent调用
final PointerRouter pointerRouter = PointerRouter();

//手势竞技场管理者,管理竞技场们的相关操作
final GestureArenaManager gestureArena = GestureArenaManager();

//hitTest列表,里面存储了被命中测试成员
final Map<int, HitTestResult> _hitTests = <int, HitTestResult>{};
复制代码

GestureBinding在_handlePointerDataPacket方法接收有Engine层传递过来的触点数据,经过数据包装转换为Framework层可处理的数据:PointerAddedEvent、PointerCancelEvent、PointerDownEvent、PointerMoveEvent、PointerUpEvent等等,随后在_handlePointerEventImmediately方法中进行命中测试和事件分发;

手指按下

当手指按下时,接收到的事件类型是PointerDownEvent

首先是命中测试

当事件类型是event is PointerDownEvent || event is PointerSignalEvent || event is PointerHoverEvent会进行新的命中测试,命中测试相关请看,得到命中测试列表后,开始调用dispatchEvent进行事件分发。

void _handlePointerEventImmediately(PointerEvent event) {
  HitTestResult? hitTestResult;
  if (event is PointerDownEvent || event is PointerSignalEvent || event is PointerHoverEvent) {
    assert(!_hitTests.containsKey(event.pointer));
    hitTestResult = HitTestResult();
    hitTest(hitTestResult, event.position);
    if (event is PointerDownEvent) {
      _hitTests[event.pointer] = hitTestResult;
    }
    assert(() {
      if (debugPrintHitTestResults)
        debugPrint('$event: $hitTestResult');
      return true;
    }());
  } else if (event is PointerUpEvent || event is PointerCancelEvent) {
    hitTestResult = _hitTests.remove(event.pointer);
  } else if (event.down) {
    //当前事件是按下状态,重用hitTest结果
    hitTestResult = _hitTests[event.pointer];
  }
  if (hitTestResult != null ||
      event is PointerAddedEvent ||
      event is PointerRemovedEvent) {
    assert(event.position != null);
    dispatchEvent(event, hitTestResult);
  }
}
复制代码

事件分发

事件分发的目的是调用命中对象的handleEvent方法以处理相关逻辑,比如我们熟知的Listener组件,它做的事就是回调相关方法,比如按下时Listener会回调onPointerDown

## GestureBinding ##
@override // from HitTestDispatcher
void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) {
  assert(!locked);
  if (hitTestResult == null) {
    assert(event is PointerAddedEvent || event is PointerRemovedEvent);
    try {
      pointerRouter.route(event);
    } catch (exception, stack) {
      ...
    }
    return;
  }
  for (final HitTestEntry entry in hitTestResult.path) {
    try {
      entry.target.handleEvent(event.transformed(entry.transform), entry);
    } catch (exception, stack) {
      ...
    }
  }
}
复制代码
## Listener ##
@override
void handleEvent(PointerEvent event, HitTestEntry entry) {
  assert(debugHandleEvent(event, 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);
}
复制代码

我们知道命中测试最后会把GestrueBinding本身加入到列表中,所以最后也会执行GestrueBinding的handleEvent方法

handleEvent

GestrueBinding.handleEvent是处理手势识别器相关的逻辑,pointerRouter.route(event)调用了识别器的handleEvent方法(需要提前进行触点注册),随后的是竞技场的相关处理;可以看这里了解手势识别器;

## GestrueBinding ##
@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);
  }
}
复制代码

手指抬起,

手指抬起会重用之前hitTest结果,并不会重新hitTest,如果是Listener组件,则会回调PointerUpEvent

扫描二维码关注公众号,回复: 13804708 查看本文章

猜你喜欢

转载自juejin.im/post/7087873740658180133