深入理解 DecorView

如果你對 Android 有一定了解的話,你一定知道 View 的樹形結(jié)構(gòu),View 的測量、繪制和事件分發(fā)都是從樹的根部逐級遍歷分發(fā)下去的,而這個樹形結(jié)構(gòu)的根部就是我們今天要講的 DecorView。下面是我畫的一張圍繞 DecorView 的層級關(guān)系圖,其中最頂層是我們熟知的 Activity,每個 Activity 會有一個 Window 對象,該 Window 對象包含的就是 DecorView:

DecorView

接下來我們就自上而下,從源碼的角度看看 DecorView 到底是什么。首先從我們最熟悉的一句代碼說起,它就是 Activity 的 setContentView(),我們在 Activity 的 onCreate() 回調(diào)里都會調(diào)用該方法將布局文件設(shè)置給 Activity,那么該方法里面做了什么事情呢?來看下面一段源碼:

public class Activity {

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
    }

}

很簡單的一句話,就是獲取 Activity 的 Window 對象將布局資源設(shè)置給它,Window 是一個抽象類,它的唯一實(shí)現(xiàn)類是 PhoneWindow,所以接下來我們就去看看 PhoneWindow 的 setContentView() 方法做了什么:

public class PhoneWindow extends Window {

    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            // 初始化 DecorView
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        // 解析我們設(shè)置的布局資源并且設(shè)置 mContentParent 為父布局
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }

}

PhoneWindow 的 setContentView() 方法主要做了兩件事情,初始化 DecorView 然后解析我們設(shè)置的布局資源到指定的父布局 mContentParent 中,那么我們先從 installDecor() 方法入手,看下 DecorView 是怎么初始化的,mContentParent 我們留到最后來講:

public class PhoneWindow extends Window {

    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
        }
    }

}

DecorView 的初始化分為兩步,分別是創(chuàng)建 DecorView 和 初始化 DecorView 的布局,其中創(chuàng)建 DecorView 的方法 generateDecor() 很簡單,里面就一句話創(chuàng)建一個新的 DecorView 對象,代碼如下所示:

public class PhoneWindow extends Window {

    protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }

}

你一定想知道 DecorView 是什么東西吧?那么我們就來看看下面的 DecorView 源碼,其實(shí) DecorView 繼承自 FrameLayout,所以它實(shí)際上就是一個 ViewGroup。

private final class DecorView extends FrameLayout {}

知道 DecorView 是一個 ViewGroup 之后,我們繼續(xù)看看它內(nèi)部都裝了什么東西,我們來看 generateLayout() 方法:

public abstract class Window {

    public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

    @Nullable
    public View findViewById(@IdRes int id) {
        return getDecorView().findViewById(id);
    }

}

public class PhoneWindow extends Window implements MenuBuilder.Callback {

    protected ViewGroup generateLayout(DecorView decor) {
        // 此處省去一堆代碼,設(shè)置窗口屬性

        int layoutResource;
        // 此處省去一堆代碼,根據(jù)不同的主題使用不同的布局資源

        // 這里才是重點(diǎn),向 DecorView 添加布局,并且從 DecorView 中查找出 contentParent
        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        // 此處省去一堆代碼設(shè)置窗口背景和標(biāo)題
        
        return contentParent;
    }
    
}

從上面的源碼可以看出實(shí)際上 DecorView 里面包含了一個系統(tǒng)內(nèi)置的布局資源,這個布局資源 layoutResource 會根據(jù)不同主題變化,其中一個資源是 com.android.internal.R.layout.screen_simple,該資源文件里有一個 ID 為 content 的 FrameLayout,它就是我們前面看到的 mContentParent,我們設(shè)置的布局文件就是被解析并添加到 mContentParent 中的:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    
    <ViewStub 
        android:id="@+id/action_mode_bar_stub"
        android:inflatedId="@+id/action_mode_bar"
        android:layout="@layout/action_mode_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
              
    <FrameLayout
        android:id="@android:id/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:foregroundInsidePadding="false"
        android:foregroundGravity="fill_horizontal|top"
        android:foreground="?android:attr/windowContentOverlay" />
        
</LinearLayout>

到此為止,我們對 DecorView 的分析就結(jié)束了,總結(jié)以下幾點(diǎn):

  • DecorView 繼承自 FrameLayout,是一個 ViewGroup
  • DecorView 是 Window / Activity 的最頂級視圖
  • DecorView 是在我們調(diào)用 Activity 的 setContentView() 方法時創(chuàng)建的,期間還會獲取并應(yīng)用我們設(shè)置的窗口屬性,所以在 setContentView() 之前設(shè)置的窗口屬性才能生效
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,940評論 25 709
  • Android控件架構(gòu)與自定義控件(一) (本文并非原創(chuàng)文章,整理摘抄方便自己查看,原文地址為Android控件架...
    b5e7a6386c84閱讀 1,046評論 0 6
  • View的繪制和事件處理是兩個重要的主題,上一篇《圖解 Android事件分發(fā)機(jī)制》已經(jīng)把事件的分發(fā)機(jī)制講得比較詳...
    Kelin閱讀 121,417評論 100 846
  • 要努力
    不絆閱讀 181評論 0 0
  • 站在天底下 拿出來晾曬 破碎殘存的些許 灰白的東山欲念瘋長 單薄的西河雜念肆意 英國人建的水塔 標(biāo)志了滄桑的回眸 ...
    營州布衣閱讀 251評論 4 10

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