このまえでた Potatotips #24で @kaelaela さんがリソースの整理整頓 という発表をされてました。
それで対抗して自分もやってみようと調べたら、結局 AppCompatの DrawableCompat を使うのでいいじゃん、という Googleの手中に落ちたネタです。
やりたかったこと
@kaelaela さんの発表では、HSBのカラーフィルタをつかってました。自分はそういう色空間を使った変換じゃなくて、指定された状態で指定された色になるようにできないかなあ、できればコードを書かないでできないかなあ、と。
ちなみにできたのは以下の様なものです。pressed
で赤くなり、disabled
で灰色になるというものです。
(マウスの周りの黒い丸は MonoSnapがクリックした時につけてくれます、念のため)
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" />
本当は、View
の backgroundTint
を使うほうが簡単だけど、これだと 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自体が淘汰されるのかもなあ、というきにもなりました。