那些你曾不知道的觸控事件—Android分發機制完全解析

codeGoogle發表於2019-02-22

最近在做高德地圖的時候,由於使用者的要求,不得不用ScrollVew巢狀MapView,雖然很官方要求不建議這樣做,但也迫於無奈… 魔高一尺,道高一丈.有什麼事是程式設計師不能解決的,如果有那就是解決兩次.

鑑於用到了觸控事件,於是就來總結了Android的觸控事件機制.

首先當使用者進行螢幕操作的時候,則有兩種情況

  • 一是按鍵事件

  • 二是觸控事件

按鍵事件分為長按和點選事件,過於簡單,這裡不再進行總結.

觸控事件

觸控事件的組成:

  • 一個actionDown
  • n個actionMove
  • 一個actionUp
  • 一個onClick
  • 一個onLongClick
  • 一個onScroll

Android元件

  • 繼承 ViewGroupo比如LinearLayout,ScrollView,GridView,extends–>ViewGroup的View
  • ViewGroup容器
  • 繼承於 View不包含其他的View ,如TextView,Edittext,Butto等

下面介紹一個講的好的一個部落格地址:

www.trinea.cn/android/tou…

MotionEvent

所有Touch事件都被封裝成了MotionEvent物件,包括Touch的位置、時間、歷史記錄以及第幾個手指(多指觸控)等。

Android 事件的處理的分類

  • 分發(很多人也稱作為傳遞事件) : dispatchTouchEvent函式

  • 消費: onTouchEvent函式和OnTouchListener函式

  • 攔截:onInterceptTouchEvent函式

我們都知道Android的觸控事件都是從外層傳遞到內層:由最外層的Activity——>ViewGroup——> ViewGroupo——>…….View.

第一觸控事件傳遞的開始一定是Activity;

第二傳遞方式是通過隧道方式傳遞;

第三一直傳遞到一個最外層的View,也就是頂級View,由該View的這個方法來進行分發。

對於Activity來說:


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean isFlag = super.dispatchTouchEvent(ev);
        return isFlag;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean isFlag = super.onTouchEvent(event);
        return isFlag;
    }複製程式碼

對於ViewGroup來說:

  @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean isFlag =super.onTouchEvent(event);
        return  isFlag;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean isFlag =super.onInterceptTouchEvent(event);
        return  isFlag;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        boolean isFlag =super.dispatchTouchEvent(event);
        return  isFlag;
    }複製程式碼

對於View來說和Activity一樣,只有消費和攔截


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean isFlag = super.dispatchTouchEvent(ev);
        return isFlag;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean isFlag = super.onTouchEvent(event);
        return isFlag;
    }複製程式碼

www.open-open.com/lib/view/op…

android中的Touch事件都是從ACTION_DOWN開始的:

單手指操作:ACTION_DOWN—ACTION_MOVE—-ACTION_UP

多手指操作:ACTION_DOWN—ACTION_POINTER_DOWN—ACTION_MOVE–ACTION_POINTER_UP—ACTION_UP.

如果都不進行攔截,都不消費的基本流程圖:

Markdown
Markdown

下面我們就用案例進行分析:

activity—-》ViewGroup(MyLinearLayout)—》ViewGroup(MySubView)

MyLinearLayout的dispatchTouchEvent返回false。

Markdown
Markdown

列印結果:

Markdown
Markdown

  • 過程及結果分析:
  • 事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控制元件的dispatchTouchEvent;

  • 而該TouchEventFather 控制元件的 dispatchTouchEvent 返回 false,表示對獲取到的事件停止向下傳遞,同時也不對該事件進行消費;

  • 由於 TouchEventFather 獲取的事件直接來自 TouchEventActivity ,則會將事件返回給 TouchEventActivity 的 onTouchEvent 進行消費;

  • 最後直接由 TouchEventActivity 來響應手指移動和抬起事件。

MyLinearLayout的dispatchTouchEvent返回true。

Markdown
Markdown

列印結果:

Markdown
Markdown

  • 過程及結果分析:
  • 事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控制元件的 dispatchTouchEvent;

  • 而該TouchEventFather 控制元件的 dispatchTouchEvent 返回 true,表示分發事件到 TouchEventFather 控制元件並由該控制元件的 dispatchTouchEvent 進行消費;

  • 又因為TouchEventActivity 不斷的分發事件到 TouchEventFather 控制元件的 dispatchTouchEvent,而 TouchEventFather 控制元件的 dispatchTouchEvent 也不斷的將獲取到的事件進行消費。

MyLinearLayout的onInterceptTouchEvent返回true。

Markdown
Markdown

列印結果:

Markdown
Markdown

  • 過程及結果分析:

  • 事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控制元件的 dispatchTouchEvent;

  • 而該TouchEventFather 控制元件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示對事件進行分發並向下傳遞給 TouchEventFather 控制元件的 onInterceptTouchEvent 方法;

  • 而該方法返回 true 表示對所獲取到的事件進行攔截並將事件傳遞給 TouchEventFather 控制元件的 onTouchEvent 進行處理,TouchEventFather 控制元件的 onTouchEvent 返回 super.onTouchEvent(ev) 表示對事件沒有做任何處理直接將事件返回給上級控制元件;

  • 由於 TouchEventFather 獲取的事件直接來自 TouchEventActivity,所以 TouchEventFather 控制元件的 onTouchEvent 會將事件以冒泡方式直接返回給 TouchEventActivity 的 onTouchEvent 進行消費;

  • 後續的事件則會跳過 TouchEventFather 直接由 TouchEventActivity 的 onTouchEvent 消費來自 TouchEventActivity 自身分發的事件。

MyLinearLayout的onInterceptTouchEvent返回false。

Markdown
Markdown

列印結果:

Markdown
Markdown

  • 過程及結果分析:

  • 事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控制元件的 dispatchTouchEvent;

  • 而該控制元件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示對事件進行分發並向下傳遞給 TouchEventFather 控制元件的 onInterceptTouchEvent 方法;

  • 該方法返回 false 表示事件會被放行並傳遞到子控制元件 TouchEventChilds 的 dispatchTouchEvent 方法;

  • 同樣 TouchEventChilds 的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示對事件進行分發並向下傳遞給 TouchEventChilds 控制元件的 onInterceptTouchEvent 方法;

  • 而TouchEventChilds 的 onInterceptTouchEvent 方法返回 super.onInterceptTouchEvent(ev) ,預設會將事件傳遞給 TouchEventChilds 的 onTouchEvent 進行處理;

  • 而TouchEventChilds 的 onTouchEvent 返回 super.onTouchEvent(ev) 表示對事件沒有做任何處理直接將事件返回給上級控制元件;

  • 由於 TouchEventChilds 獲取的事件直接來自 TouchEventFather,所以 TouchEventChilds 控制元件的 onTouchEvent 會將事件以冒泡方式直接返回給 TouchEventFather 的 onTouchEvent 進行消費;

  • 而 TouchEventFather 的 onTouchEvent 也返回了 super.onTouchEvent(ev),同樣 TouchEventFather 的 onTouchEvent 也會將事件返回給上級控制元件;

  • 而 TouchEventFather 獲取的事件直接來自 TouchEventActivity,所以 TouchEventFather 控制元件的 onTouchEvent 會將事件以冒泡方式直接返回給 TouchEventActivity 的 onTouchEvent 進行消費;

  • 後續的事件則會跳過 TouchEventFather 和 TouchEventChilds 直接由 TouchEventActivity 的 onTouchEvent 消費來自 TouchEventActivity 自身分發的事件。

MyLinearLayout的onInterceptTouchEvent返回false。MySubView返回true

Markdown
Markdown

列印結果:

Markdown
Markdown

  • 過程及結果分析:

  • 事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分發給 TouchEventFather 控制元件的 dispatchTouchEvent;

  • 該控制元件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),事件會分發到 TouchEventFather 的 onInterceptTouchEvent,此方法返回 false 表示放行當先事件;

  • 事件會被傳遞到子控制元件 TouchEventChilds 的 dispatchTouchEvent 方法,dispatchTouchEvent 返回 true 表示事件被分發到 TouchEventChilds ,並由 dispatchTouchEvent 方法消費;

  • 後續的事件也會不斷的重複上面的邏輯最終被 TouchEventChilds 的 dispatchTouchEvent 消費。

其實如果看原始碼的話比較複雜,總結一下,基本的規則是:

  • down事件首先會傳遞到onInterceptTouchEvent()方法

  • 如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之後return false,內部view將也會獲取到down,那麼後續的move, up等事件將繼續會先傳遞給該ViewGroup,之後一樣傳遞給最終的目標view的onTouchEvent()處理。

  • 如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之後return true,內部view不會貨到down,後續的move, up等事件也不再傳遞給onInterceptTouchEvent(),當然,move,up也不會傳給view

  • 如果最終需要處理事件的view的onTouchEvent()返回了false,那麼該事件將被傳遞至其上一層次的view的onTouchEvent()處理。

  • 如果最終需要處理事件的view 的onTouchEvent()返回了true,那麼後續事件將可以繼續傳遞給該view的onTouchEvent()處理。

  • group的touch和這個機制沒關係,與上一級有關係。

來自androidstarjack部落格地址:

blog.csdn.net/androidstar…

如果你覺得此文對您有所幫助,歡迎入群 QQ交流群 :232203809   

微信公眾號:終端研發部

技術+職場
技術+職場

(歡迎關注學習和交流)

相關文章