分享

LayoutInflater源码分析与应用

 fishpan_oliver 2017-06-06


今日科技快讯

据外媒报道,本周六,汽车巨头丰田支持的日本工程师展示了一款飞行汽车。除了彰显日本的技术实力,他们还希望这辆车能参与2020年东京奥运会,在开幕式上执行主火炬的点火任务。从现场演示来看,这款飞行汽车已经能轻松起飞并悬停在空中几秒钟了。工程师表示他们会不断改进飞行汽车的稳定性,以保证原型产品能有足够的滞空时间和高度来完成火炬的点火任务。除此之外,该团队还有一个目标,就是将他们的飞行汽车做成全球最小的电动飞行器,这样就能在空间紧张的城市里使用了。同时,他们希望2025年就能正式将该车推向市场。

作者简介

本篇来自 CSDN_LQR 的投稿,分享了他关于 LayoutInflater 源码分析与应用,希望大家喜欢!

 CSDN_LQR 的博客地址:

http://www.jianshu.com/u/f9de259236a3

简述

LayoutInflater 直译为 布局填充器,它是用来创建布局视图的,常用 inflate() 将一个 xml 布局文件转换成一个 View,下面先介绍下获取 LayoutInflater 的三种方式 和 创建 View 的两种方式。

实获取 LayoutInflater 的三种方式

  • LayoutInflater inflater = getLayoutInflater();  //调用Activity的getLayoutInflater()

  • LayoutInflater inflater =(LayoutInflater)context.getSystemService(Context.LAYOUT\_INFLATER\_SERVICE);

  • LayoutInflater inflater = LayoutInflater.from(context); 

其实不管是哪种方式,最后都是通过方式2获取到 LayoutInflater 的,如:

创建 View 的两种方式

  • View.inflate();

  • LayoutInflater.from(context).inflate();

源码分析

上面两种创建 View 的方式都是开发中常用的,那两者有什么关系吗?下面对 View.inflate()进行方法调用分析:

View.inflate() 最终调用方法探究

  • 按住Ctrl+鼠标左键查看  View.inflate() 方法

可以看到 View.inflate() 就是调用了 LayoutInflater.from(context).inflate()。

好,到这一步要明确,不管我们研究哪种方式,实际上都研究方式2,即 LayoutInflater.from(context).inflate()。

  • 按住Ctrl+鼠标左键查看 LayoutInflater.from(context).inflate(resource, root) 方法。

嗯? LayoutInflater.from(context).inflate(resource, root) 再调用了自己的重载 inflate(resource, root, root != null)。

  • 按住Ctrl+鼠标左键查看 LayoutInflater.from(context).inflate(resource, root).inflate(resource, root, root != null) 方法。

嗯?? LayoutInflater.from(context).inflate(resource, root).inflate(resource, root, root != null) 再再调用了自己的重载 inflate(parser, root, attachToRoot)。

  • 按住Ctrl+鼠标左键查看 LayoutInflater.from(context).inflate(resource, root).inflate(resource, root, root != null).inflate(parser, root, attachToRoot) 方法。

这下总算是到头了,不过代码太长,这里就截了一半的图(这不是重点)。

好,重点来了,到这步我们可以明白一点, View.inflate() 整个方法调用链如下:

  • LayoutInflater的inflate(parser, root, attachToRoot)做了什么?

由于代码太长,不方便截图,下面贴出代码中的重点代码:

该 inflate 方法中有以下四步操作:

  1. 通过使用 XmlPullParser parser将 xml 布局文件转换成视图 temp 。

  2. 判断 ViewGroup root 对象是否为 null,来决定要不要给temp设置 LayoutParams。

  3. 判断 boolean attachToRoot 是否为 true,来决定是否要把 temp 顺便加到 ViewGroup root 中。

  4. 最后返回视图temp。

到这里就把创建视图的流程分析完了,接下来是比较 View.inflate() 和 LayoutInflater.from(context).inflate() 的区别。

View.inflate() 和 LayoutInflater.from(context).inflate() 的区别

  • View.inflate() 第三个参数的解析:

开发中常常会对第三个参数(ViewGroup root)传入null吧,通过上面对最终 inflate 方法的分析,可以知道,如果 ViewGroup root 取值为 null,则得到的视图 temp 不会被设置 LayoutParams。下面做个试验:

打印结果如下:

同理,将第三个参数传入一个确实存在的 ViewGroup 时,结果就是视图 temp 能获取到LayoutParams,有兴趣的可以自己试试。

  • LayoutInflater.from(context).inflate() 的优势:

下面的场景分析将体现出 LayoutInflater.from(context).inflate() 的灵活性。

如果是在 RecyclerView 或 ListView 中使用 View.inflate() 创建布局视图,又想对创建出来的布局视图进行高度等参数设置时,会有什么瓶颈呢?

下面贴出我之前写过的一段用于瀑布流适配器的代码:

经过上面对 View.inflate() 的第三个参数解析之后,这代码的问题一眼就能看出来了吧,没错,就是 ViewGroup.LayoutParams params = holder.mTv.getLayoutParams() ;这行代码获取到的 LayoutParams 为空,不信?走一个。

接下来理所当然的要让得到的 LayoutParams 不为空啦,所以将 onCreateViewHolder()的代码修改如下:

传入的 ViewGroup parent 不为 null,所以肯定获取的 LayoutParams 不为空,但是又有一个问题,看报错。

为什么会报这样的错呢?回看最终 inflate() 的四个步骤:

  1. 通过使用 XmlPullParser parser将 xml 布局文件转换成视图 temp。

  2. 判断 ViewGroup root对象是否为 null,来决定要不要给temp设置 LayoutParams。

  3. 判断 boolean attachToRoot 是否为 true,来决定是否要把temp顺便加到 ViewGroup root 中。

  4. 最后返回视图 temp。

步骤2让条目获取的 LayoutParams 不为空没错,但是步骤3出问题了,当使用 View.inflate(parent.getContext(), android.R.layout.simple\_list\_item\_1, parent) 传入parent后,boolean attachToRoot 的取值就是为 true,所以创建出来的条目会顺便添加到 ViewGroup 中(这里的ViewGroup就是RecyclerView),而 RecyclerView 本身就会自动将条目添加到自身,这样就添加了两次,故报错。那为什么 attachToRoot 的取值是 true 呢?再看 View.inflate() 的整个方法调用链:

boolean attachToRoot 的取值取决于 root(也就是parent)是否为空,这就是 View.inflate() 的瓶颈,它没法灵活的指定 boolean attachToRoot 的取值。这里我就是只是想让创建出来的视图能得到 LayoutParams,但不添加到 ViewGroup 中,这样的要求可以通过 LayoutInflater.from(context).inflate() 来实现。所以下面将 onCreateViewHolder() 的代码修改如下:

代码中LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple\_list\_item\_1,parent,false) 传入了 parent(即ViewGroup不为null),所以创建出来的视图可以得到 LayoutParams,同时又指定 attachToRoot 的取值为 false,即不添加到 ViewGroup 中。到这里,上面重覆添加子控件的问题就解决了,总结一下吧:

  • View.inflate() 第三个参数若不为 null,则创建出来的视图一定能获得 LayoutParams,反之,不一定。(下面会解释)

  • LayoutInflater.from(context).inflate() 可以灵活的指定传入的 ViewGroup 是否为空来决定创建出来的视图能否获得 LayoutParams,同时又可以指定 attachToRoot 的取值来决定创建出来的视图是否要添加到 ViewGroup 中。

小细节

上面已经将 LayoutInflater 的源码分析完毕,现在还有一个小问题,其实跟本文主题没多大关系,当作拓展来看吧。

前面说到,View.inflate() 第三个参数若不为 null,则创建出来的视图一定能获得 LayoutParams,反之,不一定。这话怎么理解?

也就是说,即使 View.inflate() 第三个参数为 null,创建出来的视图也有可能获得 LayoutParams 咯?是的,说到底,这个 LayoutParams 的有无,实际取决于条目本身是否有父控件,且看上面用到的 simple\_list\_item\_1 布局:

发现了吧,就一个 TextView,没有父控件,那如果我给它加个父控件,同时使用最开始的方式也能顺利得到 LayoutParams 呢?代码如下:

item_layout 的布局代码如下:

 运行,果然可以获得LayoutParams,打印结果如下:

最后

本人也是头次写类分析型的文章,如描述有误,请不吝赐教,同时还请各位看客多担待,指出后本人会尽快修改,谢谢。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多