Hatena::ブログ(Diary)

iPhone充日記

2011-05-25

ListViewに画像を非同期で読み込む場合の注意

| 00:06

Androidでの開発でListViewにImageViewを埋め込み,画像を非同期で読み込みたい場合,いくつか注意することがあったのでまとめます。

AsyncTaskで画像を非同期に読み込む処理は以下のページを参考にさせて頂きました。

no title

セルの位置と画像表示位置が一致しない

ListViewはセルとなるViewを使い回しているためか,AsyncTaskで画像を読み込んだ場合,本来表示されるべきセル以外のセルに画像が表示されしまう場合がありました。

多少強引な方法ですが,画像のタグにURLを保存し,タグと一致するURLが読み込まれた場合のみ,画像をセットすることで解決できました。

参考:Android画像付きリストの設定(ListView) | 株式会社ランチェスター

大きめの画像を読み込むとOutOfMemoryError

大きめの画像を複数枚読み込もうとすると,割とすぐに「OutOfMemoryError」が出ます。

回避するために以下の二点を気を付けました。

読み込み時に画像サイズに制限を掛ける

BitmapFactory.Optionsを用いると,最初から画像を縮小した状態で読み込むことができます。

以下のようにすると,画像を本体の画面サイズ以下に落として読み込めます。

byte[] byteArray;
Bitmap resultImage = null;

//BitmapFactoryオプション
BitmapFactory.Options options = new BitmapFactory.Options();

//画像をメモリに展開しないで情報だけ読み取る
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length, options);

//画面サイズ以下にリサイズして展開
int displayW = getWindowManager().getDefaultDisplay().getWidth();
int displayH = getWindowManager().getDefaultDisplay().getHeight();
int scaleW= options.outWidth / displayW + 1;
int scaleH= options.outHeight / displayH + 1;
options.inSampleSize = Math.max(scaleW,scaleH);
options.inJustDecodeBounds = false;

resultImage = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length, options);

inSampleSizeは画像読み込み時のサンプリング幅を指定するプロパティ(int)で,2にすると2画素毎に原画像がサンプリングされるので画像サイズが1/2になります。

上記コードの場合,画像がかなり小さくなってしまう場合(画像がほんの少し画面サイズより大きい場合など)があります。scaleWとscaleHに1加算するのをやめれば,画像が画面サイズの2倍以上だった場合のみ縮小されますので,必要に応じて調節してください。

参考:

BitmapFactory.Options | Android Developers

AndroidでBitmapFactoryを使ってサイズの大きな画像を読み込むサンプル – hoge256 blog


キャッシュにSoftReferenceを使う

画像をキャッシュしている場合,キャッシュの扱いを間違えるとすぐメモリが足りなくなります。

iOSならば自分で適切にreleaseすれば良いのですが,AndroidだとGCに投げっぱなしなのでそうもいきません。

そのため,キャッシュにはSoftReferenceを使用し,メモリ不足時に優先的に解放されるようにすると良いようです。

参考:

no title

トラックバック - http://d.hatena.ne.jp/shoby/20110525/1306335974
リンク元