web-dev-qa-db-fra.com

Chargez de grandes images avec Picasso et un objet Transform personnalisé

Je reçois une exception de mémoire insuffisante à l'aide de Picasso lors du chargement de "grandes" images (> 1,5 Mo) à partir de Android Gallery (en utilisant startActivityForResult).

J'utilise un objet Target personnalisé car j'ai besoin de prétraiter le bitmap lorsqu'il est prêt et j'utilise également un objet Transform personnalisé pour mettre à l'échelle le bitmap.

Le problème est que la méthode public Bitmap transform(Bitmap source) sur mon objet Transform n'est jamais appelée à cause de l'exception de mémoire insuffisante, donc je n'ai pas la possibilité de rééchantillonner l'image.

Mais, si j'utilise la méthode .resize(maxWidth, maxHeight), alors elle charge l'image OK. J'ai supposé que l'objet Transform était également prévu à cet effet, mais il semble que la méthode de transformation soit appelée après le redimensionnement et, si je n'appelle pas resize, elle se terminera par une mémoire insuffisante.

Le problème est qu'avec le redimensionnement, je dois spécifier à la fois la largeur et la hauteur, mais j'ai besoin de mettre à l'échelle et de conserver les proportions.

Considérez que les images seront sélectionnées dans la galerie utilisateur, afin qu'elles puissent être plus grandes ou plus petites, portrait, carré ou paysage, etc., j'ai donc besoin de mon propre objet Transformation pour exécuter la logique qui a besoin de mon application.

23
edrian

J'ai trouvé une solution ..

Dans mon objet Transform, j'avais besoin de redimensionner l'image (en gardant le rapport d'aspect) à 1024 x 768 max.

L'objet Transform n'a jamais été appelé sauf si j'appelle .resize(width, height) pour rééchantillonner l'image.

Pour conserver les proportions et utiliser le redimensionnement, j'appelle .centerInside(). De cette façon, l'image sera redimensionnée pour s'adapter à la largeur, la hauteur).

La valeur que je donne à .resize (largeur, hauteur) est Math.ceil(Math.sqrt(1024 * 768)). De cette façon, je suis sûr d'avoir une image suffisamment élevée pour mon objet Transform personnalisé et d'éviter également l'exception de mémoire insuffisante

Mise à jour: exemple complet

En suivant cet exemple, vous obtiendrez une image qui s'inscrit dans les limites MAX_WIDTH et MAX_HEIGHT (en conservant le rapport hauteur/largeur)

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);

Et voici ma classe BitmapTransform personnalisée:

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

Il y a un très bon article sur le site Web du développeur Android qui m'a beaucoup aidé lorsque j'ai eu le même problème avec le chargement d'images volumineuses.

This article explique très bien ce qui cause l'erreur et comment la résoudre. Il existe également d'autres articles (voir menu) pour, par exemple, mettre en cache des images.

2
Sietse