LayerDrawableの落とし穴
LayerDrawable
こんにちはこんにちは!
みなさん、突然ですがLayerDrawable使ってますか?
△ ¥ ▲ ( ? 皿 ?) がしゃーん LayerDrawableだよ 自動でDrawableを重ねてくれるすごいやつだよ
使ってみる
写真を添付するようなアプリをイメージして、
取り消し用の×印と添付する画像を2つほど用意して画像を切り替えてみました。
n1.png
n2.png
x.png
test_layer.xml 上に書いたものから置かれるので下にあるものが上に来ます。
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/number"> <bitmap android:src="@drawable/n1" android:gravity="center" /> </item> <item> <bitmap android:src="@drawable/x" android:gravity="bottom|right" /> </item> </layer-list>
これをImageViewに設定するだけ。
<ImageView android:id="@+id/image" android:layout_width="fill_parent" android:layout_height="wrap_content" android:src="@drawable/test_layer" />
楽ちんですね。
LayerDrawable#setDrawableByLayerIdで先ほどの
LayerDrawable mLayer = (LayerDrawable)((ImageView)findViewById(R.id.image)).getDrawable(); ... // 画像URIから適当にDrawable生成したりして Drawable drawable = getNewDrawable(); // 入れ替え mLayer.setDrawableByLayerId(R.id.number, drawable); // 再描画 mLayer.invalidateSelf();
単純に添付取り消しを表現するなら mLayer.setVisible(View.INVISIBLE)でいい気がする。
問題発生
こんな感じで順調に行くかとおもいきや、問題が。
Galaxy Nexusだとちゃんと切り替わるのに、他のいくつかの端末では切り替えて再描画の要求をだすと消えます。×印を残して。
結論から言うとICSでDrawbleがちょいちょい変わっていて、#setDrawableByLayerIdが(少なくとも自分の)思うような動作に修正されていました。
2.3の該当箇所
http://tools.oesf.biz/android-2.3_r1.0/xref/frameworks/base/graphics/java/android/graphics/drawable/LayerDrawable.java
266 public boolean setDrawableByLayerId(int id, Drawable drawable) { 267 final ChildDrawable[] layers = mLayerState.mChildren; 268 269 for (int i = mLayerState.mNum - 1; i >= 0; i--) { 270 if (layers[i].mId == id) { 271 layers[i].mDrawable = drawable; 272 return true; 273 } 274 } 275 276 return false; 277 }
シンプルですね。つづいでICS
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/graphics/java/android/graphics/drawable/LayerDrawable.java
278 public boolean setDrawableByLayerId(int id, Drawable drawable) { 279 final ChildDrawable[] layers = mLayerState.mChildren; 280 281 for (int i = mLayerState.mNum - 1; i >= 0; i--) { 282 if (layers[i].mId == id) { 283 if (layers[i].mDrawable != null) { 284 if (drawable != null) { 285 Rect bounds = layers[i].mDrawable.getBounds(); 286 drawable.setBounds(bounds); 287 } 288 layers[i].mDrawable.setCallback(null); 289 } 290 if (drawable != null) { 291 drawable.setCallback(this); 292 } 293 layers[i].mDrawable = drawable; 294 return true; 295 } 296 } 297 298 return false; 299 }
2倍ぐらいに増えてる… 285,286、怪しさ満点です。さっきのコードのsetDrawableByLayerIdの前に付け加えてみます。
// 画像URIから適当にDrawable生成したりして Drawable drawable = getNewDrawable(); // 入れ替える前rのDrawableからbounds取ってきてセット Rect bounds = mLayer.findDrawableByLayerId(R.id.number).getBounds(); drawable.setBounds(bounds); // 入れ替え mLayer.setDrawableByLayerId(R.id.number, drawable); // 再描画 mLayer.invalidateSelf();
無事
動きました。
コード貼る所が無いので出来上がったAPKを置いておきます。
とりあえず見てみるかという方はどうぞ。apk
上のDrawableがそのままの処理で、下のDrawableがRectを設定するようにしたものです。GNだとどっちも同じように動き、2.3以前だと上は最初にボタンを押したときに消えてそのまま。IS01で動かしたらImageViewの大きさがばらばらになりました。
追記
あ、そういえばせっかくなのでgithubで公開してみました。
使い方全然わかりません\(^o^)/
https://github.com/ytRino/LayerDrawableSample