android高级UI布局层次结构及布局加载流程源码解析

[](()2、mWindow.setContentView(layoutResID)

通过上面我们知道最终调用的是PhoneWindow的setContentView,方法如下所示,主要干了两件事情,注释1用于生成DecorView和mContentParent;注释2处用于将layoutResID(也就是我们实现的布局id)设置到mContentParent中去;

public void setContentView(int layoutResID) {

if (mContentParent == null) {

installDecor(); //1

}

、、、

mLayoutInflater.inflate(layoutResID, mContentParent);//2

、、、

}

复制代码

[](()3、installDecor()

installDecor方法代码如下所示,注释1处通过generateDecor方法生成mDecor变量,generateDecor方法内部其实就是new了一个DecorView;注释2处通过generateLayout方法获得mContentParent;mDecor是PhoneWindow的成员变量,是DecorView类型的,继承自FrameLayout;而这个mContentParent代表mDecor本身或者mDecor的子布局,什么意思呢?其实在mDecor的上部有一个占位View,根据不同的主题加载不同的DecorView,如果没有加载顶部view那mContentParent就是mDecor了;

private void installDecor() {

、、、

mDecor = generateDecor(-1); //1

、、、

mDecor.setWindow(this);

、、、

mContentParent = generateLayout(mDecor); //2

}

复制代码

[](()4、布局层次

经过上面的分析,我们发现Activity持有一个PhoneWindow的成员变量;PhoneWindow持有一个DecorView的成员变量,DecorView内部持有一个mContentParent,所以布局层次结构就是 Activity – PhoneWindow – DecorView – mContentParent;

而我们写的xml布局就是放在了mContentParent中;那么xml布局是如何加载到mContentParent容器的呢?其实也就是步骤2的注释2的mLayoutInflater.inflate(layoutResID, mContentParent)进行加载的,我们在下一部分分析加载的流程;

[](()二、布局加载流程分析


1、mLayoutInflater.inflate(layoutResID, mContentParent)的调用链如下所示;

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {

return inflate(resource, root, root != null); //1

}

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {

final Resources res = getContext().getResources();

XmlResourceParser parser = res.getLayout(resource);

try {

return inflate(parser, root, attachToRoot); //2

} finally {

parser.close();

}

}

复制代码

2、最终会调用到如下inflate方法,注释1处将root赋值给result;注释2处会通过createViewFromTag创建根View;注释4处用于inflate所有的children;这里解释一下最后一个attachToRoot参数,如果设置为true,会调用注释5将根view添加到root中然后将root返回;如果设置为false,会调用到注释3处,将root的参数设置到根view中去,但并不会执行addView操作,然后调用到注释6处将根view赋值给result,这样返回的就是根view而不是root;

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {

View result = root; //1

final String name = parser.getName();

// Temp is the root view that was found in the xml

final View temp = createViewFromTag(root, name, inflaterContext, attrs); //2

ViewGroup.LayoutParams params = null;

if (root != null) {

// Create layout params that match root, if supplied

params = root.generateLayoutParams(attrs);

if (!attachToRoot) {

// Set the layout params for temp if we are not

// attaching. (If we are, we use addView, below)

temp.setLayoutParams(params); //3

}

}

// Inflate all children under temp against its context.

rInflateChildren(parser, temp, attrs, true); //4

// We are supposed to attach all the views we found (int temp)

// to root. Do that now.

if (root != null && attachToRoot) {

root.addView(temp, params); //5

}

// Decide whether to return the root that was passed in or the

// top view found in xml.

if (root == null || !attachToRoot) {

result = temp; //6

}

return result;

}

复制代码

3、通过createViewFromTag创建根view,此方法调用链如下所示;注释1处是第一次创建view;注释2和注释3最终都会调用到注释3处的方法;注释1是一种情况,注释3是另一种情况,我们分别讨论一下;

private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {

return createViewFromTag(parent, name, context, attrs, false);

}

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,

boolean ignoreThemeAttr) {

View view = tryCreateView(parent, name, context, attrs); //1

if (view == null) {

final Object lastContext = mConstructorArgs[0];

mConstructorArgs[0] = context;

try {

if (-1 == name.indexOf(‘.’)) {

view = onCreateView(context, parent, name, attrs); //2

} else {

view = createView(context, name, null, attrs); //3

}

}

}

return view;

}

复制代码

3-1 tryCreateView

第一种情况tryCreateView方法如下所示,如果mFactory2不为空,会通过注释1处mFactory2的onCreateView方法创建view;如果mFactory不为空,会通过注释2处mFactory的onCreateView创建view;那么这两个factory是什么呢?如注释3和注释4所示,Factory是一个单方法的接口,Factory2继承自Factory,重载了一个onCreateView方法;这两个变量初始都为空,因此我们开发者可以实现这两个接口,然后赋值给这两个变量,这样就会优先通过我们设置的方法加载view了;

public final View tryCreateView(@Nullable View parent, @NonNull String name,

@NonNull Context context,

@NonNull AttributeSet attrs) {

View view;

if (mFactory2 != null) {

view = mFactory2.onCreateView(parent, name, context, attrs); //1

} else if (mFactory != null) {

view = mFactory.onCreateView(name, context, attrs); //2

} else {

view = null;

}

return view;

}

private Factory mFactory; //3

private Factory2 mFactory2; //4

public interface Factory {

View onCreateView(@NonNull String name, @NonNull Context context,

@NonNull AttributeSet attrs);

}

public interface Factory2 extends Factory {

View onCreateView(@Nullable View parent, @NonNull String name,

@NonNull Context context, @NonNull AttributeSet attrs);

}

复制代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值