1. 程式人生 > >Android事件分發

Android事件分發

在android 界一直流傳著一句話,誰能掌握view,誰就能掌握android(這是我自己裝比說的)

不過view 這一塊在android中 確實是 由普通android 向高階android 進發的必須要走的一步路,我準備 一邊學,一邊記錄自己  學習view 的歷程,今天 先來講講 view 的事件分發機制--

先看一張圖:

這張圖大家應該都很熟悉, 所謂的事件分發機制 無非就是 view的 相關觸控事件的傳遞過程,

比如我點選一下螢幕,圖中各view 的 觸控事件分別是如何 響應的,分別有哪些事件會得到響應,

廢話不多話,程式碼擼起來:

Viewgroup:A

public class OneView extends LinearLayout {


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

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



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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.e("TEST", "OneView onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("TEST", "OneView onTouchEvent");
        return super.onTouchEvent(event);
    }
}

Viewgroup:B

public class TwoView extends LinearLayout {


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

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



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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.e("TEST", "TwoView onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("TEST", "TwoView onTouchEvent");
        return super.onTouchEvent(event);
    }
}

View:C

public class ThreeView extends View {

    private Paint paint;
    private int Withs;
    private int Hiths;

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

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


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        Withs = w / 2;
        Hiths = h / 2;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint = new Paint();

        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.FILL);  //設定畫筆模式為填充
        paint.setStrokeWidth(10f);         //設定畫筆寬度為10px
        paint.setTextSize(30);
        canvas.drawText("我是帥比", Withs, Hiths, paint);

    }

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("TEST", "ThreeView onTouchEvent");
        return super.onTouchEvent(event);
    }
}

當我點金 C 的時候,事件的呼叫log分別為:

很清楚 ,呼叫流程依次為: 最外層view A:dispatchTouchEvent---> onInterceptTouchEvent  ---->..........

如果這個點選事件從最外層到最裡層都沒有被 攔截消費(即return true),則會繼續從最底層的view 的 onTouchEvent 事件 往上層的 onTouchEvent 事件傳遞,直到被消費,如果 上層也沒有消費,則此次點選事件就被流放,說到這裡,其實 這並不是 所有的佈局,在我們 肉眼到的佈局之外,還包著有另外的佈局,比如 標題欄什麼的,那麼這些view 在哪裡:

如下圖:

Rootview 相當於我們佈局的根佈局 LinearLayout,在其之上 的View結構中莫名多出來的兩個東西,PhoneWindow 和 DecorView ,這兩個我們並沒有在Layout檔案中定義過,但是為什麼會存在呢?

仔細觀察上面的 layout 檔案,你會發現一個問題,我在 layout 檔案中的最頂層 View(Group) 的大小並不是填滿父窗體的,留下了大量的空白區域,由於我們的手機螢幕不能透明,所以這些空白區域肯定要顯示一些東西,那麼應該顯示什麼呢?

有過安卓開發經驗的都知道,螢幕上沒有View遮擋的部分會顯示主題的顏色。不僅如此,最上面的一個標題欄也沒有在 layout 檔案中,這個標題欄又是顯示在哪裡的呢?

你沒有猜錯,這個主題顏色和標題欄等內容就是顯示在DecorView中的。

現在知道 DecorView 是幹什麼的了,那麼PhoneWindow 又有什麼作用?

要了解 PhoneWindow 是幹啥的,首先要了解啥是 Window ,看官方說明:

Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.

簡單來說,Window是一個抽象類,是所有檢視的最頂層容器,檢視的外觀和行為都歸他管,不論是背景顯示,標題欄還是事件處理都是他管理的範疇,它其實就像是View界的太上皇(雖然能管的事情看似很多,但是沒實權,因為抽象類不能直接使用)。

而 PhoneWindow 作為 Window 的唯一親兒子(唯一實現類),自然就是 View 界的皇帝了,PhoneWindow 的權利可是非常大大,不過對於我們來說用處並不大,因為皇帝平時都是躲在深宮裡面的,雖然偶爾用特殊方法能見上一面,但想要完全指揮 PhoneWindow 為你工作是很困難的。

而上面說的 DecorView 是 PhoneWindow 的一個內部類,其職位相當於小太監,就是跟在 PhoneWindow 身邊專業為 PhoneWindow 服務的,除了自己要幹活之外,也負責訊息的傳遞,PhoneWindow 的指示通過 DecorView 傳遞給下面的 View,而下面 View 的資訊也通過 DecorView 回傳給 PhoneWindow。

這些額外的就就到這裡,有興趣的可以自行谷歌,我們繼續 我們的事件分發機制,

如最上面在沒有任何攔截的時候,事件分發可謂是一馬平川,那麼如果我們想搞點事情呢,

比如,我在B中攔截掉這個點選事件,我們看看 事件是如何分發的,

如圖我在 onInterceptTouchEvent 中直接攔截了點選事件,那麼現在的傳遞是怎麼樣呢? 

如下:

果然 事件沒有繼續往下傳遞到C,直接觸發了 B view 的onTouchEvent,由於 B中的 onTouchEvent 也沒有消費此次點選事件,故會繼續往上層的 onTouchEvent 事件傳遞,

如果我們 在B 的onTouchEvent 中 消費了此次事件呢,又會如何呢,

我們把 如圖:

onTouchEvent 的返回改為true,表示消費此次點選事件,結果呼叫方法如下:

onTouchEvent 走了三次,為什麼會走三次,因為 

onTouchEvent有3個回掉,分別為 

MotionEvent.ACTION_DOWN:

MotionEvent.ACTION_MOVE:

MotionEvent.ACTION_UP:

按下,移動,擡手,

講到這裡,我想大家基本明白事件的分發機制是如何的了