LoginSignup
9
7

More than 5 years have passed since last update.

Fab(FloatingActionButton)のアイコンをtintする

Last updated at Posted at 2018-01-05

地味にハマったので。

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ステートを使って、アイコンと色を変更してみます。

drawable/state_icon.xml
<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>
color/state_icon_color.xml
<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>
activity_sample.xml
<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のクラスであるVisibitilyAwareImageButtonFloatingActionButtonのいずれも、TintableImageSourceViewを実装していないため、21未満ではapp:tintが効かないということになります。(対応漏れでは…)


  1. support-design v27.0.2現在 

  2. 正確にはandroid:tintは使えますが、ColorStateListに対応していません(参照

9
7
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
9
7