Android视图加载到窗口的分析

本文深入探讨了Android中Activity视图加载的过程,从setContentView方法入手,解析了PhoneWindow类的作用,以及如何通过布局资源文件生成视图层级结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇主要从onCreate方法里面我们必须要写的方法setContentView开始,研究布局视图是如何加载到手机窗口上的。

当在执行到setContentView时,实际上执行的是

[java]  view plain copy
  1. public void setContentView(int layoutResID) {  
  2.         getWindow().setContentView(layoutResID);  
  3.         initActionBar();  
  4. }  

可以看到实际上是Window类的setContentView方法

[java]  view plain copy
  1. private Window mWindow;  
  2. public Window getWindow() {  
  3.         return mWindow;  
  4. }  

Window类是一个抽象类,下面主要是找到他的实现类。mWindow初始化在

[java]  view plain copy
  1. final void attach(……) {  
  2.         ……  
  3.         mWindow = PolicyManager.makeNewWindow(this);  
  4.         mWindow.setCallback(this);  
  5.         mWindow.getLayoutInflater().setPrivateFactory(this);  
  6.     ……  
  7.     }  

Attach方法在main方法中。具体查看上一篇博客。调用了PolicyManager类的静态方法makeNewWindow生成Window对象

[java]  view plain copy
  1. private static final String POLICY_IMPL_CLASS_NAME =  
  2.         "com.android.internal.policy.impl.Policy";  
  3. private static final IPolicy sPolicy;  
  4. static {  
  5.         try {  
  6.             Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);  
  7.             sPolicy = (IPolicy)policyClass.newInstance();  
  8.         }  
  9. ……  
  10.     }  
  11. public static Window makeNewWindow(Context context) {  
  12.         return sPolicy.makeNewWindow(context);  
  13. }  

可以看到是通过Policy类的makeNewWindow方法得到的Window对象,这里是通过反射机制获取的Policy的实例。

[java]  view plain copy
  1. public Window makeNewWindow(Context context) {  
  2.         return new PhoneWindow(context);  
  3. }  

可以看到实际上是一个PhoneWindow。那么根据多态,其实在上面调用的就是PhoneWindow#setContentWindow

[java]  view plain copy
  1. public void setContentView(int layoutResID) {  
  2.         if (mContentParent == null) {  
  3.             installDecor();//1  
  4.         } else {  
  5.             mContentParent.removeAllViews();  
  6.         }  
  7.         mLayoutInflater.inflate(layoutResID, mContentParent);//2 将layoutResID填充,他的父View是mContentParent是在installDecor方法里面mContentParent = generateLayout(mDecor);得到的  
  8.         final Callback cb = getCallback();  
  9.         if (cb != null && !isDestroyed()) {  
  10.             cb.onContentChanged();  
  11.         }  
  12. }  

执行到installDecor()

[java]  view plain copy
  1. private void installDecor() {  
  2.     if (mDecor == null) {  
  3.         mDecor = generateDecor();//生成装饰窗口,装饰窗口继承自FrameLayout  
  4.         mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);  
  5.         mDecor.setIsRootNamespace(true);  
  6.         if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {  
  7.             mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);  
  8.         }  
  9.     }  
  10.     if (mContentParent == null) {  
  11.         mContentParent = generateLayout(mDecor);// 产生布局,返回父布局,暂时这样理解,具体进去看代码  
  12.         mDecor.makeOptionalFitsSystemWindows();  
  13.         mTitleView = (TextView)findViewById(com.android.internal.R.id.title);  
  14.         ......  
  15.         }  
  16.     }  
  17. }  

generateLayout的代码如下

[java]  view plain copy
  1. protected ViewGroup generateLayout(DecorView decor) {  
  2.         // Apply data from current theme.  
  3.   
  4.         TypedArray a = getWindowStyle();// 获取当前设置的主题  
  5.         ......  
  6.         if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {  
  7.             requestFeature(FEATURE_NO_TITLE);//可以看到平时在AndroidManifest配置的窗口等各其实在代码里都是在这里修改的  
  8.         } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {  
  9.             // Don't allow an action bar if there is no title.  
  10.             requestFeature(FEATURE_ACTION_BAR);  
  11.         }  
  12.   
  13.         if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {  
  14.             requestFeature(FEATURE_ACTION_BAR_OVERLAY);  
  15.         }  
  16.     ......  
  17.     //19-63行根据我们指定的有无标题等各种窗口风格得到对应的默认布局,  
  18.     //这些布局在D:\SoftWare\Java\android4.2-source\frameworks\base\core\res\res\layout  
  19.         int layoutResource;  
  20.         int features = getLocalFeatures();  
  21.         if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {  
  22.             if (mIsFloating) {  
  23.                 TypedValue res = new TypedValue();  
  24.                 getContext().getTheme().resolveAttribute(  
  25.                         com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);  
  26.                 layoutResource = res.resourceId;  
  27.             } else {  
  28.                 layoutResource = com.android.internal.R.layout.screen_title_icons;  
  29.             }  
  30.             removeFeature(FEATURE_ACTION_BAR);  
  31.         } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0  
  32.                 && (features & (1 << FEATURE_ACTION_BAR)) == 0) {  
  33.             layoutResource = com.android.internal.R.layout.screen_progress;  
  34.         } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {  
  35.             if (mIsFloating) {  
  36.                 TypedValue res = new TypedValue();  
  37.                 getContext().getTheme().resolveAttribute(  
  38.                         com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);  
  39.                 layoutResource = res.resourceId;  
  40.             } else {  
  41.                 layoutResource = com.android.internal.R.layout.screen_custom_title;  
  42.             }  
  43.             removeFeature(FEATURE_ACTION_BAR);  
  44.         } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {  
  45.             if (mIsFloating) {  
  46.                 TypedValue res = new TypedValue();  
  47.                 getContext().getTheme().resolveAttribute(  
  48.                         com.android.internal.R.attr.dialogTitleDecorLayout, res, true);  
  49.                 layoutResource = res.resourceId;  
  50.             } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {  
  51.                 if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {  
  52.                     layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;  
  53.                 } else {  
  54.                     layoutResource = com.android.internal.R.layout.screen_action_bar;  
  55.                 }  
  56.             } else {  
  57.                 layoutResource = com.android.internal.R.layout.screen_title;  
  58.             }  
  59.         } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {  
  60.             layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;  
  61.         } else {  
  62.             layoutResource = com.android.internal.R.layout.screen_simple;  
  63.         }  
  64.   
  65.         mDecor.startChanging();  
  66.   
  67.         View in = mLayoutInflater.inflate(layoutResource, null);//根据上面的判断选择的layoutResource填充成View  
  68.         decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//调用装饰窗口的addView方法将上一步生成的View添加到最外层的装饰窗口  
  69.       
  70.         ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//ID_ANDROID_CONTENT对应的都是@android:id/content其实是一个FrameLayout  
  71.         ......  
  72.         mDecor.finishChanging();  
  73.         return contentParent;  
  74.     }  

下面是最常用的layoutResource布局文件他们在D:\SoftWare\Java\android4.2-source\frameworks\base\core\res\res\layout文件夹下

Screen-title.xml

[html]  view plain copy
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:orientation="vertical"  
  3.     android:fitsSystemWindows="true">  
  4.     <!-- Popout bar for action modes -->  
  5.     <ViewStub android:id="@+id/action_mode_bar_stub"  
  6.               android:inflatedId="@+id/action_mode_bar"  
  7.               android:layout="@layout/action_mode_bar"  
  8.               android:layout_width="match_parent"  
  9.               android:layout_height="wrap_content" />  
  10.     <FrameLayout  
  11.         android:layout_width="match_parent"   
  12.         android:layout_height="?android:attr/windowTitleSize"  
  13.         style="?android:attr/windowTitleBackgroundStyle">  
  14.         <TextView android:id="@android:id/title"   
  15.             style="?android:attr/windowTitleStyle"  
  16.             android:background="@null"  
  17.             android:fadingEdge="horizontal"  
  18.             android:gravity="center_vertical"  
  19.             android:layout_width="match_parent"  
  20.             android:layout_height="match_parent" />  
  21.     </FrameLayout>  
  22.     <FrameLayout android:id="@android:id/content"  
  23.         android:layout_width="match_parent"   
  24.         android:layout_height="0dip"  
  25.         android:layout_weight="1"  
  26.         android:foregroundGravity="fill_horizontal|top"  
  27.         android:foreground="?android:attr/windowContentOverlay" />  
  28. </LinearLayout>  

最外层是线性布局,包含帧布局(包含一个TextView其实就是标题)和帧布局。其实这个就是最常见的样子。默认生成的程序就是这个样子。

Screen-simple.xml

[html]  view plain copy
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="match_parent"  
  4.     android:fitsSystemWindows="true"  
  5.     android:orientation="vertical">  
  6.     <ViewStub android:id="@+id/action_mode_bar_stub"  
  7.               android:inflatedId="@+id/action_mode_bar"  
  8.               android:layout="@layout/action_mode_bar"  
  9.               android:layout_width="match_parent"  
  10.               android:layout_height="wrap_content" />  
  11.     <FrameLayout  
  12.          android:id="@android:id/content"  
  13.          android:layout_width="match_parent"  
  14.          android:layout_height="match_parent"  
  15.          android:foregroundInsidePadding="false"  
  16.          android:foregroundGravity="fill_horizontal|top"  
  17.          android:foreground="?android:attr/windowContentOverlay" />  
  18. </LinearLayout>  

显而易见这个就是全屏设置时默认加载的布局。

我们可以看到id为content的FrameLayout都存在的,因为他要装载我们填充的xml

依Screen-title.xml为例,大致是这样子的


通过以上分析明白了以下几点:

1. Activity呈现出来的界面其实是一个PhoneWindow类在管理的,这个类中有一个DecorView成员就是最外层的一个容器。

2. 上面这张图是非常重要的,显示了窗口的结构。

3. Activity到底是个什么东西?还真不好说清楚…^_^总之和刚开始的认识是不同的。

4. 遇到不懂得问题就去查看源码。代码是最好的老师!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值