布局視圖是如何載入到手機視窗上的

來源:互聯網
上載者:User

標籤:android   源碼   布局   視窗   

上一篇文章在講到Handler的時候談到了android的Activity啟動是如何執行到onCreate方法的,Android中Handler原理這篇主要從onCreate方法裡面我們必須要寫的方法setContentView開始,研究布局或者View是如何載入到手機視窗上的。

當在執行到setContentView時,實際上執行的是

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

可以看到實際上是Window類的setContentView方法

private Window mWindow;public Window getWindow() {        return mWindow;}

Window類是一個抽象類別,下面主要是找到他的實作類別。mWindow初始化在

final void attach(……) {        ……        mWindow = PolicyManager.makeNewWindow(this);        mWindow.setCallback(this);        mWindow.getLayoutInflater().setPrivateFactory(this);……    }

Attach方法在main方法中。具體查看上一篇部落格。調用了PolicyManager類的靜態方法makeNewWindow產生Window對象

private static final String POLICY_IMPL_CLASS_NAME =        "com.android.internal.policy.impl.Policy";private static final IPolicy sPolicy;static {        try {            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);            sPolicy = (IPolicy)policyClass.newInstance();        }……    }public static Window makeNewWindow(Context context) {        return sPolicy.makeNewWindow(context);}

可以看到是通過Policy類的makeNewWindow方法得到的Window對象,這裡是通過反射機制擷取的Policy的執行個體。

public Window makeNewWindow(Context context) {        return new PhoneWindow(context);}

可以看到實際上是一個PhoneWindow。那麼根據多態,其實在上面調用的就是PhoneWindow#setContentWindow

public void setContentView(int layoutResID) {        if (mContentParent == null) {            installDecor();//1        } else {            mContentParent.removeAllViews();        }        mLayoutInflater.inflate(layoutResID, mContentParent);//2 將layoutResID填充,他的父View是mContentParent是在installDecor方法裡面mContentParent = generateLayout(mDecor);得到的        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }}

執行到installDecor()

private void installDecor() {    if (mDecor == null) {        mDecor = generateDecor();//產生裝飾視窗,裝飾視窗繼承自FrameLayout        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);        mDecor.setIsRootNamespace(true);        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);        }    }    if (mContentParent == null) {        mContentParent = generateLayout(mDecor);// 產生布局,返回父布局,暫時這樣理解,具體進去看代碼        mDecor.makeOptionalFitsSystemWindows();        mTitleView = (TextView)findViewById(com.android.internal.R.id.title);        ......        }    }}

generateLayout的代碼如下

protected ViewGroup generateLayout(DecorView decor) {        // Apply data from current theme.        TypedArray a = getWindowStyle();// 擷取當前設定的主題......        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {            requestFeature(FEATURE_NO_TITLE);//可以看到平時在AndroidManifest配置的視窗等各其實在代碼裡都是在這裡修改的        } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {            // Don't allow an action bar if there is no title.            requestFeature(FEATURE_ACTION_BAR);        }        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {            requestFeature(FEATURE_ACTION_BAR_OVERLAY);        }......//19-63行根據我們指定的有無標題等各種視窗風格得到對應的預設布局,//這些布局在D:\SoftWare\Java\android4.2-source\frameworks\base\core\res\res\layout        int layoutResource;        int features = getLocalFeatures();        if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {            if (mIsFloating) {                TypedValue res = new TypedValue();                getContext().getTheme().resolveAttribute(                        com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);                layoutResource = res.resourceId;            } else {                layoutResource = com.android.internal.R.layout.screen_title_icons;            }            removeFeature(FEATURE_ACTION_BAR);        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {            layoutResource = com.android.internal.R.layout.screen_progress;        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {            if (mIsFloating) {                TypedValue res = new TypedValue();                getContext().getTheme().resolveAttribute(                        com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);                layoutResource = res.resourceId;            } else {                layoutResource = com.android.internal.R.layout.screen_custom_title;            }            removeFeature(FEATURE_ACTION_BAR);        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {            if (mIsFloating) {                TypedValue res = new TypedValue();                getContext().getTheme().resolveAttribute(                        com.android.internal.R.attr.dialogTitleDecorLayout, res, true);                layoutResource = res.resourceId;            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {                if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {                    layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;                } else {                    layoutResource = com.android.internal.R.layout.screen_action_bar;                }            } else {                layoutResource = com.android.internal.R.layout.screen_title;            }        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;        } else {            layoutResource = com.android.internal.R.layout.screen_simple;        }        mDecor.startChanging();        View in = mLayoutInflater.inflate(layoutResource, null);//根據上面的判斷選擇的layoutResource填充成View        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//調用裝飾視窗的addView方法將上一步產生的View添加到最外層的裝飾視窗        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//ID_ANDROID_CONTENT對應的都是@android:id/content其實是一個FrameLayout        ......        mDecor.finishChanging();        return contentParent;    }

下面是最常用的layoutResource布局檔案他們在D:\SoftWare\Java\android4.2-source\frameworks\base\core\res\res\layout檔案夾下

Screen-title.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:fitsSystemWindows="true">    <!-- Popout bar for action modes -->    <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:layout_width="match_parent"         android:layout_height="?android:attr/windowTitleSize"        style="?android:attr/windowTitleBackgroundStyle">        <TextView android:id="@android:id/title"             style="?android:attr/windowTitleStyle"            android:background="@null"            android:fadingEdge="horizontal"            android:gravity="center_vertical"            android:layout_width="match_parent"            android:layout_height="match_parent" />    </FrameLayout>    <FrameLayout android:id="@android:id/content"        android:layout_width="match_parent"         android:layout_height="0dip"        android:layout_weight="1"        android:foregroundGravity="fill_horizontal|top"        android:foreground="?android:attr/windowContentOverlay" /></LinearLayout>

最外層是線性布局,包含幀布局(包含一個TextView其實就是標題)和幀布局。其實這個就是最常見的樣子。預設產生的程式就是這個樣子。

Screen-simple.xml

<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>

顯而易見這個就是全屏設定時預設載入的布局。

我們可以看到id為content的FrameLayout都存在的,因為他要裝載我們填充的xml

依Screen-title.xml為例,大致是這樣子的


通過以上分析明白了以下幾點:

1. Activity呈現出來的介面其實是一個PhoneWindow類在管理的,這個類中有一個DecorView成員就是最外層的一個容器。

2. 上面這張圖是非常重要的,顯示了視窗的結構。

3. Activity到底是個什麼東西?還真不好說清楚…^_^總之和剛開始的認識是不同的。

4. 遇到不懂得問題就去查看源碼。代碼是最好的老師!









布局視圖是如何載入到手機視窗上的

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.