LayoutInflater使用
2016-05-08 13:56
615 查看
一、LayoutInflater 的使用。
LayoutInflater 可以把某个xml的layout转换为一个view对象。使用LyaoutInflater,首先要获取LayoutInflater 对象,然后调用inflate方法。
View view = LayoutInflater.from(context).inflate(R.layout.layout名, null);
1、获取 LayoutInflater 对象
常用的获取 LayoutInflater 对象的方法有三种:
1)、LayoutInflater inflater = LayoutInflater.from(context);2)、LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
3)、LayoutInflater inflater = Activity.getLayoutInflater();
使用 LayoutInflater 需要现有 LayoutInflater 对象,LayoutInflater 中,有一个
public static LayoutInflater from(Context context) 的方法,可以获取 LayoutInflater 对象,其中的实现为:
LayoutInflater.java 类中:
public static LayoutInflater from(Context context) { LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (LayoutInflater == null) { throw new AssertionError("LayoutInflater not found."); } return LayoutInflater; }
获取 LayoutInflater 对象还可以调用 Activity.getLayoutInflater() 方法。
因为 Activity 比较常用,所以专门拉出来说下。
Activity.getLayoutInflater()的实现在 PhoneWindow 类中,PhoneWindow 类在初始化的时候,会创建LayoutInflate对象,getLayoutInflater() 方法只是返回了LayoutInflate对象。
PhoneWindow.java 部分代码如下:
private LayoutInflater mLayoutInflater; public PhoneWindow(Context context) { super(context); mLayoutInflater = LayoutInflater.from(context); } public LayoutInflater getLayoutInflater() { return mLayoutInflater; }
三种方法的本质都是使用第2种方法实现的,不过第1种方法在整个Android系统中是使用最多的。
2、使用 LayoutInflater 中的方法获取View对象
LayoutInflater 中,inflate方法有4种调用方法:
1)、public View inflate(int resource, ViewGroup root)2)、public View inflate(XmlPullParser parser, ViewGroup root)
3)、public View inflate(int resource, ViewGroup root, boolean attachToRoot)
4)、public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
第一和第二种比较简单,都是根据root是不是null,然后调用三个参数的inflate方法。代码如下:
public View inflate(int resource, ViewGroup root) { return inflate(resource, root, root != null); } public View inflate(XmlPullParser parser, ViewGroup root) { return inflate(parser, root, root != null); }
第三种方法实际是也是调用第四种方法的,第三种方法的实现为:
public View inflate(int resource, ViewGroup root, boolean attachToRoot) { if (DEBUG) System.out.println("INFLATING from resource: " + resource); XmlResourceParser parser = getContext().getResources().getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } }
下来看看inflate方法,不同参数会有怎样的效果吧:一共会有4中情况:
1)、inflate(layout, null, true); 返回 XmlPullParser 对应的View, ViewGroup.LayoutParams = null; 2)、inflate(layout, null, false);返回 XmlPullParser 对应的View, ViewGroup.LayoutParams = null; 3)、inflate(layout, root, true); 返回 root,XmlPullParser 在root中,ViewGroup.LayoutParams 不为null; 4)、inflate(layout, root, false);返回 XmlPullParser 对应的View, ViewGroup.LayoutParams 不为null;
二、inflate 的实现
结论在上面已经说了,下面说说实现,实现是用源码讲的,有兴趣可以看看。//mConstructorArgs是在createView的时候,反射构造函数的参数 final Object[] mConstructorArgs = new Object[2]; public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { //XmlPullParser类继承了AttributeSet final AttributeSet attrs = Xml.asAttributeSet(parser); //这里存下原来的Context,inflate 执行完会再变回原来的Context Context lastContext = (Context)mConstructorArgs[0]; mConstructorArgs[0] = mContext; //返回数据,开始时root,最后会根据 root 和 attachToRoot 的值改变 View result = root; try { int type; // 这个while比较重要,会调用parser.next()到某个条件,后面的parser才能正常使用 while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { // Empty } ...... final String name = parser.getName(); if (TAG_MERGE.equals(name)) { // TAG_MERGE = "merge"? ...... rInflate(parser, root, attrs, false); } else { View temp; if (TAG_1995.equals(name)) { // TAG_1995 = "blink"? temp = new BlinkLayout(mContext, attrs); } else { //生成根View temp = createViewFromTag(root, name, attrs); // 正常情况下,都会调这个 } ViewGroup.LayoutParams params = null; if (root != null) { params = root.generateLayoutParams(attrs); if (!attachToRoot) { temp.setLayoutParams(params); } } //会递归的实例化子View对象,生成View树 rInflate(parser, temp, attrs, true); //如果attachToRoot为true并且root不为空,就会调用addView,并且返回root if (root != null && attachToRoot) { root.addView(temp, params); } //如果attachToRoot为false,返回的是创建出的View if (root == null || !attachToRoot) { result = temp; } } } catch (...... e) { ...... } finally { mConstructorArgs[0] = lastContext; //变回原来的Context mConstructorArgs[1] = null; } return result; }
其中会调用createViewFromTag方法找到根View,然后会调用rInflate方法生成View树。
1、createViewFromTag方法
View createViewFromTag(View parent, String name, AttributeSet attrs) { if (name.equals("view")) { name = attrs.getAttributeValue(null, "class"); } try { View view; if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, mContext, attrs); else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs); // 一般会执行这句 else view = null; ...... if (view == null) { // name中有"."证明是自定义控件,否则是系统控件 if (-1 == name.indexOf('.')) { view = onCreateView(parent, name, attrs); } else { view = createView(name, null, attrs); } } return view; } catch (...... e) { ...... } }
onCreateView的实现为:
protected View onCreateView(View parent, String name, AttributeSet attrs) throws ClassNotFoundException { return onCreateView(name, attrs); }
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)返回的其实不是LayoutInflater对象,而是 PhoneLayoutInflater 对象,PhoneLayoutInflater 类继承 LayoutInflater 类。
所以调用onCreateView(name, attrs)方法,会先调用PhoneLayoutInflater的onCreateView方法。
PhoneLayoutInflater 类的onCreateView方法:
private static final String[] sClassPrefixList = { "android.widget.", "android.webkit." }; protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { for (String prefix : sClassPrefixList) { try { View view = createView(name, prefix, attrs); if (view != null) { return view; } } catch (ClassNotFoundException e) { // In this case we want to let the base class take a crack // at it. } } return super.onCreateView(name, attrs); }
所以会调用:
View view = createView(name, “android.widget.”, attrs);和
View view = createView(name, “android.webkit.”, attrs);
如果view仍然为null,则会调用LayoutInflater的onCreateView方法。
LayoutInflater 的onCreateView方法:
protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { return createView(name, "android.view.", attrs); }
可以看到,最终会返回createView(name, “android.view.”, attrs);
这样看来,createViewFromTag返回的view是由createView创建的,那么看看createView的内容:
LayoutInflater 的createView方法:
//View 构造方法的Map private static final HashMap<String, Constructor<? extends View>> sConstructorMap = new HashMap<String, Constructor<? extends View>>(); //反射 View 的构造方法时,传入的类名参数 final Object[] mConstructorArgs = new Object[2]; //反射 View 的构造方法时,传入的类名 static final Class<?>[] mConstructorSignature = new Class[] { Context.class, AttributeSet.class}; //过滤时使用的Map,这里没有仔细看,有兴趣可以自己看看 private HashMap<String, Boolean> mFilterMap; public final View createView(String name, String prefix, AttributeSet attrs) throws ClassNotFoundException, InflateException { // Constructor 类是反射构造方法时使用的,如果不知道,搜一下java反射机制 // 这里会先在View 构造方法的Map中找构造方法 Constructor<? extends View> constructor = sConstructorMap.get(name); Class<? extends View> clazz = null; try { // 如果 constructor == null,说明这个View的对象还没有被创建过 if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(View.class); if (mFilter != null && clazz != null) { boolean allowed = mFilter.onLoadClass(clazz); if (!allowed) { failNotAllowed(name, prefix, attrs); } } // 获取 View 的构造方法 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).asSubclass(View.class); 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); } } } //mConstructorArgs变量在inflate中,对mConstructorArgs[0]进行了赋值 Object[] args = mConstructorArgs; args[1] = attrs; // 反射生成 View 对象 return constructor.newInstance(args); } catch (...... e) { ...... } }
2、rInflate方法
private static final String TAG_MERGE = "merge"; private static final String TAG_INCLUDE = "include"; private static final String TAG_1995 = "blink"; private static final String TAG_REQUEST_FOCUS = "requestFocus"; void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { 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(); if (TAG_REQUEST_FOCUS.equals(name)) { parseRequestFocus(parser, parent); } else if (TAG_INCLUDE.equals(name)) { if (parser.getDepth() == 0) { throw new InflateException("<include /> cannot be the root element"); } parseInclude(parser, parent, attrs); } else if (TAG_MERGE.equals(name)) { throw new InflateException("<merge /> must be the root element"); } else if (TAG_1995.equals(name)) { final View view = new BlinkLayout(mContext, attrs); final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); rInflate(parser, view, attrs, true); viewGroup.addView(view, params); } else { //下面这段最重要,会递归的实例化子View对象 //实例化view对象 final View view = createViewFromTag(parent, name, attrs); final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); //把view当做parent递归 rInflate(parser, view, attrs, true); viewGroup.addView(view, params); } } //结束的时候,会根据finishInflate参数,判断是否回调,一般为true if (finishInflate) parent.onFinishInflate(); }
原创文章,转载请注明出处,谢谢~
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories