关于View的创建以及ViewGroup的遍历
2020-04-24 本文已影响0人
remax1
关于view的加载
关于view的创建
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
上篇博文提到了在解析xml时,会调用inflate方法,其中这个temp就是我们要创建的view对象。来看看这两个个参数分别代表什么意思。
final AttributeSet attrs = Xml.asAttributeSet(parser);
final String name = parser.getName();
不难看出,都时xml解析根视图得来的属性。接着去看看createViewFromTag()方法干了啥事。
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
···
try {
View view = tryCreateView(parent, name, context, attrs);
//一般情况下,view的创建走的是这一段代码
//自定义控件需要全类名
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {//系统控件
view = onCreateView(context, parent, name, attrs);
} else {
//自定义控件
view = createView(context, name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
} catch (Exception e) {
····
}
接下来去看看onCreateView()做了啥事,最终还是回调了createView()方法
public final View createView(@NonNull Context viewContext, @NonNull String name,
@Nullable String prefix, @Nullable AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Objects.requireNonNull(viewContext);
Objects.requireNonNull(name);
//从map中读取构造函数
Constructor<? extends View> constructor = sConstructorMap.get(name);
if (constructor != null && !verifyClassLoader(constructor)) {
constructor = null;
sConstructorMap.remove(name);
}
Class<? extends View> clazz = null;
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
//map中没有,就通过完整包名历用反射来加载
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
mContext.getClassLoader()).asSubclass(View.class);
if (mFilter != null && clazz != null) {
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, prefix, viewContext, attrs);
}
}
constructor = clazz.getConstructor(mConstructorSignature);
constructor.setAccessible(true);
//缓存
sConstructorMap.put(name, constructor);
} else {
···
}
Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = viewContext;
Object[] args = mConstructorArgs;
args[1] = attrs;
try {
//view对象此时已经被创建了
final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
// Use the same context when inflating ViewStub later.
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
return view;
} finally {
mConstructorArgs[0] = lastContext;
}
} catch (NoSuchMethodException e) {
···
} catch (ClassCastException e) {
···
} catch (ClassNotFoundException e) {
···
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
整个流程是如果是第一次加载,就把加载的view对象放进sConstructorMap中。这样就可以知道view是通过反射来加载到内存中。当然这里是只加载了根视图,其它的子view在何时加载。也是在xml解析时有个
//加载子控件
rInflate(parser, root, inflaterContext, attrs, false);
去看看里面到底干了啥。
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)) {//include标签
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {//merge标签
throw new InflateException("<merge /> must be the root element");
} else {
//前面分析过,创建view对象的。
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
//递归来了。再次会调用rinflate(),深度优先
rInflateChildren(parser, view, attrs, true);
//解析的view添加进parent中
viewGroup.addView(view, params);
}
}
if (pendingRequestFocus) {
parent.restoreDefaultFocus();
}
if (finishInflate) {
parent.onFinishInflate();
}
}
总结
view对象的创建和viewGroup的遍历已经大致看完了,view对象是通过反射创建,viewgroup的遍历是按深度优先的算法遍历,遍历完成后添加进parent中。