Window窗口布局 --- 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 {
    
    //...

 

頁面layout被添加至窗口Window的流程大致如下:

1,Activity中調用setContentView(R.layout.custom_layout), 具體實現為PhoneWindow中的同名方法

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

 

2, PhoneWindow執行setContentView(int layoutResource)

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

 

3, 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);

 

4, PhoneWindow.generateLayout(DecorView decor)

protected ViewGroup generateLayout(DecorView decor) {
  //4.1,獲取<Application android:theme=""/>, <Activity/>節點指定的themes或者代碼requestWindowFeature()中指定的Features, 並設置
  TypedArray a = getWindowStyle();
  //...
  
  //4.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();   //4.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");   }
  //...

從上述步驟中,可以看出為什么在代碼中必須要在setContentView(...)之前才能執行requestWindowFeature(...)

 

5, 最后頁面中設置的自定義layout會被添加到mContentParent中

mLayoutInflater.inflate(layoutResID, mContentParent);

 

所以,Window窗口的布局層次結構(features不同,可能標題欄不存在, 這種情況下,窗口修飾視圖區域與mContentParent內容區域重疊)如下所示:

 

示例:

Activity代碼:

@Override
protected 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://blog.csdn.net/qinjuning/article/details/7226787
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/android/view/Window.java?av=f
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/com/android/internal/policy/impl/PhoneWindow.java?av=f
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/frameworks/base/core/res/res/layout/screen_simple.xml?av=f
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/android/app/Activity.java?av=f
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM