结合Andorid 9.0 的代码,记录一下学习过程,大多时候是用的下面这个方法
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
还有就是重载的另一个方法:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
最终都会进入下面的方法:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
...
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;
//通过XmlPullParser解析我们所定义的布局文件
//这里会通过其next()方法一直循环,直到遇到START_TAG标志
//START_TAG代表了我们定义的布局文件的最外层布局的起点
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();//name值一般是LinearLayout、FrameLayout等布局
if (TAG_MERGE.equals(name)) {//这种情况可以不用理会,直接看下面的情况
if (root == null || !attachToRoot) {
throw new InflateException(" 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
//根据上面返回的最外层布局的name值来创建View ,会利用反射的方式创建
//该方法一般会返回一个ViewGroup实例作为根布局,例如LinearLayout等
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
//下面的情况就是我们经常用的一种方法:inflate(resId,lineaViewGroup,false)
//这种情况下我们在最外层布局定义的参数会生效
if (root != null) {
......
//根据最外层的布局参数(就是我们再xml定义的宽、高这些参数)来创建一个LayoutParams
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
//设置上面返回的最外层布局的布局参数(我们在xml文件里自己定义的参数)
temp.setLayoutParams(params);
}
}
......
// Inflate all children under temp against its context.
//将最外层布局下的所有子布局(如果有的话)全部遍历一遍,每遍历一层
//便会创建一个布局实例(例如控件TextView、Button,也可能是父布局)
rInflateChildren(parser, temp, attrs, true);
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
//下面这种情况会为我们加载的布局文件指定父布局(也就是root)
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.
//root为null时会直接返回我们在xml中定义的布局
//注意此时并没有为temp设置布局参数
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
......
return result;
}
}
接下来看一下是怎么遍历最外层布局下的所有子布局的
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}
又调用了 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();
......
} else if (TAG_MERGE.equals(name)) {
throw new InflateException(" must be the root element");
} else {
final View view = createViewFromTag(parent, name, context, attrs);//再次调用了createViewFromTagz方法创建布局实例
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflateChildren(parser, view, attrs, true);//使用了递归来遍历,每递归一次便会创建一个布局实例,直到所有布局都遍历一遍
viewGroup.addView(view, params);
}
}
......
}
接下来会通过一个例子,根据Log日志看一下XmlPullParser遍历布局文件的大致流程
下面是在布局文件里定义好的要遍历的布局
linear_layout.xml
以下是主要代码:
private void getParserFromResource(int layoutResID){
final Resources res=this.getResources();
final XmlResourceParser parser=res.getLayout(layoutResID);
try{
getInflateInfo(parser);
}catch (Exception e){
}
}
private void getInflateInfo(XmlPullParser parser)throws Exception{
int type;
//type=parser.getEventType();
type=parser.next();//用next方法也可以返回 START_DOCUMENT
//下面的Log会打印不出来,如果加了 parser.nextText() 这个方法便会出现Log打印不出来的情况,这个还是需要注意一下
//Log.d(TAG,"type name:"+typeToString(type)+" parser name:"+parser.getName()+ " textName:"+parser.nextText());
Log.d(TAG,"type name:"+typeToString(type)+" parser name:"+parser.getName()
+" depth:"+parser.getDepth());
while(type != XmlPullParser.END_DOCUMENT)
{
Log.d(TAG,"type name:"+typeToString(type)+" parser name:"+parser.getName()
+" depth:"+parser.getDepth());
switch (type)
{
case XmlPullParser.START_TAG:
//Log.d(TAG,"START_TAG"+" name:"+parser.getName());
break;
case XmlPullParser.END_TAG:
//Log.d(TAG,"END_TAG"+" name:"+parser.getName());
break;
}
//不停的向下解析
type = parser.next();
}
Log.d(TAG,"type name:"+typeToString(type)+" parser name:"+parser.getName());
}
下面是打印出的Log信息:
2020-01-18 16:54:21.173 31278-31278/com.lollo.view_myxmlparser D/MainActivity: type name:START_DOCUMENT parser name:null depth:0
2020-01-18 16:54:21.173 31278-31278/com.lollo.view_myxmlparser D/MainActivity: type name:START_DOCUMENT parser name:null depth:0
2020-01-18 16:54:21.173 31278-31278/com.lollo.view_myxmlparser D/MainActivity: type name:START_TAG parser name:LinearLayout depth:1
2020-01-18 16:54:21.173 31278-31278/com.lollo.view_myxmlparser D/MainActivity: type name:START_TAG parser name:TextView depth:2
2020-01-18 16:54:21.173 31278-31278/com.lollo.view_myxmlparser D/MainActivity: type name:START_TAG parser name:Button depth:3
2020-01-18 16:54:21.173 31278-31278/com.lollo.view_myxmlparser D/MainActivity: type name:END_TAG parser name:Button depth:3
2020-01-18 16:54:21.173 31278-31278/com.lollo.view_myxmlparser D/MainActivity: type name:END_TAG parser name:TextView depth:2
2020-01-18 16:54:21.173 31278-31278/com.lollo.view_myxmlparser D/MainActivity: type name:START_TAG parser name:Button depth:2
2020-01-18 16:54:21.173 31278-31278/com.lollo.view_myxmlparser D/MainActivity: type name:END_TAG parser name:Button depth:2
2020-01-18 16:54:21.173 31278-31278/com.lollo.view_myxmlparser D/MainActivity: type name:END_TAG parser name:LinearLayout depth:1
2020-01-18 16:54:21.173 31278-31278/com.lollo.view_myxmlparser D/MainActivity: type name:END_DOCUMENT parser name:null
2020-01-18 16:54:21.173 31278-31278/com.lollo.view_myxmlparser D/MainActivity: onCreate:
从 Log 可以看到使用XmlPullParser遍历一个布局文件时,一开始是从START_DOCUMENT的标记开始的
START_DOCUMENT 代表了一个布局文件的开始
第一次出现的 START_TAG 标记表示根布局的开始,中间的STAR_TAG、END_TAG代表了子布局或者子控件的开始和结束
欢迎批评指正