
Android LayoutInflater 源码解析
正常情况下这个Factory是空的,那什么时候不为空,以及 LayoutInflater Factory 的具体用法,我们今天就带着这两个问题来详细学习下。
备注:本文基于 Android 8.1.0。
1、LayoutInflater.Factory 简介
LayoutInflater.Factory 中没有说明,那我们来看下它唯一方法的说明:
Hook you can supply that is called when inflating from a LayoutInflater. You can use this to customize the tag names available in your XML layout files.
翻译过来就是:通过 LayoutInflater 创建View时候的一个回调,可以通过LayoutInflater.Factory来改造 XML 中存在的 tag。
我们来看下这个唯一的方法:
public abstract View onCreateView (String name, Context context, AttributeSet attrs)
那么我们就明白了,如果我们设置了LayoutInflater Factory ,在LayoutInflater 的 createViewFromTag 方法中就会通过这个 Factory 的 onCreateView 方法来创建 View。
2、LayoutInflater.Factory 作用
那怎么理解上述引用的这个改造呢?举个简单的例子:比如你在 XML中 写了一个 TextView标签,然后在 onCreateView 这个回调里 判断如果 name 是 TextView 的话可以变成一个Button,这样的功能可以实现例如批量更换某一个控件等的用途。例子如下:
布局文件
接下来我们在 Java 代码中做修改:
可以看到,本来在布局文件中需要展示的是一个 TextView,但是现在却被改造成了一个 Button。
成功改造为Button
备注:其实还有一个关系密切的类 LayoutInflater.Factory2 ,与 LayoutInflater.Factory 的区别是:
LayoutInflater.Factory2 是API 11 被加进来的;
LayoutInflater.Factory2 继承自 LayoutInflater.Factory;
可以对创建 View 的 Parent 进行控制;
3、LayoutInflaterCompat
刚刚我们说到,LayoutInflater.Factory2 是API 11 被加进来的,那么 LayoutInflaterCompat 就是拿来做兼容的类。我们来看下它最重要的两个方法:
可以看到 setFactory 已经被标记为过时,更建议使用 setFactory2 方法。
这里调用 setFactory 实际上还是调用的 setFactory2 方法,同时将 LayoutInflaterFactory 包裹为 Factory2Wrapper。
4、LayoutInflater.setFactory 使用注意
如果我们将LayoutInflater.setFactory 挪到 super.onCreate 的后面可以吗? 程序竟然报错了,我们看下Log:
说明是 LayoutInflater 已经被设置了一个 Factory,而我们再设置的时候就会报错。我们跟踪下 LayoutInflater.from(this).setFactory2 方法:
可以通过这个 mFactorySet 变量看出 setFactory2 方法只能被调用一次,重复设置则会抛出异常。那Factory2是被谁设置了呢?
我们来看下 AppCompatActivity 的 onCreate 方法,
其中会调用 delegate.installViewFactory(); 最终会调用到 AppCompatDelegateImplV9 的 installViewFactory方法;
可以看到:
如果 layoutInflater.getFactory() 为空,则 AppCompatActivity 会自动设置一个 Factory2,难怪我们在 super.onCreate 之后调用会报错;
细心的小伙伴肯定也明白了,为什么我们在 super.onCreate 之前设置 Factory之后,系统再次设置 Factory 的时候不会抛出异常;
备注:聪明的小伙伴肯定能想到使用反射来改变修改 LayoutInflater 中的 mFactorySet 为false就可以在 super.onCreate 之后再次设置 Factory了。
5、AppCompatActivity 为什么 setFactory
那么为什么 AppCompatActivity 会自动设置一个 Factory呢?顺着 AppCompatDelegateImplV9 的 installViewFactory方法继续跟踪,走到了 onCreateView 方法,它最终会调用到 AppCompatViewInflater 的 createView 方法。
原来 AppCompatActivity 设置 Factory 是为了将一些 widget 自动变成 兼容widget (例如将 TextView 变成 AppCompatTextView)以便于向下兼容新版本中的效果,在高版本中的一些 widget 新特性就是这样在老版本中也能展示的。
那如果我们设置了自己的 Factory 岂不是就避开了系统的兼容?其实系统的兼容我们仍然可以保存下来,因为系统是通过 AppCompatDelegate.onCreateView 方法来实现 widget 兼容的,那我们就可以在设置 Factory 的时候先调用 AppCompatDelegate.onCreateView 方法,再来做我们的处理。
6、总结
LayoutInflater.Factory的意义:通过 LayoutInflater 创建 View 时候的一个回调,可以通过 LayoutInflater.Factory 来改造或定制创建 View 的过程。
LayoutInflater.setFactory 使用注意:不能在 super.onCreate 之后设置。
AppCompatActivity 为什么 setFactory ?向下兼容新版本中的效果。
