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);