当前位置:首页 > 嵌入式培训 > 嵌入式学习 > 讲师博文 > 性能优化之布局文件优化

性能优化之布局文件优化 时间:2018-09-26      来源:未知

随着android手机的迅速发展以及技术的更新,市场对应用的性能提出了更高的要求,android图形界面是用户直接的感受渠道,因此做好UI界面的设计日益重要。而其实相同的布局效果可以有不同的实现方式。糟糕的布局会使程序加载UI的速度非常慢,从而降低了用户体验满意度,同时也对程序的性能产生比较大的影响,因此优化布局是提高性能的一种重要途径,以下从三方面讲述布局的三重优化。

第一重优化:布局中样式的抽取

布局文件中,系统给我们提供了很多控件的属性,我们可以方便的使用,但有时在同一个布局中甚至不同的布局中会重复出现同一个控件并且其属性设置相似,这时如果我们重复的单个定义相同控件的属性会使布局代码显得庞大冗杂,减低了布局文件的可读性同时开发的效率和程序性能也会受很大的影响,这就需要我们将相同控件的相同属性抽取出来做成样式模板。

举例1:

同一个布局或不同布局文件中常常会用到控件显示文字比如TextView,Button等,而这时候对于控件的大小及显示的文字的大小和颜色等要求又是相同的,这时我们就可以将这些相同的属性抽取出来。如下图布局是一个简单的LinearLayout布局中的三个控件中间一个Button,两边各一个TextView,看代码不难发现他们的尺寸,背景,和字体大小颜色都是相同的,这时我们则可以抽取出来。

将控件相同的样式抽取到values文件夹下的style.xml文件中如下图红色圈住部分:

抽取之后,布局文件中我们只需引入此样式即可,如果其他控件有个别属性不同,则只需在布局文件中重写此属性,系统会自动将布局文件中的属性覆盖抽取出的style属性,如下图:

此时布局则大大简化,效果则完全相同,如下图:

第二重优化:布局文件复用

布局文件的复用思想和样式抽取的思想是相同的,都是将相同的东西抽取出来以达到可以重复利用的目的。当然Android也已经充分考虑到了布局重用的重要性,于是提供了这两个非常有用的标签,让我们更方便的实现代码复用。

1.<include>

<include>标签可以允许在一个布局当中引入另外一个布局,那么比如说我们程序的所有界面都有一个公共的部分,这个时候好的做法就是将这个公共的部分提取到一个独立的布局文件当中,然后在每个界面的布局文件当中来引用这个公共的布局。

举例2:我们应该都知道,目前几乎所有的软件都会有一个头布局,头布局中可以包含界面的标题、返回按钮、以及其它一些操作功能等。那这样的一个头布局,有些软件是使用ActionBar来实现的,但是由于ActionBar的灵活性不太好,因而也有很多软件会选择自己去编写实现。那如果自己去实现的话,由于这个头布局是在所有界面都要使用的,显然我们不可能在每个界面当中都去写一遍这个头布局的代码,因此这种情况下使用标签就非常合适了。这里为了给大家演示一下,我就编写一个非常简单的头布局,在res/layout文件夹中新建titlebar.xml作为头布局,代码如下所示:

titlebar.xml中的布局非常简单,外层RelativeLayout里面只有两个Button和一个TextView,左边Button用于实现返回功能,右边的Button用于实现完成功能,中间的TextView则可以用于显示当前界面的标题。我们可以来预览一下titlebar的样子,如下图所示:

titlebar的布局写完,接下来就可以对布局文件进行复用了,无论任何界面需要加入titlebar这个功能,只需要在布局文件中引入titlebar.xml就可以了。比如我们的程序当中有一个activity_main.xml文件,现在想要引入titlebar,布局文件只需要这样写:

这样就非常简单,一行include语句就搞定了,而无需重复写titlebar里的布局。标签当中可以指定一个layout属性,我们在这个layout属性中填写需要引入的布局名就可以了。而且使用这种引入的方式,以后如果titlebar的界面有所变更,我们只需要修改titlebar.xml这一个文件就可以了,而不是所有界面一个个地去修改。但需要注意的是虽然titlebar是成功引入了,但是我们activity_main.xml中本来的界面全部都不见了!出现这个问题的原因是因为titlebar的外层布局是一个宽高都是match_parent的RelativeLayout,它会将整个布局都填充满,因而我们原本的布局也就看不见了。那既然问题的原因清楚了,相信你立刻就想到应该怎么修改了,将RelativeLayout的layout_height属性修改成wrap_content不就可以了嘛。没错,这样修改当然是没问题的,不过这种修改方式会让所有引用titlebar的界面都受到影响,而如何你只希望让activity_main.xml这一个界面受影响的话,那么可以使用覆写属性的方式。

标签当中,我们是可以覆写所有layout属性的,即include中指定的layout属性将会覆盖掉titlebar中指定的layout属性。因此,这里我们希望将titlebar的高度设置成wrap_content,就可以这样写:

这时重新运行一下则是可以正常显示titlebar之外的其他内容的。

除了layout_height之外,我们还可以覆写titlebar中的任何一个layout属性,如layout_gravity、layout_margin等,而非layout属性则无法在标签当中进行覆写。另外需要注意的是,如果我们想要在标签当中覆写layout属性,必须要将layout_width和layout_height这两个属性也进行覆写,否则覆写效果将不会生效。

2.<merge>

<merge>标签是作为标签的一种辅助扩展来使用的,他的主要作用是为了防止在引用布局文件时产生多余的布局嵌套。Android去解析和展示一个布局是需要消耗时间的,布局嵌套越多解析起来越是耗时,性能也就越差,因此我们在编写布局文件时应该让嵌套的层数越少越好。

有优点,但是它也存在着一个不好的地方,就是可能会导致产生多余的布局嵌套。这里还是通过举例的方式跟大家说明一下,比如说我们需要编写一个确定取消按钮的公共布局,这样任何一个界面需要确定和取消功能时就不用再单独编写了,新建ok_cancel_layout.xml,代码如下所示:

可以看到,这个界面也是非常简单,外层是一个水平方向LinearLayout,LinearLayout中包含了两个按钮,一个用于实现确定功能,一个用于实现取消功能。现在我们可以来预览一下这个界面,如下图所示:

然后我们有一个profile.xml的界面需要编辑一些内容,那么这里就可以将ok_cancel_layout这个布局引入到profile.xml界面当中,如下所示:

运行效果如图:

这个看上去并没有什么不对,可是在你毫无察觉的情况下,目profile.xml这个界面当中其实已经存在着多余的布局嵌套了!感觉还没写几行代码呢,怎么这就已经有多余的布局嵌套了?我们可以通过View Hierarchy工具来查看一下,如下图所示:

可以看到,外层首先是一个FrameLayout,然后FrameLayout中包含的是一个LinearLayout,这个就是我们在profile.xml中定义的外层布局。接下来的部分就有问题了,在外层的LinearLayout当中包含了两个元素,一个是EditText,另一个又是一个LinearLayout,然后在这个内部的LinearLayout当中才包含了确定和取消这两个按钮。这个内部的LinearLayout就是一个多余的布局嵌套,实际上并不需要这样一层,让两个按钮直接包含在外部的LinearLayout当中就可以了。而这个多余的布局嵌套其实就是由于布局引入所导致的,因为我们在ok_cancel_layout.xml中也定义了一个LinearLayout。那么应该怎样优化掉这个问题呢?当然就是使用标签来完成了,修改ok_cancel_layout.xml中的代码,如下所示:

可以看到,这里我们将ok_cancel_layout外层的LinearLayout布局删除掉,换用了标签,这就表示当有任何一个地方去include这个布局时,会将标签内包含的内容直接填充到include的位置,不会再添加任何额外的布局结构。好的,的用法就是这么简单,现在重新运行一下程序,你会看到界面没有任何改变,然后我们再通过View Hierarchy工具来查看一下当前的View结构,如下图所示:

第三重优化:仅在需要时才加载布局

有的时候我们会遇到这样的场景,就是某个布局当中的元素非常多,但并不是所有元素都一起显示出来的,而是普通情况下只显示部分常用的元素,而那些不常用的元素只有在用户进行特定操作的情况下才会显示出来。

这里举个大家都非常熟悉的例子,我们在添加联系人的时候其实可以编辑的字段真的非常多,姓名、电话、email、传真、住址、昵称等等等等,但其实基本上大家常用的就是填一个姓名,填一个电话而已。那么将这么多繁杂的字段都一起显示在界面上其实并不是一种很好的做法,因为大多数人都是用不到这些字段的。比较聪明的做法就是把常用的姓名和电话显示在界面上,然后给用户提供一个添加更多字段的选项,当用户真的有需要去添加其它信息的时候,我们才将另外的元素显示到界面上。

说到实现这样一个功能,我相信大多数人的第一反应就是将不常用的元素使用INVISIBLE或者GONE进行隐藏,然后当用户需要使用这些元素的时候再把它们置成VISIBLE显示出来。使用这种方式肯定可以实现功能的,但是性能方面就表现得一般了,因为即使是将元素进行隐藏,它们其实还是在布局当中的,每个元素还拥有着自己的宽、高、背景等等属性,解析布局的时候也会将这些隐藏的元素一一解析出来。

那么我们如何才能让这些不常用的元素仅在需要时才去加载呢?Android为此提供了一种非常轻量级的控件,ViewStub。ViewStub虽说也是View的一种,但是它没有大小,没有绘制功能,也不参与布局,资源消耗非常低,将它放置在布局当中基本可以认为是完全不会影响性能的。

下面我们就来学习一下如何使用ViewStub来完成仅在需要时才去加载布局的功能,目前profile.xml中只有一个EditText用于编辑信息,那么比如说我们还有另外三个不太常用的EditText,就可以将它们定义在另外一个布局文件当中。新建profile_extra.xml文件,代码如下所示:

运行效果如图:

目前profile_extra.xml是一个独立的布局,和profile.xml这个布局文件是完全没有关系的。接下来我们修改profile.xml文件中的代码,如下所示:

可以看到LinearLayout布局中我们新增了一个More Button,这个按钮就是用于去加载那些不常用的元素的,然后在Button的下面定义了一个ViewStub。在ViewStub控件中,我们先是通过id属性给它指定了一个唯一标识,又通过layout属性将profile_extra布局传入进来,接着给ViewStub指定了一个宽高。注意,虽然ViewStub是不占用任何空间的,但是每个布局都必须要指定layout_width和layout_height属性,否则运行就会报错。

接着修改ProfileActivity中的代码,在Activity中添加More Button的点击事件,并在点击事件中进行如下逻辑处理:

当点击More Button之后我们首先会调用findViewById()方法将ViewStub的实例获取到,拿到ViewStub的实例之后就很简单了,调用inflate()方法或者setVisibility(View.VISIBLE)都可以将隐藏的布局给加载出来,而加载的这个布局就是刚才在XML当中配置的profile_extra布局。

调用inflate()方法之后会将加载出来的布局进行返回,之后我们就可以对这个布局进行任意的操作了,再次隐藏显示,或者获取子元素的实例等。注意这里我对ViewStub的实例进行了一个非空判断,这是因为ViewStub在XML中定义的id只在一开始有效,一旦ViewStub中指定的布局加载之后,这个id也就失败了,那么此时findViewById()得到的值也会是空。

运行结果如图所示:

可以看到,界面上只有一个More按钮,ViewStub是完全不占用任何空间的。然后点击一下More按钮,新的界面如下所示:

运行结果正常,profile_extra.xml中定义的布局已经加载出来了,而且显示的位置正是ViewStub控件定义的位置,说明我们已经成功是实现了ViewStub的功能。但是需要注意的是ViewStub所加载的布局不能使用标签,也就是说包含ViewStub控件的布局在进行复用时会导致加载出来的布局存在着多余的嵌套结构,但是对于含有较多隐藏子布局的布局文件来说,使用ViewStub还是一种相当不错的选择的,即使增加了一层无用的布局结构,仍然还是利大于弊,具体如何去选择就要具体情况具体分析了。对于性能的优化需要考虑很多的方面,这里只是简单的介绍了性能优化中的布局优化,作为一个开发者还需要有的放矢的选择和权衡各种方法对应用性能的影响,从而开发出性能优越,客户满意度高的应用。

上一篇:二维数组与行指针

下一篇:Node.js介绍和环境的安装方式

热点文章推荐
华清学员就业榜单
高薪学员经验分享
热点新闻推荐
前台专线:010-82525158 企业培训洽谈专线:010-82525379 院校合作洽谈专线:010-82525379 Copyright © 2004-2022 北京华清远见科技集团有限公司 版权所有 ,京ICP备16055225号-5京公海网安备11010802025203号

回到顶部