前言
在Flutter開發App的過程中,我們除了需要靈活的使用各種組件之外,還需要掌握手勢的識別,比如我們常常需要在操作App的時候使用到縮放,雙擊,放大,縮小等操作,這些Flutter都給我們提供了監聽的組件GestureDetector。這篇博文將詳細介紹GestureDetector手勢識別的使用規則。(拖動手勢監聽)
GestureDetector基本用法
我們前面提到過,在Flutter開發中,一切皆是組件,所以GestureDetector同樣是一個組件,我們使用它,通常是作爲一個父Widget包裹一個子Widget外面(也就是你需要捕捉那個組件的手勢,就把GestureDetector套在外外面),而內部我們通過onTap回調來實現其點擊的效果,代碼如下:
GGestureDetector(
onTap:(){
print("tap");
},
child:Container{
padding:EdgeInsets.all(20),
decoration:BoxDecoration(
color:Theme.of(context).buttonColor,
borderRadius:BorderRadius.circular(8.0),
),
child:new Text("文本"),
}
);
比如上面的代碼就是改造Text組件成爲按鈕的方式,這裏捕捉了點擊事件。
常用事件
GestureDetector手勢識別不僅僅只有onTap事件,還有很多很多的常用事件,博主通過一張表格將它們全部列舉了出來,方便大家查閱:
屬性 | 取值意義 |
---|---|
onTapDwon | 當按下屏幕時觸發 |
onTap | 當與屏幕短暫地觸碰時觸發,最常用 |
onTapUp | 當用戶停止觸碰屏幕時觸發 |
onTapCancel | 當用戶觸摸屏幕,但沒有完成Tap事件時觸發 |
onDoubleTap | 快速雙擊屏幕時觸發 |
onLongPress | 當長按屏幕時觸發(與屏幕接觸事件必須超過500ms) |
onPanUpdate | 當在屏幕上移動時觸發 |
onVerticalDragDown | 當手指觸碰屏幕且準備往屏幕垂直方向移動時觸發 |
onVerticalDragStart | 當手指觸碰屏幕且開始往屏幕垂直方向移動時觸發 |
onVerticalDragUpdate | 當手指觸碰屏幕且開始往屏幕垂直方向移動併發生位移時觸發 |
onVerticalDragEnd | 當用戶完成垂直方向觸摸屏幕時觸發 |
onVerticalDragCancel | 當用戶中斷了onVerticalDragDown時觸發 |
onHorizontalDragDown | 當手指觸摸屏幕且準備往屏幕水平方向移動時觸發 |
onHorizontalDragStart | 當手指觸摸屏幕且開始往屏幕水平方向移動時觸發 |
onHorizontalDragUpdate | 當手指觸摸屏幕且開始往屏幕水平方向移動併發生位移時觸發 |
onHorizontalDragEnd | 當用戶完成水平方向觸摸屏幕時觸發 |
onHorizontalDragCancel | 當用戶中斷了onHorizontalDragDown時觸發 |
onPanDown | 當用戶觸摸屏幕時觸發 |
onPanStart | 當用戶觸摸屏幕並開始移動時觸發 |
onPanUpdate | 當用戶觸摸屏幕併產生移動時觸發 |
onPanEnd | 當用戶完成觸摸屏幕時觸發 |
onScaleStart | 當用戶觸摸屏幕並開始縮放時觸發 |
onScaleUpdate | 當用戶觸摸屏幕併產生縮放時觸發 |
onScaleEnd | 當用戶完成縮放時觸發 |
雖然說上面表格非常詳細,但其中有些事件是互斥的,並不能同時存在,比如onVerticalUpdate,onHorizontalUpdate,onPanUpdate這些三個事件都不能同時存在,否則會報錯。
另外,onPanUpdate和onScaleUpdate也不能同時存在,這是因爲在Gesture識別器裏,Scale操作是Pan操作的超集。
監聽事件實現縮放
既然我們瞭解瞭如何使用這些事件,那麼,我們就應該實踐起來,這裏小編將用上面的事件實現一個縮放效果,代碼如下:
class _MyHomePageState extends State<MyHomePage>{
double _top=0.0;
double _left=0.0;
double _size=100.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: new Text("忽略事件"),),
body: Stack(
children: <Widget>[
Positioned(
top: this._top,
left: this._left,
child: GestureDetector(
child: FlutterLogo(
size: this._size,
),
onScaleUpdate: (e){
setState(() {
this._size=300*e.scale.clamp(.5, 10.0);////縮放倍數在0.5到10倍之間
});
},
),
),
],
),
);
}
}
代碼非常的簡單,就是縮放FlutterLogo的大小,實現的效果如下圖所示:
監聽事件實現拖拽
既然我們已經瞭解這麼多事件,不妨多來一個事件,也就是App中常用的拖拽操作,代碼如下(略微改改上上面的代碼就行):
class _MyHomePageState extends State<MyHomePage>{
double _top=0.0;
double _left=0.0;
double _size=100.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: new Text("忽略事件"),),
body: Stack(
children: <Widget>[
Positioned(
top: this._top,
left: this._left,
child: GestureDetector(
child: FlutterLogo(
size: this._size,
),
onPanUpdate: (e){
setState(() {
this._left+=e.delta.dx;
this._top+=e.delta.dy;
});
},
),
),
],
),
);
}
}
僅僅改變了事件的代碼,前面說過onPanUpdate與onScaleUpdate不能同時存在,所以不能直接添加事件,需要刪除onScaleUpdate後在添加。
事件通知
Notification是“通知”的意思,這和Android中不一樣。在Flutter裏,Notification會沿着當前的context節點從下往上傳遞,所有父節點都可以通過NotificationListener來監聽通知,這種由子向父的傳遞方式,我們稱爲“通知冒泡”,並繼承至Notification,而父Widget使用NotificationListener進行監聽並捕獲通知。常用的NotificatioListener有LayoutChangeNotification,SizeChangedLayoutNotifier,ScrollNotification等。比如本篇將監聽ListView滾動狀態:是通過NotificationListener裏的onNotification回調方法來判斷狀態。代碼如下:
class _MyHomePageState extends State<MyHomePage> {
String _message = "我是通知";
void _onScrollStart(ScrollMetrics scrollMetrics){
print(scrollMetrics.pixels);
setState(() {
this._message="滾動開始";
});
}
void _onScrollEnd(ScrollMetrics scrollMetrics){
print(scrollMetrics.pixels);
setState(() {
this._message="滾動結束";
});
}
void _onScrollUpdate(ScrollMetrics scrollMetrics){
print(scrollMetrics.pixels);
setState(() {
this._message="滾動進行時";
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: new Text("事件通知"),
),
body: Column(
children: <Widget>[
Container(
height: 50.0,
color: Colors.green,
child: Center(
child: new Text(this._message),
),
),
Expanded(
child: NotificationListener<ScrollNotification>(
// ignore: missing_return
onNotification: (scrollNotification) {
if (scrollNotification is ScrollStartNotification) {
this._onScrollStart(scrollNotification.metrics);
} else if (scrollNotification is ScrollUpdateNotification) {
this._onScrollUpdate(scrollNotification.metrics);
} else if (scrollNotification is ScrollEndNotification) {
this._onScrollEnd(scrollNotification.metrics);
}
},
child: ListView.builder(
itemCount: 30,
itemBuilder: (context, index) {
return ListTile(title: Text("索引:$index"),);
}),
),
),
],
),
);
}
}
實現效果如下圖所示,代碼很好理解,這裏就不在贅述了: