天天看點

Android裡面的Attr、Style和Theme 解析

前言

上面的三個概念貫穿Android架構的方方面面,是Android程式設計中很重要的一環,了解它們,并能學以緻用,不但可以讓你的代碼變得簡潔明了,還可以讓你的應用更加靈活。而且,讓你對Android的世界又進一步了解。下面一起探索一下。

概念說明

Attr:屬性,風格樣式的最小單元;

Style:風格,它是一系列Attr的集合用以定義一個View的樣式,比如height、width、padding等;

Theme:主題,它與Style作用一樣,不同于Style作用于個一個單獨View,而它是作用于Activity上或是整個應用。

Attr的定義

我們先舉一個架構中的源碼例子,用來介紹下Android中是如何定義一個Attr的,比如以下建立一個簡單的TextView布局

TextView

其中layout_width對應到架構中的attr資訊如下:

<declare-styleable name="ViewGroup_Layout">
    <attr name="layout_width"                    format="dimension">
        <enum name="fill_parent" value="-1" />
        <enum name="match_parent" value="-1" />
        <enum name="wrap_content" value="-2" />
    </attr>
    ...
</declare-styleable>
           

從上可以看到layout_width可以使用三個枚舉值,并且其中fill_parent和match_parent的value值都為-1。做過Android開發的童鞋肯定知道,從2.2開始Android架構就推薦用match_parent代替fill_parent,而以上代碼正實作了相容,因為它們對應的值都為-1。

以上的textStyle的屬性資訊在源碼中如下:

<attr name="textStyle">
    <flag name="normal" value="0" />
    <flag name="bold" value="1" />
    <flag name="italic" value="2" />
</attr>
           

它也對應了三個值,但這裡卻使用了flag标簽。細心的童鞋可能已經明白了flag與enum的差别,flag表示這幾個值可以做或運算,比如上面的textStyle,你可以疊加使用,如用bold|italic表示既加粗也變成斜體,而enum隻能讓你選擇其中一個值。

看完上例後,我們來試着自己自定義一個自己的屬性,在values目錄下建立一個attrs.xml檔案,在元素裡面首先申明一個自己的表示一個屬性組,再在裡面加上屬性就行。如下我們定義一個TestStyle的屬性組,其中有三個屬性一個是testSex,一個是testName,testName的格式我們設定為string,最後一個是testColor,這樣一個屬于我們自己的屬性就定義成功了。

TestStyle attr的format根據字面意思也挺容易了解的,這裡我解釋下reference的用法。它用在一些可以設定引用值的情況,比如@drawable/myImage、@color/myColor等。當然format也可以進行或運算,一般我們定義color類型的屬性時,也一般會把format寫成format=”reference|color”

TIPS:format即使用錯,隻要你自定義的View中擷取對應類型值也是可以的,隻是在布局中寫代碼時,IDE就不會根據你定義的format給出相應的提示了,是以最好在自定義View時還是仔細斟酌下類型。

Style的使用

如下我們在styles.xml中定義一個雪納瑞風格

<style name="XqStyle">
    <item name="testName">雪納瑞</item>
    <item name="tesColor">@drawable/schnauzer</item>
    <item name="tesSex">boy</item>
</style>
           

下面我們看下如何讓一個Style作用在一個View上的。

首先我們自定義了一個View命名為TestView,然後建立一個布局檔案中加入該TestView視圖,并讓該View使用TestStyle風格。代碼如下:

<cn.xq.test.TestView
    style="@style/TestStyle"
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"/>
           

移步到TestView的Java代碼中,我們可以通過theme的obtainStyledAttributes方法來獲得我們剛剛定義的幾個Attr屬性在Style中的内容,如下我們舉一個獲得testName的例子:

final Resources.Theme theme = context.getTheme();
TypedArray testArray = theme.obtainStyledAttributes(attrs, R.styleable.TestStyle, defStyleAttr, defStyleRes);
String name = testArray.getString(R.styleable.TestStyle_testName);
Log.e("test", "name = " + name);
testArray.recycle();
           

以上obtainStyledAttributes有四個入參,前兩個比較容易了解,後兩個用作指定預設的Style,表示如果attrs中沒有你想獲得的屬性,但如果你指定了預設Style,它會去從該預設的Style裡面找你想要的屬性。defStyleAttr和defStyleRes功能一樣,指定的資源形式不同,前者表示一個預設的指向一個style風格的attr屬性,而後者你可以直接傳入一個style風格的id。注意以上定義的Style隻能在這個TestView中被使用,如果你想在其他View使用,就需要再在需要使用的View中增加這個Style。這就是先前我們說的Style隻能作用于一個View。

Theme的使用

Theme與Style使用同一個元素标簽差別在于所包含的屬性不同,并且使用的地方也不一樣。Theme你需要設定到AndroidManifest.xml的或者标簽下,設定後,被設定的Activity或整個應用下所有的View都可以使用《style》裡面的屬性了。

比如在上例中,我們直接把TestStyle設定到标簽中,并把布局檔案中TestView元素的style=”@style/TestStyle”欄位删除,以此來測試下,這個Activity下的所有View是不是可以直接使用theme中聲明的這些屬性。

<activity
    android:name=".MainActivity"
    android:theme="@style/TestStyle">
    ...
           

以上理論上是可行的,不過運作後,程式卻出現奔潰,出現以下錯誤提示:

java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
           

有些同學一眼可能就看出,因為在這裡Activity或Application的需要很多屬性才能工作的,而此處我們隻給它傳一個TestStyle,這當然不行,是以我們需要對這個Style做下處理,讓TestStyle繼承一個系統主題,如下:

<style name="TestStyle" parent="Theme.AppCompat">
    <item name="testName">雪納瑞</item>
    <item name="testColor">@drawable/schnauzer</item>
    <item name="testSex">boy</item>
</style>
           

這樣一個雪納瑞主題就誕生了,而在這個Activity下的所有View都可以用雪納瑞的資訊了。Application中定義theme的原理一樣,這裡就不多說了。

TIPS:架構使用Attr的順序是:View中的Style會優先于Activity中的Theme,Activity中的Theme會優先于Application中的Theme,是以說你可以定義整個應用的總體風格,但局部風格你也可以做出自己的調整。

Attr的獲得方法

有些情況下,我們可能需要使用theme中的屬性值,比如下面我們想讓一個TextView直接顯示testName這個屬性的内容,并且使用系統字型的顔色,則可以如下做:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textColor="?android:textColorSecondary"
    android:text="?attr/testName"/>
           

獲得一個Attr的方法,不同于普通資源使用@符号獲得的方式,而是需要使用?符号來獲得屬性,整體的表達方式如下:

?[:][/]

如果是本應用中的attr使用,則可以省去部分。

此處的textColor使用目前主題的android:textColorSecondary屬性内容。因為資源工具知道此處是一個屬性,是以省去了attr (完整寫法:?android:attr/textColorSecondary)。

繼續閱讀