Android视图载入到窗体的过程分析

满堂花醉三千客,一剑霜寒十四州。这篇文章主要讲述Android视图载入到窗体的过程分析相关的知识,希望能为你提供帮助。
上一篇博客Android中Handler原理在讲到Handler的时候谈到了android的Activity启动是怎样运行到onCreate方法的,这篇主要从onCreate方法里面我们必需要写的方法setContentView開始,研究布局视图是怎样载入到手机窗体上的。
当在运行到setContentView时,实际上运行的是

public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); initActionBar(); }


能够看到实际上是Window类的setContentView方法
private Window mWindow; public Window getWindow() { return mWindow; }

Window类是一个抽象类,以下主要是找到他的实现类。mWindow初始化在
final void attach(……) { …… mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); …… }

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

private static final String POLICY_IMPL_CLASS_NAME = " com.android.internal.policy.impl.Policy" ; private static final IPolicy sPolicy; static { try { Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME); sPolicy = (IPolicy)policyClass.newInstance(); } …… } public static Window makeNewWindow(Context context) { return sPolicy.makeNewWindow(context); }


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

public Window makeNewWindow(Context context) { return new PhoneWindow(context); }

能够看到实际上是一个PhoneWindow。那么依据多态。事实上在上面调用的就是PhoneWindow#setContentWindow
public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); //1 } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent); //2 将layoutResID填充。他的父View是mContentParent是在installDecor方法里面mContentParent = generateLayout(mDecor); 得到的 final Callback cb = getCallback(); if (cb != null & & !isDestroyed()) { cb.onContentChanged(); } }

运行到installDecor()

private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); //生成装饰窗体。装饰窗体继承自FrameLayout mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted & & mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } if (mContentParent == null) { mContentParent = generateLayout(mDecor); // 产生布局。返回父布局,临时这样理解,详细进去看代码 mDecor.makeOptionalFitsSystemWindows(); mTitleView = (TextView)findViewById(com.android.internal.R.id.title); ...... } } }


generateLayout的代码例如以下
protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme.TypedArray a = getWindowStyle(); // 获取当前设置的主题 ...... if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) { requestFeature(FEATURE_NO_TITLE); //能够看到平时在AndroidManifest配置的窗体等各事实上在代码里都是在这里改动的 } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) { // Don' t allow an action bar if there is no title. requestFeature(FEATURE_ACTION_BAR); }if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) { requestFeature(FEATURE_ACTION_BAR_OVERLAY); } ...... //19-63行依据我们指定的有无标题等各种窗体风格得到相应的默认布局。 //这些布局在D:\SoftWare\java\android4.2-source\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; } else if ((features & (1 < < FEATURE_CUSTOM_TITLE)) != 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = com.android.internal.R.layout.screen_custom_title; } removeFeature(FEATURE_ACTION_BAR); } else if ((features & (1 < < FEATURE_NO_TITLE)) == 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( com.android.internal.R.attr.dialogTitleDecorLayout, res, true); layoutResource = res.resourceId; } else if ((features & (1 < < FEATURE_ACTION_BAR)) != 0) { if ((features & (1 < < FEATURE_ACTION_BAR_OVERLAY)) != 0) { layoutResource = com.android.internal.R.layout.screen_action_bar_overlay; } else { layoutResource = com.android.internal.R.layout.screen_action_bar; } } else { layoutResource = com.android.internal.R.layout.screen_title; } } else if ((features & (1 < < FEATURE_ACTION_MODE_OVERLAY)) != 0) { layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode; } else { layoutResource = com.android.internal.R.layout.screen_simple; }mDecor.startChanging(); View in = mLayoutInflater.inflate(layoutResource, null); //依据上面的推断选择的layoutResource填充成View decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); //调用装饰窗体的addView方法将上一步生成的View加入到最外层的装饰窗体 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //ID_ANDROID_CONTENT相应的都是@android:id/content事实上是一个FrameLayout ...... mDecor.finishChanging(); return contentParent; }

以下是最经常使用的layoutResource布局文件他们在D:\SoftWare\Java\android4.2-source\frameworks\base\core\res\res\layout目录下
Screen-title.xml
< LinearLayout xmlns:android=" http://schemas.android.com/apk/res/android" android:orientation=" vertical" android:fitsSystemWindows=" true" > < !-- Popout bar for action modes --> < 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:layout_width=" match_parent" android:layout_height=" ?android:attr/windowTitleSize" style=" ?android:attr/windowTitleBackgroundStyle" > < TextView android:id=" @android:id/title" style=" ?
android:attr/windowTitleStyle" android:background=" @null" android:fadingEdge=" horizontal" android:gravity=" center_vertical" android:layout_width=" match_parent" android:layout_height=" match_parent" /> < /FrameLayout> < FrameLayout android:id=" @android:id/content" android:layout_width=" match_parent" android:layout_height=" 0dip" android:layout_weight=" 1" android:foregroundGravity=" fill_horizontal|top" android:foreground=" ?android:attr/windowContentOverlay" /> < /LinearLayout>

最外层是线性布局。包括帧布局(包括一个TextView事实上就是标题)和帧布局。事实上这个就是最常见的样子。
默认生成的程序就是这个样子。
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为content的FrameLayout都存在的,由于他要装载我们填充的xml
依Screen-title.xml为例。大致是这样子的
Android视图载入到窗体的过程分析

通过以上分析明确了下面几点:
1. Activity呈现出来的界面事实上是一个PhoneWindow类在管理的。这个类中有一个DecorView成员就是最外层的一个容器。

2. 上面这张图是很重要的,显示了窗体的结构。

3. Activity究竟是个什么东西?还真不好说清楚…^_^总之和刚開始的认识是不同的。
4. 遇到不懂得问题就去查看源代码。代码是最好的老师!








【Android视图载入到窗体的过程分析】













    推荐阅读