238

I have a GridView. The data of GridView is request from a server.

Here is the item layout in 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>

I request data from server, get image url and load image to 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;
    }
}

and there is the code of getView(int position, View convertView, ViewGroup parent) method in adapter

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

The image size is 210*210. I run my application on my Nexus 4. The image does fill ImageView width, but the ImageView height does not scale. ImageView does not show the whole image.

How do I solve this problem?

Brian
  • 14,610
  • 7
  • 35
  • 43
Joe
  • 3,581
  • 4
  • 23
  • 28

16 Answers16

634

Without using any custom classes or libraries:

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

scaleType="fitCenter" (default when omitted)

  • will make it as wide as the parent allows and up/down-scale as needed keeping aspect ratio.

scaleType="centerInside"

  • if the intrinsic width of src is smaller than parent width
    will center the image horizontally
  • if the intrinsic width of src is larger than parent width
    will make it as wide as the parent allows and down-scale keeping aspect ratio.

It doesn't matter if you use android:src or ImageView.setImage* methods and the key is probably the adjustViewBounds.

TWiStErRob
  • 44,762
  • 26
  • 170
  • 254
  • 14
    android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="fitCenter" does the trick – James Tan Sep 22 '15 at 16:36
  • @JamesTan `fill_parent` for width is just a good practice, a fixed size works as well. – TWiStErRob Sep 22 '15 at 17:26
  • @TWiStErRob i realize it needs android:adjustViewBounds="true" with it, otherwise it does not fit accordingly for height. and yes, the width to fit parent is the complete version – James Tan Sep 23 '15 at 15:57
  • 9
    Works like charm on API 19+, but not on API 16 (tested). Alex Semeniuk's custom view works on API 16 too. – Ashkan Sarlak Sep 27 '15 at 10:25
  • @AshkanSarlak no problem for me in Api 16 – abbasalim Nov 12 '15 at 07:28
  • android:adjustViewBounds="true" is really a magic.. I was trying to solve this from last 3 days. – Irfan Raza Oct 09 '16 at 22:03
  • I needed `android:layout_height="wrap_content"` and `android:adjustViewBounds="true"` to get the ratio to be even. Also `android:layout_gravity="center"` to center it in the view. – Chris Cirefice Jun 25 '17 at 15:45
  • without a custom view, it may not work in all cases/on all devices with varying image sizes. I followed these instructions to get it to work consistently on all devices https://www.ryadel.com/en/android-proportionally-stretch-imageview-fit-whole-screen-width-maintaining-aspect-ratio/ – caitcoo0odes Nov 24 '17 at 18:08
  • Doesn't work for me. For some reason it gives white space for height. – Zhen Liu Nov 28 '17 at 18:55
  • Since we have 2020 please change `fill_parent` to `match_parent` – F.Mysir Apr 08 '20 at 06:18
  • 1
    @F.Mysir whops , yes, it doesn't matter which one for the issue, but for spreading good practice, I totally agree. – TWiStErRob Apr 08 '20 at 09:07
43

I like answer of arnefm but he made a small mistake (see comments) which I will try to correct:

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

}

Thus your ImageView will be scaled properly and will have no dimension problems if (for instance) put inside of ScrollView

Alex Semeniuk
  • 1,865
  • 19
  • 28
  • thx, i've found the solution, just below arnefm's answer, the first comment that i added. that `there` is a link – Joe Jan 08 '14 at 07:08
40

I had a similar problem once. I solved it by making a custom ImageView.

public class CustomImageView extends ImageView

Then override the onMeasure method of the imageview. I did something like this I believe:

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

This will stretch the image to fit the screen while maintaining the aspect ratio.

arnefm
  • 1,008
  • 8
  • 7
  • 1
    see [there](http://stackoverflow.com/questions/2991110/android-how-to-stretch-an-image-to-the-screen-width-while-maintaining-aspect-ra). the first answer – Joe Aug 07 '13 at 01:12
  • Well, this answer is not accurate. Consider situation where you set `android:layout_height="wrap_content"`. In this case `MeasureSpec.getSize(heightMeasureSpec)` will be `0` and your `viewSideRatio` will result in `Infinity` (no need to wonder, Java float allows such values.) In this case both your `height` and `width` will be `0`. – Alex Semeniuk Nov 22 '13 at 13:20
  • 1
    For me to make the fill work i needed to swap (imageSideRatio >= viewSideRatio) to (imageSideRatio < viewSideRatio).. otherwise a good answer – Marek Halmo May 01 '15 at 12:38
29

Use android:scaleType="centerCrop".

Mark Buikema
  • 2,483
  • 30
  • 53
10

FOR IMAGE VIEW (set these parameters)

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

Now whatever the size of the image is there, it's width will match the parent and height will be according to match the ratio. I have tested this and I am 100% sure.

we want this --> adjustViewBound = true

not this --> adjustViewBound = false

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

// like the first one
Vijay
  • 1,163
  • 8
  • 22
9

I did something similar to the above and then banged my head against the wall for a few hours because it did not work inside a RelativeLayout. I ended up with the following code:

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

And then to prevent RelativeLayout from ignoring the measured dimension I did this:

    <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>
Dave Cole
  • 558
  • 4
  • 14
7

Yo don't need any java code. You just have to :

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

The key is in the match parent for width and height

Romu Dizzy
  • 113
  • 2
  • 8
7

This will not be applicable if you set image as background in ImageView, need to set at src(android:src).

Thanks.

Guna Sekhar
  • 355
  • 1
  • 3
  • 14
6

To create an image with width equals screen width, and height proportionally set according to aspect ratio, do the following.

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

Hope this helps.

Fathima km
  • 2,539
  • 3
  • 18
  • 26
2

Use these properties in ImageView to keep aspect ratio:

android:adjustViewBounds="true"
android:scaleType="fitXY"
Atif Mahmood
  • 8,882
  • 2
  • 41
  • 44
1

You can try to do what you're doing by manually loading the images, but I would very very strongly recommend taking a look at Universal Image Loader.

I recently integrated it into my project and I have to say its fantastic. Does all the worrying about making things asynchronous, resizing, caching images for you. It's really easy to integrate and set up. Within 5 minutes you can probably get it doing what you want.

Example code:

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

This can lazy load the images, fit them correctly to the size of the imageView showing a placeholder image while it loads, and showing a default icon if loading fails and caching the resources.

-- I should also add that this current config keeps the image aspect ratio, hence applicable to your original question

rcbevans
  • 7,101
  • 4
  • 30
  • 46
1

Try this: it solved the problem for me

   android:adjustViewBounds="true"
    android:scaleType="fitXY"
Engineering Mind
  • 113
  • 3
  • 12
  • 1
    How is this answer different from https://stackoverflow.com/a/37044123/1101730? What is more it's a wrong answer as fitXY does NOT keep the aspect ratio. – Micer Sep 18 '20 at 10:16
0

Just use UniversalImageLoader and set

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

and no scale settings on ImageView

Artem
  • 896
  • 1
  • 11
  • 16
0

try with this simple line... add this line in your xml code in image view tag with out adding any dependency android:scaleType="fitXY"

ziddi609
  • 29
  • 7
0

use android:ScaleType="fitXY" im ImageView xml

0

I had a similar issue, I found the reason for this is because you need to calculate the dp. Android studio is calculating the ImageView when you load it from the drawable, but when you are using another method, like loading from bitmap the dp is not automatically accounted for,

Here is my 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" />

I'm using Kotlin, and loading drawable from an asset file, here's how I calculate this

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
DarkPh03n1X
  • 600
  • 1
  • 7
  • 17