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

## 追記

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

## TL;DR

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

## 環境

- Support Library v4 23.4.0/~~24.0.0~~

## 現象

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

```xml: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に指定し、

```xml: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を適用して色を変えます。

```java: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](https://i.imgur.com/6U5VcM7.gif)

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

![Imgur](http://i.imgur.com/jGisI52.gif)

## 原因

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

```java: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を設定します。

```java: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](http://i.imgur.com/pOXKfrH.gif)

## おわりに

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

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