缩放图像以填充ImageView宽度并保持宽高比


198

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

这是中的项目布局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>

我从服务器请求数据,获取图像网址并将图像加载到 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不显示整个图像。

我该如何解决这个问题?

Answers:


544

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

<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:src还是ImageView.setImage*方法,密钥都可能是adjustViewBounds


5
android:layout_height =“ wrap_content” android:adjustViewBounds =“ true” android:scaleType =“ fitCenter”达到目的
James Tan

@JamesTan fill_parent的宽度只是一个好习惯,固定大小也可以。
TWiStErRob

@TWiStErRob我意识到它需要android:adjustViewBounds =“ true”,否则它不适合高度。是的,适合父级的宽度是完整版本
James Tan

9
在API 19+上可像魅力一样工作,但在API 16(已测试)上却无法工作。Alex Semeniuk的自定义视图也适用于API 16。
Ashkan Sarlak 2015年

1
@ F.Mysir太😅了,是的,哪个问题无关紧要,但是对于传播良好实践,我完全同意。
TWiStErRob

42

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

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

}

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


谢谢,我在arnefm的答案下面找到了解决方案,这是我添加的第一条评论。这there是一个链接

36

我曾经有过类似的问题。我通过制作自定义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);
    }

这将拉伸图像以适合屏幕,同时保持宽高比。


1
那里。第一个答案

好吧,这个答案是不准确的。考虑一下您设置的情况android:layout_height="wrap_content"。在这种情况下,MeasureSpec.getSize(heightMeasureSpec)将是0并且您viewSideRatio将得到Infinity(不必怀疑,Java float允许此类值。)在这种情况下,您heightwidth将都是0
Alex Semeniuk

1
对我来说,要使填充工作有效,我需要将(imageSideRatio> = viewSideRatio)交换为(imageSideRatio <viewSideRatio)..否则,一个很好的答案
Marek Halmo


9

我做了与上面相似的事,然后将头撞在墙上了几个小时,因为它不能在墙壁上工作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>



5

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

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
Karthick Ramanathan

4

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

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

4

不需要任何Java代码。您只需要:

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

关键在宽度和高度的匹配父项中


centerCrop实际上会剪切图像的某些部分。
Sreekanth Karumanaghat

2

尝试使用这条简单的线...在您的xml代码中的图像视图标签中添加此行,而不添加任何依赖项android:scaleType =“ fitXY”


fitXY不会保持纵横比,因此图像可能会被拉伸
ProjectDelta


1

您可以尝试通过手动加载图像来完成自己的工作,但是我强烈建议您看一下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的大小,从而在加载时显示占位符图像,如果加载失败并缓存资源,则显示默认图标。

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


0

只需使用UniversalImageLoader并设置

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

并且在ImageView上没有比例设置


0

我有一个类似的问题,我发现这是因为您需要计算dpImageView当您从加载Android Studio时,系统会在计算drawable,但是当您使用其他方法(例如从位图加载)时,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,并从资产文件加载drawable,这是我的计算方式

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

-1

使用易于使用的毕加索。

在您的适配器..

@Override
public void getView(int position, View convertView, ViewGroup parent) {

 ImageView view = (ImageView) convertView.findViewById(R.id.ranking_prod_pic);

 Picasso.with(context).load(url).into(view); //url is image url

 //you can resize image if you want

 /* Picasso.with(context) .load(url) .resize(50, 50) .centerCrop() .into(view) */

}

http://square.github.io/picasso/ 在此处输入图片说明

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.