2016/10/28

如何在 Drawable 的 Selector 中使用 Theme 所定義的 Color 值

在最新的 Android 版本中提供了一項方便的功能,可以指定 Theme 中定義好的顏色到 Layout 中的對應 Attribute。例如:在 Theme 中會定義 android:colorPrimary 來代表主要的顏色,此時可以指定 TextView 的 textColor 為 "?android:attr/colorPrimary”,則 Text 的顏色就會呈現 colorPrimary 所設定的數值。當 Theme 的 colorPrimary 更改時,TextView 上的 Text 顏色也會隨之修正。

不過這並不是這個功能的優勢,在 textColor 中指定 “@color/primary”,再透過修改預先在 res/values/colors.xml 中定義的顏色也可以達到同樣的效果。這項功能最主要的好處是 textColor 會隨著所在的 Theme 而呈現不同的顏色值。如果有一個 Layout 含有上述的 TextView,且在不同的 Activity 間共用,但每一個 Activity 都有指定自己專屬的 Theme。此時,例子中 TextView 上的文字顏色在不同的 Activity 上就會隨著 Theme 而改變,甚至是啟動 Activity 前另外指定 Theme 也會讓 textColor 隨之不同。

這也就是為什麼在最新的 SDK 中呼叫特定的函式時,都會被建議要加上 Theme 的參數。因為在 Layout 中有可能指定 Theme 中某一個內容,如果沒有一併指定 Theme 時,會造成這些資訊沒有辦法取得而出現問題。

如果想要讓 View 在不同的 State 中顯示不一樣的顏色可以使用 ColorStateList 的 Selector 來達成效果。但對 android:background 的 Attribute 不適用,在 android:background 中要指定 Drawable 類型的資源。最直接的方法就是同樣的 Selector 檔案移到 res/drawable 路徑下,原本的內容中 android:color 要改成 android:drawable。

只不過此時會有一個惱人的問題出現,在使用 android:drawable 時沒有辦法以 ?android:attr 的方式來指定 Theme 中所定義的顏色。這下就有點傷腦筋了,豈不是要回復到之前每一個 Theme 都要建立一組 Selector 的麻煩方式。同時,在某些情況下要用程式指定 Selector 時還要先判斷目前的 Theme 來決定產生的 Instance,平白多打很多的程式碼。

所幸,經過上網查詢之後找到了解決的方法,可以將 Selector 改用以下的方式替代: