Android UI繪製分析(一)-解析佈局資源,完成佈局

本文源碼基於 Android sdk 26, 爲了邏輯清晰,省略了無關代碼,不排除後期從新加上相關代碼bash

當用戶在activity中 調用setContentView 方法時,將本身的佈局id 設置到activity中。而activity經過內部持有的window對象 來設置。ide

/**
     * Set the activity content from a layout resource.  The resource will be  inflated, 
     * adding all top-level views to the activity.

     * @param layoutResID Resource ID to be inflated.
     */
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
複製代碼

getWindow()方法 返回 內部持有的window對象,其具體實例是PhoneWindow。佈局

mWindow = new PhoneWindow(this, window, activityConfigCallback);
複製代碼

顯然,咱們須要看看phoneWindow中setContentView作了什麼。ui

@Override
    public void setContentView(int layoutResID) {

        if (mContentParent == null) {
            installDecor();
        } 
        mLayoutInflater.inflate(layoutResID, mContentParent);
        mContentParent.requestApplyInsets();
    }
複製代碼

mContentParent 實際上就是一個ViewGroup, setContentView方法實際上就把設置的佈局資源解析成View後,再添加到 mContentParent中,完成整個佈局。so,這個mContentParent從何而來,this

根據Activity主題配置,建立DecorView

phoneWindow的setContentView 方法中,首先調用installDecor(),初始化DecorView , 和mContentParent。spa

private void installDecor() {
        if (mDecor == null) {
            //建立 DecorView
            mDecor = generateDecor(-1);
        } 
        if (mContentParent == null) {
           //解析mContetnParent
            mContentParent = generateLayout(mDecor);
        }
}

複製代碼

而DecorView 本質上就是一個FrameLayout。code

public class DecorView extends FrameLayout
複製代碼

咱們先看如何初始化DecorView。cdn

protected DecorView generateDecor(int featureId) {
        return new DecorView(context, featureId, this, getAttributes());
    }
複製代碼

嗯,很簡單。 咱們再看如何初始化 mContentView對象

protected ViewGroup generateLayout(DecorView decor) {
        int layoutResource;
        int features = getLocalFeatures();
        //根據activity主題中設置的參數 獲取不一樣的系統內置的佈局資源id
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;

        }  else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = R.layout.screen_simple_overlay_action_mode;

        } else {
            layoutResource = R.layout.screen_simple;
        }
        //將獲取到佈局資源id 添加到mDecor中
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        //系統內置所有佈局資源中 都會一個id爲ID_ANDROID_CONTENT 的 ViewGroup,獲取到並賦值給contentParent
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        return contentParent;
    }

複製代碼

解析用戶設置的view,並添加到DecorView中

在上面方法中,第一步,會根據當前activity的主題信息 加載不一樣的佈局資源,並添加到DecorView中。 第二步,從佈局資源中獲取 id爲 ID_ANDROID_CONTENT 的view 並賦值給contentParent 返回,這個view即是上文中 mContentParent,在以後,會將用戶本身的佈局 添加到mContentParent中,從而達到界面顯示的目的。blog

/**
     *DecorView 直接解析根據主題配置 獲取到的系統佈局id,並添加到本身身上
     */
    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        final View root = inflater.inflate(layoutResource, null);
        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }
複製代碼

因此最終一個Activity界面顯示的佈局應該是這樣的:

activity界面顯示佈局視圖.png

總結

因此一個View是如何被添加到屏幕窗口上的呢。

  1. 建立頂層佈局容器DecorView
  2. 在頂層佈局中加載基礎佈局ViewGroup
  3. 將ContentView添加到基礎佈局中的FrameLayout中
相關文章
相關標籤/搜索