3

Here is my basic layout:

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">

    <ImageView
    android:adjustViewBounds="true"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:src="@drawable/welcome_hero" />

</LinearLayout>

I want the Image to scale while maintaining the aspect ratio to fit the screen, pushing any following views down.

This works just beautifully on my mdpi (LG LGP509) and hdpi (HTC G2, Nexus One) devices. However, on my Galaxy Nexus (xhdpi), the image stubbornly does not scale to fit. It leaves extra space on the sides.

Here is some more info:

  • the layout is in the generic layout/ folder

  • welcome_hero.png is in the drawables/ folder and has 320px width and 161px height

AndroidManifest.xml has this entry:

<uses-sdk
android:targetSdkVersion="10"
android:minSdkVersion="8" />

<supports-screens
android:xlargeScreens="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="false" />

Some things I have tried:

  • changing the scaleType to fitCenter, fitInside, or matrix yielded the same results as without
  • changing ImageView height to fill_parent correctly scaled the image, but pushed all other views off screen
  • threatening monitor with physical violence left me more frustrated
  • explicitly set height and width to 320dp and 161dp changed nothing
  • as mentioned above, I have tried new layouts both in IntelliJ's previewer and on physical devices

I have been going through StackOverflow for about 1.5 hours now looking at the hundreds of posts that seem similar to mine, but none seem to make things work on the Galaxy Nexus. I am trying desperately to be a good developer and make my stuff scale up to new phones, but this is proving to be difficult.

Jim Cortez
  • 471
  • 5
  • 19

4 Answers4

3

Use a combination of layout markup and code to do the scaling:

keep your layout_width="fill_parent" and layout_height="wrap_content". Add

android:scaleType="fitXY"

In your code:

ImageView mImageView = findViewById (R.id.yourimageid);
LayoutParams lp = mImageView.getLayoutParams();
lp.height = (int)((float)context.getWindowManager().getDefaultDisplay().getWidth() *
    ((float)mImageView.getDrawable().getIntrinsicHeight() / (float)mImageView.getDrawable().getIntrinsicWidth()));
mImageView.setLayoutParams(lp);

i.e. you're setting a new value for layout_height by multiplying the screen width * the ratio of image height to width.

CSmith
  • 13,318
  • 3
  • 39
  • 42
  • mImageView.getLayoutParams() is returning null in both onCreate and onStart, should this be placed somewhere else? – Jim Cortez Mar 02 '12 at 21:56
  • It would have to be somewhere after the view is inflated. Before the view is inflated, the layout params won't be set. You should get non null layout params in onResume of a regular activity or onCreateView in a fragment. This answer will work, but you should be able to get the same result in xml (then again this is android...) – Sam Judd Mar 02 '12 at 23:10
3

You probably want to set android:adjustViewBounds to true. Keep your width fill_parent and height wrap_content. You may still have to play with your scaleType to get it to work as expected, although scaling up tends to be tricky using this approach.

Another option is create your own subclass of ImageView. The following has worked ok for me, but may need some modification for your specific case.

public class AspectRatioImageView extends ImageView {
    private final boolean horizontal;

    public AspectRatioImageView(Context context) {
        this(context, null);
    }

    public AspectRatioImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AspectRatioImageView(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        TypedArray array = context.obtainStyledAttributes(attrs,
                R.styleable.AspectRatioImageView, defStyle, 0);
        int h = array
                .getInt(R.styleable.AspectRatioImageView_fixedDimension, 0);
        horizontal = (h == 0);
        array.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        boolean widthExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
        boolean heightExactly = MeasureSpec.getMode(heightMeasureSpec) ==           MeasureSpec.EXACTLY;

        float ratio;
        try {
            ratio = ((float) ((BitmapDrawable) getDrawable())
                    .getIntrinsicWidth())
                    / ((float) ((BitmapDrawable) getDrawable())
                            .getIntrinsicHeight());
        } catch (ClassCastException e) {
            ratio = MeasureSpec.getSize(widthMeasureSpec)
                    / MeasureSpec.getSize(heightMeasureSpec);
        }

        int heightRatioSpec = MeasureSpec.makeMeasureSpec(
                (int) (MeasureSpec.getSize(widthMeasureSpec) / ratio),
                MeasureSpec.EXACTLY);   

        int widthRatioSpec = MeasureSpec.makeMeasureSpec(
                (int) (MeasureSpec.getSize(heightMeasureSpec) * ratio),
                MeasureSpec.EXACTLY);   

        if (widthExactly) {
            if (heightExactly) {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            } else {
                super.onMeasure(widthMeasureSpec, heightRatioSpec);
            }
        } else {
            if (heightExactly) {
                super.onMeasure(widthRatioSpec, heightMeasureSpec);
            } else {
                if (horizontal) {
                    super.onMeasure(widthMeasureSpec, heightRatioSpec);
                } else {
                    super.onMeasure(widthRatioSpec, heightMeasureSpec);
                }
            }
        }
    }
}

You'll also need to add

<declare-styleable name="AspectRatioImageView">
    <attr format="enum" name="fixedDimension">
        <enum name="horizontal" value="0" />
        <enum name="vertical" value="1" />
    </attr>
</declare-styleable>

to your /values/attr.xml so you can use the app:fixedDimension=horizontal attribute to let it know to scale the vertical dimension.

keyboardr
  • 841
  • 1
  • 8
  • 20
1

So you want your view to stretch to be as wide as the screen, and then whatever height would be required to maintain the original aspect ratio at that width?

I believe setting a scaleType of centerInside will make this work. Center crop is worth a try if center inside doesn't work. See: http://developer.android.com/reference/android/widget/ImageView.ScaleType.html

Sam Judd
  • 7,317
  • 1
  • 38
  • 38
-1

instead of using android:src on your imageView XML, use android:background. That should solve the scale issues.

see here: What is the difference between src and background of ImageView

Community
  • 1
  • 1
Lior Iluz
  • 26,213
  • 16
  • 65
  • 114
  • The image doesn't keep the aspect ratio when I do this, it just keeps the same height and stretches horizontally. – Jim Cortez Mar 02 '12 at 21:31