41

I have an ImageView with android:layout_width=100dp, android:layout_height=wrap_content and android:adjustViewBounds=true

It's source is a 50 x 50 px picture. But the aspect ratio is not preserved - height of the ImageView is 50px, not 100px (i.e. adjustViewBounds is not working). If I have a 200x200px picture it works - width and height are 100px. This code results in a 100px wide and 50px tall picture but the src image is square:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <ImageView
        android:id="@+id/photo"
        android:src="@drawable/icon"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:scaleType="fitXY"
        android:adjustViewBounds="true" />

</LinearLayout>
Atul O Holic
  • 6,692
  • 4
  • 39
  • 74
fhucho
  • 34,062
  • 40
  • 136
  • 186
  • That normally works; can you provide an exact code snippet? Also what's the ImageView's parent? Perhaps its parent is forcing a certain size, not allowing the ImageView to resize. – Roman Nurik Oct 11 '11 at 00:06
  • Ah I found the issue, will provide an answer now. – Roman Nurik Oct 11 '11 at 21:17

5 Answers5

69

The issue is that adjustViewBounds will not increase the size of the ImageView beyond the natural dimensions of the drawable. It will only shrink the view to maintain aspect ratio; if you provide a 500x500 image instead of a 50x50 image, this should work.

If you're interested in the spot where this behavior is implemented, see ImageView.java's onMeasure implementation.

One workaround is to implement a custom ImageView that changes this behavior in onMeasure.

Roman Nurik
  • 29,665
  • 7
  • 84
  • 82
  • do you know of any library that fixes this behavior of the imageView? – android developer Sep 30 '12 at 15:46
  • 7
    I have now created a version of ImageView which also scales up *and* scales down if you have match_parent/fill_parent, which the original ImageView neither does. In effect, I find this version of ImageView to be much more practical when doing aspect-ratio scaling in a dynamic layout. Feel free to copy. Source: https://gist.github.com/Nilzor/5402789 – Nilzor Apr 17 '13 at 08:59
  • 5
    A ScalingImageView which works as if adjustViewBounds=true and always changes the height based on the width and drawable intrinsic aspect ratio: https://github.com/triposo/barone/blob/master/src/com/triposo/barone/ScalingImageView.java – aleb Aug 01 '13 at 10:34
  • 16
    This behaviour seems to have changed with Android 4.3. My views now increase beyond their natural size. Can you confirm that @RomanNurik? – devisnik Aug 09 '13 at 08:51
  • @aleb Thanks works fine! devisnik - It doesn't increase size of a image on my S5 with 4.4.2 and Galaxy Nexus 4.3. Maybe it's device specific. – tomrozb Aug 18 '14 at 10:43
  • @devisnik, u'r right. That method now can scale the dimension up beyond the image size – suitianshi Mar 02 '16 at 08:55
13

There's a more simple way. Make your ImageView like this:

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

This way drawable will stretch to fit in the ImageView center by preserving the aspect ratio. We just have to calculate the right height to make it proportional so we don't have any blank space:

private void setImageBitmap(Bitmap bitmap, ImageView imageView){
    float i = ((float)imageWidth)/((float)bitmap.getWidth());
    float imageHeight = i * (bitmap.getHeight());
    imageView.setLayoutParams(new RelativeLayout.LayoutParams(imageWidth, (int) imageHeight));
    imageView.setImageBitmap(bitmap);
}
vladexologija
  • 6,857
  • 4
  • 29
  • 28
  • 1
    This only a good solution if align the image in the center of the view is what you want. If you need to have a flexible alignment, see Roman Nuriks answer and my comment there. – Nilzor Apr 17 '13 at 09:05
2

In addition to @RomanNurik's answer

You can find working solution here, either copy-paste code or just add the Gradle dependency

dependencies {
    compile 'com.inthecheesefactory.thecheeselibrary:adjustable-imageview:1.0.1'
}

P.S. Solution provided by @Nilzor didn't work for me

Community
  • 1
  • 1
duviteck
  • 421
  • 4
  • 8
0

I had a similar requirement; in my case, I wanted the image to be square, and wanted the ImageView to match the aspect ratio so I could use its background and padding to draw a border.

I read the answers here but instead of overriding ImageView, I decided to make a Layout that guarantees its contents (should be only one view) are square. That way I could use a standard ImageView inside it. (And you never know, I might want to make something else square later. Although probably not.)

In case it's useful for anyone else, here's the code (feel free to copy). There are probably bugs as I just made it work for my app then stopped. :)

public class SquareLayout extends ViewGroup
{
    public SquareLayout(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    public SquareLayout(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    public SquareLayout(Context context)
    {
        super(context);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {
        // Work out width and height, and square size.
        int width = r - l;
        int height = b - t;

        int size, xOffset, yOffset;
        if(width < height)
        {
            size = width;
            xOffset = 0;
            yOffset = (height - size) / 2;
        }
        else
        {
            size = height;
            xOffset = (width - size) / 2;
            yOffset = 0;
        }

        for(int i = 0; i < getChildCount(); i++)
        {
            View child = getChildAt(i);
            child.layout(xOffset, yOffset, size + xOffset, size + yOffset);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        // Get width and height.
        int w = -1, h = -1;
        switch(MeasureSpec.getMode(widthMeasureSpec))
        {
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            w = MeasureSpec.getSize(widthMeasureSpec);
            break;

        case MeasureSpec.UNSPECIFIED:
            break;
        }
        switch(MeasureSpec.getMode(heightMeasureSpec))
        {
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            h = MeasureSpec.getSize(heightMeasureSpec);
            break;

        case MeasureSpec.UNSPECIFIED:
            break;
        }

        // If only one of width/height is unspecified, set them both the same.
        if(w == -1 && h != -1)
        {
            w = h;
        }
        else if(h == -1 && w != -1)
        {
            h = w;
        }

        // Either they're both specified or both unspecified.
        int childMeasureSpec;
        if(w == -1)
        {
            childMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        }
        else
        {
            childMeasureSpec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY);
        }

        // Pass through to children.
        int maxDimension = 1;
        for(int i = 0; i < getChildCount(); i++)
        {
            View child = getChildAt(i);
            child.measure(childMeasureSpec, childMeasureSpec);
            maxDimension = Math.max(maxDimension, child.getMeasuredWidth());
            maxDimension = Math.max(maxDimension, child.getMeasuredHeight());
        }

        if(w == -1)
        {
            w = maxDimension;
            h = maxDimension;
        }

        setMeasuredDimension(w, h);
    }
}
sam
  • 2,105
  • 2
  • 15
  • 18
-1

this line will do it for you android:scaleType="fitXY"