Android ImageView chia tỷ lệ hình ảnh nhỏ hơn theo chiều rộng với chiều cao linh hoạt mà không bị cắt hoặc biến dạng


79

Thường được hỏi, không bao giờ được trả lời (ít nhất là không theo cách có thể tái tạo).

Tôi có chế độ xem hình ảnh với hình ảnh nhỏ hơn chế độ xem. Tôi muốn chia tỷ lệ hình ảnh theo chiều rộng của màn hình và điều chỉnh chiều cao của ImageView để phản ánh chiều cao hình ảnh theo đúng tỷ lệ.

<ImageView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
/>

Điều này dẫn đến hình ảnh được căn giữa ở kích thước ban đầu (nhỏ hơn chiều rộng màn hình) với các lề ở bên cạnh. Không tốt.

Vì vậy, tôi đã thêm

android:adjustViewBounds="true" 

Hiệu ứng tương tự, không tốt. Tôi đã thêm

android:scaleType="centerInside"

Hiệu ứng tương tự, không tốt. Tôi đã thay đổi centerInsidethành fitCenter. Hiệu ứng tương tự, không tốt. Tôi đã thay đổi centerInsidethành centerCrop.

android:scaleType="centerCrop"

Bây giờ, cuối cùng, hình ảnh được thu nhỏ theo chiều rộng của màn hình - nhưng bị cắt ở trên và dưới! Vì vậy, tôi đã thay đổi centerCropthành fitXY.

android:scaleType="fitXY"

Bây giờ hình ảnh được thu nhỏ theo chiều rộng của màn hình nhưng không được thu nhỏ trên trục y, dẫn đến hình ảnh bị méo .

Loại bỏ android:adjustViewBounds="true"không có hiệu lực. Thêm một android:layout_gravity, như được đề xuất ở nơi khác, lại không có tác dụng.

Tôi đã thử các cách kết hợp khác - không có kết quả. Vì vậy, xin vui lòng có ai biết:

Làm cách nào để bạn thiết lập XML của ImageView để lấp đầy chiều rộng của màn hình, chia tỷ lệ hình ảnh nhỏ hơn để lấp đầy toàn bộ chế độ xem, hiển thị hình ảnh với tỷ lệ khung hình mà không bị biến dạng hoặc cắt xén?

CHỈNH SỬA: Tôi cũng đã thử đặt chiều cao số tùy ý. Điều này chỉ ảnh hưởng đến centerCropcài đặt. Nó sẽ làm biến dạng hình ảnh theo chiều dọc theo chiều cao xem.


Bạn đã thử android:scaleType="fitCenter"chưa?
Michael Celey 21/12/12

1
@ BrainSlugs83 Tôi có và nó vẫn hoạt động với tôi. Ngoài ra, câu hỏi được đặt ra để xác định những gì người hỏi đã thử. Không cần đến sự nhanh nhẹn, đặc biệt là không quá bảy tháng sau khi nó được đăng.
Michael Celey

Nó không hoạt động. -- Xem bên dưới; Ngoài ra, tôi đã thử nó và có thể xác minh rằng nó không hoạt động (nếu bạn đã thử nó và thấy kết quả khác nhau, vui lòng thảo luận bên dưới và cho chúng tôi biết chúng tôi đang làm gì sai - kết luận duy nhất mà tôi có thể đưa ra là bạn đã hiểu sai câu hỏi, hoặc chưa thử).
BrainSlugs83

Câu trả lời:


110

Tôi đã giải quyết vấn đề này bằng cách tạo một lớp java mà bạn đưa vào tệp bố cục của mình:

public class DynamicImageView extends ImageView {

    public DynamicImageView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        final Drawable d = this.getDrawable();

        if (d != null) {
            // ceil not round - avoid thin vertical gaps along the left/right edges
        final int width = MeasureSpec.getSize(widthMeasureSpec);
        final int height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth());
            this.setMeasuredDimension(width, height);
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}

Bây giờ, bạn sử dụng điều này bằng cách thêm lớp của bạn vào tệp bố cục của bạn:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <my.package.name.DynamicImageView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:scaleType="centerCrop"
        android:src="@drawable/about_image" />
</RelativeLayout>

4
+1 Giải pháp tuyệt vời, cũng hoạt động hoàn hảo cho Nexus 7. Và phần tốt nhất là nó cũng hoạt động chính xác trong Trình chỉnh sửa bố cục trong Eclipse.
Thật nực cười vào

Hoạt động trên Galaxy S4. Tại sao không nên nó :) THX
Malachiasz

Cảm ơn. Giải pháp tuyệt vời. Đủ dễ dàng sửa đổi để làm điều tương tự dựa trên heightMeasureSpec.
speedynomads

1
Có nhưng không ... Giải pháp này làm mờ hình ảnh. Nếu tôi sử dụng ImageView và fitCenter với chiều cao cố định, hình ảnh không bị mờ (nhưng chiều cao cố định không phải là giải pháp cho tôi). Làm thế nào để tránh blging?
Geltrude

Tôi đã tìm kiếm giải pháp này trong nhiều tuần, kudo! Tôi muốn một hình ảnh cao cấp mặc dù nhưng công trình này hoàn hảo
Soko

36

Tôi đã có cùng một vấn đề, như của bạn. Sau 30 phút thử các biến thể khác nhau, tôi đã giải quyết được vấn đề theo cách này. Nó cung cấp cho bạn chiều cao linh hoạtchiều rộng được điều chỉnh theo chiều rộng chính

<ImageView
            android:id="@+id/imageview_company_logo"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:adjustViewBounds="true"
            android:scaleType="fitCenter"
            android:src="@drawable/appicon" />

4
Chìa khóa ở đây là sử dụng AdjustViewBounds
user3690202

@ user3690202 bạn nói đúng, nó cũng giải quyết được vấn đề của tôi. Cảm ơn
Parth Patel

22

Không có giải pháp khả thi nào trong tiêu chuẩn bố cục XML.

Cách đáng tin cậy duy nhất để phản ứng với kích thước hình ảnh động là sử dụng LayoutParamstrong mã.

Thật đáng thất vọng.


8
  android:scaleType="fitCenter"

Tính toán tỷ lệ sẽ duy trì tỷ lệ khung hình src ban đầu, nhưng cũng sẽ đảm bảo rằng src hoàn toàn phù hợp bên trong dst. Ít nhất một trục (X hoặc Y) sẽ khớp chính xác. Kết quả được căn giữa bên trong dst.

biên tập:

Vấn đề ở đây là layout_height="wrap_content"không "cho phép" hình ảnh mở rộng. Bạn sẽ phải đặt kích thước cho nó, cho sự thay đổi đó

  android:layout_height="wrap_content"

đến

  android:layout_height="100dp"  // or whatever size you want it to be

sửa2:

hoạt động tốt:

<ImageView
    android:id="@+id/imageView1"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:scaleType="fitCenter"
    android:src="@drawable/img715945m" />

2
Điều này dẫn đến phiên bản đầu tiên của hình ảnh: căn giữa, không được mở rộng theo chiều ngang, với lề ở cả hai bên. Không tốt.
Mundi

2
Điều này không hoạt động. Nó dẫn đến một hình ảnh có chiều cao chính xác, nhưng không được mở rộng theo chiều ngang. Không tốt. Cũng xem chỉnh sửa của tôi trong câu hỏi.
Mundi

1
nasa.gov/images/content/715945main__NOB0093_4x3_516-387.jpg Phần quan trọng là nó có vẻ nhỏ hơn màn hình của Nexus 7.
Mundi

2
Không tốt. Tôi không biết chiều cao - nó là động. Nó sẽ dẫn đến những biến dạng khó lường. Chỉnh sửa trong câu hỏi của tôi đã bao gồm điều này. Xin lỗi - nhưng cảm ơn vì những nỗ lực của bạn! Thật xấu hổ trên Google!
Mundi

1
Tôi biết rằng đây là một tùy chọn, nhưng tôi đang tìm kiếm một giải pháp XML . Tuy nhiên, bạn nên chỉnh sửa điều này thành câu trả lời của mình. Cảm ơn!
Mundi

8

Đây là một bổ sung nhỏ cho giải pháp tuyệt vời của Mark Martinsson.

Nếu chiều rộng hình ảnh của bạn lớn hơn chiều cao của nó, thì giải pháp của Mark sẽ để lại khoảng trống ở đầu và cuối màn hình.

Phần dưới đây khắc phục điều này bằng cách trước tiên so sánh chiều rộng và chiều cao: nếu chiều rộng hình ảnh> = chiều cao, thì nó sẽ chia tỷ lệ chiều cao để khớp với chiều cao màn hình, sau đó chia tỷ lệ chiều rộng để giữ nguyên tỷ lệ khung hình. Tương tự, nếu chiều cao hình ảnh> chiều rộng, thì nó sẽ chia tỷ lệ chiều rộng để khớp với chiều rộng màn hình và sau đó chia tỷ lệ chiều cao để bảo toàn tỷ lệ khung hình.

Nói cách khác, nó thỏa mãn đúng định nghĩa của scaleType="centerCrop":

http://developer.android.com/reference/android/widget/ImageView.ScaleType.html

Chia tỷ lệ hình ảnh một cách đồng nhất (duy trì tỷ lệ co của hình ảnh) để cả hai kích thước (chiều rộng và chiều cao) của hình ảnh sẽ bằng hoặc lớn hơn kích thước tương ứng của chế độ xem (trừ phần đệm).

package com.mypackage;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;

public class FixedCenterCrop extends ImageView
{
    public FixedCenterCrop(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)
    {
        final Drawable d = this.getDrawable();

        if(d != null) {
            int height = MeasureSpec.getSize(heightMeasureSpec);
            int width = MeasureSpec.getSize(widthMeasureSpec);

            if(width >= height)
                height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth());
            else
                width = (int) Math.ceil(height * (float) d.getIntrinsicWidth() / d.getIntrinsicHeight());

            this.setMeasuredDimension(width, height);

        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}

Giải pháp này tự động hoạt động ở chế độ dọc hoặc ngang. Bạn tham chiếu nó trong bố cục của mình giống như bạn làm trong giải pháp của Mark. Ví dụ:

<com.mypackage.FixedCenterCrop
    android:id="@+id/imgLoginBackground"
    android:src="@drawable/mybackground"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:scaleType="centerCrop" />

3

Tôi gặp phải vấn đề tương tự, nhưng với chiều cao cố định, chiều rộng được chia tỷ lệ vẫn giữ nguyên tỷ lệ khung hình ban đầu của hình ảnh. Tôi đã giải quyết nó thông qua một bố cục tuyến tính có trọng số. Hy vọng bạn có thể sửa đổi nó cho nhu cầu của bạn.

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/image"
        android:layout_width="0px"
        android:layout_height="180dip"
        android:layout_weight="1.0"
        android:adjustViewBounds="true"
        android:scaleType="fitStart" />

</LinearLayout>

Giải pháp này làm việc tuyệt vời cho tôi. Nó cho phép tôi cung cấp cho ImageView của mình một chiều cao cụ thể và để ImageView tự động điều chỉnh chiều rộng của nó để duy trì tỷ lệ co của nó.
Luke

0

Phiên bản Kotlin của câu trả lời của Mark Martinsson :

class DynamicImageView(context: Context, attrs: AttributeSet) : AppCompatImageView(context, attrs) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    val drawable = this.drawable

    if (drawable != null) {
        //Ceil not round - avoid thin vertical gaps along the left/right edges
        val width = View.MeasureSpec.getSize(widthMeasureSpec)
        val height = Math.ceil((width * drawable.intrinsicHeight.toFloat() / drawable.intrinsicWidth).toDouble()).toInt()
        this.setMeasuredDimension(width, height)
    } else {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }
}

0

Một bổ sung cho giải pháp của Mark Matinsson. Tôi nhận thấy rằng một số hình ảnh của tôi bị thu nhỏ quá mức so với những hình ảnh khác. Vì vậy, tôi đã sửa đổi lớp để hình ảnh được thu nhỏ bằng hệ số tối đa. Nếu hình ảnh quá nhỏ để được chia tỷ lệ ở chiều rộng tối đa mà không bị mờ, nó sẽ ngừng chia tỷ lệ vượt quá giới hạn tối đa.

public class DynamicImageView extends ImageView {

    final int MAX_SCALE_FACTOR = 2;
    public DynamicImageView(final Context context) {
        super(context);
    }

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        final Drawable d = this.getDrawable();

        if (d != null) {
            // ceil not round - avoid thin vertical gaps along the left/right edges
            int width = View.MeasureSpec.getSize(widthMeasureSpec);
            if (width > (d.getIntrinsicWidth()*MAX_SCALE_FACTOR)) width = d.getIntrinsicWidth()*MAX_SCALE_FACTOR;
            final int height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth());
            this.setMeasuredDimension(width, height);
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}
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.