前言
前几天流行界面一行代码变灰白,这其中大多用到了一个技术原理就是viewtree生成过程的拦截技术。
在Android中LayoutInflater是一个服务,它辅助我们将xml中定义的布局生成view tree,我们通常使用以下两种方式来获取LayoutInflater:LayoutInflater.from(context)或者context.getSystemService(LAYOUT_INFLATER_SERVICE)
通过自定义LayoutInflater可以在生成view的过程进行拦截,例如AppCompatActivity使用自己的对view创建过程进行拦截,实现使用Material组件替换原有的组件的目的。
1. 继承OnCreateView方法
LayoutInflater中使用了责任链的设计模式,创建过程会先委托给Activity或者Fragment中的OnCreateView方法,如果只是简单的拦截,只需要重写Activity或者Fragment的OnCreateView方法即可.
2. 暴力反射
LayoutInflater允许我们试着Factory和Factory2,但是只允许设置一次。如果再次设置则会抛出Caused by: java.lang.IllegalStateException: A factory has already been set on this LayoutInflater的异常。然而如果你用的是AppcompatActivity,这一次机会已经被使用了,所以只能暴力反射:
public static void setFactory2(
@NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) {
final LayoutInflater.Factory existingFactory = inflater.getFactory();
try {
Field field = LayoutInflater.class.getDeclaredField("mFactorySet");
field.setAccessible(true);
field.setBoolean(inflater, false);
inflater.setFactory2(factory);
} catch (Exception e) {
e.printStackTrace();
}
}
3. cloneInContext
官方也考虑了这种需求,允许clone一个LayoutInflater,这样可以允许自己设置一个Factory2,内部的Factory2将被merge。
LayoutInflater inflater = LayoutInflater.from(this);
LayoutInflater layoutInflater = inflater.cloneInContext(this);
layoutInflater.setFactory2(new LayoutInflater.Factory2() {
...
});
中
4. theme指定
如果使用AppcompatActivity那一套东西,它默认使用的是AppCompatViewInflater子类这个工具类来创建view,并且允许我们通过设置theme指定自定义的AppCompatViewInflater子类。例如com.google.android.material:material包中使用以下方式指定自己的AppCompatViewInflater子类:
<item name="viewInflaterClass">com.google.android.material.theme.MaterialComponentsViewInflater</item>