上次讲到下面这么一段代码,这段代码的作用就是解析xml文件成为view并显示到屏幕上的。
@Override
//设置contentview,也就是activity或fragment加载视图,即view的函数,接受的参数是资源id
public void setContentView(int layoutResID) {
//mContentParent是个ViewGroup ,这里mContentParent == null肯定是成立的
if (mContentParent == null) {
//成立后就会运行这一句话,从字面意思就可以看出,是安装解析器来着
installDecor();
} else {
mContentParent.removeAllViews();
}
//这一句话开始解析并初始化资源了
mLayoutInflater.inflate(layoutResID, mContentParent);
//取得相应的回调
final Callback cb = getCallback();
if (cb != null) {
//一旦有回调被调用了,就要通知视图改变
cb.onContentChanged();
}
}
纵观上面的一段代码,可能最有用的无非这么一句
mLayoutInflater.inflate(layoutResID, mContentParent);
他解析了id为layoutResId的资源。
mLayoutInflater这个成员变量,说起这个东西。又是一堆废话要讲。大家千万不要嫌我啰嗦,因为最终我会回到一个非常有用的主题,在阐述这个主题之前,这些“小知识”都是必备的,就比如说爱爱之前总要来点前戏对吧。
如果你听说过Fragment,那么应该知道fragment中加载视图是通过类似
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View layout = inflater.inflate(R.layout.main_activity_menu, null);
initLayouView(layout);
return layout;
}
这样的代码实现的。
这就是inflate的作用,他就是用来解析xml并显示到屏幕的,至于解析的代码我这边就不多做阐述了,毕竟已经超越了我们的主题太远。我们还是看inflate是如何给解析的xml分配内存,并添加到view上的吧!
这里是LayoutInflater.java的源代码
其中inflate函数的实现在这里:
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final AttributeSet attrs = Xml.asAttributeSet(parser);
mConstructorArgs[0] = mContext;
View result = root;
try {
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!");
}
//这里取得XML标签名字,比如说LinearLayout
final String name = parser.getName();
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, attrs);
} else {
// 在此处创建视图,参数是前面取得的name,也就是说取得了LinearLayout开始new这个对象了!第二个参数就是这个视图的属性
View temp = createViewFromTag(name, attrs);
//以下的代码省略......
以上的代码中,注意这一行:
View temp = createViewFromTag(name, attrs);
这一行就是通过解析tag来创建view的,这个方法的实现如下:
//这里的整个过程是:先判断是不是view,如果是的话,再取得属性值
View createViewFromTag(String name, AttributeSet attrs) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
try {//这里的mFactory是一个叫做Factory的interface,在Inflater类构造的时候新建了mFactory对象
View view = (mFactory == null) ? null : mFactory.onCreateView(name,
mContext, attrs);
if (view == null) {
if (-1 == name.indexOf('.')) {
view = onCreateView(name, attrs);
} else {
view = createView(name, null, attrs);
}
}
//以下代码省略...
再关注里面的
view = createView(name, null, attrs);
这句话,正式开始创建了!
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
//这里取得了xml的标签,比如说LinearLayout,这个构造器类是通过java的反射获取对象以及参数
Constructor constructor = sConstructorMap.get(name);
Class clazz = null;
try {
if (constructor == null) {
// 这个是我们熟悉的类加载器,是第二种初始化对象的方法(第一种是直接new出来)
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name);
if (mFilter != null && clazz != null) {
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
}
constructor = clazz.getConstructor(mConstructorSignature);
sConstructorMap.put(name, constructor);
} else {
// If we have a filter, apply it to cached constructor
if (mFilter != null) {
// Have we seen this name before?
Boolean allowedState = mFilterMap.get(name);
if (allowedState == null) {
// New class -- remember whether it is allowed
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name);
boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
mFilterMap.put(name, allowed);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
} else if (allowedState.equals(Boolean.FALSE)) {
failNotAllowed(name, prefix, attrs);
}
}
}
Object[] args = mConstructorArgs;
args[1] = attrs;
//上面取得了view,在这里又取得了attrs,换句话说,既取得了对象(比如说LinearLayout),又取得了参数(比如说高度,宽度等等)
return (View) constructor.newInstance(args);//以下代码省略...
创建成功了!