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

Mundi picture Mundi · Dec 21, 2012 · Viewed 87.3k times · Source

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.

Answer

Mark Martinsson picture Mark Martinsson · Jul 2, 2013

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>