Android ImageView 将较小的图像缩放到具有灵活高度的宽度,而不会裁剪或扭曲

Posted

技术标签:

【中文标题】Android ImageView 将较小的图像缩放到具有灵活高度的宽度,而不会裁剪或扭曲【英文标题】:Android ImageView scale smaller image to width with flexible height without cropping or distortion 【发布时间】:2012-12-09 04:12:16 【问题描述】:

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

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

<ImageView
    android:layout_
    android:layout_
/>

这会导致图像以原始尺寸为中心(小于屏幕宽度),边距位于侧面。不好。

所以我加了

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"吗? @BrainSlugs83 我有,它仍然对我有用。此外,提出问题是为了确定提问者尝试了什么。不需要刻薄,尤其是在发布后的七个月内。 它不起作用。 - 见下文;另外,我已经尝试过并且可以验证它不起作用(如果您尝试过并看到不同的结果,请在下面讨论并向我们展示我们做错了什么——我能得出的唯一结论是您误解了问题,或者没有尝试过)。 【参考方案1】:

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

【讨论】:

【参考方案2】:

Mark Martinsson 的 answer 的 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)
    

【讨论】:

【参考方案3】:

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

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

【讨论】:

这里的关键是使用adjustViewBounds @user3690202 你是对的,它也解决了我的问题。谢谢【参考方案4】:

这是对 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_
    android:layout_
    android:layout_centerInParent="true"
    android:scaleType="centerCrop" />

【讨论】:

【参考方案5】:

我遇到了同样的问题,但高度固定,缩放宽度保持图像的原始纵横比。我通过加权线性布局解决了它。希望您可以根据需要对其进行修改。

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

    <ImageView
        android:id="@+id/image"
        android:layout_
        android:layout_
        android:layout_weight="1.0"
        android:adjustViewBounds="true"
        android:scaleType="fitStart" />

</LinearLayout>

【讨论】:

这个解决方案对我很有效。它允许我给我的 ImageView 一个特定的高度,并让 ImageView 动态调整它的宽度以保持它的纵横比。【参考方案6】:

我通过创建一个包含在布局文件中的 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_
    android:layout_ >

    <my.package.name.DynamicImageView
        android:layout_
        android:layout_
        android:scaleType="centerCrop"
        android:src="@drawable/about_image" />
</RelativeLayout>

【讨论】:

+1 很棒的解决方案,也适用于 Nexus 7。最好的部分是,它在 Eclipse 的布局编辑器中也可以正常工作。 适用于 Galaxy S4。为什么不应该:) THx 谢谢。很好的解决方案。很容易根据 heightMeasureSpec 进行修改。 是但不是...此解决方案使图像模糊。如果我使用具有固定高度的 ImageView 和 fitCenter 图像不会模糊(但固定高度不是我的解决方案)。如何避免模糊? 我一直在寻找这个解决方案好几个星期了,赞!我想放大一张图片,但这很完美【参考方案7】:

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

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

令人失望。

【讨论】:

请更改已接受的答案。现在错了。 感谢@CoolMind,但我已经很多年没有看过这个框架了。你更喜欢哪个答案? 感谢您的回复! kigibek 的解决方案帮助解决了我的特定问题。可能我说的不对,你可以随便选。【参考方案8】:
  android:scaleType="fitCenter"

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

编辑:

这里的问题是layout_height="wrap_content" 没有“允许”图像展开。您必须为它设置一个大小,以进行更改

  android:layout_

  android:layout_  // or whatever size you want it to be

edit2:

工作正常:

<ImageView
    android:id="@+id/imageView1"
    android:layout_
    android:layout_
    android:scaleType="fitCenter"
    android:src="@drawable/img715945m" />

【讨论】:

这导致图像的第一个版本:居中,不水平扩展,两边都有边距。不好。 这不起作用。它会生成具有精确高度的图像,但不会水平扩展。不好。另请参阅我在问题中的编辑。 nasa.gov/images/content/715945main__NOB0093_4x3_516-387.jpg 重要的是它似乎比 Nexus 7 的屏幕要小。 不好。我不知道高度 - 它是动态的。这将导致不可预测的扭曲。我的问题中的编辑已经涵盖了这一点。抱歉 - 但感谢您的努力!在 Google 上感到羞耻! 我知道这是一个选项,但我一直在寻找 XML 解决方案。不过,您应该将其编辑到您的答案中。谢谢!

以上是关于Android ImageView 将较小的图像缩放到具有灵活高度的宽度,而不会裁剪或扭曲的主要内容,如果未能解决你的问题,请参考以下文章

将较小的 UIWebView 显示为表单

HBase 链 MapReduce 作业,将较小的表广播到所有 Mapper

如何以编程方式确定如何将较小的盒子装入较大的包装中? [关闭]

在 git 中,如何避免将较小的更正合并到在原始内容的每一行之间插入一行新内容的分支中的冲突?

Android ImageView - 当adjustViewBounds=true时minWidth/minHeight没有效果

在放大和缩小时在imageview上移动对象