关于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>