Android吃透inflate方法(一)
从inflate方法开始,搞懂LayoutInflater的inflate过程在Android开发过程中,很多地方都不可避免的使用到inflate方法,如在使用RecycleView的时候构建ViewHolder对象,给Fragment进行CreateView我们通常是inflater.inflate(R.layout.xxx, container, false)或者LayoutInflater.
从inflate方法开始,搞懂LayoutInflater的inflate过程
在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 的?
- 我除了常见的用法还能怎么用它
问题一:
思考的First Step,问其所来
- 这玩意是用来将 xml 转换为 view 的
- 这玩意不能直接new初始化,通过
Activity
和SystemService
获取,你也可以自定义他的工厂方法- 因为性能问题,他只能把写在layout里被预处理过的 xml 转换为 view ,不能随便找个xml文件就让他转换
总结:
那好了,第一个问题解决了,LayoutInflater
是一个不能直接new的类,他来管 xml 转换为 view ,我们在adapter
里通过LayoutInflater.from(context)
获取实例,fragment
则是直接使用了FragmentManager
调用Fragment.onCreateView
的时候传过来的inflater
对象
问题二:
inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
方法有三个参数,第一个参数很好理解,就是前文所说的, xml 转换为 view 中 layout xml 对应的资源ID。第二第三个参数又什么意思呢?我转换成View为什么需要它呢?
- root是要转换的 xml 将要存在的外部
ViewGroup
- xml 转换成 view 后要不要
addView
到root
(ViewGroup
)是不是看不懂,我们接着看!!!
这里提一下:View和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
的layoutparam
,如果为null,那xml
里定义的最外层view
的layoutparam
将全部无效attachToRoot
表示是否需要一键addView()
,如果root
为null,那这个参数将被自动忽略(表示是否将第一个参数所指定的布局添加到第二个参数的View中。)
第一点失效的详解:
我们在开发的过程中给控件所指定的layout_width和layout_height到底是什么意思?该属性的表示一个控件在容器中的大小,就是说这个控件必须在容器中,这个属性才有意义,否则无意义。
这就意味着如果我直接将linearlayout加载进来而不给它指定一个父布局,则inflate布局的根节点的layout_width和layout_height属性将会失效(因为这个时候linearlayout将不处于任何容器中,那么它的根节点的宽高自然会失效)
如果我想让linearlayout的根节点有效,又不想让其处于某一个容器中,那我就可以设置root不为null,而attachToRoot为false。这样,指定root的目的也就很明确了,即root会协助linearlayout的根节点生成布局参数,只有这一个作用。
原因:
那为什么Activity布局的根节点的宽高属性会生效?其实原因很简单,大部分情况下我们一个Activity页面由两部分组成(Android的版本号和应用主题会影响到Activity页面组成,这里以常见页面为例),我们的页面中有一个顶级View叫做DecorView,DecorView中包含一个竖直方向的LinearLayout,LinearLayout由两部分组成,第一部分是标题栏,第二部分是内容栏,内容栏是一个FrameLayout,我们在Activity中调用setContentView就是将View添加到这个FrameLayout中,所以给大家一种错觉仿佛Activity的根布局很特殊,其实不然。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)