Android ImageView缩小图像到宽度,灵活高度,无裁剪或失真

时间:2012-12-21 15:12:12

标签: android imageview image-scaling

经常被问到,从未回答过(至少不是以可重复的方式回答)。

我的图像视图的图像比视图。我想将图像缩放到屏幕的宽度,并调整ImageView的高度以反映图像的比例正确高度。

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

这会导致图像以其原始大小为中心(小于屏幕宽度),边缘为边距。不好。

所以我添加了

android:adjustViewBounds="true" 

同样的效果,没有好处。我添加了

android:scaleType="centerInside"

同样的效果,没有好处。我将centerInside更改为fitCenter。同样的效果,没有好处。我将centerInside更改为centerCrop

android:scaleType="centerCrop"

现在,最后,图像缩放到屏幕宽度 - 但顶部和底部裁剪!所以我将centerCrop更改为fitXY

android:scaleType="fitXY"

现在图像缩放到屏幕宽度,但在y轴上缩放,导致扭曲图像。

删除android:adjustViewBounds="true"无效。如其他地方所建议的那样添加android:layout_gravity再次没有效果。

我尝试过其他组合 - 无济于事。所以,请有人知道:

如何设置ImageView的XML以填充屏幕宽度,缩放较小的图像以填充整个视图,显示具有宽高比的图像而不会失真或裁剪?

编辑:我也尝试设置任意数字高度。这仅对centerCrop设置有效。它会根据视图高度垂直扭曲图像。

8 个答案:

答案 0 :(得分:108)

我通过创建一个包含在layout-file中的java类来解决这个问题:

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

现在,您可以通过将您的类添加到布局文件来使用它:

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

答案 1 :(得分:32)

我和你的一样有同样的问题。在尝试不同变化30分钟后,我以这种方式解决了问题。它为您提供灵活高度宽度调整为父宽度

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

答案 2 :(得分:20)

XML布局标准中没有可行的解决方案。

对动态图像大小做出反应的唯一可靠方法是在代码中使用LayoutParams

令人失望。

答案 3 :(得分:8)

  android:scaleType="fitCenter"

计算将保持原始src宽高比的比例,但也将确保src完全适合dst。至少一个轴(X或Y)将完全适合。结果集中在dst内。

修改

这里的问题是layout_height="wrap_content"不是“允许”图像扩展。你必须为它做一个大小,为那个改变

  android:layout_height="wrap_content"

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

<强> EDIT2:

工作正常:

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

答案 4 :(得分:7)

这是Mark Martinsson优秀解决方案的一小部分。

如果您的图片宽度大于其高度,那么Mark的解决方案将在屏幕的顶部和底部留出空间。

下面通过首先比较宽度和高度来解决这个问题:如果图像宽度&gt; =高度,那么它将缩放高度以匹配屏幕高度,然后缩放宽度以保持宽高比。类似地,如果图像高度> width,然后它将缩放宽度以匹配屏幕宽度,然后缩放高度以保持宽高比。

换句话说,它恰当地满足scaleType="centerCrop"

的定义

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

  

均匀缩放图像(保持图像的纵横比)以便   图片的尺寸(宽度和高度)等于或   大于视图的相应维度(减去填充)。

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

此解决方案可自动以纵向或横向模式运行。您可以像在Mark的解决方案中一样在布局中引用它。 E.g:

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

答案 5 :(得分:3)

我遇到了同样的问题,但是使用固定的高度,缩放的宽度保持了图像的原始宽高比。我通过加权线性布局解决了这个问题。您可以根据需要修改它。

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

答案 6 :(得分:0)

Mark Matinsson解决方案的另外一个补充。我发现我的一些图像比其他图像过度缩放。所以我修改了类,以便通过最大因子缩放图像。如果图像太小而无法在最大宽度上缩放而不会变得模糊,则会停止缩放超过最大限制。

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

答案 7 :(得分:0)

Kotlin版Mark Martinsson的answer

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