Flutter学习日记(九)--手势识别以及处理
最近发现用思维导图边看边总结的话,思路就会清晰很多,而且有时候忘了,也可以直接查看自己的思维导图,一下就可以找到自己想要的点,关于这个我也是建议小伙伴们,也可以尝试下的哈!!
image.png
然后,后面也发现了一个比较恶心的东西,就是如果读者看这个思维导图,好像是找不到重点一样,所以我还是按照这个思维导图的思路给敲出来吧!
一、指针按下(也就是手指按下)的事件处理
1、Flutter会进行命中测试,从最内部的组件往上传递到树路径上的所有组件,然后通过Listener监听原始触摸事件来进行处理:
(1)Listener有哪些处理?
Listener({
Key key,
this.onPointerDown, //手指按下回调
this.onPointerMove, //手指移动回调
this.onPointerUp,//手指抬起回调
this.onPointerCancel,//触摸事件取消回调
this.behavior = HitTestBehavior.deferToChild, //在命中测试期间如何表现
Widget child
})
behavior属性,它决定子组件如何响应命中测试,它的值类型为HitTestBehavior,这是一个枚举类,有三个枚举值:
deferToChild:子组件会一个接一个的进行命中测试,如果子组件中有测试通过的,则当前组件通过,这就意味着,如果指针事件作用于子组件上时,其父级组件也肯定可以收到该事件。
opaque:在命中测试时,将当前组件当成不透明处理(即使本身是透明的),最终的效果相当于当前Widget的整个区域都是点击区域(该属性并不能用于在组件树中拦截(忽略)事件,它只是决定命中测试时的组件大小。)
translucent:当点击组件透明区域时,可以对自身边界内及底部可视区域都进行命中测试,这意味着点击顶部组件透明区域时,顶部组件和底部组件都可以接收到事件
(2)点击,拖动的数据变化
...
//定义一个状态,保存当前指针位置
PointerEvent _event;
...
Listener(
child: Container(
alignment: Alignment.center,
color: Colors.blue,
width: 300.0,
height: 150.0,
child: Text(_event?.toString()??"",style: TextStyle(color: Colors.white)),
),
onPointerDown: (PointerDownEvent event) => setState(()=>_event=event),
onPointerMove: (PointerMoveEvent event) => setState(()=>_event=event),
onPointerUp: (PointerUpEvent event) => setState(()=>_event=event),
),
参数PointerDownEvent、PointerMoveEvent、PointerUpEvent都是PointerEvent的一个子类,PointerEvent类中包括当前指针的一些信息:
position:它是鼠标相对于当对于全局坐标的偏移。
delta:两次指针移动事(PointerMoveEvent)的距离。
pressure:按压力度,如果手机屏幕支持压力传感器(如iPhone的3D Touch),此属性会更有意义,如果手机不支持,则始终为1。
orientation:指针移动方向,是一个角度值。
2、
假如我们不想让某个子树响应PointerEvent的话,我们可以使用IgnorePointer和AbsorbPointer,这两个组件都能阻止子树接收指针事件,不同之处在于AbsorbPointer本身会参与命中测试,而IgnorePointer本身不会参与,这就意味着AbsorbPointer本身是可以接收指针事件的(但其子树不行),而IgnorePointer不可以。
注意:AbsorbPointer和IgnorePointer是当作widget使用的
二、手势识别
1、GestureDetector
(1)、点击类型
onTap: () {},//点击
onDoubleTap: () {}, //双击
onLongPress: (){}, //长按
注意:当同时监听onTap和onDoubleTap事件时,当用户触发tap事件时,会有200毫秒左右的延时,这是因为当用户点击完之后很可能会再次点击以触发双击事件,所以GestureDetector会等一段时间来确定是否为双击事件。如果用户只监听了onTap(没有监听onDoubleTap)事件时,则没有延时.
(2)、拖动
拖动、滑动类型处理:
//手指按下时会触发此回调
onPanDown: (DragDownDetails e) {
//打印手指按下的位置(相对于屏幕)
print("用户手指按下:${e.globalPosition}");
},
//手指滑动时会触发此回调
onPanUpdate: (DragUpdateDetails e) {
//用户手指滑动时,更新偏移,重新构建
setState(() {
_left += e.delta.dx;
_top += e.delta.dy;
});
},
onPanEnd: (DragEndDetails e){
//打印滑动结束时在x、y轴上的速度
print(e.velocity);
},
DragDownDetails.globalPosition:当用户按下时,此属性为用户按下的位置相对于屏幕(而非父组件)原点(左上角)的偏移。
DragUpdateDetails.delta:当用户在屏幕上滑动时,会触发多次Update事件,delta指一次Update事件的滑动的偏移量。
DragEndDetails.velocity:该属性代表用户抬起手指时的滑动速度(包含x、y两个轴的),示例中并没有处理手指抬起时的速度,常见的效果是根据用户抬起手指时的速度做一个减速动画。
(3)、控制只能单方向拖动
GestureDetector(
child: CircleAvatar(child: Text("A")),
//垂直方向拖动事件
onVerticalDragUpdate: (DragUpdateDetails details) {
setState(() {
_top += details.delta.dy;
});
}
),
水平方向同理
(4)、双指缩事件的处理
GestureDetector(
//指定宽度,高度自适应
child: Image.asset("./images/sea.png", width: _width),
onScaleUpdate: (ScaleUpdateDetails details) {
setState(() {
//缩放倍数在0.8到10倍之间
_width=200*details.scale.clamp(.8, 10.0);
});
},
),
2、GestureRecognizer
GestureDetector内部是使用一个或多个GestureRecognizer来识别各种手势的,而GestureRecognizer的作用就是通过Listener来将原始指针事件转换为语义手势,GestureDetector直接可以接收一个子widget。
class _GestureRecognizerTestRouteState
extends State<_GestureRecognizerTestRoute> {
TapGestureRecognizer _tapGestureRecognizer = new TapGestureRecognizer();
bool _toggle = false; //变色开关
@override
void dispose() {
//用到GestureRecognizer的话一定要调用其dispose方法释放资源
_tapGestureRecognizer.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Center(
child: Text.rich(
TextSpan(
children: [
TextSpan(text: "你好世界"),
TextSpan(
text: "点我变色",
style: TextStyle(
fontSize: 30.0,
color: _toggle ? Colors.blue : Colors.red
),
recognizer: _tapGestureRecognizer
..onTap = () {
setState(() {
_toggle = !_toggle;
});
},
),
TextSpan(text: "你好世界"),
]
)
),
);
}
}
三、手势竞争
1、移动时两个轴上的位移分量,哪个轴的大,哪个轴在本次滑动事件竞争中就胜出,跑下这个案例就知道
class BothDirectionTestRoute extends StatefulWidget {
@override
BothDirectionTestRouteState createState() =>
new BothDirectionTestRouteState();
}
class BothDirectionTestRouteState extends State<BothDirectionTestRoute> {
double _top = 0.0;
double _left = 0.0;
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Positioned(
top: _top,
left: _left,
child: GestureDetector(
child: CircleAvatar(child: Text("A")),
//垂直方向拖动事件
onVerticalDragUpdate: (DragUpdateDetails details) {
setState(() {
_top += details.delta.dy;
});
},
onHorizontalDragUpdate: (DragUpdateDetails details) {
setState(() {
_left += details.delta.dx;
});
},
),
)
],
);
}
}
2、手势冲突:遇到复杂的手势情况处理,例如,在拖动手势过程,还要处理按下和松开手指的处理,通过Listener监听原始指针事件就行