Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android進階系列5—從LayoutInflater到setContentView的setContentView

Android進階系列5—從LayoutInflater到setContentView的setContentView

編輯:關於Android編程

當我們用XML中寫完一個布局文件,想在某個Activity中顯示的時候,往往通過setContentView方法加載。在上一篇文章中,我們知道如何通過LayoutInflater將一個布局文件加載到指定的父布局中。如果Activity也提供一個布局,那將xml顯示出來想必離不開LayoutInflater。

Activity–setContentView

Activity中調用setContentView的代碼如下:

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

setContentView調用了getWindow方法裡面的setContentView方法。

public Window getWindow() {
     return mWindow;
}

而mWindow初始化為了PhoneWindow的對象實例

mWindow = new PhoneWindow(this);

我們知道,PhoneWindow是抽象類Window的實現類。所以Activity中的setContentView方法其實調用的是PhoneWindow類的setContentView。PhoneWindow屬於framework層。

PhoneWindow–setContentView

再看PhoneWindow中的setContentView方法

 @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

找到關鍵了mLayoutInflater.inflate(layoutResID, mContentParent),設置的布局id,被添加到了mContentParent父布局中。是不是覺得setContentView的機制你已經清楚了呢?如果你清楚了LayoutInflater的加載機制,那setContentView你就清楚了一大半了。但是還有一點不明白,Activity的界面到底由哪些部分組成,mContentParent又是啥。我們接著把代碼看完,想必心中就有譜了。
上述代碼首先是對mContentParent判空,如果空就調用installDecor()。想必mContentParent的初始化就是在該方法體裡面。

installDecor()

private void installDecor() {
     if (mDecor == null) {
          mDecor = generateDecor();
                 mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        if (mContentParent == null) {
            //根據窗口的風格修飾,選擇對應的修飾布局文件,並且將id為content的FrameLayout賦值給mContentParent
            mContentParent = generateLayout(mDecor);
            //......
            //初始化一堆屬性值
        }
    }

可以看到mContentParent的初始化借助了mDecor,mDecor在初始化mContentParent之前借助generateDecor()完成。mDecor是DecorView的對象,DecorView是FrameLayout的子類,如下代碼所示。

    // This is the top-level view of the window, containing the window decor.
    private DecorView mDecor;
    //......
    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
       //......

調用generateLayout創建mContentParent對象之後,就可以調用findViewById生成一些標題、ActionBar對象。那在generateLayout中應該進行了放入了一些布局控件等。打開源碼瞅一瞅。

generateLayout

protected ViewGroup generateLayout(DecorView decor) {
    // Apply data from current theme.
    TypedArray a = getWindowStyle();
    //...Window_windowIsFloating,Window_windowNoTitle,Window_windowActionBar...
    //首先通過WindowStyle中設置的各種屬性,對Window進行requestFeature或者setFlags
    if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
        requestFeature(FEATURE_NO_TITLE);
    }
    //...
    if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
        setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
    }
    //...根據當前sdk的版本確定是否需要menukey
    WindowManager.LayoutParams params = getAttributes();
    //通過a中設置的屬性,設置  params.softInputMode 軟鍵盤的模式;
    //如果當前是浮動Activity,在params中設置FLAG_DIM_BEHIND並記錄dimAmount的值。
    //以及在params.windowAnimations記錄WindowAnimationStyle

    //Inflate the window decor.
    int layoutResource;//將要選擇的窗口根部局
    int features = getLocalFeatures();
    // System.out.println("Features: 0x" + Integer.toHexString(features));
    /*是根據窗口的風格修飾類型為該窗口選擇不同的窗口根布局文件*/
    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;
        }
        // XXX Remove this once action bar supports these features.
        removeFeature(FEATURE_ACTION_BAR);
        // System.out.println("Title Icons!");
    } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
            && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
        // Special case for a window with only a progress bar (and title).
        // XXX Need to have a no-title version of embedded windows.
        layoutResource = com.android.internal.R.layout.screen_progress;
        // System.out.println("Progress!");
    } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
        // Special case for a window with a custom title.
        // If the window is floating, we need a dialog layout
        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;
        }
        // XXX Remove this once action bar supports these features.
        removeFeature(FEATURE_ACTION_BAR);
    } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
        // If no other features and not embedded, only need a title.
        // If the window is floating, we need a dialog layout
        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) {
            layoutResource = com.android.internal.R.layout.screen_action_bar;
        } else {
            layoutResource = com.android.internal.R.layout.screen_title;
        }
        // System.out.println("Title!");
    } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
        layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
    } else {
        // Embedded, so no decoration is needed.
        layoutResource = com.android.internal.R.layout.screen_simple;
        // System.out.println("Simple!");
    }
    //根部局文件inflate到decor中
    View in = mLayoutInflater.inflate(layoutResource, null);
    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    //加入mDecor(布局)中的id為content的View返回給contentParent
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    //...

    return contentParent;
     }
}               

總的來說generateLayout流程就是,mDecor是一個FrameLayout(子類),根據theme選用系統布局文件(一般包含title,actionbar和id 為content的FrameLayout),並inflate為View,加入到mDecor中。而布局文件中包含的id為content的FrameLayout將返回給mContentParent。一旦我們有了mContentParent,就把我們setContentView的布局添加到mContentParent上了。

總結

經過上面的Activity setContentView調用到PhoneWindow setContentView;再利用PhoneWindow中的installDecor初始化mDecor,PhoneWindow的generatelayout完成mDecor對mContentParent的初始化。
給大家看一個例子
AndroidManifest.xml設置如下




    
        ......
    

generateLayout方法中的layoutResource變量值為R.layout.screen_simple,所以我們看下系統這個screen_simple.xml布局文件,如下:


    
    <framelayout android:foreground="?android:attr/windowContentOverlay" android:foregroundgravity="fill_horizontal|top" android:foregroundinsidepadding="false" android:id="@android:id/content" android:layout_height="match_parent" android:layout_width="match_parent">
</framelayout>

布局中,一般會包含ActionBar,TitleBar,和一個id為content的FrameLayout,這個布局是NoTitle的,所以在HierarchyViewer沒有顯現。
再來看下上面這個App的hierarchyviewer圖譜,如下:
\
根部局是DecorView(FrameLayout子類),LinearLayout是根據Theme設置選擇的系統布局文件,並添加到DecorView中,返回其中id為content的FrameLayout給mContentParent,用來承接setContentView傳進來的布局文件。至此大家對setContentView的布局加載流程應該有了一個比較清楚的認識了。一個完整的Activity默認視圖結構如圖
\

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved