Layout inflation在Android环境中是一个专业术语,用于表示某个XML资源布局文件被解析并转化成为一个层级的View对象的过程。

它是Android SDK中的一个常见用法,但或许你会惊奇的发现,这里有一些LayoutInflater的错误使用方式,或许你的应用就是这些错误中的一个。如果你曾在Android应用程序中写过像下面代码使用LayoutInflater的方式:

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

请继续阅读,因为你正在犯错误,接下来我将为你解释这个是为什么。


了解LayoutInflater

首先让我们来看看LayoutInflater是如何工作的,inflate()方法有两个可用版本提供给标准的应用程序。

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

第一个参数指向要被图形化的布局资源文件。第二个参数是图形化资源要依附的根级视图层。第三个参数的出现,代表图形化后的视图对象是否要提供给根级视图。

最后的两个参数会让我产生一些迷惑。这两个参数的方法LayoutInflater将自动尝试将图形化后的视图对象提供给根试图。然而android框架在这个地方有一个检查,防止开发者使用null来替代根级视图而造成应用程序的崩溃。 

很多开发者使用向参数传递null值,这样做可以正确让图形化的视图不再依附顶级视图。在很多情况下甚至没有意识到 三个参数inflate()方法的存在。这种编码方式导致我们忽略了另一个根级视图的重要的功能...,但我们已经领先了一步。

来自Android Framework的例子

让我们来查看一下在Android框架内是如何希望你作为一个开发者去交互图形化视图部分的情况

适配器是最常用的LayoutInflater方式,自定义ListView控件适配器时要覆盖getView()方法,它有如下的方法特征。

getView(int position, View convertView, ViewGroup parent)

Fragment当创建可见视图onCreateView()经常使用图形化 注意它的方法特征。

onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)

你是否注意到无论何时android框架要图形化一个布局它都会传递给你父ViewGroup对象它将最终被附属上吗?注意在大多数情况下(包括上面的两个例子),随后LayoutInflater 如果被允许自动尝试图形化后的视图依附到根视图,它将抛出一个异常

所以你会猜为什么提供给我们ViewGroup,如果我们不使用它呢?原来在图形化过程中父级视图是非常重要的部分,因为它需要按顺序评估XML文件根级元素中LayoutParams的声明。不传任何东西就是告诉Android框架抱歉,我不知道这个视图将要依附给那个父视图。

问题是android:layout_xxx这些属性总被考虑到父视图的上下文中,结果却不知道父视图是谁,你声明在XML文件的根节点中所有的LayoutParams将被忽略,接下来您会问:为什么Android框架忽略我自定义的部分?我最好查找一下原因并提交一个bug

没有父视图的LayoutParams,最终ViewGroup完成图形化布局会为你产生一套默认设置。如果你幸运(在很多情况)这个默认参数是和你曾经在XML表标注的界面是相同的。

应用实例:

所以,你声称从来没有在你的应用程序中见到过这种情况?看看下面我们要去为ListView图形化行的简单布局。

R.layout.item_row

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

我们要将ListView的行高设置成为一个固定高度,在这种情况下现在item的高为当前主体的高...似乎是合理的。

然而,当我们图形化这个布局的时候是错误的方法。

public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        convertView = inflate(R.layout.item_row, null);
    }
 
    return convertView;
}

最终的结果看起来像这样:


发生了什么呢?恒定的高度我们设置了吗?这最终通常是对你所有的子视图设置固定高度,切换到根元素高度或者成为wrap_content,进而没有真正理解为什么被破坏了。(你或许会在这个过程中诅咒谷歌)

如果我们使用相同的布局替换图形化

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布局,但设置它作为Dialog视图。

这里的问题是AlertDialog.Builder支持一个自定义视图,但不能提供一个setView()实现这带一个布局资源;所以你必须手动图形化XML。然后,因为结果将进入对话框,这里不需要暴露根视图(事实上,它也不存在),我们没有访问最终的父布局,所以我们不能使用它图形化。事实证明,这是不相干,因为AlertDialog将抹去LayoutParams很多布局并替换成match_parent.


所以下次你的手指将忍不住的向inflate()方法中填入null,你需要停止并且问问自己我真的不知道这个view将结束吗?

至少,你应当想到两个参数版本的inflate()是快捷忽略true作为第三个参数的方式。你不应该向参数传入null是快捷忽略false的方式。


译文链接:https://possiblemobile.com/2013/05/layout-inflation-as-intended/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值