Thay đổi kích thước hình ảnh thành chiều rộng đầy đủ và chiều cao có thể thay đổi với Picasso


84

Tôi có một listView với bộ điều hợp có ImageViewkích thước thay đổi (chiều rộng và chiều cao). Tôi cần thay đổi kích thước các bức ảnh tải bằng Picasso thành chiều rộng tối đa của bố cục và chiều cao có thể thay đổi theo tỷ lệ khung hình của bức tranh.

Tôi đã kiểm tra câu hỏi này: Thay đổi kích thước hình ảnh thành chiều rộng đầy đủ và chiều cao cố định với Picasso

Các fit()tác phẩm nhưng tôi không tìm thấy gì để giữ tỷ lệ khung hình của bức tranh.

Mã này hoạt động một phần nếu tôi đã sửa chiều cao trong bố cục của bộ điều hợp:

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

Nhưng nó tạo ra khoảng trống giữa các ảnh của listView vì ảnh có thể không có chiều cao đó.

Cảm ơn trước.

Câu trả lời:


89

Kể từ Picasso 2.4.0, thao tác này hiện đã được hỗ trợ trực tiếp . Chỉ cần thêm một .resize()yêu cầu với một trong các thứ nguyên như 0. Ví dụ: để có chiều rộng thay đổi, lệnh gọi của bạn sẽ trở thành:

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

Lưu ý rằng cuộc gọi này sử dụng .getHeight()và do đó giả định rằng message_pictuređã được đo lường. Nếu không phải như vậy, chẳng hạn như khi bạn tăng cường chế độ xem mới trong a ListAdapter, bạn có thể trì hoãn cuộc gọi này cho đến sau khi đo bằng cách thêm một OnGlobalLayoutListenervào chế độ xem:

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
với giải pháp này, tôi nhận được java.lang.IllegalArgumentException: At least one dimension has to be positive number.lỗi khi quay, đây là trong phân đoạn, bất kỳ ý tưởng tại sao điều này có thể xảy ra?
Lukasz 'Severiaan' Grela,

1
Hum, Nếu tôi thêm việc kiểm tra này, tôi không có vấn đề này nữa nhưng hình ảnh không được thay đổi kích thước ...
mrroboaat

2
@ Lukasz'Severiaan'Grela Tôi cũng gặp vấn đề tương tự. Để sửa ví dụ này cho khớp với câu hỏi ban đầu, bạn phải xoay chuyển các đối số:.resize(holder.message_picture.getWidth(), 0)
Christiaan

4
Bạn đã cho tôi ý tưởng. Cảm ơn. Để những người muốn có hình ảnh toàn bộ chiều rộng với chiều cao biến, sử dụng: Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); int width = size.x; .resize(width, 0)
fahrulazmi

1
Tôi đã sử dụng phương pháp này, khi bạn đưa ứng dụng xuống nền và quay lại, ứng dụng onGlobalLayout()không được gọi và hình ảnh sẽ không hiển thị.
Gokhan Arik

82

Tôi đã gặp phải vấn đề tương tự và tôi đã mất một lúc để tìm ra giải pháp nhưng cuối cùng tôi đã tìm ra thứ phù hợp với mình.

Đầu tiên, tôi đã thay đổi cuộc gọi Picasso thành

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

Loại bỏ fitcenterInside. Tiếp theo, bạn cần thêm các dòng sau vào ImageView trong XML của bạn

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

Hy vọng rằng nó cũng sẽ làm việc cho bạn.


2
Cảm ơn nhưng điều này không hiệu quả với tôi. Tôi không thể xem ảnh, nhận được cảnh báo logcat về kích thước của Bitmap (thông báo cổ điển: 2048x2048 là kích thước tối đa).
wendigo

4
Rất tiếc khi biết điều đó. Đó sẽ là điểm hạn chế của phương pháp này. Picasso không bắt buộc phải thay đổi kích thước hình ảnh, chỉ cần tải nó ở kích thước đầy đủ. Có thể gây ra sự cố bộ nhớ.
drspaceboo

Cảm ơn rât nhiều. Nó hoạt động như một sự quyến rũ, nhanh chóng và dễ dàng;)
Foo vào

@drspaceboo là gì của bạn layout_widthlayout_heighttrên ImageView? Tôi đang thử với match_parentwrap_contenttương ứng nhưng nó không hoạt động :(
Vicky Chijwani

@VickyChijwani từ bộ nhớ Tôi nghĩ rằng tôi đã có 0dpmatch_parentvới trọng lượng 1nhưng không chắc chắn 100% và tôi không nghĩ rằng chúng tôi có điều này trong ứng dụng của mình nữa.
drspaceboo

60

Cuối cùng, tôi đã giải quyết nó bằng cách chuyển đổi Picasso, đây là đoạn mã:

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

Dòng này để tùy chỉnh với chiều rộng mong muốn của bạn:

int targetWidth = holder.message_picture.getWidth();

Ngoài ra, đoạn trích này bao gồm Gọi lại để tải ẩn và lỗi có thể vẽ được tích hợp sẵn trong Picasso.

Nếu bạn cần thêm thông tin để gỡ lỗi bất kỳ lỗi nào, bạn PHẢI triển khai trình xử lý tùy chỉnh (trình tạo Picasso) vì onError Callbackthông tin là "null". Bạn chỉ biết rằng có một lỗi đối với hành vi giao diện người dùng.

Tôi hy vọng điều này sẽ giúp ai đó tiết kiệm được nhiều giờ.


Có vẻ như bạn chỉ tái chế nguồn nếu nó giống với kết quả. Bạn có muốn tái chế nó bất kể và chỉ trả lại kết quả không?
Wenger

@Wenger, Không, Picasso phàn nàn nếu bạn làm vậy.
George Hilliard

Tuyệt quá!! Thực sự tuyệt vời
ElOjcar

Đúng, điều này đã hiệu quả. Nhưng trong mycase, tôi phải đặt chiều rộng ImageView thành match_parent hoặc một chiều rộng cụ thể. "wrap_content" trả về 0 (không) trong phép biến đổi và đưa ra một ngoại lệ.
tức giậnITguy

Trong khi cuộn, đôi khi holder.message_picture.getWidth()trả về 0 và nó gây ra lỗi width and height must be > 0. Bất kỳ ý tưởng làm thế nào để sửa chữa lỗi này?
Shahood ul Hassan

9

Có thể Chấp nhận Câu trả lời là Hữu ích cho tất cả nhưng Nếu bạn đang ràng buộc Nhiều ViewHoldercho Nhiều Viewsthì bạn có thể giảm mã của mình bằng cách Tạo Lớp cho Chuyển đổi và chuyển ImageView từ đó 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";
            }
        };
    }
}

Gọi từ 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);

Hy vọng nó sẽ giúp bạn.


Cảm ơn, giải pháp tuyệt vời cho ImageView, một câu hỏi: Chúng tôi có thể làm điều này cho kích thước VideoView giống như ImageView được truyền vào tham số không?
Vrajesh

@Pratik tôi có chế độ xem lại và khi cuộn nhanh, tôi có ngoại lệ: Chuyển đổi chuyển đổi mong muốn Chiều rộng bị lỗi ngoại lệ. Gây ra bởi: java.lang.IllegalArgumentException: chiều rộng và chiều cao phải> 0
Vrajesh

Trong khi cuộn, đôi khi imageView.getWidth()trả về 0 và nó gây ra lỗi width and height must be > 0. Bất kỳ ý tưởng làm thế nào để sửa chữa lỗi này?
Shahood ul Hassan

Trong trường hợp đó, có thể url hình ảnh của bạn là null vì vậy hãy kiểm tra nó trước nếu null hay không.
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"/>

Điều này sẽ giúp bạn với chiều cao thay đổi của hình ảnh cho tất cả các thiết bị


0

Tôi đã viết trình trợ giúp đơn giản quan tâm đến việc thêm trình nghe hoàn chỉnh bố cục và gọi vào (imageView) khi quá trình bố trí hoàn tất.

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

}

Vì vậy, bạn có thể dễ dàng sử dụng nó như thế này, chẳng hạn như trong onViewCreate () của phân mảnh

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

Các biến đổi khác: https://github.com/wasabeef/picasso-transformations


Điều gì nên là layout_widthlayout_heightcủa ImageViewtrong trường hợp này?
Shahood ul Hassan

0

mở rộng ImageView sau đó ghi đè phương thức onMeasure như sau.

@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

Trên thực tế, tôi đã vào trong khi tải hình ảnh trong CustomImageView có chức năng có thể thu phóng

Lỗi là

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

Tôi đã giải quyết nó bằng cách chỉnh sửa mã được đưa ra từ câu trả lời được chấp nhận, tôi đã nhận được chiều rộng tối đa của màn hình như thể chiều rộng lần xem hình ảnh của tôi đã là 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);

Hãy thử mã này, Làm việc cho tôi.


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


    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.