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