歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

簡單例子了解View的事件分發

什麼是事件分發

我們在寫自定義ViewGroup或者自定義View的時候經常要處理用戶的點擊事件,如果我們的View在最底層,他在很多ViewGroup裡面,我們如何讓我們的點擊事件准確傳遞到View裡面,這就涉及到一個View很重要的知識點,View的事件分發。事件分發,分開來講就是事件+分發,所謂事件指的就是View的被手機觸摸後產生的事件MotionEvent,而分發指的就是MotionEvent的傳遞和處理。
下面,我們說一下單手指觸摸事件有哪些

ACTION_DOWN——手指剛觸摸屏幕
ACTION_MOVE——手指在屏幕上移動
ACTION_UP———手指從屏幕上松開的一瞬間

事件講完了,我們接下來說一下分發過程中涉及到的方法

dispatchTouchEvent(MotionEvent ev)
onInterceptTouchEvent(MotionEvent ev)
onTouchEvent(MotionEvent event)

所以事件分發,結合這些代碼就是每一個ACTION皆會觸發那些方法。我們在要做就是根據需求來決定那個事件分發到那層,以及搞清楚為什麼會這樣分發。
接下來,我們通過一個例子來仔細講講這三個方法以及上述三個事件。

簡單的例子了解事件分發

測試的例子如上,我們編寫三個自定義view來演示這個效果,第一個是ViewGroupA,也就是最外層的綠的,第二個是ViewGroupB,也就是中間的藍的,然後是最裡面的黑色的View。XML布局如下:

   <com.byhieg.viewdipatch.custormview.ViewGroupA
        Android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="@android:color/holo_green_light">

        <com.byhieg.viewdipatch.custormview.ViewGroupB
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:background="@android:color/holo_blue_light">
        <com.byhieg.viewdipatch.custormview.ViewTest
            android:layout_width="100dp"
            android:layout_height="100dp" />
        </com.byhieg.viewdipatch.custormview.ViewGroupB>

    </com.byhieg.viewdipatch.custormview.ViewGroupA>

ViewGroupA 裡面放入子View ——ViewGroupB 然後ViewGroupB放入子View-ViewTest
三者的代碼如下:
ViewGroupA

public class ViewGroupA extends ViewGroup{

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

    public ViewGroupA(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ViewGroupA(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }



    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measureChildren(widthMeasureSpec,heightMeasureSpec);

    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();
        for(int i = 0;i < childCount;i++) {
            View child = getChildAt(i);
            child.layout(0,0, Change.dip2px(getContext(),200),Change.dip2px(getContext(),200));

        }
    }

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

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("ViewGroupA","ViewGroupA onTouchEvent" + event.getAction());
        return super.onTouchEvent(event);
    }
}

ViewGroupB:

public class ViewGroupB extends ViewGroup{

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

    public ViewGroupB(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ViewGroupB(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }



    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measureChildren(widthMeasureSpec,heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();
        for(int i = 0;i < childCount;i++) {
            View child = getChildAt(i);
            child.layout(0,0,getMeasuredWidth(),getMeasuredHeight());
        }
    }

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

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("ViewGroupB","ViewGroupB onTouchEvent" + event.getAction());
        return super.onTouchEvent(event);
    }
}

ViewTest

public class ViewTest extends View{

    private Paint paint;

    public ViewTest(Context context) {
        super(context);
        init();
    }

    public ViewTest(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ViewTest(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.BLACK);
        paint.setStrokeWidth(10);
        paint.setStyle(Paint.Style.FILL);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawRect(0,0, Change.dip2px(getContext(),100), Change.dip2px(getContext(),100),paint);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e("ViewTest","View dispatchTouchEvent" + event.getAction());
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("ViewTest","View onTouchEvent" + event.getAction());
        return super.onTouchEvent(event);
    }
}

根據我們寫入的Log,當我們點擊最裡面的ViewTest的時候,我們會看到如下的Log輸出

08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onTouchEvent0

這些方法默認返回時false 然後按照如下傳遞原則:

在事件分發的時候,最外層的ViewGroup首先調用dispatchTouchEvent()方法,然後再執行onInterceptTouchEvent()方法,而View是沒有onInterceptTouchEvent()的方法的,在傳遞的時候,如果這些方法返回的是false,則表示不攔截,事件會下面傳遞。我們在覆寫這些方法的時候,不作處理,默認返回時false,所以我們可以看到事件傳遞到了ViewTest的dispatchTouchEvent(),但注意dispatchTouchEvent()與onInterceptTouchEvent()的區別,如果事件傳遞到了這個View,則dispatchTouchEvent()方法一定會調用,而onInterceptTouchEvent()方法則在dispatchTouchEvent()內部調用,表示是否攔截事件,所以當我們需要攔截的時候一般改寫onInterceptTouchEvent()
在事件處理的時候,則是從分發到了最底層的View開始向上處理,在onTouchEvent(),返回了true,則表示這個View已經處理了 ,不必在向上傳遞,但我們覆寫這些方法的時候,不作處理,默認返回時false,所以繼續向上傳遞,到了最上層的ViewGroup中。這就是我們驗證的結果。

這種結果我們可以用現實中的例子來解釋,這個例子是網上看到了,很生動形象。我們把整個事件傳遞的View看成是一個公司,ViewGroupA是一個總經理,ViewGroupB是一個部長,ViewTest是一個員工。現在來了一個任務,總經理覺得不需要自己處理,返回了false,就傳遞給了下一層部長,部長看了也覺得不需要處理,繼續返回false,傳遞給了底層員工,員工做了做,發現太難了,處理不了,就返回了false,然後上層部長接手,發現確實很難,也處理不了,繼續返回false,傳遞給了總經理。整個事件分發處理就結束了。
現在,又來了一個新任務,我們總經理有了前車之鑒,決定自己處理這件事,不在交給下面的人做,因為給了他們,他們也處理不好,所以他決定自己攔截這個事件,於是在ViewGroupA中的onInterceptTouchEvent()中返回true,查看效果

08-30 08:53:31.125 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 08:53:31.126 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 08:53:31.126 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onTouchEvent0

確實如我們之前所說的,是這樣一個處理流程,ViewGroupA自己弄完了所有事情,隨著事件的變多,總經理終於累倒了,於是他決定把事情分給部長,自己只處理部長處理不了的,於是我們總經理不攔截事件,而是部長攔截事件,我們看看效果

08-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onTouchEvent0
08-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onTouchEvent0

確實,事件分發到了部長,而部長也如願的沒處理好事件,傳遞給了總經理,如此以往,因為處理如此多的事件,總經理的病再也沒好。於是,部長,和底層員工決定好好提高自身水平,不在把事件傳遞給總經理,於是,我們在底層員工ViewTest的onTouchEvent()返回true,表示他已經處理好事情,我們看看效果

08-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent0

這是一部分的Log,後面還有,不過我們先講解上半部分,在這裡,我們看到事件處理到ViewTest就為止了,所以事件處理沒有傳遞到ViewGroupB,當然有些問題,確實底層員工處理不了,於是,我們在ViewGroupB的onTouchEvent()返回true。

08-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent0

這樣,事件確實沒有在傳遞給總經理。底層員工,部長已經足夠勝任工作,總經理因為沒有雜事壓身,身體也逐漸康復,這家公司正常運轉。Happy Ending

更多規則

在view進行事件處理的時候,如果他設置了onTouchListener,那麼onTouchListener中的onTouch()方法將被調用,這時,我們需要根據onTouch()的返回值來決定onTouchEvent會不會調用,如果返回的是false,則表示會繼續調用onTouchEvent,返回的是true,則不會。下面我們驗證一下:返回為true的時候。
在MainActivity中,設置viewTest的事件,代碼如下:

ViewTest view = (ViewTest)findViewById(R.id.viewTest);
        view.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                Log.e("onTouch","我中彈了");
                return true;
            }
        });
08-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/onTouch: 我中彈了

可以看出沒有打印出viewTest的onTouchEvent日志,證明確實沒有調用,現在設置返回為false, onTouchEvent被調用的日志會出現,受限於篇幅,結果就不放出來了。
接下來,我們在ViewTest的onTouchEvent中設置一個點擊監聽,查看一些好玩的東西。首先,我先設置我們的ViewTest為可點擊,viewTest的onTouch返回值也是false,然後我們看一下onTouchEvent代碼:

   @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("ViewTest","View onTouchEvent" + event.getAction());
        Log.e("More","我被調用");
        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.e("ViewTest","原來中彈的不是我");
            }
        });
        return false;
    }

這時點擊我們的viewTest,我們發現他竟然沒有點擊事件的日志。這是因為我們直接返回的false,截斷了後面的點擊事件的觸發,所以決定什麼都不做,采用默認的實現,即

return super.onTouchEvent(event);

神奇的事情發生了,出現了該有的點擊效果。我們查看Log

08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/onTouch: 我中彈了
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent0
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/More: 我被調用
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent1
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent1
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent1
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent1
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent1
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/onTouch: 我中彈了
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent1
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/More: 我被調用
08-30 13:07:04.932 16083-16083/com.byhieg.viewdipatch E/ViewTest: 原來中彈的不是我

這是完整的調用日志,我們可以仔細看一下,會發現出現點擊事件的日志在最後一行,而之前重���出現了ViewGroupA和ViewGroupB的事件分發。是時候揭露出完整的觸發過程了。

當我們手指按下時候,會觸發ACTION_DOWN的事件,這時會進行一系列的事件分發,分發規則如上面所講,當我們手指松開的時候,會觸發ACTION_UP的事件,這同樣會進行一系列的事件分發,最後當這些都弄完之後,會觸發onClick()事件,也就是說onClick()是事件觸發的尾端。一旦前面決定不繼續傳遞事件,則onClick()事件就不會觸發。

所以這裡面有一個概念叫做同一事件序列:指從手指接觸屏幕的那一刻起,到手指離開屏幕的那一刻結束,在這個過程中所產生的一系列事件,這個事件序列以down事件開始,中間含有數個move事件,最終以up事件結束。
簡而言之就是down-> move-> move-> move-> move......->up
而上面我們提到的onclick()如果大家看源碼的話,會發現他是ACTION_UP之後通過performClick()調用的。所以如果view設置了點擊事件並且能向後傳遞,則會最後調用onClick()。
如果仔細看上面的日志,會發現我們的打印日志的代碼是這樣寫的Log.e("ViewTest","View onTouchEvent" + event.getAction());日志最後會出現事件的名字,在日志裡面,由於getAction返回的int型的靜態常量,所以是用0, 1表示,0代表ACTION_DOWN,1代表ACTION_UP,所以通過這個日志,我們可以驗證這個同一事件序列這個過程。前面我放出的日志實際也是不全 我也只是把DOWN的會就放出來,日志後面還有UP的事件,處於篇幅就不全放了而已。
而針對同一事件序列 上面的例子還會說明一個特性

注意,一開始,我們直接將onTouchEvent(MotionEvent event)返回值設置為false,這樣他會截斷後面ACTION_UP事件的觸發,通俗來講,就是傳遞到VIew的DOWN事件,他都沒有消耗他,沒處理好他,返回了false,讓他上層處理,則同一事件序列的MOVE,UP也不會給他。結合我們之前公司的例子,就是這個DOWN事件都辦不好,後面也不會給他其他任務了。

我們驗證一下:

08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/onTouch: 我中彈了
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/More: 我被調用
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onTouchEvent0

這是全部的日志,確實沒有UP事件。

復雜的規則

下面的規則是出自《Android開發藝術探索》,我們對這些規則進行講解,驗證

  1. 同一事件序列是指從手機接觸屏幕的那一刻起,到手指離開屏幕的那一刻結束,在這個過程中所產生的一些列事件,這個事件序列以DOWN事件開始,中間含有數個MOVE事件,最終以UP事件結束
  2. 某個View一旦決定攔截,那麼這一個事件序列都只能有他處理,並且它的onInterceptTouchEvent不會被調用
  3. 正常情況下,一個事件序列只能被一個View攔截且消耗。
  4. 某個View一旦開始處理事件,如果他不消耗ACTION_DOWN,那麼同一事件序列中其他事件不會交給他處理,並且事件將重新交由它的父元素去處理,即父元素的onTouchEvent會被調用
  5. 如果View不消耗除了ACTION_DOWN以外的其他事件,那麼這個點擊事件會消失,此時父元素的onTouchEvent並不會被調用,並且當前View可以持續受到後續的事件,最終這些消失的點擊事件會傳遞給Activity處理
  6. ViewGroup默認不攔截任何事件。
  7. View沒有onInterceptTouchEvent方法,一旦有點擊事件傳遞給他,那麼他的onTouchEvent方法會被調用
  8. View的onTouchEvent默認會消耗事件,除非他是不可點擊的。
  9. View的enable屬性不影響onTouchEvent的默認返回值。哪怕一個View是disable狀態,只要他的clickable或者longClickable有一個true,那麼他的onTouchEvent返回true。
  10. onClick會發生的前提是當前View是可點擊的,並且他收到了down和up事件。

我們分別講解和驗證一下大神總結這些結論。
第一條,之前已經講過了,就不說了
第二條,一旦View決定攔截,則表明這個View要自己上手處理這個事件,那他勢必不會再將事件傳遞下去,自然也不會調用onInterceptTouchEvent
第三條,一般,一個事件序列:down-> move-> move-> move-> move......->up只會交給一個View處理,因為第二條,一旦決定攔截,則表明他要自己上手處理這個。特殊情況會通過onTouchEvent強行傳遞給其他View,這也是書中原話,但至今不清楚怎麼強行傳遞給其他View。
第四條,這句話的意思就是如果DOWN事件返回的是false,則UP事件和MOVE事件也不會用來處理。而且會把事件交給父元素去處理,剛才的日志,已經驗證了這點。
第五條,如果DOWN事件返回的是true,而MOVE事件返回的false,則這個事件會消失,父元素的onTouchEvent並不會調用,我們修改下我們的代碼驗證下:

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.e("ViewTest","View onTouchEvent" + event.getAction());
                return true;
            case MotionEvent.ACTION_MOVE:
                Log.e("ViewTest","View onTouchEvent" + event.getAction());
                return false;
            case MotionEvent.ACTION_UP:
                Log.e("ViewTest","View onTouchEvent" + event.getAction());
                return true;
        }
        return super.onTouchEvent(event);
    }

我們將代碼改成這個樣子,然後運行看日志:

08-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-31 08:09:13.077 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent0
08-31 08:09:13.148 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent2
08-31 08:09:13.148 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent2
08-31 08:09:13.148 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent2
08-31 08:09:13.148 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent2
08-31 08:09:13.149 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent2
08-31 08:09:13.149 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent2
08-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent2
08-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent2
08-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent2
08-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent2
08-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent2
08-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent2
08-31 08:09:13.180 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent2
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent2
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent2
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent2
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent2
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent2
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent1
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent1
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent1
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent1
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent1
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent1

日志很長,因為不小心移動多了,觸發了多個移動事件,我們會發現,滿足了上述的條件,然後我們的父View確實沒有觸發onTouchEvent。斯國一,確實是這個樣子的
第六條和第七條,上文提到過,這個不說了 ,因為ViewGroup的這個方法默認返回是false,默認不攔截。而View沒有攔截方法,所以必須要處理。
第八條和第九條,處理事件的時候,view的onTouchEvent默認返回true,表示自己消耗這件事情,這個和我們上面說的默認返回時false是沖突的,這個是因為他後面補了一個條件,這它默認是不點擊的,在我們上面的例子中,我們用的自定義的ViewTest,這個在沒有引入討論onClick的時候,他就是默認不可點擊的,所以是false,當他可點擊之後,我們在xml中設立了clickable屬性,他的onTouchEvent則表示會消耗事件,返回是true,也可以理解嘛,設立了他可點擊,就是想讓他處理onclick事件。所以,一旦有一個Clickable或者longClickable為true,那麼他的onTouchEvent就返回true。
第十條,onClick觸發的前提是view點擊,並且收到了down和up事件,這個上面也驗證過了。

總結

這十條說完,發現很多東西,我們上文已經提到過,只是歸納的沒有他好。他這十條,總體說來,就是
默認的事件傳遞會到底層的view,根據view的onTouchEvent方法中對DOWN事件的處理不同,會有不同的結果,如果返回了true,表示處理好了,則後續的MOVE,UP事件還會交給他來做,如果此外還設置了Clickable,則表示這個view也會響應onClick,而如果MOVE和UP返回了false,則會調用父View的onTouchEvent,如果返回了false,表示,這個view不堪重任,後續的MOVE和UP也不會給他做。
如果我們什麼都不做,不去主動的更改onTouchEvent的返回結果,則會因為這個View是否能被點擊來決定他的事件處理是否向上傳遞,如果能點擊,則不傳遞,他自己來處理,如果不能點擊,則返回false,向上傳遞。
至此,View事件的分發就到此結束了。後續可能會有根據源碼去分析View分發的過程,這個就看事件啦。

Copyright © Linux教程網 All Rights Reserved