原文:Mastering tools namespace on Android
作者:Alexandru Simonescu
译者:lovexiaov
你可能注意到了 tools 命名空间会出现在许多 Google 提供的样例布局 XML 文件中。此命名空间在开发阶段很有用而且不会影响用户体验。它包含了帮助我们在 Android Studio 设计视图中渲染布局的一套方便的属性。
有时这些巧妙的属性会节约我们的构建时间。我并不是说会加快构建速度,而是构建相关的 UI 改变会减少。
<LinearLayout 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"/>
tools 命名空间的 URI 是 http://schemas.android.com/tools
,通常使用 tools
前缀绑定,但你也可以使用任何其他前缀。
该命名空间中的所有属性都不会影响运行时或 apk 的大小,它们会在 Gradle 打包应用时被剥离出去。
你可以使用 Android Studio 提供的快捷键快速添加 tools 命名空间。只需输入 toolsNS
然后按下 TAB
键。
值得一提的是截止到写这篇文章时,Android Studio 并没有太多对此 xml 语法补全支持,不过别担心,即使 AS 没有语法提示,你仍然可以覆写 tools
属性。最简单的使用方式是:首先书写基于 android:
命名空间的属性,然后使用 CMD + D
复制这行,并替换它的前缀(为 tools
)。
开始使用
当我刚做 Android 开发时,曾使用 android:text=""
属性结合一些硬编码的假文本在 预览窗口 中查看 TextView 或 EditText 如何显示。但是 Lint 工具会检查出硬编码字符串的问题,最后我只能去定义 strings(来消除此问题),然而这样做对用户没有任何意义,还使我的 .apk 中包含了没用的资源。
(解决上述问题的)技巧是使用 tools:text"@string"
来在预览窗口中查看预填充了数据的视图。你会得到类似如下的 xml 代码:
<TextView
tools:text="Mastering ToolsNs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
使用以上代码片段,在设计时你会看到 TextView
中的文字,而在运行时将不会有该属性存在。
运行时和设计时的不同属性
需要注意的是你可以同时使用 android
和 tools
命名空间。tools
命名空间将会用在设计阶段而前者会用在运行时。
有时你希望在运行时开启某些特性在设计预览时关闭。Android 文档展示了 ListView
的例子:
<ListView
android:id="@+id/listView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fastScrollAlwaysVisible="true"
tools:fastScrollAlwaysVisible=""/>
这里你可以看到:在运行时开启了 fastScrollAlwaysVisible
功能,而在设计时关闭了它。
其实你可以覆盖所有已存在与 android
命名空间中的属性,但无法覆盖自定义属性。
在XML 中指定目标 API 版本
你可以在 XML 中执行 API 级别,就想在 Java 中使用 @TargetApi
一样。API 版本可以通过一个整形或它的代号指定。这将避免 Lint 上报版本特定 XML 属性的问题。
<TextView
tools:text="Mastering ToolsNs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:layout_height="match_parent"
tools:targetApi="M"
/>
告知 Lint 你的字符串是正确的
由于 Android Studio / Lint 默认语言是英语,如果你有其他语言的字符串资源,它将会显示如下的排版警告。
告知 Lint 你本地化资源的技巧:
<resources xmlns:tools="http://schemas.android.com/tools"
tools:locale="es"/>
这样就不会显示排版警告了。
在 fragment 和自定义视图上预览布局
我发现这(tools 命名空间)在使用 Fragment
和自定义视图时非常有用。通过 tools:layout="@layout/your_layout"
属性你可以设置在预览窗口中显示一个布局。
<LinearLayout
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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity"
>
<fragment
android:name="com.alexsimo.mastertoolsnamespace.BooksFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:layout="@layout/fragment_books"
/>
</LinearLayout>
上述代码使用了 tools:layout
属性来预览 BooksFragment
布局,而不用将工程运行在设备或模拟器上。
我们来看一下视图结构:
activity_main:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity"
>
<fragment
android:name="com.alexsimo.mastertoolsnamespace.BooksFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:layout="@layout/fragment_book"
/>
</LinearLayout>
fragment_book:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/list"
android:name="com.alexsimo.mastertoolsnamespace.BooksFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:context="com.alexsimo.mastertoolsnamespace.BooksFragment"
tools:listitem="@layout/fragment_book_list_item"
/>
fragment_book_list_item:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
xmlns:tools="http://schemas.android.com/tools"
>
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:id="@+id/imageView"
tools:src="@android:drawable/ic_media_play"
/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="@+id/id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:textAppearance="?attr/textAppearanceListItem"
tools:text="My book title"
/>
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:textAppearance="?attr/textAppearanceListItem"
tools:text="My book description"
/>
</LinearLayout>
</LinearLayout>
打开 activity_main
的预览窗口,你将会看到如下界面:
预览列表项布局
如果你比较细心,你会看到上面 xml 代码片段中的 tools:listitem=""
一行。这在预览列表时会显示你自定义的列表项而不是默认的 @android:layout/list_content"
。
还有更多相关的属性,但是 RecyclerView
没有 header 或 footer 属性(这两个属性只能用在 ListView
上)。这两个属性分别是 tools:listheader
和 tools:listfooter
。
带父容器上下文的视图
假如你有一个自定义视图或可重用的布局会通过 <include>
标签被用在许多地方。当设计该视图时,预览它在想要包含它的父容器中如何显示将会很有帮助。
在上面的 fragment_book_list_item
中,如果我们添加 tools:showIn="@layout/activity_main"
属性,将可以预览该列表条目如何显示在 activity_main 中。这没有多大意义,只是为了演示这个概念。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="@layout/activity_main"
>
<!-- Remaining views removed -->
</LinearLayout>
预览界面将类似于这样:
该特性也依赖于 Android Studio 的版本。截止到写此文之时,我使用的是 Android Studio v2.1。
关联 XML 到一个 activity
上
我很确定你已经知道该属性了,当我们使用 Android Studio 引导创建一个 Activity
时,在默认生成的 XML 文件中你会找到该属性 tools:context=".MainActivity"
。如你所知,单个 xml 布局可以被用在多个 Activity 或 Fragment 中,使用此属性,你就告诉了 Android Studio 那个 .java
Activity 类与之相关联。
这将帮助布局修改这猜测 Activity 的主题,因为主题曾被定义在 AndroidManifest.xml
文件中。
忽略 Lint 警告
你应该谨慎使用此属性,因为忽略 Lint 警告不是一个好主意。如果 Lint 上报问题,你应该行动起来并修复错误和警告。但有时 Lint 给出的是错误警告,我们明确知道(或许不知道)我们在做什么。这种情况下你可以使用 tools:ignore=""
。
想想一下我们有一个图标找不到它对应的像素密度文件夹,我们可能会使用 tools:ignore="IconMissingDensityFolder"
忽略 Lint 警告。你可以在 Android 官方文档 中阅读更多关于 Lint 的内容。
带菜单预览布局
默认情况下,定义在 Activity.onCreateOptionsMenu()
中的菜单会被渲染到预览窗口。但你可以使用 tools:menu="comma separated menu IDs"
覆盖此菜单。我个人不会使用该属性,但它可能会对你有用。
设置 Toolbar 导航模式
此属性很简单!使用 tools:actionBarNavMode="standard|list|tabs"
你可以设置 ActivityBar 的导航模式。
收缩资源
Android tools 命名空间中有许多关于收缩资源的属性,比如 tools:shrinkMode="strict|safe"
,tools:keep="@layout|layout_wildcard"
,tools:discard="@layout/unused"
等,但我不准备在此讨论它们,因为本文不是讨论收缩资源的,如果你感兴趣,可以在 Android Studio 官方文档中了解更多信息。