廢話不多說,直接上代碼:
常用的:
View inflate = View.inflate(context, resource, null);
是不是經(jīng)常用這種方式來讀取xml,生成view.
如果你點(diǎn)開源碼去看就會知道,調(diào)用的其實(shí)是LayoutInflater
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
LayoutInflater factory = LayoutInflater.from(context); //這里需要一個(gè)上下文
return factory.inflate(resource, root);//調(diào)用的是LayoutInflater的inflate方法.
}
為什么要傳入一個(gè)上下文呢?
public static layoufrom(Context context) {
//context.getSystemService,這不是activity里獲取系統(tǒng)服務(wù)類的方法么.
//看看介紹LAYOUT_INFLATER_SERVICE LayoutInflater 取得xml里定義的view
LayoutInflater LayoutInflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) { //如果拿不到這個(gè)服務(wù)類就會拋出異常.
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
可以看出,LayoutInflater,其實(shí)是通過context 的getSystemService獲取到的系統(tǒng)服務(wù)對象.
接著往下看,查源碼,一路找到contextImpl類,這是抽象類context的實(shí)現(xiàn)類
系統(tǒng)關(guān)鍵代碼:
//這里可以理解為一種單例模式,通過hashmap來保存多個(gè)單例,并通過key,來獲取相應(yīng)的單例
//但是這里并不是直接保存單例,而保存了生成單例的對應(yīng)工廠.
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)) {
//將初始化時(shí)的順序賦值給ServiceFetcher中的mContextCacheIndex
fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
}
//將生成的ServiceFetcher工廠保存起來.
SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
}
//通過靜態(tài)代碼塊,創(chuàng)建覆蓋了createService()方法的ServiceFetcher工廠來綁定對應(yīng)的系統(tǒng)服務(wù).
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) {
//通過名字拿到對應(yīng)的工廠,來獲取對應(yīng)的服務(wù)對象
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. */
//上面注釋的大概意思是,如果不需要緩存,就復(fù)寫這個(gè)方法.
public Object getService(ContextImpl ctx) {
//ContextImpl中的service緩存集合
ArrayList<Object> cache = ctx.mServiceCache;
Object service;
synchronized (cache) {
if (cache.size() == 0) { //如果沒有緩存,則添加sNextPerContextServiceCacheIndex數(shù)量的長度.
for (int i = 0; i < sNextPerContextServiceCacheIndex; i++) {
cache.add(null);
} else {
//如果有緩存,則通過該工廠保存的mContextCacheIndex角標(biāo)從contextImpl的cache中拿到service
service = cache.get(mContextCacheIndex);
if (service != null) { //不為null就直接返回
return service;
}
}
//如果為null,則調(diào)用這個(gè)工廠的生成方法,來生成對應(yīng)的系統(tǒng)服務(wù)
//就通過靜態(tài)代碼塊里面,重寫了createService方法的匿名類來獲取
service = createService(ctx);
//這里通過代碼塊里面的生成順序,來緩存service
cache.set(mContextCacheIndex, service);
//返回我們需要的service
return service;
}
}
}
扯遠(yuǎn)了..
通過這塊內(nèi)容
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
}});
可以看到是通過PolicyManager來獲取LayoutInflater實(shí)例的
接著看下去
private static final IPolicy sPolicy;
..........
public static LayoutInflater makeNewLayoutInflater(Context context) {
return sPolicy.makeNewLayoutInflater(context);
}
這里IPolicy 是個(gè)接口,而sPolicy是通過反射生成的
PolicyManager主要用于創(chuàng)建Window類普舆、LayoutInflater類和WindowManagerPolicy類,它扮演著簡單工廠模式中的工廠類角色涝缝,而抽象產(chǎn)品角色由IPolicy接口實(shí)現(xiàn)腔剂,具體產(chǎn)品角色由Policy類實(shí)現(xiàn)谢鹊。
源碼就寫這些...
------------------------
看看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.發(fā)現(xiàn)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的根節(jié)點(diǎn)為merge,則root不能為null,否則拋出異常
//2.如果根節(jié)點(diǎn)為blink,則生成BlinkLayout作為根布局
//3.如果1,2不成立,則根據(jù)根節(jié)點(diǎn)名稱,生成對應(yīng)的根布局
if (TAG_1995.equals(name)) { //TAG_1995 ="blink";
temp = new BlinkLayout(mContext, attrs);
} else {
temp = createViewFromTag(root, name, attrs);
}
//4.如果root不為null,則拿到root的layoutparams,來設(shè)置根布局的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; //返回解析出來的
}}
總結(jié):
舉例:
//例1.生成view,但不指定父容器,如果父容器為null,那么設(shè)置ture還是false結(jié)果都一樣
LayoutInflater.from(context).inflate(id,null);
//例2.結(jié)果和1一樣,
LayoutInflater.from(context).inflate(id,null,false);
//例3.結(jié)果和1一樣
LayoutInflater.from(context).inflate(id,null,true);
//例4.生成view,指定父容器,并添加到其中,獲取parent的Layoutparmas設(shè)置view的Layoutparmas.
LayoutInflater.from(context).inflate(id,parent,true);
//例5.生成view,指定父容器,不添加到其中,獲取parent的Layoutparmas設(shè)置view的Layoutparmas.
LayoutInflater.from(context).inflate(id,parent,false);
用例1,2,3生成的view是沒有Layoutparmas的.所以必須手動設(shè)置,不然xml里面設(shè)置的屬性會失效
用例4,5設(shè)生成的view,不管設(shè)置的true還是false,生成的view都會從parent中得到Layoutparmas
,區(qū)別在于,是否添加到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().