LoginSignup
6
8

More than 5 years have passed since last update.

LollipopでBackgrountTintが即時反映されない

Last updated at Posted at 2016-07-08

追記

最新の24.0.0では修正されています。

TL;DR

  • Lollipopでは ViewCompat#setBackgroundTintList を実行しても即時反映されない
  • Lollipopで即時反映させるには setBackgroundTintList の前に setBackgroundTintMode する

環境

  • Support Library v4 23.4.0/24.0.0

現象

backgroundで使用するShapeを用意して、

test_background.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
    <corners android:radius="8dp" />
    <solid android:color="#000000" />
</shape>

Viewのbackgroundに指定し、

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    tools:context="com.chibatching.sandbox.MainActivity">

    <TextView
        android:id="@+id/version_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="50dp"
        android:textColor="#000000"
        android:textSize="32dp" />

    <TextView
        android:id="@+id/background_tint_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/test_background"
        android:textColor="#ffffff"
        android:textStyle="bold"
        android:padding="30dp"
        android:text="Background Tint" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:text="Button" />
</LinearLayout>

ボタンクリックでTextViewのbackgroundにtintを適用して色を変えます。

MainActivity.java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final TextView versionText = (TextView) findViewById(R.id.version_text);
        versionText.setText("Android SDK " + Build.VERSION.SDK_INT);

        final TextView textView = (TextView) findViewById(R.id.background_tint_text);
        final ColorStateList stateList = new ColorStateList(
                new int[][]{{}}, new int[]{Color.parseColor("#aa0000")});

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ViewCompat.setBackgroundTintList(textView, stateList);
            }
        });
    }
}

実行するとLollipopだけ色が反映されません。

backgroundTint.gif

ただし、バックグラウンドなどから復帰するなどすると反映されます。

Imgur

原因

ViewCompat#setBackgroundTintList の処理を追っていくと、 ViewCompatLollipop#setBackgroundTintList に「tintを適用しても状態が更新されないLのバグを回避するワークアラウンドだよ」とコメントされているコードが埋め込まれています。

ViewCompatLollipop.java
static void setBackgroundTintList(View view, ColorStateList tintList) {
    view.setBackgroundTintList(tintList);

    if (Build.VERSION.SDK_INT == 21) {
        // Work around a bug in L that did not update the state of the background
        // after applying the tint
        Drawable background = view.getBackground();
        boolean hasTint = (view.getBackgroundTintList() != null)
                && (view.getBackgroundTintMode() != null);
        if ((background != null) && hasTint) {
            if (background.isStateful()) {
                background.setState(view.getDrawableState());
            }
            view.setBackground(background);
        }
    }
}

しかし、最初のsetBackgroundTintListを実行しただけではview.getBackgroundTintMode()がnullのままになるため、hasTint = falseとなってしまい、結局何も処理が行われません。

対策

view.getBackgroundTintMode()がnullにならなければ、その先の処理も実行されるはずです。
なので、ViewCompat#setBackgroundTintList 実行前にTint modeを設定します。

MainActivity.java
@Override
public void onClick(View v) {
    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
        if (textView.getBackgroundTintMode() == null) {
            textView.setBackgroundTintMode(PorterDuff.Mode.SRC_IN);
        }
    }
    ViewCompat.setBackgroundTintList(textView, stateList);
}

これでLollipopでもbackgroundTintが即時反映されるようになりました。

Imgur

おわりに

バージョン差異をいい感じに吸収してくれるはずのSupport Libraryでこういうのあるとはまりますね。。。

(追記)
雑にAOSPにissue上げました。スター増えると直るかも?
https://code.google.com/p/android/issues/detail?id=215422

6
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8