热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:Android视图载入到窗体的过程分析

篇首语:本文由编程笔记#小编为大家整理,主要介绍了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 < 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 < && (features & (1 < layoutResource = com.android.internal.R.layout.screen_progress;
} else if ((features & (1 < 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 < if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 < if ((features & (1 < 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 < 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

android:orientation="vertical"
android:fitsSystemWindows="true">

android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_top"
android:foreground="?android:attr/windowContentOverlay" />

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

我们能够看到id为content的FrameLayout都存在的,由于他要装载我们填充的xml

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

技术分享

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

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

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

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

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

















推荐阅读
author-avatar
冬季梅花1991_156
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有