随着android手机的迅速发展以及技术的更新,市场对应用的性能提出了更高的要求,android图形界面是用户直接的感受渠道,因此做好UI界面的设计日益重要。而其实相同的布局效果可以有不同的实现方式。糟糕的布局会使程序加载UI的速度非常慢,从而降低了用户体验满意度,同时也对程序的性能产生比较大的影响,因此优化布局是提高性能的一种重要途径,以下从三方面讲述布局的三重优化。
第一重优化:布局中样式的抽取
布局文件中,系统给我们提供了很多控件的属性,我们可以方便的使用,但有时在同一个布局中甚至不同的布局中会重复出现同一个控件并且其属性设置相似,这时如果我们重复的单个定义相同控件的属性会使布局代码显得庞大冗杂,减低了布局文件的可读性同时开发的效率和程序性能也会受很大的影响,这就需要我们将相同控件的相同属性抽取出来做成样式模板。
举例1:
同一个布局或不同布局文件中常常会用到控件显示文字比如TextView,Button等,而这时候对于控件的大小及显示的文字的大小和颜色等要求又是相同的,这时我们就可以将这些相同的属性抽取出来。如下图布局是一个简单的LinearLayout布局中的三个控件中间一个Button,两边各一个TextView,看代码不难发现他们的尺寸,背景,和字体大小颜色都是相同的,这时我们则可以抽取出来。
将控件相同的样式抽取到values文件夹下的style.xml文件中如下图红色圈住部分:
抽取之后,布局文件中我们只需引入此样式即可,如果其他控件有个别属性不同,则只需在布局文件中重写此属性,系统会自动将布局文件中的属性覆盖抽取出的style属性,如下图:
此时布局则大大简化,效果则完全相同,如下图:
第二重优化:布局文件复用
布局文件的复用思想和样式抽取的思想是相同的,都是将相同的东西抽取出来以达到可以重复利用的目的。当然Android也已经充分考虑到了布局重用的重要性,于是提供了
1.<include>
<include>
举例2:我们应该都知道,目前几乎所有的软件都会有一个头布局,头布局中可以包含界面的标题、返回按钮、以及其它一些操作功能等。那这样的一个头布局,有些软件是使用ActionBar来实现的,但是由于ActionBar的灵活性不太好,因而也有很多软件会选择自己去编写实现。那如果自己去实现的话,由于这个头布局是在所有界面都要使用的,显然我们不可能在每个界面当中都去写一遍这个头布局的代码,因此这种情况下使用
titlebar.xml中的布局非常简单,外层RelativeLayout里面只有两个Button和一个TextView,左边Button用于实现返回功能,右边的Button用于实现完成功能,中间的TextView则可以用于显示当前界面的标题。我们可以来预览一下titlebar的样子,如下图所示:
titlebar的布局写完,接下来就可以对布局文件进行复用了,无论任何界面需要加入titlebar这个功能,只需要在布局文件中引入titlebar.xml就可以了。比如我们的程序当中有一个activity_main.xml文件,现在想要引入titlebar,布局文件只需要这样写:
这样就非常简单,一行include语句就搞定了,而无需重复写titlebar里的布局。
在
这时重新运行一下则是可以正常显示titlebar之外的其他内容的。
除了layout_height之外,我们还可以覆写titlebar中的任何一个layout属性,如layout_gravity、layout_margin等,而非layout属性则无法在
2.
<merge>标签是作为
可以看到,这个界面也是非常简单,外层是一个水平方向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外层的LinearLayout布局删除掉,换用了
第三重优化:仅在需要时才加载布局
有的时候我们会遇到这样的场景,就是某个布局当中的元素非常多,但并不是所有元素都一起显示出来的,而是普通情况下只显示部分常用的元素,而那些不常用的元素只有在用户进行特定操作的情况下才会显示出来。
这里举个大家都非常熟悉的例子,我们在添加联系人的时候其实可以编辑的字段真的非常多,姓名、电话、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所加载的布局不能使用