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

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

Screen-simple.xml

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

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

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

ebba073942be2561e5d300444ac021c9.png

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

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

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值