Android中LayoutInflater赏析
Android中,为了载入某个布局文件,可以利用LayoutInflator类中的inflator方法,得到View实例。
值得注意的是,除了通常使用的inflator(int resource)这个重载,LayoutInflator也支持通过XmlPullParser来获取外部资源,如inflate(XmlPullParser parser, ViewGroup root),而之前我在没有查阅官方Reference时仅凭借对其他项目源码的实例阅读,就断定不能简单地通过android自身类库来调用外部布局文件,真可谓坐井观天。
AndroidDraw是界面生成工具Droiddraw的子项目,负责Layout Resource XML在客户端的解析生效,实现了类似于LayoutInflator的功能。它使用了辅助栈来还原UI树,并通过XmlPullParser的事件来解析整个XML。源码详见这里。
Android源码可以通过SDK Manager下载得到,在android-sdk\sources\android-APILevel\文件夹中。为了了解LayoutInflater的实现,自然寻找目录下LayoutInflater.java文件。
在LayoutInflator中,我没有按照method调用的顺序去阅读,而是下意识匆忙寻找onCreateView,该方法是Factory类的方法,但是在Android源码中并不存在Factory类!原来,Factory是接口- -
需要注意两个方法:
/** * Recursive method used to descend down the xml hierarchy and instantiate views, instantiate their children, and then call onFinishInflate(). */ void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException /** * Low-level function for instantiating a view by name. This attempts to instantiate a view class of the given <var>name</var> found in this * LayoutInflater's ClassLoader. */
public final View createView(String name, String prefix, AttributeSet attrs) throws ClassNotFoundException, InflateException
注释很详细,rInflate递归地处理XML事件,并调用createView来生成具体的View,如Button等。createView中出现两个局部变量:
Constructor<? extends View> constructor = sConstructorMap.get(name); Class<? extends View> clazz = null;
问号是什么?Constructor关键字又是什么?在去年求职过程中,我才对Java泛型乃至STL有了一定的了解,更不用说Reflection。
Android官方实现与AndroidDraw作者实现区别就在于,Android源码采用了Reflection,避免了过多的条件嵌套:
clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(View.class); ... constructor = clazz.getConstructor(mConstructorSignature); return constructor.newInstance(args);
出于好奇,还给AndroidDraw作者发了邮件,询问为什么要自己重写inflate。
总结:
多读高质量的代码,从中可以学到很多东西,不仅仅是针对一个case的实现,还包括代码组织的思想。
不要坐井观天,你知道的真的太少了,不要学得什么都是半桶水,Java不知Reflection,C#不知Delegate,……