ChatGPT解决这个技术问题 Extra ChatGPT

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

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.

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?
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.
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?
See my modified answer, which should do exactly as you wished it :)

D
David Manpearl

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"

This works if you don't want the image to be scaled up if it is too small.
how do i scale it up if its too small and also maintain the aspect ratio ?
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
to scale up small images use scaleType="centerCrop" instead.
one more thing for me to work with this solution is to use "android:src" not the "android:background" to ref my image.
J
Jarno Argillander

(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


The ImageView will always be 250*250.
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.
didn't realize you could replace android: with a:
hi can some one say me what is Ion in the line bitmap = Ion.with(view).getBitmap();
Ion is a framework for asynchronous networking and image loading: github.com/koush/ion
M
Mark van Lent
<ImageView android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:scaleType="centerCrop"
           android:adjustViewBounds="true"/>

N
Naresh Sharma

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.


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.
This actually works perfectly, although you don't need ivHeight or newWidth, just put ivWidth into the calculation instead.
S
SMR

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


This will modify the aspect ratio if the original image is not squared.
fitXY will almost always change the aspect ratio of the image. OP clearly mentions that the aspect ratio MUST be maintained.
T
Thunderstick

this can all be done using XML... the other methods seem pretty complicated. Anyway, you just set the height to what ever you want in dp, then set the width to wrap content or visa versa. Use scaleType fitCenter to adjust the size of the image.

<ImageView
    android:layout_height="200dp"
    android:layout_width="wrap_content"
    android:scaleType="fitCenter"
    android:adjustViewBounds="true"
    android:src="@mipmap/ic_launcher"
    android:layout_below="@+id/title"
    android:layout_margin="5dip"
    android:id="@+id/imageView1">

L
LonsomeHell

The Best solution that works in most cases is

Here is an example:

<ImageView android:id="@+id/avatar"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:scaleType="fitXY"/>

Do not rely on deprecated API (fill_parent)
how does this answer OP's question. This won't maintain aspet ratio
M
MikeyB

After searching for a day, I think this is the easiest solution:

imageView.getLayoutParams().width = 250;
imageView.getLayoutParams().height = 250;
imageView.setAdjustViewBounds(true);

Thanks for your nice answer, But I think it is better to add adjustViewBounds to XML
V
Volodymyr Kulyk

Edited Jarno Argillanders answer: How to fit Image with your Width and Height: 1) Initialize ImageView and set Image:

iv = (ImageView) findViewById(R.id.iv_image);
iv.setImageBitmap(image);

2) Now resize:

scaleImage(iv);

Edited scaleImage method: (you can replace EXPECTED bounding values)

private void scaleImage(ImageView view) {
    Drawable drawing = view.getDrawable();
    if (drawing == null) {
        return;
    }
    Bitmap bitmap = ((BitmapDrawable) drawing).getBitmap();

    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    int xBounding = ((View) view.getParent()).getWidth();//EXPECTED WIDTH
    int yBounding = ((View) view.getParent()).getHeight();//EXPECTED HEIGHT

    float xScale = ((float) xBounding) / width;
    float yScale = ((float) yBounding) / height;

    Matrix matrix = new Matrix();
    matrix.postScale(xScale, yScale);

    Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
    width = scaledBitmap.getWidth();
    height = scaledBitmap.getHeight();
    BitmapDrawable result = new BitmapDrawable(context.getResources(), scaledBitmap);

    view.setImageDrawable(result);

    LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams(); 
    params.width = width;
    params.height = height;
    view.setLayoutParams(params);
}

And .xml:

<ImageView
    android:id="@+id/iv_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal" />

I think this cast: LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams(); should go the other way, since MarginLayoutParams inherit from ViewGroup.LayoutParams.
H
Harvinder Singh

if it's not working for you then replace android:background with android:src

android:src will play the major trick

    <ImageView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:scaleType="fitCenter"
    android:src="@drawable/bg_hc" />

it's working fine like a charm

https://i.stack.imgur.com/w38vF.png


u
user1438038

Use this code:

<ImageView android:id="@+id/avatar"
           android:layout_width="fill_parent"
           android:layout_height="match_parent"
           android:scaleType="fitXY" />

B
Bikram Kumar

This did it for my case.

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

a
alekshandru

I needed to have an ImageView and an Bitmap, so the Bitmap is scaled to ImageView size, and size of the ImageView is the same of the scaled Bitmap :).

I was looking through this post for how to do it, and finally did what I want, not the way described here though.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/acpt_frag_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/imageBackground"
android:orientation="vertical">

<ImageView
    android:id="@+id/acpt_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:adjustViewBounds="true"
    android:layout_margin="@dimen/document_editor_image_margin"
    android:background="@color/imageBackground"
    android:elevation="@dimen/document_image_elevation" />

and then in onCreateView method

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.fragment_scanner_acpt, null);

    progress = view.findViewById(R.id.progress);

    imageView = view.findViewById(R.id.acpt_image);
    imageView.setImageBitmap( bitmap );

    imageView.getViewTreeObserver().addOnGlobalLayoutListener(()->
        layoutImageView()
    );

    return view;
}

and then layoutImageView() code

private void layoutImageView(){

    float[] matrixv = new float[ 9 ];

    imageView.getImageMatrix().getValues(matrixv);

    int w = (int) ( matrixv[Matrix.MSCALE_X] * bitmap.getWidth() );
    int h = (int) ( matrixv[Matrix.MSCALE_Y] * bitmap.getHeight() );

    imageView.setMaxHeight(h);
    imageView.setMaxWidth(w);

}

And the result is that image fits inside perfectly, keeping aspect ratio, and doesn't have extra leftover pixels from ImageView when the Bitmap is inside.

Result

It's important ImageView to have wrap_content and adjustViewBounds to true, then setMaxWidth and setMaxHeight will work, this is written in the source code of ImageView,

/*An optional argument to supply a maximum height for this view. Only valid if
 * {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a
 * maximum of 100 x 100 while preserving the original aspect ratio, do the following: 1) set
 * adjustViewBounds to true 2) set maxWidth and maxHeight to 100 3) set the height and width
 * layout params to WRAP_CONTENT. */

t
the_dude_abides

I needed to get this done in a constraint layout with Picasso, so I munged together some of the above answers and came up with this solution (I already know the aspect ratio of the image I'm loading, so that helps):

Called in my activity code somewhere after setContentView(...)

protected void setBoxshotBackgroundImage() {
    ImageView backgroundImageView = (ImageView) findViewById(R.id.background_image_view);

    if(backgroundImageView != null) {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        int width = displayMetrics.widthPixels;
        int height = (int) Math.round(width * ImageLoader.BOXART_HEIGHT_ASPECT_RATIO);

        // we adjust the height of this element, as the width is already pinned to the parent in xml
        backgroundImageView.getLayoutParams().height = height;

        // implement your Picasso loading code here
    } else {
        // fallback if no element in layout...
    }
}

In my XML

<?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout_editor_absoluteY="0dp"
tools:layout_editor_absoluteX="0dp">

    <ImageView
        android:id="@+id/background_image_view"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="fitStart"
        app:srcCompat="@color/background"
        android:adjustViewBounds="true"
        tools:layout_editor_absoluteY="0dp"
        android:layout_marginTop="0dp"
        android:layout_marginBottom="0dp"
        android:layout_marginRight="0dp"
        android:layout_marginLeft="0dp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <!-- other elements of this layout here... -->

</android.support.constraint.ConstraintLayout>

Note the lack of a constraintBottom_toBottomOf attribute. ImageLoader is my own static class for image loading util methods and constants.


B
Bilal Demir

I am using a very simple solution. Here my code:

imageView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT));
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.getLayoutParams().height = imageView.getLayoutParams().width;
imageView.setMinimumHeight(imageView.getLayoutParams().width);

My pictures are added dynamically in a gridview. When you make these settings to the imageview, the picture can be automatically displayed in 1:1 ratio.


S
Sam

Use Simple math to resize the image . either you can resize ImageView or you can resize drawable image than set on ImageView . find the width and height of your bitmap which you want to set on ImageView and call the desired method. suppose your width 500 is greater than height than call method

//250 is the width you want after resize bitmap
Bitmat bmp = BitmapScaler.scaleToFitWidth(bitmap, 250) ;
ImageView image = (ImageView) findViewById(R.id.picture);
image.setImageBitmap(bmp);

You use this class for resize bitmap.

public class BitmapScaler{
// Scale and maintain aspect ratio given a desired width
// BitmapScaler.scaleToFitWidth(bitmap, 100);
 public static Bitmap scaleToFitWidth(Bitmap b, int width)
  {
    float factor = width / (float) b.getWidth();
    return Bitmap.createScaledBitmap(b, width, (int) (b.getHeight() * factor), true);
  }


  // Scale and maintain aspect ratio given a desired height
  // BitmapScaler.scaleToFitHeight(bitmap, 100);
  public static Bitmap scaleToFitHeight(Bitmap b, int height)
  {
    float factor = height / (float) b.getHeight();
    return Bitmap.createScaledBitmap(b, (int) (b.getWidth() * factor), height, true);
   }
 }

xml code is

<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"
android:scaleType="fitcenter" />

m
myworldbox

Quick answer:

<ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleType="center"
        android:src="@drawable/yourImage"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

V
Vahit Keskin

Just write it in xml

android:scaleType="centerCrop"

Worked for me