Android ImageView scale smaller image to width with flexible height without cropping or distortion

androidimage-scalingimageview

Often asked, never answered (at least not in a reproducible way).

I have an image view with an image that is smaller than the view. I want to scale the image to the width of the screen and adjust the height of the ImageView to reflect the proportionally correct height of the image.

<ImageView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
/>

This results in the image centered at its original size (smaller then the screen width) with margins at the side. No good.

So I added

android:adjustViewBounds="true" 

Same effect, no good. I added

android:scaleType="centerInside"

Same effect, no good. I changed centerInside to fitCenter. Same effect, no good. I changed centerInside to centerCrop.

android:scaleType="centerCrop"

Now, finally, the image is scaled to the width of the screen – but cropped at top and bottom! So I changed centerCrop to fitXY.

android:scaleType="fitXY"

Now the image is scaled to the width of the screen but not scaled on the y-axis, resulting in a distorted image.

Removing android:adjustViewBounds="true" has no effect. Adding an android:layout_gravity, as suggested elsewhere, has again no effect.

I have tried other combinations — to no avail. So, please does anyone know:

How do you set up the XML of an ImageView to fill the width of the screen, scale a smaller image to fill the entire view, displaying the image with its aspect ratio without distortion or cropping?

EDIT: I also tried setting an arbitrary numeric height. This only has an effect with the centerCrop setting. It will distort the image vertically according to the view height.

Best Answer

I have solved this by creating a java-class that you include in your layout-file:

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

Now, you use this by added your class to your layout-file:

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