Scale Image to fill ImageView width and keep aspect ratio

AndroidImageview

Android Problem Overview


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?

Android Solutions


Solution 1 - Android

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.

Solution 2 - Android

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

Solution 3 - Android

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.

Solution 4 - Android

Use android:scaleType="centerCrop".

Solution 5 - Android

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>

Solution 6 - Android

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

Solution 7 - Android

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

Thanks.

Solution 8 - Android

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

Solution 9 - Android

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.

Solution 10 - Android

Use these properties in ImageView to keep aspect ratio:

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

Solution 11 - Android

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

Solution 12 - Android

Try this: it solved the problem for me

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

Solution 13 - Android

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

Solution 14 - Android

use android:ScaleType="fitXY" im ImageView xml

Solution 15 - Android

Just use UniversalImageLoader and set

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

and no scale settings on ImageView

Solution 16 - Android

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJoeView Question on Stackoverflow
Solution 1 - AndroidTWiStErRobView Answer on Stackoverflow
Solution 2 - AndroidAlex SemeniukView Answer on Stackoverflow
Solution 3 - AndroidarnefmView Answer on Stackoverflow
Solution 4 - AndroidMark BuikemaView Answer on Stackoverflow
Solution 5 - AndroidDave ColeView Answer on Stackoverflow
Solution 6 - AndroidVijayView Answer on Stackoverflow
Solution 7 - AndroidGuna SekharView Answer on Stackoverflow
Solution 8 - AndroidRomu DizzyView Answer on Stackoverflow
Solution 9 - AndroidFathima kmView Answer on Stackoverflow
Solution 10 - AndroidAtif MahmoodView Answer on Stackoverflow
Solution 11 - AndroidrcbevansView Answer on Stackoverflow
Solution 12 - AndroidEngineering MindView Answer on Stackoverflow
Solution 13 - Androidziddi609View Answer on Stackoverflow
Solution 14 - AndroidNaveed NaeemView Answer on Stackoverflow
Solution 15 - AndroidArtemView Answer on Stackoverflow
Solution 16 - AndroidDarkPh03n1XView Answer on Stackoverflow