Fit image into ImageView, keep aspect ratio and then resize ImageView to image dimensions?

358,709

Solution 1

(The answer was heavily modified after clarifications to the original question)

After clarifications:
This cannot be done in xml only. It is not possible to scale both the image and the ImageView so that image's one dimension would always be 250dp and the ImageView would have the same dimensions as the image.

This code scales Drawable of an ImageView to stay in a square like 250dp x 250dp with one dimension exactly 250dp and keeping the aspect ratio. Then the ImageView is resized to match the dimensions of the scaled image. The code is used in an activity. I tested it via button click handler.

Enjoy. :)

private void scaleImage(ImageView view) throws NoSuchElementException  {
    // Get bitmap from the the ImageView.
    Bitmap bitmap = null;

    try {
        Drawable drawing = view.getDrawable();
        bitmap = ((BitmapDrawable) drawing).getBitmap();
    } catch (NullPointerException e) {
        throw new NoSuchElementException("No drawable on given view");
    } catch (ClassCastException e) {
        // Check bitmap is Ion drawable
        bitmap = Ion.with(view).getBitmap();
    }

    // Get current dimensions AND the desired bounding box
    int width = 0;

    try {
        width = bitmap.getWidth();
    } catch (NullPointerException e) {
        throw new NoSuchElementException("Can't find bitmap on given view/drawable");
    }

    int height = bitmap.getHeight();
    int bounding = dpToPx(250);
    Log.i("Test", "original width = " + Integer.toString(width));
    Log.i("Test", "original height = " + Integer.toString(height));
    Log.i("Test", "bounding = " + Integer.toString(bounding));

    // Determine how much to scale: the dimension requiring less scaling is
    // closer to the its side. This way the image always stays inside your
    // bounding box AND either x/y axis touches it.  
    float xScale = ((float) bounding) / width;
    float yScale = ((float) bounding) / height;
    float scale = (xScale <= yScale) ? xScale : yScale;
    Log.i("Test", "xScale = " + Float.toString(xScale));
    Log.i("Test", "yScale = " + Float.toString(yScale));
    Log.i("Test", "scale = " + Float.toString(scale));

    // Create a matrix for the scaling and add the scaling data
    Matrix matrix = new Matrix();
    matrix.postScale(scale, scale);

    // Create a new bitmap and convert it to a format understood by the ImageView 
    Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
    width = scaledBitmap.getWidth(); // re-use
    height = scaledBitmap.getHeight(); // re-use
    BitmapDrawable result = new BitmapDrawable(scaledBitmap);
    Log.i("Test", "scaled width = " + Integer.toString(width));
    Log.i("Test", "scaled height = " + Integer.toString(height));

    // Apply the scaled bitmap
    view.setImageDrawable(result);

    // Now change ImageView's dimensions to match the scaled image
    LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams(); 
    params.width = width;
    params.height = height;
    view.setLayoutParams(params);

    Log.i("Test", "done");
}

private int dpToPx(int dp) {
    float density = getApplicationContext().getResources().getDisplayMetrics().density;
    return Math.round((float)dp * density);
}

The xml code for the ImageView:

<ImageView a:id="@+id/image_box"
    a:background="#ff0000"
    a:src="@drawable/star"
    a:layout_width="wrap_content"
    a:layout_height="wrap_content"
    a:layout_marginTop="20dp"
    a:layout_gravity="center_horizontal"/>


Thanks to this discussion for the scaling code:
http://www.anddev.org/resize_and_rotate_image_-_example-t621.html


UPDATE 7th, November 2012:
Added null pointer check as suggested in comments

Solution 2

May not be answer for this specific question, but if someone is, like me, searching for answer how to fit image in ImageView with bounded size (for example, maxWidth) while preserving Aspect Ratio and then get rid of excessive space occupied by ImageView, then the simplest solution is to use the following properties in XML:

    android:scaleType="centerInside"
    android:adjustViewBounds="true"

Solution 3

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

Solution 4

The Below code make the bitmap perfectly with same size of the imageview. Get the bitmap image height and width and then calculate the new height and width with the help of imageview's parameters. That give you required image with best aspect ratio.

int currentBitmapWidth = bitMap.getWidth();
int currentBitmapHeight = bitMap.getHeight();

int ivWidth = imageView.getWidth();
int ivHeight = imageView.getHeight();
int newWidth = ivWidth;

newHeight = (int) Math.floor((double) currentBitmapHeight *( (double) new_width / (double) currentBitmapWidth));

Bitmap newbitMap = Bitmap.createScaledBitmap(bitMap, newWidth, newHeight, true);

imageView.setImageBitmap(newbitMap)

enjoy.

Solution 5

try adding android:scaleType="fitXY" to your ImageView.

Share:
358,709

Related videos on Youtube

jul
Author by

jul

Updated on July 27, 2022

Comments

  • jul
    jul almost 2 years

    How to fit an image of random size to an ImageView?
    When:

    • Initially ImageView dimensions are 250dp * 250dp
    • The image's larger dimension should be scaled up/down to 250dp
    • The image should keep its aspect ratio
    • The ImageView dimensions should match scaled image's dimensions after scaling

    E.g. for an image of 100*150, the image and the ImageView should be 166*250.
    E.g. for an image of 150*100, the image and the ImageView should be 250*166.

    If I set the bounds as

    <ImageView
        android:id="@+id/picture"
        android:layout_width="250dp"
        android:layout_height="250dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:adjustViewBounds="true" />
    

    images fit properly in the ImageView, but the ImageView is always 250dp * 250dp.

    • Jarno Argillander
      Jarno Argillander over 12 years
      Uh, do you mean to change the size of the ImageView to the image size? E.g. image of 100dp x 150dp would scale ImageView to the same measures? Or do you mean how to scale the image to the ImageView bounds. E.g. image of 1000dp x 875dp would be scaled into 250dp x 250dp. Do you need to maintain aspect ratio?
    • jul
      jul over 12 years
      I want the ImageView to have the dimensions of the image, and the image to have its largest dimension equals to 250dp and to keep its aspect ratio. E.g. for an image of 100*150, I want the image and the ImageView to be 166*250. I'll update my question.
    • Jarno Argillander
      Jarno Argillander over 12 years
      Do you want to do scaling/adjustment only when displaying an activity (do once) or when doing something on the activity like selecting a picture from gallery/web (do many times but not on load) or both?
    • Jarno Argillander
      Jarno Argillander over 12 years
      See my modified answer, which should do exactly as you wished it :)
  • jul
    jul over 12 years
    The ImageView will always be 250*250.
  • Jarno Argillander
    Jarno Argillander over 12 years
    Ok. That cannot be done in xml only. Java code is required. With xml you can either scale the image or the ImageView, not both.
  • StackOverflowed
    StackOverflowed over 11 years
    didn't realize you could replace android: with a:
  • jul
    jul almost 11 years
    This will modify the aspect ratio if the original image is not squared.
  • Admin
    Admin almost 10 years
    Thanks for your nice answer, But I think it is better to add adjustViewBounds to XML
  • SHRISH M
    SHRISH M over 9 years
    This works if you don't want the image to be scaled up if it is too small.
  • Sumit Trehan
    Sumit Trehan over 8 years
    This will just reduce the original height by the same factor by which the width was reduced. This won't gurantee that newHeight < ivHeight. Ideally you should check which ratio is bigger (currentBitmapHeight /ivHeight , currentBitmapWidth /ivWidth ) and then on the basis of this take further decision.
  • Karthik
    Karthik about 8 years
    hi can some one say me what is Ion in the line bitmap = Ion.with(view).getBitmap();
  • IcyFlame
    IcyFlame almost 8 years
    fitXY will almost always change the aspect ratio of the image. OP clearly mentions that the aspect ratio MUST be maintained.
  • Thomas
    Thomas over 7 years
    Ion is a framework for asynchronous networking and image loading: github.com/koush/ion
  • Jay M
    Jay M almost 7 years
    I think this cast: LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams(); should go the other way, since MarginLayoutParams inherit from ViewGroup.LayoutParams.
  • Chirag Jain
    Chirag Jain over 6 years
    Thanks, +1 for you, It really helped me.
  • Chirag Jain
    Chirag Jain over 6 years
    How can we do same thing for view like Relative Layout with maintaining aspect ratio.
  • Kaustubh Bhagwat
    Kaustubh Bhagwat over 6 years
    how do i scale it up if its too small and also maintain the aspect ratio ?
  • yogesh prajapati
    yogesh prajapati over 6 years
    if someone needs, "fitCenter" is another attribute for scaleType, and it will not scale up the image, but for any big image, it will fit the maximum size of the image inside the view box maintaining the aspect ratio
  • fdermishin
    fdermishin over 6 years
    Do not rely on deprecated API (fill_parent)
  • Eaweb
    Eaweb over 5 years
    to scale up small images use scaleType="centerCrop" instead.
  • Alex
    Alex about 5 years
    how does this answer OP's question. This won't maintain aspet ratio
  • Stuart
    Stuart over 4 years
    This actually works perfectly, although you don't need ivHeight or newWidth, just put ivWidth into the calculation instead.
  • Codingpan
    Codingpan over 4 years
    one more thing for me to work with this solution is to use "android:src" not the "android:background" to ref my image.
  • haart
    haart about 3 years
    If someone just needs a resized ImageView, you can skip the middle part and just resize the ImageView and after that set the drawable which will adapt to it following its scaleType