Layout Inflation :Unconditional layout, inflation from view adapter
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
。
Layout Inflation :Unconditional layout, inflation from view adapter的更多相关文章
- iOS UIKit:Auto Layout
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css); @import url(/ ...
- LayoutInflater作用是将layout的xml布局文件实例化为View类对象。
获取LayoutInflater的方法有如下三种: LayoutInflater inflater=(LayoutInflater)context.getSystemService(Context.L ...
- 自己定义View Layout过程 - 最易懂的自己定义View原理系列(3)
前言 自己定义View是Android开发人员必须了解的基础 网上有大量关于自己定义View原理的文章.但存在一些问题:内容不全.思路不清晰.无源代码分析.简单问题复杂化等等 今天,我将全面总结自己定 ...
- Android 性能优化(4)Optimizing Layout Hierarchies:用Hierarchy Viewer和Layoutopt优化布局
Optimizing Layout Hierarchies This lesson teaches you to Inspect Your Layout Revise Your Layout Use ...
- 自定义控件(视图)2期笔记05:自定义控件之继承自View(滑动开关)
1. 开关按钮点击效果,如下: 2. 继承已有View实现自定义View 3. 下面通过一个案例实现滑动开关的案例: (1)新建一个新的Android工程,命名为" 开关按钮", ...
- RecyclerView Bug:IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter的解决方案(转)
转自:RecyclerView Bug:IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter的解 ...
- Auto Layout Guide----(一)-----Understanding Auto Layout
Understanding Auto Layout 理解自动布局 Auto Layout dynamically calculates the size and position of all the ...
- myeclipse报错:Could not create the view: An unexpected exception was thrown.
打开server窗口,发现显示:Could not create the view: An unexpected exception was thrown. 此处解决方法: 关闭myeclipse 删 ...
- MyEclipse无法创建servers视图:Could not create the view: An unexpected exception was thrown
今天上班刚打开MyEclipse,就发现servers视图无法打开了,显示:Could not create the view: An unexpected exception was thrown. ...
随机推荐
- VM 安装 linux Enterprise_R5_U4_Server_I386_DVD教程图解
ocp 学习笔记 20161126--------linux 笔记整理 一:安装linux系统环境: 1:linux 系统安装包下载路径:链接:链接: https://pan.baidu.com/s/ ...
- linux 后台运行nohup & ctrl+z
使用Linux时,经常希望有些命令结果不在前台显示,如sh脚本,耗时的命令等.一般情况下,使用 & 将命令结果后台运行,如sh test.sh,脚本后台执行. 有时候命令已经在前台执行了,需要 ...
- jvm字节占用空间分析
一个对象实例占用了多少字节,消耗了多少内存?这样的问题在c或c++里使用sizeof()方法就可以得到明确答案,在java里好像没有这样的方法(java一样可以实现),不过通过jmap工具倒是可以查看 ...
- Java+Jmeter接口测试
一.创建工程.引包 1.创建JAVA工程 2.引入Jmeter中lib\ext基础包:ApacheJMeter_java.jar.ApacheJMeter_core.jar 3.引入Jmeter日志包 ...
- 并发之线程封闭与ThreadLocal解析
并发之线程封闭与ThreadLocal解析 什么是线程封闭 实现一个好的并发并非易事,最好的并发代码就是尽量避免并发.而避免并发的最好办法就是线程封闭,那什么是线程封闭呢? 线程封闭(thread c ...
- python接口自动化测试十五:解决密码动态,无法登录情况
解决问题:每次密码都是变化的,无法通过账号密码登录 (总不能每次去fiddler复制吧????) 解决思路: 1.先用selenium调用浏览器(不会selenium的自己想办法了), 2.登录后从浏 ...
- 详解kubeadm生成的证书(转)
https://docs.lvrui.io/2018/09/28/%E8%AF%A6%E8%A7%A3kubeadm%E7%94%9F%E6%88%90%E7%9A%84%E8%AF%81%E4%B9 ...
- 和组合数有关的dp
1. UVaLive 7143 Room Assignment 用dp[i][r]表示,前i个盒子已经放完了,手上还拿着r对同色球. 状态转移方程为:dp[i+1][r-a-b] = dp[i][r] ...
- 061 hive中的三种join与数据倾斜
一:hive中的三种join 1.map join 应用场景:小表join大表 一:设置mapjoin的方式: )如果有一张表是小表,小表将自动执行map join. 默认是true. <pro ...
- laravel5 项目上线后务必将开发环境更改为生产环境
如果以开发环境上线,出错信息将全通过json暴露出来了,屏蔽方式如下: .env 文件设置如下APP_ENV=productionAPP_DEBUG=false 改完设置后把缓存清理一遍 如果更改后清 ...