Zmień rozmiar obrazu do pełnej szerokości i zmiennej wysokości za pomocą Picassa

84

Mam listView z adapterem, który zawiera ImageViewzmienny rozmiar (szerokość i wysokość). Potrzebuję zmienić rozmiar zdjęć ładowanych za pomocą Picassa do maksymalnej szerokości układu i zmiennej wysokości określonej przez współczynnik kształtu obrazu.

Sprawdziłem to pytanie: Zmień rozmiar obrazu na pełną szerokość i stałą wysokość za pomocą Picassa

fit()Działa, ale nie znalazłem nic, aby utrzymać współczynnik kształtu obrazu.

Ten kod częściowo działa, jeśli naprawiłem wysokość w układzie adaptera:

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

Ale generuje puste spacje między obrazami listView, ponieważ obrazy mogą nie mieć tej wysokości.

Z góry dziękuję.

wendigo
źródło

Odpowiedzi:

89

Od wersji Picasso 2.4.0 ta operacja jest teraz obsługiwana bezpośrednio . Po prostu dodaj .resize()zapytanie z jednym z wymiarów jako 0. Na przykład, aby mieć zmienną szerokość, wywołanie będzie wyglądać następująco:

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

Należy pamiętać, że to wezwanie używa, .getHeight()a zatem zakłada, że message_picturezostało już zmierzone. Jeśli tak nie jest, na przykład gdy nadmuchałeś nowy widok w a ListAdapter, możesz opóźnić to wywołanie do zakończenia pomiaru, dodając OnGlobalLayoutListenerdo widoku:

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
źródło
8
przy takim rozwiązaniu wyskakuje mi java.lang.IllegalArgumentException: At least one dimension has to be positive number.błąd przy rotacji, to jest we fragmencie jakieś pomysły jak to się może stać?
Łukasz 'Severiaan' Grela
1
Hum, jeśli dodam ten czek, nie mam już tego problemu, ale obraz nie
zmienia
2
@ Lukasz'Severiaan'Grela Miałem ten sam problem. Aby naprawić ten przykład tak, aby pasował do pierwotnego pytania, musisz odwrócić argumenty:.resize(holder.message_picture.getWidth(), 0)
Christiaan,
4
Dałeś mi pomysł. Dzięki. Ci, którzy chcą mieć obraz o pełnej szerokości i zmiennej wysokości, użyj: Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); int width = size.x; .resize(width, 0)
fahrulazmi
1
Użyłem tej metody, gdy przenosisz aplikację na tło i wracasz, onGlobalLayout()nie jest wywoływana i obraz się nie pojawia.
Gokhan Arik
82

Natknąłem się na ten sam problem i znalezienie rozwiązania zajęło mi trochę czasu, ale w końcu znalazłem coś, co działa dla mnie.

Najpierw zmieniłem połączenie Picassa na

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

Usuwanie fiti centerInside. Następnie musisz dodać następujące wiersze do ImageView w pliku XML

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

Miejmy nadzieję, że to zadziała również dla Ciebie.

drspaceboo
źródło
2
Dzięki, ale to nie działa dla mnie. Nie widzę zdjęć, otrzymuję ostrzeżenie logcat o rozmiarze mapy bitowej (klasyczny komunikat: 2048x2048 to maksymalny rozmiar).
wendigo
4
Przykro mi to słyszeć. To byłaby wada tej metody. Picasso w ogóle nie zmienia rozmiaru obrazu, po prostu załaduj go w pełnym rozmiarze. Może powodować problemy z pamięcią.
drspaceboo
Dziękuję Ci bardzo. Działa jak urok, szybko i łatwo;)
Foo
@drspaceboo jakie jest twoje layout_widthi layout_heightna ImageView? Próbuję odpowiednio z match_parenti, wrap_contentale to nie działa :(
Vicky Chijwani
@VickyChijwani z pamięci Myślę, że miałem 0dpiz match_parentwagą, 1ale nie jestem w 100% pewny i nie sądzę, że mamy to już w naszej aplikacji.
drspaceboo
60

Wreszcie rozwiązałem to, dokonując transformacji Picassa, oto fragment:

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

Ta linia służy do dostosowania żądanej szerokości:

int targetWidth = holder.message_picture.getWidth();

Dodatkowo to wycięte zawiera wywołanie zwrotne do wczytywania ukrywania i błędów do rysowania wbudowanego Picassa.

Jeśli potrzebujesz więcej informacji do debugowania dowolnego błędu, MUSISZ zaimplementować niestandardowy odbiornik (konstruktor Picassa), ponieważ onError Callbackinformacja ma wartość „null”. Wiesz tylko, że wystąpił błąd dotyczący zachowania interfejsu użytkownika.

Mam nadzieję, że pomoże to komuś zaoszczędzić wiele godzin.

wendigo
źródło
Wygląda na to, że recyklingujesz źródło tylko wtedy, gdy jest takie samo jak wynik. Czy nie chciałbyś go poddać recyklingowi i po prostu zwrócić wynik?
Wenger,
@Wenger, nie, Picasso narzeka, jeśli to zrobisz.
George Hilliard
Świetny!! Naprawdę świetne
ElOjcar
Tak, to zadziałało. Ale w mycase musiałem ustawić szerokość ImageView na match_parent lub określoną szerokość. „wrap_content” zwraca 0 (zero) w transformacji i zgłasza wyjątek.
angryITguy
Podczas przewijania czasami holder.message_picture.getWidth()zwraca 0 i powoduje błąd width and height must be > 0. Jakieś pomysły, jak naprawić ten błąd?
Shahood ul Hassan
9

Może zaakceptować odpowiedź jest przydatna dla wszystkich, ale jeśli wiążesz Multiple ViewHolderdla Multiple Views, możesz zredukować swój kod, tworząc klasę transformacji i przekazując ImageView z 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";
            }
        };
    }
}

Dzwonię z 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);

Mam nadzieję, że ci to pomoże.

Pratik Butani
źródło
Dzięki, świetne rozwiązanie dla ImageView, jedno pytanie: czy możemy to zrobić dla rozmiaru VideoView takiego samego, jak przekazany w parametrze ImageView?
Vrajesh
@Pratik Mam widok recyklingu i podczas szybkiego przewijania dostałem wyjątek: Transformacja transformacji requiredWidth uległa awarii z wyjątkiem. Spowodowane przez: java.lang.IllegalArgumentException: szerokość i wysokość muszą być> 0
Vrajesh
Podczas przewijania czasami imageView.getWidth()zwraca 0 i powoduje błąd width and height must be > 0. Jakieś pomysły, jak naprawić ten błąd?
Shahood ul Hassan
W takim przypadku może być adres URL Twojego obrazu, więc najpierw sprawdź, czy ma wartość null, czy nie.
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"/>

Pomoże Ci to przy zmiennej wysokości obrazów dla wszystkich urządzeń

Kiran
źródło
0

Napisałem prostego pomocnika, który dba o dodanie odbiornika pełnego układu i wywołanie (imageView) po zakończeniu procesu układu.

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

}

Możesz więc łatwo użyć tego w ten sposób, na przykład w onViewCreated () fragmentu

new PicassoDelegate(customerPhoto, Picasso.with(getActivity()).load(user.getPhotoUrl()).centerCrop());
Dmytro
źródło
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
źródło
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 + ")";
  }

Więcej przekształceń: https://github.com/wasabeef/picasso-transformations

Pablo Cegarra
źródło
Jaka powinna być layout_widthi layout_heightz ImageViewtą sprawą?
Shahood ul Hassan
0

przedłużyć ImageView, a następnie zastąpić metodę onMeasure, jak poniżej.

@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
źródło
0

Właściwie wchodziłem podczas ładowania obrazu w CustomImageView, który miał funkcję powiększania

Błąd był

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

Rozwiązałem to, edytując kod podany z zaakceptowanej odpowiedzi, otrzymałem maksymalną szerokość mojego ekranu, tak jakby moja szerokość widoku obrazu była już 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
źródło
0
Picasso.get()
.load(message_pic_url)
.fit()
.centerCrop()
.placeholder(R.drawable.profile_wall_picture)
.into(holder.message_picture);

Wypróbuj ten kod, pracował dla mnie.

Afinas EM
źródło
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
źródło