1、概述
LayoutInflater setFactory是什麼,幹什麼用的,這裏不多說,這裏推薦鴻洋大神的一篇以及上一篇換膚技術博客,相信解讀完之後你就知道它有什麼魔力,以及能熟練運用Factory。
用法:http://blog.csdn.net/lmj623565791/article/details/51503977
換膚技術:http://blog.csdn.net/zhongwn/article/details/52891902
LayoutInflater setFactory確實是個好東西,v7包新組建的新特性向下兼容都是通過這玩意來做到的,也很佩服谷歌這幫工程師的設計,因爲這個Factory就像是加載view的後門,我相信他們也是想到了後續可能用到而設計的框架。好了不多說了,接下來我們就一探究竟,看看它有什麼魅力與魔力。
2、源碼解讀
LayoutInflater.from(this).inflate(R.Layout.xml,null);
View.inflate(this, android.R.layout.activity_list_item,null);而第二種我們跟進去看看
public static View inflate(Context context, int resource, ViewGroup root) { LayoutInflater factory = LayoutInflater.from(context); return factory.inflate(resource, root); }發現也是調用第一種方式來加載佈局。那麼我們就來分析分析第一種方式,而且也是順着加載佈局開始一點點來分析。接下來看看from()這個靜態方法
/** * Obtains the LayoutInflater from the given context. */ 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; }這裏只是通過context從系統服務中獲取LayoutInflater一個實例,所以我們重點放在inflate()方法中,進入LayoutInfater中你會發現裏邊有4個inflate方法,其中有2個分別調用2個,而最後都統一調用一個方法來解析,這裏貼着兩個方法:
public View inflate(int resource, ViewGroup root, boolean attachToRoot) { final Resources res = getContext().getResources(); if (DEBUG) { Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" (" + Integer.toHexString(resource) + ")"); } final XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } }這個方法很短,一看就知道它只是簡單的獲取Resource資源,然後通過我們傳進來的R.layout.id獲取一個xml解析器,最後還是調用到第二個方法中;其中有兩個方法值得關注與思考如下:
getContext().getResources();
res.getLayout(resource);它是怎麼獲取Resources實例以及getLayout是怎麼創建這個parser解析器實例的,這裏我們暫時不做分析,留給讀者自行分析。這裏我們只要知道它獲取的是R.layout.xml佈局的解析器,接下來我們有理由相信所有的開始源於第二個方法咯。瞅瞅看便知咯
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context)mConstructorArgs[0]; mConstructorArgs[0] = mContext; View result = root; try { // Look for the root node. 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!"); } 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, false); } else { // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, attrs, false); 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, 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; } Trace.traceEnd(Trace.TRACE_TAG_VIEW); return result; } }這個方法有點長,我們挑重點看:
final AttributeSet attrs = Xml.asAttributeSet(parser);這裏通過解析器獲取整個xml屬性集合attrs即將xml存儲在attrs,它相當於xml佈局的文檔樹。
// Look for the root node. 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!"); }
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, false, false); } else { // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, attrs, false);
View createViewFromTag(View parent, String name, AttributeSet attrs, boolean inheritContext) { //省略多餘代碼………… if (name.equals(TAG_1995)) { // Let's party like it's 1995! return new BlinkLayout(viewContext, attrs); } try { View view; if (mFactory2 != null) { view = mFactory2.onCreateView(parent, name, viewContext, attrs); } else if (mFactory != null) { view = mFactory.onCreateView(name, viewContext, attrs); } else { view = null; } if (view == null && mPrivateFactory != null) { view = mPrivateFactory.onCreateView(parent, name, viewContext, attrs); } if (view == null) { //省略多餘代碼………… if (-1 == name.indexOf('.')) { view = onCreateView(parent, name, attrs); } else { view = createView(name, null, attrs); } //省略多餘代碼………… } return view;第一個if判斷當前節點(第一次時是根節點,其它都是子節點)是否是TAG_1995,
TAG_1995 = "blink"這個當然不是,我們的組件中從沒見過blink這個節點也沒用過,只知道是繼承自FrameLayout喜歡探究的自行研究,所以我們已正常的思路走下去。往下看,亮點來了……
private Factory mFactory; private Factory2 mFactory2; private Factory2 mPrivateFactory;
public interface Factory { public View onCreateView(String name, Context context, AttributeSet attrs); }
public interface Factory2 extends Factory { public View onCreateView(View parent, String name, Context context, AttributeSet attrs); }功能基本一樣,都是同一個Factory。
//省略多餘代碼………… if (-1 == name.indexOf('.')) { view = onCreateView(parent, name, attrs); } else { view = createView(name, null, attrs); } //省略多餘代碼…………
protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { return createView(name, "android.view.", attrs); }
public void setFactory(Factory factory) { if (mFactorySet) { throw new IllegalStateException("A factory has already been set on this LayoutInflater"); } if (factory == null) { throw new NullPointerException("Given factory can not be null"); } mFactorySet = true; if (mFactory == null) { mFactory = factory; } else { mFactory = new FactoryMerger(factory, null, mFactory, mFactory2); } } /** * Like {@link #setFactory}, but allows you to set a {@link Factory2} * interface. */ public void setFactory2(Factory2 factory) { if (mFactorySet) { throw new IllegalStateException("A factory has already been set on this LayoutInflater"); } if (factory == null) { throw new NullPointerException("Given factory can not be null"); } mFactorySet = true; if (mFactory == null) { mFactory = mFactory2 = factory; } else { mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2); } } /** * @hide for use by framework */ public void setPrivateFactory(Factory2 factory) { if (mPrivateFactory == null) { mPrivateFactory = factory; } else { mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory); } }
private static class FactoryMerger implements Factory2 { private final Factory mF1, mF2; private final Factory2 mF12, mF22; FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) { mF1 = f1; mF2 = f2; mF12 = f12; mF22 = f22; } public View onCreateView(String name, Context context, AttributeSet attrs) { View v = mF1.onCreateView(name, context, attrs); if (v != null) return v; return mF2.onCreateView(name, context, attrs); } public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs) : mF1.onCreateView(name, context, attrs); if (v != null) return v; return mF22 != null ? mF22.onCreateView(parent, name, context, attrs) : mF2.onCreateView(name, context, attrs); } }
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, false); } else { // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, attrs, false); 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, true);
// Inflate all children under temp rInflate(parser, temp, attrs, true, true);
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs, boolean finishInflate, boolean inheritContext) 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_TAG.equals(name)) { parseViewTag(parser, parent, attrs); } else if (TAG_INCLUDE.equals(name)) { if (parser.getDepth() == 0) { throw new InflateException("<include /> cannot be the root element"); } parseInclude(parser, parent, attrs, inheritContext); } else if (TAG_MERGE.equals(name)) { throw new InflateException("<merge /> must be the root element"); } else { final View view = createViewFromTag(parent, name, attrs, inheritContext); final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); rInflate(parser, view, attrs, true, true); viewGroup.addView(view, params); } } if (finishInflate) parent.onFinishInflate(); }
一:這裏假設這個佈局沒有使用merge和include
else if (TAG_TAG.equals(name)) { parseViewTag(parser, parent, attrs); } else if (TAG_INCLUDE.equals(name)) { if (parser.getDepth() == 0) { throw new InflateException("<include /> cannot be the root element"); } parseInclude(parser, parent, attrs, inheritContext);
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, false); } else { // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, attrs, false); 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, true);