注意! この記事はQiitaにて公開されていた内容をimportしたものです。
これらの内容は場合によっては陳腐化していて役に立たなくなっていたり、有害であったり、現在の著者の主張と異なることがあります。
皆様の判断の上でご利用いただけますと幸いです(度を超してヤバいものは著者に連絡して頂ければ対応します m(_ _)m)


はじめに

既に修正されているけど古いAPIでは問題となり続けるframework側のバグを踏み抜くと禿げますね(しろめ)

事案

Viewを操作するため、以下の実装をしました。

// mIsSelected は該当Viewの選択状況をもつフラグ
mLinearLayout.setBackgroundColor(
    mIsSelected ? R.color.General_ThemeColor : android.R.color.transparent
)

そして、R.color.General_ThemeColorはActionBarの背景色に指定してありました。

<style name="AppActionBar" parent="@android:style/Widget.Holo.Light.ActionBar">
        <item name="android:background">@color/General.ThemeColor</item>
</style>

このとき、View#setBackgroundColor()が実行されると、mLinearLayoutの色はmIsSelectedの状態に合わせて、透明になったり色がついたりします。

これがAndroid 4.4のような比較的新しい環境であれば問題ありませんが、Android 4.0などの環境で上記操作を行うと、次に開いたActivityのActionBarの色が白(透明)になるという事象が確認されました。

frameworkの不具合

色々と(8時間くらい)検証し、ふと思いついてAPI19とAPI14のView#setBackgroundColor()の実装を見比べた結果、明らかにコードが変化していたので、更に調査した所、以下のIssueとCommitを発見しました。

詳しくはよく分かりませんが、Drawable#getDrawable(int)が最終的にgetCachedDrawable(LongSparseArray<WeakReference<ConstantState>>, long)を呼び出していて、これがキャッシュを検索し返却するたぐいのもので、そのキャッシュがヒットすることがもろもろの問題であるらしいです、がよく分かりません。誰か解説・・・

回避方法

以下のコードで場当たり的ですが回避出来ます。

mLinearLayout.setBackgroundDrawable(
    new ColorDrawable(ContextCompat.getColor(getContext(), mIsSelected ? R.color.General_ThemeColor : android.R.color.transparent))
);

なお、View#setBackgroundDrawable(Drawable)はDeprecatedですが、サジェストされているView#setBackground(Drawable)はAPI16から提供されているということで諦めてsetBackgroundDrawableを使いました。

ActionBarStyleをThemeでセットしている場合

次のように、Themeにbackground@color/xxxxをセットしていると同じことが起こります。

<style name="AppTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar">
    ...
    <item name="android:actionBarStyle">@style/BaseActionBar</item>
    <item name="actionBarStyle">@style/BaseActionBar</item>
</style>


<style name="BaseActionBar" parent="@style/Widget.AppCompat.Light.ActionBar.Solid">
    ...
    <item name="android:background">@color/primary</item>
    <item name="background">@color/primary</item>
</style>

drawable/bg_toolbar.xmlのようなdrawableを作って、@color/xxxxの代わりにセットするようにすれば回避出来ます。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/primary"/>
</shape>
<style name="BaseActionBar" parent="@style/Widget.AppCompat.Light.ActionBar.Solid">
    ...
    <item name="android:background">@drawable/bg_toolbar</item>
    <item name="background">@drawable/bg_toolbar</item>
</style>