使用毕加索将图像调整为全宽和可变高度


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的图片之间生成空白,因为图片可能没有该高度。

提前致谢。

Answers:


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已经进行了测量。如果不是这种情况(例如,当您在中扩展了一个新视图时)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.旋转错误,这是在片段中,关于为什么会发生这种情况的任何想法?
Lukasz'Severiaan'Grela 2014年

1
哼,如果我添加此检查,我不再有这个问题,但是图像无法调整大小...
mrroboaat 2014年

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 2015年

1
我使用了这种方法,当您将应用程序带到后台再返回时,onGlobalLayout()不会被调用,并且图像也不会显示。
Gokhan Arik

82

我遇到了同样的问题,花了我一段时间才找到解决方案,但最终我遇到了对我有用的东西。

首先,我将毕加索的通话更改为

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

删除fitcenterInside。接下来,您需要将以下行添加到XML中的ImageView中

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

希望它也会为您服务。


2
谢谢,但这对我不起作用。我看不到图片,得到logcat关于位图大小的警告(经典消息:最大大小为2048x2048)。
wendigo 2014年

4
很抱歉听到这个消息。那将是这种方法的缺点。毕加索根本没有调整图像的大小,而只是将其以完整尺寸加载。可能导致内存问题。
drspaceboo 2014年

非常感谢你。它就像一个魅力,快速又轻松;)
Foo 2014年

@drspaceboo你是什么layout_widthlayout_height在ImageView上是什么?我正在尝试分别使用match_parent和,wrap_content但是它不起作用:(
Vicky Chijwani 2015年

@VickyChijwani从内存中,我想我有0dpmatch_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();

此外,此片段还包括用于加载隐藏和可绘制错误的内置毕加索的Callback。

如果您需要更多信息来调试任何错误,则必须实现自定义侦听器(Picasso构建器),因为该onError Callback信息为“空”。您只知道UI行为有误。

我希望这可以帮助某人节省很多时间。


看起来您只是在回收与结果相同的源。您是否要回收它,而只返回结果?
温格2014年

@Wenger,不,如果您这样做,毕加索会抱怨。
乔治·希利亚德2014年

大!!真的很棒
ElOjcar

是的,这行得通。但是在我的情况下,我必须将ImageView的宽度设置为match_parent或特定的宽度。“ wrap_content”在转换中返回0(零),并引发异常。
AngryITguy

滚动时,有时holder.message_picture.getWidth()返回0并导致错误width and height must be > 0。任何想法如何解决此错误?
Shahood ul Hassan,

9

可能接受的答案对所有人都有用,但是如果您将Multiple ViewHolderfor Multiple绑定,Views则可以通过创建Transformation类并从中传递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,当快速滚动时,出现异常:转换transformationWidth异常崩溃。由以下原因引起:java.lang.IllegalArgumentException:宽度和高度必须> 0
Vrajesh

滚动时,有时imageView.getWidth()返回0并导致错误width and height must be > 0。任何想法如何解决此错误?
Shahood ul Hassan,

在这种情况下,您的图片网址可能为null,因此请先检查是否为null。
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_widthlayout_heightImageView含义是什么?
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.

我通过编辑从接受的答案给出的代码来解决它,我得到了显示器的最大宽度,好像我的imageview宽度已经是match_parent一样。

如果(!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);


    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.