Android 事件攔截機制一種粗鄙的解釋

  對於Android事件攔截機制,相信對於大多數Android初學者是一個抓耳撓腮難於理解的問題。其實理解這個問題並不困難。android

  首先,你的明白事件攔截機制究竟是怎麼一回事?這裏說的事件攔截機制,指的是對觸摸事件的攔截機制。那何爲觸摸事件?所謂的觸摸事件,就是指系統捕獲的觸摸屏幕所產生的事件。當咱們點擊按鈕時候,此時其實就產生了三個事件。按鈕按下,這是事件之一;若是你不當心滑動了一點兒,這是事件之二;若是你擡起,這是事件之三。Android爲咱們這個觸摸事件封裝了一個類——MotionEvent。在OnTouchEvent事件中,可以很是方便的監聽這三個事件。架構

  既然,可以監聽這個觸摸事件,那麼何來事件攔截之說了。那麼,接下來,請同窗們設想這樣一個場景好嗎?一個View放在一個ViewGroup裏面,這個父ViewGroup控件又放在另一個VIewGroup裏面,甚至還能夠繼續嵌套,這樣子子孫孫無窮盡彥。那麼問題來了呀?可觸摸事件這裏只有一個呀,我到底把他給誰。爹爹ViewGroup與子View都想處理這個觸摸事件了,因而"事件攔截"這個很霸氣的名字就應運而生。ide

  要理解這個事件攔截機制,我這裏須要置身於一個設身處地的場景。就比如你所在一個公司,有一個CEO,CEO下面有總監,總監下面是經理,經理下面有個苦逼的你。此時,來了一項任務之後,CEO把他分配總監,總監分配給經理,經理就把他交給你。這樣任務上傳下達的流程,就與事件分發與攔截流程蠻像了。spa

  爲了事件更好的理解這個案例,我這裏就用控件結構模擬這樣的組織結構,來較深刻的講解事件攔截機制。日誌

  咱們看一下這個控件UI架構圖:code

  怎麼在判斷是父容器與子控件發生了事件攔截,在父容器(ViewGroup)中監聽OnTouchEvent()事件,DispatchEvent()事件與OninterceptEvent()事件,在子View監聽OnTouchEvent()事件與DispatchEvent()事件。怎麼看這些事件進行調用了,咱們在每一個事件打印相應的日誌,就ok了。好好好,有了這樣大致一個思路,咱們就能夠上源代碼了。blog

  子View源代碼以下:事件

public class MyView extends View {

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

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

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

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

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

}

  父ViewGroup源代碼以下:get

public class MyViewGroupC extends LinearLayout {

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

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

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

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

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

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

  祖ViewGroup的源代碼以下:io

public class MyViewGroupB extends LinearLayout {

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

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

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

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

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

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

  曾祖ViewGroup控件的源代碼以下:

public class MyViewGroupA extends LinearLayout {

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

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

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

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

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

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

  咱們在每一個要監聽事件裏面都埋下了點。

  咱們運行程序,看每一個控件的事件執行的前後順序:

 

  咱們觸摸子View時候,發現越頂層父控件控件DispatchEvent,onInterceptTouchEvent事件越先執行。當裏層OnTouchEvent方法執行徹底,而後由裏到外執行OnTouchEvent方法。執行流程圖是這樣的:

  咱們稍微修改一會兒控件的代碼,在onTouchEvent方法中返回true的話,此時運行效果就是這樣的了。

 

 

  咱們,就能夠得出來這樣的結論,若是是return true 之後,就是把該事件進行截取,要不向下傳遞的事件。這就是android事件攔截的本質。

  return false——事件聽任自流,該傳遞的就進行傳遞,return true——本身作了,不麻煩別人了,事件截獲,不進行傳遞了。

  這就是我對事件攔截機制的總結,懇請你們吐槽。

相關文章
相關標籤/搜索