简介
public abstract class LayoutInflater
LayoutInflater,布局填充器,从名字就可以看出它用于加载布局。
我们常用的方式大概如下:
1 | // 方法定义:inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) |
这样我们就可以把一个 XML 文件实例化成一个 View 来供我们使用。
PS:LayoutInflater的获取方式不止这一种,实际最终调用的都是 Context.getSystemService 方法,最终拿到的是PhoneLayoutInflater,有兴趣的同学可以去看看我的Context.getSystemService分析。
上述代码我写过无数遍,但是心中一直有很多疑问:
- 上述方法中的
root、attachToRoot究竟有什么作用? - 它究竟是在哪里实例化View又是如何去实例化 View 的?
- 为什么系统的View我们在Xml里不需要写全路径,而自定义View却需要?
- 它又是如何处理
fragment以及各种标签如include、merge的? View的onFinishInflate是否跟它有关呢?
这一切都藏在源码里,所以深入源码一点点了解吧!
inflate深入解析
上面的例子中可以看到,我们调用的是inflate(in resource, in root, in attachToRoot)方法,所以首先分析一下该方法。
1 | public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) { |
上面代码可以看到该方法主要是获取一个XmlResourceParser对象parser(这里不关心它是如何来的)。
不过需要提一下的这里解析XML采用的是 Pull方法(不知道的自行Google)。
然后调用了另外一个inflate方法,所以我们还需要继续跟踪inflate(parser, root, attachToRoot)才能进一步理解。
上代码!
1 | public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { |
可以看到该方法是重点了。
这里出现了merge的踪迹,可以看到遇到merge标签,当root为null或者attachToRoot为false的时候,直接抛了异常!
也可以看到如果是merge标签,走的是rInflate方法(不过这里暂时不分析rInflate方法)。
关键的是,我看到inflate方法处理了root与attachToRoot参数。
围绕root是否为null有两种处理分支:
第一种 当root不为null的时候:
- 调用
root.generateLayoutParams方法来生成LayoutParamas并赋值给paramas - 然后如果
attachToRoot为false,则把paramas赋值给createViewFromTag解析出来的temp(XML里的根布局) - 而如果
attachToRoot为true的话,则会 调用root.addView(temp, params);直接把temp给加到root里去。如果我们自己再调用addView则会报错!
这里再提一下root 对topView的LayoutParamas的影响:
需要先提一下 LayoutParamas 一般有3种来源:
- 用户完全自定义 自己
new出来 - ViewGroup.generateLayoutParams 方法生成 上面已经提到
- ViewGroup.generateDefaultLayoutParams 方法生成,在
addView的时候 如果childView没有的话 LayoutParams 属性的话,会由这个方法生成。
来看一下 addView 里对 paramas 的操作就明白了:
1 | public void addView(View child) { |
所以当 root 不为 null 的时候,topview 的 paramas 是通过generateLayoutParams生成的。
需要注意的是:generateLayoutParams与generateDefaultLayoutParams生成的 paramas 是不同的,会无视我们在 xml 里配置的属性,所以它会影响到布局效果。
第二种 当root为null的时候:
是null的时候会返回temp (XML里的根布局)
1 | // null 或是 false 那么result=temp |
也可以看到,root==null如果成立,那么attachToRoot也就没有用了。
所以attachToRoot只有在root不为 null 的时候才有效。
大致总结成流程图如下所示:

搞清楚root以及attachToRoot参数的影响之后,来看View究竟是如何被创建的。
进入createViewFromTag方法。
createViewFromTag解析
上面提到的createViewFromTag方法如下:
1 | /** |
可以看到,它又调用了另外一个重载函数,并从注释中我们可以看到了include的信息。
该方法把ignoreThemeAttr属性赋值为了false,继续跟下去。
1 | /** |
该createViewFromTag方法 先处理了主题属性,再走入创建View的流程。
这里还涉及到了几个Factory,这其实是系统留给我们的Hook入口,我们可以人为的干涉系统创建View,可以添加更多功能,比如夜间模式。
Factory相关的知识后续再讲。
另外,我们可以看到该方法依然没有涉及到创建View的具体实现,而是又会去调用onCreateView以及createView方法,这俩方法总应该是View创建的具体地方了吧?!!
onCreateView 与 createView
初步来看onCreateView方法负责创建自定义View,而createView方法负责创建系统自带的View。
但是感觉比较奇怪,因为不管是什么View,创建的套路应该是一样才对啊~
感觉有诈!
1 | protected View onCreateView(String name, AttributeSet attrs) |
咦!~ onCreateView调用了createView,到最后,其实都是调用createView方法啦!
另外还传入了android.view.的一个参数,咦?这不是系统自带的View的包路径吗?
继续深入createView。
1 | public final View createView(String name, String prefix, AttributeSet attrs) |
调来调去,终于到真正实例化View的地方了。
看到这方法的 clazz = mContext.getClassLoader().loadClass(prefix != null ? (prefix + name) : name).asSubclass(View.class) 步骤会把系统自带的View的路径拼起来,把类加载进来;
然后clazz.getConstructor(mConstructorSignature);获取View的构造方法,最终通过反射constructor.newInstance(args);实例化View。
如果你足够机智,你会发现这里出来一个问题,WebView 怎么办?
它的路径可是android.webkit啊~
其实这里涉及到 LayoutInflater 的一个子类com.android.internal.policy.PhoneLayoutInflater,它处理了android.widget.、android.webkit.、android.app.这些路径。
事实上,我们最开始使用LayoutInflater.from(cxt)获取的就是PhoneLayoutInflater的实例。
另外这里又涉及到一个Hook入口,即Filter,但是我不知道它的使用场景。
createView方法里解答了我 View是哪里实例化的以及XML中系统View为什么不需要写全路径 这两个疑问。
小结
这一篇中分析了如下方法(省去了参数):
inflate:LayoutInflater对外开放的入口,这里分析了 root与attachToRoot 参数的作用。createViewFromTag:处理主题属性与Factory的HookonCreateView: 处理系统自带View的路径,android.view.,实际调用的还是createView方法createView: 真正实例化View的地方,通过View的路径去加载类并获取构造方法,通过反射获取View的实例。
本篇解决了一些疑问:
- 上述方法中的
root、attachToRoot究竟有什么作用?- 影响了
merge标签, - View是否直接被 add 到 root
- View 的 LayoutParams 从何而来
- inflate 方法的返回值
- 影响了
- 为什么系统的View我们在Xml里不需要写全路径,而自定义View却需要?
- 针对系统 View,会帮忙拼全路径,所以不需要写全
- 它究竟是在哪里实例化View又是如何实例化 View 的?
- 在 createView 方法中,默认利用反射实例化 View
- 也可通过 Factory hook 的方式实例化
但是还有好多疑问没有解决,也还有部分重要的方法没有解析,所以需要继续探索。
篇幅太长了,所以先小结一下,换一篇继续。
下一篇着重分析merge、include等标签是如何处理的。
已经写好啦:LayoutInflater源码分析(二)之include以及merge标签的处理
推荐阅读
一步一步深入理解CoordinatorLayout
ViewStub是如何实现懒加载的
Space源码分析
LayoutInflater源码分析(二)之include以及merge标签的处理