2012年6月8日金曜日

Androidアプリ開発でBitmapを扱う時に知っておくと便利かもしれない小技集

役に立つか分かりませんが、画像編集系のAndroidアプリを1本作る過程で調べたことをメモしておきます。
画像編集がメインじゃなくても画像を扱うアプリってけっこう多いと思うので開発の一助になれば幸いです。


1. 読み込む画像サイズを制限する

スマートフォンが高機能化したといっても、アプリが使えるメモリの制限はけっこう厳しいので、大きな画像をガンガン使うとOutOfMemoryErrorでアプリがすこーんと落ちることが結構あります。
そもそもでかい画像を扱わなければ死活的な問題になることはないわけで、必要がなければサイズを制限して画像を読み込んだ方が得策です。
Uri uri = "画像のパス";
Bitmap imageBitmap;
try {
    ContentResolver contentResolver = getContentResolver();
    InputStream inputStream;
    BitmapFactory.Options imageOptions;

    // メモリ上に画像を読み込まず、画像サイズ情報のみを取得する
    inputStream = contentResolver.openInputStream(uri);
    imageOptions = new BitmapFactory.Options();                   
    imageOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(inputStream, null, imageOptions);
    inputStream.close();
        
    // もし読み込む画像が大きかったら縮小して読み込む
    inputStream = contentResolver.openInputStream(uri);
    if (imageOptions.outWidth > 2048 && imageOptions.outHeight > 2048) {
        imageOptions = new BitmapFactory.Options();
        imageOptions.inSampleSize = 2;
        imageBitmap = BitmapFactory.decodeStream(inputStream, null, imageOptions);
    } else {
        imageBitmap = BitmapFactory.decodeStream(inputStream, null, null);
    }
    inputStream.close();
 } catch (Exception e) {}
BitmapFactory.OptionsinSampleSizeで読み込む画像サイズを指定できます。
ただ、このフィールドで指定できるのは2, 4, 8… と2の倍数のみで、指定した数字で画像を縮小します。(2を指定すると横を1/2、縦を1/2倍します。面積としては1/4)
ようするに、細かくサイズを指定できません。


2. 画像をリサイズする

画像の縦/横を0.6倍する場合はこんな感じです。
Matrix matrix = new Matrix();
matrix.postScale(0.6f, 0.6f);

Bitmap resizedImageBitmap;
resizedImageBitmap = Bitmap.createBitmap(imageBitmap, 0, 0, 
                                        imageBitmap.getWidth(), imageBitmap.getHeight(), matrix, true);



3. 保存した画像をギャラリーに即座に表示する

画像を扱うアプリ(ギャラリーなど)はストレージに画像を書き出しても即座に読み取ってくれないんですよね。
そのままだと端末の再起動など、スキャンしてくれるタイミングを待つ必要があります。
画像を書きこんだ後にこちらからスキャンをリクエストすると、ギャラリーに即座に表示されます。
String[] paths = {imagePath};
String[] mimeTypes = {"image/jpeg"};
MediaScannerConnection.scanFile(context, paths, mimeTypes, null);



4. ギャラリーに表示したくない画像を隠す

画像をストレージに保存する場合、キャッシュディレクトリのファイルなどは隠しておきたいですよね。

.nomediaという空のファイルをディレクトリに置くと、そのディレクトリ内のファイルはメディアを扱うアプリ(ギャラリーなど)の読み取り対象からはずれます。
File cacheDir = context.getExternalCacheDir();
if (!cacheDir.exists()) {
    cacheDir.mkdirs();
}

File noMediaFile = new File(cacheDir, ".nomedia");
if (!noMediaFile.exists()) {
    try {
        noMediaFile.createNewFile();
    } catch (IOException e) {}
}


5. 省メモリでimmutableな画像をmutableな画像に変換する

例えば、ファイルやリソースから読み込んだ画像をCanvasを使って編集する場合。
Bitmap immutableImage = Bitmap.decodeFile(...);
Canvas c = new Canvas(immutableImage);
CanvasのコンストラクタにimmutableなBitmapを渡すと例外が発生します。
java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor

BitmapFactoryのdecodeFileやdecodeResourceメソッドにはBitmapFactory.Optionsを渡すことができ、inMutableでmutableなBitmapを返すかimmutableなBitmapを返すかを指定できるのですが、このフィールドを使えるのはAPIレベル11からです。

そのため、若いAPIレベルをサポートする場合、immutableなBitmapをmutableなBitmapに変換するために画像をコピーする必要があります。
Bitmap mutableBitmap = immutableImage.copy(immutableImage.getConfig(), true);
この形だと一時的に画像ふたつ分のメモリが必要となります。

よりメモリを節約するテクニックとして下のリンクをご覧下さい。
一時的に画像をファイルに書き出し、一度メモリをクリアしてからmutableな画像を読み込んでいます。

http://pastebin.com/Fd05CPn3

0 件のコメント: