当前位置: 首页 > 编程日记 > 正文

View_01_LayoutInflater的原理、使用方法

View_01_LayoutInflater的原理、使用方法

本篇博客是郭神博客Android视图状态及重绘流程分析,带你一步步深入了解View(一)的读书笔记的笔记。

LayoutInflater简单介绍

setContentView()内部是使用LayoutInflater来完毕载入布局的。

setContentView()方法内部的源代码是internal的。不太easy查到。
翻了七八个类后,在AppCompatDelegateImplV7.java中找到了这段代码

@Override
public void setContentView(int resId) {ensureSubDecor();ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);contentParent.removeAllViews();LayoutInflater.from(mContext).inflate(resId, contentParent);mOriginalWindowCallback.onContentChanged();
}

获取LayoutInflater实例

第一种方法:

     LayoutInflater layoutInflater = LayoutInflater.from(context);

另外一种方法:

     LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

事实上另外一种方法是第一种的简单写法,仅仅是Android给我们做了封装而已。

layoutInflater.inflate()方法

演示样例:

layoutInflater.inflate(resourceId, root);

第一个參数就是要载入的布局id,第二个參数是指给该布局的外部再嵌套一层父布局,假设不须要就直接传null。这样就成功成功创建了一个布局的实例。之后再将它加入到指定的位置就能够显示出来了。

实例:LayoutInflater的使用方法

activity_main.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/main_layout"android:layout_width="match_parent"android:layout_height="match_parent" ></LinearLayout>

button_layout.xml

 <Button xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Button" ></Button>

如今我们要想办法,怎样通过LayoutInflater来将button_layout这个布局加入到主布局文件的LinearLayout中。依据刚刚介绍的使用方法,改动MainActivity中的代码,例如以下所看到的:

MainActivity.java

public class MainActivity extends Activity {private LinearLayout mainLayout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mainLayout = (LinearLayout) findViewById(R.id.main_layout);LayoutInflater layoutInflater = LayoutInflater.from(this);View buttonLayout = layoutInflater.inflate(R.layout.button_layout, null);mainLayout.addView(buttonLayout);}}

结果例如以下图所看到的

picture

LayoutInflater技术广泛应用于须要动态加入View的时候,比方在ScrollView和ListView中,常常都能够看到LayoutInflater的身影。

LayoutInflater的工作原理

inflate源代码

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {synchronized (mConstructorArgs) {final AttributeSet attrs = Xml.asAttributeSet(parser);mConstructorArgs[0] = mContext;View result = root;try {int type;while ((type = parser.next()) != XmlPullParser.START_TAG &&type != XmlPullParser.END_DOCUMENT) {}if (type != XmlPullParser.START_TAG) {throw new InflateException(parser.getPositionDescription()+ ": No start tag found!");}final String name = parser.getName();if (TAG_MERGE.equals(name)) {if (root == null || !attachToRoot) {throw new InflateException("merge can be used only with a valid "+ "ViewGroup root and attachToRoot=true");}rInflate(parser, root, attrs);} else {View temp = createViewFromTag(name, attrs);ViewGroup.LayoutParams params = null;if (root != null) {params = root.generateLayoutParams(attrs);if (!attachToRoot) {temp.setLayoutParams(params);}}rInflate(parser, temp, attrs);if (root != null && attachToRoot) {root.addView(temp, params);}if (root == null || !attachToRoot) {result = temp;}}} catch (XmlPullParserException e) {InflateException ex = new InflateException(e.getMessage());ex.initCause(e);throw ex;} catch (IOException e) {InflateException ex = new InflateException(parser.getPositionDescription()+ ": " + e.getMessage());ex.initCause(e);throw ex;}return result;}
}

从这里我们就能够清楚地看出,LayoutInflater事实上就是使用Android提供的pull解析方式来解析布局文件的。
这里我们注意看下第23行,调用了createViewFromTag()这种方法。并把节点名和參数传了进去。

看到这种方法名。我们就应该能猜到。它是用于依据节点名来创建View对象的。确实如此,在createViewFromTag()方法的内部又会去调用createView()方法,然后使用反射的方式创建出View的实例并返回。
当然,这里仅仅是创建出了一个根布局的实例而已。接下来会在第31行调用rInflate()方法来循环遍历这个根布局下的子元素,代码例如以下所看到的:

rInflate源代码

private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)throws XmlPullParserException, IOException {final int depth = parser.getDepth();int type;while (((type = parser.next()) != XmlPullParser.END_TAG ||parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {if (type != XmlPullParser.START_TAG) {continue;}final String name = parser.getName();if (TAG_REQUEST_FOCUS.equals(name)) {parseRequestFocus(parser, parent);} else if (TAG_INCLUDE.equals(name)) {if (parser.getDepth() == 0) {throw new InflateException("<include /> cannot be the root element");}parseInclude(parser, parent, attrs);} else if (TAG_MERGE.equals(name)) {throw new InflateException("<merge /> must be the root element");} else {final View view = createViewFromTag(name, attrs);final ViewGroup viewGroup = (ViewGroup) parent;final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);rInflate(parser, view, attrs);viewGroup.addView(view, params);}}parent.onFinishInflate();
}

这种话。把整个布局文件都解析完毕后就形成了一个完整的DOM结构。终于会把最顶层的根布局返回,至此inflate()过程所有结束。
比較细心的朋友或许会注意到,inflate()方法还有个接收三个參数的方法重载,结构例如以下:

inflate()方法的第三个參数

inflate(int resource, ViewGroup root, boolean attachToRoot)
1. 假设root为null。attachToRoot将失去作用。设置不论什么值都没有意义。
2. 假设root不为null,attachToRoot设为true,则会给载入的布局文件的指定一个父布局,即root。
3. 假设root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被加入到父view其中时,这些layout属性会自己主动生效。


4. 在不设置attachToRoot參数的情况下,假设root不为null,attachToRoot參数默觉得true。

将上面实例中的Button按钮变大

事实上这里无论你将Button的layout_width和layout_height的值改动成多少。都不会有不论什么效果的,由于这两个值如今已经全然失去了作用。

平时我们常常使用layout_width和layout_height来设置View的大小,而且一直都能正常工作。就好像这两个属性确实是用于设置View的大小的。

而实际上则不然,它们事实上是用于设置View在布局中的大小的,也就是说,首先View必须存在于一个布局中,之后假设将layout_width设置成match_parent表示让View的宽度填充满布局,假设设置成wrap_content表示让View的宽度刚好能够包括其内容,假设设置成详细的数值则View的宽度会变成对应的数值。这也是为什么这两个属性叫作layout_width和layout_height,而不是width和height。

改动button的布局文件。不能变大

<Button xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="300dp"android:layout_height="80dp"android:text="Button" >
</Button>

在button外嵌套一层布局,可使其变大

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent" ><Button
        android:layout_width="300dp"android:layout_height="80dp"android:text="Button" ></Button>
</RelativeLayout>

能够看到,这里我们又加入了一个RelativeLayout,此时的Button存在与RelativeLayout之中。layout_width和layout_height属性也就有作用了。

当然,处于最外层的RelativeLayout,它的layout_width和layout_height则会失去作用。如今又一次执行一下程序,结果例如以下图所看到的

picture

setContentView()为什么叫setContentView ?

看到这里,或许有些朋友心中会有一个巨大的疑惑。不正确呀!平时在Activity中指定布局文件的时候,最外层的那个布局是能够指定大小的呀,layout_width和layout_height都是有作用的。

确实,这主要是由于。在setContentView()方法中,Android会自己主动在布局文件的最外层再嵌套一个FrameLayout。所以layout_width和layout_height属性才会有效果。那么我们来证实一下吧。改动MainActivity中的代码,例如以下所看到的:

MainActivity.java

public class MainActivity extends Activity {private LinearLayout mainLayout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mainLayout = (LinearLayout) findViewById(R.id.main_layout);ViewParent viewParent = mainLayout.getParent();Log.d("TAG", "the parent of mainLayout is " + viewParent);}}

打印结果:

picture

LinearLayout的父布局确实是一个FrameLayout,而这个FrameLayout就是由系统自己主动帮我们加入上的。
讲到这里。尽管setContentView()方法大家都会用,但实际上Android界面显示的原理要比我们所看到的东西复杂得多。不论什么一个Activity中显示的界面事实上主要都由两部分组成,标题栏和内容布局。

标题栏就是在非常多界面顶部显示的那部分内容,比方刚刚我们的那个样例其中就有标题栏,能够在代码中控制让它是否显示。

而内容布局就是一个FrameLayout,这个布局的id叫作content,我们调用setContentView()方法时所传入的布局事实上就是放到这个FrameLayout中的。这也是为什么这种方法名叫作setContentView(),而不是叫setView()。
最后再附上一张Activity窗体的组成图吧,以便于大家更加直观地理解:

picture

小结

本篇主要介绍了LayoutInflater的原理、使用方法,为深入理解View做了准备。

作者信息

  • 个人博客:http://blog.csdn.net/fy9987899
  • github项目:https://github.com/fanyu2013/Android-Blogs

相关文章:

【ubuntu】在ubuntu下无法输出拼音输入法中的中括号“【” 和 “】”的解决方法

问题 在新装的ubuntu16.04中&#xff0c;打不出中括号【】&#xff0c;而变成 “”和“「” 解决方法 修改文件/usr/share/fcitx/data/punc.mb.zh_CN sudo vi /usr/share/fcitx/data/punc.mb.zh_CN将18、19行改为如下内容 18 [ 【 19 ] 】

配置数据源和分页

1.tomcat的版本&#xff0c;最好不要是安装版的&#xff0c;要解压缩的&#xff0c;不然无法连接数据源。2.conf》context.xml<Resource name"hotel" auth"Container" type"javax.sql.DataSource" driverClassName"oracle.jdbc.…

售价1万7的华为Mate X很贵吗?

整理 | 琥珀 出品 | AI科技大本营&#xff08;ID: rgznai100&#xff09; 60s测试&#xff1a;你是否适合转型人工智能&#xff1f; https://edu.csdn.net/topic/ai30?utm_sourcecxrs_bw 继三星之后&#xff0c;网友们期待已久的华为终于忍不住宣布了一系列重磅消息&#xff…

【C】数组数组初始化总结

C数组初始化总结 发现一个新方法&#xff0c;可以分段初始化数组 eg&#xff1a;int arrayC[MAX_LEN] {[1 … 5]9, [6 … 9] 8}; 代码如下 #include <stdio.h> #define MAX_LEN 10int main (int argc, char *argv[]) { //不初始化&#xff0c;参数值随机分配 //[0][0…

Golang的反射reflect深入理解和示例

[TOC] Golang的反射reflect深入理解和示例 【记录于2018年2月】 编程语言中反射的概念 在计算机科学领域&#xff0c;反射是指一类应用&#xff0c;它们能够自描述和自控制。也就是说&#xff0c;这类应用通过采用某种机制来实现对自己行为的描述&#xff08;self-representati…

如何读取多个文件,文件后缀名不一致,不过类似source.1 source.2 source.3等

#include <stdio.h> #include <stdlib.h> //为了使用exit() char *itoa(int num,char *str,int radix); int main() { int ch; FILE* fp; // char fname[50]"scan1.source.2100"; //用于存放文件名 char fname[20]"source."; …

AtCoder Petrozavodsk Contest 001

第一场apc&#xff0c;5H的持久战&#xff0c;我当然水几个题就睡了 A - Two Integers Time limit : 2sec / Memory limit : 256MB Score : 100 points Problem Statement You are given positive integers X and Y. If there exists a positive integer not greater than 1018…

【Qt】使用QCamera获取摄像头,并使用图像视图框架QGraphics*来显示

代码下载 https://download.csdn.net/download/u010168781/10373174 #####头文件 #ifndef CAMERATEST_H#define CAMERATEST_H#include <QMainWindow> #include <QGraphicsView> #include <QKeyEvent> #include <QTimer>namespace Ui { class Camera…

CVPR 2019收录论文ID公开,你上榜了吗?

整理 | 琥珀 出品 | AI科技大本营&#xff08;ID: rgznai100&#xff09; 计算机视觉和模式识别大会 CVPR&#xff08;Conference on Computer Vision and Pattern Recognition&#xff09;作为人工智能领域计算机视觉方向的重要学术会议&#xff0c;每年都会吸引全球最顶尖的…

什么是 prelink

2019独角兽企业重金招聘Python工程师标准>>> Most programs require libraries to function. Libraries can be integrated into a program once, by a linker, when it is compiled (static linking) or they can be integrated when the program is run by a load…

PythonR爬取分析赶集网北京二手房数据(附详细代码)

本文转载自数据森麟&#xff08;ID:shujusenlin&#xff09; 作者介绍&#xff1a;徐涛&#xff0c;19年应届毕业生&#xff0c;专注于珊瑚礁研究&#xff0c;喜欢用R各种清洗数据。 知乎&#xff1a;parkson 如何挑战百万年薪的人工智能&#xff01; https://edu.csdn.net/t…

【Qt】QCloseEvent的使用小结

问题描述 在程序中使用QCloseEvent时,有时没有反应,没有关闭程序。 原因 经测试只有在界面起来以后,使用event->accept()才能关闭程序 测试如下 在构造函数中调用close() 在构造函数中调用close()时,会触发QCloseEvent事件,但是程序界面没有关闭。 使用按钮触发…

Java反射 - 私有字段和方法

尽管普遍认为通过Java Reflection可以访问其他类的私有字段和方法。 这并不困难。 这在单元测试中可以非常方便。 本文将告诉你如何。 访问私有字段 要访问私有字段&#xff0c;您需要调用Class.getDeclaredField&#xff08;String name&#xff09;或Class.getDeclaredFields…

.Net 程序员面试 C# 语言篇 (回答Scott Hanselman的问题)

过去几年都在忙着找项目&#xff0c;赶项目&#xff0c;没有时间好好整理深究自己在工作中学到的东西。现在好了&#xff0c;趁着找工作的这段空余时间&#xff0c;正好可以总结和再继续夯实自己的.Net, C#基本功。在05年的时候&#xff0c;Scott Hanselman(微软的一个Principa…

一个小小的AI训练营竟然卧虎藏龙

年前&#xff0c;我来到了一个近墨者黑的地方&#xff0c;黑的不能再黑。。。这个神秘的组织叫做 21 天入门机器学习训练营。讲真的&#xff0c;当初报名这个训练营&#xff0c;我是冲着机器学习来的&#xff0c;主要是好奇想转型&#xff0c;而且听说这个课程对小白很友好&…

【Qt】QCamera查询和设置摄像头的分辨率

查询和设置摄像头分辨率的API QCamera::supportedViewfinderResolutions() QCamera::setViewfinderSettings() 设置摄像头帧率、比例、分辨率、格式的类&#xff1a;QCameraViewfinderSettings 使用注意事项 查询和设置摄像头分辨率时&#xff0c;需要在摄像头启动后调用&a…

附录G Netty与NettyUtils

版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 https://blog.csdn.net/beliefer/article/details/77450134 注&#xff1a;本文是为了配合《Spark内核设计的艺术 架构设计与实现》一书的内容而编写&#xff0c;目的是为了节省成本、方便读者查阅。…

grails日志系统的研究

对于grails的日志输出&#xff0c;我真的是给弄吐血了。开始以为很简单&#xff0c;后来发现grails封装log4j做的有点太多了&#xff0c;很多东西的封装理解了觉得还挺合理&#xff0c;但是不理解的话真是无比迷茫。对于是否有必要做这么多强制性约束&#xff0c;我保留意见...…

给老婆写个Python教程

作者 | 水风 来源 | 水风知乎问答 如何挑战百万年薪的人工智能&#xff01; https://edu.csdn.net/topic/ai30?utm_sourcecsdn_bw 什么是code code就是一种语言&#xff0c;一种计算机能读懂的语言。计算机是一个傻逼&#xff0c;他理解不了默认两可的任何东西。比如&#xf…

SpringBoot的修改操作

今天学习SpringBoot 的 CRUD 操作&#xff0c;练习 修改操作 时&#xff0c;发生了如下的异常&#xff1a; [nio-8080-exec-7] .m.m.a.ExceptionHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.dao.InvalidDataAccessApiUsa…

【Qt】QImage、QPixmap、QBitmap和QPicture

简述 Qt 提供了四个用于处理图像数据的类: QImage、 QPixmap、 QBitmap和QPicture。QImage是为 I/O 设计和优化的, 用于直接像素访问和操作, 而QPixmap是为在屏幕上显示图像而设计和优化的。QBitmap继承自QPixmap&#xff0c;用在位深为1&#xff08;黑白图片&#xff09;上。…

ASP.NET,IIS7.0 上传大视频文件报错

一、问题概述&#xff1a; 最近开发上传视频文件的功能。基本流程已经跑通了&#xff0c;可是上传30M以上的文件时就会报错。 二、资料海洋瞎扑腾 从网上查了一些资料&#xff0c;一般都是下面这种说法&#xff1a; 看着步骤倒是也不算繁琐&#xff0c;可是本人照着步骤做了却没…

【imx6】Unable to find the ncurses libraries的解决办法

问题描述 在执行make menuconfig时&#xff0c;报错&#xff1a; Unable to find the ncurses libraries… 解决方法 安装ncurses和ncursesw库 sudo apt-get insatll ncurses-dev sudo apt-get insatll ncursesw-dev 注意&#xff1a;ncursesw库是ncurses的升级版本&#…

Elasticsearch6.1.3 for CRUD

为什么80%的码农都做不了架构师&#xff1f;>>> 一、创建文档 [root AOS2 AutoTest01:/root]#curl -X PUT 9.1.6.140:9200/students/class1/1?pretty -d > { > "first_name": "changwei", > "last_name": "…

指纹锁就安全了?防火防盗还得防AI

整理 | 一一 出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09; 如何挑战百万年薪的人工智能 https://edu.csdn.net/topic/ai30?utm_sourcecsdn_bw 近日&#xff0c;你应该看到了社交媒体上对于网站 ThisPersonDoesNotExist.com&#xff0c;生成无数不存在人脸的铺天…

迪杰斯特拉算法(C语言实现)

迪杰斯特拉算法&#xff08;C语言实现&#xff09; 如上图&#xff0c;求以a为源点到个顶点的最短路劲。 #include "stdio.h"#include "stdlib.h"//用一个最大数表示顶点之间不相关#define MAX 999//设置顶点个数#define MAX_VERTEX_NUM 7//表示顶点之间不…

小米半年来最大调整:成立技术委员会,雷军称技术事关生死存亡

整理 | 琥珀出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;昨晚&#xff0c;小米集团组织部下发正式文件&#xff0c;宣布了最新一轮组织架构调整&#xff0c;任命了崔宝秋为集团副总裁&#xff0c;集团技术委员会主席&#xff0c;并且在核心管理岗位上共任命了 14 …

【驱动】在内核源码中添加驱动程序

以wifi驱动(RTL8188EUS驱动)为例 添加源码 将源码rtl8188EUS添加到drivers/net/wireless/rtl818x/目录下 添加Kconfig 在drivers/net/wireless/rtl818x/rtl8188EUS添加Kconfig&#xff0c;内容如下&#xff1a; config RTL8188EUtristate "Realtek 8188E USB WiFi&qu…

怎么让wordpress用sqlite3 搭建轻量级博客系统

wordpress 默认是用mysql作为数据库支持&#xff0c;这个对个人站长来说还是有点麻烦了些。特别是如果以后网站备份迁移就有点事多了。 之前用django开发自己的博客感觉其实用sqlite3作为数据库插好&#xff0c;就是一个文件而已。备份网站&#xff0c;直接打包整个目录即可方便…

IBM蓝色基因/Q将采用NAND闪存存储

IBM将在计划中的高性能“怪兽”——蓝色基因/Q中采用NAND闪存存储。 这是一款采用水冷方式的高性能计算系统&#xff0c;IBM在近日的SC10大会上展示了其原型机的组件。 蓝色基因/Q将采用的闪存是来自SMART的XceedIOPS MLC NAND产品&#xff0c;它使用34nm制程工艺&…