热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

LayoutInflater源码分析(二)

上一篇LayoutInflater源码分析(一)我们分析了LayoutInflater的from()方法,这节我们来分析一下inflate()方法。viewLayoutInflat

上一篇LayoutInflater源码分析(一)我们分析了LayoutInflater的from()方法,这节我们来分析一下inflate()方法。

view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_vip, parent, false);

最终都会进入到如下代码:

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;
//令人窒息操作part one~
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();

//去掉部分代码
//令人窒息操作part two ~
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 {
//令人窒息操作part three ~
// 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;
}
}

通过代码和注释我们可以看到,拿到XmlResourceParser parser是用于节点的解析。

比如part one 从头到尾遍历xml文件的标签,直到
文档结束才跳出循环。如果该xml没有开始标签,则抛异常。

part two 讲的是什么呢?TAG_MERGE=”merge”,如果读到的标签是merge,判断是否有父View,没有则抛异常,有则跳转到rInflate()解析merge的xml。

part three就是当前的标签没有其他子xml,要直接解析啦。关键方法就是createViewFromTag()方法了。

(一)rInflate()解析

void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
//获取该xml的深度 ~~
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();
//判断标签是否为requestFocus[1]。requestFocus标签于指定屏幕内的焦点View
if (TAG_REQUEST_FOCUS.equals(name)) {
parseRequestFocus(parser, parent);
//判断标签是否为tag。
//设置一个文本标签。可以通过View.getTag()或 for with View.findViewWithTag()检索含有该标签字符串的View。
//但一般最好通过ID来查询View,因为它的速度更快,并且允许编译时类型检查。
} else if (TAG_TAG.equals(name)) {
parseViewTag(parser, parent, attrs);
//判断标签是否为include。
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException(" cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
//判断标签是否为merge。如果是,则直接抛出异常,因为Merge必须为根元素,也就是深度为0的节点。
} else if (TAG_MERGE.equals(name)) {
throw new InflateException(" must be the root element");
} else {
//没啥奇奇怪怪标签了。又出现了这个createViewFromTag()方法。这个方法其实就是根据标签(节点名)称创建View。
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);
//将该view添加进Parent布局
viewGroup.addView(view, params);
}
}
//通知父View,解析完成。
if (finishInflate) {
parent.onFinishInflate();
}
}

我们点击进入rInflateChildren()方法,发现其实也是调用的rInflate()方法:

final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}

(二)createViewFromTag()解析

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
//解析view标签,注意哦,是view不是View,这个标签一般大家不太常用。[2]
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
//如果需要该标签与主题相关,需要对context进行包装,将主题信息加入context包装类ContextWrapper
//好吧其实我不知道这个是什么鬼,哈哈哈
if (!ignoreThemeAttr) {
final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
final int themeResId = ta.getResourceId(0, 0);
if (themeResId != 0) {
cOntext= new ContextThemeWrapper(context, themeResId);
}
ta.recycle();
}
//TAG_1995="blink"
if (name.equals(TAG_1995)) {
//BlinkLayou继承自FrameLayout,它包裹的内容会一直闪烁。[3]
return new BlinkLayout(context, attrs);
}

try {
View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
if (view == null) {
final Object lastCOntext= mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
//indexOf()的用法:
//返回字符中indexof(string)中字串string在父串中首次出现的位置,从0开始!没有返回-1。
//下面判断语句是对自定义View和原生的控件进行判断。如果name中包含.即为自定义View,否则为原生的View控件
//例如:

推荐阅读
author-avatar
840126054贝尔
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有