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

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

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

标签: android imageview image-scaling


【解决方案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_width="fill_parent"
                  android:layout_height="fill_parent"
                  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_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_centerInParent="true"
          android:scaleType="centerCrop" />
      

      【讨论】:

        【解决方案5】:

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

        <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 动态调整它的宽度以保持它的纵横比。
        【解决方案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_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 很棒的解决方案,也适用于 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_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" />
        

        【讨论】:

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