备注

原发表于2016.06.20,资料已过时,仅作备份,谨慎参考

前言

感觉很长时间没写文章了,这个星期因为回家和处理项目问题,还是花了很多时间的。虽然知道很多东西如果只是看一下用一次,很快就会遗忘,但认认真真地做输出还是需要一定恒心的。

这次写 LayoutInflater 的工作流程,是由于小组一位成员在调用inflate 方法时,没有传入 parent 参数导致生成的布局宽高失效的问题。

这里先说原因,是因为如果 inflate 的 View,没有包含在某个 Viewgroup 下,也没有传入 parent 参数,那么他的 layout_width 等属性就会失效,这些属性是需要 View 处在某个布局下才能生效。

使用

获取 LayoutInflater

通过 LayoutInflater layoutInflater = LayoutInflater.from(context) 就可以获取到 LayoutInflater。

如果调用 View.inflate 也是先会获取 LayoutInflater,再使用 LayoutInflater 去加载布局。获取 LayoutInflater 的源码如下:

public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}

可以看到,我们其实也可以自己去调用 getSystemService 来获取 LayoutInflater。

加载布局

接下来,使用 layoutInflater.inflate 方法即可加载相应布局,有四个重载方法如下图:

其中 parser 参数是一个描述 View 层次的 XML 文件,在 Android 中我们就是用 XML 来描述布局的,所以直接传入布局的 int 值就可以了。

另外两个需要注意的参数是 root 和 attachToRoot,root 是指定了本次加载布局的父容器,attachToRoot 则表示是否将本次加载的内容添加到父容器中。

我们常常会碰到在 adapter 中,设置 attachToRoot 为 true 时会报错,是因为 adapter 会自己将加载的布局添加到父容器里,如果自己设置的话,就会导致重复添加了。

工作流程

所有的 inflate 到最后都会返回到下面这个方法中来进行布局加载:

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root; try {
// Look for the root node.
int type;
... 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, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; if (root != null) { // Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
} // Inflate all children under temp against its context.
rInflateChildren(parser, temp, attrs, true); // We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
root.addView(temp, params);
} // Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
} } catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (Exception e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
} Trace.traceEnd(Trace.TRACE_TAG_VIEW); return result;
}
}

代码较多,我去掉了有关 DEBUG 的部分,同时最后异常处理的部分也可以略过不看,那么逻辑就比较清晰了。LayoutInflater 使用 XmlPullParser 来解析布局文件,首先根据我们传入的布局参数创建根布局:

final View temp = createViewFromTag(root, name, inflaterContext, attrs);

ViewGroup.LayoutParams params = null;

这个 params 参数是用来为 temp 即加载的根布局进行属性参数设置的,他有以下三种情况:

  1. ViewGroup root 参数为 null,则返回加载的布局不做其他设置

  2. root != null && attachToRoot == null,则使用父布局的参数对 temp 进行设置

     params = root.generateLayoutParams(attrs);
    temp.setLayoutParams(params);
  3. root != null && attachToRoot != null,则将加载的布局添加到父布局,再返回父布局:

     root.addView(temp, params);

接下来还会遍历加载根布局的子元素:

rInflateChildren(parser, temp, attrs, true);

这样就完成了一次布局的加载,具体是如何加载的,就得去学习 View 的工作原理了。

参考资料

这里也提示一下,你看到的资料不一定都是最正确的,尽量接受多方的信息,比如一篇博客看完后,最好是能把评论也翻看一遍。

Android LayoutInflater原理分析,带你一步步深入了解View(一)

[旧][Android] LayoutInflater 工作流程的更多相关文章

  1. [旧][Android] ButterKnifeProcessor 工作流程分析

    备注 原发表于2016.05.21,资料已过时,仅作备份,谨慎参考 前言 在 [Android] ButterKnife 浅析 中,我们了解了 ButterKnife 的用法,比较简单. 本次文章我们 ...

  2. [旧][Android] View 工作原理(二)

    备注 原发表于2016.05.27,资料已过时,仅作备份,谨慎参考 前言 本文大量参照<Android 开发艺术探索>及参考资料的内容整合,主要帮助自己理清 View 的工作原理.深入学习 ...

  3. [旧][Android] View 工作原理(一)

    备注 原发表于2016.05.23,资料已过时,仅作备份,谨慎参考 前言 本文参考<Android 开发艺术探索>及网上各种资料进行撰写,目的是为自己理清 Android 中 View 的 ...

  4. Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程

    本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处 本文代码以MTK平台Android 4.4为分析对象,与Google原生AOSP有些许差异,请读者知悉. ...

  5. Android 4.4 Kitkat Phone工作流程浅析(八)__Phone状态分析

    本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处 本文代码以MTK平台Android 4.4为分析对象.与Google原生AOSP有些许差异.请读者知悉. ...

  6. Android 4.4 Kitkat Phone工作流程浅析(七)__来电(MT)响铃流程

    本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处 本文代码以MTK平台Android 4.4为分析对象,与Google原生AOSP有些许差异,请读者知悉. ...

  7. MapReduce简述、工作流程及新旧API对照

    什么是MapReduce? 你想数出一摞牌中有多少张黑桃.直观方式是一张一张检查而且数出有多少张是黑桃. MapReduce方法则是: 1. 给在座的全部玩家中分配这摞牌. 2. 让每一个玩家数自己手 ...

  8. 分析Android中View的工作流程

    在分析View的工作流程时,需要先分析一个很重要的类,MeasureSpec.这个类在View的测量(Measure)过程中会用到. MeasureSpec MeasureSpec是View的静态内部 ...

  9. Android事件分发机制三:事件分发工作流程

    前言 很高兴遇见你~ 本文是事件分发系列的第三篇. 在前两篇文章中,Android事件分发机制一:事件是如何到达activity的? 分析了事件分发的真正起点:viewRootImpl,Activit ...

随机推荐

  1. rocketmq之延迟队列(按照18个等级来发送)

    1 启动消费者等待传入的订阅消息 import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache ...

  2. Docker 私服

    目录 什么是 Docker 私服? Docker 私服搭建 上传镜像至私服 从私服拉取镜像 什么是 Docker 私服? Docker 官方的 Docker Hub 是一个用于管理公共镜像的仓库,我们 ...

  3. 使用Cesium Stories来可视化时序数据

    Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ Cesium可以用来可视化随时间变化的数据,无论是跨越数百年的地 ...

  4. synergy最佳解决方案——barrier

    synergy最佳解决方案--barrier ​ 不知道大家有没有一套键盘鼠标控制多台电脑的需求,主流的硬件或说软件有大神整理如下: 软件方案: Windows 之间:Mouse Without Bo ...

  5. selenium获取cookies并持久化登陆

    selenium获取cookies并持久化登陆 需求背景: ​ 这几天需要写一个接口,用来批量上传数据,最开始考虑的是 UI 自动化,然后选值的时候自动化难以判别,最终选择 接口 自动化. ​ 然后操 ...

  6. golang中int、float、string数据类型之间的转换

    package main import ( "fmt" "strconv" ) func main() { var num1 int = 88 var num2 ...

  7. CSS基本语法(三)

    目录 CSS基础语法(三) 十五.CSS定位 1.为什么要使用定位 2.定位组成 定位模式 静态定位 相对定位 绝对定位** 固定定位 粘性定位 边偏移 子绝父相 3.定位的叠放次序 4.拓展 绝对定 ...

  8. 前端vue-TinyMCE富文本编辑器表情插件报错解决

    最近项目中需要使用文本编辑器,比较了下最终选择了TinyMCE这款富文本编辑器.我安装的是TinyMCE v5但是在使用表情插件的时候,表情一直都出不来,报错信息如下: Uncaught Syntax ...

  9. 基于Redis&MySQL接口幂等性设计

    基于Redis&MySQL接口幂等性设计 欲把相思说似谁,浅情人不知. 1.幂等 幂等性即多次调用接口或方法不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致. 2.幂等使用场景 前 ...

  10. 随机IP代理插件Scrapy-Proxies

    安装: pip install scrapy_proxies github:   https://github.com/aivarsk/scrapy-proxies scrapy爬虫配置文件setti ...