Android ImageView如何缩放较小的图片以适应宽度,并具有灵活的高度而无需裁剪或扭曲

89

经常被问,但从未得到答案(至少没有以可重复的方式回答)。

我有一个图像视图,其中图像比视图。 我想将图像缩放到屏幕宽度,并调整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设置产生影响。它将根据视图高度垂直扭曲图像。


你尝试过使用 android:scaleType="fitCenter" 吗? - Michael Celey
1
@BrainSlugs83 我已经尝试过并且对我来说确实有效。此外,问题是为了确定提问者尝试了什么而提出的。没有必要使用讽刺的语气,特别是在发布七个月后。 - Michael Celey
它不起作用。请参见下文;此外,我已经尝试过并可以证实它不起作用(如果您尝试过并看到不同的结果,请在下面讨论并向我们展示我们做错了什么--我唯一能得出的结论是您误解了问题或者没有尝试)。 - BrainSlugs83
8个回答

114

我通过创建一个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>

4
+1 很棒的解决方案,对于Nexus 7也能完美运行。最好的部分是,它在Eclipse的布局编辑器中也能正确运行。 - Ridcully
适用于Galaxy S4。为什么不呢 :) 谢谢 - Malachiasz
谢谢。很棒的解决方案。容易修改以根据heightMeasureSpec执行相同的操作。 - speedynomads
1
是的,但不... 这个解决方案会使图像模糊。如果我使用ImageView和fitCenter并固定高度,则图像不会模糊(但固定高度对我来说不是一个解决方案)。如何避免模糊? - Geltrude
根据文档,仅从API18及以上版本开始进行升级处理,而此解决方案非常适用于早期版本。 http://developer.android.com/reference/android/widget/ImageView.html#setAdjustViewBounds%28boolean%29 - Gianluca P.
显示剩余2条评论

44

我曾经和你遇到同样的问题。尝试了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" />

6
关键在于使用adjustViewBounds。 - user3690202
@user3690202 你说得对,它也解决了我的问题。谢谢。 - Parth Patel

24

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

唯一可靠的应对动态图片尺寸的方法是在代码中使用LayoutParams

令人失望。


请更改已接受的答案。现在它是错误的。 - CoolMind
谢谢@CoolMind,但我已经好几年没看过这个框架了。你更喜欢哪个答案? - Mundi
谢谢您的回复!kigibek的解决方案对我的问题有所帮助。也许我不是正确的,您可以选择任何您喜欢的方式。 - CoolMind

9
  android:scaleType="fitCenter"

计算一个比例,它将保持原始src纵横比,但也确保src完全适合dst。至少一个轴(X或Y)将完全适合。结果位于dst的中心。

编辑:

问题在于layout_height="wrap_content"没有“允许”图像扩展。您需要为其设置大小,进行更改。

  android:layout_height="wrap_content"

to

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

编辑2:

功能正常:

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

2
这导致图像的第一个版本:居中,水平不扩展,两侧有边距。不好。 - Mundi
2
这个不起作用。它会生成一个确切高度的图像,但横向没有扩展。不好。还请查看我在问题中的编辑。 - Mundi
1
http://www.nasa.gov/images/content/715945main__NOB0093_4x3_516-387.jpg 的重要部分是它似乎比Nexus 7的屏幕小。 - Mundi
2
不好。我不知道高度 - 它是动态的。这将导致不可预测的扭曲。我的问题编辑已经涵盖了这一点。抱歉 - 但还是谢谢你的努力!Google真丢人! - Mundi
1
我知道这是一个选项,但我正在寻找一个XML解决方案。不过,你应该将它编辑到你的答案中。谢谢! - Mundi
显示剩余6条评论

8
这是对Mark Martinsson的优秀解决方案的小补充。
如果您的图像宽度大于其高度,则Mark的解决方案将在屏幕顶部和底部留下空间。
以下内容通过首先比较宽度和高度来修复此问题:如果图像宽度> =高度,则将缩放高度以匹配屏幕高度,然后缩放宽度以保持纵横比。同样,如果图像高度>宽度,则将缩放宽度以匹配屏幕宽度,然后缩放高度以保持纵横比。
换句话说,它正确满足了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 的解决方案中一样,在布局中引用它。例如:

<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
我遇到了同样的问题,但是图片保持原始宽高比缩放时有固定的高度。我通过使用加权线性布局来解决它。你可以希望按照你的需要进行修改。
<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>

这个解决方案对我非常有效。它允许我为我的ImageView设置特定的高度,并动态调整其宽度以保持其纵横比。 - Luke

1

这是Mark Martinsson的答案的Kotlin版本:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接