1. 程式人生 > >android原始碼分析之View的事件分發(上)

android原始碼分析之View的事件分發(上)

1、View的繼承關係圖

View的繼承關係圖如下:
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的建立時序圖如下:
這裡寫圖片描述
此處不做詳細分析,由時序圖即可看出大概流程。

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中響應輸入事件的入口,本小節將繼續分析事件的分發流程:
這裡寫圖片描述
此時序圖分析了View事件中的OnClick事件的分發過程,由圖可知,ViewRootImpl擷取到事件後會分發給ViewPostImeInputStage處理,而ViewPostImeInputStage接著將事件分發給具體的View,此時根據View裡面註冊的監聽事件,呼叫其回撥函式,最終響應我們自定義的操作。至此,View的事件分發分析結束。