Android で画像を読み込むのは結構厄介。一つの ImageView に読み込むならそんなに悩まないけど、 ListView やら GridView やらで画像を扱うとすぐ OutOfMemory って怒られる。限られた資源、守りたいですね。
基本は BitmapFactory.Options.inJustDecodeBounds = true にして、画像の情報だけを とって BitmapFactory.Options.inSampleSize を適切にしてあげて全体を無難に読み込む。そうじゃなくて、大きな画像の一部を切り抜いてもいいんじゃないかと。そんな画像切り抜き BitmapRegionDecoder クラスというのがあります。
とりあえず
サンプルは Github に上げた。
読み込み
画像の読み込みはバックグラウンドで行うのが基本。AsyncTaskLoader で行なっている。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 基本は一緒 | |
// inJustDecodeBounds = true で読み込んで画像サイズを取得する | |
// options.outWidth, options.outHeight にサイズが入る | |
inputStreamForDecodeBounds = manager.open(FILE_NAME); | |
BitmapFactory.Options options = new BitmapFactory.Options(); | |
options.inJustDecodeBounds = true; | |
BitmapFactory.decodeStream(inputStreamForDecodeBounds, null, options); | |
int bitmapWidth = options.outWidth; | |
int bitmapHeight = options.outHeight; | |
// 0 <= width <= bitmapWidth | |
// 0 <= height <= bitmapHeight | |
// でくり抜ける | |
// InputStream は使い回しできないので作り直し!! | |
inputStream = manager.open(FILE_NAME); | |
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(inputStream, true); | |
// Rect rect = new Rect(0, 0, 200, 200); // 左上から 200 * 200 で切り出す。 | |
Rect rect = new Rect(150, 150, 400, 400); // 左上 (50, 100)から 400 * 400 で切り出す。 | |
// 右下100 * 100 を切り出す | |
// Rect rect = new Rect(bitmapWidth - 200, bitmapHeight - 200, bitmapWidth, | |
// bitmapHeight); | |
// Rect rect = new Rect(0, 0, bitmapWidth, bitmapHeight); // 全読み | |
// inJustDecodeBounds = false; にして実際に読み込む | |
options.inJustDecodeBounds = false; | |
Bitmap bitmap = decoder.decodeRegion(rect, options); | |
return bitmap; |
例では assets にある画像ファイルを読み込んで表示しようとしています。読み込んだ画像よりも Rect のサイズが大きいと例外になると思いきや、はみ出した部分は真っ黒で塗りつぶされた画像が出来上がる。綺麗にやろうとしたらちゃんとサイズを見たほうがいいと思う。
そもそも画像をくり抜こうとしたら Bitmap 作って そこから Canvas 作って Rect 作ってと・・・意外にめんどい。しかも メモリ節約のために小さく切り抜くのにあたらに Bitmap 作るなんて本末転倒。そこはさくっと BitmapRegionDecoder で仕上げるのが吉。
ただし、BitmapRegionDecoder は API Level 10 以上。Froyo[1] Gingerbread (API Level 9, < Android OS 2.3.3) には対応していません。Android OS 1.6, 2.2 以上がターゲットのアプリには使えないことになります。 まあ、今なら Gingerbread (API Level 10, >= Android OS 2.3.3)もしくは ICS 以降をターゲットにするでしょうから、全く問題ない範囲だと思います。
そもそも画像をくり抜こうとしたら Bitmap 作って そこから Canvas 作って Rect 作ってと・・・意外にめんどい。しかも メモリ節約のために小さく切り抜くのにあたらに Bitmap 作るなんて本末転倒。そこはさくっと BitmapRegionDecoder で仕上げるのが吉。
ただし、BitmapRegionDecoder は API Level 10 以上。
無駄にハマったこと
BitmapFactory.Options.inJustDecodeBounds = true で必要な InputStream と BitmapFactory.Options.inJustDecodeBounds = false で実際に読み込む時の InputStream。同じ物を使いまわすとデコードできません。BitmapRegionDecoder#decodeRegion() の場合は
java.io.IOException: Image format not supportedの IOException が出ます。IOException でそのメッセージですか、そうですか・・・。InputStream は別個に作成しましょう。