Hacks布局篇-Hack2 使用<include />标签避免代码重复

<include />

作者:李旺成

时间:2016年5月7日


在这个 Hack 中将详细介绍 <include /> 标签的使用,以及一些注意事项。

一、每个页面添加页脚

添加页脚示意图

假设:现在有这样一个需求,为应用中的每个页面都添加一个页脚。这里简单处理,要添加的页脚就是一个显示应用名称的文本(使用 TextView 来显示),如上图所示。大多数的应用都是由多个 Activity 组成的,多个 Activity 一般对应多个布局文件。那是不是要把这个页脚 TextView 一个个拷贝到每个布局文件中?

听过这么一句话:当你的程序中出现大量的重复代码时你得小心了。(不是我杜撰的,大致是这个意思,没有找到出处...)

确实是这样,不要把一段相同的代码到处“复制/粘贴”,有一个很明显的弊端摆在眼前,如果以后需要修改这段代码就悲剧了。

所以,要实现上述需求“复制/粘贴”不是我们要的解决方案。解决该问题最简单的方法是使用 <include /> 标签。当然,使用 style 来保证这些页脚都使用同一个样式也是可以的,但没有 <include /> 标签简便,再说 style 的主要功能可不是用来解决重复代码问题的。

好了需求明确的,看看怎么实现。

使用 <include /> 标签为页面添加页脚

首先,创建一个页脚布局 footer_app_name.xml:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_marginBottom="20dp"
    android:gravity="center"
    android:text="AndroidHacksLayout"
    android:textColor="@android:color/holo_red_dark"/>

然后,使用 <include /> 为布局添加页脚 activity_hack2_1.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.diygreen.androidhackslayout.Hack2Activity">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="演示使用 <include /> 标签避免代码重复"/>
    <!-- 插入页脚 -->
    <include
        layout="@layout/footer_app_name"/>
</RelativeLayout>

上面的示例代码很简单,为 <include /> 标签的 layout 属性指定一个表示页脚的布局即可。仔细一点的同学可能看到了:页脚布局中设置了 android:layout_alignParentBottom="true" 属性,那么问题来了,这个属性是 RelativeLayout 中子控件才有用,如果把 activity_hack2_1.xml 的根布局修改为 FrameLayout 那不就不行了吗?

根布局修改为 FrameLayout

上图中可以看到,将根布局修改为 FrameLayout 之后,“页脚”就不是在该页面的底部了,那要怎么做了?不急,下面会给出解决方案,且继续往下阅读。

二、<include /> 标签使用详解

<include /> 标签使用虽然挺简单的,但也有一些细节要注意,这里做个简单的总结供大家参考。

include 属性有两种设置方式

1、在子布局中设置
就是上面的示例代码中使用的方式,直接在子布局中设置好所有的属性,使用的时候只需要在主布局中为 include 设置 layout 属性即可。

这样用很方便,但是 Android 中提供了很多布局技术,你在子布局中设置的属性可能只适用与某一个/某一类布局。上面的示例中就是这样,只有主布局为 RelativeLayout 的时候页面才能在指定位置显示,如果替换为 FrameLayout 就显示不正确了。这就反映了一个问题 —— 不灵活。那怎么解决呢?include 属性还有另外一种设置方式。

2、在 <include /> 标签里设置
说到这里,那先把示例中遗留的问题解决了,就是将 RelativeLayout 替换为 FrameLayout 之后页面位置的问题。

先看解决后的效果:

在 <include /> 标签里设置属性

解决方案代码:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    >
    ...
    <include
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        layout="@layout/footer_app_name"/>
</FrameLayout>

上面的示例代码中与根布局为 RelativeLayout 对比的主要修改处是在 <include /> 标签中添加了 android:layout_gravity 属性,并将其属性值设置为了 "bottom" ,这就可以保证页脚会在页面的底部显示了。

说明:其实和 style 的使用非常类似,你可以直接使用 style,也可以覆盖 style 中的一些属性。

在 <include /> 标签里设置属性一般是这样用的,下面使用添加一个页眉作为示例:

  1. 创建页面 header_app_name.xml:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:gravity="center"
    android:text="我是-页眉-"
    android:textColor="@android:color/holo_blue_dark"/>
  1. 使用 <include /> 标签添加页眉
<include
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dp"
    layout="@layout/header_app_name"/>

效果如下:

添加页眉

注意:在示例中页面布局的宽度高度都设置为 0dp 了,这么做的目的就是由 header_app_name.xml 文件的使用者在 <include /> 标签中指定 layout_width 和 layout_height 属性。如果使用者不指定这两个属性,它们的默认值都是 0dp,那便看不到页眉。

三、<include /> 标签注意事项

这里罗列了一些我在使用 <include /> 标签的时候踩过的一些坑,希望大家以后可以绕过去。

findViewById 出现 NullPointerException

具体情况是这样的,如果 include 一个布局时,并没有给 <include /> 标签设置 id 属性,那么你直接使用 findViewById 来找 include 指定布局中控件是没有问题的。

但是,一旦你为 <include /> 标签设置了 id ,就不能直接把它里面的控件当成主布局文件中的控件来直接获取了,必须先获得这个 <include /> 标签指定的布局文件,再通过该布局文件 findViewById 来获得其子控件。

<include /> 标签失效了

Android 的缺陷(Issue)跟踪系统中报告过一个缺陷,缺陷的标题是:“<include /> 标签失效了,如果想通过 <include /> 标签的属性覆盖被包含的布局所指定的属性是行不通的。”。

这个 issue 描述的问题在一定程度上是正确的,问题出在如果想在 <include /> 标签中覆盖被包含布局所指定的任何 android:layout_* 属性,必须在 <include /> 标签中同时指定 android:layout_width 和 android:layout_height 这两个属性。(参考自:《50 Android Hacks》)

PS:话说如果你是用 AndroidStudio 的话,可能根本就不会遇到这个问题,因为,如果你在指定其他的 android:layout_* 属性时,如果没有同时指定 android:layout_width 和 android:layout_height 这两个属性,AndroidStudio 是会报错的:

提示添加 layout_width 和 layout_height

自定义控件与 <include />

如果是一些比较简单,include 进来之后不需要有过多操作的,使用 <include /> 完全可以胜任了。对于一些比较复杂,而且要添加很多响应事件等的使用场景,建议使用自定义控件。如果还是使用 <include /> 标签,那么不可避免在 Java 代码中又得写一堆类似的代码来添加相应逻辑。

项目地址

AndroidHacks合集
布局篇
个人博客
示例用到代码见:
Hack2Activity.java
activity_hack2_1.xml
activity_hack2_2.xml
header_app_name.xml
footer_app_name.xml

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,465评论 25 707
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,727评论 1 92
  • 在前面几篇文章当中,我们学习了如何通过合理管理内存,以及高性能编码技巧的方式来提升应用程序的性能。然而实际上界面布...
    Ten_Minutes阅读 1,214评论 1 8
  • 人是有品级的,不管是心灵还是精神。有品级的男人,不一定是你看到的功成名就的“大叔”,也不一定是叱咤风云的权利男人。...
    wsxyz阅读 180评论 0 0
  • 每个人眼中的挫折都是不一样的。是某些人眼中的失败,是失去了某些重要的东西,而在史铁生眼中是他的残疾。在老师的指导...
    王之宝玉阅读 267评论 0 1