Змініть розмір зображення на повну ширину та змінну висоту за допомогою Пікассо


84

У мене є listView з адаптером, який містить ImageViewзмінний розмір (ширину та висоту). Мені потрібно змінити розмір завантажених зображень за допомогою Picasso до максимальної ширини макета та змінної висоти, заданої співвідношенням сторін зображення.

Я перевірив це питання: Змініть розмір зображення на повну ширину та фіксовану висоту за допомогою Пікассо

fit()Працює , але я не знайшов нічого , щоб зберегти співвідношення сторін зображення.

Цей код частково працює, якщо я зафіксував висоту в компонуванні адаптера:

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

Але це генерує порожні проміжки між зображеннями listView, тому що зображення можуть мати таку висоту.

Заздалегідь спасибі.

Відповіді:


89

Починаючи з Picasso 2.4.0, ця операція зараз підтримується безпосередньо . Просто додайте .resize()запит з одним із розмірів як 0. Наприклад, щоб мати змінну ширину, ваш дзвінок стане:

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

Зверніть увагу, що цей виклик використовує .getHeight()і тому припускає, що message_pictureвже виміряно. Якщо це не так, наприклад, коли ви надули новий вигляд у a ListAdapter, ви можете затримати цей дзвінок до закінчення вимірювання, додавши OnGlobalLayoutListenerдо перегляду:

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
з цим рішенням я отримую java.lang.IllegalArgumentException: At least one dimension has to be positive number.помилку при обертанні, це у фрагменті, будь-які ідеї, чому це може статися?
Лукаш Северян Грела

1
Хам, якщо я додаю цю перевірку, у мене вже немає цієї проблеми, але розмір зображення не змінюється ...
mrroboaat

2
@ Lukasz'Severiaan'Grela У мене була та сама проблема. Щоб виправити цей приклад, щоб він відповідав вихідному питанню, вам слід перевернути аргументи:.resize(holder.message_picture.getWidth(), 0)
Крістіан

4
Ви дали мені ідею. Дякую. Для тих, хто хоче мати зображення в повну ширину зі змінною висотою, використовуйте: Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); int width = size.x; .resize(width, 0)
fahrulazmi

1
Я використовував цей метод, коли ви переносите програму у фоновий режим і повертаєтесь, onGlobalLayout()не викликається, і зображення не відображається.
Гохан Арік

82

Я зіткнувся з тією ж проблемою, і мені знадобився час, щоб знайти рішення, але нарешті я натрапив на щось, що працює для мене.

Спочатку я змінив дзвінок Пікассо на

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

Видалення fitта centerInside. Далі вам потрібно додати наступні рядки до ImageView у вашому XML

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

Сподіваємось, це спрацює і на вас.


2
Дякую, але це не працює для мене. Я не бачу зображень, отримуючи попередження logcat про розмір растрової карти (класичне повідомлення: 2048x2048 - максимальний розмір).
wendigo

4
Прикро це чути. Це був би недолік цього методу. Пікассо взагалі не змінює розмір зображення, просто завантажте його в повному розмірі. Може спричинити проблеми з пам’яттю.
drspaceboo

Дуже дякую. Це працює як шарм, швидко і легко;)
Фу,

@drspaceboo що таке ваш layout_widthі layout_heightна ImageView? Я намагаюся match_parentі wrap_contentвідповідно, але це не працює :(
Вікі Чійвані

@VickyChijwani з пам’яті, я думаю, що мав це 0dpі match_parentз вагою, 1але не впевненим на 100%, і я не думаю, що у нас це вже є в нашому додатку.
drspaceboo

60

Нарешті я вирішив це, перетворивши Пікассо, ось фрагмент:

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

Цей рядок призначений для налаштування відповідно до бажаної ширини:

int targetWidth = holder.message_picture.getWidth();

Крім того, цей фрагмент включає зворотний виклик для завантаження вбудованого Пікассо з приховуванням та помилками.

Якщо вам потрібна додаткова інформація для налагодження будь-якої помилки, ПОВИНЕН реалізувати власний прослуховувач (конструктор Пікассо), оскільки onError Callbackінформація "нульова". Ви лише знаєте, що існує помилка поведінки інтерфейсу користувача.

Сподіваюся, це допоможе комусь заощадити багато годин.


Схоже, ви переробляєте джерело лише в тому випадку, якщо воно збігається з результатом. Чи не хотіли б ви переробити його незалежно і просто повернути результат?
Wenger

@Wenger, Ні, Пікассо скаржиться, якщо ти це зробиш.
Джордж Хілліард,

Чудово !! Дійсно чудово
ElOjcar

Так, це спрацювало. Але в моєму випадку мені довелося встановити для ImageView ширину match_parent або певну ширину. "wrap_content" повертає 0 (нуль) у перетворенні та викликає виняток.
angryITguy

Під час прокрутки іноді holder.message_picture.getWidth()повертає 0, і це викликає помилку width and height must be > 0. Будь-які ідеї, як виправити цю помилку?
Shahood ul Hassan

9

Травня Приймається Відповідь Корисно для всіх , але якщо ви прив'язки декількох ViewHolderдля Multiple , Viewsто ви можете зменшити ваш код Створення класу для трансформації і передачі ImageView з 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";
            }
        };
    }
}

Дзвінок з 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);

Сподіваюся, це допоможе вам.


Дякуємо, чудове рішення для ImageView, одне питання: чи можемо ми зробити це для VideoView такого ж розміру, як ImageView, переданий у параметрі?
Враєш,

@Pratik, у мене є reciclerview, і при швидкій прокрутці я отримав виняток: бажана трансформація трансформації Ширина зазнала аварії за винятком. Викликано: java.lang.IllegalArgumentException: ширина та висота повинні бути> 0
Враєш

Під час прокрутки іноді imageView.getWidth()повертає 0, і це викликає помилку width and height must be > 0. Будь-які ідеї, як виправити цю помилку?
Shahood ul Hassan

У такому випадку URL-адреса вашого зображення може бути нульовою, тому перевірте її спочатку, якщо вона нульова чи ні.
Пратік Бутані

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"/>

Це допоможе вам із змінною висотою зображень для всіх пристроїв


0

Я написав простий помічник, який подбає про додавання прослуховувача макета в повному обсязі та виклику в (imageView) після завершення процесу макетування.

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

}

Таким чином, ви можете легко використовувати його таким чином, наприклад, у фрагменті onViewCreated ()

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

Більше трансформацій: https://github.com/wasabeef/picasso-transformations


Що повинно бути layout_widthі layout_heightз ImageViewв цьому випадку?
Shahood ul Hassan

0

розширити ImageView, а потім перевизначити метод onMeasure, як показано нижче.

@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

Насправді я потрапляв під час завантаження зображення в CustomImageView, яке мало функціональність, яку можна змінити

Помилка була

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

Я вирішив це, відредагувавши код, отриманий із прийнятої відповіді, і отримав максимальну ширину мого дисплея, ніби моя ширина перегляду зображень вже відповідала_відповіді.

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

Спробуйте цей код, працював для мене.


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


    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.