前言

inflater.inflate(R.layout.layout_inflate_test,null);

inflater.inflate(R.layout.layout_inflate_test, root,false);

inflater.inflate(R.layout.layout_inflate_test, root,true);

看到上面这几个方法是不是非常眼熟,基本上做过Android开发的人都会调用过inflate方法,可是你真的了解inflate方法吗?各个参数都是什么含义?传递不同参数会产生什么效果?以前的观点是钥匙就是用来开锁的,椅子就是用来坐的,能用不就行了,管它原理是什么。这种观点对于一个新手还好说,刨根问底确实有点难度,但是随着时间的推移,你总会不断遇到一些相同的问题。如果你不早点把原理搞清楚,那么你就像路过没有灯光的胡同,会在相同的地方跌倒一次又一次。扯远了哈,其实网上对inflate方法太多的总结和分析,我在这里主要是自己记录总结,当然能帮到有需要的人更好。

其实博主我,之前没写这篇博客的时候,只会一直用,然后都不知道LayoutInflater的加载原理,每次直接

LayoutInflater.from(context).inflate(R.layout.activity_test, root, false);

//不行就这样,反正有一种能实现我要的效果

LayoutInflater.from(context).inflate(R.layout.activity_test, null);

反正总有一种方式适合我。

上面摘录自第三篇博客,我深有共鸣。我以前也是这样,虽然闭着眼睛,凭着经验也可以迈过一些坑。但是如果你是一个有追求的想让自己的title加上高级两个字的工程师,你就要去看源码,去了解有疑问的地方的原理,不然你去大厂面试的时候深深体会到书到用时方恨少,胸中无墨,哑口无言的真正含义。

分析

首先

放源码之前先要知道inflate方法是干嘛的,看返回是一个View,就知道这个方法是要根据布局id把这个布局加载成一个View并返回的。

源码分析

/**

* ...

*/

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {

return inflate(resource, root, root != null);

}

/**

* ...

*/

public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {

return inflate(parser, root, root != null);

}

/**

* Inflate a new view hierarchy from the specified xml resource. Throws

* {@link InflateException} if there is an error.

*

* @param resource ID for an XML layout resource to load (e.g.,

* R.layout.main_page)

* @param root Optional view to be the parent of the generated hierarchy (if

* attachToRoot is true), or else simply an object that

* provides a set of LayoutParams values for root of the returned

* hierarchy (if attachToRoot is false.)

* @param attachToRoot Whether the inflated hierarchy should be attached to

* the root parameter? If false, root is only used to create the

* correct subclass of LayoutParams for the root view in the XML.

* @return The root View of the inflated hierarchy. If root was supplied and

* attachToRoot is true, this is root; otherwise it is the root of

* the inflated XML file.

*/

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {

final Resources res = getContext().getResources();

if (DEBUG) {

Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("

+ Integer.toHexString(resource) + ")");

}

final XmlResourceParser parser = res.getLayout(resource);

try {

return inflate(parser, root, attachToRoot);

} finally {

parser.close();

}

}

/**

* Inflate a new view hierarchy from the specified XML node. Throws

* {@link InflateException} if there is an error.

*

* Important   For performance

* reasons, view inflation relies heavily on pre-processing of XML files

* that is done at build time. Therefore, it is not currently possible to

* use LayoutInflater with an XmlPullParser over a plain XML file at runtime.

*

* @param parser XML dom node containing the description of the view

* hierarchy.

* @param root Optional view to be the parent of the generated hierarchy (if

* attachToRoot is true), or else simply an object that

* provides a set of LayoutParams values for root of the returned

* hierarchy (if attachToRoot is false.)

* @param attachToRoot Whether the inflated hierarchy should be attached to

* the root parameter? If false, root is only used to create the

* correct subclass of LayoutParams for the root view in the XML.

* @return The root View of the inflated hierarchy. If root was supplied and

* attachToRoot is true, this is root; otherwise it is the root of

* the inflated XML file.

*/

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;

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("**************************");

}

if (TAG_MERGE.equals(name)) {

if (root == null || !attachToRoot) {

throw new InflateException(" 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) {

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)

temp.setLayoutParams(params);

}

}

if (DEBUG) {

System.out.println("-----> start inflating children");

}

// Inflate all children under temp against its context.

rInflateChildren(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) {

final InflateException ie = new InflateException(e.getMessage(), e);

ie.setStackTrace(EMPTY_STACK_TRACE);

throw ie;

} catch (Exception e) {

final InflateException ie = new InflateException(parser.getPositionDescription()

+ ": " + e.getMessage(), e);

ie.setStackTrace(EMPTY_STACK_TRACE);

throw ie;

} finally {

// Don't retain static reference on context.

mConstructorArgs[0] = lastContext;

mConstructorArgs[1] = null;

Trace.traceEnd(Trace.TRACE_TAG_VIEW);

}

return result;

}

}

源码有点长,不要被吓到,下面一一拆解

看源码一共有四个inflate方法,这四个又分为两类:

首参数为布局id,@LayoutRes int resource ,也是我们经常用的;

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)

首参数为Parser,XmlPullParser parse ,我开发中好像没有用到过。

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)

源码中inflate(R.layout.layout_inflate_test,null)其实调用的是inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot),inflate(XmlPullParser parser, @Nullable ViewGroup root)调用的是inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)。

final XmlResourceParser parser = res.getLayout(resource);

try {

return inflate(parser, root, attachToRoot);

} finally {

parser.close();

}

第一类其实调用的也是第四个方法。那就着重看一下第四个方法inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)。

开始分析:

final Context inflaterContext = mContext;

final AttributeSet attrs = Xml.asAttributeSet(parser);

Context lastContext = (Context) mConstructorArgs[0];

mConstructorArgs[0] = inflaterContext;

View result = root;

看开始的几句代码,先不管前面的那几句定义,最后一句话View result = root;那么这个result基本就是作为返回值了,看这个方法最后return result;好吧果然是的。result = root,也就是将第二个参数ViewGroup root返回了,但是使用的inflate方法的时候我们有可能传递的是null也有可能不是null,继续往下看

// Look for the root node.

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 (TAG_MERGE.equals(name)) {

if (root == null || !attachToRoot) {

throw new InflateException(" can be used only with a valid "

+ "ViewGroup root and attachToRoot=true");

}

rInflate(parser, root, inflaterContext, attrs, false);

}

如果根节点的标签是merge,如果root为null或者attachToRoot为false会直接抛异常,也就是当根标签为merge的时候必须使用inflater.inflate(R.layout.layout_inflate_test, root,true);这种形式,不然会报错,你可以自己试验一下。实验结果:

329DC4C2-A7AF-4B70-807C.png

继续往下看:rInflate(parser, root, inflaterContext, attrs, false);

/**

* Recursive method used to descend down the xml hierarchy and instantiate

* views, instantiate their children, and then call onFinishInflate().

*

* Note: Default visibility so the BridgeInflater can

* override it.

*/

void rInflate(XmlPullParser parser, View parent, Context context,

AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {

final int depth = parser.getDepth();

int type;

boolean pendingRequestFocus = false;

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)) {

pendingRequestFocus = true;

consumeChildElements(parser);

} else if (TAG_TAG.equals(name)) {

parseViewTag(parser, parent, attrs);

} else if (TAG_INCLUDE.equals(name)) {

if (parser.getDepth() == 0) {

throw new InflateException(" cannot be the root element");

}

parseInclude(parser, context, parent, attrs);

} else if (TAG_MERGE.equals(name)) {

throw new InflateException(" must be the root element");

} else {

final View view = createViewFromTag(parent, name, context, attrs);

final ViewGroup viewGroup = (ViewGroup) parent;

final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);

rInflateChildren(parser, view, attrs, true);

viewGroup.addView(view, params);

}

}

if (pendingRequestFocus) {

parent.restoreDefaultFocus();

}

if (finishInflate) {

parent.onFinishInflate();

}

}

这个方法的意思是:递归方法进入xml层次结构并实例化视图,实例化它们的子项,然后调用onFinishInflate()。这个方法的内容可以先不看,知道它的作用就行了,就是把这个布局里面的各个子项实例化。举个例子一个完整的快递肯定是大盒子包小盒子再包,有的包了好几层最后才是你的商品。你想要的肯定不是最外层的那个空盒子,你需要的是一个完整的快递。这个方法就是用来把一个或者好几个商品用纸盒子一层层包起来组成一个可以运输的快递的,上面的root就是最外面的那个盒子。

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) {

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)

temp.setLayoutParams(params);

}

}

if (DEBUG) {

System.out.println("-----> start inflating children");

}

// Inflate all children under temp against its context.

rInflateChildren(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;

}

}

刚才哪个merge是特殊情况,一般常见的是else里面的情况

// Temp is the root view that was found in the xml

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

生成一个root view,也就是根据根目录的标签生成的view。这里有个需要注意的地方,最后一个参数attrs,也就是说这个根视图view的一些属性还是会被添加上去例如背景颜色等属性

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)

temp.setLayoutParams(params);

}

}

考点来了,如果root != null,创建LayoutParams,params = root.generateLayoutParams(attrs);这个参数attrs来自上面的final AttributeSet attrs = Xml.asAttributeSet(parser);Xml.asAttributeSet(parser)这句代码我也不是很懂,但是有时候可以通过具体现象或返回值推算某一句代码的作用。它返回一个AttributeSet,AttributeSet是view的布局属性集合,所以这里的作用就是把我们传入的布局的属性拿到。然后后面根据这些属性创建LayoutParams。看下面

/**

* Returns a new set of layout parameters based on the supplied attributes set.

* 根据提供的属性集返回一个新的LayoutParams

* @param attrs the attributes to build the layout parameters from

*

* @return an instance of {@link android.view.ViewGroup.LayoutParams} or one

* of its descendants

*/

public LayoutParams generateLayoutParams(AttributeSet attrs) {

return new LayoutParams(getContext(), attrs);

}

...

/**

* Creates a new set of layout parameters. The values are extracted from

* the supplied attributes set and context. The XML attributes mapped

* to this set of layout parameters are:

*

*

*

layout_width: the width, either an exact value,

* {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by

* {@link #MATCH_PARENT} in API Level 8)

*

layout_height: the height, either an exact value,

* {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by

* {@link #MATCH_PARENT} in API Level 8)

*

*

* @param c the application environment

* @param attrs the set of attributes from which to extract the layout

* parameters' values

*/

public LayoutParams(Context c, AttributeSet attrs) {

TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);

setBaseAttributes(a,

R.styleable.ViewGroup_Layout_layout_width,

R.styleable.ViewGroup_Layout_layout_height);

a.recycle();

}

现在有了LayoutParams,如果attachToRoot为false时执行temp.setLayoutParams(params);将上面得到的LayoutParams给temp--也就是我们上面根据布局根目录标签创建的的那个View设置上。布局根目录一般都是LinearLayout,RelativeLayout等这些ViewGroup,当然也可以是view

再往下看

if (DEBUG) {

System.out.println("-----> start inflating children");

}

// Inflate all children under temp against its context.

rInflateChildren(parser, temp, attrs, true);

if (DEBUG) {

System.out.println("-----> done inflating children");

}

/**

* Recursive method used to inflate internal (non-root) children. This

* method calls through to {@link #rInflate} using the parent context as

* the inflation context.

* Note: Default visibility so the BridgeInflater can

* call it.

*/

final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,

boolean finishInflate) throws XmlPullParserException, IOException {

rInflate(parser, parent, parent.getContext(), attrs, finishInflate);

}

跟刚才哪个merge标签的情况一样打包快递,把子布局子view都一层层组装起来,装到temp里。继续

// 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);

}

又到考点了,假如root不为空,并且attachToRoot为true,那么root就把生成的temp装到自己里面addView,后面还有参数params。temp会被添加到root的最后,并且params设置给temp。

// Decide whether to return the root that was passed in or the

// top view found in xml.

if (root == null || !attachToRoot) {

result = temp;

}

最后一个考点,如果root为空,那么attachToRoot就不用看了,attachToRoot为true或者false都没有意义, 直接result = temp。注意这里temp是没有被设置刚才的LayoutParams的,而LayoutParams是用来设置位置、高、宽等信息,也就意味着temp的这些属性是全新的。

结论

但是使用的时候我们更关心的是各个参数传递给我们带来的影响和效果。那么通过看源码我们得到什么样的结论呢?

先把郭神的结论贴出来:

如果root为null,attachToRoot将失去作用,设置任何值都没有意义。

如果root不为null,attachToRoot设为true,则会给加载的布局文件的指定一个父布局,即root。

如果root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效。

在不设置attachToRoot参数的情况下,如果root不为null,attachToRoot参数默认为true。

关于上面还有一些补充说明,如果root不为null,布局文件最外层的layout关于LayoutParams设置的属性和其他属性都会被保留下来,attachToRoot设为true,则会给加载的布局文件的指定一个父布局,我们不需要自己在addView,否则会报错;attachToRoot设为false,需要我们自己addView,root为null时,被加载的布局LayoutParams的属性会被改变,但是其它属性例如背景颜色什么的会被保留。

参考链接

android inflate 参数,Android inflate方法总结相关推荐

  1. android final参数,Android应用开发之Android Jetpack-Navigation 使用中参数的传递方法

    本文将带你了解Android应用开发之Android Jetpack-Navigation 使用中参数的传递方法,希望本文对大家学Android有所帮助. 由于使用了Navigation,导致Frag ...

  2. android onpagescrolled 参数,Android

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? 背景 ViewPager 在应用中使用较为广泛,诸如页面轮播图,App引导页,大图预览等.其中指示器页比较重要 它用于提 ...

  3. android 多类型参数,Android的数据绑定-类型参数T具有不兼容的上限:ViewDataBinding和MainActivity...

    我正在使用Android Studio 2.0 Preview 4.我正在使用Android SDK工具25 rc1. 无论我清理/重建项目多少次,此错误仍然存在. File-> Invalid ...

  4. android asynctask 参数,Android AsyncTask 详细解析

    结构 继承关系 public abstract class AsyncTask extends Object java.lang.Object android.os.AsyncTask 类概述 Asy ...

  5. android datepicker 参数,android常用组件之DatePicker和TimePicker

    在android中,DatePicker组件和TimePicker组件分别是日期选择器和时间选择器. 该实例中,当点击日期选择器按钮后,弹出日期选择框,用户选择日期后andoid会将日期显示到Text ...

  6. android registerreceiver() 参数,android – 如何使用registerReceiver方法?

    我想使用动态注册的BroadcastReceiver有一个Activity的引用,所以它可以修改其UI.我使用Context.registerReceiver()方法,但接收器的onReceive() ...

  7. android invoke 参数,android 使用反射獲取MediaPlayer的Invoke方法

    最近有需求需要使用MediaPlayer的invoke接口去實現某些功能, 但是invoke接口是隱藏的, 沒有在sdk中開放出來. 所以使用反射的方法來獲取invoke接口, 但在實現的過程中出現一 ...

  8. android gravity参数,android - 如何以编程方式设置layout_gravity?

    android - 如何以编程方式设置layout_gravity? 我的问题很简单, 如何以编程方式设置我的按钮layout_gravity? 我在互联网上发现了这个,但它只是抛出了一个Nullpo ...

  9. android onitemclicklistener 参数,android – OnItemClickListener从模型中获取数据

    我是Android开发的新手,我正在尝试构建一个ListView,它使用gson从Web服务获取数据.我有一个模型类,一个列表类,一个适配器类和活动类. 该列表工作正常,它获得了数据,现在我想将OnI ...

  10. android getevent参数,android getevent、sendevent、input keyevent 使用说明

    设备节点: dev 字符设备节点: shell@android:/dev/input $ ll crw-rw---- root input 13, 64 2013-11-28 17:23 event0 ...

最新文章

  1. 找出两个字符串中最大子字符串,如abractyeyt,dgdsaeactyey的最大子串为actyet
  2. 烧钱大战数以亿计 无人驾驶无法突破局限?
  3. UNIX中的文件和目录
  4. 4.11 一维到三维推广-深度学习第四课《卷积神经网络》-Stanford吴恩达教授
  5. Jython 安装使用
  6. Python中MySQLdb的各种常见用法
  7. charts漏斗图表_用echarts写的转换率图表(漏斗图 + 象形柱图)
  8. 网站实用性是这样建出来的
  9. java应届生面试收集总结
  10. 7-20 表达式转换(中缀转后缀)
  11. 二进制、八进制、十进制、十六进制的前缀和后缀
  12. MATLAB 神经网络模板
  13. 马云雕像事件谁在背后操刀?
  14. 走进“开源SDR实验室” 一起玩转GNU Radio:生成噪声信号
  15. Keepalived监测脚本不执行
  16. 【MD】高等数学常用符号
  17. 安卓手机投屏软件_手机投屏软件,居然支持所有网址......
  18. FL Studio 2023中文高级版水果编曲软件下载
  19. 黑苹果下耳机杂音,爆音,人声小问题的解决方案
  20. 河北职称计算机 英语考试报名,河北省职称计算机考试

热门文章

  1. write-through与write-back的区别
  2. iOS开发:Protocol协议以及委托代理传值
  3. java 表格tr td_table、tr、td表格的行、单元格等属性说明
  4. PotPlayer不支持S/W HEVC(H.265)解码怎么办?一招解决所有的不支持解码
  5. 这部关于 AI 的纪录片,还是值得一看的
  6. 上海泛微面经(从Java开发到项目实施岗)
  7. 考HCIE大概需要多少钱?
  8. 深度学习(一、入门)
  9. 美团后台开发暑期实习面经(一面+二面)已offer
  10. 3dMax模型数据转cesium Gltf模型