Cambiar el tamaño de la imagen a ancho completo y altura variable con Picasso

84

Tengo un listView con un adaptador que contiene un ImageViewtamaño variable (ancho y alto). Necesito cambiar el tamaño de la carga de imágenes con Picasso al ancho máximo de diseño y una altura variable dada por la relación de aspecto de la imagen.

He comprobado esta pregunta: Cambiar el tamaño de la imagen a ancho completo y alto fijo con Picasso

El fit()funciona pero no he encontrado nada para mantener la relación de aspecto de la imagen.

Este código funciona parcialmente si arreglé la altura en el diseño del adaptador:

Picasso.with(this.context).load(message_pic_url)
.placeholder(R.drawable.profile_wall_picture)
.fit().centerInside()
.into(holder.message_picture);

Pero genera espacios en blanco entre las imágenes del listView porque las imágenes pueden ser que no tengan esa altura.

Gracias por adelantado.

wendigo
fuente

Respuestas:

89

A partir de Picasso 2.4.0, esta operación ahora es compatible directamente . Simplemente agregue una .resize()solicitud con una de las dimensiones como 0. Por ejemplo, para tener un ancho variable, su llamada se convertiría en:

Picasso.with(this.context)
       .load(message_pic_url)
       .placeholder(R.drawable.profile_wall_picture)
       .resize(0, holder.message_picture.getHeight()),
       .into(holder.message_picture);

Tenga en cuenta que esta llamada utiliza .getHeight()y, por lo tanto, asume que message_pictureya se ha medido. Si ese no es el caso, como cuando ha inflado una nueva vista en a ListAdapter, puede retrasar esta llamada hasta después de la medición agregando un OnGlobalLayoutListenera la vista:

holder.message_picture.getViewTreeObserver()
      .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            // Wait until layout to call Picasso
            @Override
            public void onGlobalLayout() {
                // Ensure we call this only once
                imageView.getViewTreeObserver()
                         .removeOnGlobalLayoutListener(this);


                Picasso.with(this.context)
                       .load(message_pic_url)
                       .placeholder(R.drawable.profile_wall_picture)
                       .resize(0, holder.message_picture.getHeight())
                       .into(holder.message_picture);
            }
        });
George Hilliard
fuente
8
con esta solución recibo el java.lang.IllegalArgumentException: At least one dimension has to be positive number.error en la rotación, esto está en el fragmento, ¿alguna idea de por qué puede suceder esto?
Lukasz 'Severiaan' Grela
1
Hum, si añado esta comprobación, no tengo este problema más, pero la imagen no es cambiar el tamaño ...
mrroboaat
2
@ Lukasz'Severiaan'Grela Tuve el mismo problema. Para corregir este ejemplo para que coincida con la pregunta original, debe cambiar los argumentos:.resize(holder.message_picture.getWidth(), 0)
Christiaan
4
Tú me diste la idea. Gracias. Para aquellos que quieran tener una imagen de ancho completo con altura variable, use: Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); int width = size.x; .resize(width, 0)
fahrulazmi
1
Utilicé este método, cuando llevas la aplicación a un segundo plano y vuelves, onGlobalLayout()no se llama y la imagen no se muestra.
Gokhan Arik
82

Me encontré con el mismo problema y me tomó un tiempo encontrar una solución, pero finalmente encontré algo que funciona para mí.

Primero cambié la llamada de Picasso a

Picasso.with(this.context).load(message_pic_url)
.placeholder(R.drawable.profile_wall_picture)
.into(holder.message_picture);

Eliminar el fity el centerInside. A continuación, debe agregar las siguientes líneas a ImageView en su XML

android:scaleType="fitStart"
android:adjustViewBounds="true"

Con suerte, también funcionará para usted.

drspaceboo
fuente
2
Gracias, pero esto no me funciona. No puedo ver las imágenes, obteniendo una advertencia de logcat sobre el tamaño del mapa de bits (el mensaje clásico: 2048x2048 es el tamaño máximo).
Wendigo
4
Siento oír eso. Ese sería el inconveniente de este método. No está haciendo que Picasso cambie el tamaño de la imagen en absoluto, simplemente cárguela a tamaño completo. Podría causar problemas de memoria.
drspaceboo
Muchas gracias. Funciona como un encanto, rápido y fácil;)
Foo
@drspaceboo ¿cuál es tu layout_widthy layout_heighten ImageView? Lo intento con match_parenty wrap_contentrespectivamente, pero no funciona :(
Vicky Chijwani
@VickyChijwani de memoria creo que lo tenía 0dpy match_parentcon un peso de 1pero no 100% seguro y no creo que tengamos esto en nuestra aplicación más.
drspaceboo
60

Finalmente lo resolví haciendo una transformación de Picasso, aquí está el fragmento:

    Transformation transformation = new Transformation() {

        @Override
        public Bitmap transform(Bitmap source) {
            int targetWidth = holder.message_picture.getWidth();

            double aspectRatio = (double) source.getHeight() / (double) source.getWidth();
            int targetHeight = (int) (targetWidth * aspectRatio);
            Bitmap result = Bitmap.createScaledBitmap(source, targetWidth, targetHeight, false);
            if (result != source) {
                // Same bitmap is returned if sizes are the same
                source.recycle();
            }
            return result;
        }

        @Override
        public String key() {
            return "transformation" + " desiredWidth";
        }
    };

    mMessage_pic_url = message_pic_url;

    Picasso.with(this.context)
        .load(message_pic_url)
        .error(android.R.drawable.stat_notify_error)
        .transform(transformation)
        .into(holder.message_picture, new Callback() {
            @Override
            public void onSuccess() {
                holder.progressBar_picture.setVisibility(View.GONE);
            }

            @Override
            public void onError() {
                Log.e(LOGTAG, "error");
                holder.progressBar_picture.setVisibility(View.GONE);
            }
    });

Esta línea es para personalizar con el ancho deseado:

int targetWidth = holder.message_picture.getWidth();

Además, este recorte incluye la devolución de llamada para cargar la ocultación y el error de Picasso integrado.

Si necesita más información para depurar cualquier error, DEBE implementar un oyente personalizado (constructor de Picasso) porque la onError Callbackinformación es "nula". Solo sabe que hay un error en el comportamiento de la interfaz de usuario.

Espero que esto ayude a alguien a ahorrar muchas horas.

wendigo
fuente
Parece que solo está reciclando la fuente si es el mismo que el resultado. ¿No querría reciclarlo independientemente y simplemente devolver el resultado?
Wenger
@Wenger, No, Picasso se queja si haces eso.
George Hilliard
¡¡Excelente!! Realmente genial
ElOjcar
Sí, esto funcionó. Pero en mi caso, tuve que establecer el ancho de ImageView en match_parent, o un ancho específico. "wrap_content" devuelve 0 (cero) en la transformación y genera una excepción.
angryITguy
Mientras se desplaza, a veces holder.message_picture.getWidth()devuelve 0 y provoca un error width and height must be > 0. ¿Alguna idea de cómo solucionar este error?
Shahood ul Hassan
9

May Accepted Answer es útil para todos, pero si está vinculando Multiple ViewHolderfor Multiple Views, puede reducir su código creando Class for Transformation y pasando ImageView desde ViewHolder.

/**
 * Created by Pratik Butani
 */
public class ImageTransformation {

    public static Transformation getTransformation(final ImageView imageView) {
        return new Transformation() {

            @Override
            public Bitmap transform(Bitmap source) {
                int targetWidth = imageView.getWidth();

                double aspectRatio = (double) source.getHeight() / (double) source.getWidth();
                int targetHeight = (int) (targetWidth * aspectRatio);
                Bitmap result = Bitmap.createScaledBitmap(source, targetWidth, targetHeight, false);
                if (result != source) {
                    // Same bitmap is returned if sizes are the same
                    source.recycle();
                }
                return result;
            }

            @Override
            public String key() {
                return "transformation" + " desiredWidth";
            }
        };
    }
}

Llamando desde ViewHolder:

Picasso.with(context).load(baseUrlForImage)
                     .transform(ImageTransformation.getTransformation(holder.ImageView1))
                     .error(R.drawable.ic_place_holder_circle)
                     .placeholder(R.drawable.ic_place_holder_circle)
                     .into(holder.mMainPhotoImageView1);

Espero que te ayude.

Pratik Butani
fuente
Gracias, gran solución para ImageView, una pregunta: ¿Podemos hacer esto para el tamaño de VideoView igual que ImageView pasado en el parámetro?
Vrajesh
@Pratik tengo una vista de reciclaje y cuando me desplazo rápido, obtuve una excepción: la transformación de transformación deseada El ancho se bloqueó con una excepción. Causado por: java.lang.IllegalArgumentException: el ancho y el alto deben ser> 0
Vrajesh
Mientras se desplaza, a veces imageView.getWidth()devuelve 0 y provoca un error width and height must be > 0. ¿Alguna idea de cómo solucionar este error?
Shahood ul Hassan
En ese caso, es posible que la URL de su imagen sea nula, así que verifíquela primero si es nula o no.
Pratik Butani
3
    Picasso.with(this).load(url).resize(1800, 1800).centerInside().into(secondImageView)

    <ImageView
        android:id="@+id/SecondImage"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentLeft="true"
        android:adjustViewBounds="true"
        android:layout_margin="10dp"
        android:visibility="gone"/>

Esto le ayudará con la altura variable de las imágenes para todos los dispositivos.

Kiran
fuente
0

Escribí un asistente simple que se encarga de agregar un oyente completo de diseño y llamar a (imageView) cuando se completa el proceso de diseño.

public class PicassoDelegate {

private RequestCreator mRequestCreator;

public PicassoDelegate(ImageView target, RequestCreator requestCreator) {
    if (target.getWidth() > 0 && target.getHeight() > 0) {
        complete(target, requestCreator);
    } else {
        mRequestCreator = requestCreator;
        target.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                v.removeOnLayoutChangeListener(this);
                complete((ImageView) v, mRequestCreator);
            }
        });

    }

}

private void complete(ImageView target, RequestCreator requestCreator) {
    if (target.getWidth() > 0 && target.getHeight() > 0) {
        requestCreator.resize(target.getWidth(), target.getHeight());
    }

    requestCreator.into(target);
}

}

Para que pueda usarlo fácilmente así, por ejemplo, en onViewCreated () del fragmento

new PicassoDelegate(customerPhoto, Picasso.with(getActivity()).load(user.getPhotoUrl()).centerCrop());
Dmytro
fuente
0
imageView.post(new Runnable() {
      @Override public void run() {
        Picasso.with(context)
            .resize(0, imageView.getHeight())
            .onlyScaleDown()
            .into(imageView, new ImageCallback(callback, null));
      }
    });
Igor Bykov
fuente
0
public class CropSquareTransformation implements Transformation {

  private int mWidth;
  private int mHeight;

  @Override public Bitmap transform(Bitmap source) {
    int size = Math.min(source.getWidth(), source.getHeight());

    mWidth = (source.getWidth() - size) / 2;
    mHeight = (source.getHeight() - size) / 2;

    Bitmap bitmap = Bitmap.createBitmap(source, mWidth, mHeight, size, size);
    if (bitmap != source) {
      source.recycle();
    }

    return bitmap;
  }

  @Override public String key() {
    return "CropSquareTransformation(width=" + mWidth + ", height=" + mHeight + ")";
  }

Más transformaciones: https://github.com/wasabeef/picasso-transformations

Pablo Cegarra
fuente
¿Cuál debería ser el layout_widthy layout_heightdel ImageViewen este caso?
Shahood ul Hassan
0

extienda ImageView y luego anule el método onMeasure como el siguiente.

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
        Drawable d = getDrawable();

        if(d!=null && fittingType == FittingTypeEnum.FIT_TO_WIDTH){
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = (int) Math.ceil((float) width * (float) d.getIntrinsicHeight() / (float) d.getIntrinsicWidth());
            setMeasuredDimension(width, height);
        }else{
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
Jinichi
fuente
0

En realidad, estaba ingresando mientras cargaba la imagen en CustomImageView que tenía funcionalidad con zoom

El error fue

java.lang.RuntimeException: Transformation transformation desiredWidth crashed with exception.

Lo resolví editando el código dado a partir de la respuesta aceptada. Obtuve el ancho máximo de mi pantalla como si el ancho de mi vista de imagen ya fuera match_parent.

if (! imgUrl.equals ("")) {

        DisplayMetrics displayMetrics = new DisplayMetrics();
        ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        int height = displayMetrics.heightPixels;
        int width = displayMetrics.widthPixels;

        Picasso.with(context).load(imgUrl)
                .transform(getTransformation(width, imageView))
                .into(imageView, new Callback() {
                    @Override
                    public void onSuccess() {
                        if (progressBar != null) {
                            progressBar.setVisibility(View.GONE);
                        }
                    }

                    @Override
                    public void onError() {
                        if (progressBar != null) {
                            progressBar.setVisibility(View.GONE);
                        }
                    }
                });
    }

    public static Transformation getTransformation(final int width, final ImageView imageView) {
        return new Transformation() {
            @Override
            public Bitmap transform(Bitmap source) {
                int targetWidth = width;
                double aspectRatio = (double) source.getHeight() / (double) source.getWidth();
                int targetHeight = (int) (targetWidth * aspectRatio);
                Bitmap result = Bitmap.createScaledBitmap(source, targetWidth, targetHeight, false);
                if (result != source) {
                    // Same bitmap is returned if sizes are the same
                    source.recycle();
                }
                return result;
            }

            @Override
            public String key() {
                return "transformation" + " desiredWidth";
            }
        };
    }
Vivek Barai
fuente
0
Picasso.get()
.load(message_pic_url)
.fit()
.centerCrop()
.placeholder(R.drawable.profile_wall_picture)
.into(holder.message_picture);

Prueba este código, funcionó para mí.

Afinas EM
fuente
0
@Override
    protected void onResume() {
        super.onResume();

        imageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                loadImageIfReady();
            }
        });

    }

    private void loadImageIfReady() {
        if (imageView.getMeasuredWidth() <= 0 || mPayload == null)
            this.finish();    // if not ready GTFO

        Picasso.with(this)
                    .load(mPayload)
                    .resize(imageView.getMeasuredWidth(), imageView.getMeasuredWidth())
                    .centerInside()
                    .into(imageView);


    }
Avinash
fuente