Android開發(fā)之事件分發(fā)機制

Android的兩種控件View和ViewGroup

ViewGroup可以有子控件,如:LinearLayout,ListView
View也就是普通控件,沒有子布局,如:TextView Button

事件分發(fā)需要了解的三個方法

ViewGroup的dispatchTouchEvent, onInterceptTouchEvent和onTouchEvent

1)public boolean dispatchTouchEvent(MotionEvent ev) 這個方法用來分發(fā)TouchEvent
2)public boolean onInterceptTouchEvent(MotionEvent ev) 這個方法用來攔截TouchEvent
3)public boolean onTouchEvent(MotionEvent ev) 這個方法用來處理TouchEvent

View的dispatchTouchEvent和onTouchEvent

1)public boolean dispatchTouchEvent(MotionEvent ev) 這個方法用來分發(fā)TouchEvent
2)public boolean onTouchEvent(MotionEvent ev) 這個方法用來處理TouchEvent

對于一個ViewGroup來說

點擊事件首先是調(diào)用dispatchTouchEvent。
如果這這個ViewGroup的onInterceptTouchEvent返回true就表示它要攔截此事件,接著這個ViewGroup的onTouchEvent就會被調(diào)用。
如果這個ViweGroup的onInterceptTouch返回false,那么就會繼續(xù)向下調(diào)用子控件的dipatchTouchEvent。

對于View來說
事件傳遞機制有兩個函數(shù):dispatchTouchEvent負責(zé)分發(fā)事件,在dispatch***里又會調(diào)用onTouchEvent表示執(zhí)行事件。
或者說消費事件,結(jié)合源碼分析其流程。事件傳遞的入口是View的dispatchTouchEvent()函數(shù).

1、事件入口是dispatchTouchEvent(),它會先執(zhí)行注冊的onTouch監(jiān)聽,如果一切順利的話,接著執(zhí)行onTouchEvent,在onTouchEvent里會執(zhí)行onClick監(jiān)聽。
2、無論是dispatchTouchEvent還是onTouchEvent,如果返回true表示這個事件已經(jīng)被消費、處理了,不再往下傳了。在dispathTouchEvent的源碼里可以看到,如果onTouchEvent返回了true,那么它也返回true。如果dispatch***在執(zhí)行onTouch監(jiān)聽的時候,onTouch返回了true,那么它也返回true,這個事件提前被onTouch消費掉了。就不再執(zhí)行onTouchEvent了,更別說onClick監(jiān)聽了。
3、我們通常在onTouch監(jiān)聽了設(shè)置圖片一旦被觸摸就改變它的背景、透明度之類的,這個onTouch表示事件的時機。而在onClick監(jiān)聽了去具體干某些事。
4、什么時候不會走onTouchEvent呢?當(dāng)重寫dispatchTouchEvent,不走super.dispatchTouchEvent直接返回false,它就不會走onTouchEvent。當(dāng)然這樣做是違反android架構(gòu)常理的,一般的dispatchTouchEvent是不建議重寫的。不過通過這個案例我們可以總結(jié)出這么一個結(jié)論.
5、在事件到達view的時候,先走dispatchTouchEvent,在系統(tǒng)的dispatchTouchEvent中它會調(diào)用該view的Ontouch方法如果此onTouch方法的down事件里返回true,則dispatchTouchEvent方法也返回true,且把以后的move事件,up事件都傳給onTouch。之后的move事件及up事件的返回值,onTouch返回什么dispatchTouchEvent也返回什么。相反如果傳第一個down事件給ontouch的時候,ontouch返回的是false,從此事件不再會傳過來,也就是不會走dispatchTouchEvent。更不會走ontouchevent,相反如果傳第一個down事件給ontouch的時候,ontouch返回的是false,從此事件不再會傳過來,也就是不會走dispatchTouchEvent。更不會走ontouchevent

下面結(jié)合源碼來分析

自定義控件ViewGropu為MyLinearLayout

package com.project1.View;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

/**
 * Created by Administrator on 2017/1/5.
 */
public class MyLinearLayout extends LinearLayout {
    private final String TAG = "MyLinearLayout";
    public MyLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        Log.d(TAG, TAG);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "dispatchTouchEvent action:ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "dispatchTouchEvent action:ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "dispatchTouchEvent action:ACTION_UP");
                break;
            case MotionEvent.ACTION_CANCEL:
                Log.d(TAG, "dispatchTouchEvent action:ACTION_CANCEL");
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "onInterceptTouchEvent action:ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "onInterceptTouchEvent action:ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "onInterceptTouchEvent action:ACTION_UP");
                break;
            case MotionEvent.ACTION_CANCEL:
                Log.d(TAG, "onInterceptTouchEvent action:ACTION_CANCEL");
                break;
        }
        return true;

    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "---onTouchEvent action:ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "---onTouchEvent action:ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "---onTouchEvent action:ACTION_UP");
                break;
            case MotionEvent.ACTION_CANCEL:
                Log.d(TAG, "---onTouchEvent action:ACTION_CANCEL");
                break;
        }
        return false;
    }
}

自定義控件View為MyTextView

package com.project1.View;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.TextView;

public class MyTextView extends TextView {
    private final String TAG = "MyTextView";
    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    @Override 
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction(); 
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d(TAG, "dispatchTouchEvent action:ACTION_DOWN");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d(TAG, "dispatchTouchEvent action:ACTION_MOVE");
            break;
        case MotionEvent.ACTION_UP:
            Log.d(TAG, "dispatchTouchEvent action:ACTION_UP");
            break;
        case MotionEvent.ACTION_CANCEL:
            Log.d(TAG, "onTouchEvent action:ACTION_CANCEL");
            break;
        }
        return super.dispatchTouchEvent(ev); 
    } 


    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
        int action = ev.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            Log.d(TAG, "---onTouchEvent action:ACTION_DOWN");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.d(TAG, "---onTouchEvent action:ACTION_MOVE");
            break;
        case MotionEvent.ACTION_UP:
            Log.d(TAG, "---onTouchEvent action:ACTION_UP");
            break;
        case MotionEvent.ACTION_CANCEL:
            Log.d(TAG, "---onTouchEvent action:ACTION_CANCEL");
            break;
        }
        return false;
    }
 
}  

當(dāng)MyLinearLayout中的dispatchTouchEvent為true時

Paste_Image.png
當(dāng)MyLinearLayout
  • onInterceptTouchEvent為false,
  • onTouchEvent為true
當(dāng)MyTextView中
  • onTouchEvent為true


    onTouchEvent完全由MyTextView處理

當(dāng)MyLinearLayout中

  • onInterceptTouchEvent為false
  • onTouchEvent為false

MyTextView中

  • onTouchEvent為true


    onTouchEvent完全由MyTextView處理,基本和上面一致

當(dāng)MyLinearLayout中

  • onInterceptTouchEvent為false
  • onTouchEvent為true

當(dāng)MyTextView中

  • onTouchEvent為false


    MyTextView只處理了ACTION_DOWN事件其他完全由MyLinearLayout的onTouchEvent處理

當(dāng)MyLinearLayout中

  • onInterceptTouchEvent為false
  • onTouchEvent為false

當(dāng)MyTextView中

  • onTouchEvent為false


    都只處理了ACTION_DOWN事件

****onInterceptTouchEvent為ture(時)無需理會子控件的値****

當(dāng)MyLinearLayout中

  • onInterceptTouchEvent為ture
  • onTouchEvent為true


    MyLinearLayout自己處理所有的onTouchEvent事件

當(dāng)MyLinearlayout中

  • onInterceptTouchEvent為true
  • onTouchEvent為false;


    MyLinearLayout只處理了ACTION_DOWN事件

如果想要父控件把事件交給子控件處理onInterceptTouchEvent不能為true,否則子控件不能處理任何事件。

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

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

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