一:LayoutInflaterFactory的用途:
自行創(chuàng)建自定義的View,而不是讓系統(tǒng)去創(chuàng)建脊岳,可以避免系統(tǒng)的反射過程逝段,提升性能;
在XML使用自定義View的時候垛玻,可以不聲明全限定名稱;
更換系統(tǒng)View為自己定義View奶躯,這正是Appcompat庫替換默認(rèn)的系統(tǒng)View的方式帚桩。
二:使用LayoutInflaterFactory的方式
(1)自定義LayoutInflaterFactory
通過繼承Support Library的LayoutInflaterFactory,在Activity里面設(shè)置自定義的LayoutInflaterFactory
@Override public void onCreate(Bundle savedInstanceState) {
LayoutInflaterCompat.setFactory(getLayoutInflater(), new MyLayoutInflaterFactory(this));
super.onCreate(savedInstanceState);
...
}
注意:在super.onCreate(savedInstanceState);
之前設(shè)置自定義LayoutInflaterFactory嘹黔;否則自定義的LayoutInflaterFactory不會生效账嚎。
LayoutInflater factories的最大限制是一個factory只能綁定一個LayoutInflater,因?yàn)閟upport library已經(jīng)綁定自己的factory儡蔓;設(shè)置自定義的LayoutInflaterFactory可能會帶來一些問題郭蕉,比如無法從XML文件中加載Fragment,無法加載v21包里面的屬性;
官方文檔:
如果使用自定義的Factory浙值,可以忽略調(diào)用 installViewFactory 方法恳不,然后直接調(diào)用 createView() 方法 返回兼容的View;
也就是說自定義LayoutInflaterFactory負(fù)責(zé)調(diào)用
AppCompatDelegate#createView(android.view.View, String, android.content.Context, android.util.AttributeSet
下面這個方法可以克服一個LayoutInflater只能設(shè)置一個LayoutInflaterFactory的缺陷;它會創(chuàng)建一個新的LayoutInflater實(shí)例开呐,然后綁定你可以給它綁定自定義的Factory烟勋;它是通過合并自定義factory和support library的 factory 來實(shí)現(xiàn)的;如果調(diào)用這個方法來創(chuàng)建LayoutInflater實(shí)例筐付,需要在Activity中重寫getLayoutInflater()
和getSystemService(String)
來返回自己的LayoutInflater
LayoutInflater#cloneInContext(Context)
(2)使用Activity作為LayoutInflaterFactory
所有的LayoutInflater都設(shè)置了一個默認(rèn)的LayoutInflaterFactory卵惦,Activity默認(rèn)實(shí)現(xiàn)了Factory和Factory2;
這樣在Activity中就允許 override下面的兩個方法來處理自定義view的加載瓦戚。
View onCreateView(View, String, Context, AttributeSet);
View onCreateView(String, Context, AttributeSet);
三:LayoutInflater Factory創(chuàng)建方法
(1)一個在XML文件中的移除自定義View全限定名稱的 Factory
移除自定義View全限定名稱的好處:(1)如果自定義View被重構(gòu)(refactor)了沮尿,不用在XML文件中修改肢扯;(2)增強(qiáng)可讀性
一個Factory的例子
public class MyLayoutInflaterFactory implements LayoutInflaterFactory {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
if (TextUtils.equals(name, "DebugDrawerLayout")) {
return new DebugDrawerLayout(context, attrs);
} else if (TextUtils.equals(name, "ScrimInsetsFrameLayout") {
return new ScrimInsetsFrameLayout(context, attrs);
}
// and so on...
}
}
上面代碼的問題: 如果按照上面的寫法可能要列出所有的自定義View,然后一個個判斷姿搜;
改進(jìn)為使用反射的方式創(chuàng)建;
public class MyLayoutInflaterFactory implements LayoutInflaterFactory {
private static final String CUSTOM_VIEWS_PACKAGE = "com.example.ui.customviews.";
private static final Class<?>[] constructorSignature = new Class[] {
Context.class, AttributeSet.class };
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
Constructor<? extends View> constructor = null;
//通過classLoader來加載類(全限定名稱)呀洲;
Class<? extends View> clazz = context.getClassLoader()
.loadClass(CUSTOM_VIEWS_PACKAGE + name).asSubclass(View.class);
//獲取構(gòu)造函數(shù)
constructor = clazz.getConstructor(constructorSignature);
constructor.setAccessible(true);
return constructor.newInstance(context, attrs);
}
}
上面代碼的缺陷是沒有反射cache;
四:如何在自定義LayoutInflaterFactory中保留support library的特性印衔;
public class CustomViewsLayoutInflaterFactory implements LayoutInflaterFactory {
private AppCompatDelegate appCompatDelegate;
public CustomViewsLayoutInflaterFactory(AppCompatDelegate appCompatDelegate) {
this.appCompatDelegate = appCompatDelegate;
}
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
View result = null;
// todo: your custom inflation code here!
if (result == null) {
// Get themed views from AppCompat
result = appCompatDelegate.createView(parent, name, context, attrs);
}
return result;
}
}