3

I have an ImageView which I use as a button and I want it to scale to the device width and then scale the height to keep the aspect ratio. I have been following this thread: How to scale an Image in ImageView to keep the aspect ratio and tried everything and nothing has worked.

Here is my XML:

 <ImageView
        android:id="@+id/th_button"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/towing_button"
        android:layout_centerHorizontal="true"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:layout_marginTop="50dp"
        android:adjustViewBounds="true"
        android:clickable="true"
        android:fitsSystemWindows="true"
        android:scaleType="fitCenter"
        android:src="@drawable/call_th_button_selector" />

The weird thing is that it is working correctly in the preview in Eclipse but when I test on my device it wont scale the height.

Anyone aware of a solution to this?

Community
  • 1
  • 1
PaperThick
  • 2,749
  • 4
  • 24
  • 42

2 Answers2

3

I actually made a widget for this. You basically just put the ImageView inside of the RatioRelativeLayout (this widget: https://github.com/elimirks/RatioRelativeLayout).

The example is on the github page, but I will paste it here as well:

<my.package.RatioRelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    heightRatio="0.75"
>
    <View
        android:layout_width="25dp"
        android:layout_height="match_parent"
        android:background="#f00"
    />
</my.package.RatioRelativeLayout>

You could also set the maximum height that it will allow using setMaxHeight. In my apps, I use this for viewing photos in landscape mode so that the image is not taller than the width of the device.

elimirks
  • 1,452
  • 2
  • 17
  • 30
3

Preserving the ratio will not work in the ImageView if the image is smaller than the actual view. I overcame that problem by extending the ImageView's onMeasure() method:

public class ExpandableImageView extends ImageView {

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

    private boolean getAdjustViewBound(){
        return true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int w;
        int h;

        // Desired aspect ratio of the view's contents (not including padding)
        float desiredAspect = 0.0f;

        // We are allowed to change the view's width
        boolean resizeWidth = false;

        // We are allowed to change the view's height
        boolean resizeHeight = false;

        final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

        if (getDrawable() == null) {
            w = h = 0;
        } else {
            w = getDrawable().getIntrinsicWidth();
            h = getDrawable().getIntrinsicHeight();
            if (w <= 0) w = 1;
            if (h <= 0) h = 1;

            // We are supposed to adjust view bounds to match the aspect
            // ratio of our drawable. See if that is possible.
            if (getAdjustViewBound()) {
                resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
                resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;

                desiredAspect = (float) w / (float) h;
            }
        }

        int pleft = getPaddingLeft();
        int pright = getPaddingRight();
        int ptop = getPaddingTop();
        int pbottom = getPaddingBottom();

        int widthSize;
        int heightSize;

        if (resizeWidth || resizeHeight) {
            /* If we get here, it means we want to resize to match the
                drawables aspect ratio, and we have the freedom to change at
                least one dimension. 
            */

            // Get the max possible width given our constraints
            widthSize = resolveAdjustedSize(w + pleft + pright, widthMeasureSpec);

            // Get the max possible height given our constraints
            heightSize = (int) (widthSize / desiredAspect);

            if (desiredAspect != 0.0f) {
                // See what our actual aspect ratio is
                float actualAspect = (float)(widthSize - pleft - pright) /
                                        (heightSize - ptop - pbottom);

                if (Math.abs(actualAspect - desiredAspect) > 0.0000001) {

                    boolean done = false;

                    // Try adjusting width to be proportional to height
                    if (resizeWidth) {
                        int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) +
                                pleft + pright;
                        if (newWidth <= widthSize) {
                            widthSize = newWidth;
                            done = true;
                        } 
                    }

                    // Try adjusting height to be proportional to width
                    if (!done && resizeHeight) {
                        int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) +
                                ptop + pbottom;
                        if (newHeight <= heightSize) {
                            heightSize = newHeight;
                        }
                    }
                }
            }
        } else {
            /* We are either don't want to preserve the drawables aspect ratio,
               or we are not allowed to change view dimensions. Just measure in
               the normal way.
            */
            w += pleft + pright;
            h += ptop + pbottom;

            w = Math.max(w, getSuggestedMinimumWidth());
            h = Math.max(h, getSuggestedMinimumHeight());

            widthSize = resolveSizeAndState(w, widthMeasureSpec, 0);
            heightSize = resolveSizeAndState(h, heightMeasureSpec, 0);
        }

        setMeasuredDimension(widthSize, heightSize);
    }


    private int resolveAdjustedSize(int desiredSize, int measureSpec) {
        int result = desiredSize;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize =  MeasureSpec.getSize(measureSpec);
        switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                /* Parent says we can be as big as we want. Just don't be larger
                   than max size imposed on ourselves.
                */
                result = desiredSize;
                break;
            case MeasureSpec.AT_MOST:
                // Parent says we can be as big as we want, up to specSize. 
                // Don't be larger than specSize, and don't be larger than 
                // the max size imposed on ourselves.
                result = Math.min(desiredSize, specSize);
                break;
            case MeasureSpec.EXACTLY:
                // No choice. Do what we are told.
                result = specSize;
                break;
        }
        return result;
    }
}

Then in XML use the FitXY scale type:

<com.thepackagename.ExpandableImageView  
    android:id="@+id/image"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:scaleType="fitXY"
    android:src="@drawable/img"/>

Hope that helps!

Marqs
  • 17,800
  • 4
  • 30
  • 40