62

Update : I solved this issue by using the method described in this answer

I'm a bit stuck with this issue, which I think should be pretty simple.

So my app downloads an image, and renders the bitmap in an ImageView, a child element of a RelativeLayout. I would like the ImageView to fit the parent width, and to adapt it's size to keep the aspect ratio.

Here is my XML :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<RelativeLayout android:id="@+id/banner" android:layout_width="fill_parent" android:layout_height="wrap_content"></RelativeLayout>
<TextView  
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:text="@string/hello"
/>
</LinearLayout>

And the code :

public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);


    RelativeLayout banner = (RelativeLayout) findViewById(R.id.banner);
    ImageView imgV = new ImageView(this);

    imgV.setScaleType(ImageView.ScaleType.CENTER_CROP);
    // I tried all the scale types : CENTER_INSIDE : same effect, FIT_CENTER : same effect... 

    imgV.setBackgroundColor(0x00FFFF00);

    imgV.setAdjustViewBounds(Color.BLUE);


    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);

    banner.addView(imgV,params);

    // Some code downloading the image stream

    bitmap = BitmapFactory.decodeStream(stream);


    imgV.setImageBitmap(bitmap);

    }

Desired :

Desired result

Result :

Current result

Community
  • 1
  • 1
Julien
  • 9,312
  • 10
  • 63
  • 86
  • possible duplicate of [Android: How to stretch an image to the screen width while maintaining aspect ratio?](http://stackoverflow.com/questions/2991110/android-how-to-stretch-an-image-to-the-screen-width-while-maintaining-aspect-ra) – Jim G. Mar 16 '14 at 22:56

5 Answers5

147

Thanks to @Julien and @js. Here is complete solution of ImageView that will stretch bitmap height preserving aspect ratio even if bitmap is smaller than ImageView.

public class ResizableImageView extends ImageView {

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

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
         Drawable d = getDrawable();

         if(d!=null){
                 // ceil not round - avoid thin vertical gaps along the left/right edges
                 int width = MeasureSpec.getSize(widthMeasureSpec);
                 int height = (int) Math.ceil((float) width * (float) d.getIntrinsicHeight() / (float) d.getIntrinsicWidth());
                 setMeasuredDimension(width, height);
         }else{
                 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         }
    }

}

You can use this class in your xml layouts instead ImageView.

<com.example.ResizableImageView
    android:id="@+id/banner"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:src="@drawable/banner" />
Community
  • 1
  • 1
Serafim Suhenky
  • 2,140
  • 1
  • 15
  • 18
  • 1
    I guess this deserves to be the final answer, but as @Js mentionned, you should null-check the Drawable. If you update your code, I'll mark it as accepted. – Julien Sep 06 '12 at 08:43
  • It perfectly work with UrlImageViewHelper. https://github.com/koush/UrlImageViewHelper – Youngjae May 15 '13 at 20:26
  • 1
    this solution is ok for image where `height <= width` but otherwise you have to change order `int height = (int) Math.ceil((float) width * (float) d.getIntrinsicWidth() / (float) d.getIntrinsicHeight());` right? – deadfish Jun 20 '13 at 12:11
  • Silly that this can't be done from xml with all those scale options, but thank you! – Chuck Pinkert Sep 04 '13 at 04:48
  • One other thing to consider is the drawable might be a ColorDrawable which means getIntrinsicWidth() and getIntrinsicHeight() both return -1. This means the view will stretch to a square filling the space.. I'm not sure what it should do though since colors don't have a size. – Cameron Ketcham Sep 12 '14 at 22:16
  • Bad behavior when this ResizableImageView is inside layout with weight specified... – Suvitruf - Andrei Apanasik Oct 28 '14 at 09:43
  • Brillliant ! Just what I wanted. Thanks :) – Prakash Raman Oct 09 '15 at 11:23
51

You're probably looking for android:adjustViewBounds="true" in xml or imageView.setAdjustViewBounds(true) in Java.

Matthew
  • 44,826
  • 10
  • 98
  • 87
  • That is in my code : imgV.setAdjustViewBounds(true); setAdjustViewBounds is only for ImageView, and I'd like a property like this for the RelativeLayout – Julien Apr 06 '11 at 09:39
  • Could you post your full xml layout? Small differences can make a large impact. – Matthew Apr 06 '11 at 14:32
  • @Julien Have you found a solution? I have exactly the same problem. Can you post the solution? – Erik May 15 '11 at 13:08
  • @Erik : I subclassed ImageView, I needed to override the onMeasure protected method, basically I changed directly the view's size keeping the aspect ratio (don't forget to super.onMeasure() before doing anything) – Julien May 15 '11 at 13:38
  • @Erik : Ok, I found it ! Hope it will help – Julien May 18 '11 at 14:40
8

As I mentioned in a comment, I subclassed the ImageView. I found my code, here you go :

    protected class ResizableImageView extends ImageView
    {

        private Bitmap mBitmap;

        // Constructor

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


        // Overriden methods


          @Override 
          protected void onMeasure(int widthMeasureSpec,
                  int heightMeasureSpec) {
              if(mBitmap != null)
              {
                    int width = MeasureSpec.getSize(widthMeasureSpec);
                    int height = width * mBitmap.getHeight() / mBitmap.getWidth();
                    setMeasuredDimension(width, height);

              } 
              else
              {
                  super.onMeasure(widthMeasureSpec,
                          heightMeasureSpec);
              }
              }

            @Override
            public void setImageBitmap(Bitmap bitmap)
            {
                mBitmap = bitmap;
                 super.setImageBitmap(bitmap);
            }

    }
Julien
  • 9,312
  • 10
  • 63
  • 86
  • An improvement I made is not using a bitmap for getting height and width in `onMeasure` but using `getDrawable().getIntrinsicHeight()` and `getDrawable().getIntrinsicWidth()` (of course with null-checking the drawable). – js- Feb 16 '12 at 14:18
2

I made a slight change to @Seraphim S's solution to account for cases where the image may be wide, and the view's bounds also wide (for example, rotating the device to landscape mode).

public class ResizableImageView extends ImageView {
    public ResizableImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Drawable d = getDrawable();

        if (d != null) {

            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);

            if (width >= height) {
                width = (int) Math.ceil((float) height * (float) d.getIntrinsicWidth() / (float) d.getIntrinsicHeight());
            } else {
                height = (int) Math.ceil((float) width * (float) d.getIntrinsicHeight() / (float) d.getIntrinsicWidth());
            }

            setMeasuredDimension(width, height);
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}
chuz
  • 506
  • 1
  • 4
  • 10
  • i got only one question d.getIntrinsicWidth() / d.getIntrinsicHeight() = ??? divide and rule :D and u will get one big nothing :) – ceph3us Nov 29 '15 at 00:44
2

I think you should change your imgV width to "match_parent"

You should change the scale type to imgV.setScaleType(ImageView.ScaleType.CENTER_INSIDE);

Twobard
  • 2,553
  • 1
  • 24
  • 27
  • Unfortunately, it doesn't change, center_inside has the same effect. And match_parent and fill_parent have the same value (-1) as said in http://developer.android.com/reference/android/view/ViewGroup.LayoutParams.html#FILL_PARENT – Julien Apr 06 '11 at 09:41
  • Ah, sorry, scratch that, i'm an idiot. At which point are you assigning layout parameters to your imageview? – Twobard Apr 06 '11 at 10:03
  • When adding the view to the "banner" : RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT); banner.addView(imgV,params); – Julien Apr 06 '11 at 10:06
  • This is a stab in the dark.. have you tried the following: params.addRule(RelativeLayout.ALIGN_PARENT_LEFT); params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); – Twobard Apr 06 '11 at 10:17
  • 1
    Nice idea, but it didn't work. The image view setAdjustViewBounds is apparently not working for the height – Julien Apr 06 '11 at 10:35
  • Sorry I couldn't be of any more help. Good luck with your problem. – Twobard Apr 06 '11 at 10:36