关于LayoutInflater的用法
TamMuhammad
8年前
<p>废话不多说,直接上代码:</p> <p>常用的:</p> <pre> <code class="language-java">View inflate = View.inflate(context, resource, null);</code></pre> <p>是不是经常用这种方式来读取xml,生成view.</p> <p>如果你点开源码去看就会知道,调用的其实是LayoutInflater</p> <pre> <code class="language-java">public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) { LayoutInflater factory = LayoutInflater.from(context); //这里需要一个上下文 return factory.inflate(resource, root);//调用的是LayoutInflater的inflate方法. }</code></pre> <p>为什么要传入一个上下文呢?</p> <pre> <code class="language-java">public static layoufrom(Context context) { //context.getSystemService,这不是activity里获取系统服务类的方法么. //看看介绍LAYOUT_INFLATER_SERVICE LayoutInflater 取得xml里定义的view LayoutInflater LayoutInflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (LayoutInflater == null) { //如果拿不到这个服务类就会抛出异常. throw new AssertionError("LayoutInflater not found."); } return LayoutInflater; }</code></pre> <p>可以看出,LayoutInflater,其实是通过context 的getSystemService获取到的系统服务对象.</p> <p>接着往下看,查源码,一路找到contextImpl类,这是抽象类context的实现类</p> <h2><strong>系统关键代码:</strong></h2> <pre> <code class="language-java">//这里可以理解为一种单例模式,通过hashmap来保存多个单例,并通过key,来获取相应的单例 //但是这里并不是直接保存单例,而保存了生成单例的对应工厂. private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP = new HashMap<String, ServiceFetcher>(); private static int sNextPerContextServiceCacheIndex = 0; private static void registerService(String serviceName, ServiceFetcher fetcher) { if (!(fetcher instanceof StaticServiceFetcher)) { //将初始化时的顺序赋值给ServiceFetcher中的mContextCacheIndex fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++; } //将生成的ServiceFetcher工厂保存起来. SYSTEM_SERVICE_MAP.put(serviceName, fetcher); } //通过静态代码块,创建覆盖了createService()方法的ServiceFetcher工厂来绑定对应的系统服务. static { registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext()); }}); //很多registerService(String serviceName, ServiceFetcher fetcher); ....... } @Override public Object getSystemService(String name) { //通过名字拿到对应的工厂,来获取对应的服务对象 ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name); return fetcher == null ? null : fetcher.getService(this); } static class ServiceFetcher { int mContextCacheIndex = -1; /** * Main entrypoint; only override if you don't need caching. */ //上面注释的大概意思是,如果不需要缓存,就复写这个方法. public Object getService(ContextImpl ctx) { //ContextImpl中的service缓存集合 ArrayList<Object> cache = ctx.mServiceCache; Object service; synchronized (cache) { if (cache.size() == 0) { //如果没有缓存,则添加sNextPerContextServiceCacheIndex数量的长度. for (int i = 0; i < sNextPerContextServiceCacheIndex; i++) { cache.add(null); } else { //如果有缓存,则通过该工厂保存的mContextCacheIndex角标从contextImpl的cache中拿到service service = cache.get(mContextCacheIndex); if (service != null) { //不为null就直接返回 return service; } } //如果为null,则调用这个工厂的生成方法,来生成对应的系统服务 //就通过静态代码块里面,重写了createService方法的匿名类来获取 service = createService(ctx); //这里通过代码块里面的生成顺序,来缓存service cache.set(mContextCacheIndex, service); //返回我们需要的service return service; } } }</code></pre> <p>通过这块内容</p> <pre> <code class="language-java">registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext()); }});</code></pre> <p>可以看到是通过PolicyManager来获取LayoutInflater实例的</p> <p>接着看下去</p> <pre> <code class="language-java">private static final IPolicy sPolicy; .......... public static LayoutInflater makeNewLayoutInflater(Context context) { return sPolicy.makeNewLayoutInflater(context); }</code></pre> <p>这里IPolicy 是个接口,而sPolicy是通过反射生成的</p> <p>PolicyManager主要用于创建Window类、LayoutInflater类和WindowManagerPolicy类,它扮演着简单工厂模式中的工厂类角色,而抽象产品角色由IPolicy接口实现,具体产品角色由Policy类实现。</p> <h2><strong>看看LayoutInflater用法:</strong></h2> <pre> <code class="language-java">//1.传入xml的解析,父容器,用的较少 public View inflate(XmlPullParser parser, ViewGroup root) { return inflate(parser, root, root != null); } //2.传入资源Id.与父容器,走到方法3 public View inflate(int resource, ViewGroup root) { return inflate(resource, root, root != null); } //3.传入资源Id.与父容器,是否加载到父容器,用的较多 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 { //传入xml的解析,父容器,是否直接添加到父t容器 return inflate(parser, root, attachToRoot); } finally { parser.close(); }}</code></pre> <pre> <code class="language-java">//4.发现1,2,3,最终都是走到这里 /** * parser xml资源 * root 父容器 * attachToRoot 是否直接加载到父容器中 **/ 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 { //解析xml,代码较多,部分代码省略 //1.如果xml的根节点为merge,则root不能为null,否则抛出异常 //2.如果根节点为blink,则生成BlinkLayout作为根布局 //3.如果1,2不成立,则根据根节点名称,生成对应的根布局 if (TAG_1995.equals(name)) { //TAG_1995 ="blink"; temp = new BlinkLayout(mContext, attrs); } else { temp = createViewFromTag(root, name, attrs); } //4.如果root不为null,则拿到root的layoutparams,来设置根布局的layoutparams. if (root != null) { params = root.generateLayoutParams(attrs); if (!attachToRoot) { temp.setLayoutParams(params); } } //5.通过解析出的根布局,然后解析其包含的所有子控件. rInflate(parser, temp, attrs, true); //6.如果传入的root不为null并且attachToRoot为true,则将解析出来的view添加到root容器中 if (root != null && attachToRoot) { root.addView(temp, params); } //7.如果传入的root为null或者attachToRoot为false,则不添加 if (root == null || !attachToRoot) { result = temp; } } finally { // Don't retain static reference on context. mConstructorArgs[0] = lastContext; mConstructorArgs[1] = null; } Trace.traceEnd(Trace.TRACE_TAG_VIEW); return result; //返回解析出来的 }}</code></pre> <h2><strong>总结:</strong></h2> <p>举例:</p> <pre> <code class="language-java">//例1.生成view,但不指定父容器,如果父容器为null,那么设置ture还是false结果都一样 LayoutInflater.from(context).inflate(id,null); //例2.结果和1一样, LayoutInflater.from(context).inflate(id,null,false); //例3.结果和1一样 LayoutInflater.from(context).inflate(id,null,true); //例4.生成view,指定父容器,并添加到其中,获取parent的Layoutparmas设置view的Layoutparmas. LayoutInflater.from(context).inflate(id,parent,true); //例5.生成view,指定父容器,不添加到其中,获取parent的Layoutparmas设置view的Layoutparmas. LayoutInflater.from(context).inflate(id,parent,false);</code></pre> <p>用例1,2,3生成的view是没有Layoutparmas的.所以必须手动设置,不然xml里面设置的属性会失效</p> <p>用例4,5设生成的view,不管设置的true还是false,生成的view都会从parent中得到Layoutparmas</p> <p>,区别在于,是否添加到parent中</p> <p>如果使用例4,则不要再去parent.addview(),否则会抛出异常</p> <pre> <code class="language-java">if (child.getParent() != null) { throw new IllegalStateException( "The specified child already has a parent." "You must call removeView() on the child's parent first."); }</code></pre> <p>使用例5,则可以使用parent.addview().</p> <p> </p> <p>来自:http://www.jianshu.com/p/065d052bb66c</p> <p> </p>