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

我有一个 listView,它有一个适配器,其中包含可变大小(宽度和高度)的 ImageView。我需要调整图片加载与毕加索的最大宽度的布局和一个可变的高度给予图片的纵横比。

我检查过这个问题: 使用毕加索将图像调整到全宽和固定高度

fit()工作,但我没有找到任何保持图片的纵横比。

如果我在适配器的布局中固定了高度,这段代码会部分工作:

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

但是它会在 listView 的图片之间生成空格,因为图片可能没有那么高。

先谢谢你。

89005 次浏览

I came across the same issue and it took me a while to track down a solution but I finally came across something that works for me.

Firstly I changed the Picasso call to

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

Removing the fit and the centerInside. Next you need to add the following lines to the ImageView in your XML

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

Hopefully it will work for you as well.

Finally I solved it doing a transformation of Picasso, here is the snippet:

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

This line is for customize with your desired width:

int targetWidth = holder.message_picture.getWidth();

Additionally this snipped include Callback for loading hide and error drawable built-in Picasso.

If you need more information to debug any error, you MUST implement a custom listener (Picasso builder) beacuse the onError Callback information is "null". You only know that there is an error for UI behavior.

I hope this helps someone to save many hours.

As of Picasso 2.4.0, this operation is now directly supported. Simply add a .resize() request with one of the dimensions as 0. For example, to have a variable width, your call would become:

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

Note that this call uses .getHeight() and therefore assumes the message_picture has already been measured. If that isn't the case, such as when you have inflated a new view in a ListAdapter, you can delay this call until after measurement by adding an OnGlobalLayoutListener to the view:

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

I've wrote simple helper that take care about adding layout complete listener and call into(imageView) when layout process complete.

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 you can easily use it like this for example in fragment's onViewCreated()

new PicassoDelegate(customerPhoto, Picasso.with(getActivity()).load(user.getPhotoUrl()).centerCrop());

May Accepted Answer is Useful to all but If you are binding Multiple ViewHolder for Multiple Views then you can reduce your code by Creating Class for Transformation and passing ImageView from 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";
}
};
}
}

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

Hope it will Help you.

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

This will help you with variable height of images for all devices

imageView.post(new Runnable() {
@Override public void run() {
Picasso.with(context)
.resize(0, imageView.getHeight())
.onlyScaleDown()
.into(imageView, new ImageCallback(callback, null));
}
});
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 + ")";
}

More transformations: https://github.com/wasabeef/picasso-transformations

extend ImageView then override onMeasure method like the following.

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

Actually i was getting in while loading image in CustomImageView which had zoomable functionality

Error was

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

I solved it by editing code given from accepted answer i got the max width of my display as if my imageview width was already 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";
}
};
}
Picasso.get()
.load(message_pic_url)
.fit()
.centerCrop()
.placeholder(R.drawable.profile_wall_picture)
.into(holder.message_picture);

Try this code, Worked for me.

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




}

I think the value fitXY will work for you, here is an example that worked for me :

    <ImageView
android:id="@+id/imageView_answer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:scaleType="fitXY"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@string/answer_image" />