Android 使用 Picasso 和自定义 Transform 对象加载大图像

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23740307/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 07:23:30  来源:igfitidea点击:

Load large images with Picasso and custom Transform object

androidpicasso

提问by edrian

I'm getting an Out Of Memory exception using Picasso when loading "large" images (> 1.5MB) from Android Gallery (using startActivityForResult).

从 Android Gallery(使用 startActivityForResult)加载“大”图像(> 1.5MB)时,我使用 Picasso 遇到内存不足异常。

I'm using a custom Target object because I need to preprocess the Bitmap when it is ready and also I'm using a custom Transform object to scale the Bitmap.

我正在使用自定义 Target 对象,因为我需要在 Bitmap 准备好时对其进行预处理,而且我正在使用自定义 Transform 对象来缩放 Bitmap。

The problem is that the method public Bitmap transform(Bitmap source)on my Transform object is never called because the Out Of Memory Exception, so I don't get the oportunity to resample the image.

问题是public Bitmap transform(Bitmap source)我的 Transform 对象上的方法从未被调用,因为内存不足异常,所以我没有机会重新采样图像。

But, if I use the .resize(maxWidth, maxHeight)method, then it loads the image OK. I supossed that the Transform object was for that purpose too, but it seems that the transform method is called after the resize, and, if I don't call resize, then it will end in an Out of Memory..

但是,如果我使用该.resize(maxWidth, maxHeight)方法,则它可以正常加载图像。我假设 Transform 对象也是为了这个目的,但似乎在调整大小之后调用了转换方法,如果我不调用调整大小,那么它将以内存不足结束..

The problem is that with resize I need to specify both width and height, but I need to scale and keep the aspect ratio.

问题是,在调整大小时,我需要指定宽度和高度,但我需要缩放并保持纵横比。

Consider that the images will be selected from user Gallery, so they can be bigger or smaller, portrait, squared or landscape, etc, so I need my own Transformation object to perform the logic that needs my application.

考虑到图像将从用户图库中选择,因此它们可以更大或更小、纵向、方形或横向等,因此我需要自己的 Transformation 对象来执行需要我的应用程序的逻辑。

回答by edrian

I found a solution..

我找到了解决方案..

In my Transform object I needed to scale the image (keeping aspect ratio) to 1024 x 768 max.

在我的 Transform 对象中,我需要将图像(保持纵横比)缩放到最大 1024 x 768。

Transform object was never called unless I call .resize(width, height)to resample down the image.

除非我调用.resize(width, height)重新采样图像,否则从未调用变换对象。

For keeping aspect ratio and using resize I call .centerInside(). This way image will be scaled resample to fit width, height).

为了保持纵横比并使用调整大小,我调用.centerInside(). 这样图像将被缩放重新采样以适应宽度、高度)。

The value that I give to .resize(width, height) is Math.ceil(Math.sqrt(1024 * 768)). This way I'm sure to have an image higher enough for my custom Transform object, and also avoid Out Of Memory exception

我给 .resize(width, height) 的值是Math.ceil(Math.sqrt(1024 * 768)). 通过这种方式,我肯定会为我的自定义 Transform 对象提供足够高的图像,并且还可以避免 Out Of Memory 异常

Update: Full example

更新:完整示例

Following this example you will get a image that fits inside MAX_WIDTH and MAX_HEIGHT bounds (keeping aspect ratio)

按照此示例,您将获得一个适合 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);

And this is my custom BitmapTransform class:

这是我的自定义 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;
    }

}

回答by Sietse

There's a very good article on the Android Developer website which helped me alot when I had the same problem with loading large images.

Android 开发人员网站上有一篇非常好的文章,当我在加载大图像时遇到同样的问题时,它对我有很大帮助。

Thisarticle explains really well what causes the error and how to solve it. There are also other articles (see menu) to, for example, cache images.

这篇文章很好地解释了导致错误的原因以及如何解决它。还有其他文章(见菜单),例如,缓存图像。