從建立一個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);