Android Styles and Themes

zeroXuan發表於2019-05-06

樣式

樣式是指為 View 或視窗指定外觀和格式的屬性集合。樣式可以指定高度、填充、字型顏色、字號、背景色等許多屬性。 樣式是在與指定佈局的 XML 不同的 XML 資源中進行定義。

主題

主題是對整個Activity或應用而不是對單個View應用的樣式。以主題形式應用樣式時,Activity或者應用中的每個檢視都將應用其支援的每個樣式屬性。

定義樣式

要建立一組樣式,請在您的專案的 res/values/ 目錄中儲存一個 XML 檔案。 可任意指定該 XML 檔案的名稱,但它必須使用 .xml 副檔名,並且必須儲存在 res/values/ 資料夾內。

該 XML 檔案的根節點必須是 <resources>

對於您想建立的每個樣式,向該檔案新增一個 <style> 元素,該元素帶有對樣式進行唯一標識的 name 屬性(該屬性為必需屬性)。然後為該樣式的每個屬性新增一個 <item> 元素,該元素帶有宣告樣式屬性以及屬性值的 name(該屬性為必需屬性)。 根據樣式屬性,` 的值可以是關鍵字字串、十六進位制顏色值、對另一資源型別的引用或其他值。以下是一個包含單個樣式的示例檔案:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="CodeFont" parent="@android:style/TextAppearance.Medium">
        <item name="android:layout_width">fill_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:textColor">#00FF00</item>
        <item name="android:typeface">monospace</item>
    </style>
</resources>
複製程式碼

<resources> 元素的每個子項都會在編譯時轉換成一個應用資源物件,該物件可由 <style> 元素的 name 屬性中的值引用。 可從 XML 佈局以 @style/CodeFont 形式引用該示例樣式(如上文引言中所示)。

<style> 元素中的 parent 屬性是可選屬性,它指定應作為此樣式所繼承屬性來源的另一樣式的資源 ID。 如果願意,您可在隨後替換這些繼承的樣式屬性。

切記,在 XML 中定義您想用作Activity或應用主題的樣式與定義檢視樣式的方法完全相同。諸如上文所定義的樣式可作為單個檢視的樣式加以應用,也可作為整個Activity` 或應用的主題加以應用。 後文將闡述如何為單個檢視應用樣式或如何以應用主題形式應用樣式。

樣式繼承

您可以通過 <style> 元素中的 parent 屬性指定應作為您的樣式所繼承屬性來源的樣式。您可以利用它來繼承現有樣式的屬性,然後只定義您想要更改或新增的屬性。 您可以從自行建立的樣式或平臺內建的樣式繼承屬性。 例如,您可以繼承 Android 平臺的預設文字外觀,然後對其進行修改:

    <style name="GreenText" parent="@android:style/TextAppearance">
        <item name="android:textColor">#00FF00</item>
    </style>
複製程式碼

如果您想從自行定義的樣式繼承屬性,則不必使用 parent 屬性, 而是隻需將您想繼承的樣式的名稱以字首形式新增到新樣式的名稱之中,並以句點進行分隔。 例如,要建立一個繼承上文定義的 CodeFont 樣式的新樣式,但將顏色設定為紅色,您可以按如下方式建立這個新樣式:

    <style name="CodeFont.Red">
        <item name="android:textColor">#FF0000</item>
    </style>
複製程式碼

請注意,<style> 標記中沒有 parent 屬性,但由於name屬性以 CodeFont 樣式名稱(這是您建立的一個樣式)開頭,因此這個樣式會繼承該樣式的所有樣式屬性。 這個樣式隨後會替換 android:textColor 屬性,將文字設定為紅色。 您可以 @style/CodeFont.Red 形式引用這個新樣式。

您可以通過使用句點連結名稱繼續進行這樣的繼承,次數不限。 例如,您可以通過以下程式碼將 CodeFont.Red 擴大:

    <style name="CodeFont.Red.Big">
        <item name="android:textSize">30sp</item>
    </style>
複製程式碼

這段程式碼同時從 CodeFont 和 CodeFont.Red 樣式繼承,然後新增 android:textSize 屬性。

注:這種通過將名稱連結起來的繼承方法只適用於由您自己的資源定義的樣式。 您無法通過這種方法繼承 Android 內建樣式。 要引用內建樣式(例如 TextAppearance),您必須使用 parent 屬性。

對 UI 應用樣式和主題

設定樣式的方法有兩種:

  • 如果是對單個檢視應用樣式,請為佈局 XML 中的 View 元素新增 style 屬性。
    <TextView
    style="@style/CodeFont"
    android:text="@string/hello" />
    複製程式碼
  • 或者,如果是對整個 Activity 或應用來應用樣式,請為 Android 清單中的 或 元素新增 android:theme 屬性。
    <application android:theme="@style/CustomTheme">
    
    <activity android:theme="@android:style/Theme.Dialog">
    複製程式碼

當您對佈局中的單個View應用樣式時,該樣式定義的屬性只應用於該 View。 如果對ViewGroup應用樣式,子View元素將不會繼承樣式屬性只有被您直接應用樣式的元素才會應用其屬性。 不過,您可以通過以主題形式應用樣式,使所應用的樣式作用於所有 View 元素。

要以主題形式應用樣式定義,您必須在 Android 清單中將樣式應用於 Activity 或應用。 如果您這樣做,Activity 或應用內的每個 View 都將應用其支援的每個屬性。 例如,如果您對某個 Activity 應用前面示例中的 CodeFont 樣式,則所有支援這些文字樣式屬性的 View 元素也會應用這些屬性。 任何不支援這些屬性的 View 都會忽略這些屬性。 如果某個 View 僅支援部分屬性,將只應用這些屬性。

根據平臺版本選擇主題

新版本的 Android 可為應用提供更多主題,您可能希望在這些平臺上執行時可以使用這些新增主題,同時仍可相容舊版本。 您可以通過自定義主題來實現這一目的,該主題根據平臺版本利用資源選擇在不同父主題之間切換。

例如,以下這個宣告所對應的自定義主題就是標準的平臺預設明亮主題。 它位於 res/values 之下的一個 XML 檔案(通常是 res/values/styles.xml)中:

<style name="LightThemeSelector" parent="android:Theme.Light">
    ...
</style>
複製程式碼

為了讓該主題在應用執行在 Android 3.0(API 級別 11)或更高版本系統上時使用更新的全息主題,您可以在 res/values-v11 下的 XML 檔案中加入一個替代主題宣告,但將父主題設定為全息主題:

<style name="LightThemeSelector" parent="android:Theme.Holo.Light">
    ...
</style>
複製程式碼

現在像您使用任何其他主題那樣使用該主題,您的應用將在其執行於 Android 3.0 或更高版本的系統上時自動切換到全息主題。

@ 和?符號的引用區別

“@”“?” 符號的引用在使用時都有一個規範的格式:“@[+][package:]type:name”“?[package:][type:]name”。可以看到,二者均包含引用符號、資源所屬的包、資源型別和資源名稱。

@ 資源引用

“@” 符號用於引用系統和我們在專案中新增的一些固有資源(drawable,string 等),或者定義的 style 樣式。比如:

android:text="@string/app_name"
複製程式碼

這裡的 app_name 就是我們自己定義在專案檔案 values/strings.xml 中的字串資源。

android:text="@android:string/cancel"
複製程式碼

而這裡的cancel屬於Android SDK中的系統字串資源,所以需要新增 @android: 來指明引用來源。android:package:的一個具體例項。

? 屬性引用

“?”符號用於引用當前主題中定義的一些屬性值。

注意,“?” 符號通過屬性名字間接引用當前主題中的對應屬性值,而不是屬性本身。

舉個例子:

android:divider="?android:listDivider"
複製程式碼

這裡的 “?” 符號通過屬性名 android:listDivider 間接獲取當前主題賦予該屬性的值。如同 @android: 一般,?android: 表示該值源自 Android SDK 系統屬性。由於在當前主題中尋找對應屬性名的值,所以沒有指定屬性型別,其實等同於:?android:attr/listDivider

那如何引用專案中自定義的屬性呢?我們在 attrs.xml 中定義一個屬性,如:

<declare-styleable name="CustomTextView">
    <attr name="colorTextCustom" format="reference|color"/>
</declare-styleable>
複製程式碼

顯然,此時我們定義的 colorTextCustom 屬性是沒有值的,直接引用沒有任何作用。需要在主題 style 中賦值:

<style name="BaseTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="colorTextCustom">#FF0000</item>
</style>
<style name="AppTheme" parent="BaseTheme">
    <item name="android:textColor">?colorTextCustom</item>
</style>
複製程式碼

可以看到,這裡在BaseTheme中對colorTextCustom屬性賦值,並在 AppTheme中通過“?colorTextCustom” 引用該屬性值。由於是本地專案中定義的屬性,所以沒有新增 android: 名稱空間。其實,這種做法的好處是,AppTheme所覆蓋的 View 均可通過建構函式獲取當前主題中的 colorTextCustom 屬性值。

相關文章