[譯]精通 Android 中的 tools 命名空間

原文: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 鍵。

Quick add Tools namespace

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

Duplicate and replace namespace

開始使用

當(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í)使用 androidtools 命名空間。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ì)顯示如下的排版警告。

Language typos warnings

告知 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:listheadertools: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è) ActivityFragment 中,使用此屬性,你就告訴了 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 官方文檔中了解更多信息。

引用:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容