转自:http://www.doubleencore.com/2013/05/layout-inflation-as-intended/

Layout inflation is the term used within the context of Android to indicate when an XML layout resource is parsed and converted into a hierarchy of View objects.

It’s common practice in the Android SDK, but you may be surprised to find that there is a wrong way to use LayoutInflater, and your application might be one of the offenders. If you’ve ever written something like the following code using LayoutInflater in your Android application:

inflater.inflate(R.layout.my_layout, null);

PLEASE read on, because you’re doing it wrong and I want to explain to you why.

Get to Know LayoutInflater

Let’s first take a look at how LayoutInflater works. There are two usable versions of the inflate() method for a standard application:

inflate(int resource, ViewGroup root)
inflate(int resource, ViewGroup root, boolean attachToRoot)

The first parameter points to the layout resource you want to inflate. The second parameter is the root view of the hierarchy you are inflating the resource to attach to. When the third parameter is present, it governs whether or not the inflated view is attached to the supplied root after inflation.

It is these last two parameters that can cause a bit of confusion. With the two parameter version of this method, LayoutInflater will automatically attempt to attach the inflated view to the supplied root. However, the framework has a check in place that if you pass null for the root it bypasses this attempt to avoid an application crash.

Many developers take this behavior to mean that the proper way to disable attachment on inflation is by passing null as root; in many cases not even realizing that the three parameter version of inflate() exists. By doing things this way, we also disable another very important function the root view has…but I’m getting ahead of myself.

Examples from the Framework

Let’s examine some situations in Android where the framework expects you as a developer to interactively inflate portions of the view.

Adapters are the most common case for using LayoutInflater is custom ListView adapters overriding getView(), which has the following method signature:

getView(int position, View convertView, ViewGroup parent)

Fragments also use inflation often when creating views via onCreateView(); notice its method signature:

onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)

Have you noticed that every time the framework wants you to inflate a layout, they also pass you the parent ViewGroup it will eventually be attached to? Notice also that in most cases (including the above two examples), it will throw an Exception later on if LayoutInflater is allowed to automatically attach the inflated view to the root.

So why do you suppose we are given this ViewGroup if we are not supposed to attach to it? It turns out the parent view is a very important part of the inflation process because it is necessary in order to evaluate the LayoutParams declared in the root element of the XML being inflated. Passing nothing here is akin to telling the framework “I don’t know what parent this view will be attached to, sorry.”

The problem with this is android:layout_xxx attributes are always be evaluated in the context of the parent view. As a result, without any known parent, all LayoutParams you declared on the root element of your XML tree will just get thrown away, and then you’ll be left asking “why is the framework ignoring the layout customizations I defined? I’d better check SO and then file a bug.”

Without LayoutParams, the ViewGroup that eventually hosts the inflated layout is left to generate a default set for you. If you are lucky (and in many cases you are) these default parameters are the same as what you had in XML…masking the fact that something is amiss.

Application Example

So you claim you’ve never seen this happen in an application? Take the following simple layout that we want to inflate for a ListView row:

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>

We want to set the height of our row to be a fixed height, in this case the preferred item height for the current theme…seems reasonable.

However, when we inflate this layout the wrong way

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;
}

we end up with a result that looks like this

What happened to the fixed height we set?? This is usually where you end up setting the fixed height on all your child views, switching the root elements height to wrap_content, and move on without really knowing why it broke (you may have even cursed at Google in the process).

If we instead inflate the same layout this way

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;
}

we end up with what we expected in the first place.

Hooray!

Every Rule Has An Exception

There are of course instances where you can truly justify a null parent during inflation, but they are few. One such instance occurs when you are inflating a custom layout to be attached to an AlertDialog. Consider the following example where we want to use our same XML layout but set it as the dialog view:

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();

The issue here is that AlertDialog.Builder supports a custom view, but does not provide an implementation of setView() that takes a layout resource; so you must inflate the XML manually. However, because the result will go into the dialog, which does not expose its root view (in fact, it doesn’t exist yet), we do not have access to the eventual parent of the layout, so we cannot use it for inflation. It turns out, this is irrelevant, because AlertDialog will erase any LayoutParams on the layout anyway and replace them with match_parent.

So the next time your fingers are tempted to just type null into inflate(), you should stop and ask yourself “do I really not know where this view will end up?”

Bottom line, you should think of the two parameter version of inflate() as a convenience shortcut to omit true as the third paramter. You should not think of passing null as a convenience shortcut to omit false.

For future insights, please subscribe to our email list at the bottom of the page (below the comments)!

关于LayoutInflater的错误用法的更多相关文章

  1. 关于LayoutInflater的错误用法(警告提示:Avoid passing null as the view root)

    项目中用LayoutInflater加载xml布局一直飘黄警告,上网搜了搜发现没有解释明白的,到是找到了一篇外国的博文,但是竟然是英文...幸好以前上学时候的英语不是很差加上谷歌的辅助,简单翻一下!  ...

  2. 避免常见的6种HTML5错误用法

    一.不要使用section作为div的替代品 人们在标签使用中最常见到的错误之一就是随意将HTML5的<section>等价于<div>——具体地说,就是直接用作替代品(用于样 ...

  3. 一直被用错的6种SQL 错误用法

    一直被用错的6种SQL 错误用法 1.LIMIT 语句 2.隐式转换 3.关联更新.删除 4.EXISTS语句 5.条件下推 6.提前缩小范围 sql语句的执行顺序: FROM ON JOIN WHE ...

  4. C++成员函数指针错误用法警示(成员函数指针与高性能的C++委托,三篇),附好多评论

    今天做一个成绩管理系统的并发引擎,用Qt做的,仿照QtConcurrent搞了个模板基类.这里为了隐藏细节,隔离变化,把并发的东西全部包含在模板基类中.子类只需注册需要并发执行的入口函数即可在单独线程 ...

  5. 8种常见的SQL错误用法

    常见SQL错误用法 1. LIMIT 语句 分页查询是最常用的场景之一,但也通常也是最容易出问题的地方.比如对于下面简单的语句,一般DBA想到的办法是在type, name, create_time字 ...

  6. MySQL常见的8种SQL错误用法

    MySQL常见的8种SQL错误用法 前言 MySQL在2016年仍然保持强劲的数据库流行度增长趋势.越来越多的客户将自己的应用建立在MySQL数据库之上,甚至是从Oracle迁移到MySQL上来.但也 ...

  7. spring @Transactional的自调用失效问题与事务的典型错误用法剖析

    @Transactional的自调用失效问题 有时候配置了注解@Transactional,但是它会失效,这里要注意一些细节问题,以避免落入陷阱. 注解@Transaction的底层实现是Spring ...

  8. MySQL · 性能优化 · MySQL常见SQL错误用法(转自-阿里云云栖社区)

    作者:阿里云云栖社区链接:https://zhuanlan.zhihu.com/p/26043916来源:知乎著作权归作者所有,转载请联系作者获得授权. 前言 MySQL在2016年仍然保持强劲的数据 ...

  9. if else的错误用法!

    在学习C语言的时候,我们会了解和认识if  else 的用法, 并在程序里面使用它来作为判断条件! if(表达式)       //先定义一个条件,如果成立,则循环if里面的语句. { 语句 } el ...

随机推荐

  1. hdu 4547 LCA **

    题意:在Windows下我们可以通过cmd运行DOS的部分功能,其中CD是一条很有意思的命令,通过CD操作,我们可以改变当前目录. 这里我们简化一下问题,假设只有一个根目录,CD操作也只有两种方式: ...

  2. BZOJ 1007: [HNOI2008]水平可见直线 栈/计算几何

    1007: [HNOI2008]水平可见直线 Time Limit: 1 Sec  Memory Limit: 162 MB 题目连接 http://www.lydsy.com/JudgeOnline ...

  3. wikioi 1048 石子归并

    dp[i][j]=min(dp[i][j],dp[i][k],dp[k+1][j]+sum[i][j]); 表示i-j的最小合并代价. #include <iostream> #inclu ...

  4. .net orm类库 kiss.data 简单文档记录

    kiss.data的简单记录 == [github地址](https://github.com/sdming/Kiss.Data) kiss.data是golang的数据库访问类库[kdb](http ...

  5. [Javascript]XMLHttpRequest对象实现下载进度条

    摘要 可以通过设置一个XMLHttpRequest对象的responseType属性来改变一个从服务器上返回的响应的数据类型.可用的属性值为空字符串 (默认), "arraybuffer&q ...

  6. 苹果Mac OS X系统十三年视觉变化发展史

    1Mac OS 9 一个普通的桌面操作系统 经过多个测试版本后,苹果终于正式公布OS X 10.10 Yosemite操作系统.苹果称这个第11版的OS X系统是自从2001年问世以来在视觉效果上变化 ...

  7. Microsoft Composition (MEF 2)

    This packages provides a version of the Managed Extensibility Framework (MEF) that is lightweight an ...

  8. jQuery碎语(2) 事件

    4.事件 ● 通过方法名给元素绑定事件: $('li').click(function(event){}) ● 通过bind方法给元素绑定事件: $('li') .bind('click',funct ...

  9. MVC文件上传02-使用HttpPostedFileBase上传多个文件

    本篇体验上传多个文件.兄弟篇为: MVC文件上传01-使用jquery异步上传并客户端验证类型和大小           MVC最基本上传文件方式中的一个遗漏点 □ 前台视图部分 1: <% u ...

  10. zookeeper 伪分布式安装

    1 下载zookeeper安装包 下载地址 http://apache.fayea.com/zookeeper/ 我下载的是zookeeper-3.4.6.tar.gz 2 解压缩 将zookeepe ...