Flutter中的事件处理

Flutter中的事件处理

无论是在Android中还是iOS中,都是有事件响应的。其主要是通过手指进行触摸,当手指接触到屏幕后,便开始进行事件响应了。

基本概念:指针事件

在Flutter的原始事件模型中,在手指接触屏幕发起接触事件时,flutter会首先确定手指与屏幕发生接触的位置上究竟有哪些组件。然后通过命中测试(Hit Test)交给最内层的组件去响应。换句话说,也就是先从渲染树的最底层的根的位置向上遍历,直到遍历到根结点位置。

flutter中的主要的事件响应分为三个部分组成 PointerDownEventPointerMoveEventPointerUpEvent 三个事件,分别是手指下落事件,手指移动事件,手指抬起事件。PointerEvent 是Flutter的原始指针事件的基础类。主要返回有

  • position:全局坐标的偏移量
  • delta:两次指针移动事件的距离
  • pressure:按压力度,如果手机不支持,始终返回1
  • orientation:指针移动方向,是一个角度值

对于原始指针事件的监听,Flutter提供了一个Listener类。这个类可以用它监听包裹的子组件的原始指针事件。

Listener(
	onPointerDown:(downPointEvent){},
	onPointerMove:(movePointEvent){},
	onPointerUp:(upPointEvent){},
  behavior:HitTestBehavior,
	child: Widget
)

behavior是决定子组件如何响应命中测试,值类型是HitTestBehavior,是一个枚举类型。主要的取值有

  • deferToChild:子组件一个接一个地命中测试,如果子组件中有命中测试的,那么当前组件会收到指针事件,并且父组件也会收到指针事件。
  • opaque:在进行命中测试时,当前组件会被当成不透明进行处理,单击的响应区域即为单击区域。
  • translucent:组件自身和底部可视区域都能够响应命中测试,当点击顶部组件时,顶部组件和底部组件都可以接收到指针事件。

忽略事件

两个组件 AbsorbPointer , IgnorePointer

编号 组件名称 组件说明
1 AbsorbPointer 其包裹的组件不能够响应事件,但是其本身能够响应指针事件
2 IgnorePointer 包裹的组件以及其本身都不能够响应指针事件

手势识别:GestureDetector

如果想从组件层监听手势,可以使用GestureDetector等手势响应组件。该组件可以监听各种触摸的行为,常用的事件如下:

编号 事件名称 描述
1 onTapDown 接触屏幕时触发
2 onTapUp 离开屏幕时触发
3 onTap 点击屏幕时触发
4 onTapCancel 触发onTapDown事件但不会触发onTap事件
5 onDoubleTap 用户连续两次在同一位置快读点击屏幕
6 onLongPress 在相同位置与屏幕保持长时间接触
7 onVerticalDragStart 与屏幕接触并可能开始垂直移动
8 onVerticalDragUpdate 与屏幕接触并沿垂直方向移动
9 onVerticalDragEnd 之前与屏幕接触并垂直移动的指针不再与屏幕接触
10 onHorizontalDragStart 与屏幕接触并可能开始水平移动
11 onHorizontalDragUpdate 与屏幕接触并已沿水平方向移动
12 onHorizontalDragEnd 之前与屏幕接触并水平移动的指针不再与屏幕接触

如果同时监测onTap和onDoubleTap,那么在onTap后有200ms的延迟。

GestureDetector之所以能够识别各种手势,是因为其内部使用了一个或者多个GestureRecognizer手势识别器。在使用手势识别器后,需要调用 dispose() 进行资源的释放,否则会造成大量的资源消耗。

手势竞争与冲突

在flutter中,引入了手势竞技场的概念,用来识别究竟是哪个手势最终响应用户事件。该 手势竞技场 通过综合对比用户触摸屏幕的时长、位移、拖拽方向来确定最终的手势。换句话说,如果在屏幕上拖拽一个小球,那么该小球就会通过手势竞技场进行判断移动的方向。一般只有单一方向,垂直移动或者水平移动。

事件总线

事件总线是广播机制的一种实现方式(广播为跨页面事件通信提供了有效的解决方案)。订阅者模式中包含两种角色:发布者和订阅者。在Flutter中:

  • 发布者主要负责在状态改变时通知所有的订阅者
  • 观察者则负责订阅事件并对接收到的事件进行处理。

使用事件总线可以实现组件之间状态的共享,但是对于复杂场景来说,可以使用专门的管理框架例如:redux、ScopeModel或者Provider

事件通知

如果在一个比较复杂的页面进行数据传递,那么使用到 事件通知 这个机制,在子节点跨层级传递消息机制。在Flutter的组件树中,每一个节点都可以发送通知,通知会沿着当前节点向上传递,父节点则使用NotificationListener监听子节点传递的消息,这种机制称为 通知冒泡

通知冒泡用户触摸事件冒泡 不太一样,通知冒泡可以中止,用户触摸事件冒泡不可以中止。

自定义通知

如果需要实现自定义通知,需要实现一个类并且继承 Notification 。在Notification类中,有一个dispatch() 可以用这个类进行分发事件通知。

冒泡通知的原理

因为通知 Notification 是通过dispatch进行触发的,因此查看dispatch的源码,如下:

void dispatch(BuildContext? target) {
    // The `target` may be null if the subtree the notification is supposed to be
    // dispatched in is in the process of being disposed.
    target?.visitAncestorElements(visitAncestor);
}

可以知道,其是调用了visitAncestorElements方法,从当前元素开始向上遍历父元素。当遍历到根元素或者遍历回调返回false时,遍历过程中止。该冒泡通知的原理是一套自底向上的消息传递机制。

posted @ 2021-11-04 11:17  野生的Lemon柠檬  阅读(844)  评论(1编辑  收藏  举报

呱呱呱呱呱🐸