Android GUI之Activity、Window、View,androidactivity
相信大家在接觸Android之初就已經知道了Activity中的setContentView方法的作用了,很明顯此方法是用於為Activity填充相應的布局的。那麼,Activity是如何將填充的布局繪製出來的呢?實際上Activity將View的繪製與顯示交給了Window對象來處理,下面我們通過源碼來進行跟蹤分析。
Activity的源碼如下,只給出我們關注的部分:
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback { …… …… private Window mWindow; private WindowManager mWindowManager; …… /** * Retrieve the current {@link android.view.Window} for the activity. * This can be used to directly access parts of the Window API that * are not available through Activity/Screen. * * @return Window The current window, or null if the activity is not * visual. */ public Window getWindow() { return mWindow; } …… /** * 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. * * @see #setContentView(android.view.View) * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) */ public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); } /** * Set the activity content to an explicit view. This view is placed * directly into the activity's view hierarchy. It can itself be a complex * view hierarchy. When calling this method, the layout parameters of the * specified view are ignored. Both the width and the height of the view are * set by default to {@link ViewGroup.LayoutParams#MATCH_PARENT}. To use * your own layout parameters, invoke * {@link #setContentView(android.view.View,android.view.ViewGroup.LayoutParams)} * instead. * * @param view The desired content to display. * * @see #setContentView(int) * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) */ public void setContentView(View view) { getWindow().setContentView(view); initWindowDecorActionBar(); } final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, IVoiceInteractor voiceInteractor) { attachBaseContext(context); mFragments.attachActivity(this, mContainer, null); mWindow = PolicyManager.makeNewWindow(this); …… }……}
PolicyManager的部分源碼:
public final class PolicyManager { ……private static final IPolicy sPolicy; static { // Pull in the actual implementation of the policy at run-time …… sPolicy = (IPolicy)policyClass.newInstance(); …… } // Cannot instantiate this class private PolicyManager() {} // The static methods to spawn new policy-specific objects public static Window makeNewWindow(Context context) { return sPolicy.makeNewWindow(context); } ……}
Policy的部分源碼:
public class Policy implements IPolicy { …… public Window makeNewWindow(Context context) { return new PhoneWindow(context);}……}
從給出的源碼我們可以看到,Activity內部含有一個Window類型的對象mWindow,當我們調用setContentView方法時,實際上是委託給了Window對象進行處理。Window本身是一個抽象類別,它描述了android視窗的基本屬性和行為特徵。在activity的attach方法中通過mWindow = PolicyManager.makeNewWindow(this)建立了Window對象。通過追蹤代碼可知, PolicyManager.makeNewWindow(this)實際上是調用Policy中的makeNewWindow方法,在此方法中建立了一個PhoneWindow對象。而PhoneWindow正是Window的子類。他們的關係圖如下:
繼續追蹤源碼,PhoneWindow對Window的抽象方法setContentView(int layoutResId)進行了實現,具體源碼如下:
@Override public void setContentView(int layoutResID) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens. if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); } final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
在這個方法中我們可以看到首先對mContentParent進行了判斷,如果為空白的話則調用installDecor方法,通過hasFeature判斷window是否具備某些特徵,如果視窗不含有FEATURE_CONTENT_TRANSITIONS特徵,則清空mContentParent中的所有子項目,為後面載入布局檔案到mContentParent中做好準備。通過後面的判斷,我們也可以看出無論走那個分支,其實都是對mContentParent布局內容做了更新。由此我們可以推斷出mContentParent其實就是我們自己的布局的存放容器,它在PhoneWindow中定義如下:
// This is the view in which the window contents are placed. It is either // mDecor itself, or a child of mDecor where the contents go. private ViewGroup mContentParent;
那麼mContentParent是在哪裡被建立的呢,很顯然是在方法installDecor中,方法installDecor的關鍵代碼如下:
private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); …… } if (mContentParent == null) {mContentParent = generateLayout(mDecor); …… }}
在這個方法中,我們可以看到,首先對mDecor進行判斷,如果為空白在調用generateDecor方法產生mDecor對象,那麼mDecor對象是什麼呢?通過查看代碼,可以知道mDecor的類型為DecorView,此類型是定義在PhoneWindow中的一個內部類,它繼承了FrameLayout。緊接著判斷mContentParent是否為空白,為空白則調用generateLayout並通過傳入參數mDecor產生了mContentParent對象。在這個方法中通過應用的主題、視窗特徵等來確定使用的布局資源並將使用的布局添加mDecor中,而這些布局中都會含有一個id為content的ViewGroup(FrameLayout),此ViewGroup正是mContentParent,方法關鍵代碼如下:
protected ViewGroup generateLayout(DecorView decor) { …… View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); mContentRoot = (ViewGroup) in; ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); …… return contentParent; }
由此我們可以確定,view的顯示處理順序為Activity->PhoneWindow->DecorView->ViewGroup(mContentView)->自訂的View(布局)。
Activity中顯示視圖的階層,具體如下:
疑問諮詢或技術交流,請加入官方QQ群: (452379712)
作者:傑瑞教育
出處:http://blog.csdn.net/jerehedu/
本文著作權歸煙台傑瑞教育科技有限公司和CSDN共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文串連,否則保留追究法律責任的權利。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。