Android ImageView将较小的图像缩放为宽度,并具有灵活的高度,而不会裁剪或变形


79

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

我有一个图像视图,其图像小于该视图。我想将图像缩放到屏幕的宽度,并调整ImageView的高度以按比例地反映图像的正确高度。

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

这导致图像以其原始大小(小于屏幕宽度)为中心,并在侧面留有边距。不好。

所以我加了

android:adjustViewBounds="true" 

效果一样,不好。我加了

android:scaleType="centerInside"

效果一样,不好。我更改centerInsidefitCenter。效果一样,不好。我改centerInsidecenterCrop

android:scaleType="centerCrop"

现在,最后,图像被缩放到屏幕的宽度-但在顶部和底部被裁剪!所以我改变了centerCropfitXY

android:scaleType="fitXY"

现在,图像被缩放到屏幕的宽度,但没有在y轴上缩放,从而导致图像失真

删除android:adjustViewBounds="true"无效。android:layout_gravity如在其他地方建议的那样,添加仍然无效。

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

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

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


你试过了android:scaleType="fitCenter"吗?
迈克尔·塞利

1
@ BrainSlugs83我有,它仍然对我有用。同样,提出了一个问题来确定询问者尝试了什么。不需要贪睡,尤其是发布后的七个月。
迈克尔·塞利

这是行不通的。 - 见下文; 另外,我已经尝试过并可以验证它是否有效(如果您尝试过并看到不同的结果,请在下面进行讨论并向我们展示我们做错了什么—我唯一可以得出的结论是,您误解了问题,或者没有尝试过)。
BrainSlugs83

Answers:


110

我已经通过创建一个包含在布局文件中的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。为什么不应该这样:) THx
Malachiasz

谢谢。很好的解决方案。足够容易地修改以基于heightMeasureSpec进行相同的操作。
speedynomads 2013年

1
是的,但是不是。如果我使用固定高度的ImageView和fitCenter,则图像不会模糊(但是固定高度对我来说不是解决方案)。如何避免模糊?
Geltrude 2013年

我一直在寻找这种解决方案好几个星期了!我想放大一张图片,但是效果很好
Soko 2014年

36

我和你有同样的问题。经过30分钟的尝试各种变化后,我以这种方式解决了问题。它为您提供了灵活的height,并且宽度已调整为父级宽度

<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
这里的关键是使用adjustViewBounds
user3690202

@ user3690202您说得对,它也解决了我的问题。谢谢
Parth Patel

22

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

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

令人失望。


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

编辑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 2012年

1
nasa.gov/images/content/715945main__NOB0093_4x3_516-387.jpg最重要的部分是,它似乎比Nexus 7的屏幕更小
芒迪

2
不好。我不知道高度-它是动态的。这将导致无法预料的失真。我的问题中的修改已经涵盖了这一点。抱歉-感谢您的努力!对Google感到羞耻!
Mundi 2012年

1
我知道这是一个选择,但我一直在寻找XML解决方案。不过,您应该将其编辑为答案。谢谢!
Mundi

8

这是Mark Martinsson出色解决方案的一小部分。

如果图像的宽度大于高度,则Mark的解决方案将在屏幕的顶部和底部保留空间。

下面通过首先比较宽度和高度来解决此问题:如果图像宽度> = height,则它将缩放高度以匹配屏幕高度,然后缩放宽度以保留宽高比。同样,如果图像高度>宽度,则它将缩放宽度以匹配屏幕宽度,然后缩放高度以保留宽高比。

换句话说,它完全满足以下定义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指定特定的高度,并让ImageView动态调整其宽度以保持其纵横比。
路加福音

0

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);
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.