Ridimensiona l'immagine a tutta larghezza e altezza variabile con Picasso


84

Ho un listView con un adattatore che contiene ImageViewdimensioni variabili (larghezza e altezza). Ho bisogno di ridimensionare le immagini caricate con Picasso alla larghezza massima del layout e un'altezza variabile data dalle proporzioni dell'immagine.

Ho verificato questa domanda: ridimensiona l'immagine a tutta larghezza e altezza fissa con Picasso

Il fit()funziona ma non ho trovato nulla di mantenere il rapporto di aspetto dell'immagine.

Questo codice funziona parzialmente se ho fissato l'altezza nel layout dell'adattatore:

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

Ma genera spazi vuoti tra le immagini del listView perché le immagini potrebbero non avere quell'altezza.

Grazie in anticipo.

Risposte:


89

A partire da Picasso 2.4.0, questa operazione è ora supportata direttamente . Aggiungi semplicemente una .resize()richiesta con una delle dimensioni come 0. Ad esempio, per avere una larghezza variabile, la tua chiamata diventerebbe:

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

Si noti che questa chiamata utilizza .getHeight()e quindi presuppone che message_picturesia già stato misurato. Se non è così, ad esempio quando hai gonfiato una nuova visualizzazione in a ListAdapter, puoi ritardare questa chiamata fino a dopo la misurazione aggiungendo un OnGlobalLayoutListeneralla visualizzazione:

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

8
con questa soluzione ricevo l' java.lang.IllegalArgumentException: At least one dimension has to be positive number.errore sulla rotazione, questo è nel frammento, qualche idea sul perché può accadere?
Lukasz 'Severiaan' Grela

1
Hum, se aggiungo questo controllo, non ho più questo problema ma l'immagine non viene ridimensionata ...
mrroboaat

2
@ Lukasz'Severiaan'Grela ho avuto lo stesso problema. Per correggere questo esempio in modo che corrisponda alla domanda originale, devi capovolgere gli argomenti:.resize(holder.message_picture.getWidth(), 0)
Christiaan

4
Mi hai dato l'idea. Grazie. Per coloro che desiderano avere un'immagine a tutta larghezza con altezza variabile, utilizzare: Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); int width = size.x; .resize(width, 0)
fahrulazmi

1
Ho usato questo metodo, quando porti l'app in background e torni, onGlobalLayout()non viene chiamato e l'immagine non viene visualizzata.
Gokhan Arik

82

Mi sono imbattuto nello stesso problema e mi ci è voluto un po 'per trovare una soluzione, ma alla fine ho trovato qualcosa che funziona per me.

Per prima cosa ho cambiato la chiamata di Picasso in

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

Rimozione di fite di centerInside. Successivamente è necessario aggiungere le seguenti righe a ImageView nel proprio XML

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

Spero che funzioni anche per te.


2
Grazie ma questo non funziona per me. Non riesco a vedere le immagini, ricevo l'avviso di logcat sulla dimensione di Bitmap (il messaggio classico: 2048x2048 è la dimensione massima).
wendigo

4
Mi dispiace sentirlo. Questo sarebbe lo svantaggio di questo metodo. Picasso non ridimensiona affatto l'immagine, basta caricarla a grandezza naturale. Potrebbe causare problemi di memoria.
Cassie

Grazie mille. Funziona a meraviglia, veloce e facile;)
Foo

@drspaceboo qual è il tuo layout_widthe layout_heightsu ImageView? Sto provando con match_parente wrap_contentrispettivamente ma non funziona :(
Vicky Chijwani

@VickyChijwani a memoria penso di averlo avuto 0dpe match_parentcon un peso di 1ma non sicuro al 100% e non penso che lo abbiamo più nella nostra applicazione.
Cassie

60

Finalmente l'ho risolto facendo una trasformazione di Picasso, ecco lo snippet:

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

Questa linea è per personalizzare con la larghezza desiderata:

int targetWidth = holder.message_picture.getWidth();

Inoltre, questo frammento include Callback per il caricamento di Hide e Error Drawable built-in Picasso.

Se hai bisogno di più informazioni per eseguire il debug di qualsiasi errore, DEVI implementare un listener personalizzato (Picasso builder) perché l' onError Callbackinformazione è "nulla". Sai solo che c'è un errore per il comportamento dell'interfaccia utente.

Spero che questo aiuti qualcuno a risparmiare molte ore.


Sembra che tu stia riciclando la fonte solo se è uguale al risultato. Non vorresti riciclarlo a prescindere e restituire semplicemente il risultato?
Wenger

@Wenger, No, Picasso si lamenta se lo fai.
George Hilliard

Grande!! Davvero fantastico
ElOjcar

Sì, ha funzionato. Ma nel mio caso ho dovuto impostare la larghezza di ImageView su match_parent o una larghezza specifica. "wrap_content" restituisce 0 (zero) nella trasformazione e solleva un'eccezione.
angryITguy

Durante lo scorrimento, a volte holder.message_picture.getWidth()restituisce 0 e provoca un errore width and height must be > 0. Qualche idea su come correggere questo errore?
Shahood ul Hassan

10

La risposta può essere accettata è utile per tutti, ma se si associa Multiple ViewHolderper Multiple, Viewsè possibile ridurre il codice creando una classe per la trasformazione e passando ImageView da 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";
            }
        };
    }
}

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

Spero che ti possa aiutare.


Grazie, ottima soluzione per ImageView, una domanda: possiamo farlo per le dimensioni di VideoView uguali a quelle del parametro passato da ImageView?
Vrajesh

@Pratik ho recyclerview e quando scorro velocemente, ho ricevuto un'eccezione: trasformazione della trasformazione desiderataLa larghezza si è bloccata con eccezione. Causato da: java.lang.IllegalArgumentException: larghezza e altezza devono essere> 0
Vrajesh

Durante lo scorrimento, a volte imageView.getWidth()restituisce 0 e provoca un errore width and height must be > 0. Qualche idea su come correggere questo errore?
Shahood ul Hassan

In tal caso, l'URL della tua immagine potrebbe essere nullo, quindi controllalo prima se nullo o meno.
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"/>

Questo ti aiuterà con l'altezza variabile delle immagini per tutti i dispositivi


0

Ho scritto semplici helper che si occupano di aggiungere listener completo di layout e chiamare in (imageView) quando il processo di layout è completo.

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

}

Quindi puoi facilmente usarlo in questo modo, ad esempio in onViewCreated () di fragment

new PicassoDelegate(customerPhoto, Picasso.with(getActivity()).load(user.getPhotoUrl()).centerCrop());

0
imageView.post(new Runnable() {
      @Override public void run() {
        Picasso.with(context)
            .resize(0, imageView.getHeight())
            .onlyScaleDown()
            .into(imageView, new ImageCallback(callback, null));
      }
    });

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 + ")";
  }

Altre trasformazioni: https://github.com/wasabeef/picasso-transformations


Quale dovrebbe essere il layout_widthe layout_heightdel ImageViewin questo caso?
Shahood ul Hassan

0

estendere ImageView quindi sovrascrivere il metodo onMeasure come segue.

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

0

In realtà stavo entrando durante il caricamento dell'immagine in CustomImageView che aveva funzionalità zoomabili

L'errore era

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

L'ho risolto modificando il codice fornito dalla risposta accettata ho ottenuto la larghezza massima del mio display come se la larghezza della mia visualizzazione dell'immagine fosse già 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";
            }
        };
    }

0
Picasso.get()
.load(message_pic_url)
.fit()
.centerCrop()
.placeholder(R.drawable.profile_wall_picture)
.into(holder.message_picture);

Prova questo codice, ha funzionato per me.


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


    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.