¿Encajar la imagen en ImageView, mantener la relación de aspecto y luego cambiar el tamaño de ImageView a las dimensiones de la imagen?

164

¿Cómo ajustar una imagen de tamaño aleatorio a una ImageView?
Cuando:

  • Inicialmente las ImageViewdimensiones son 250dp * 250dp
  • La dimensión más grande de la imagen debe ampliarse / reducirse a 250 dp
  • La imagen debe mantener su relación de aspecto.
  • Las ImageViewdimensiones deben coincidir con las dimensiones de la imagen escalada después de escalar

Por ejemplo, para una imagen de 100 * 150, la imagen y la imagen ImageViewdeben ser 166 * 250.
Por ejemplo, para una imagen de 150 * 100, la imagen y la imagen ImageViewdeben ser 250 * 166.

Si establezco los límites como

<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" />

las imágenes se ajustan correctamente en el ImageView, pero ImageViewsiempre es 250dp * 250dp.

jul
fuente
Uh, ¿quieres decir cambiar el tamaño del tamaño de ImageViewla imagen? Por ejemplo, una imagen de 100dp x 150dp se ajustaría ImageViewa las mismas medidas? ¿O quieres decir cómo escalar la imagen a los ImageViewlímites? Por ejemplo, la imagen de 1000dp x 875dp se escalaría a 250dp x 250dp. ¿Necesitas mantener la relación de aspecto?
Jarno Argillander
Quiero que el ImageView tenga las dimensiones de la imagen, y que la imagen tenga su dimensión más grande igual a 250dp y que mantenga su relación de aspecto. Por ejemplo, para una imagen de 100 * 150, quiero que la imagen y el ImageView sean 166 * 250. Actualizaré mi pregunta.
Jul
¿Desea escalar / ajustar solo cuando muestra una actividad (hacer una vez) o cuando hace algo en la actividad como seleccionar una imagen de la galería / web (hacer muchas veces pero no en carga) o ambas?
Jarno Argillander
Véase mi respuesta modificada, lo que debería hacer exactamente lo que usted desea que sea :)
Jarno Argillander

Respuestas:

137

(La respuesta fue muy modificada después de aclaraciones a la pregunta original)

Después de aclaraciones:
esto no se puede hacer solo en xml . No es posible escalar tanto la imagen como la imagen ImageViewpara que la única dimensión de la imagen sea siempre 250dp y ImageViewtenga las mismas dimensiones que la imagen.

Este código escalas Drawable de una ImageViewa permanecer en una plaza como 250dp x 250dp con una dimensión exactamente 250dp y mantener la relación de aspecto. Luego ImageViewse cambia el tamaño para que coincida con las dimensiones de la imagen a escala. El código se usa en una actividad. Lo probé a través del controlador de clic de botón.

Disfrutar. :)

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);
}

El código xml para 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"/>


Gracias a esta discusión por el código de escala:
http://www.anddev.org/resize_and_rotate_image_-_example-t621.html


ACTUALIZACIÓN 7 de noviembre de 2012: se
agregó la comprobación de puntero nulo como se sugiere en los comentarios

Jarno Argillander
fuente
1
El ImageView siempre será 250 * 250.
Jul
2
Okay. Eso no se puede hacer solo en xml. Se requiere código Java. Con xml puedes escalar la imagen o el ImageView, no ambos.
Jarno Argillander
93
no se dio cuenta de que podía reemplazar Android: con un:
StackOverflowed
2
Ion es un marco para redes asíncronas y carga de imágenes: github.com/koush/ion
Thomas
1
Java es un lenguaje extremadamente feo porque requiere escribir mucho código para tareas tan simples.
Dmitry
245

Puede que no sea la respuesta para esta pregunta específica, pero si alguien, como yo, está buscando una respuesta para ajustar la imagen en ImageView con un tamaño acotado (por ejemplo, maxWidth) mientras conserva la relación de aspecto y luego elimina el espacio excesivo ocupado por ImageView, entonces la solución más simple es usar las siguientes propiedades en XML:

    android:scaleType="centerInside"
    android:adjustViewBounds="true"
Nombre para mostrar
fuente
13
Esto funciona si no desea que la imagen se amplíe si es demasiado pequeña.
Janusz
¿Cómo lo escalo si es demasiado pequeño y también mantengo la relación de aspecto?
Kaustubh Bhagwat
si alguien lo necesita, "fitCenter" es otro atributo para scaleType, y no escalará la imagen, pero para cualquier imagen grande, se ajustará al tamaño máximo de la imagen dentro del cuadro de vista manteniendo la relación de aspecto
yogesh prajapati
para escalar imágenes pequeñas use scaleType = "centerCrop" en su lugar.
Eaweb
Una cosa más para trabajar con esta solución es usar "android: src" y no "android: background" para hacer referencia a mi imagen.
Codingpan
45
<ImageView android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:scaleType="centerCrop"
           android:adjustViewBounds="true"/>
diesel
fuente
23

El código de abajo hace el mapa de bits perfectamente con el mismo tamaño de la vista de imagen. Obtenga la altura y el ancho de la imagen de mapa de bits y luego calcule la nueva altura y ancho con la ayuda de los parámetros de imageview. Eso le da la imagen requerida con la mejor relación de aspecto.

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)

disfrutar.

Naresh Sharma
fuente
3
Esto solo reducirá la altura original en el mismo factor por el cual se redujo el ancho. Esto no garantizará que newHeight <ivHeight. Idealmente, debe verificar qué proporción es mayor (currentBitmapHeight / ivHeight, currentBitmapWidth / ivWidth) y luego, sobre la base de esto, tomar más decisiones.
Sumit Trehan
1
Esto realmente funciona perfectamente, aunque no necesita ivHeight o newWidth, simplemente coloque ivWidth en el cálculo.
Stuart
14

intenta agregar android:scaleType="fitXY"a tu ImageView.

DeeFour
fuente
55
Esto modificará la relación de aspecto si la imagen original no está al cuadrado.
jul
1
fitXYcasi siempre cambiará la relación de aspecto de la imagen. OP menciona claramente que la relación de aspecto DEBE mantenerse.
IcyFlame
7

Después de buscar un día, creo que esta es la solución más fácil:

imageView.getLayoutParams().width = 250;
imageView.getLayoutParams().height = 250;
imageView.setAdjustViewBounds(true);
MikeyB
fuente
2
Gracias por su buena respuesta, pero creo que es mejor agregar adjustViewBoundsa XML
7

La mejor solución que funciona en la mayoría de los casos es

Aquí hay un ejemplo:

<ImageView android:id="@+id/avatar"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:scaleType="fitXY"/>
Nikita Vishwakarma
fuente
1
No confíe en API en desuso (fill_parent)
fdermishin
¿Cómo responde esto a la pregunta de OP? Esto no mantendrá una proporción de aspeto
Alex
6

Todo esto se puede hacer usando XML ... los otros métodos parecen bastante complicados. De todos modos, solo establece la altura a lo que quiera en dp, luego establece el ancho para envolver el contenido o viceversa. Use scaleType fitCenter para ajustar el tamaño de la imagen.

<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">
Thunderstick
fuente
4

Usa este código:

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

Respuesta editada de Jarno Argillanders :

Cómo ajustar la imagen con su ancho y alto:

1) Inicialice ImageView y configure Image:

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

2) Ahora cambia el tamaño:

scaleImage(iv);

scaleImageMétodo editado : ( puede reemplazar los valores de límite ESPERADOS )

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);
}

Y .xml:

<ImageView
    android:id="@+id/iv_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal" />
Volodymyr Kulyk
fuente
Creo que este elenco: LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams (); debería ir a la inversa, ya que MarginLayoutParams hereda de ViewGroup.LayoutParams.
Jay Jacobs
3

Esto lo hizo para mi caso.

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

si no funciona para usted, reemplace android: background con android: src

android: src jugará el mayor truco

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

funciona bien como un encanto

ingrese la descripción de la imagen aquí

Harvinder Singh
fuente
1

Necesitaba tener un ImageView y un Bitmap, por lo que el Bitmap se escala al tamaño de ImageView, y el tamaño de ImageView es el mismo del Bitmap escalado :).

Estaba buscando en esta publicación cómo hacerlo, y finalmente hice lo que quería, no de la manera descrita aquí.

<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" />

y luego en el método onCreateView

@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;
}

y luego el código layoutImageView ()

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);

}

Y el resultado es que la imagen encaja perfectamente en el interior, manteniendo la relación de aspecto, y no tiene píxeles sobrantes adicionales de ImageView cuando el mapa de bits está dentro.

Resultado

Es importante que ImageView tenga wrap_content y ajuste ViewBounds a true, luego setMaxWidth y setMaxHeight funcionarán, esto está escrito en el código fuente de 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. */
alekshandru
fuente
0

Necesitaba hacer esto en un diseño de restricción con Picasso, así que reuní algunas de las respuestas anteriores y se me ocurrió esta solución (ya sé la relación de aspecto de la imagen que estoy cargando, así que eso ayuda):

Llamé a mi código de actividad en algún lugar después de 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...
    }
}

En mi 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>

Tenga en cuenta la falta de un atributo restrictintBottom_toBottomOf. ImageLoader es mi propia clase estática para métodos de carga de imágenes y constantes.

el tipo respeta
fuente
0

Estoy usando una solución muy simple. Aquí mi código:

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);

Mis imágenes se agregan dinámicamente en una vista de cuadrícula. Cuando realiza estos ajustes en la vista de imagen, la imagen se puede mostrar automáticamente en una proporción de 1: 1.

Bilal Demir
fuente
0

Utilice las matemáticas simples para cambiar el tamaño de la imagen. puede cambiar el tamaño ImageViewo puede cambiar el tamaño de la imagen dibujable que la establecida ImageView. encuentre el ancho y la altura de su mapa de bits en el que desea establecer ImageViewy llame al método deseado. supongamos que su ancho 500 es mayor que la altura que el método de llamada

//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);

Utiliza esta clase para cambiar el tamaño del mapa de bits.

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);
   }
 }

el código xml es

<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" />
Ritesh Namdev
fuente
0

Respuesta rápida:

<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" />
myworldbox
fuente