深入理解LayoutInflater.inflate()

由于我们很容易习惯公式化的预置代码,有时我们会忽略很优雅的细节。LayoutInflater以及它在Fragment的onCreateView()中填充View的方式带给我的就是这样的感受。这个类用于将XML文件转换成相对应的ViewGroup和控件Widget。我尝试在Google官方文档与网络上其他讨论中寻找有关的说明,而后发现许多人不但不清楚LayoutInflater的inflate()方法的细节,而且甚至在误用它。

这里的困惑很大程度上是因为Google上有关attachToRoot(也就是inflate()方法第三个参数)的文档太模糊:

被填充的层是否应该附在root参数内部?如果是false,root参数只适用于为XML根元素View创建正确的LayoutParams的子类。

其实意思就是:如果attachToRoot是true的话,那第一个参数的layout文件就会被填充并附加在第二个参数所指定的ViewGroup内。方法返回结合后的View,根元素是第二个参数ViewGroup。如果是false的话,第一个参数所指定的layout文件会被填充并作为View返回。这个View的根元素就是layout文件的根元素。不管是true还是false,都需要ViewGroup的LayoutParams来正确的测量与放置layout文件所产生的View对象。

attachToRoot传入true,代表layout文件填充的View会被直接添加进ViewGroup,而传入false则代表创建的View会以其他方式被添加进ViewGroup。

让我们就两种情况多举一些例子来更深入的理解。

attachToRoot是True

假设我们在XML layout文件中写了一个Button并指定了宽高为match_parent。

<Button xmlns:android=“http://schemas.android.com/apk/res/android”android:layout_width=“match_parent”android:layout_height=“match_parent”android:id=“@+id/custom_button”>
</Button>

现在我们想动态地把这个按钮添加进Fragment或Activity的LinearLayout中。如果这里LinearLayout已经是一个成员变量mLinearLayout了,我们只需要通过如下代码达成目标:

inflater.inflate(R.layout.custom_button, mLinearLayout, true);

我们指定了用于填充button的layout资源文件,然后我们告诉LayoutInflater我们想把button添加到mLinearLayout中。这里Button的LayoutParams种类为LinearLayout.LayoutParams。
 
下面的代码也有同样的效果。LayoutInflater的两个参数的inflate()方法自动将attachToRoot设置为true。

inflater.inflate(R.layout.custom_button, mLinearLayout);

另一种在attachToRoot中传递true的情况是使用自定义View。我们看一个layout文件中根元素有标签的例子。标签标识着这个layout文件的根ViewGroup可以有多种类型。

public class MyCustomView extends LinearLayout {...private void init() {LayoutInflater inflater = LayoutInflater.from(getContext());inflater.inflate(R.layout.view_with_merge_tag, this);}
}

这就是一个很好的使用attachToRoot的例子。这个例子中layout文件没有ViewGroup作为根元素,所以我们指定我们自定义的LinearLayout作为根元素。如果layout文件有一个FrameLayout作为根元素而不是,那么FrameLayout和它的子元素都可以正常填充,而后都会被添加到LinearLayout中,LinearLayout是根ViewGroup,包含着FrameLayout和其子元素。

attachToRoot是False

我们看一下什么时候attachToRoot应该是false。在这种情况下,inflate()方法中的第一个参数所指定的View不会被添加到第二个参数所指定的ViewGroup中。

回忆一下刚才的例子中的Button,我们想通过layout文件添加自定义的Button至mLinearLayout中。当attachToRoot为false时,我们仍可以将Button添加到mLinearLayout中,但是这需要我们自己动手。

Button button = (Button) inflater.inflate(R.layout.custom_button, LinearLayout, false);
mLinearLayout.addView(button);

这两行代码与刚才attachToRoot为true时的一行代码等效。通过传入false,我们告诉LayoutInflater我们不暂时还想将View添加到根元素ViewGroup中,意思是我们一会儿再添加。在这个例子中,一会儿再添加就是在inflate()后调用addView()方法。

在将attachToRoot设置为false的例子中,由于要手动添加View进ViewGroup导致代码变多了。将Button添加到LinearLayout中还是用一行代码直接将attachToRoot设置为true简便一些。下面我们看一下什么情况下attachToRoot必须传入false。

每一个RecyclerView的子元素都要在attachToRoot设置为false的情况下填充。这里子View在onCreateViewHolder()中填充。

public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {LayoutInflater inflater = LayoutInflater.from(getActivity());View view = inflater.inflate(android.R.layout.list_item_recyclerView, parent, false);return new ViewHolder(view);
}

RecyclerView负责决定什么时候展示它的子View,这个不由我们决定。在任何我们不负责将View添加进ViewGroup的情况下都应该将attachToRoot设置为false。

当在Fragment的onCreateView()方法中填充并返回View时,要将attachToRoot设为false。如果传入true,会抛出IllegalStateException,因为指定的子View已经有父View了。你需要指定在哪里将Fragment的View放进Activity里,而添加、移除或替换Fragment则是FragmentManager的事情。

FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.root_viewGroup);if (fragment == null) {fragment = new MainFragment();fragmentManager.beginTransaction().add(R.id.root_viewGroup, fragment).commit();
}

上面代码中root_viewGroup就是Activity中用于放置Fragment的容器,它会作为inflate()方法中的第二个参数被传入onCreateView()中。它也是你在inflate()方法中传入的ViewGroup。FragmentManager会将Fragment的View添加到ViewGroup中,你可不想添加两次。

public View onCreateView(LayoutInflater inflater, ViewGroup parentViewGroup, Bundle savedInstanceState) {View view = inflater.inflate(R.layout.fragment_layout, parentViewGroup, false);…return view;
}

问题是:如果我们不需在onCreateView()中将View添加进ViewGroup,为什么还要传入ViewGroup呢?为什么inflate()方法必须要传入根ViewGroup?

原因是及时不需要马上将新填充的View添加进ViewGroup,我们还是需要这个父元素的LayoutParams来在将来添加时决定View的size和position。

你在网上一定会遇到一些不正确的建议。有些人会建议你如果将attachToRoot设置为false的话直接将根ViewGroup传入null。但是,如果有父元素的话,还是应该传入的。

Lint会警告你不要讲null作为root传入。你的App不会挂掉,但是可能会表现异常。当你的子View没有正确的LayoutParams时,它会自己通过 generateDefaultLayoutParams)计算。

你可能并不想要这些默认的LayoutParams。你在XML指定的LayoutParams会被忽略。我们可能已经指定了子View要填充父元素的宽度,但父View又wrap_content导致最终的View小很多。

下面是一种没有ViewGroup作为root传入inflate()方法的情况。当为AlertDialog创建自定义View时,还无法访问父元素。

    AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);View customView = inflater.inflate(R.layout.custom_alert_dialog, null);...dialogBuilder.setView(customView);dialogBuilder.show();

在这种情况下,可以将null作为root的ViewGroup传入。后来我发现AlertDialog还是会重写LayoutParams并设置各项参数为match_parent。但是,规则还是在有ViewGroup可以传入时传入它。

避开崩溃、异常表现与误解

希望这篇文章可以帮助你在使用LayoutInflater时避开崩溃、异常表现与误解。下面整理了文章的要点:

  • 如果可以传入ViewGroup作为根元素,那就传入它。

  • 避免将null作为根ViewGroup传入。

  • 当我们不负责将layout文件的View添加进ViewGroup时设置attachToRoot参数为false。

  • 不要在View已经被添加进ViewGroup时传入true。

  • 自定义View时很适合将attachToRoot设置为true。

深入理解LayoutInflater.inflate()相关推荐

  1. 深入理解 LayoutInflater.inflate() 方法

    目录 1. 前言 2. 正文 2.1 inflate() 方法分析 2.1.1 根节点不是 merge 时,第一组取值情况分析 2.1.2 根节点不是 merge 时,第二组取值情况分析 2.1.3 ...

  2. LayoutInflater——inflate方法不同参数的区别

    LayoutInflater有两个参数inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot),inflate(XmlPu ...

  3. LayoutInflater.inflate的用法总结

    LayoutInflater.inflate的用法总结 1.inflate是Android开发中经常使用到的将布局作为一个view而引进的一个方法.我们知道inflate具有两个可以使用重载方法,他们 ...

  4. Android LayoutInflater.inflate详解

    1. 作用 官方释义 Inflate a new view hierarchy from the specified xml resource 大概意思就是从给定的xml中加载view树. 2. 用法 ...

  5. LayoutInflater.inflate()方法解析

    1.基本介绍 在开发中 LayoutInflater. inflate() 这个方法还是非常有用的,它的作用类似于 findViewById().不同点是 inflate() 是用来找 res/lay ...

  6. android LayoutInflater.inflate()的参数及其用法

    很多人在网上问LayoutInflater类的用法,以及inflate()方法参数的含义,现解释如下: inflate()的作用就是将一个用xml定义的布局文件查找出来,注意与findViewById ...

  7. LayoutInflater.inflate()详解

    学习自定义View一段时间了,从开始的一窍不通到现在终于能写出点东西了,前面也写过几篇关于自定义view的博客,但是感觉这东西吧,一天不敲又忘记了,所以准备写一篇自定义View系列的博客,这也算是我这 ...

  8. LayoutInflater.inflate()方法两个参数和三个参数

    转载请标明出处:https://www.cnblogs.com/tangZH/p/7074853.html 很多人都用过LayoutInflater(布局填充器) 对于我来说通常使用下面两种:Layo ...

  9. LayoutInflater inflate参数详解

    LayoutInflater 类概述: 实例化一个XML布局文件到相应的View对象,并不直接使用.使用getLayoutInflater()或getSystemService(String)来获取一 ...

  10. Android LayoutInflater.inflate源码解析

    一年多以前看过源码,感觉了解比较透彻了,长时间不经大脑思考,靠曾经总结的经验使用inflate方法,突然发现不知道什么时候忘记其中的原理了,上网查了一些资料,还各有不同,反而把我搞糊涂了,还是自己看源 ...

最新文章

  1. installshield 安装文件属性的原始文件名 如何修改_iPhone修改微信提示音,iOS12可用,无需越狱详细教程...
  2. myeclipse6-ejb3入门
  3. //yield return用于无缝实现迭代模式。
  4. haproxy详细介绍
  5. 【python、pyqt5】,打包出现的若干问题
  6. 我来学网络——三种数据通信方式
  7. msfconsole 控制台使用和操作
  8. SwiftUI实战教程 第三章 土豆List
  9. 无线网络服务器网络异常,无线网络连接上但上不了网怎么解决
  10. 知识图谱是什么,知识图谱有什么特点?
  11. strcpy()、strncpy()函数
  12. 房产抵押贷款必须搞懂的七个问题
  13. AS打包的应用安装时解析包错误或没有签名
  14. java后端系统学习总结 03_java Web基础学习
  15. windows注册表自定义添加右键菜单
  16. python棋盘放麦粒_棋盘上的麦粒有什么问题?
  17. 打开工控机电源而计算机没有反应,工控机开机后无反应如何解决
  18. 「读书感悟系列」失明症漫记
  19. 2021年安全员-C证(山东省-2020版)考试试卷及安全员-C证(山东省-2020版)操作证考试
  20. linux dmidecode命令,linux下dmidecode命令获取硬件信息

热门文章

  1. 网易云信赵加雨:极致匠心的技术团队撑起60万开发者
  2. 聚观早报 | 通信行程卡正式宣布下线;《三体》首日播放量破1亿
  3. 双11临近,电脑无缘无故的弹出了双11天猫广告,必须追踪到底
  4. 神来之笔之傅里叶变换(Fourier Tranformation)
  5. 判断三维空间中三点是否共线
  6. Quartz_2.2.X学习系列四: Tutorials - Lesson 4: More About Triggers
  7. 经济基础知识(初级)【17】
  8. 51单片机农历转换公历c语言算法,用51单片机实现公历与农历星期的转换
  9. postman支持https、安卓抓包
  10. 服务器起到的是什么作用是什么,服务器的作用是什么