關于LayoutInflater的用法

廢話不多說,直接上代碼:

常用的:

View inflate = View.inflate(context, resource, null);

是不是經常用這種方式來讀取xml,生成view.

如果你點開源碼去看就會知道,調用的其實是LayoutInflater

public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) { 
   LayoutInflater factory = LayoutInflater.from(context); //這里需要一個上下文
   return factory.inflate(resource, root);//調用的是LayoutInflater的inflate方法.
}

為什么要傳入一個上下文呢?

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

可以看出,LayoutInflater,其實是通過context 的getSystemService獲取到的系統服務對象.

接著往下看,查源碼,一路找到contextImpl類,這是抽象類context的實現類

系統關鍵代碼:

//這里可以理解為一種單例模式,通過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;   
     }
  }
}

通過這塊內容

registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {  
      public Object createService(ContextImpl ctx) { 
           return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());  
      }});

可以看到是通過PolicyManager來獲取LayoutInflater實例的

接著看下去

private static final IPolicy sPolicy;
..........
public static LayoutInflater makeNewLayoutInflater(Context context) { 
   return sPolicy.makeNewLayoutInflater(context);
}

這里IPolicy 是個接口,而sPolicy是通過反射生成的

PolicyManager主要用于創建Window類、LayoutInflater類和WindowManagerPolicy類,它扮演著簡單工廠模式中的工廠類角色,而抽象產品角色由IPolicy接口實現,具體產品角色由Policy類實現。

看看LayoutInflater用法:

//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(); 
  }}
//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;  //返回解析出來的
  }}

總結:

舉例:

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

用例1,2,3生成的view是沒有Layoutparmas的.所以必須手動設置,不然xml里面設置的屬性會失效

用例4,5設生成的view,不管設置的true還是false,生成的view都會從parent中得到Layoutparmas

,區別在于,是否添加到parent中

如果使用例4,則不要再去parent.addview(),否則會拋出異常

if (child.getParent() != null) {  
        throw new IllegalStateException(
    "The specified child already has a parent." 
    "You must call removeView() on the child's parent first.");
}

使用例5,則可以使用parent.addview().

 

來自:http://www.jianshu.com/p/065d052bb66c

 

 本文由用戶 TamMuhammad 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!