从建立一个LayoutInflater的方式咱们能够知道,LayoutInflater是系统提供的单例对象android
LayoutInflater layoutInflater = getLayoutInflater(); ↓ LayoutInflater layoutInflater = LayoutInflater.from(context); ↓ LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
boolean equals = getLayoutInflater().equals(LayoutInflater.from(this)); Log.e("equals", "value="+equals);
#输出value=true #说明LayoutInflater具备全局属性
关于Inflate方法,主要分为2组,但前2组最终也是经过调用后2组中的某一个方法来实现的缓存
inflate(int resource, ViewGroup root) inflate(int resource, ViewGroup root, boolean attachToRoot) ------------------------------------------------------------------------- inflate(XmlPullParser parser, ViewGroup root) inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
inflate方法最终会调用以下方法,固然这是必然的,由于咱们须要解析这个布局文件app
inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
关于XmlPullParser parser解析器的获取,Android内部采用了XmlpullParser解析xml,这种解析相似与SAX Parser技术,效率高,由于它以IO流的方式进行解析和读取ide
咱们来看一下XmlPullParser获取的这个实现方式布局
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(); } }
在Android内部XmlResourceParser继承自XmlPullParserthis
public interface XmlResourceParser extends XmlPullParser, AttributeSet { /** * Close this interface to the resource. Calls on the interface are no * longer value after this call. */ public void close(); }
实际咱们经过Resource获取到的是XmlResourceParserspa
XmlResourceParser xml = getResources().getXml(R.xml.gateway); XmlResourceParser layout = getResources().getLayout(R.layout.fragment_main);
这里注意,其实获取资源的时候,都会经过XmlResourceParser,只是内部进行了必要的封装而已,有兴趣的 猿猿同窗 能够查看Resource的实现代码code
再来看看inflate的实现xml
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context)mConstructorArgs[0]; mConstructorArgs[0] = mContext; View result = root; try { int type; while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { //从文档头文档开始解析,这里忽略文档头 } if (type != XmlPullParser.START_TAG) { throw new InflateException(parser.getPositionDescription() + ": No start tag found!"); } final String name = parser.getName(); if (DEBUG) { System.out.println("**************************"); System.out.println("Creating root view: " + name); System.out.println("**************************"); } 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, false); } else { // Temp is the root view that was found in the xml View temp; if (TAG_1995.equals(name)) { temp = new BlinkLayout(mContext, attrs); } else { temp = createViewFromTag(root, name, attrs); } ViewGroup.LayoutParams params = null; if (root != null) { if (DEBUG) { System.out.println("Creating params from root: " + root); } // Create layout params that match root, if supplied params = root.generateLayoutParams(attrs); if (!attachToRoot) { // Set the layout params for temp if we are not // attaching. (If we are, we use addView, below) temp.setLayoutParams(params); } } if (DEBUG) { System.out.println("-----> start inflating children"); } // Inflate all children under temp rInflate(parser, temp, attrs, true); if (DEBUG) { System.out.println("-----> done inflating children"); } // We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root != null && attachToRoot) { root.addView(temp, params); } // Decide whether to return the root that was passed in or the // top view found in xml. if (root == null || !attachToRoot) { result = temp; } } } catch (XmlPullParserException e) { InflateException ex = new InflateException(e.getMessage()); ex.initCause(e); throw ex; } catch (IOException e) { InflateException ex = new InflateException( parser.getPositionDescription() + ": " + e.getMessage()); ex.initCause(e); throw ex; } finally { // Don't retain static reference on context. mConstructorArgs[0] = lastContext; mConstructorArgs[1] = null; } return result; } }
基本上就是解析xml的过程,xml解析原本过慢,所以在开发中应该减小使用inflate,采用ViewHolder和SparseArray缓存View是一个不错的选择对象
对于属性的读取,咱们看到
XmlResourceParser parser = getResources().getLayout(resid); final AttributeSet attrs = Xml.asAttributeSet(parser);
咱们看到,使用了android.util.Xml类
public static AttributeSet asAttributeSet(XmlPullParser parser) { return (parser instanceof AttributeSet) ? (AttributeSet) parser : new XmlPullAttributes(parser); }
AttributeSet 会最终被返回给View的Context
Android自定义控件的思想,获取自定义属性通常会在构造方法中,经过TypeArray和obtainStyledAttributes(resid, attrs)方法,但obtainStyledAttributes(resid, attrs)是没有公开的方法,对于这一点要特别之处,在外部没法得到自定属性,除非从新使用XmlPullParser解析,从而获得相应的数据值。
特别是对于Android MVVM开发而言,如何访问绑定字段,对于这一问而言,咱们须要构建本身的LayoutInflater或者经过XmlPullParser解析以后与对应的view自动匹配,不事后者简单,但效率会有所损失。
综上,obtainStyledAttributes(resid, attrs)具备局限性,咱们能够利用LayoutInflater.Factory2来实现属性的获取和View的重建。
先看看LayoutInflater类中的createViewFromTag源码:
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) { if (name.equals("view")) { name = attrs.getAttributeValue(null, "class"); } if (!ignoreThemeAttr) { final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); final int themeResId = ta.getResourceId(0, 0); if (themeResId != 0) { context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); } if (name.equals(TAG_1995)) { return new BlinkLayout(context, attrs); //内部Layout,不用理会 } try { View view; if (mFactory2 != null) { view = mFactory2.onCreateView(parent, name, context, attrs); //在这里调用了Factory2的接口 } else if (mFactory != null) { view = mFactory.onCreateView(name, context, attrs);//在这里调用了Factory的接口,Factory2也继承了Factory } else { view = null; } if (view == null && mPrivateFactory != null) { view = mPrivateFactory.onCreateView(parent, name, context, attrs); //私有实现的Factory2 } if (view == null) { final Object lastContext = mConstructorArgs[0]; mConstructorArgs[0] = context; try { if (-1 == name.indexOf('.')) { view = onCreateView(parent, name, attrs); } else { view = createView(name, null, attrs); } } finally { mConstructorArgs[0] = lastContext; } } return view; } catch (InflateException e) { throw e; } catch (ClassNotFoundException e) { final InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + name); ie.initCause(e); throw ie; } catch (Exception e) { final InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + name); ie.initCause(e); throw ie; } }
咱们实现Factory2工厂便可,返回值能够为null,也能够自行实现建立View,咱们这里选择实现建立View
public class ViewCreateFactory implements Factory2 ,LayoutInflater.Filter{ private static ViewCreateFactory instance; private Context mContext; private OnInflaterListener onInflaterlistener; private LayoutInflater mInflater; private LayoutInflater.Filter mFilter; private final static String DEFAULT_VIEW_PREFIX = "android.view."; private final static String DEFAULT_WIDGET_PREFIX = "android.widget."; public void setOnInflaterListener(OnInflaterListener listener) { this.onInflaterlistener = listener; } public static ViewCreateFactory create(Context ctx,LayoutInflater inflater) //在onCreate中调用 { if(instance ==null) { synchronized (ViewCreateFactory.class) { if(instance ==null) { instance = new ViewCreateFactory(ctx); } } } instance.setOnInflaterListener(null); instance.setFilter(null); instance.setLayoutInflater(inflater); return instance; } public void setLayoutInflater(LayoutInflater inflater) { this.mInflater = inflater; this.mInflater.setFactory2(this); //建工厂设置到LayoutInflater中 this.mFilter = this.mInflater.getFilter(); this.mInflater.setFilter(this); } private ViewCreateFactory(Context context) { this.mContext = context; } public void setFilter(LayoutInflater.Filter filter) { this.mFilter = filter; } @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { return onCreateView(name, context, attrs); } @Override public View onCreateView(String name, Context context, AttributeSet attrs) { try{ View view = null; if (-1 == name.indexOf('.')) { Class<?> clazz = ReflectUtils.loadClass(mContext, DEFAULT_VIEW_PREFIX.concat(name)); if(clazz!=null) { view = mInflater.createView(name, DEFAULT_VIEW_PREFIX, attrs); //这里咱们调用LayoutInflater建立View的方法,固然也能够自定义 } else { view = mInflater.createView(name,DEFAULT_WIDGET_PREFIX, attrs); } } else { view = mInflater.createView(name, null, attrs); } if(onInflaterlistener !=null) { onInflaterlistener.onCreateView(view,attrs); } return view; } catch (Exception e) { Log.e("InflaterERROR",e.getLocalizedMessage()); e.printStackTrace(); } return null; } @Override public boolean onLoadClass(Class clazz) { onInflaterlistener.onLoadClass(clazz); if(this.mFilter!=null) { return mFilter.onLoadClass(clazz); } return true; } //在onDestory中调用,由于LayoutInflater是全局的,所以,为了让Activity回收正常,结束时必须调用此方法 public void release() { this.mInflater.setFactory2(null); this.mInflater.setFilter(null); this.mInflater = null; } public interface OnInflaterListener { //用于向外部提供属性和View,咱们能够从类外部获取到属性了 public void onCreateView(view,attrs); } }
//这里咱们调用LayoutInflater建立View的方法,固然也能够自定义实现本身建立方法 view = mInflater.createView(name, prefix, attrs);
public interface OnInflaterListener { //用于向外部提供属性和View,咱们能够从类外部获取到属性了 public void onCreateView(View view,AttributeSet attrs); }
使用方式
LayoutInflater inflater = LayoutInflater.from(context); ViewCreateFactory factory = ViewCreateFactory.create(context,inflater); factory.setOnInflaterListener(new ViewCreateFactory.OnInflaterListener{ public void onCreateView(View view,AttributeSet attrs) { //从这里获取attrs,里面包含自定义属性 } });
TextView myView = (TextView)View.inflate(context, resource, root);