1.问题:在Activity中调用setContentView方法,是如何让控件显示在屏幕上的?
2.下面以Activity为例子
2.1 调用Activity的setContentView方法
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
2.2 调用PhoneWindow的setContentView方法
public void setContentView(int layoutResID) {
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 {
//将资源id所在的布局加到mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
2.3 调用LayoutInflater的inflate方法
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
...
if (TAG_MERGE.equals(name)) {
//如果是merge标签,将xml标签解析后放入视图树
rInflate(parser, root, inflaterContext, attrs, false);
} else {
...
//将xml标签解析成类名
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
...
}
}
2.4 调用LayoutInflater的createViewFromTag方法
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
····
View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
//如果工厂不为空,就调用重写的onCreateView方法
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
···
//如果view为空
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
//标签的名字中不含有.,则说明是原生控件
//createView(name, "android.view.", attrs);
view = onCreateView(parent, name, attrs);
} else {
//标签名字中含有.,则说明是自定义控件
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
}
2.5 调用LayoutInflater的createView方法
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
···
//拼接名字
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
···
//通过反射创建控件
final View view = constructor.newInstance(args);
···
}
2.6 图样演示
setContentView.png
2.11 填充布局的三种方式
LayoutInflater.from(context);
inflate(int resource, ViewGroup root, boolean attachToRoot) ;
inflater.inflate(R.layout.item_list, null); —-> 显示布局变成默认包裹内容
inflater.inflate(R.layout.item_list, parent,false);—–> 正常显示布局
借鉴别人的理解:
- 如果root为null,attachToRoot将失去作用,设置任何值都没有意义。
- 如果root不为null,attachToRoot设为true,则会给加载的布局文件的指定一个父布局,即root。
- 如果root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效。
- 在不设置attachToRoot参数的情况下,如果root不为null,attachToRoot参数默认为true。
3总结
- 当setContentView设置显示OK以后会回调Activity的onContentChanged方法。Activity的各种View的findViewById()方法等都可以放到该方法中,系统会帮忙回调
- 当我们自定义View时在构造函数inflate一个xml后可以实现onFinishInflate这个方法一些自定义的逻辑
网友评论