【问题标题】:Scale Image to fill ImageView width and keep aspect ratio缩放图像以填充 ImageView 宽度并保持纵横比
【发布时间】:2013-08-07 06:59:25
【问题描述】:

我有一个GridViewGridView的数据是来自服务器的请求。

这是GridView中的项目布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/analysis_micon_bg"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingBottom="@dimen/half_activity_vertical_margin"
    android:paddingLeft="@dimen/half_activity_horizontal_margin"
    android:paddingRight="@dimen/half_activity_horizontal_margin"
    android:paddingTop="@dimen/half_activity_vertical_margin" >

    <ImageView
        android:id="@+id/ranking_prod_pic"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:contentDescription="@string/app_name"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/ranking_rank_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/ranking_prod_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/ranking_prod_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

我从服务器请求数据,获取图片url并将图片加载到Bitmap

public static Bitmap loadBitmapFromInputStream(InputStream is) {
    return BitmapFactory.decodeStream(is);
}

public static Bitmap loadBitmapFromHttpUrl(String url) {
    try {
        return loadBitmapFromInputStream((InputStream) (new URL(url).getContent()));
    } catch (Exception e) {
        Log.e(TAG, e.getMessage());
        return null;
    }
}

适配器中有getView(int position, View convertView, ViewGroup parent)方法的代码

Bitmap bitmap = BitmapUtil.loadBitmapFromHttpUrl(product.getHttpUrl());
prodImg.setImageBitmap(bitmap);

图片大小为210*210。我在 Nexus 4 上运行我的应用程序。图像确实填充了 ImageView 宽度,但 ImageView 高度没有缩放。 ImageView 不显示整个图像。

我该如何解决这个问题?

【问题讨论】:

    标签: android imageview


    【解决方案1】:

    不使用任何自定义类或库:

    <ImageView
        android:id="@id/img"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:scaleType="fitCenter" />
    

    scaleType="fitCenter"(省略时默认)

    • 将使其尽可能宽,并根据需要保持纵横比。

    scaleType="centerInside"

    • 如果src 的固有宽度小于父宽度
      将图像水平居中
    • 如果 src 的固有宽度大于父宽度
      将使其与父宽度一样宽,并按比例缩小以保持纵横比。

    使用android:srcImageView.setImage*方法都没有关系,关键可能是adjustViewBounds

    【讨论】:

    • android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="fitCenter" 成功了
    • @JamesTan fill_parent 宽度只是一个好习惯,固定大小也可以。
    • @TWiStErRob 我意识到它需要 android:adjustViewBounds="true" ,否则它不适合高度。是的,适合父母的宽度是完整版本
    • 在 API 19+ 上像魅力一样工作,但在 API 16 上却不行(经过测试)。 Alex Semeniuk 的自定义视图也适用于 API 16。
    • @F.Mysir whops ?,是的,不管是哪个问题,但对于传播良好实践,我完全同意。
    【解决方案2】:

    我喜欢 arnefm 的回答,但他犯了一个小错误(请参阅 cmets),我将尝试纠正:

    import android.content.Context;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.widget.ImageView;
    
    /**
     * ImageView that keeps aspect ratio when scaled
     */
    public class ScaleImageView extends ImageView {
    
      public ScaleImageView(Context context) {
        super(context);
      }
    
      public ScaleImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
      }
    
      public ScaleImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
      }
    
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        try {
          Drawable drawable = getDrawable();
          if (drawable == null) {
            setMeasuredDimension(0, 0);
          } else {
            int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
            int measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
            if (measuredHeight == 0 && measuredWidth == 0) { //Height and width set to wrap_content
              setMeasuredDimension(measuredWidth, measuredHeight);
            } else if (measuredHeight == 0) { //Height set to wrap_content
              int width = measuredWidth;
              int height = width *  drawable.getIntrinsicHeight() / drawable.getIntrinsicWidth();
              setMeasuredDimension(width, height);
            } else if (measuredWidth == 0){ //Width set to wrap_content
              int height = measuredHeight;
              int width = height * drawable.getIntrinsicWidth() / drawable.getIntrinsicHeight();
              setMeasuredDimension(width, height);
            } else { //Width and height are explicitly set (either to match_parent or to exact value)
              setMeasuredDimension(measuredWidth, measuredHeight);
            }
          }
        } catch (Exception e) {
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
      }
    
    }
    

    因此,如果(例如)放在 ScrollView 内部,您的 ImageView 将被正确缩放并且不会出现尺寸问题

    【讨论】:

    • thx,我找到了解决方案,就在 arnefm 的回答下方,即我添加的第一条评论。 there 是一个链接
    【解决方案3】:

    我曾经遇到过类似的问题。我通过制作自定义 ImageView 解决了这个问题。

    public class CustomImageView extends ImageView
    

    然后覆盖imageview的onMeasure方法。我相信我做了这样的事情:

        @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        try {
            Drawable drawable = getDrawable();
    
            if (drawable == null) {
                setMeasuredDimension(0, 0);
            } else {
                float imageSideRatio = (float)drawable.getIntrinsicWidth() / (float)drawable.getIntrinsicHeight();
                float viewSideRatio = (float)MeasureSpec.getSize(widthMeasureSpec) / (float)MeasureSpec.getSize(heightMeasureSpec);
                if (imageSideRatio >= viewSideRatio) {
                    // Image is wider than the display (ratio)
                    int width = MeasureSpec.getSize(widthMeasureSpec);
                    int height = (int)(width / imageSideRatio);
                    setMeasuredDimension(width, height);
                } else {
                    // Image is taller than the display (ratio)
                    int height = MeasureSpec.getSize(heightMeasureSpec);
                    int width = (int)(height * imageSideRatio);
                    setMeasuredDimension(width, height);
                }
            }
        } catch (Exception e) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    

    这将拉伸图像以适应屏幕,同时保持纵横比。

    【讨论】:

    • there。第一个答案
    • 嗯,这个答案并不准确。考虑设置android:layout_height="wrap_content" 的情况。在这种情况下,MeasureSpec.getSize(heightMeasureSpec) 将是 0,而您的 viewSideRatio 将导致 Infinity(不用怀疑,Java 浮点数允许这样的值。)在这种情况下,您的 heightwidth 都将是 @ 987654331@.
    • 对我来说,我需要将 (imageSideRatio >= viewSideRatio) 交换为 (imageSideRatio
    【解决方案4】:

    使用android:scaleType="centerCrop"

    【讨论】:

    • 不完全是问题的要求,但正是我需要的!
    • 你能详细说明@Jameson吗?您是指 Android 版本吗?
    • 如果我使用 centerCrop,图像会在顶部和底部被切掉一点。
    【解决方案5】:

    我做了与上述类似的事情,然后用头撞墙了几个小时,因为它在 RelativeLayout 内不起作用。我最终得到了以下代码:

    package com.example;
    
    import android.content.Context;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.widget.ImageView;
    
    public class ScaledImageView extends ImageView {
        public ScaledImageView(final Context context, final AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
            final Drawable d = getDrawable();
    
            if (d != null) {
                int width;
                int height;
                if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
                    height = MeasureSpec.getSize(heightMeasureSpec);
                    width = (int) Math.ceil(height * (float) d.getIntrinsicWidth() / d.getIntrinsicHeight());
                } else {
                    width = MeasureSpec.getSize(widthMeasureSpec);
                    height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth());
                }
                setMeasuredDimension(width, height);
            } else {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            }
        }
    }
    

    然后为了防止RelativeLayout 忽略测量的尺寸,我这样做了:

        <FrameLayout
            android:id="@+id/image_frame"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_below="@+id/something">
    
            <com.example.ScaledImageView
                android:id="@+id/image"
                android:layout_width="wrap_content"
                android:layout_height="150dp"/>
        </FrameLayout>
    

    【讨论】:

      【解决方案6】:

      FOR IMAGE VIEW(设置这些参数)

      android:layout_width     = "match_parent"
      android:layout_height    = "wrap_content"
      android:scaleType        = "fitCenter"
      android:adjustViewBounds = "true"
      

      现在无论图像的大小如何,它的宽度都将与父级匹配,高度将根据比例匹配。我已经对此进行了测试,我 100% 确定。

      我们想要这个 -->

      不是这个 -->

      // Results will be: 
      Image width -> stretched as match parent
      Image height -> according to image width (maximum to aspect ratio)
      
      // like the first one
      

      【讨论】:

        【解决方案7】:

        如果在ImageView中将图片设置为背景则不适用,需要在src(android:src)处设置。

        谢谢。

        【讨论】:

          【解决方案8】:

          你不需要任何 java 代码。你只需要:

          <ImageView
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:adjustViewBounds="true"
              android:scaleType="centerCrop" />
          

          宽度和高度的键在匹配父级中

          【讨论】:

          • centerCrop 实际上剪切了图像的某些部分。
          【解决方案9】:

          要创建一个宽度等于屏幕宽度,高度根据纵横比按比例设置的图像,请执行以下操作。

          Glide.with(context).load(url).asBitmap().into(new SimpleTarget<Bitmap>() {
                              @Override
                              public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
          
                                  // creating the image that maintain aspect ratio with width of image is set to screenwidth.
                                  int width = imageView.getMeasuredWidth();
                                  int diw = resource.getWidth();
                                  if (diw > 0) {
                                      int height = 0;
                                      height = width * resource.getHeight() / diw;
                                      resource = Bitmap.createScaledBitmap(resource, width, height, false);
                                  }
                                                        imageView.setImageBitmap(resource);
                              }
                          });
          

          希望这会有所帮助。

          【讨论】:

          • 这是我找到的最佳答案,我搜索了 2 天,谢谢 fathima
          【解决方案10】:

          在 ImageView 中使用这些属性来保持纵横比:

          android:adjustViewBounds="true"
          android:scaleType="fitXY"
          

          【讨论】:

          • fitXY 不保持纵横比。它会拉伸图像以完全适合布局的宽度和高度。
          【解决方案11】:

          试试这个:它为我解决了问题

             android:adjustViewBounds="true"
              android:scaleType="fitXY"
          

          【讨论】:

          【解决方案12】:

          尝试使用这个简单的行...在图像视图标记中的 xml 代码中添加此行,而不添加任何依赖项 android:scaleType="fitXY"

          【讨论】:

          • fitXY 不保持纵横比,因此图像可能会被拉伸
          【解决方案13】:

          使用 android:ScaleType="fitXY" im ImageView xml

          【讨论】:

          • fitXY 不保持纵横比,因此图像可能会被拉伸
          【解决方案14】:

          您可以尝试通过手动加载图像来完成您正在做的事情,但我强烈建议您查看Universal Image Loader

          我最近将它集成到我的项目中,我不得不说它太棒了。是否为您担心使事情异步、调整大小、缓存图像。集成和设置非常容易。在 5 分钟内,您可能可以让它为所欲为。

          示例代码:

          //ImageLoader config
          DisplayImageOptions displayimageOptions = new DisplayImageOptions.Builder().showStubImage(R.drawable.downloadplaceholder).cacheInMemory().cacheOnDisc().showImageOnFail(R.drawable.loading).build();
          
              ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()).
                      defaultDisplayImageOptions(displayimageOptions).memoryCache(new WeakMemoryCache()).discCache(new UnlimitedDiscCache(cacheDir)).build();
          
              if (ImageLoader.getInstance().isInited()) {
                  ImageLoader.getInstance().destroy();
              }
              ImageLoader.getInstance().init(config);
          
              imageLoadingListener = new ImageLoadingListener() {
                  @Override
                  public void onLoadingStarted(String s, View view) {
          
                  }
          
                  @Override
                  public void onLoadingFailed(String s, View view, FailReason failReason) {
                      ImageView imageView = (ImageView) view;
                      imageView.setImageResource(R.drawable.android);
                      Log.i("Failed to Load " + s, failReason.toString());
                  }
          
                  @Override
                  public void onLoadingComplete(String s, View view, Bitmap bitmap) {
          
                  }
          
                  @Override
                  public void onLoadingCancelled(String s, View view) {
          
                  }
              };
          
          //Imageloader usage
          ImageView imageView = new ImageView(getApplicationContext());
              if (orientation == 1) {
                  imageView.setLayoutParams(new LinearLayout.LayoutParams(width / 6, width / 6));
              } else {
                  imageView.setLayoutParams(new LinearLayout.LayoutParams(height / 6, height / 6));
              }
              imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
              imageLoader.displayImage(SERVER_HOSTNAME + "demos" + demo.getPathRoot() + demo.getRootName() + ".png", imageView, imageLoadingListener);
          

          这可以延迟加载图像,使其适合 imageView 的大小,在加载时显示占位符图像,并在加载失败时显示默认图标并缓存资源。

          -- 我还应该补充一点,这个当前配置保持图像纵横比,因此适用于您的原始问题

          【讨论】:

            【解决方案15】:

            只需使用 UniversalImageLoader 并设置

            DisplayImageOptions.Builder()
                .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)
                .build();
            

            ImageView 上没有缩放设置

            【讨论】:

              【解决方案16】:

              我遇到了类似的问题,我发现这是因为您需要计算dp。当您从 drawable 加载时,Android Studio 正在计算 ImageView,但是当您使用其他方法时,例如从 位图 加载时,dp 不会自动计算,

              这是我的 xml

              <ImageView
                android:id="@+id/imageViewer"
                android:layout_width="match_parent"
                android:layout_height="match_parent"//dp is not automaticly updated, when loading from a other source
                android:scaleType="fitCenter"
                tools:srcCompat="@drawable/a8" />
              

              我正在使用 Kotlin,并从资产文件加载可绘制对象,这是我的计算方法

              val d = Drawable.createFromStream(assets.open("imageData/${imageName}.png"), null)
              bitHeight = d.minimumHeight//get the image height
              imageViewer.layoutParams.height = (bitHeight * resources.displayMetrics.density).toInt()//set the height
              imageViewer.setImageDrawable(d)//set the image from the drawable
              imageViewer.requestLayout()//here I apply it to the layout
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-04-01
                • 2012-06-05
                • 2016-01-17
                • 2012-03-26
                相关资源
                最近更新 更多