下一节 : 安卓 - 源码分析 - LayoutInflater(二)
先看一下LayoutInflater的说明:
/**
* LayoutInflater用于解析并根据xml布局文件生成对应的View对象
* 该类不应该被直接调用,而是通过相关方法获取:
* Activity.getLayoutInflater / Context.getSystemService
* 通过该方式,才能获取正确绑定上下文的LayoutInflater对象。
*
* 如果你的View需要用额外的Factory去创建自定义的LayoutInflater,
* 可以cloneInContext复制一个LayoutInflater,之后,
* 通过setFactory方法将你的Factory添加到LayoutInflater中。
*
* 由于性能原因,View的填充过程重度依赖于xml文件的预处理,
* 所有目前LayoutInflater不支持在运行时直接解析未编译的xml。
*/
类说明为我们明确了3点:
不要直接创建该类,需要使用指定方法获取LayoutInflater对象。
可以通过Factory对LayoutInflater的填充过程进行自定义;
可以对LayoutInflater复制并重新设置Factory。
LayoutInflater只能解析编译过的xml布局文件。
其中,所有指定的方法,最终都是通过Context.getSystemService实现:
/*
*内部实现:
* getWindow().getLayoutInflater()
* getWindow()得到的是Activity的mWindow对象:
* mWindow = new PhoneWindow(this, window);
* 即调用了PhoneWindow.getLayoutInflater(),其实现:
* mLayoutInflater = LayoutInflater.from(context);
*/
Activity.getLayoutInflater();
/*
* 内部实现:
* LayoutInflater factory = LayoutInflater.from(context);
* return factory.inflate(resource, root);
*/
View.inflate(Context context, @LayoutRes int resource, ViewGroup root);
/*
* 内部实现:
* LayoutInflater LayoutInflater = (LayoutInflater) context
* .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
*/
LayoutInflater.from(context);
然后,我们再看一下内部的几个接口和对象:
private boolean mFactorySet; //是否调用过setFactory
private Factory mFactory; //继承Factory的对象
private Factory2 mFactory2; //继承Factory2的对象
private Factory2 mPrivateFactory; //由系统维护的对象
private Filter mFilter; //过滤器
/*
* 过滤器
* 如果要填充的View不被过滤器允许,将会抛出InflateException
*/
public interface Filter {
boolean onLoadClass(Class clazz);
}
/*
* Factory
* 3个参数分别为:
* name :需要填充的Tag名称,即View名称;
* context :View所在的上下文环境;
* attrs :View在xml定义中定义的属性,
* 当只处理个别View时,其他的View可以返回null。
*/
public interface Factory {
public View onCreateView(String name, Context context, AttributeSet attrs);
}
/*
* Factory2
* 重载onCreateView,增加了ViewParent参数。
*/
public interface Factory2 extends Factory {
public View onCreateView(View parent, String name,
Context context, AttributeSet attrs);
}
关于过滤器Filter:
通过setFilter方法,可以为LayoutInflater设置自定义的过滤器
/*
* 为LayoutInflater添加过滤器,当一个View不被过滤器允许时,
* 在填充过程的inflate方法里将会抛出InflateException,
* 新设置的filter将会覆盖之前所有设置的filter。
*/
public void setFilter(Filter filter) {
mFilter = filter;
if (filter != null)
mFilterMap = new HashMap<String, Boolean>();
}
注意新设置Filter的会覆盖旧的Filter,同时mFilterMap重新初始化。
mFilterMap的作用:记录Filter对不同View的过滤结果,当View进入过滤时,优先查找mFilterMap,避免Filter重复使用。
Filter的调用过程将会在后面的解析过程中分析。
关于Factory:
Factory可以让你对需要填充的View进行自定义。
Factory接口中,方法参数有name、context、attrs,Factory2则多了一个增加parent参数的重载。
应该关注的参数是:name和attrs:
name:
需要填充的View的类名,即对应xml中的Tag名。例如TextView。
你可以根据这个类名自定义View的创建,可以创建对应的View,也可以创建另外的View。
如:AppCompatActivity处理TextView,会返回AppCompatTextView而不是TextView。
attrs:
View属性,可以对这个属性进行修改,例如添加新属性,修改原有属性等。
通过修改attrs,可以轻松打造换肤功能。
使用setFactory方法,对LayoutInflater添加自定义Factory:
/*
* 可以为LayoutInflater添加一个实现Factory接口的类去改变View创建的过程,
* 这个Factory对象不能为null,且只能设置一次,设置后将不能进行变更,
* 在解析xml布局文件过程中,解析到View时,Factory会被调用,
* 如果Factory返回一个View,这个View则会被添加到控件层次中,
* 如果返回空,将会调用的默认的onCreateView方法创建View。
*/
public void setFactory(Factory factory) {
// 已设置与非空判定,判定不通过会抛异常
if (mFactorySet)
throw new IllegalStateException("A factory has already "
+ "been set on this LayoutInflater");
if (factory == null)
throw new NullPointerException("Given factory can not be null");
// mFactorySet默认为false,这里将mFactorySet设为true
// 再次调用setFactory将抛出上面的异常
mFactorySet = true;
// 如果原有的LayoutInflater带有mFactory
// 需要保留原有mFactory,下面再去介绍
if (mFactory == null)
mFactory = factory;
else
mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
}
也可以使用setFactory2,设置一个Factory2对象。
setFactory2和setFactory方法一样,但同时会将Factory2设置为mFactory和mFactory2:
public void setFactory2(Factory2 factory) {
...
if (mFactory == null) {
mFactory = mFactory2 = factory;
} else {
mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
}
}
上面涉及到了一个FactoryMerger类
private static class FactoryMerger implements Factory2 {
private final Factory mF1, mF2;
private final Factory2 mF12, mF22;
FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
mF1 = f1; mF2 = f2; mF12 = f12; mF22 = f22;
}
public View onCreateView(String name, Context context, AttributeSet attrs) {
View v = mF1.onCreateView(name, context, attrs);
if (v != null) return v;
return mF2.onCreateView(name, context, attrs);
}
public View onCreateView(View parent, String name,
Context context, AttributeSet attrs) {
View v = mF12 != null ?
mF12.onCreateView(parent, name, context, attrs) :
mF1.onCreateView(name, context, attrs);
if (v != null) return v;
return mF22 != null ?
mF22.onCreateView(parent, name, context, attrs) :
mF2.onCreateView(name, context, attrs);
}
}
这个类实现了Factory2接口,即是Factory的实现类,
它的作用,其实就是保留上一个Factory作为默认View创建方法。
例如:
现在有一个LayoutInflater:
LayoutInflater inflater;
inflater.setFactory((name, context, attrs) -> {
if(TextUtils.equals(name, TextView.class.getSimpleName()))
return new EditText(context,attrs);
return null;
})
这时候,想得到一个对EditView做处理,同时保留原来Factory行为的新LayoutInflater:
// 复制原有的LayoutInflater
LayoutInflater newInflater = inflater.cloneInContext(context);
newInflater.setFactory((name, context, attrs) -> {
if(TextUtils.equals(name, EditText.class.getSimpleName()))
return new TextView(context,attrs);
return null;
})
由于newInflater已经存在一个Factory,所以setFactory会为我们创建一个FactoryMerger对象:
public void setFactory(Factory factory) {
if (mFactory == null)
mFactory = factory;
else
mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
}
当填充EditText时,新的Factory会返回一个TextView对象;
而填充TextView时,则会返回null,这时会调用原有的Factory进行解析,即会返回一个EditText对象。
余下的分析放在
下一节 : 安卓 - 源码分析 - LayoutInflater(二)