Android使用LayoutInflater来进行布局加载,通常获取方式有两种:

第一种:

LayoutInflater layoutInflater = LayoutInflater.from(context); 

第二种:

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

从源码中可以看出第一种是第二种的封装简化,便于使用:

 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;
}

我们通过调用inflate方法便可以完成对布局的加载:

layoutInflater.inflate(resource, root, true);  

LayoutInflater中的inflate方法有若干种重载方式,最终都调用了如下代码:

 public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
//获取xml中属性信息
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mContext;
View result = root; try {
// 查找根节点.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
} if (type != XmlPullParser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
//获取根节点名称
final String name = parser.getName(); if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: "
+ name);
System.out.println("**************************");
}
//如果是merge标签,必须保证父节点不为null且attachToRoot为true
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, false);
} else {
//代表布局文件中根节点的view
View temp;
if (TAG_1995.equals(name)) {
temp = new BlinkLayout(mContext, attrs);
} else {
//利用反射,通过root名称创建view
temp = createViewFromTag(root, name, attrs);
} ViewGroup.LayoutParams params = null; if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// 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)
//当不把当前view附加到父容器中,则设置获取到的布局参数
//否则使用下面的addView方法设置
temp.setLayoutParams(params);
}
} if (DEBUG) {
System.out.println("-----> start inflating children");
}
// Inflate all children under temp
//递归调用此方法加载子布局
rInflate(parser, temp, attrs, true);
if (DEBUG) {
System.out.println("-----> done inflating children");
} // 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 (IOException 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;
} return result;
}
}

这里,Android使用了PULL来解析xml布局文件,并通过反射来创建出当前view:

temp = createViewFromTag(root, name, attrs);

我们查看一下源码:

 View createViewFromTag(View parent, String name, AttributeSet attrs) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
} if (DEBUG) System.out.println("******** Creating view: " + name); try {
View view;
if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, mContext, attrs);
else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs);
else view = null; if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, mContext, attrs);
} if (view == null) {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);
}
} if (DEBUG) System.out.println("Created view is: " + view);
return view; } catch (InflateException e) {
throw e; } catch (ClassNotFoundException e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + name);
ie.initCause(e);
throw ie; } catch (Exception e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class " + name);
ie.initCause(e);
throw ie;
}
}

里面根据不同情况,调用了onCreateView方法,利用反射来创建view。其中可以使用指定的factory来创建view,这样的钩子设计使得inflate方法变得十分灵活。

然后调用rInflate(parser, temp, attrs, true)方法来递归查找temp中的子view,并添加到上层view中:

 void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
boolean finishInflate) 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 if (TAG_1995.equals(name)) {
final View view = new BlinkLayout(mContext, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs, true);
viewGroup.addView(view, params);
} else {
final View view = createViewFromTag(parent, name, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs, true);
viewGroup.addView(view, params);
}
} if (finishInflate) parent.onFinishInflate();
}

里面也用到onCreateView方法创建子view,然后将其加入到父view中返回。

通过查看上面的源码,我们可以发现inflate方法中的三个参数int resource, ViewGroup root, boolean attachToRoot的作用如下:

resource指定了要加载的view,root作为view外面一层的父容器,attachToRoot表示是否将view加入到父容器。

当指定了父容器,并且attachToRoot为true,则将view加入到父容器中。

如果指定了父容器,却将attachToRoot设置为false,那么只是从父容器中生成了view布局的参数并设置给view

当未指定父容器时,直接返回view本身。

总结

通过研究LayoutInflater源码的设计,我们了解到代码的执行细节的同时,也可以发现:

LayoutInflater创建view对象时候使用了简单工厂模式,并通过加入钩子方法,利用抽象工厂模式让coder可以使用自定义的工厂方法来创建view。

LayoutInflater源码解析的更多相关文章

  1. Android LayoutInflater源码解析:你真的能正确使用吗?

    版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 好久没写博客了,最近忙着换工作,没时间写,工作刚定下来.稍后有时间会写一下换工作经历.接下来进入本篇主题,本来没想写LayoutInflater的 ...

  2. android源码解析(十七)-->Activity布局加载流程

    版权声明:本文为博主原创文章,未经博主允许不得转载. 好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与andr ...

  3. Android源码解析——Toast

    简介 Toast是一种向用户快速展示少量信息的视图.当它显示时,它会浮在整个应用层的上面,并且不会获取到焦点.它的设计思想是能够向用户展示些信息,但又能尽量不显得唐突.本篇我们来研读一下Toast的源 ...

  4. 【Android应用开发】EasyDialog 源码解析

    示例源码下载 : http://download.csdn.net/detail/han1202012/9115227 EasyDialog 简介 : -- 作用 : 用于在界面进行一些介绍, 说明; ...

  5. 阿里ARouter使用及源码解析(一)

    在app的开发中,页面之间的相互跳转是最基本常用的功能.在Android中的跳转一般通过显式intent和隐式intent两种方式实现的,而Android的原生跳转方式会存在一些缺点: 显式inten ...

  6. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  7. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  8. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  9. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

随机推荐

  1. theano和keras安装

    最近在学深度学习框架,要用到keras库,keras可以搭建在tensorflow和theano上,我电脑装的是Windows,因此决定在电脑上搭建theano框架 下面回顾我的安装过程: 1.安装a ...

  2. 测试 Zoundry Raven

    安装很方便,看看发布的内容是否好用 但发现从博客上取下来的内容是有问题的,不能正常打开

  3. [iOS Reverse]logify日志追踪,锁定注入口-控制台查看

    前言 logify是theos的一个组件,路径是: /opt/theos/bin/logify.pl 我们还是以微信红包为例子,根据[iOS Hacking]运行时分析cycript得到的入口文件: ...

  4. Challenge–response authentication 挑战(询问)应答机制

    In computer security, challenge–response authentication is a family of protocols in which one party ...

  5. php 加密解密函数封装

    算法一: //加密函数 function lock_url($txt,$key='yang') { $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi ...

  6. python sqlalthemy 总结

    orm 数据状态的预知识 瞬时状态:刚创建的对象还没有被Session持久化.缓存中不存在这个对象的数据并且数据库中没有这个对象对应的数据为瞬时状态这个时候是没有OID. 持久状态:对象经过Sessi ...

  7. js中call、apply、bind的区别

    var Person = { name : 'alice', say : function(txt1,txt2) { console.info(txt1+txt2); console.info(thi ...

  8. python3实现UDP协议的简单服务器和客户端

    利用python中的socket模块中的来实现UDP协议,这里写一个简单的服务器和客户端.为了说明网络编程中UDP的应用,这里就不写图形化了,在两台电脑上分别打开UDP的客户端和服务端就可以了. UD ...

  9. P1040 加分二叉树(树上记忆化搜素)

    这道题很水 但我没做出来……………………………… 我写的时候状态设计错了,设计dp[l][m][r]为从l到r以m为根的值 这样写遍历状态就是n^3的,会TLE. 而且写路径的时候是用结构体写的,这样 ...

  10. STM32 HAL库的定时器中断回调函数跟串口中断回调函数

    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { //添加回调后的程序逻辑 if (htim->Instance == ...