Flutter 手勢(shì)系列教程---Listener

Listener介紹

Listener它是主要的功能是用來(lái)監(jiān)聽(tīng)屏幕觸摸事件,取決于它的子組件區(qū)域范圍,比如按下、移動(dòng)、抬起、取消等操作時(shí)可以添加監(jiān)聽(tīng)。

我們知道Flutter組件只有按鈕才會(huì)有事件,那么如果我需要在文字或者某個(gè)容器上添加事件那我就需要借助Listener

視頻教程地址

手勢(shì)系列視頻教程地址

在什么情況下使用Listener?

Listener常用于當(dāng)手指滑動(dòng)屏幕時(shí)進(jìn)行隱藏鍵盤或者下拉刷新、上拉加載時(shí)進(jìn)行事件監(jiān)聽(tīng)。

一般在實(shí)際的開發(fā)過(guò)程中我們很少會(huì)用到Listener來(lái)監(jiān)聽(tīng)手勢(shì),一般都是通過(guò)GestureDetector來(lái)進(jìn)行監(jiān)聽(tīng)或者使用MouseRegion來(lái)監(jiān)聽(tīng)鼠標(biāo)的事件,而MouseRegion常用于web開發(fā)中,GestureDetector常用于app。

Listener原理

  • 當(dāng)指針按下時(shí),F(xiàn)lutter會(huì)對(duì)應(yīng)用程序執(zhí)行命中測(cè)試(Hit Test),以確定指針與屏幕接觸的位置存在哪些組件(widget)
  • 指針按下事件(以及該指針的后續(xù)事件)然后被分發(fā)到由命中測(cè)試發(fā)現(xiàn)的最內(nèi)部的組件
  • 事件會(huì)沿著最內(nèi)部的組件向組件樹的根冒泡分發(fā)
  • 沒(méi)有機(jī)制取消或停止“冒泡”過(guò)程

Listener構(gòu)造函數(shù)

我們經(jīng)常使用的回調(diào)函數(shù)主要有三個(gè)

  • onPointerDown()
  • onPointerMove()
  • onpointUp()
const Listener({
    Key key,
    this.onPointerDown,
    this.onPointerMove,
    // We have to ignore the lint rule here in order to use deprecated
    // parameters and keep backward compatibility.
    // TODO(tongmu): After it goes stable, remove these 3 parameters from Listener
    // and Listener should no longer need an intermediate class _PointerListener.
    // https://github.com/flutter/flutter/issues/36085
    @Deprecated(
      'Use MouseRegion.onEnter instead. See MouseRegion.opaque for behavioral difference. '
      'This feature was deprecated after v1.10.14.'
    )
    this.onPointerEnter,
    @Deprecated(
      'Use MouseRegion.onExit instead. See MouseRegion.opaque for behavioral difference. '
      'This feature was deprecated after v1.10.14.'
    )
    this.onPointerExit,
    @Deprecated(
      'Use MouseRegion.onHover instead. See MouseRegion.opaque for behavioral difference. '
      'This feature was deprecated after v1.10.14.'
    )
    this.onPointerHover,
    this.onPointerUp,
    this.onPointerCancel,
    this.onPointerSignal,
    this.behavior = HitTestBehavior.deferToChild,
    Widget child,
  }) : assert(behavior != null),
       _child = child,
       super(key: key);

Listener屬性和說(shuō)明

字段 屬性 描述
onPointerDown PointerDownEventListener 指針按下時(shí)觸發(fā)回調(diào)
onPointerMove PointerMoveEventListener 指針移動(dòng)時(shí)觸發(fā)回調(diào)
onPointerUp PointerUpEventListener 指針移開時(shí)觸發(fā)回調(diào)
onPointerSignal PointerSignalEventListener 當(dāng)指針信號(hào)出現(xiàn)時(shí)調(diào)用
onPointerCancel PointerCancelEventListener 指針取消時(shí)觸發(fā)回調(diào)
onPointerEnter PointerEnterEventListener 當(dāng)指針進(jìn)入?yún)^(qū)域時(shí)回調(diào)(已廢棄)
onPointerExit PointerExitEventListener 當(dāng)指針移出區(qū)域時(shí)回調(diào)(已廢棄)
onPointerHover PointerHoverEventListener 當(dāng)沒(méi)有觸發(fā) [onPointerDown] 的指針改變時(shí)調(diào)用
behavior HitTestBehavior 在命中測(cè)試期間如何表現(xiàn)
child Widget 子組件

Listener基本使用

我們這里主要是針對(duì)onPointerDown、onPointerMoveonPointerUp 進(jìn)行演示,因?yàn)槲覀冊(cè)谄綍r(shí)的開發(fā)過(guò)程中最常用到的屬性就是這三個(gè),而且其他的屬性也都被廢棄掉了。

import 'package:flutter/material.dart';


class ListenerExample extends StatefulWidget {
  @override
  _ListenerExampleState createState() => _ListenerExampleState();
}

class _ListenerExampleState extends State<ListenerExample> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Listener"),
      ),
      body: Center(
        child: Stack(
          children: [
            Listener(
              onPointerDown: (event) {
                print("onPointerDown----$event");
              },
              onPointerMove: (event) {
                print("onPointerMove----$event");
              },
              onPointerUp: (event) {
                print("onPointerUp----$event");
              },
              // onPointerSignal: (event) {
              //   print("onPointerSignal----$event");
              // },
              // onPointerCancel: (event) {
              //   print("onPointerCancel----$event");
              // },
              // onPointerEnter: (event) {
              //   print("onPointerEnter----$event");
              // },
              // onPointerExit: (event) {
              //   print("onPointerExit----$event");
              // },
              // onPointerHover: (event) {
              //   print("onPointerHover----$event");
              // },
              child: Container(
                color: Colors.pink,
                child: Text("Jimi",
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 30
                  ),
                ),
              ),
            ),
            Positioned(
              child: Listener(
                onPointerDown: (event) {
                  print("red---- $event");
                },
                child: Container(
                  width: 100,
                  height: 100,
                  color: Colors.red,
                  child: Text("Jimi"),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

控制臺(tái)輸出

我們這里先點(diǎn)擊橙色容器,在點(diǎn)擊一次紅色容器,他們打印的結(jié)果如下。

flutter: onPointerDown----PointerDownEvent#128be(position: Offset(204.5, 403.5))
flutter: onPointerUp----PointerUpEvent#a9558(position: Offset(204.5, 403.5))
flutter: red---- PointerDownEvent#8ffdf(position: Offset(140.5, 317.0))

PointerEvent介紹

PointerEvent是觸摸、手寫筆、鼠標(biāo)事件的基類。

在上文中,我們知道了什么是Listener并寫了一個(gè)簡(jiǎn)單的案例,在使用案例的過(guò)程中我們的事件里面都帶了一個(gè)event參數(shù),而所有的事件最終都是繼承自PointerEvent,那我們接下來(lái)看看event的參數(shù)有什么作用。

PointerEvent構(gòu)造函數(shù)

const PointerEvent({
  this.embedderId = 0,
  this.timeStamp = Duration.zero,
  this.pointer = 0,
  this.kind = PointerDeviceKind.touch,
  this.device = 0,
  this.position = Offset.zero,
  Offset? localPosition,
  this.delta = Offset.zero,
  Offset? localDelta,
  this.buttons = 0,
  this.down = false,
  this.obscured = false,
  this.pressure = 1.0,
  this.pressureMin = 1.0,
  this.pressureMax = 1.0,
  this.distance = 0.0,
  this.distanceMax = 0.0,
  this.size = 0.0,
  this.radiusMajor = 0.0,
  this.radiusMinor = 0.0,
  this.radiusMin = 0.0,
  this.radiusMax = 0.0,
  this.orientation = 0.0,
  this.tilt = 0.0,
  this.platformData = 0,
  this.synthesized = false,
  this.transform,
  this.original,
}) : localPosition = localPosition ?? position,
     localDelta = localDelta ?? delta;

PointerEvent屬性和說(shuō)明

PointerEvent的屬性非常多,但在我們實(shí)際的開發(fā)過(guò)程中很少會(huì)使用到,只有在特定的情景下才會(huì)使用對(duì)應(yīng)的屬性。

如需要做一個(gè)全局懸浮的按鈕我們會(huì)使用到position

如需要做繪圖軟件我們需要用到buttonskind

所以大家可以根據(jù)實(shí)際的應(yīng)用場(chǎng)景來(lái)使用對(duì)應(yīng)的屬性即可,下面是我對(duì)PointerEvent的屬性進(jìn)行的一個(gè)詳細(xì)描述。

總共29個(gè)屬性

字段 屬性 描述
embedderId int 標(biāo)識(shí)平臺(tái)事件ID
timeStamp Duration 事件調(diào)度時(shí)間
pointer int 指針唯一標(biāo)識(shí)符,每一次點(diǎn)擊都會(huì)是一個(gè)新的,不會(huì)重復(fù)
kind PointerDeviceKind 指針事件的輸入設(shè)備類型
device int 設(shè)備唯一標(biāo)識(shí)符,在交互中會(huì)重復(fù)使用
position Offset 指針相對(duì)于全局坐標(biāo)的偏移
localPosition Offset 指針相對(duì)于當(dāng)前容器坐標(biāo)的偏移
delta Offset 兩次指針移動(dòng)事件的距離
localDelta Offset 兩次指針移動(dòng)事件的距離(當(dāng)前容器)
buttons int 它是*button常量,經(jīng)常與kind配合使用,做繪圖的畫筆軟件是經(jīng)常用到該屬性,如果它的值為6,kind是PointerDeviceKind.invertedStylus,那么這表示一個(gè)倒置觸控筆
down bool 設(shè)置當(dāng)前指針是否按下
obscured bool 是否遮擋應(yīng)用程序的窗口,該屬性官方還沒(méi)實(shí)現(xiàn)
pressure double 按壓力度,壓力傳感器(如iPhone的3D Touch)中使用,取值0.0-1.0
pressureMin double 按壓力度最小值
pressureMax double 按壓力度最大值
distance double 檢測(cè)物體與輸入表面的距離
distanceMin double 限制檢測(cè)物體與輸入表面的距離最小值
distanceMax double 限制檢測(cè)物體與輸入表面的距離最大值
size double 被按下屏幕的區(qū)域大小
radiusMajor double 接觸橢圓沿主軸的半徑,以邏輯像素為單位
radiusMinor double 接觸橢圓沿短軸的半徑,以邏輯像素為單位
radiusMin double radiusMajor和radiusMinor報(bào)告的最小值
radiusMax double radiusMajor和radiusMinor報(bào)告的最大值
orientation double 檢測(cè)到的物體的方向(指針移動(dòng)方向),以弧度為單位
tilt double 檢測(cè)到的物體的傾斜角度,以弧度為單位
platformData int 與事件關(guān)聯(lián)的不透明平臺(tái)特定數(shù)據(jù)
synthesized bool 設(shè)置事件是否由 Flutter 合成。
transform Matrix4 用于從全局坐標(biāo)轉(zhuǎn)換此事件的轉(zhuǎn)換
original PointerEvent 在任何transform之前的原始未轉(zhuǎn)換PointerEvent事件

behavior屬性

behavior屬性,它決定子組件如何響應(yīng)命中測(cè)試,它的值類型為HitTestBehavior,這是一個(gè)枚舉類,有三個(gè)枚舉值

HitTestBehavior.deferToChild

對(duì)子組件一個(gè)接一個(gè)的進(jìn)行命中測(cè)試,如果子組件中有測(cè)試通過(guò)的,則當(dāng)前組件通過(guò),這就意味著,如果指針事件作用于子組件上時(shí),其父級(jí)組件也肯定可以收到該事件。

HitTestBehavior.opaque

在命中測(cè)試時(shí),將當(dāng)前組件當(dāng)成不透明處理(即使本身是透明的),最終的效果相當(dāng)于當(dāng)前Widget的整個(gè)區(qū)域都是點(diǎn)擊區(qū)域

HitTestBehavior.translucent

點(diǎn)擊組件透明區(qū)域時(shí),可以對(duì)自身邊界內(nèi)及底部可視區(qū)域都進(jìn)行命中測(cè)試,這意味著點(diǎn)擊頂部組件透明區(qū)域時(shí),頂部組件和底部組件都可以接收到事件

代碼演示

import 'package:flutter/material.dart';


class ListenerSimpleExample extends StatefulWidget {
  @override
  _ListenerSimpleExampleState createState() => _ListenerSimpleExampleState();
}

class _ListenerSimpleExampleState extends State<ListenerSimpleExample> with AutomaticKeepAliveClientMixin {

  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(
      appBar: AppBar(
        title: Text("Listener"),
      ),
      body: Center(
        child: Stack(
          children: [
            Listener(
              child: ConstrainedBox(
                  constraints: BoxConstraints.tight(Size(400, 200)),
                  child: Container(
                    color: Colors.greenAccent,
                  )
              ),
              onPointerDown: (event) => print("綠色盒子被點(diǎn)擊了"),
            ),
            Listener(
              child: ConstrainedBox(
                constraints: BoxConstraints.tight(Size(400, 200)),
                child: Center(child: Text("點(diǎn)擊文字", style: TextStyle(
                  color: Colors.white,
                  fontSize: 30
                ),)),
              ),
              onPointerDown: (event) => print("文字點(diǎn)擊事件回調(diào)"),
              behavior: HitTestBehavior.deferToChild,
              // behavior: HitTestBehavior.opaque,
              // behavior: HitTestBehavior.translucent,
            )
          ],
        ),
      ),
    );
  }
}

當(dāng)屬性設(shè)置為HitTestBehavior.deferToChild控制臺(tái)輸出結(jié)果

我們這里演示每次都是先點(diǎn)擊綠色盒子在點(diǎn)擊文字,以便大家能更好的分辨出這三個(gè)屬性的使用區(qū)別

flutter: 綠色盒子被點(diǎn)擊了
flutter: 文字點(diǎn)擊事件回調(diào)

當(dāng)屬性設(shè)置為HitTestBehavior.opaque控制臺(tái)輸出結(jié)果

flutter: 文字點(diǎn)擊事件回調(diào)
flutter: 文字點(diǎn)擊事件回調(diào)

當(dāng)屬性設(shè)置為HitTestBehavior.translucent控制臺(tái)輸出結(jié)果

flutter: 文字點(diǎn)擊事件回調(diào)
flutter: 綠色盒子被點(diǎn)擊了
flutter: 文字點(diǎn)擊事件回調(diào)

總結(jié)

ListenerFlutter中比較重要的功能性組件,它主要的功能是用來(lái)監(jiān)聽(tīng)屏幕觸摸事件,事件回調(diào)可以獲取對(duì)應(yīng)的屬性來(lái)個(gè)性化定制app功能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容