原文:Mastering tools namespace on Android
作者:Alexandru Simonescu
譯者:lovexiaov
你可能注意到了 tools 命名空間會(huì)出現(xiàn)在許多 Google 提供的樣例布局 XML 文件中。此命名空間在開發(fā)階段很有用而且不會(huì)影響用戶體驗(yàn)。它包含了幫助我們在 Android Studio 設(shè)計(jì)視圖中渲染布局的一套方便的屬性。
有時(shí)這些巧妙的屬性會(huì)節(jié)約我們的構(gòu)建時(shí)間。我并不是說會(huì)加快構(gòu)建速度,而是構(gòu)建相關(guān)的 UI 改變會(huì)減少。
<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 前綴綁定,但你也可以使用任何其他前綴。
該命名空間中的所有屬性都不會(huì)影響運(yùn)行時(shí)或 apk 的大小,它們會(huì)在 Gradle 打包應(yīng)用時(shí)被剝離出去。
你可以使用 Android Studio 提供的快捷鍵快速添加 tools 命名空間。只需輸入 toolsNS 然后按下 TAB 鍵。

值得一提的是截止到寫這篇文章時(shí),Android Studio 并沒有太多對此 xml 語法補(bǔ)全支持,不過別擔(dān)心,即使 AS 沒有語法提示,你仍然可以覆寫 tools 屬性。最簡單的使用方式是:首先書寫基于 android: 命名空間的屬性,然后使用 CMD + D 復(fù)制這行,并替換它的前綴(為 tools)。

開始使用
當(dāng)我剛做 Android 開發(fā)時(shí),曾使用 android:text="" 屬性結(jié)合一些硬編碼的假文本在 預(yù)覽窗口 中查看 TextView 或 EditText 如何顯示。但是 Lint 工具會(huì)檢查出硬編碼字符串的問題,最后我只能去定義 strings(來消除此問題),然而這樣做對用戶沒有任何意義,還使我的 .apk 中包含了沒用的資源。
(解決上述問題的)技巧是使用 tools:text"@string" 來在預(yù)覽窗口中查看預(yù)填充了數(shù)據(jù)的視圖。你會(huì)得到類似如下的 xml 代碼:
<TextView
tools:text="Mastering ToolsNs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
使用以上代碼片段,在設(shè)計(jì)時(shí)你會(huì)看到 TextView 中的文字,而在運(yùn)行時(shí)將不會(huì)有該屬性存在。
運(yùn)行時(shí)和設(shè)計(jì)時(shí)的不同屬性
需要注意的是你可以同時(shí)使用 android 和 tools 命名空間。tools 命名空間將會(huì)用在設(shè)計(jì)階段而前者會(huì)用在運(yùn)行時(shí)。
有時(shí)你希望在運(yùn)行時(shí)開啟某些特性在設(shè)計(jì)預(yù)覽時(shí)關(guān)閉。Android 文檔展示了 ListView 的例子:
<ListView
android:id="@+id/listView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fastScrollAlwaysVisible="true"
tools:fastScrollAlwaysVisible=""/>
這里你可以看到:在運(yùn)行時(shí)開啟了 fastScrollAlwaysVisible 功能,而在設(shè)計(jì)時(shí)關(guān)閉了它。
其實(shí)你可以覆蓋所有已存在與 android 命名空間中的屬性,但無法覆蓋自定義屬性。
在XML 中指定目標(biāo) API 版本
你可以在 XML 中執(zhí)行 API 級(jí)別,就想在 Java 中使用 @TargetApi 一樣。API 版本可以通過一個(gè)整形或它的代號(hào)指定。這將避免 Lint 上報(bào)版本特定 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 默認(rèn)語言是英語,如果你有其他語言的字符串資源,它將會(huì)顯示如下的排版警告。

告知 Lint 你本地化資源的技巧:
<resources xmlns:tools="http://schemas.android.com/tools"
tools:locale="es"/>
這樣就不會(huì)顯示排版警告了。
在 fragment 和自定義視圖上預(yù)覽布局
我發(fā)現(xiàn)這(tools 命名空間)在使用 Fragment 和自定義視圖時(shí)非常有用。通過 tools:layout="@layout/your_layout" 屬性你可以設(shè)置在預(yù)覽窗口中顯示一個(gè)布局。
<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 屬性來預(yù)覽 BooksFragment 布局,而不用將工程運(yùn)行在設(shè)備或模擬器上。
我們來看一下視圖結(jié)構(gòu):
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 的預(yù)覽窗口,你將會(huì)看到如下界面:

預(yù)覽列表項(xiàng)布局
如果你比較細(xì)心,你會(huì)看到上面 xml 代碼片段中的 tools:listitem="" 一行。這在預(yù)覽列表時(shí)會(huì)顯示你自定義的列表項(xiàng)而不是默認(rèn)的 @android:layout/list_content"。
還有更多相關(guān)的屬性,但是 RecyclerView 沒有 header 或 footer 屬性(這兩個(gè)屬性只能用在 ListView 上)。這兩個(gè)屬性分別是 tools:listheader 和 tools:listfooter。
帶父容器上下文的視圖
假如你有一個(gè)自定義視圖或可重用的布局會(huì)通過 <include> 標(biāo)簽被用在許多地方。當(dāng)設(shè)計(jì)該視圖時(shí),預(yù)覽它在想要包含它的父容器中如何顯示將會(huì)很有幫助。
在上面的 fragment_book_list_item 中,如果我們添加 tools:showIn="@layout/activity_main" 屬性,將可以預(yù)覽該列表?xiàng)l目如何顯示在 activity_main 中。這沒有多大意義,只是為了演示這個(gè)概念。
<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>
預(yù)覽界面將類似于這樣:

該特性也依賴于 Android Studio 的版本。截止到寫此文之時(shí),我使用的是 Android Studio v2.1。
關(guān)聯(lián) XML 到一個(gè) activity 上
我很確定你已經(jīng)知道該屬性了,當(dāng)我們使用 Android Studio 引導(dǎo)創(chuàng)建一個(gè) Activity 時(shí),在默認(rèn)生成的 XML 文件中你會(huì)找到該屬性 tools:context=".MainActivity"。如你所知,單個(gè) xml 布局可以被用在多個(gè) Activity 或 Fragment 中,使用此屬性,你就告訴了 Android Studio 那個(gè) .java Activity 類與之相關(guān)聯(lián)。
這將幫助布局修改這猜測 Activity 的主題,因?yàn)橹黝}曾被定義在 AndroidManifest.xml 文件中。
忽略 Lint 警告
你應(yīng)該謹(jǐn)慎使用此屬性,因?yàn)楹雎?Lint 警告不是一個(gè)好主意。如果 Lint 上報(bào)問題,你應(yīng)該行動(dòng)起來并修復(fù)錯(cuò)誤和警告。但有時(shí) Lint 給出的是錯(cuò)誤警告,我們明確知道(或許不知道)我們在做什么。這種情況下你可以使用 tools:ignore=""。
想想一下我們有一個(gè)圖標(biāo)找不到它對應(yīng)的像素密度文件夾,我們可能會(huì)使用 tools:ignore="IconMissingDensityFolder" 忽略 Lint 警告。你可以在 Android 官方文檔 中閱讀更多關(guān)于 Lint 的內(nèi)容。
帶菜單預(yù)覽布局
默認(rèn)情況下,定義在 Activity.onCreateOptionsMenu() 中的菜單會(huì)被渲染到預(yù)覽窗口。但你可以使用 tools:menu="comma separated menu IDs" 覆蓋此菜單。我個(gè)人不會(huì)使用該屬性,但它可能會(huì)對你有用。
設(shè)置 Toolbar 導(dǎo)航模式
此屬性很簡單!使用 tools:actionBarNavMode="standard|list|tabs" 你可以設(shè)置 ActivityBar 的導(dǎo)航模式。
收縮資源
Android tools 命名空間中有許多關(guān)于收縮資源的屬性,比如 tools:shrinkMode="strict|safe",tools:keep="@layout|layout_wildcard",tools:discard="@layout/unused" 等,但我不準(zhǔn)備在此討論它們,因?yàn)楸疚牟皇怯懻撌湛s資源的,如果你感興趣,可以在 Android Studio 官方文檔中了解更多信息。