android - Scale Image to fill ImageView width and keep aspect ratio


Translate

I have a GridView. The data of GridView is request from a server.

Here is the item layout in GridView:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/analysis_micon_bg"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingBottom="@dimen/half_activity_vertical_margin"
    android:paddingLeft="@dimen/half_activity_horizontal_margin"
    android:paddingRight="@dimen/half_activity_horizontal_margin"
    android:paddingTop="@dimen/half_activity_vertical_margin" >

    <ImageView
        android:id="@+id/ranking_prod_pic"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:contentDescription="@string/app_name"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/ranking_rank_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/ranking_prod_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/ranking_prod_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

I request data from server, get image url and load image to Bitmap

public static Bitmap loadBitmapFromInputStream(InputStream is) {
    return BitmapFactory.decodeStream(is);
}

public static Bitmap loadBitmapFromHttpUrl(String url) {
    try {
        return loadBitmapFromInputStream((InputStream) (new URL(url).getContent()));
    } catch (Exception e) {
        Log.e(TAG, e.getMessage());
        return null;
    }
}

and there is the code of getView(int position, View convertView, ViewGroup parent) method in adapter

Bitmap bitmap = BitmapUtil.loadBitmapFromHttpUrl(product.getHttpUrl());
prodImg.setImageBitmap(bitmap);

The image size is 210*210. I run my application on my Nexus 4. The image does fill ImageView width, but the ImageView height does not scale. ImageView does not show the whole image.

How do I solve this problem?


All Answers
  • Translate

    Without using any custom classes or libraries:

    <ImageView
        android:id="@id/img"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:scaleType="fitCenter" />
    

    scaleType="fitCenter" (default when omitted)

    • will make it as wide as the parent allows and up/down-scale as needed keeping aspect ratio.

    scaleType="centerInside"

    • if the intrinsic width of src is smaller than parent width
      will center the image horizontally
    • if the intrinsic width of src is larger than parent width
      will make it as wide as the parent allows and down-scale keeping aspect ratio.

    It doesn't matter if you use android:src or ImageView.setImage* methods and the key is probably the adjustViewBounds.


  • Translate

    I like answer of arnefm but he made a small mistake (see comments) which I will try to correct:

    import android.content.Context;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.widget.ImageView;
    
    /**
     * ImageView that keeps aspect ratio when scaled
     */
    public class ScaleImageView extends ImageView {
    
      public ScaleImageView(Context context) {
        super(context);
      }
    
      public ScaleImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
      }
    
      public ScaleImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
      }
    
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        try {
          Drawable drawable = getDrawable();
          if (drawable == null) {
            setMeasuredDimension(0, 0);
          } else {
            int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
            int measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
            if (measuredHeight == 0 && measuredWidth == 0) { //Height and width set to wrap_content
              setMeasuredDimension(measuredWidth, measuredHeight);
            } else if (measuredHeight == 0) { //Height set to wrap_content
              int width = measuredWidth;
              int height = width *  drawable.getIntrinsicHeight() / drawable.getIntrinsicWidth();
              setMeasuredDimension(width, height);
            } else if (measuredWidth == 0){ //Width set to wrap_content
              int height = measuredHeight;
              int width = height * drawable.getIntrinsicWidth() / drawable.getIntrinsicHeight();
              setMeasuredDimension(width, height);
            } else { //Width and height are explicitly set (either to match_parent or to exact value)
              setMeasuredDimension(measuredWidth, measuredHeight);
            }
          }
        } catch (Exception e) {
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
      }
    
    }
    

    Thus your ImageView will be scaled properly and will have no dimension problems if (for instance) put inside of ScrollView


  • Translate

    I had a similar problem once. I solved it by making a custom ImageView.

    public class CustomImageView extends ImageView
    

    Then override the onMeasure method of the imageview. I did something like this I believe:

        @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        try {
            Drawable drawable = getDrawable();
    
            if (drawable == null) {
                setMeasuredDimension(0, 0);
            } else {
                float imageSideRatio = (float)drawable.getIntrinsicWidth() / (float)drawable.getIntrinsicHeight();
                float viewSideRatio = (float)MeasureSpec.getSize(widthMeasureSpec) / (float)MeasureSpec.getSize(heightMeasureSpec);
                if (imageSideRatio >= viewSideRatio) {
                    // Image is wider than the display (ratio)
                    int width = MeasureSpec.getSize(widthMeasureSpec);
                    int height = (int)(width / imageSideRatio);
                    setMeasuredDimension(width, height);
                } else {
                    // Image is taller than the display (ratio)
                    int height = MeasureSpec.getSize(heightMeasureSpec);
                    int width = (int)(height * imageSideRatio);
                    setMeasuredDimension(width, height);
                }
            }
        } catch (Exception e) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    

    This will stretch the image to fit the screen while maintaining the aspect ratio.


  • Translate

    Use android:scaleType="centerCrop".


  • Translate

    I did something similar to the above and then banged my head against the wall for a few hours because it did not work inside a RelativeLayout. I ended up with the following code:

    package com.example;
    
    import android.content.Context;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.widget.ImageView;
    
    public class ScaledImageView extends ImageView {
        public ScaledImageView(final Context context, final AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
            final Drawable d = getDrawable();
    
            if (d != null) {
                int width;
                int height;
                if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
                    height = MeasureSpec.getSize(heightMeasureSpec);
                    width = (int) Math.ceil(height * (float) d.getIntrinsicWidth() / d.getIntrinsicHeight());
                } else {
                    width = MeasureSpec.getSize(widthMeasureSpec);
                    height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth());
                }
                setMeasuredDimension(width, height);
            } else {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            }
        }
    }
    

    And then to prevent RelativeLayout from ignoring the measured dimension I did this:

        <FrameLayout
            android:id="@+id/image_frame"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_below="@+id/something">
    
            <com.example.ScaledImageView
                android:id="@+id/image"
                android:layout_width="wrap_content"
                android:layout_height="150dp"/>
        </FrameLayout>
    

  • Translate

    Try this: it solved the problem for me

       android:adjustViewBounds="true"
        android:scaleType="fitXY"
    

  • Translate

    This will not be applicable if you set image as background in ImageView, need to set at src(android:src).

    Thanks.


  • Translate

    Use these properties in ImageView to keep aspect ratio:

    android:adjustViewBounds="true"
    android:scaleType="fitXY"
    

  • Translate

    Yo don't need any java code. You just have to :

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

    The key is in the match parent for width and height


  • Translate

    To create an image with width equals screen width, and height proportionally set according to aspect ratio, do the following.

    Glide.with(context).load(url).asBitmap().into(new SimpleTarget<Bitmap>() {
                        @Override
                        public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
    
                            // creating the image that maintain aspect ratio with width of image is set to screenwidth.
                            int width = imageView.getMeasuredWidth();
                            int diw = resource.getWidth();
                            if (diw > 0) {
                                int height = 0;
                                height = width * resource.getHeight() / diw;
                                resource = Bitmap.createScaledBitmap(resource, width, height, false);
                            }
                                                  imageView.setImageBitmap(resource);
                        }
                    });
    

    Hope this helps.


  • Translate

    try with this simple line... add this line in your xml code in image view tag with out adding any dependency android:scaleType="fitXY"


  • Translate

    use android:ScaleType="fitXY" im ImageView xml


  • Translate

    You can try to do what you're doing by manually loading the images, but I would very very strongly recommend taking a look at Universal Image Loader.

    I recently integrated it into my project and I have to say its fantastic. Does all the worrying about making things asynchronous, resizing, caching images for you. It's really easy to integrate and set up. Within 5 minutes you can probably get it doing what you want.

    Example code:

    //ImageLoader config
    DisplayImageOptions displayimageOptions = new DisplayImageOptions.Builder().showStubImage(R.drawable.downloadplaceholder).cacheInMemory().cacheOnDisc().showImageOnFail(R.drawable.loading).build();
    
        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()).
                defaultDisplayImageOptions(displayimageOptions).memoryCache(new WeakMemoryCache()).discCache(new UnlimitedDiscCache(cacheDir)).build();
    
        if (ImageLoader.getInstance().isInited()) {
            ImageLoader.getInstance().destroy();
        }
        ImageLoader.getInstance().init(config);
    
        imageLoadingListener = new ImageLoadingListener() {
            @Override
            public void onLoadingStarted(String s, View view) {
    
            }
    
            @Override
            public void onLoadingFailed(String s, View view, FailReason failReason) {
                ImageView imageView = (ImageView) view;
                imageView.setImageResource(R.drawable.android);
                Log.i("Failed to Load " + s, failReason.toString());
            }
    
            @Override
            public void onLoadingComplete(String s, View view, Bitmap bitmap) {
    
            }
    
            @Override
            public void onLoadingCancelled(String s, View view) {
    
            }
        };
    
    //Imageloader usage
    ImageView imageView = new ImageView(getApplicationContext());
        if (orientation == 1) {
            imageView.setLayoutParams(new LinearLayout.LayoutParams(width / 6, width / 6));
        } else {
            imageView.setLayoutParams(new LinearLayout.LayoutParams(height / 6, height / 6));
        }
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        imageLoader.displayImage(SERVER_HOSTNAME + "demos" + demo.getPathRoot() + demo.getRootName() + ".png", imageView, imageLoadingListener);
    

    This can lazy load the images, fit them correctly to the size of the imageView showing a placeholder image while it loads, and showing a default icon if loading fails and caching the resources.

    -- I should also add that this current config keeps the image aspect ratio, hence applicable to your original question


  • Translate

    Use picasso which is easy to use..

    In your Adapter ..

    @Override
    public void getView(int position, View convertView, ViewGroup parent) {
    
     ImageView view = (ImageView) convertView.findViewById(R.id.ranking_prod_pic);
    
     Picasso.with(context).load(url).into(view); //url is image url
    
     //you can resize image if you want
    
     /* Picasso.with(context) .load(url) .resize(50, 50) .centerCrop() .into(view) */
    
    }
    

    http://square.github.io/picasso/ enter image description here


  • Translate

    Just use UniversalImageLoader and set

    DisplayImageOptions.Builder()
        .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)
        .build();
    

    and no scale settings on ImageView