Flutter(十三)——事件處理:手勢識別與事件通知

前言

在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"),);
                  }),
            ),
          ),
        ],
      ),
    );
  }
}

實現效果如下圖所示,代碼很好理解,這裏就不在贅述了:
事件通知

發佈了109 篇原創文章 · 獲贊 144 · 訪問量 105萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章