android解析布局树,android布局文件解析成view树浅析

android解析布局树,android布局文件解析成view树浅析_第1张图片 拿这个布局为例,让我们跟随eclipse进入解析xml成view树的代码;

先上一段熟悉的代码:

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

//1、该方法最终也会调用到 LayoutInflater的inflate()方法中去解析。

setContentView(R.layout.main);

//2、使用常见的API方法去解析xml布局文件,

LayoutInflater layoutInflater = (LayoutInflater)getSystemService();

View root = layoutInflater.inflate(R.layout.main, null,false);

}

我们知道,在activity的onCreate里面设置布局文件的id,那么这个activity显示的就是这个布局文件。当我们设置了layout之后还是会调用LayoutInflater的inflate去解析xml文件成为view树。

先看看inflate函数:

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

//获取一个XmlResourceParser来解析XML文件---布局文件。

//XmlResourceParser类以及xml是如何解析的,大家自己有兴趣找找。

XmlResourceParser parser = getContext().getResources().getLayout(resource);

try {

return inflate(parser, root, attachToRoot);

} finally {

parser.close();

}

}

接着跟进去

public abstract class LayoutInflater {

...

/**

* Inflate a new view hierarchy from the specified XML node. Throws

* {@link InflateException} if there is an error.

*/

//我们传递过来的参数如下: root 为null , attachToRoot为false 。

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

synchronized (mConstructorArgs) {

final AttributeSet attrs = Xml.asAttributeSet(parser);

Context lastContext = (Context)mConstructorArgs[0];

mConstructorArgs[0] = mContext; //该mConstructorArgs属性最后会作为参数传递给View的构造函数

View result = root; //根View

try {

// Look for the root node.

int type;

while ((type = parser.next()) != XmlPullParser.START_TAG &&

type != XmlPullParser.END_DOCUMENT) {

// Empty

}

...

final String name = parser.getName(); //节点名,即API中的控件或者自定义View完整限定名。

if (TAG_MERGE.equals(name)) { // 处理标签

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

throw new InflateException(" can be used only with a valid "

+ "ViewGroup root and attachToRoot=true");

}

//将标签的View树添加至root中,该函数稍后讲到。

rInflate(parser, root, attrs);

} else {

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

//创建该xml布局文件所对应的根View。

View temp = createViewFromTag(name, attrs);

ViewGroup.LayoutParams params = null;

if (root != null) {

// Create layout params that match root, if supplied

//根据AttributeSet属性获得一个LayoutParams实例,记住调用者为root。

params = root.generateLayoutParams(attrs);

if (!attachToRoot) { //重新设置temp的LayoutParams

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

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

temp.setLayoutParams(params);

}

}

// Inflate all children under temp

//添加所有其子节点,即添加所有字View

rInflate(parser, temp, attrs);

// 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;

}

}

}

...

return result;

}

}

}

inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)先会判断root是否为null,如果是null,则先创建根节点,然后调用rInflate,否则直接调用rInflate。

下面我们来看一下rInflate函数

**

* Recursive method used to descend down the xml hierarchy and instantiate

* views, instantiate their children, and then call onFinishInflate().

*/

//递归调用每个字节点

private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)

throws XmlPullParserException, IOException {

final int depth = parser.getDepth();

int type;

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)) { //处理标签

parseRequestFocus(parser, parent);

} else if (TAG_INCLUDE.equals(name)) { //处理标签

if (parser.getDepth() == 0) {

throw new InflateException(" cannot be the root element");

}

parseInclude(parser, parent, attrs);//解析节点

} else if (TAG_MERGE.equals(name)) { //处理标签

throw new InflateException(" must be the root element");

} else {

//根据节点名构建一个View实例对象

final View view = createViewFromTag(name, attrs);

final ViewGroup viewGroup = (ViewGroup) parent;

//调用generateLayoutParams()方法返回一个LayoutParams实例对象,

final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);

rInflate(parser, view, attrs); //继续递归调用

viewGroup.addView(view, params); //OK,将该View以特定LayoutParams值添加至父View中

}

}

parent.onFinishInflate(); //完成了解析过程,通知....

}

这是要解析的布局

android解析布局树,android布局文件解析成view树浅析_第2张图片

对应的布局文件为:

<1>

<2>

<3>

3>

<4>

4>

2>

<5>

<6>

6>

<7>

7>

5>

<8>

<9>

9>

<10>

10>

8>

<11>

<12>

12>

<13>

13>

11>

1>

xml文件解析成树之后的图形:

android解析布局树,android布局文件解析成view树浅析_第3张图片

当前xml对应 rInflate函数解析的第一步就是控件2(1是根节点,在inflate里面已经生成),然后执行

//根据节点名构建一个View实例对象

final View view = createViewFromTag(name, attrs);

final ViewGroup viewGroup = (ViewGroup) parent;

//调用generateLayoutParams()方法返回一个LayoutParams实例对象,

final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);

rInflate(parser, view, attrs); //继续递归调用

viewGroup.addView(view, params); //OK,将该View以特定LayoutParams值添加至父View中

注明:接下来以上代码用“代码片段1”来表示

先是生成控件2,然后递归调用rInflate函数(把自己当成parent,和同一个parser对象传过去,这时parser已经解析过2了)

又是执行“代码片段1”,这时parent是控件2, parser.next()后得到3的attr,3也去执行“代码片段1”但是在while判断的时候由于4的深度和3是一样的(都是3),所以直接返回,于是在3执行“代码片段1”的最后一句2把3添加了进去(2是parent)并回到2。

这时parser.next()过了3了, 根节点调用的“代码片段1”会继续调用rInflate函数并把2作为parent传入,由于4和3一样,在生成自己的view之后调用Inflate执行while的时候发现5的深度(值为2)不比自己大,被2添加到试图之后而结束递归。

结束之后2被添加到了parent(就是根节点1),这时根节点调用的 “代码片段1”中只执行了一次while.

然后再执行一次while到5,5添加了6、7,后再被1添加,这时根节点调用的 “代码片段1”中执行了两次while.

然后再执行一次while到8,8添加了9、10之后被1添加,这时根节点调用的 “代码片段1”中执行了三次while.

然后再执行一次while到11,11添加了12、13之后被1添加。这时根节点调用的 “代码片段1”中执行了四次while.

最后在执行第五次while判断的时候由于

type != XmlPullParser.END_DOCUMENT

而结束。

至此:整颗view树解析完毕!

你可能感兴趣的:(android解析布局树)