LayoutInflater 到底怎么把xml添加到decorview
2017-05-14 21:10
369 查看
LayoutInflater 到底怎么把xml添加到decorview
Actity调用setContentView时初始化decorview后会根据layoutResID加载布局。
下面是源码加载过程:
PhoneWindow.java:
LayoutInflater.java:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
//将布局id和容器id传入
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
//根据layoutId获取xml解析器
final XmlResourceParser parser = res.getLayout(resource);
try {
//将xml解析器和ViewGroup容器、是否加载到根节点下传入 inflate方法中
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
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; //把(contentPerent)ViewGroup赋值给result
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)) {//判断根节点是否为merge标签
if (root == null || !attachToRoot) {//判断root是否为空,merge必须添加到ViewGroup里
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {//当根节点不是merge标签时
// 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);//调用content的FrameLayout的属性解析
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) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (Exception e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
return result;
}
}
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}
/**
* Recursive method used to descend down the xml hierarchy and instantiate
* views, instantiate their children, and then call onFinishInflate().
* <p>
* <strong>Note:</strong> Default visibility so the BridgeInflater can
* override it.
*/
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) 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_TAG.equals(name)) {
parseViewTag(parser, parent, attrs);
} else if (TAG_INCLUDE.equals(name)) {//如果标签名为include
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");//include 标签不能作为根节点
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {//如果标签名为merge
throw new InflateException("<merge /> must be the root element"); //merge标签只能作为根节点 //merge标签加载后会减少节点层级
} 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);
}
}
//解析merge标签时finishInflate为true
if (finishInflate) {
parent.onFinishInflate();
} }
LayoutInflater解析的过程图,通过深度优先遍历:
Actity调用setContentView时初始化decorview后会根据layoutResID加载布局。
下面是源码加载过程:
PhoneWindow.java:
@Override public void setContentView(int layoutResID) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens. if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); }
LayoutInflater.java:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
//将布局id和容器id传入
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
//根据layoutId获取xml解析器
final XmlResourceParser parser = res.getLayout(resource);
try {
//将xml解析器和ViewGroup容器、是否加载到根节点下传入 inflate方法中
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
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; //把(contentPerent)ViewGroup赋值给result
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)) {//判断根节点是否为merge标签
if (root == null || !attachToRoot) {//判断root是否为空,merge必须添加到ViewGroup里
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {//当根节点不是merge标签时
// 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);//调用content的FrameLayout的属性解析
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) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (Exception e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
return result;
}
}
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}
/**
* Recursive method used to descend down the xml hierarchy and instantiate
* views, instantiate their children, and then call onFinishInflate().
* <p>
* <strong>Note:</strong> Default visibility so the BridgeInflater can
* override it.
*/
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) 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_TAG.equals(name)) {
parseViewTag(parser, parent, attrs);
} else if (TAG_INCLUDE.equals(name)) {//如果标签名为include
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");//include 标签不能作为根节点
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {//如果标签名为merge
throw new InflateException("<merge /> must be the root element"); //merge标签只能作为根节点 //merge标签加载后会减少节点层级
} 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);
}
}
//解析merge标签时finishInflate为true
if (finishInflate) {
parent.onFinishInflate();
} }
LayoutInflater解析的过程图,通过深度优先遍历:
相关文章推荐
- LayoutInflater 是怎么把xml添加到decorview?
- Android布局中LayoutInflater的使用(利用代码添加xml形式的Layout布局)
- android更新到4.0后的,我xml写的界面无法在graphical layout中显示,怎么处理?
- 获得xml解析器LayoutInflater 实例的三种方式
- Android stutio 中怎么将XML中文件快速findById——Android Layout ID Converter插件
- Android LayoutInflater 动态地添加删除View
- 使用LayoutInflater获取XML配置的Layout
- 在xml中有一个LinearLayout,在fragment中动态加入多个Button,为其赋值并添加监听,然后加入到LinearLayout
- 从LayoutInflater分析XML布局解析成View的树形结构的过程
- 11_Android中HttpClient的应用,读取网络xml及xml解析流,Handler的应用,LayoutInflater的使用,SmartImageView的使用
- LayoutInflater和inflate()方法的用法 (自定义View中加载xml布局)
- Activity到底是啥?生命周期是怎么回事?xml有什么关系?
- 怎么在android的XML文件中添加注释
- 使用LayoutInflater添加一个布局引用
- duilib中添加自定义控件之后怎么能够在xml文件中配置使用
- 怎么为struts.xml或dwr.xml的编写添加自动提示功能
- LayoutInflater作用是将layout的xml布局文件实例化为View类对象。
- Android中将xml布局文件转化为View树的过程分析(下)-- LayoutInflater源码分析
- 从LayoutInflater分析XML布局解析成View的树形结构的过程
- xml中搭建布局,当添加控件的时候自动生成的<Button android:layout_width="" android:layout_height="" />变成一行了