Escale la imagen para llenar el ancho de ImageView y mantener la relación de aspecto

198

Tengo una GridView. Los datos deGridView es solicitud de un servidor.

Aquí está el diseño del artículo en 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>

Solicito datos del servidor, obtengo la URL de la imagen y cargo la imagen en 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;
    }
}

y hay un código de getView(int position, View convertView, ViewGroup parent)método en el adaptador

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

El tamaño de la imagen es 210*210. Ejecuto mi aplicación en mi Nexus 4. La imagen llena el ImageViewancho, pero la ImageViewaltura no escala. ImageViewNo muestra toda la imagen.

¿Cómo resuelvo este problema?

Joe
fuente

Respuestas:

544

Sin usar ninguna clase o biblioteca personalizada:

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

scaleType="fitCenter" (predeterminado cuando se omite)

  • lo hará tan ancho como lo permita el padre y aumentará / disminuirá según sea necesario manteniendo la relación de aspecto.

scaleType="centerInside"

  • si el ancho intrínseco de srces menor que el ancho principal
    , centrará la imagen horizontalmente
  • si el ancho intrínseco de srces mayor que el ancho primario,
    lo hará tan ancho como lo permita el primario y reducirá la relación de aspecto.

No importa si usa android:srco ImageView.setImage*métodos y la clave es probablemente la adjustViewBounds.

TWiStErRob
fuente
55
android: layout_height = "wrap_content" android: ajustarViewBounds = "true" android: scaleType = "fitCenter" hace el truco
James Tan
@JamesTan fill_parentpara el ancho es solo una buena práctica, un tamaño fijo también funciona.
TWiStErRob
@TWiStErRob Me doy cuenta de que necesita Android: ajusteViewBounds = "true" con él, de lo contrario no se ajusta en consecuencia para la altura. y sí, el ancho para ajustarse a los padres es la versión completa
James Tan
9
Funciona como encanto en API 19+, pero no en API 16 (probado). La vista personalizada de Alex Semeniuk también funciona en API 16.
Ashkan Sarlak
1
@ F.Mysir whops 😅, sí, no importa cuál sea el problema, pero por difundir las buenas prácticas, estoy totalmente de acuerdo.
TWiStErRob
42

Me gusta la respuesta de arnefm pero cometió un pequeño error (ver comentarios) que intentaré corregir:

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

}

Por lo tanto ImageView, se escalará correctamente y no tendrá problemas de dimensión si (por ejemplo) se coloca dentro deScrollView

Alex Semeniuk
fuente
Gracias, he encontrado la solución, justo debajo de la respuesta de arnefm, el primer comentario que agregué. eso therees un enlace
Joe
36

Tuve un problema similar una vez. Lo resolví haciendo un ImageView personalizado.

public class CustomImageView extends ImageView

Luego anule el método onMeasure de la vista de imagen. Hice algo como esto, creo:

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

Esto estirará la imagen para que se ajuste a la pantalla mientras se mantiene la relación de aspecto.

arnefm
fuente
1
ver allí . la primera respuesta
Joe
Bueno, esta respuesta no es precisa. Considere la situación donde se establece android:layout_height="wrap_content". En este caso MeasureSpec.getSize(heightMeasureSpec)será 0y su viewSideRatioresultado será Infinity(no hay que preguntarse, Java float permite tales valores). En este caso, tanto su heightcomo widthserá 0.
Alex Semeniuk
1
Para que el relleno funcione, necesitaba cambiar (imageSideRatio> = viewSideRatio) a (imageSideRatio <viewSideRatio) ... de lo contrario, una buena respuesta
Marek Halmo
23

Uso android:scaleType="centerCrop".

Mark Buikema
fuente
¡No exactamente lo que estaba pidiendo la pregunta, sino exactamente lo que necesitaba!
nickjm
¿Puedes elaborar @Jameson? ¿Te refieres a las versiones de Android?
Mick
Si uso centerCrop, la imagen se corta un poco en la parte superior e inferior.
Sreekanth Karumanaghat
9

Hice algo similar a lo anterior y luego me golpeé la cabeza contra la pared durante unas horas porque no funcionaba dentro de a RelativeLayout. Terminé con el siguiente código:

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

Y luego, para evitar RelativeLayoutignorar la dimensión medida, hice esto:

    <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>
Dave Cole
fuente
5

Use estas propiedades en ImageView para mantener la relación de aspecto:

android:adjustViewBounds="true"
android:scaleType="fitXY"
Atif Mahmood
fuente
9
fitXY NO mantiene la relación de aspecto. Estira la imagen para que se ajuste exactamente al ancho y la altura del diseño.
Carrito abandonado el
5

Esto no será aplicable si configura la imagen como fondo en ImageView, debe establecerla en src (android: src).

Gracias.

Guna Sekhar
fuente
5

Para crear una imagen con un ancho igual al ancho de la pantalla y una altura establecida proporcionalmente según la relación de aspecto, haga lo siguiente.

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

Espero que esto ayude.

Km de Fathima
fuente
esta es la mejor respuesta que encontré, busqué durante 2 días, gracias fathima
Karthick Ramanathan
4

Prueba esto: me resolvió el problema

   android:adjustViewBounds="true"
    android:scaleType="fitXY"
Mente de ingeniería
fuente
4

No necesitas ningún código java. Sólo tienes que :

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

La clave está en el elemento primario coincidente para ancho y alto

Romu Dizzy
fuente
centerCrop en realidad corta parte de la imagen.
Sreekanth Karumanaghat
2

intente con esta línea simple ... agregue esta línea en su código xml en la etiqueta de vista de imagen sin agregar ninguna dependencia android: scaleType = "fitXY"

ziddi609
fuente
fitXY no mantiene la relación de aspecto, por lo que la imagen probablemente se
estirará
2

use android: ScaleType = "fitXY" im ImageView xml

Naveed Naeem
fuente
fitXY no mantiene la relación de aspecto, por lo que la imagen probablemente se
estirará
1

Puede intentar hacer lo que está haciendo cargando manualmente las imágenes, pero le recomiendo que eche un vistazo a Universal Image Loader .

Recientemente lo integré en mi proyecto y tengo que decir que es fantástico. Hace toda la preocupación por hacer las cosas asíncronas, cambiar el tamaño y almacenar en caché las imágenes para usted. Es realmente fácil de integrar y configurar. En 5 minutos, probablemente pueda hacer lo que quiera.

Código de ejemplo:

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

Esto puede cargar de forma diferida las imágenes, ajustarlas correctamente al tamaño de imageView que muestra una imagen de marcador de posición mientras se carga, y muestra un icono predeterminado si falla la carga y almacena en caché los recursos.

- También debo agregar que esta configuración actual mantiene la relación de aspecto de la imagen, por lo tanto, aplicable a su pregunta original

rcbevans
fuente
0

Simplemente use UniversalImageLoader y establezca

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

y sin configuraciones de escala en ImageView

Artem
fuente
0

Tuve un problema similar, encontré que la razón de esto es porque necesita calcular el dp. Android Studio está calculando el ImageViewcuando se carga desde el drawable, pero cuando se utiliza otro método, como la carga de mapa de bits de ladp que no se contabiliza automáticamente,

Aquí está mi xml

<ImageView
  android:id="@+id/imageViewer"
  android:layout_width="match_parent"
  android:layout_height="match_parent"//dp is not automaticly updated, when loading from a other source
  android:scaleType="fitCenter"
  tools:srcCompat="@drawable/a8" />

Estoy usando Kotlin, y cargando extraíble de un archivo de activos, así es como calculo esto

val d = Drawable.createFromStream(assets.open("imageData/${imageName}.png"), null)
bitHeight = d.minimumHeight//get the image height
imageViewer.layoutParams.height = (bitHeight * resources.displayMetrics.density).toInt()//set the height
imageViewer.setImageDrawable(d)//set the image from the drawable
imageViewer.requestLayout()//here I apply it to the layout
DarkPh03n1X
fuente
-1

Use picasso que es fácil de usar.

En su adaptador ..

@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/ ingrese la descripción de la imagen aquí

chiragkyada
fuente