一:LayoutInflaterFactory的用途:
自行创建自定义的View,而不是让系统去创建,可以避免系统的反射过程,提升性能;
在XML使用自定义View的时候,可以不声明全限定名称;
更换系统View为自己定义View,这正是Appcompat库替换默认的系统View的方式。
二:使用LayoutInflaterFactory的方式
(1)自定义LayoutInflaterFactory
通过继承Support Library的LayoutInflaterFactory,在Activity里面设置自定义的LayoutInflaterFactory
@Override public void onCreate(Bundle savedInstanceState) {
LayoutInflaterCompat.setFactory(getLayoutInflater(), new MyLayoutInflaterFactory(this));
super.onCreate(savedInstanceState);
...
}
注意:在super.onCreate(savedInstanceState);
之前设置自定义LayoutInflaterFactory;否则自定义的LayoutInflaterFactory不会生效。
LayoutInflater factories的最大限制是一个factory只能绑定一个LayoutInflater,因为support library已经绑定自己的factory;设置自定义的LayoutInflaterFactory可能会带来一些问题,比如无法从XML文件中加载Fragment,无法加载v21包里面的属性;
官方文档:
如果使用自定义的Factory,可以忽略调用 installViewFactory 方法,然后直接调用 createView() 方法 返回兼容的View;
也就是说自定义LayoutInflaterFactory负责调用
AppCompatDelegate#createView(android.view.View, String, android.content.Context, android.util.AttributeSet
下面这个方法可以克服一个LayoutInflater只能设置一个LayoutInflaterFactory的缺陷;它会创建一个新的LayoutInflater实例,然后绑定你可以给它绑定自定义的Factory;它是通过合并自定义factory和support library的 factory 来实现的;如果调用这个方法来创建LayoutInflater实例,需要在Activity中重写getLayoutInflater()
和getSystemService(String)
来返回自己的LayoutInflater
LayoutInflater#cloneInContext(Context)
(2)使用Activity作为LayoutInflaterFactory
所有的LayoutInflater都设置了一个默认的LayoutInflaterFactory,Activity默认实现了Factory和Factory2;
这样在Activity中就允许 override下面的两个方法来处理自定义view的加载。
View onCreateView(View, String, Context, AttributeSet);
View onCreateView(String, Context, AttributeSet);
三:LayoutInflater Factory创建方法
(1)一个在XML文件中的移除自定义View全限定名称的 Factory
移除自定义View全限定名称的好处:(1)如果自定义View被重构(refactor)了,不用在XML文件中修改;(2)增强可读性
一个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,然后一个个判断;
改进为使用反射的方式创建;
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);
//获取构造函数
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;
}
}