Android,androidsdk

來源:互聯網
上載者:User

Android,androidsdk
DecorView

開發中,通常都是在onCreate()中調用setContentView(R.layout.custom_layout)來實現想要的頁面配置。頁面都是依附在視窗之上的,而DecorView即是視窗最頂層的視圖。Android frameworks中,與視窗視圖處理相關的類,主要是Window及其實作類別PhoneWindow

public class PhoneWindow extends Window implements MenuBuilder.Callback {    //...  //視窗頂層View   private DecorView mDecor;     //所有自訂View的根View, id="@android:id/content"   private ViewGroup mContentParent;  //...}

DecorView其實是PhoneWindow中的一個內部類,本質上也是一個View,其只是擴充了FrameLayout的實現

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker
添加至視窗流程

public void setContentView(int layoutResID) {     //getWindow()擷取的即是PhoneWindow對象  getWindow().setContentView(layoutResID); }

看一下window類

public abstract class Window {        //...      //指定Activity視窗的風格類型      public static final int FEATURE_NO_TITLE = 1;      public static final int FEATURE_INDETERMINATE_PROGRESS = 5;            //設定布局檔案      public abstract void setContentView(int layoutResID);        public abstract void setContentView(View view);        //請求指定Activity視窗的風格類型      public boolean requestFeature(int featureId) {          final int flag = 1<<featureId;          mFeatures |= flag;          mLocalFeatures |= mContainer != null ? (flag&~mContainer.mFeatures) : flag;          return (mFeatures&flag) != 0;      }          //...  }
  • PhoneWindow執行setContentView(int layoutResource)

PhoneWindow該類繼承於Window類,是Window類的具體實現,即我們可以通過該類具體去繪製視窗。並且,該類內部包含了一個DecorView對象,該DectorView對象是所有應用視窗(Activity介面)的根View。 簡而言之,PhoneWindow類是把一個FrameLayout類即DecorView對象進行一定的封裝,將它作為應用視窗的根View,並提供一組通用的視窗操作介面。

public void setContentView(int layoutResID) {  //初始,mContentParent為空白  if (mContentParent == null) {    installDecor();  } else {    mContentParent.removeAllViews();  }  //inflate自訂layout, 並將mContentParent作為其根視圖  mLayoutInflater.inflate(layoutResID, mContentParent);      //...}

該方法根據首先判斷是否已經由setContentView()了擷取mContentParent即View對象, 即是否是第一次調用該PhoneWindow對象setContentView()方法。如果是第一次調用,則調用installDecor()方法,否則,移除該mContentParent內所有的所有子View。最後將我們的資源檔通過LayoutInflater對象轉換為View樹,並且添加至mContentParent視圖中(在應用程式裡,我們可以多次調用setContentView()來顯示我們的介面。)。

  • PhoneWindow.installDecor()

 

private void installDecor() {   if (mDecor == null) {     //new一個DecorView     mDecor = generateDecor();     mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);     mDecor.setIsRootNamespace(true);     if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {       mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);     }   }   if (mContentParent == null) {     //這一步會設定視窗的修飾檔案,並將id為ID_ANDROID_CONTENT的view find出來作為傳回值賦值給mContentParent     mContentParent = generateLayout(mDecor);        //...}

 

  • PhoneWindow.generateLayout(DecorView decor)

 

protected ViewGroup generateLayout(DecorView decor) {  //1,擷取<Application android:theme=""/>, <Activity/>節點指定的themes或者代碼requestWindowFeature()中指定的Features, 並設定  TypedArray a = getWindowStyle();  //...    //2,擷取視窗Features, 設定相應的修飾布局檔案,這些xml檔案位於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;  //...    mDecor.startChanging();  //3, 將上面選定的布局檔案inflate為View樹,添加到decorView中  View in = mLayoutInflater.inflate(layoutResource, null);  decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));  //將視窗修飾布局檔案中id="@android:id/content"的View賦值給mContentParent, 後續自訂的view/layout都將是其子View  ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);  if (contentParent == null) {    throw new RuntimeException("Window couldn't find content container view");  }  //...}

該方法會做如下事情:

根據視窗的風格修飾類型為該視窗選擇不同的視窗布局檔案(根視圖)。這些視窗修飾布局檔案指定一個用來存放Activity自訂布局檔案的ViewGroup視圖,一般為FrameLayout 其id 為: android:id="@android:id/content"。

例如視窗修飾類型包括FullScreen(全屏)、NoTitleBar(不含標題列)等。選定視窗修飾類型有兩種:

           ①指定requestFeature()指定視窗修飾符,PhoneWindow對象調用getLocalFeature()方法擷取值;

           ②為我們的Activity配置相應屬性,即android:theme=“”,PhoneWindow對象調用getWindowStyle()方法擷取值。

舉例如下,隱藏標題列有如下方法:

requestWindowFeature(Window.FEATURE_NO_TITLE);

或者為Activity配置xml屬性:

android:theme="@android:style/Theme.NoTitleBar"

因此,在Activity中必須在setContentView之前調用requestFeature()方法。

確定好視窗風格之後,選定該風格對應的布局檔案,這些布局檔案位於 frameworks/base/core/res/layout/  ,

典型的視窗布局檔案有:

R.layout.dialog_titile_icons                          R.layout.screen_title_iconsR.layout.screen_progress                             R.layout.dialog_custom_titleR.layout.dialog_title   R.layout.screen_title         // 最常用的Activity視窗修飾布局檔案R.layout.screen_simple    //全屏的Activity視窗布局檔案
  • 最後頁面中設定的自訂layout會被添加到mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);

整個過程主要是如何把Activity的布局檔案添加至視窗裡,上面的過程可以概括為:

最後,當AMS(ActivityManagerService)準備resume一個Activity時,會回調該Activity的handleResumeActivity()方法,該方法會調用Activity的makeVisible方法 ,顯示我們剛才建立的mDecor 視圖族。

//系統resume一個Activity時,調用此方法  final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {      ActivityRecord r = performResumeActivity(token, clearHide);      //...       if (r.activity.mVisibleFromClient) {           r.activity.makeVisible();       }  }
void makeVisible() {      if (!mWindowAdded) {          ViewManager wm = getWindowManager();   // 擷取WindowManager對象          wm.addView(mDecor, getWindow().getAttributes());          mWindowAdded = true;      }      mDecor.setVisibility(View.VISIBLE); //使其處於顯示狀況  }
布局階層

@Overrideprotected void onCreate(Bundle savedInstanceState) {  //設定視窗無標題列  requestWindowFeature(Window.FEATURE_NO_TITLE);  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_decor);}

activity_decor.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    android:paddingBottom="@dimen/activity_vertical_margin"    tools:context=".DecorActivity">    <TextView        android:text="@string/hello_world"        android:layout_width="wrap_content"        android:layout_height="wrap_content" />    <TextView        android:text="@string/hello_world"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"/></RelativeLayout>
 

onCreate()中設定的Window.FEATURE_NO_TITLE對應的視窗修飾布局檔案為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為"@android:id/content"的FrameLayout就是內容地區,在整個流程中,其會賦值給PhoneWindow類中的屬性mContentParent, 運行應用後,使用SDK提供的hierarchyviewer工具查看頁面的ViewTree結構,可以看到結構如下:

我是天王蓋地虎的分割線

 

 

參考:http://www.cnblogs.com/yogin/p/4061050.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.