web-dev-qa-db-ja.com

PicassoとカスタムTransformオブジェクトを使用して大きな画像を読み込む

Android Gallery(startActivityForResultを使用))から「大きな」画像(> 1.5MB)を読み込むときに、ピカソを使用してメモリ不足例外が発生します。

準備ができたらビットマップを前処理する必要があるため、カスタムターゲットオブジェクトを使用しています。また、ビットマップをスケーリングするためにカスタムトランスフォームオブジェクトを使用しています。

問題は、メモリ不足の例外のために、Transformオブジェクトのpublic Bitmap transform(Bitmap source)メソッドが呼び出されないため、画像をリサンプリングする機会が得られないことです。

ただし、.resize(maxWidth, maxHeight)メソッドを使用すると、画像が読み込まれます。 Transformオブジェクトもその目的のためだと思いましたが、サイズ変更後にtransformメソッドが呼び出されるようです。resizeを呼び出さないと、メモリ不足になります。

問題は、サイズ変更では幅と高さの両方を指定する必要があるが、縦横比をスケーリングして維持する必要があることです。

ユーザーギャラリーから画像が選択されることを考慮してください。画像は大きく、小さく、縦、正方形、横などになります。そのため、アプリケーションを必要とするロジックを実行するには、独自の変換オブジェクトが必要です。

23
edrian

解決策を見つけました。

Transformオブジェクトでは、画像をアスペクト比を維持して1024 x 768にスケーリングする必要がありました。

.resize(width, height)を呼び出して画像をダウンサンプリングしない限り、変換オブジェクトは呼び出されませんでした。

アスペクト比を維持し、サイズ変更を使用するには、.centerInside()を呼び出します。この方法で、画像は幅、高さに合わせて再サンプルされます)。

.resize(width、height)に与える値はMath.ceil(Math.sqrt(1024 * 768))です。このようにして、カスタムTransformオブジェクトに十分な画像を確保し、Out Of Memory例外も回避します。

更新:完全な例

この例に従って、MAX_WIDTHおよびMAX_HEIGHTの境界内に収まる画像を取得します(アスペクト比を維持)

private static final int MAX_WIDTH = 1024;
private static final int MAX_HEIGHT = 768;

int size = (int) Math.ceil(Math.sqrt(MAX_WIDTH * MAX_HEIGHT));

// Loads given image
Picasso.with(imageView.getContext())
    .load(imagePath)
    .transform(new BitmapTransform(MAX_WIDTH, MAX_HEIGHT))
    .skipMemoryCache()
    .resize(size, size)
    .centerInside()
    .into(imageView);

そして、これは私のカスタムBitmapTransformクラスです:

import Android.graphics.Bitmap;
import com.squareup.picasso.Transformation;

/**
 * Transformate the loaded image to avoid OutOfMemoryException
 */
public class BitmapTransform implements Transformation {

    private final int maxWidth;
    private final int maxHeight;

    public BitmapTransform(int maxWidth, int maxHeight) {
        this.maxWidth = maxWidth;
        this.maxHeight = maxHeight;
    }

    @Override
    public Bitmap transform(Bitmap source) {
        int targetWidth, targetHeight;
        double aspectRatio;

        if (source.getWidth() > source.getHeight()) {
            targetWidth = maxWidth;
            aspectRatio = (double) source.getHeight() / (double) source.getWidth();
            targetHeight = (int) (targetWidth * aspectRatio);
        } else {
            targetHeight = maxHeight;
            aspectRatio = (double) source.getWidth() / (double) source.getHeight();
            targetWidth = (int) (targetHeight * aspectRatio);
        }

        Bitmap result = Bitmap.createScaledBitmap(source, targetWidth, targetHeight, false);
        if (result != source) {
            source.recycle();
        }
        return result;
    }

    @Override
    public String key() {
        return maxWidth + "x" + maxHeight;
    }

}
59
edrian

Android開発者のウェブサイトには非常に良い記事があり、大きな画像の読み込みで同じ問題が発生したときに多くの助けになりました。

This の記事では、エラーの原因と解決方法を説明しています。たとえば、画像をキャッシュする他の記事(メニューを参照)もあります。

2
Sietse