Измените размер изображения до полной ширины и переменной высоты с помощью Picasso


84

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

Я проверил этот вопрос: изменить размер изображения до полной ширины и фиксированной высоты с помощью 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.ошибку при вращении, это во фрагменте, какие-либо идеи относительно того, почему это может произойти?
Лукаш 'Severiaan' Грела

1
Хм, если я добавлю эту проверку, у меня больше не будет этой проблемы, но размер изображения не
изменится

2
@ Lukasz'Severiaan'Grela У меня была такая же проблема. Чтобы исправить этот пример, чтобы он соответствовал исходному вопросу, вам нужно перевернуть аргументы:.resize(holder.message_picture.getWidth(), 0)
Christiaan

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 о размере Bitmap (классическое сообщение: 2048x2048 - максимальный размер).
Вендиго

4
Жаль это слышать. Это было бы недостатком этого метода. Пикассо вообще не заставляет изменять размер изображения, просто загрузите его в полном размере. Может вызвать проблемы с памятью.
Кэсси

Большое спасибо. Работает как шарм, быстро и легко;)
Foo

@drspaceboo какой у вас layout_widthи layout_heightна ImageView? Я пытаюсь использовать match_parentи wrap_contentсоответственно, но это не работает :(
Вики Чижвани,

@VickyChijwani по памяти, я думаю, у меня был 0dpи match_parentс весом, 1но не на 100% уверен, и я не думаю, что у нас это больше есть в нашем приложении.
Кэсси

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

Кроме того, этот фрагмент включает обратный вызов для загрузки скрытого и встроенного Picasso с возможностью вывода ошибок.

Если вам нужна дополнительная информация для отладки какой-либо ошибки, вы ДОЛЖНЫ реализовать настраиваемый прослушиватель (построитель Picasso), потому что onError Callbackинформация имеет значение «null». Вы знаете только, что есть ошибка поведения пользовательского интерфейса.

Надеюсь, это поможет кому-то сэкономить много часов.


Похоже, вы перерабатываете только исходный код, если он совпадает с результатом. Разве вы не захотели бы переработать его все равно и просто вернуть результат?
Wenger

@Wenger, нет, Пикассо жалуется, если ты так поступишь.
Джордж Хиллиард

Большой!! Действительно здорово
ElOjcar

Да, это сработало. Но в моем случае мне пришлось установить ширину ImageView равной match_parent или определенной ширине. «wrap_content» возвращает 0 (ноль) в преобразовании и вызывает исключение.
angryITguy 01

При прокрутке иногда holder.message_picture.getWidth()возвращает 0 и вызывает ошибку width and height must be > 0. Есть идеи, как исправить эту ошибку?
Shahood ul Hassan

10

Май Принимается Ответ Полезно для всех , но если вы привязки нескольких 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, переданного в параметре?
Vrajesh

@Pratik У меня есть recyclerview, и при быстрой прокрутке у меня возникло исключение: преобразование трансформации желаемой ширины разбилось с исключением. Вызвано: java.lang.IllegalArgumentException: ширина и высота должны быть> 0
Vrajesh

При прокрутке иногда imageView.getWidth()возвращает 0 и вызывает ошибку width and height must be > 0. Есть идеи, как исправить эту ошибку?
Shahood ul Hassan

В этом случае, возможно, ваш URL-адрес изображения равен нулю, поэтому сначала проверьте его, если он равен нулю.
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"/>

Это поможет вам с переменной высотой изображений для всех устройств.


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.

Я решил это, отредактировав код, указанный в принятом ответе, я получил максимальную ширину моего дисплея, как если бы ширина моего изображения уже была 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);

Попробуйте этот код, работал у меня.


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.