地味にハマったので。
API Level 21 (Android 5.0) 以上
普通に書けば終わりです。
<FloatingActionButton
android:src="@drawable/icon"
android:tint="@color/fab_color" />
API Level 21未満
FloatingActionButtonはAppCompatImageButton
ではないです。1
そのため、android:tint
はおろかapp:tint
も使えません。2
Drawableをtintする
Viewがtintをサポートしていないのであれば、Drawableがサポートすれば対応できます。
DrawableCompat.setTintList(icon, colorStateList);
しかし、DrawableCompat.setTintList()
は、21未満の場合TintAwareDrawable
のサブクラスである必要があります。
そのため、TintAwareDrawable
でwrapする必要があります。
icon = DrawableCompat.wrap(icon);
あとは、FloatingActionButtonに上のDrawableを与えるだけです。
BindingAdapterで属性として実装する
以上の実装をBindingAdapterで実装します。
@BindingAdapter("tintCompat")
public static void setTintCompat(FloatingActionButton fab, ColorStateList colorStateList) {
Drawable icon = fab.getDrawable();
if (icon != null) {
icon = DrawableCompat.wrap(icon);
DrawableCompat.setTintList(icon, colorStateList);
fab.setImageDrawable(icon);
}
}
つかってみる
activateステートを使って、アイコンと色を変更してみます。
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/ic_star_black_24dp"
android:state_activated="true" />
<item android:drawable="@drawable/ic_star_outline_black_24dp" />
</selector>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:color="@color/red"
android:state_activated="true" />
<item android:color="@color/gray" />
</selector>
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="favorite"
type="boolean" />
</data>
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.design.widget.FloatingActionButton
tintCompat="@{@colorStateList/state_icon_color}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
app:activated="@{favorite}"
app:backgroundTint="@android:color/white"
app:srcCompat="@drawable/state_icon" />
</<android.support.design.widget.CoordinatorLayout>
</layout>
selectorで状態別に色を定義する場合は、@colorStateList
式で指定する必要があります。
@color
式で参照すると、default色をColorStateListにするという動きをします。
参考
番外編:なぜFloatingActionButtonのapp:tint
が21未満で効かないのか
注:support-design内部の実装のため、修正がかかる可能性があります。
app:tint
属性は、実際はAppCompatImageView
ではなく、AppCompatImageHelper
が読み取ります。
FloatingActionButtonは、AppCompatImageView
のサブクラスではありませんが、AppCompatImageHelper
を使っているため、以下の属性が有効です。
- app:srcCompat
- app:tint
- app:tintMode
ではなぜ、app:tint
をサポートしているにもかかわらず、FloatingActionButtonには効果が得られないのでしょうか…?
AppCompatImageHelperのtint属性の実装は以下のようになっています。(AOSP)
if (a.hasValue(R.styleable.AppCompatImageView_tint)) {
ImageViewCompat.setImageTintList(mView,
a.getColorStateList(R.styleable.AppCompatImageView_tint));
}
ImageViewCompat.setImageTintList()
は21未満の場合、TintableImageSourceView
を実装している場合のみ、tintを有効にする実装となっています。
ここで、FloatingActionButtonの継承関係を見ると
android.widget.ImageView
│
└ android.widget.ImageButton
│
└ android.support.design.widget.VisibilityAwareImageButton
│
└ FloatingActionButton
となっています。
このうちsupport-designのクラスであるVisibitilyAwareImageButton
、FloatingActionButton
のいずれも、TintableImageSourceView
を実装していないため、21未満ではapp:tint
が効かないということになります。(対応漏れでは…)