AndroidSdk源码分析之--LayoutInflater加载生成View树的过程
LayoutInflater在Android中叫做布局管理器,很多时候我们都会用到LayoutInflater来加载指定的布局,然而,我对Android的UI加载View部分的源码进行了学习,也发现了,最终我们的布局也是通过 mLayoutInflater.inflate(layoutResID, mContentParent);进行渲染进去的
- PhoneWindow.java类
没错,就是上面的mLayoutInflater.inflate(layoutResID, mContentParent),至于mLayoutInflater的初始化是在PhoneWindow的构造方法里面实现的,这里就不贴出来了,很简单。
下面我们点进这个方法看看做了些什么事情?
-
LayoutInflater.java
注意:这个类是一个抽象类,具体的实现,我们一会再说
这里我们需要注意一下最后一个参数attachToRoot:root != null,而我们前面root是有值的mContentParent,所以这个值为true。
接下来,我们继续跟下去
final XmlResourceParser parser = res.getLayout(resource); 通过res.getLayout(resource)来查找参数resource所描述的UI布局文件
- Resources.java
参数id描述的是一个资源ID,TypeValue value = obtainTempTypedValue();来获得ID所对应的资源值,并且保存在一个类型为TypedValue的变量value中。
后面的就暂时不看了,反正就是解析读取处理XML布局,然后返回一个XmlResourceParser对象
现在我们看第三张截图里面的 return inflate(parser,root,attachToRoot),点进去看看
- LayoutInflater.java
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
final String name = parser.getName();
if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: "
+ name);
System.out.println("**************************");
}
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// 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);
}
}
if (DEBUG) {
System.out.println("-----> start inflating children");
}
// Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
// 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);
}
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
final InflateException ie = new InflateException(e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (Exception e) {
final InflateException ie = new InflateException(parser.getPositionDescription()
+ ": " + e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
return result;
}
}
上面的代码那么多,我们来结合图来具体说明一下
下面就是看一下rInflate方法
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
boolean pendingRequestFocus = false;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
pendingRequestFocus = true;
consumeChildElements(parser);
} else if (TAG_TAG.equals(name)) {
parseViewTag(parser, parent, attrs);
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
} else {
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
if (pendingRequestFocus) {
parent.restoreDefaultFocus();
}
if (finishInflate) {
parent.onFinishInflate();
}
}
里面有两处关键点,分别是
rInflateChildren(parser, view, attrs, true); 顾明思议就是递归的解析出来所有的子View,然后直接调用parent.onFinishInflate(),表明遍历完结,然后就会在栈中一步步的向上调用 parent.onFinishInflate()方法来通知父容器,本View已经解析完成.
createViewFromTag(…)负责根据指定的 name 生成具体的 View
viewGroup.addView(view, params),该句负责把每次解析出来的 View 都添加进本次的根 ViewGroup 中,这样最终会将其下所有的子 View 都添加进来。
我们就将 LayoutInflate 解析 xml ,从而生成 View树 的过程讲解完了,当然,具体的内容还有很多,接下来我会一步步的去讲解,在本篇中的一些迷惑,你也会一一解开。