Android 事件分發(fā)機(jī)制

Android 事件分發(fā)一直都是一個(gè)難點(diǎn),讓人很模糊,感覺自己知道點(diǎn),但又不是很清楚。最重要的是我知道了理論,怎么沒感覺在實(shí)際開發(fā)中用到呢?今天決定來好好研究一下這個(gè)事件分發(fā)機(jī)制,如有不正確的地方,請(qǐng)多多指教。


假設(shè)這是手機(jī)界面圖.png

首先看上圖,我們要知道這個(gè)層次關(guān)系,你可以把每一層理解為一張紙放在桌子上,那就是Activity這張紙?jiān)谧钕旅?,ViewGroup在Activity的上面,View在ViewGroup的上面。

好了,理解完層次關(guān)系,我們可以點(diǎn)一下View內(nèi)任意一點(diǎn)。這時(shí)View是接受了一個(gè)事件(先不用管是什么事件,隨便你就理解為一個(gè)ACTION_DOWN事件)。到這里我要說下我原來的錯(cuò)誤認(rèn)識(shí),做個(gè)反面教程,讓大家更好的理解。我原來認(rèn)為,View最先接收到到事件,所以事件的傳遞肯定是View---ViewGroup---Activity這樣由外往里傳遞的。

其實(shí)不然,你可以這樣理解。Activity、ViewGroup、View是一個(gè)部門的三個(gè)同事,只不過Activity是部門主任,ViewGroup是主管,View是一個(gè)職員。

突然有一天View接受到一個(gè)任務(wù)(觸摸事件),那我們要想一下這個(gè)任務(wù)是怎么來的呢,無論這個(gè)任務(wù)是你自己部門內(nèi)部的,還是兄弟部門的協(xié)作任務(wù)。只要給你分配任務(wù),那都要必須經(jīng)過你的主任和主管的同意,因?yàn)樗麄兪悄愕捻旑^上司,而你是他們的干將。所以說這個(gè)任務(wù)也就是這個(gè)事件,在傳到View之前,早已經(jīng)經(jīng)過了你的領(lǐng)導(dǎo)的同意,也就是早就經(jīng)過了Activity和ViewGroup。如果領(lǐng)導(dǎo)不同意,或是半路領(lǐng)導(dǎo)就一句話的事幫你把事給解決了。那這個(gè)任務(wù)也就不會(huì)傳遞到View這來了。

總結(jié):雖然我們是對(duì)View發(fā)起了觸摸事件,但是事件的傳遞開始于Activity。由Activiy----ViewGroup---View。

好了,到這里大體知道了事件傳遞流程了。但是我們還要知道幾個(gè)重要的方法。如下圖所示,除了ViewGroup多了一個(gè)onInterceptTouchEvent方法外,他們都有dispatchTouchEventonTouchEvent

方法.png

接下來我們看看這幾個(gè)方法都是什么意思,還是說下我原來的認(rèn)識(shí)誤區(qū)。我原來認(rèn)為dispatchTouchEvent是事件分發(fā)的方法,所以它返回true代表向下分發(fā)事件,false代表不向下分發(fā),如果return super. dispatchTouchEvent(),我就認(rèn)為交給父類處理。

我當(dāng)時(shí)的認(rèn)知是不完全正確的,那說下這幾個(gè)方法分別返回 true或false或是return super.xxx到底是什么意思。
  • dispatchTouchEvent
    true:代表消費(fèi)掉事件,不再向下傳遞
    false:代表不向下分發(fā)事件,不處理,再把事件返還到上一層
    super:表示繼續(xù)向下傳遞
  • onInterceptTouchEvent
    true: 代表攔截事件,不向下傳遞,交給自己的onTouchEvent處理
    false和super:都表示不攔截,繼續(xù)向下傳遞事件
  • onTouchEvent:
    true:表示消費(fèi)掉事件
    false:Activity中表示丟棄事件不處理,而如果是在ViewGroup和View中表示自己不處理,交給上級(jí)
    super:交給上級(jí)處理

這里還要區(qū)分是誰的dispatchTouchEvent方法,Activity和View的這個(gè)方法同樣的返回值,所做的操作是有區(qū)別的。詳細(xì)可以看下面的流程圖

是時(shí)候展示真正的技術(shù)了:我看了好多類似的事件分發(fā)流程圖,乍眼一看都有點(diǎn)蒙圈,覺得好凌亂復(fù)雜。所以我也畫了一個(gè)圖,個(gè)人認(rèn)為還算清晰簡潔。

Android事件分發(fā)機(jī)制流程圖.png

你不需要太仔細(xì)看這個(gè)圖,先簡單的看下。然后就是需要自己擼代碼驗(yàn)證的時(shí)刻了,這時(shí)會(huì)加深你的理解。擼代碼回來再仔細(xì)看,你就會(huì)恍然大明白了。

那怎樣寫代碼,很簡單:

  1. 先自定義一個(gè)ViewGroup繼承LinearLayout,重寫需要的幾個(gè)方法
  2. 自定義View繼承TextView,重寫需要的方法
  3. 在Activity的布局代碼中加入你自定義的ViewGroup,ViewGroup里面是的自定義的TextView。Activity中重寫需要的方法。
//自定義ViewGroup
public class MyViewGroup extends LinearLayout {

    public MyViewGroup(Context context) {
        super(context);
    }

    public MyViewGroup(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

   /*************下面是我們需要的方法*************/

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e("ViewGroup", "dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.e("ViewGroup", "onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("ViewGroup", "onTouchEvent");
        return super.onTouchEvent(event);
    }
}
//自定義TextView
public class MyTextView extends android.support.v7.widget.AppCompatTextView {

    public MyTextView(Context context) {
        super(context);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

  /*************下面是我們需要的方法*************/
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e("View", "dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("View", "onTouchEvent");
        return super.onTouchEvent(event);
    }
}
//Activity中的代碼
public class Main6Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main6);
    }

  // 這里只打印ACTION_DOWN的傳遞,不然會(huì)有移動(dòng)和抬起的事件,在一起比較亂
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction()==MotionEvent.ACTION_DOWN){
            Log.e("Activity", "dispatchTouchEvent");
            return super.dispatchTouchEvent(ev);
        }
        return false;
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("Activity", "onTouchEvent");
        return super.onTouchEvent(event);
    }
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.zsl.aa.Main6Activity">

    <com.zsl.aa.MyViewGroup
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.zsl.aa.MyTextView
            android:id="@+id/tv_view"
            android:layout_width="120dp"
            android:layout_height="130dp"
            android:background="#ff00ff" />
    </com.zsl.aa.MyViewGroup>

</LinearLayout>

ok,到這里準(zhǔn)備工作就完事了,非常簡單的代碼。運(yùn)行程序后,點(diǎn)下View看看打印結(jié)果。然后再分不同的情況,分別更改返回的值為true或false或是super.xxx。分別看看打印情況,你就會(huì)知道了傳遞流程了。

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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