【转】decorView和window之间的层级及关系

转载请注明出处:http://blog.csdn.net/guxiao1201/article/details/41744107

首先贴出实现Activity对话框圆角的核心代码

[java] view plain copy
 
  1. @Override  
  2.     public void onAttachedToWindow() {  
  3.         super.onAttachedToWindow();  
  4.         DisplayMetrics dm = new DisplayMetrics();  
  5.         getWindowManager().getDefaultDisplay().getMetrics(dm);  
  6.         View view = getWindow().getDecorView();  
  7.         WindowManager.LayoutParams lp = (WindowManager.LayoutParams)view.getLayoutParams();  
  8.         lp.gravity = Gravity.CENTER;  
  9.         lp.width = (dm.widthPixels * 4) / 5;  
  10.         lp.height = (dm.widthPixels * 4) / 5;  
  11.         getWindowManager().updateViewLayout(view,lp);  
  12.         getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));  
  13.         view.setBackgroundResource(R.drawable.dialog_activity_bg);  
  14.     }  

在上篇博客onAttachedToWindow()在整个Activity生命周期的位置及使用中解释了为什么在onAttachedToWindow中修改窗口尺寸,上面代码最后两行分别对window和decorView设置背景,那么问题来了,一个窗体中decorView和window之间的关系是什么?

通过文章开始贴出的代码View view = getWindow().getDecorView();就可以对Window和DecorView的层级关键猜测一二,decorView是否作为一个变量由window维护?
和之前思路一样,想探究这个问题就得看源码说话。这里依然参考老罗的博客Android应用程序窗口(Activity)的视图对象(View)的创建过程分析

既然猜测decorView是window的变量,那么就先找到window和activity之间的关系。通过源码发现

[java] view plain copy
 
  1. public class Activity extends ContextThemeWrapper    
  2.         implements LayoutInflater.Factory,    
  3.         Window.Callback, KeyEvent.Callback,    
  4.         OnCreateContextMenuListener, ComponentCallbacks {    
  5.     ......    
  6.     
  7.     private Window mWindow;    
  8.     ......    
  9.     
  10.     public Window getWindow() {    
  11.         return mWindow;    
  12.     }    
  13.     ......    
  14.     
  15.     public void setContentView(int layoutResID) {    
  16.         getWindow().setContentView(layoutResID);    
  17.     }    
  18.     
  19.     ......    
  20. }    

Window原来是Activity的一个变量,可以通过getWindow()获取,而且Activity中经常用到的setContentView原来调用的是window的setContentView。那么Window是什么?在Activity中起什么作用?OK~带着问题再查阅Google的官方文档。

Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.

The only existing implementation of this abstract class is Android.policy.PhoneWindow, which you should instantiate when needing a Window. Eventually that class will be refactored and a factory method added for creating Window instances without knowing about a particular implementation.

Google说Window是WindowManager最顶层的视图,它负责背景(窗口背景)、Title之类的标准的UI元素,Window是一个抽象类,整个Android系统中PhoneWindow是Winodw的唯一实现类。所以接下来进入PhoneWinodw一探究竟。

[java] view plain copy
 
  1. public class PhoneWindow extends Window implements MenuBuilder.Callback {    
  2.     ......    
  3.     
  4.     // This is the top-level view of the window, containing the window decor.    
  5.     private DecorView mDecor;    
  6.     ......    
  7.     
  8.     // This is the view in which the window contents are placed. It is either    
  9.     // mDecor itself, or a child of mDecor where the contents go.    
  10.     private ViewGroup mContentParent;    
  11.     ......    
  12.     
  13.     private TextView mTitleView;    
  14.     ......    
  15.     
  16.     private CharSequence mTitle = null;    
  17.     ......    
  18.     
  19.     private void installDecor() {    
  20.         if (mDecor == null) {    
  21.             mDecor = generateDecor();    
  22.             ......    
  23.         }    
  24.         if (mContentParent == null) {    
  25.             mContentParent = generateLayout(mDecor);    
  26.     
  27.             mTitleView = (TextView)findViewById(com.android.internal.R.id.title);    
  28.             if (mTitleView != null) {    
  29.                 if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {    
  30.                     View titleContainer = findViewById(com.android.internal.R.id.title_container);    
  31.                     if (titleContainer != null) {    
  32.                         titleContainer.setVisibility(View.GONE);    
  33.                     } else {    
  34.                         mTitleView.setVisibility(View.GONE);    
  35.                     }    
  36.                     if (mContentParent instanceof FrameLayout) {    
  37.                         ((FrameLayout)mContentParent).setForeground(null);    
  38.                     }    
  39.                 } else {    
  40.                     mTitleView.setText(mTitle);    
  41.                 }    
  42.             }    
  43.         }    
  44.     }    
  45.     
  46.     ......    
  47. }    

在这里总算找到了DecorView,和上面猜测的一样,DecorView确实为Window的变量。同时还发现一个名为mContentParent的ViewGroup,那么这个变量的作用是什么?和DecorView有什么关系?带着问题接着往下看。

PhoneWindow的setContentView方法

[java] view plain copy
 
  1. @Override  
  2.     public void setContentView(int layoutResID) {  
  3.         if (mContentParent == null) {  
  4.             installDecor();  
  5.         } else {  
  6.             mContentParent.removeAllViews();  
  7.         }  
  8.         mLayoutInflater.inflate(layoutResID, mContentParent);  
  9.         final Callback cb = getCallback();  
  10.         if (cb != null && !isDestroyed()) {  
  11.             cb.onContentChanged();  
  12.         }  
  13.     }  

在mContentParent为null时会调用installDecor()来创建应用程序窗口视图对象。接着在installDecor()中调用generateLayout为mContentParent赋值。

[java] view plain copy
 
  1. protected ViewGroup generateLayout(DecorView decor) {  
  2.     // 获取<Activity android:theme=""/>中的theme属性或者代码requestWindowFeature()中指定的Features  
  3.   
  4.     TypedArray a = getWindowStyle();  
  5.   
  6.     if (false) {  
  7.         System.out.println("From style:");  
  8.         String s = "Attrs:";  
  9.         for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) {  
  10.             s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="  
  11.                     + a.getString(i);  
  12.         }  
  13.         System.out.println(s);  
  14.     }  
  15.   
  16.     mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);  
  17.     int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)  
  18.             & (~getForcedWindowFlags());  
  19.     if (mIsFloating) {  
  20.         setLayout(WRAP_CONTENT, WRAP_CONTENT);  
  21.         setFlags(0, flagsToUpdate);  
  22.     } else {  
  23.         setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);  
  24.     }  
  25.   
  26.     //...  
  27.   
  28.   
  29.     // Inflate the window decor.  
  30.   
  31.     int layoutResource;  
  32.     int features = getLocalFeatures();  
  33.     // System.out.println("Features: 0x" + Integer.toHexString(features));  
  34.     if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {  
  35.         if (mIsFloating) {  
  36.             TypedValue res = new TypedValue();  
  37.             getContext().getTheme().resolveAttribute(  
  38.                     com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);  
  39.             layoutResource = res.resourceId;  
  40.         } else {  
  41.             layoutResource = com.android.internal.R.layout.screen_title_icons;  
  42.         }  
  43.         // XXX Remove this once action bar supports these features.  
  44.         removeFeature(FEATURE_ACTION_BAR);  
  45.         // System.out.println("Title Icons!");  
  46.     } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0  
  47.             && (features & (1 << FEATURE_ACTION_BAR)) == 0) {  
  48.         // Special case for a window with only a progress bar (and title).  
  49.         // XXX Need to have a no-title version of embedded windows.  
  50.         layoutResource = com.android.internal.R.layout.screen_progress;  
  51.         // System.out.println("Progress!");  
  52.     } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {  
  53.         // Special case for a window with a custom title.  
  54.         // If the window is floating, we need a dialog layout  
  55.         if (mIsFloating) {  
  56.             TypedValue res = new TypedValue();  
  57.             getContext().getTheme().resolveAttribute(  
  58.                     com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);  
  59.             layoutResource = res.resourceId;  
  60.         } else {  
  61.             layoutResource = com.android.internal.R.layout.screen_custom_title;  
  62.         }  
  63.         // XXX Remove this once action bar supports these features.  
  64.         removeFeature(FEATURE_ACTION_BAR);  
  65.     } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {  
  66.         // If no other features and not embedded, only need a title.  
  67.         // If the window is floating, we need a dialog layout  
  68.         if (mIsFloating) {  
  69.             TypedValue res = new TypedValue();  
  70.             getContext().getTheme().resolveAttribute(  
  71.                     com.android.internal.R.attr.dialogTitleDecorLayout, res, true);  
  72.             layoutResource = res.resourceId;  
  73.         } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {  
  74.             layoutResource = com.android.internal.R.layout.screen_action_bar;  
  75.         } else {  
  76.             layoutResource = com.android.internal.R.layout.screen_title;  
  77.         }  
  78.         // System.out.println("Title!");  
  79.     } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {  
  80.         layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;  
  81.     } else {  
  82.         // Embedded, so no decoration is needed.  
  83.         layoutResource = com.android.internal.R.layout.screen_simple;  
  84.         // System.out.println("Simple!");  
  85.     }  
  86.   
  87.     mDecor.startChanging();  
  88.   
  89.     View in = mLayoutInflater.inflate(layoutResource, null);  
  90.     decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));  
  91.   
  92.     ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);  
  93.     if (contentParent == null) {  
  94.         throw new RuntimeException("Window couldn't find content container view");  
  95.     }  
  96.   
  97.     if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {  
  98.         ProgressBar progress = getCircularProgressBar(false);  
  99.         if (progress != null) {  
  100.             progress.setIndeterminate(true);  
  101.         }  
  102.     }  
  103.   
  104.     // Remaining setup -- of background and title -- that only applies  
  105.     // to top-level windows.  
  106.     if (getContainer() == null) {  
  107.         Drawable drawable = mBackgroundDrawable;  
  108.         if (mBackgroundResource != 0) {  
  109.             drawable = getContext().getResources().getDrawable(mBackgroundResource);  
  110.         }  
  111.         mDecor.setWindowBackground(drawable);  
  112.         drawable = null;  
  113.         if (mFrameResource != 0) {  
  114.             drawable = getContext().getResources().getDrawable(mFrameResource);  
  115.         }  
  116.         mDecor.setWindowFrame(drawable);  
  117.   
  118.         // System.out.println("Text=" + Integer.toHexString(mTextColor) +  
  119.         // " Sel=" + Integer.toHexString(mTextSelectedColor) +  
  120.         // " Title=" + Integer.toHexString(mTitleColor));  
  121.   
  122.         if (mTitleColor == 0) {  
  123.             mTitleColor = mTextColor;  
  124.         }  
  125.   
  126.         if (mTitle != null) {  
  127.             setTitle(mTitle);  
  128.         }  
  129.         setTitleColor(mTitleColor);  
  130.     }  
  131.   
  132.     mDecor.finishChanging();  
  133.   
  134.     return contentParent;  
  135. }  

上面代码可看到将一些页面特性布局,例如ActionBar、Title等添加到decorView中,并且根据代码ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);可看到原来mContentParent是id="@android:id/content"的ViewGroup。再回到PhoneWindow.setContentView

[java] view plain copy
 
  1. mLayoutInflater.inflate(layoutResID, mContentParent);  

原来我们自定义的View都是mContentParent的子View。并且上面这段代码还发现了为什么必须在setContentView之前才能执行requestWindowFeature的彩蛋。

根据上面所有的猜测和分析,最终发现Window、DecorView和mContentParent的层级关系如下图(如有误请指正)


参考资料:

Window窗口布局--DecorView浅析

Android应用程序窗口(Activity)的视图对象(View)的创建过程分析

from:http://blog.csdn.net/guxiao1201/article/details/41744107

原文地址:https://www.cnblogs.com/xuan52rock/p/6513173.html