【安卓开发】Layout Inflation不能这么用
Layout inflation在Android上下文环境下转换XML文件成View结构对象的时候需要用到。
LayoutInflater这个对象在Android的SDK中很常见,但是你绝对没想到竟然能够找到一个使用误区。说不定你的App里就是这么用的!如果你在写APP的时候像如下代码一样使用LayoutInflater的话:
1
|
inflater.inflate(R.layout.my_layout, null ); |
请你继续读完这篇文章,稍后我会解释为什么这样做不对。
认识LayoutInflater
首先看一下LayoutInflater的工作原理,有两个重载的版本可以使用:
inflate(int resource, ViewGroup root)
和 inflate(int resource, ViewGroup root, boolean attachToRoot)
第一个参数指出要载入的布局文件资源,第二个参数指出视图结构中载入的布局将要放入的根视图。如果有第三个参数,那么它用来决定是否把载入后的视图绑定到给出的根视图中。
最后两个参数可能会导致一些问题。如果使用两个参数的版本,Layoutinflater会自动尝试把载入的视图绑定到给定的根视图对象中。但是,如果你传递null
,系统就不会尝试绑定操作了,否则应用程序就崩溃了。
很多开发者会这样做,认为传递null
作为根视图就可以禁用绑定操作了。很多时候很多开发者甚至不知道还有三个参数的Layoutinflater版本的存在,如果这么做的话,也会同时禁用了根视图的一个很重要的函数……但是之前我没有研究过。
框架中的示例
现在我们来仔细看看Android框架关于动态载入布局的场景。
Adapter是最常用的场景,我们经常需要使用LayoutInflater
来自定义ListView
(通过重写getView()
方法),具体的方法签名是这样的:
1
|
getView( int position, View convertView, ViewGroup parent) |
Fragment也会用到inflation操作,通过onCreateView()
方法创建view的时候会用到。这个方法的签名是这样的:
1
|
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) |
不知你有没有注意到这一点,每次Framework需要你去载入一个布局文件时,都会传入一个ViewGroup参数(最后需要绑定到的根视图),如果Layoutinflater设为自动绑定到根视图的话,会抛出一个异常。
所以你想想看,如果我做绑定操作的话,为什么要给你一个ViewGroup参数呢?事实证明父视图在这个inflation操作过程中是很重要的,它会计算被载入的XML在根元素中的LayoutParams,如果传入null
话,就等于是告诉框架“我不知道载入的View要放到哪个父视图中”。
问题在于,android:layout_xxx属性会在父视图对象中被重新计算,结果就是所有你定义的LayoutParams都会被忽略掉(因为没有已知的父视图对象)。然后你就纳闷“为什么框架会忽略掉我自己定义的布局属性呢?还是去StackOverFlow上看看,提一个bug吧”。
如果没有设置LayoutParams,那么最终ViewGroup也会给你生成一个默认的属性,幸运的话(很多时候),这些默认的设置正好和你在XML文件中定义的一样……所以你就察觉不到其实已经出现问题了。
应用案例
你敢说你没有在应用中碰到过这样的场景吗?看看下面的代码,为Listview
简单地载入一个布局文件:
R.layout.item_row
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" android:layout_width = "match_parent" android:layout_height = "?android:attr/listPreferredItemHeight" android:gravity = "center_vertical" android:orientation = "horizontal" > < TextView android:id = "@+id/text1" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:paddingRight = "15dp" android:text = "Text1" /> < TextView android:id = "@+id/text2" android:layout_width = "0dp" android:layout_height = "wrap_content" android:layout_weight = "1" android:text = "Text2" /> </ LinearLayout > |
这里我们想把高度设置为固定高度,上面把它设为当前主题下的推荐高度……看似很合理。
但是,当我们这样载入布局文件的时候,就不对了:
1
2
3
4
5
6
7
|
public View getView( int position, View convertView, ViewGroup parent) { if (convertView == null ) { convertView = inflate(R.layout.item_row, null ); } return convertView; } |
然后结果就变成这样了:
为什么设定的固定高度不起作用?这是因为你没有把所有子View的高都设为固定高度,只需要把根视图的高设置成wrap_content就可以了。不需要知道为什么会这样(你可以吐槽一下Google为什么这么处理!)。
而如果这样载入布局的话就没有问题:
1
2
3
4
5
6
7
|
public View getView( int position, View convertView, ViewGroup parent) { if (convertView == null ) { convertView = inflate(R.layout.item_row, parent, false ); } return convertView; } |
这样我们就得到了想要的结果:
任何规则都有例外
当然,也有需要在载入布局的时候指定null
作为父布局对象,但这种情况非常少。一个典型的例子就是为AlertDialog中载入一个自定义布局。看看下面的例子,使用和上面一样的XML布局文件来作为对话框的布局:
1
2
3
4
5
6
7
|
AlertDialog.Builder builder = new AlertDialog.Builder(context); View content = LayoutInflater.from(context).inflate(R.layout.item_row, null ); builder.setTitle( "My Dialog" ); builder.setView(content); builder.setPositiveButton( "OK" , null ); builder.show(); |
这里的问题就是,AlertDialog.Builder
支持自定义布局,但是setView()
方法不提供带有布局文件作为参数的版本,所以只能先手动载入XML布局文件。由于最终会进入到对话框里面,不会接触到根布局(事实上这时候还没有根布局),所以我们也操作不了布局文件的最终父视图对象,当然也就不能用于载入使用了。事实证明,这些都是无关紧要的,因为AlertDialog
会擦除布局上的所有Layoutparams
然后替换为match_parent
。
所以,下次使用inflate()
函数时,如果还想输入null
应该停下来想一想“我真的不知道它该放到哪里吗?”
最后,你应该想想两个参数的inflate()
版本作为一个便捷的使用方式,可以忽略第三个参数(默认为true
),但是不要想着为了方便而传递一个null
却忽略了第三个参数会默认是false
。
关于作者: chris
【安卓开发】Layout Inflation不能这么用的更多相关文章
- 安卓开发:初步了解布局文件layout
了解完项目的目录结构,主要文件的作用之后. 了解完各常量文件的定义和使用之后,接下来的重头戏肯定是布局文件layout. 果然,网上关于“安卓布局文件layout”的各种介绍.解析.深入分析,等等资料 ...
- 安卓开发常见Bug-setContentView(R.layout.....)报错
这是安卓开发的常见错误,当你在引用或者复制别人的Layout xml文件时需要在AndroidManifest.xml中添加东西 需要将图中的activity android:name添加进去,否则是 ...
- 安卓开发30:AsyncTask的用法
http://blog.csdn.net/initphp/article/details/10392093 安卓开发笔记系列(43) 在开发Android应用时必须遵守单线程模型的原则: Andro ...
- 安卓开发笔记——关于照片墙的实现(完美缓存策略LruCache+DiskLruCache)
这几天一直研究在安卓开发中图片应该如何处理,在网上翻了好多资料,这里做点小总结,如果朋友们有更好的解决方案,可以留言一起交流下. 内存缓存技术 在我们开发程序中要在界面上加载一张图片是件非常容易的事情 ...
- 安卓开发笔记——Fragment+ViewPager组件(高仿微信界面)
什么是ViewPager? 关于ViewPager的介绍和使用,在之前我写过一篇相关的文章<安卓开发复习笔记——ViewPager组件(仿微信引导界面)>,不清楚的朋友可以看看,这里就不再 ...
- 安卓开发笔记——深入Activity
在上一篇文章<安卓开发笔记——重识Activity >中,我们了解了Activity生命周期的执行顺序和一些基本的数据保存操作,但如果只知道这些是对于我们的开发需求来说是远远不够的,今天我 ...
- 安卓开发笔记——自定义广告轮播Banner(实现无限循环)
关于广告轮播,大家肯定不会陌生,它在现手机市场各大APP出现的频率极高,它的优点在于"不占屏",可以仅用小小的固定空位来展示几个甚至几十个广告条,而且动态效果很好,具有很好的用户& ...
- 安卓开发_深入学习ViewPager控件
一.概述 ViewPager是android扩展包v4包(android.support.v4.view.ViewPager)中的类,这个类可以让用户左右切换当前的view. ViewPager特点: ...
- 安卓开发_浅谈ListView(SimpleAdapter数组适配器)
安卓开发_浅谈ListView(ArrayAdapter数组适配器) 学习使用ListView组件和SimapleAdapter适配器实现一个带图标的ListView列表 总共3部分 一.MainAc ...
随机推荐
- Ubuntu系统安装Pyenv
安装Pyenv curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | ...
- [HNOI 2001]矩阵乘积
Description Input Output Sample Input 1 2 3 4 2 3 1 1 3 1 4 5 2 2 1 3 1 2 1 2 2 2 1 1 3 1 2 3 2 4 1 ...
- [HNOI2009]有趣的数列
题目描述 我们称一个长度为2n的数列是有趣的,当且仅当该数列满足以下三个条件: (1)它是从1到2n共2n个整数的一个排列{ai}: (2)所有的奇数项满足a1<a3<...<a2n ...
- [UOJ]#33. 【UR #2】树上GCD
题目大意:给定一棵有根树,边长均为1,对于每一个i,求树上有多少个点对,他们到lca距离的gcd是i.(n<=200,000) 做法:先容斥,求出gcd是i的倍数的点对,考虑长链剖分后从小到大合 ...
- bzoj 5297: [Cqoi2018]社交网络
Description 当今社会,在社交网络上看朋友的消息已经成为许多人生活的一部分.通常,一个用户在社交网络上发布一条消息 (例如微博.状态.Tweet等)后,他的好友们也可以看见这条消息,并可能转 ...
- 计蒜客NOIP2017提高组模拟赛(五)day2-成绩统计
传送门 用hash,因为map的复杂度可能在这题中因为多一个log卡掉,但是hash不会 可能因为这个生成的随机数有循环的情况,不是完全均匀的 而且这题hash表的长度也可以开的很大 #include ...
- POJ1509 Glass Beads
Glass Beads Time Limit: 3000MS Memory Limit: 10000K Total Submissions: 4314 Accepted: 2448 Descr ...
- poj 3904(莫比乌斯反演)
POJ 3904 题意: 从n个数中选择4个数使他们的GCD = 1,求总共有多少种方法 Sample Input 4 2 3 4 5 4 2 4 6 8 7 2 3 4 5 7 6 8 Sample ...
- hdu 3939(勾股+容斥)
题意: 给定一个整数L(L<=1e12),计算(x,y,z)组的个数.其中x<y<z,x^2+y^2=z^2,gcd(x,y)==1,gcd(x,z)==1,gcd(y,z)==1. ...
- [Codeforces]605E Intergalaxy Trips
小C比较棘手的概率期望题,感觉以后这样的题还会贴几道出来. Description 给定一个n*n的邻接矩阵,邻接矩阵中元素pi,j表示的是从 i 到 j 这条单向道路在这一秒出现的概率百分比,走一条 ...