Edited at

AppCompat と tintを使ってリソースを減らす

More than 3 years have passed since last update.

このまえでた Potatotips #24で @kaelaela さんがリソースの整理整頓 という発表をされてました。

それで対抗して自分もやってみようと調べたら、結局 AppCompatの DrawableCompat を使うのでいいじゃん、という Googleの手中に落ちたネタです。


やりたかったこと

@kaelaela さんの発表では、HSBのカラーフィルタをつかってました。自分はそういう色空間を使った変換じゃなくて、指定された状態で指定された色になるようにできないかなあ、できればコードを書かないでできないかなあ、と。

ちなみにできたのは以下の様なものです。pressedで赤くなり、disabled で灰色になるというものです。

tintDrawables.gif

(マウスの周りの黒い丸は MonoSnapがクリックした時につけてくれます、念のため)

github/dagezi/AndroidTintTest


Lollipop以降ではできる。はずだけど

ちょっと調べたら Lollipop以降では tint属性を使ってできることがわかりました。

以下のように nine-patchとか bitmap という要素を使えば、Drawableの stateごとに色を指定できるのでした:

<nine-patch

xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/balloon"
android:tint="@color/button_colors" />

本当は、ViewbackgroundTint を使うほうが簡単だけど、これだと ImageButton とかには適用できない、って違いがあります。

まあでもこれには落とし穴があって Buttonと組み合わせるとうごかねーぞ って bugがあるようです。


Lollipop以前でもできる

ということで DrawableCompatを使ったほうが安定してるし、古いのでも使えるしいいよね、ってことらしいです。

普通のボタンの場合、AppCompatButtonで置き換えると app:backgroundTint 属性が使えるようです。しかも、AppCompatActivityでは、layoutに Buttonと書くだけで AppCompatButton になります。すげえ。ここで属性の名前空間は android:にはならないことに注意:

   <Button

app:backgroundTint="@color/button_colors"
android:background="@drawable/balloon" ... />

ImageButton のばあい、AppCompatImageButtonがありますが、background drawableの tintしか指定できません。表の画像の色を変えるには面倒くさいけど、codeで書くしかなさそう。ここまで頑張って欲しかった:

        Drawable roundRects = DrawableCompat.wrap(getResources().getDrawable(R.drawable.round_rect));

DrawableCompat.setTintList(roundRects, getResources().getColorStateList(R.color.button_colors));
iButton.setImageDrawable(roundRects);


コード書かなきゃいけないの無念

ということで、AppCompat-v7のおかげでほぼ目標は達成できたのだけど、リソースだけで解決できなかったのは無念。まあ、ImageDrawabeなんて昨今使わないからいいのかしらん。

個人的には、Androidの resourceの拡張性、とくに Drawableに関する拡張性が低いのがいかんのではないか。layoutにはユーザが作った Viewを指定できるのに、ユーザ定義の Drawableは指定できない。そこができれば、DrawableWrapperとかをリソースに指定してできるのになあ。

まあそもそも Materialな時代になって、こういう UI自体が淘汰されるのかもなあ、というきにもなりました。