【安卓开发】Layout Inflation不能这么用
2015-03-23 14:13
357 查看
Layout inflation在Android上下文环境下转换XML文件成View结构对象的时候需要用到。
LayoutInflater这个对象在Android的SDK中很常见,但是你绝对没想到竟然能够找到一个使用误区。说不定你的App里就是这么用的!如果你在写APP的时候像如下代码一样使用LayoutInflater的话:
请你继续读完这篇文章,稍后我会解释为什么这样做不对。
第一个参数指出要载入的布局文件资源,第二个参数指出视图结构中载入的布局将要放入的根视图。如果有第三个参数,那么它用来决定是否把载入后的视图绑定到给出的根视图中。
最后两个参数可能会导致一些问题。如果使用两个参数的版本,Layoutinflater会自动尝试把载入的视图绑定到给定的根视图对象中。但是,如果你传递
很多开发者会这样做,认为传递
Adapter是最常用的场景,我们经常需要使用
Fragment也会用到inflation操作,通过
不知你有没有注意到这一点,每次Framework需要你去载入一个布局文件时,都会传入一个ViewGroup参数(最后需要绑定到的根视图),如果Layoutinflater设为自动绑定到根视图的话,会抛出一个异常。
所以你想想看,如果我做绑定操作的话,为什么要给你一个ViewGroup参数呢?事实证明父视图在这个inflation操作过程中是很重要的,它会计算被载入的XML在根元素中的LayoutParams,如果传入
问题在于,android:layout_xxx属性会在父视图对象中被重新计算,结果就是所有你定义的LayoutParams都会被忽略掉(因为没有已知的父视图对象)。然后你就纳闷“为什么框架会忽略掉我自己定义的布局属性呢?还是去StackOverFlow上看看,提一个bug吧”。
如果没有设置LayoutParams,那么最终ViewGroup也会给你生成一个默认的属性,幸运的话(很多时候),这些默认的设置正好和你在XML文件中定义的一样……所以你就察觉不到其实已经出现问题了。
R.layout.item_row
这里我们想把高度设置为固定高度,上面把它设为当前主题下的推荐高度……看似很合理。
但是,当我们这样载入布局文件的时候,就不对了:
然后结果就变成这样了:
为什么设定的固定高度不起作用?这是因为你没有把所有子View的高都设为固定高度,只需要把根视图的高设置成wrap_content就可以了。不需要知道为什么会这样(你可以吐槽一下Google为什么这么处理!)。
而如果这样载入布局的话就没有问题:
这样我们就得到了想要的结果:
这里的问题就是,
所以,下次使用
最后,你应该想想两个参数的
LayoutInflater这个对象在Android的SDK中很常见,但是你绝对没想到竟然能够找到一个使用误区。说不定你的App里就是这么用的!如果你在写APP的时候像如下代码一样使用LayoutInflater的话:
认识LayoutInflater
首先看一下LayoutInflater的工作原理,有两个重载的版本可以使用:inflate(int resource, ViewGroup root)和
inflate(int resource, ViewGroup root, boolean attachToRoot)
第一个参数指出要载入的布局文件资源,第二个参数指出视图结构中载入的布局将要放入的根视图。如果有第三个参数,那么它用来决定是否把载入后的视图绑定到给出的根视图中。
最后两个参数可能会导致一些问题。如果使用两个参数的版本,Layoutinflater会自动尝试把载入的视图绑定到给定的根视图对象中。但是,如果你传递
null,系统就不会尝试绑定操作了,否则应用程序就崩溃了。
很多开发者会这样做,认为传递
null作为根视图就可以禁用绑定操作了。很多时候很多开发者甚至不知道还有三个参数的Layoutinflater版本的存在,如果这么做的话,也会同时禁用了根视图的一个很重要的函数……但是之前我没有研究过。
框架中的示例
现在我们来仔细看看Android框架关于动态载入布局的场景。Adapter是最常用的场景,我们经常需要使用
LayoutInflater来自定义
ListView(通过重写
getView()方法),具体的方法签名是这样的:
onCreateView()方法创建view的时候会用到。这个方法的签名是这样的:
所以你想想看,如果我做绑定操作的话,为什么要给你一个ViewGroup参数呢?事实证明父视图在这个inflation操作过程中是很重要的,它会计算被载入的XML在根元素中的LayoutParams,如果传入
null话,就等于是告诉框架“我不知道载入的View要放到哪个父视图中”。
问题在于,android:layout_xxx属性会在父视图对象中被重新计算,结果就是所有你定义的LayoutParams都会被忽略掉(因为没有已知的父视图对象)。然后你就纳闷“为什么框架会忽略掉我自己定义的布局属性呢?还是去StackOverFlow上看看,提一个bug吧”。
如果没有设置LayoutParams,那么最终ViewGroup也会给你生成一个默认的属性,幸运的话(很多时候),这些默认的设置正好和你在XML文件中定义的一样……所以你就察觉不到其实已经出现问题了。
应用案例
你敢说你没有在应用中碰到过这样的场景吗?看看下面的代码,为Listview简单地载入一个布局文件:
R.layout.item_row
但是,当我们这样载入布局文件的时候,就不对了:
为什么设定的固定高度不起作用?这是因为你没有把所有子View的高都设为固定高度,只需要把根视图的高设置成wrap_content就可以了。不需要知道为什么会这样(你可以吐槽一下Google为什么这么处理!)。
而如果这样载入布局的话就没有问题:
任何规则都有例外
当然,也有需要在载入布局的时候指定null作为父布局对象,但这种情况非常少。一个典型的例子就是为AlertDialog中载入一个自定义布局。看看下面的例子,使用和上面一样的XML布局文件来作为对话框的布局:
AlertDialog.Builder支持自定义布局,但是
setView()方法不提供带有布局文件作为参数的版本,所以只能先手动载入XML布局文件。由于最终会进入到对话框里面,不会接触到根布局(事实上这时候还没有根布局),所以我们也操作不了布局文件的最终父视图对象,当然也就不能用于载入使用了。事实证明,这些都是无关紧要的,因为
AlertDialog会擦除布局上的所有
Layoutparams然后替换为
match_parent。
所以,下次使用
inflate()函数时,如果还想输入
null应该停下来想一想“我真的不知道它该放到哪里吗?”
最后,你应该想想两个参数的
inflate()版本作为一个便捷的使用方式,可以忽略第三个参数(默认为
true),但是不要想着为了方便而传递一个
null却忽略了第三个参数会默认是
false。
关于作者: chris
相关文章推荐
- 【安卓开发】Layout Inflation不能这么用
- 安卓google地图开发,只显示灰色方格,不能正常显示地图?求解。网上说key不对,我换了N多个key都不行。
- 安卓开发, 遇到WebView不能加载静态网页, WebView显示 "net::ERR_PROXY_CONNECTON_FAILED"
- 安卓开发中eclipse不能识别设备原因及解决办法
- [Phonegap+Sencha Touch] 移动开发39 某些安卓手机的webview使用location.href="tel:123456"不能调到打电话的界面
- 安卓开发, 遇到WebView不能加载静态网页, WebView显示 "net::ERR_PROXY_CONNECTON_FAILED"
- 关于在安卓开发中百度地图功能不能实现定位。
- 安卓开发问题一:在虚拟机上运行APP不能运行,即闪退现象
- 安卓开发的实用技巧 -----关于android:layout_gravity 不能完全居右
- 请问这段安卓开发的代码不能通过编译的原因是什么
- 安卓开发环境的搭建和解决在Eclipse新建安卓5.1工程不能自动生成R文件的问题
- 安卓开发app之 不能跳过的初始强制界面
- 安卓开发:荣耀,红米,不能真机调试的问题
- 安卓开发调用第三方应用(如WPS)打开文档(比如Word),如何设置文件只读,不能被修改?
- 安卓开发——Android反编译就是这么简单 过程详解
- 【安卓开发】为什么不能往Android的Application对象里存储数据
- 安卓开发中ScrollView不能用RelativeLayout的解决方案
- 【安卓开发】为什么不能往Android的Application对象里存储数据
- [安卓开发] 完美解决Dialog不能全屏