Ändern Sie die Bildgröße mit Picasso auf volle Breite und variable Höhe

84

Ich habe eine listView mit einem Adapter, ImageViewder eine variable Größe (Breite und Höhe) enthält. Ich muss die Größe der mit Picasso geladenen Bilder auf die maximale Breite des Layouts und eine variable Höhe ändern, die durch das Seitenverhältnis des Bildes gegeben ist.

Ich habe diese Frage überprüft: Ändern Sie die Bildgröße mit Picasso auf volle Breite und feste Höhe

Die fit()Arbeiten, aber ich habe nichts gefunden, um das Seitenverhältnis des Bildes zu halten.

Dieser Code funktioniert teilweise, wenn ich die Höhe im Layout des Adapters festgelegt habe:

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

Es werden jedoch Leerzeichen zwischen den Bildern der listView generiert, da die Bilder möglicherweise nicht so hoch sind.

Danke im Voraus.

Wendigo
quelle

Antworten:

87

Ab Picasso 2.4.0 wird dieser Vorgang jetzt direkt unterstützt . Fügen Sie einfach eine .resize()Anfrage mit einer der Dimensionen als hinzu 0. Um beispielsweise eine variable Breite zu haben, würde Ihr Anruf wie folgt lauten:

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

Beachten Sie, dass dieser Aufruf verwendet .getHeight()und daher davon ausgeht, dass der message_picturebereits gemessen wurde. Wenn dies nicht der Fall ist, z. B. wenn Sie eine neue Ansicht in a aufgeblasen haben ListAdapter, können Sie diesen Aufruf bis nach der Messung verzögern, indem Sie OnGlobalLayoutListenerder Ansicht ein hinzufügen :

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
quelle
8
Mit dieser Lösung erhalte ich den java.lang.IllegalArgumentException: At least one dimension has to be positive number.Fehler bei der Rotation, dies ist im Fragment, irgendwelche Ideen, warum dies passieren kann?
Lukasz 'Severiaan' Grela
1
Hum, wenn ich diesen Scheck hinzufüge, habe ich dieses Problem nicht mehr, aber die Bildgröße wird nicht geändert ...
mrroboaat
2
@ Lukasz'Severiaan'Grela Ich hatte das gleiche Problem. Um dieses Beispiel so zu korrigieren, dass es mit der ursprünglichen Frage übereinstimmt, müssen Sie die Argumente umdrehen:.resize(holder.message_picture.getWidth(), 0)
Christiaan
4
Du hast mir die Idee gegeben. Vielen Dank. Für diejenigen, die ein Bild in voller Breite mit variabler Höhe haben möchten, verwenden Sie: Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); int width = size.x; .resize(width, 0)
fahrulazmi
1
Ich habe diese Methode verwendet, wenn Sie die App in den Hintergrund stellen und zurückkehren, sie onGlobalLayout()nicht aufgerufen wird und das Bild nicht angezeigt wird.
Gokhan Arik
81

Ich bin auf dasselbe Problem gestoßen und habe eine Weile gebraucht, um eine Lösung zu finden, aber ich bin schließlich auf etwas gestoßen, das für mich funktioniert.

Zuerst habe ich den Picasso-Anruf in geändert

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

Entfernen der fitund der centerInside. Als Nächstes müssen Sie der ImageView in Ihrem XML die folgenden Zeilen hinzufügen

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

Hoffentlich funktioniert es auch für Sie.

drspaceboo
quelle
2
Danke, aber das funktioniert bei mir nicht. Ich kann die Bilder nicht sehen und erhalte eine Logcat-Warnung über die Größe von Bitmap (die klassische Meldung: 2048 x 2048 ist die maximale Größe).
Wendigo
4
Tut mir leid das zu hören. Das wäre der Nachteil dieser Methode. Picasso kann das Bild überhaupt nicht in der Größe ändern. Laden Sie es einfach in voller Größe. Kann Speicherprobleme verursachen.
Drspaceboo
Vielen Dank. Es funktioniert wie ein Zauber, schnell und einfach;)
Foo
@drspaceboo was ist dein layout_widthund layout_heightauf der ImageView? Ich versuche , mit match_parentund wrap_contentjeweils aber es funktioniert nicht :(
Vicky Chijwani
@ VickyChijwani aus dem Gedächtnis Ich glaube ich hatte 0dpund match_parentmit einem Gewicht von 1aber nicht 100% sicher und ich glaube nicht, dass wir dies mehr in unserer Anwendung haben.
Drspaceboo
60

Schließlich habe ich es gelöst, indem ich eine Transformation von Picasso durchgeführt habe. Hier ist der Ausschnitt:

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

Diese Linie kann mit Ihrer gewünschten Breite angepasst werden:

int targetWidth = holder.message_picture.getWidth();

Zusätzlich enthält dieser Ausschnitt einen Rückruf zum Laden von verstecktem und fehlerzeichenbarem integriertem Picasso.

Wenn Sie weitere Informationen zum Debuggen von Fehlern benötigen, MÜSSEN Sie einen benutzerdefinierten Listener (Picasso Builder) implementieren, da die onError CallbackInformationen "null" sind. Sie wissen nur, dass ein Fehler im Verhalten der Benutzeroberfläche vorliegt.

Ich hoffe, das hilft jemandem, viele Stunden zu sparen.

Wendigo
quelle
Es sieht so aus, als würden Sie die Quelle nur dann recyceln, wenn sie mit dem Ergebnis übereinstimmt. Möchten Sie es nicht trotzdem recyceln und nur das Ergebnis zurückgeben?
Wenger
@ Wenger, nein, Picasso beschwert sich, wenn Sie das tun.
George Hilliard
Toll!! Wirklich toll
ElOjcar
Ja, das hat funktioniert. In meinem Fall musste ich die ImageView-Breite auf match_parent oder eine bestimmte Breite einstellen. "wrap_content" gibt 0 (Null) in der Transformation zurück und löst eine Ausnahme aus.
AngryITguy
Gibt beim Scrollen manchmal holder.message_picture.getWidth()0 zurück und verursacht einen Fehler width and height must be > 0. Irgendwelche Ideen, wie man diesen Fehler behebt?
Shahood ul Hassan
9

Mai akzeptierte Antwort ist nützlich für alle , aber wenn Sie mehr verbindlich ViewHolderfür Multiple Viewsdann können Sie Ihren Code reduzieren Klasse Erstellen für Transformation und vorbei Image aus 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";
            }
        };
    }
}

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

Hoffe es wird dir helfen.

Pratik Butani
quelle
Vielen Dank, großartige Lösung für ImageView, eine Frage: Können wir dies für die VideoView-Größe tun, die der im Parameter übergebenen ImageView entspricht?
Vrajesh
@Pratik Ich habe Recyclerview und wenn ich schnell scrolle, habe ich eine Ausnahme: Transformation Transformation erwünscht. Breite mit Ausnahme abgestürzt. Auslöser: java.lang.IllegalArgumentException: Breite und Höhe müssen> 0 sein
Vrajesh
Gibt beim Scrollen manchmal imageView.getWidth()0 zurück und verursacht einen Fehler width and height must be > 0. Irgendwelche Ideen, wie man diesen Fehler behebt?
Shahood ul Hassan
In diesem Fall ist Ihre Bild-URL möglicherweise null. Überprüfen Sie daher zuerst, ob null oder nicht.
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"/>

Dies hilft Ihnen bei der variablen Höhe der Bilder für alle Geräte

Kiran
quelle
0

Ich habe einen einfachen Helfer geschrieben, der sich darum kümmert, den Listener für das vollständige Layout hinzuzufügen und (imageView) aufzurufen, wenn der Layoutprozess abgeschlossen ist.

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

}}

So können Sie es einfach so verwenden, zum Beispiel in onViewCreated () von fragment.

new PicassoDelegate(customerPhoto, Picasso.with(getActivity()).load(user.getPhotoUrl()).centerCrop());
Dmytro
quelle
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
quelle
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 + ")";
  }

Weitere Transformationen: https://github.com/wasabeef/picasso-transformations

Pablo Cegarra
quelle
Was soll das layout_widthund layout_heightvon dem ImageViewin diesem Fall sein?
Shahood ul Hassan
0

Erweitern Sie ImageView und überschreiben Sie dann die onMeasure-Methode wie folgt.

@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
quelle
0

Eigentlich stieg ich beim Laden des Bildes in CustomImageView ein, das über eine zoombare Funktionalität verfügte

Fehler war

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

Ich habe es gelöst, indem ich den Code aus der akzeptierten Antwort bearbeitet habe. Ich habe die maximale Breite meiner Anzeige erhalten, als ob meine Bildansichtsbreite bereits match_parent wäre.

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
quelle
0
Picasso.get()
.load(message_pic_url)
.fit()
.centerCrop()
.placeholder(R.drawable.profile_wall_picture)
.into(holder.message_picture);

Versuchen Sie diesen Code, arbeitete für mich.

Afinas EM
quelle
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
quelle