Android系统中已经提供了很是多好用的控件,这让咱们在编写布局的时候能够很轻松。可是有些时候咱们可能须要反复利用某个已经写好的布局,若是你老是使用复制粘贴的方式来进行布局重用,这显然是一种很笨的作法。而Android固然也已经充分考虑到了布局重用的重要性,因而提供了<include>和<merge>这两个很是有用的标签,下面咱们就来逐个学习一下。android
<include>标签能够容许在一个布局当中引入另一个布局,那么好比说咱们程序的全部界面都有一个公共的部分,这个时候最好的作法就是将这个公共的部分提取到一个独立的布局文件当中,而后在每一个界面的布局文件当中来引用这个公共的布局。工具
这里举个例子吧,咱们应该都知道,目前几乎全部的软件都会有一个头布局,头布局中能够包含界面的标题、返回按钮、以及其它一些操做功能等。那这样的一个头布局,有些软件是使用ActionBar来实现的,可是因为ActionBar的灵活性不太好,于是也有不少软件会选择本身去编写实现。那若是本身去实现的话,因为这个头布局是在全部界面都要使用的,显然咱们不可能在每一个界面当中都去写一遍这个头布局的代码,所以这种状况下使用<include>标签就很是合适了。这里为了给你们演示一下,我就编写一个很是简单的头布局,在res/layout文件夹中新建titlebar.xml做为头布局,代码以下所示:布局
能够看到,titlebar.xml中的布局很是简单,外层是一个RelativeLayout,里面只有两个Button和一个TextView,左边的Button用于实现返回功能,右边的Button用于实现完成功能,中间的TextView则能够用于显示当前界面的标题。咱们能够来预览一下titlebar的样子,以下图所示:性能
好的,那titlebar做为一个独立的布局如今咱们已经编写完了,接下来的工做就很是简单了,不管任何界面须要加入titlebar这个功能,只须要在布局文件中引入titlebar.xml就能够了。那么好比说咱们的程序当中有一个activity_main.xml文件,如今想要引入titlebar只须要这样写:学习
很是简单吧,一行include语句就能够搞定了。<include>标签当中能够指定一个layout属性,咱们在这个layout属性中填写须要引入的布局名就能够了。并且使用这种引入的方式,之后若是titlebar的界面有所变动,咱们只须要修改titlebar.xml这一个文件就能够了,而不是全部界面一个个地去修改。优化
等等!如今若是你运行一下程序会发现出大问题了,虽然titlebar是成功引入了,可是咱们activity_main.xml中原本的界面所有都不见了!出现这个问题是缘由是由于titlebar的最外层布局是一个宽高都是match_parent的RelativeLayout,它会将整个布局都填充满,于是咱们本来的布局也就看不见了。那既然问题的缘由清楚了,相信你马上就想到应该怎么修改了,将RelativeLayout的layout_height属性修改为wrap_content不就能够了嘛。没错,这样修改固然是没问题的,不过这种修改方式会让全部引用titlebar的界面都受到影响,而如何你只但愿让activity_main.xml这一个界面受影响的话,那么可使用覆写<include>属性的方式。spa
在<include>标签当中,咱们是能够覆写全部layout属性的,即include中指定的layout属性将会覆盖掉titlebar中指定的layout属性。所以,这里咱们但愿将titlebar的高度设置成wrap_content,就能够这样写:.net
如今从新运行一下程序应该就能够一切正常了,以下图所示:xml
除了layout_height以外,咱们还能够覆写titlebar中的任何一个layout属性,如layout_gravity、layout_margin等,而非layout属性则没法在<include>标签当中进行覆写。另外须要注意的是,若是咱们想要在<include>标签当中覆写layout属性,必需要将layout_width和layout_height这两个属性也进行覆写,不然覆写效果将不会生效。blog
<merge>标签是做为<include>标签的一种辅助扩展来使用的,它的主要做用是为了防止在引用布局文件时产生多余的布局嵌套。你们都知道,Android去解析和展现一个布局是须要消耗时间的,布局嵌套的越多,那么解析起来就越耗时,性能也就越差,所以咱们在编写布局文件时应该让嵌套的层数越少越好。
在上面咱们讲解<include>标签的用法时主要介绍了它优势,可是它也存在着一个很差的地方,就是可能会致使产生多余的布局嵌套。这里仍是经过举例的方式跟你们说明一下,好比说咱们须要编写一个肯定取消按钮的公共布局,这样任何一个界面须要肯定和取消功能时就不用再单独编写了,新建ok_cancel_layout.xml,代码以下所示:
能够看到,这个界面也是很是简单,外层是一个垂直方向的LinearLayout,LinearLayout中包含了两个按钮,一个用于实现肯定功能,一个用于实现取消功能。如今咱们能够来预览一下这个界面,以下图所示:
好的,而后咱们有一个profile.xml的界面须要编辑一些内容,那么这里就能够将ok_cancel_layout这个布局引入到profile.xml界面当中,以下所示:
在profile.xml当中有一个EditText控件用于编辑内容,而后下面使用了<include>标签来将ok_cancel_layout布局进行引入,如今从新运行一下程序,界面效果以下图所示:
看上去效果很是不错对吗?但是在你毫无察觉的状况下,目前profile.xml这个界面当中其实已经存在着多余的布局嵌套了!感受还没写几行代码呢,怎么这就已经有多余的布局嵌套了?不信的话咱们能够经过View Hierarchy工具来查看一下,以下图所示:
能够看到,最外层首先是一个FrameLayout,这个无可厚非,不知道为何最外层是FrameLayout的朋友能够去参考 Android LayoutInflater原理分析,带你一步步深刻了解View(一) 这篇文章。而后FrameLayout中包含的是一个LinearLayout,这个就是咱们在profile.xml中定义的最外层布局。接下来的部分就有问题了,在最外层的LinearLayout当中包含了两个元素,一个是EditText,另外一个又是一个LinearLayout,而后在这个内部的LinearLayout当中才包含了肯定和取消这两个按钮。
相信你们已经能够看出来了吧,这个内部的LinearLayout就是一个多余的布局嵌套,实际上并不须要这样一层,让两个按钮直接包含在外部的LinearLayout当中就能够了。而这个多余的布局嵌套其实就是因为布局引入所致使的,由于咱们在ok_cancel_layout.xml中也定义了一个LinearLayout。那么应该怎样优化掉这个问题呢?固然就是使用<merge>标签来完成了,修改ok_cancel_layout.xml中的代码,以下所示:
能够看到,这里咱们将ok_cancel_layout最外层的LinearLayout布局删除掉,换用了<merge>标签,这就表示当有任何一个地方去include这个布局时,会将<merge>标签内包含的内容直接填充到include的位置,不会再添加任何额外的布局结构。好的,<merge>的用法就是这么简单,如今从新运行一下程序,你会看到界面没有任何改变,而后咱们再经过View Hierarchy工具来查看一下当前的View结构,以下图所示:
OK,能够看到,如今EditText和两个按钮都直接包含在了LinearLayout下面,咱们的profile.xml当中也就不存在多余的布局嵌套了。
有的时候咱们会遇到这样的场景,就是某个布局当中的元素很是多,但并非全部元素都一块儿显示出来的,而是普通状况下只显示部分经常使用的元素,而那些不经常使用的元素只有在用户进行特定操做的状况下才会显示出来。
这里举个你们都很是熟悉的例子,咱们在添加联系人的时候其实能够编辑的字段真的很是多,姓名、电话、email、传真、住址、昵称等等等等,但其实基本上你们最经常使用的就是填一个姓名,填一个电话而已。那么将这么多繁杂的字段都一块儿显示在界面上其实并非一种很好的作法,由于大多数人都是用不到这些字段的。比较聪明的作法就是把最经常使用的姓名和电话显示在界面上,而后给用户提供一个添加更多字段的选项,当用户真的有须要去添加其它信息的时候,咱们才将另外的元素显示到界面上。
说到实现这样一个功能,我相信大多数人的第一反应就是将不经常使用的元素使用INVISIBLE或者GONE进行隐藏,而后当用户须要使用这些元素的时候再把它们置成VISIBLE显示出来。使用这种方式确定能够实现功能的,可是性能方面就表现得通常了,由于即便是将元素进行隐藏,它们其实仍是在布局当中的,每一个元素还拥有着本身的宽、高、背景等等属性,解析布局的时候也会将这些隐藏的元素一一解析出来。
那么咱们如何才能让这些不经常使用的元素仅在须要时才去加载呢?Android为此提供了一种很是轻量级的控件,ViewStub。ViewStub虽然说也是View的一种,可是它没有大小,没有绘制功能,也不参与布局,资源消耗很是低,将它放置在布局当中基本能够认为是彻底不会影响性能的。
下面咱们就来学习一下如何使用ViewStub来完成仅在须要时才去加载布局的功能,目前profile.xml中只有一个EditText用于编辑信息,那么好比说咱们还有另外三个不太经常使用的EditText,就能够将它们定义在另一个布局文件当中。新建profile_extra.xml文件,代码以下所示:
能够看到,在profile_extra.xml这个布局文件当中定义了三个EditText,也就是用于编辑那些不经常使用信息的控件,如今咱们能够来预览一下这个布局,以下图所示:
目前profile_extra.xml是一个独立的布局,和profile.xml这个布局文件是彻底没有关系的。接下来咱们修改profile.xml文件中的代码,以下所示:
能够看到,这里咱们新增了一个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中定义的布局已经加载出来了,并且显示的位置也是在More按钮和OK按钮之间,正是ViewStub控件定义的位置,说明咱们确实已经将ViewStub成功使用起来了。
另外须要提醒你们一点,ViewStub所加载的布局是不可使用<merge>标签的,所以这有可能致使加载出来的布局存在着多余的嵌套结构,具体如何去取舍就要根据各自的实际状况来决定了,对于那些隐藏的布局文件结构至关复杂的状况,使用ViewStub仍是一种至关不错的选择的,即便增长了一层无用的布局结构,仍然仍是利大于弊。