Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 分析Android多主題顏色的相關問題

分析Android多主題顏色的相關問題

編輯:關於Android編程

如果您通過以下的代碼來獲取定義的顏色值

context.getResources().getColor(R.color.some_color_resource_id);

在 Android Studio 中會有一個 lint 警告,提示您 Resources#getColor(int)Marshmallow 中被廢棄了,建議使用主題可知的 Resources#getColor(int, Theme) 函數。 為了避免該警告,則可以使用 ContextCompat

ContextCompat.getColor(context, R.color.some_color_resource_id);

該函數的實現是這樣的:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 return context.getResources().getColor(id, context.getTheme());
} else {
 return context.getResources().getColor(id);
}

看起來很簡單。但是為什麼會這樣呢? 為什麼會開始使用帶主題的函數而廢棄之前的函數呢?

Resources#getColor(int) & Resources#getColorStateList(int) 的問題

首先來看看這兩個被廢棄的函數是干啥的:
      – Resources#getColor(int) 返回一個資源 id 對應的顏色值,如果該資源為 ColorStateList 則返回 ColorStateList 的默認顏色值

      – Resources#getColorStateList(int) 返回對應的 ColorStateList

上面的代碼在什麼情況下會破壞我的代碼呢?

要理解為何廢棄這兩個函數,來看個 ColorStateList 的例子。 當在 TextView 中使用自定義的 ColorStateList 的時候, TextView 不可用狀態和可用狀態的文字顏色分別使用 R.attr.colorAccentR.attr.colorPrimary 表示。

XHTML

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:color="?attr/colorAccent" android:state_enabled="false"/>
  <item android:color="?attr/colorPrimary"/>
</selector>

現在如果您通過如下的代碼來獲取這個ColorStateList

ColorStateList csl = context.getResources().getColorStateList(R.color.button_text_csl);

上面的代碼會拋出一個異常(查看logcat 可以看到如下的信息)

W/Resources: ColorStateList color/button_text_csl has unresolved theme attributes!
       Consider using Resources.getColorStateList(int, Theme)
       or Context.getColorStateList(int)
    at android.content.res.Resources.getColorStateList(Resources.java:1011)
    ...

哪裡出錯了呢?

問題的根源在於 Resources 對象並沒有和一個 Theme 對象關聯,當使用 R.attr.colorAccent R.attr.colorPrimary 指代顏色的時候,在代碼中通過上面的函數解析的時候沒有指定對應的 Theme導致無法解析出結果。 所以在 Marshmallow 中添加了 ColorStateList 對 Theme 的支持並且添加了這兩個新的函數:Resources#getColor(int, Theme) Resources#getColorStateList(int, Theme),並使用 Theme 參數來解析裡面的 attributes 屬性。

在新版本的 Support 庫中也有對應的實現,分別位於 ResourcesCompat ContextCompat 類中。

如何解決該問題呢?

使用 AppCompat v24+ 版本可以很容易的解決該問題。

ColorStateList csl = AppCompatResources.getColorStateList(context, R.color.button_text_csl);

在 23+ 版本上直接使用系統的函數,在之前的版本上 AppCompat 自己解析這些 xml 文件從裡面提取 attr 屬性指代的數值。 AppCompat 同時還支持 ColorStateList 新的 android:alpha 屬性。

Resources#getDrawable(int) 的問題

Resources#getDrawable(int) 和前面的兩個函數的問題是類似的。 在 Lollipop 之前的版本中無法支持 Theme attr 。

為啥我這樣用也沒有出現異常呢?

異常並不總是會出現。

VectorDrawableCompatAnimatedVectorDrawableCompat 類中添加了和 AppCompatResources 類類似的功能。比如在 矢量圖中你可以使用 ?attr/colorControlNormal 來設置矢量圖的顏色,VectorDrawableCompat 會自動完成解析該 屬性的工作:

XHTML

<vector 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:width="24dp"
  android:height="24dp"
  android:viewportWidth="24.0"
  android:viewportHeight="24.0"
  android:tint="?attr/colorControlNormal">
 
  <path
    android:pathData="..."
    android:fillColor="@android:color/white"/>
</vector>

小測試

下面使用一個小測試來回顧一下前面介紹的內容。 假設有下面一個 ColorStateList:

XHTML

<!-- res/colors/button_text_csl.xml -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:color="?attr/colorAccent" android:state_enabled="false"/>
  <item android:color="?attr/colorPrimary"/>
</selector>

在應用中定義了如下的 Theme:

XHTML

<!-- res/values/themes.xml -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
  <item name="colorPrimary">@color/vanillared500</item>
  <item name="colorPrimaryDark">@color/vanillared700</item>
  <item name="colorAccent">@color/googgreen500</item>
</style>
 
<style name="CustomButtonTheme" parent="ThemeOverlay.AppCompat.Light">
  <item name="colorPrimary">@color/brown500</item>
  <item name="colorAccent">@color/yellow900</item>
</style>

在代碼中有如下的函數用來解析顏色值並在代碼中創建 ColorStateList:

@ColorInt
private static int getThemeAttrColor(Context context, @AttrRes int colorAttr) {
 TypedArray array = context.obtainStyledAttributes(null, new int[]{colorAttr});
 try {
  return array.getColor(0, 0);
 } finally {
  array.recycle();
 }
}
 
private static ColorStateList createColorStateList(Context context) {
 return new ColorStateList(
   new int[][]{
     new int[]{-android.R.attr.state_enabled}, // Disabled state.
     StateSet.WILD_CARD,            // Enabled state.
   },
   new int[]{
     getThemeAttrColor(context, R.attr.colorAccent), // Disabled state.
     getThemeAttrColor(context, R.attr.colorPrimary), // Enabled state.
   });
}

 看看是否能猜出在 API 19 和 API 23 版本上文字禁用狀態和正常狀態的顏色,實現代碼如下(5和8的情況,在TextView xml 中指定了 android:theme=”@style/CustomButtonTheme” ):

Resources res = ctx.getResources();
 
// (1)
int deprecatedTextColor = res.getColor(R.color.button_text_csl);
button1.setTextColor(deprecatedTextColor);
 
// (2)
ColorStateList deprecatedTextCsl = res.getColorStateList(R.color.button_text_csl);
button2.setTextColor(deprecatedTextCsl);
 
// (3)
int textColorXml = 
  AppCompatResources.getColorStateList(ctx, R.color.button_text_csl).getDefaultColor();
button3.setTextColor(textColorXml);
 
// (4)
ColorStateList textCslXml = AppCompatResources.getColorStateList(ctx, R.color.button_text_csl);
button4.setTextColor(textCslXml);
 
// (5)
Context themedCtx = button5.getContext();
ColorStateList textCslXmlWithCustomTheme =
  AppCompatResources.getColorStateList(themedCtx, R.color.button_text_csl);
button5.setTextColor(textCslXmlWithCustomTheme);
 
// (6)
int textColorJava = getThemeAttrColor(ctx, R.attr.colorPrimary);
button6.setTextColor(textColorJava);
 
// (7)
ColorStateList textCslJava = createColorStateList(ctx);
button7.setTextColor(textCslJava);
 
// (8)
Context themedCtx = button8.getContext();
ColorStateList textCslJavaWithCustomTheme = createColorStateList(themedCtx);
button8.setTextColor(textCslJavaWithCustomTheme);

下面是對應的實現截圖:

 

總結 

以上就是關於分析Android多主題顏色的相關問題的全部內容,希望本文的內容對大家開發Android能有所幫助。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved