最近flutter稳定版本由1.5升级到了1.7,GestureDetector手势检测类也得到了完善,新增了很多事件回调方法。接下来主要介绍下GestureDetector基本使用、注意事项、1.5到1.7升级引起的问题和滑动冲突。
GestureDetector简介
-
GestureDetector接受child参数,检测child上手势操作的回调 -
down和cancelGestureRecognizer之间相互竞争,down事件可能会有多个手势生出并接收回调,当某一个事件流程正式胜出,其他GestureRecognizer会调用其cancel方法// down onTapDown; onVerticalDragDown; onHorizontalDragDown; onPanDown; // cancel onTapCancel; onVerticalDragCancel; onHorizontalDragCancel; onPanCancel; -
tap单击事件回调顺序
onTapDown -> onTapUp -> onTap-
onTap: 单击回调 -
onTapUp: 单击手势抬起回调
-
-
longPress长按事件回调顺序
onLongPressStart -> onLongPress -> onLongPressMoveUpdate -> onLongPressEnd -> onLongPressUp-
onLongPressStart:长按开始 -
onLongPress:长按回调 -
onLongPressMoveUpdate:长按移动 -
onLongPressEnd:长按事件结束 -
onLongPressUp:长按手势抬起
-
-
onDoubleTap双击回调 -
drag水平/垂直拖拽事件回调顺序
onXXDragStart -> onXXDragUpdate -> onXXDragEnd-
onXXDragStart:拖拽开始 -
onXXDragUpdate:拖拽过程移动 -
onXXDragEnd:拖拽结束
-
-
scale缩放事件回调顺序
onScaleStart -> onScaleUpdate -> onScaleEnd-
onScaleStart:缩放开始 -
onScaleUpdate:缩放更新 -
onScaleEnd:缩放结束
-
-
pan拖拽事件回调顺序
onPanStart -> onPanUpdate -> onPanEnd-
onPanStart:拖拽开始 -
onPanUpdate:拖拽过程移动 -
onPanEnd:拖拽结束
-
Listener简介
如果需要处理原始用户手势事件,可以使用Listner,其提供了原始手指操作的回调,核心方法有onPointerDown,onPointerMove和onPointerUp,每一个事件流都是按照onPointerDown -> onPointerMove -> onPointerUp的顺序进行的,可以继承至Listner定制自定义手势检测。
-
onPointerDown:手指按下屏幕 -
onPointerMove:手指滑动 -
onPointerUp:手指离开屏幕
GestureDetector手势事件冲突
-
scale和pan事件冲突,不能同时存在,建议直接使用scale,ScaleUpdateDetails参数中,如果scale == 1则可以认为进入pan流程,前后两个focalPoint的差值即是DragUpdateDetails中的delta -
HorizontalDrag和VerticalDrag不能共存,且如果存在scale的话,Drag事件优先级比较高,scale事件将被忽略。如果既要检测横向拖拽又要检测纵向拖拽,有两种方案:-
GestureDetector嵌套,分别提供vertivalDrag和horizontalDrag的回调,优势是简单方便,劣势是不能检测任意方向上的滑动 -
pan事件处理,直接处理pan事件流,可以检测到任意方向上的拖拽,有点是比较灵活和全面,缺点是稍显复杂
-
升级1.7遇到的问题和解决方案
1.5及1.5以前的版本可以嵌套GestureDetector同时检测scale和pan,嵌套内层的GestureDetector优先处理事件流,如果内层嵌套不处理,则外层嵌套处理事件流,非常方便。
1.7上scale事件完全成为pan事件流的超集,不能共存,且不能嵌套,需要在scale相关回调中自行判断是滑动、缩放还是转动事件,通常,在不考虑转动事件下,认为若scale == 1为拖拽事件流,否则为缩放事件流,再进行相应操作。
滑动冲突的解决方案
典型场景,TabBarView结合内部可以滑动的view,例如大图页面ScalableImageWidget,会产生典型的滑动冲突。
解决方案是内部ScalableImageWidget优先级较高,决定是否处理滑动事件,若不处理,则移交给外部TabBarView处理。具体如下:
- root页面持有
TabBarView和其child ScalableImageWidget构造方法,设置标志位_needHandleScroll决定TabBarView的physics属性取PageScrollPhysics还是NeverScrollableScrollPhysics,设置ScalableImageWidget回调scrollStateCallback -
ScalableImageWidget根据边界条件决定是否消费滑动事件,若不消费,则调用ScrollStateCallback回调设置root页面_needHandleScroll标志位,将处理权交给TabBarView - 若
TabBarView获取了滑动事件处理权,则监听滑动动画的执行,滑动动画结束后将处理权移交给内部child








网友评论