从inflate方法开始,搞懂LayoutInflater的inflate过程(上)
本来是完整一份的,结果一页写不下,分页,感觉知乎显示效果不行,来,点我博客查看
本文主要介绍:LayoutInflater
本文适合对象:打算搞懂LayoutInflater的inflate的开发者
本文字数:约2.5万,阅读时间:约2H
问题
先从表象入手
在Android开发过程中,很多地方都不可避免的使用到inflate方法,如在使用
RecycleView
的时候构建
ViewHolder
对象,给
Fragment
进行
CreateView
我们通常是
inflater.inflate(R.layout.xxx, container, false)
或者
LayoutInflater.from(parent.context).inflate(R.layout.xxx, parent, false)
来调用inflate方法的,不难发现,inflate方法的作用是将一个
xml布局文件变成一个
view对象。然而仅仅是根据模板,按照固定的"规律"去修改某些参数来实现目标,只能叫做是「使用」而已
那么,我们就来将它「分解」成明确的「问题」,来具体的「学习」吧
-
、LayoutInflater
这些语句的「头部」是什么?怎么来的?inflater
-
方法的「参数」是什么意思,有什么用?inflate
- 这些语句是怎么实现转换 xml 为 view 的?
- 我除了常见的用法还能怎么用它
Question One:「头部」
官方文档
思考的First Step,问其所来
官方文档对
LayoutInflater
的说明如下:
简单的翻译过来就是:
- 这玩意是用来将 xml 转换为 view 的
- 这玩意不能直接new初始化,通过
和Activity
获取,你也可以自定义他的工厂方法SystemService
- 因为性能问题,他只能把写在layout里被预处理过的 xml 转换为 view ,不能随便找个xml文件就让他转换
回归表象
那好了,第一个问题解决了,
LayoutInflater
是一个不能直接new的类,他来管
xml转换为
view,我们在
adapter
里通过
LayoutInflater.from(context)
获取实例,
fragment
则是直接使用了
FragmentManager
调用
Fragment.onCreateView
的时候传过来的
inflater
对象
Question Two:「方法」
inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
方法有三个参数,第一个参数很好理解,就是前文所说的,
xml转换为
view中
layout xml对应的资源ID。第二第三个参数又什么意思呢?我转换成View为什么需要它呢?
官方文档
简单翻译:
- root是要转换的 xml 将要存在的外部
ViewGroup
- xml 转换成 view 后要不要
到addView
(root
)ViewGroup
这是个啥意思呢,看了之后似懂非懂,我还是一脸懵逼。
测试
纸上学来终觉浅,只是看文档还是不行,不如自己上手试试,把自己脑子里的可能性都弄出来试试看效果,跑出来啥样就是啥样了。
按排列组合来说,我们一共有四种(如果你想到更多可能性,不妨自己写出来跑跑看)
- root = null, attachToRoot = false
- root = null, attachToRoot = true
- root = viewgroup, attachToRoot = false
- root = viewgroup, attachToRoot = true
接下来我们一个个实验,实验的过程为,通过
activity
的
getLayoutInflater()
方法获取
inflater
对象,调用其
inflate
方法,传递不同的参数,将得到的
view
添加到
activity
布局的
viewgroup
中,查看结果。
首先是布局展示,
activity
的布局只有一个蓝底的ViewGroup,而要加载的
view
也只是一个黄色的View
注意我给蓝底加了一句
android:paddingTop="32dp"
,黄底加了一句
android:layout_margin="4dp"
测试①
我们看到黄色的
view
几乎填满了整个
activity
,
view
的
width
,
height
和
margin
都无效,但是
viewgroup
的
padding
是有效的。
但是我们还不能确定是
root = null
、
attachToRoot = false
中哪个的原因,我们继续测试
测试②
我们可以看到黄色的
view
里面设置的
width
height
margin
还是无效的,但是
viewgroup
的
padding
是有效的。
通过这两个测试,我猜测
root
的效果是控制
xml里关于
layoutparam
的设置是否有效,但是不是这样还要看接下来的测试。而
viewgroup
的padding参数是不受影响的,这个也好理解,因为是ViewGroup的属性,在onDraw方法里处理的。
测试③
我们可以看到黄色的
view
里面设置的
width
height
margin
也都有效了
也就是说,root的猜测基本是坐实了,接下来就剩
attachToRoot
还是一头雾水了
测试④
Crash!出问题了,看看报错信息:
The specified child already has a parent. You must call removeView() on the child's parent first.
这娃儿已经有个爹了,你要当他爹得先让他现在的爹 removeView()
啥意思,已经有个爹了?这爹是谁,他转换的过程也就接触到一个viewgroup啊,难道说
attachToRoot = true
的话就直接
addView()
了?试试看
测试⑤
果然和我们想的一样……那么,可以总结一下了
总结
-
参数将决定root
的view
,如果为null,那layoutparam
里定义的最外层xml
的view
将全部无效layoutparam
-
表示是否需要一键attachToRoot
,如果addView()
为null,那这个参数将被自动忽略root
下篇