深入解析Android布局 inflate 过程原理

更新:11-12 现代故事 我要投稿 纠错 投诉

大家好,今天来为大家分享深入解析Android布局 inflate 过程原理的一些知识点,和的问题解析,大家要是都明白,那么可以忽略,如果不太清楚的话可以看看本篇文章,相信很大概率可以解决您的问题,接下来我们就一起来看看吧!

当然还有另一种写法也能达到同样的效果:[java]view plaincopy

查看代码上的代码片段派生到我的代码片段LayoutInflaterlayoutInflater=(LayoutInflater) context

.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

其实第一个是第二个的简单写法,不过Android已经帮我们封装好了。获得LayoutInflater的实例后,可以调用其inflate()方法来加载布局,如下所示:[java]view plaincopy

查看代码上的代码片段派生到我的代码片段layoutInflater.inflate(resourceId, root);

inflate()方法一般接收两个参数。第一个参数是要加载的布局id。第二个参数是指在布局之外嵌套一个父布局。如果不需要,直接传null。这将成功创建布局的实例,然后将其添加到指定位置以显示它。

我们通过一个非常简单的例子来更直观的了解一下LayoutInflater的用法。例如,目前有一个项目,其中MainActivity对应的布局文件名为activity_main.xml。代码如下:

[html]查看明文

查看代码上的代码片段,派生到我的代码片段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。代码如下:

[html]查看明文

查看代码上的代码片段,派生到我的代码片段http://schemas.android.com/apk/res/android"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="按钮"

这个布局文件也很简单,只有一个Button按钮。现在我们要弄清楚如何通过LayoutInflater将button_layout布局添加到主布局文件的LinearLayout中。根据刚刚介绍的用法,修改MainActivity中的代码如下:[java]view plaincopy

查看代码上的代码片段派生到我的代码片段public class MainActivity extends Activity {

私有LinearLayout mainLayout;

@覆盖

protected void onCreate(Bundle savingInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mainLayout=(LinearLayout) findViewById(R.id.main_layout);

LayoutInflaterlayoutInflater=LayoutInflater.from(this);

查看buttonLayout=layoutInflater.inflate(R.layout.button_layout, null);

mainLayout.addView(buttonLayout);

}}

可以看到,首先获取了LayoutInflater的实例,然后调用其inflate()方法加载button_layout布局,最后调用LinearLayout的addView()方法将其添加到LinearLayout中。

现在可以运行程序了,结果如下图:

界面上显示按钮!说明我们确实在LayoutInflater的帮助下成功将button_layout布局添加到了LinearLayout中。当需要动态添加View时,LayoutInflater技术被广泛使用。例如,在ScrollView和ListView中,经常可以看到LayoutInflater。

当然,仅仅介绍如何使用LayoutInflater显然远远不能满足大家的求知欲。要知道它,你还必须知道为什么。接下来我们从源码的角度看一下LayoutInflater是如何工作的。

无论你使用哪种重载的inflate() 方法,最终都会调用到LayoutInflater 的以下代码:

[java]查看plaincopy查看代码上的代码片段派生到我的代码片段public View inflate(XmlPullParser parser, ViewGroup root, boolean AttachToRoot) {

同步(mConstructorArgs){

最终AttributeSet attrs=Xml.asAttributeSet(parser);

mConstructorArgs[0]=mContext;

查看结果=根;

尝试{

int 类型;

while ((type=parser.next()) !=XmlPullParser.START_TAG

类型!=XmlPullParser.END_DOCUMENT) {

}

如果(类型!=XmlPullParser.START_TAG){

抛出新的InflateException(parser.getPositionDescription()

+ ": 未找到开始标记!");

}

最终字符串名称=parser.getName();

if (TAG_MERGE.equals(name)) {

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

throw new InflateException("merge 只能与有效的"

+ "ViewGroup 根和attachToRoot=true");

}

rInflate(解析器,根,属性);

} 别的{

视图temp=createViewFromTag(name, attrs);

ViewGroup.LayoutParams 参数=null;

如果(根!=空){

params=root.generateLayoutParams(attrs);

如果(!attachToRoot){

temp.setLayoutParams(params);

}

}

rInflate(解析器,临时,属性);

如果(根!=null AttachToRoot){

root.addView(temp, params);

}

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

结果=温度;

}

}

} catch (XmlPullParserException e) {

InflateException ex=new InflateException(e.getMessage());

例如initCause(e);

扔前;

} catch (IOException e) {

InflateException ex=新的InflateException(

parser.getPositionDescription()

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

例如initCause(e);

扔前;

}

返回结果;

}

}

从这里我们可以清楚地看到,LayoutInflater实际上是使用Android提供的pull解析方法来解析布局文件的。不熟悉pull解析方法的朋友可以网上搜索一下。教程很多,我就不详细说了。这里注意第23行,这里调用了createViewFromTag()方法,并传入了节点名和参数。看到这个方法名,我们应该能猜到它是用来根据节点名创建View对象的。确实,createView()方法是在createViewFromTag()方法内部调用的,然后使用反射来创建View的实例并返回它。

当然,这里我们只是创建根布局的实例。接下来,在第31 行调用rInflate() 方法来循环访问此根布局的子元素。代码如下:

[java]查看明文

查看代码上的代码片段派生到我的代码片段private void rInflate(XmlPullParser parser, View Parent, Final AttributeSet attrs)

抛出XmlPullParserException,IOException {

最终int 深度=parser.getDepth();

int 类型;

while (((type=parser.next()) !=XmlPullParser.END_TAG ||

parser.getDepth() 深度) 类型!=XmlPullParser.END_DOCUMENT) {

如果(类型!=XmlPullParser.START_TAG){

继续;

}

最终字符串名称=parser.getName();

如果(TAG_REQUEST_FOCUS.equals(名称)){

parseRequestFocus(解析器,父级);

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

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

throw new InflateException("不能是根元素");

}

parseIninclude(解析器,父级,属性);

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

throw new InflateException("必须是根元素");

} 别的{

最终视图视图=createViewFromTag(name, attrs);

最终ViewGroup viewGroup=(ViewGroup) 父级;

最终ViewGroup.LayoutParams params=viewGroup.generateLayoutParams(attrs);

rInflate(解析器,视图,属性);

viewGroup.addView(视图, 参数);

}

}

onFinishInflate();

}

可以看到,第21行也是使用了createViewFromTag()方法创建了View的实例,然后第24行递归调用了rInflate()方法来查找这个View下的子元素。每次递归完成后,这个View都会被添加到父布局中。

这样的话,整个布局文件解析完毕后,就会形成一个完整的DOM结构,最终会返回顶层根布局,inflate()过程也就结束了。

细心的朋友可能会注意到,inflate()方法还有一个接收三个参数的方法重载。结构如下:

[java]查看明文

查看代码上的代码片段派生到我的代码片段膨胀(int资源,ViewGroup根,布尔值attachToRoot)

那么第三个参数attachToRoot是什么意思呢?其实,如果你仔细阅读上面的源码,你应该能够自己分析出答案。这里我先说一下结论。有兴趣的朋友可以再次阅读源码来验证我的结论是否正确。

如果root 为null,attachToRoot 将不起作用,设置任何值都没有意义。如果root不为null并且attachToRoot设置为true,则加载的布局文件的最外层将嵌套另一层根布局。如果root 不为null 并且AttachToRoot 设置为false,则root 参数无效。在不设置attachToRoot参数的情况下,如果root不为null,则attachToRoot参数默认为true。

好了,现在你已经弄清楚了LayoutInflater的工作原理和流程,你应该满意了。前额。仍然觉得这个例子中的按钮看起来有点小,想把它们做得更大?很简单,修改button_layout.xml中的代码如下:

[html]查看plaincopy查看代码上的代码片段派生到我的代码片段http://schemas.android.com/apk/res/android"

android:layout_width="300dp"

android:layout_height="80dp"

android:text="按钮"

这里我们将按钮的宽度更改为300dp,高度更改为80dp。这个够大吗?现在重新运行程序观察效果。啊?为什么按钮还是一样大小,没有任何变化!按钮还不够大吗?可以做大一点吗?还是没用啊!

事实上,无论你如何改变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_layout.xml。很明显,Button 控件当前不存在于任何布局中,因此layout_width 和layout_height 这两个属性当然不起作用。那么如何修改来改变按钮的大小呢?其实解决办法有很多种。最简单的方式就是在Button外面再嵌套一层布局,如下图:

[html]查看明文

查看代码上的代码片段,派生到我的代码片段http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

正如你所看到的,这里我们添加了另一个RelativeLayout。此时Button就存在于RelativeLayout中,layout_width和layout_height属性也发挥了作用。当然,最外面的RelativeLayout的layout_width和layout_height就会失去作用。现在重新运行程序,结果如下图:

好的!按钮终于可以做大了,终于满足了大家的要求。

看到这里,有些朋友心里可能会产生巨大的疑惑。这是不对的!在Activity中指定布局文件时,可以指定最外层布局的大小。 layout_width 和layout_height 都有效。确实,这主要是因为,在setContentView()方法中,Android会自动在布局文件的最外层嵌套一个FrameLayout,因此layout_width和layout_height属性就会产生作用。那么我们确认一下,修改MainActivity中的代码如下:

[java]查看plaincopy查看代码上的代码片段派生到我的代码片段public class MainActivity extends Activity {

私有LinearLayout mainLayout;

@覆盖

protected void onCreate(Bundle savingInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mainLayout=(LinearLayout) findViewById(R.id.main_layout);

ViewParent viewParent=mainLayout.getParent();

Log.d("TAG", "mainLayout的父级是" + viewParent);

}}

用户评论

▼遗忘那段似水年华

我一直很好奇LayoutInflater是怎么把XML布局文件转换到View的?

    有8位网友表示赞同!

迁心

这篇文章会不会详细讲讲Inflate方法内部的工作原理?

    有17位网友表示赞同!

你是梦遥不可及

我对了解如何控制 View 的创建和使用很感兴趣,这个标题听起来很有帮助。

    有10位网友表示赞同!

优雅的叶子

如果对Android开发有深入了解的人读了这篇文章该会有哪些收获呢?

    有11位网友表示赞同!

不忘初心

不知道Inflation的这种机制在性能优化上有没有什么影响?

    有9位网友表示赞同!

花菲

我觉得了解这种原理有助于我们更好地写更精简、高效的代码。

    有6位网友表示赞同!

信仰

我一直在尝试解决一些自定义View的问题,或许这篇文章能给我一些启发。

    有19位网友表示赞同!

眉黛如画

学习Android开发的过程中,View的生命周期和LayoutInflater是比较难懂的部分。

    有20位网友表示赞同!

冷青裳

看了这个标题就想测试一下自己对 LayoutInflater 的理解深度。

    有11位网友表示赞同!

笑傲苍穹

不知道有没有人会分享一些实际应用案例来加深我们的理解?

    有16位网友表示赞同!

安之若素

这篇文章能帮助我更全面地掌握Android UI开发的原理吗?

    有17位网友表示赞同!

軨倾词

如果要优化代码性能,了解Inflate机制有哪些技巧呢?

    有9位网友表示赞同!

旧爱剩女

这个标题让我想起以前学习iOS的时候,他们的View加载方式类似吗?

    有8位网友表示赞同!

该用户已上天

这篇文章会涉及到ResourceLoader和Cache机制吗?我很想深入了解这些细节。

    有16位网友表示赞同!

我家的爱豆是怪比i

是不是每个Android项目都需要用到LayoutInflater呢? 还是有其他的替代方案?

    有18位网友表示赞同!

残花为谁悲丶

我想看看这篇文章有没有对比不同版本Android中LayoutInflater的变化?

    有8位网友表示赞同!

放肆丶小侽人

如果我们自定义View,应该怎么和LayoutInflater配合使用呢?

    有20位网友表示赞同!

命运不堪浮华

对于初学者来说,这篇文章会不会太深入了?有没有一些入门级的讲解资源?

    有15位网友表示赞同!

孤独症

我发现越来越多的开发关注Android性能优化,这样深入的文章很有价值。

    有15位网友表示赞同!

我就是这样一个人

学习Android的过程中遇到了一些问题,希望这篇分析能解答我的疑虑。

    有15位网友表示赞同!

【深入解析Android布局 inflate 过程原理】相关文章:

1.动物故事精选:寓教于乐的儿童故事宝库

2.《寓教于乐:精选动物故事助力儿童成长》

3.探索动物旅行的奇幻冒险:专为儿童打造的童话故事

4.《趣味动物刷牙小故事》

5.探索坚韧之旅:小蜗牛的勇敢冒险

6.传统风味烤小猪,美食探索之旅

7.探索奇幻故事:大熊的精彩篇章

8.狮子与猫咪的奇妙邂逅:一场跨界的友谊故事

9.揭秘情感的力量:如何影响我们的生活与决策

10.跨越两岸:探索彼此的独特世界

上一篇:2003远程桌面端口修改教程:轻松学习如何调整远程桌面端口 下一篇:光碟种类及擦除与刻录技巧详析