真的知道LayoutInflater的正确用法么?

Posted 從前以後

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了真的知道LayoutInflater的正确用法么?相关的知识,希望对你有一定的参考价值。

LayoutInflater

LayoutInflater中文译为布局膨胀,是android中使用context来调用的一个术语,用来指导一个XML布局资源被正确解析,并且转换成一个视图对象的层次结构。

当我们有一个XML布局文件需要解析成View的时候,LayoutInflater就会被用到,但是你有可能会惊讶的发现也或者不会发现,有一种错误的LayoutInflater使用方式经常出现在代码中。如果在你的应用程序中写过类似下面的LayoutInflater用法,那么很遗憾,这种写法是错误的。

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

为什么?

首先来看一下LayoutInflater是如何工作的。在Android SDK中提供了2个应用版本

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

其中第一个参数指向要Inflater的布局资源,第二个参数是Inflater的资源关联到那个具有层次结构的根视图上,当第三个参数存在的时候,它决定是否将Inflater的视图关联到提供的根视图上。两个参数的方法LayoutInflater会自动把Inflater的视图提供给根视图,但是该框架有一个检查,如果根视图参数传入的是null,那么它将绕过来避免应用程序崩溃。许多开发者采用这种方式,以为根视图参数传入null,就是避免Inflater加载的View关联到根视图的正确做法,在许多情况下,甚至没有意识到有三个参数的inflate方法存在。如果运用两个参数的方法并且传入null,在避免inflate出来的View关联到根视图的时候我们同样也失去了根视图中的一些重要的功能。也有可能我们回来一句”I don’t care!”。

下面列举一些在平常开发中用到的情况:

LayoutInflater最常见的运用就是在适配器(Adapter)中,特别是需要适配ListView或者GridView并且需要覆盖其getview()方法时。

getView(int position, View convertView, ViewGroup parent)

fragment的onCreateView方法中也经常用到LayoutInflater,注意onCreateView方法的参数

onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)

通过观察参数,你有没有意识到,每当Android的框架需要你去Inflater一个XML布局,框架也会传递来一个ViewGroup,这个ViewGroup就是Inflater出来的View是不是需要关联的根视图。

也许我们在进行编码的时候,从来就没有重视它,也不管这个参数有什么用。但其实这个根视图是Inflater中非常重要的一部分,它决定了Inflater出来的View在根视图中的LayoutParams。如果传入null,那么就是告诉框架我不知道这个Inflater出来的View需要关联的根视图是什么。

因为Android的layout_xxx属性总是在父视图的影响下进行评估,如果不知道任何父视图的结果就是所有你在XML中定义的LayoutParams元素属性都会没用,然后或许你会问一句“为毛我在XML里面定义的属性都被忽略了,妹的这时哪里的bug?”

没有LayoutParams,最终viewgroup会给你生成一个默认的设置,如果你足够幸运,这些默认的参数和你在XML中定义的一样,掩盖了有些对象不对劲的事实。

例如下面的例子:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:background="#ffffff"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="text1" />

</LinearLayout>

我们要设置我们的行高是一个固定的高度100dp,这种情况下看起来一切都很正常。但是如果我们用下面错误的方式inflate。

public View getView(int position, View convertView, ViewGroup parent) 

    if (convertView == null) 
        convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item, null);

    

    return convertView;

最终结果为:

观看结果发现高度并不是我们设定的100dp,
而当我们用:

public View getView(int position, View convertView, ViewGroup parent) 

    if (convertView == null) 
        convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item,parent, false);

    

    return convertView;

最后的结果为:

这时候高度结果正是我们设置的100dp。

上面的结果可以看出,当我们使用两个参数的方法,并传入null的时候。我们设置的LayoutParamters都是无用的,系统会自动给一个默认值。而选用三个参数的时候,则显示正常

当然,在Android开发中,也有一些inflate传入null的情况,不过这种情况真的很少,一个例子就是,inflate出一个View并把它关联到AlertDialog中。例如下面的例子:

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()来解析布局资源,所以需要我们手动inflate。我们无法访问到他的父布局,其实它也不存在父布局。但是这都无关紧要,因为AlertDialog会擦除一切的LayoutParams,并且用match_parent来替换它。

结语

说了这么多,所以下次在用LayoutInflater的时候,应该考虑用两个参数的方法来代替三个参数方法中,第三个参数为true的情况(也就是关联到父视图)。不需要关联的时候,则不应该为了方便,而使用两个参数并且第二个参数传入null方法,这么做则会丢失掉我们Inflater出视图的LayoutParamters。

以上是关于真的知道LayoutInflater的正确用法么?的主要内容,如果未能解决你的问题,请参考以下文章

「RecyclerView中的位置」你真的会正确获取Item的位置么?

使用layoutinflater的正确姿势

使用layoutinflater的正确姿势

使用layoutinflater的正确姿势

NSTimer 的正确用法你真的知道吗?

NSTimer 的正确用法你真的知道吗?