天天看點

android源碼分析之View的事件分發(上)1、View的繼承關系圖2、事件3、View的事件分發

1、View的繼承關系圖

View的繼承關系圖如下:

android源碼分析之View的事件分發(上)1、View的繼承關系圖2、事件3、View的事件分發

其中最重要的子類為ViewGroup,View是所有UI元件的基類,而ViewGroup是容納這些元件的容器,同時它也是繼承于View類。而UI元件的繼承關系如上圖,比較常用的元件類用紅色字型标出。

2、事件

2.1 事件類型

當使用者觸摸螢幕,根據不同的動作會産生不同的按鍵事件,如OnClick, OnLongClick, OnTouchEvent等。每個View會重寫相應的回調方法,而具體的回調方法則是在View類中進行了定義,如OnLongClick見View的源碼如下:

public interface OnLongClickListener {
        /**
         * Called when a view has been clicked and held.
         *
         * @param v The view that was clicked and held.
         *
         * @return true if the callback consumed the long click,
         * false otherwise.
         */
        boolean onLongClick(View v);
    }
           

而具體的事件類型主要有如下三類:

MotionEvent.ACTION_DOWN  //按下View,是所有事件的開始

MotionEvent.ACTION_MOVE  //滑動事件

MotionEvent.ACTION_UP    //與down對應,表示擡起
           

2.2 事件響應機制

1>注冊一個監聽對象。

2>實作監聽對象的監聽事件,即事件分發時的回調方法。

3>當某一觸發事件到來,在觸發事件中通過注冊過的監聽對象,回調注冊對象的響應事件,來完成使用者自定義實作。

注:具體的實作過程見第三節。

3、View的事件分發

3.1 ViewRootImpl的建立過程分析

這裡介紹一個重要的類:ViewRootImpl類

簡單來說,ViewRootImpl相當于是視窗系統中的MVC模型中的Controller,它的主要職責為:

1. 負責為應用程式視窗視圖建立Surface。

2. 配合WindowManagerService來管理系統的應用程式視窗。

3. 負責管理、布局和渲染應用程式視窗視圖的UI。

ViewRootImpl有兩個建立時機:

1>Activity元件在啟動的時候,系統會為它建立視窗對象(Window),同時,系統也會為這個視窗對象建立ViewRootImpl對象。

2>當Activity元件被激活的時候,系統如果發現與它的應用程式視窗視圖對象所關聯的ViewRoot對象還沒有建立,那麼就會先建立這個ViewRoot對象,以便接下來可以将它的UI渲染出來。

ViewRootImpl的建立時序圖如下:

android源碼分析之View的事件分發(上)1、View的繼承關系圖2、事件3、View的事件分發

此處不做詳細分析,由時序圖即可看出大概流程。

3.2 View事件處理對象ViewPostImeInputStage的注冊過程分析

此處先分析ViewRootImpl類的setView方法:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        ...
        res = mWindowSession.addToDisplay(mWindow, mSeq,
                        mWindowAttributes,getHostVisibility(),
                        mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, 
                        mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mInputChannel);
        ...
        // Set up the input pipeline.
        CharSequence counterSuffix = attrs.getTitle();
        mSyntheticInputStage = new SyntheticInputStage();
        InputStage viewPostImeStage = 
            new ViewPostImeInputStage(mSyntheticInputStage);
        InputStage nativePostImeStage = 
            new NativePostImeInputStage(viewPostImeStage,
                        "aq:native-post-ime:" + counterSuffix);
        InputStage earlyPostImeStage = 
            new EarlyPostImeInputStage(nativePostImeStage);
        InputStage imeStage = 
            new ImeInputStage(earlyPostImeStage,
                        "aq:ime:" + counterSuffix);
        InputStage viewPreImeStage = 
            new ViewPreImeInputStage(imeStage);
        InputStage nativePreImeStage = 
            new NativePreImeInputStage(viewPreImeStage,
                        "aq:native-pre-ime:" + counterSuffix);

        mFirstInputStage = nativePreImeStage;
        mFirstPostImeInputStage = earlyPostImeStage;
        ...
           

由以上代碼可知,setView方法中會建立輸入管道,此處采用責任鍊模式,即觸摸事件最終還是要分發給具體的View來處理的,是以最後對事件的處理會由此責任鍊來負責,而此責任鍊在此預先注冊了的InputStage主要有SyntheticInputStage、ViewPostImeInputStage、NativePostImeInputStage、EarlyPostImeInputStage、ImeInputStage、ViewPreImeInputStage、NativePreImeInputStage等。此處隻分析ViewPostImeInputStage,至于其他的自行分析。

本文假設讀者已經預先了解了android的輸入子系統,是以本文不解釋輸入子系統的工作原理。

在Input子系統的native層socket用戶端讀取輸入事件,最終調用InputEventReceiver類子類的onInputEvent()方法,ViewRootImpl的setView方法中初始化了WindowInputEventReceiver對象,它繼承自類InputEventReceiver。

mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
           

其中mInputChannel與Input輸入子系統建立了聯系,是以最終會調用WindowInputEventReceiver的onInputEvent()方法。

3.3 View的事件分發流程分析

在3.2節中分析了處理ViewPostImeInputStage的注冊,以及ViewRootImpl中響應輸入事件的入口,本小節将繼續分析事件的分發流程:

android源碼分析之View的事件分發(上)1、View的繼承關系圖2、事件3、View的事件分發

此時序圖分析了View事件中的OnClick事件的分發過程,由圖可知,ViewRootImpl截取到事件後會分發給ViewPostImeInputStage處理,而ViewPostImeInputStage接着将事件分發給具體的View,此時根據View裡面注冊的監聽事件,調用其回調函數,最終響應我們自定義的操作。至此,View的事件分發分析結束。

繼續閱讀