Android: ¿Cómo estirar una imagen al ancho de la pantalla mientras se mantiene la relación de aspecto?

Quiero descargar una imagen (de tamaño desconocido, pero que siempre es más o menos cuadrada) y mostrarla de modo que ocupe la pantalla horizontalmente y se extienda verticalmente para mantener la relación de aspecto de la imagen en cualquier tamaño de pantalla. Aquí está mi código (no funciona). Estira la imagen horizontalmente, pero no verticalmente, por lo que queda aplastada …

ImageView mainImageView = new ImageView(context); mainImageView.setImageBitmap(mainImage); //downloaded from server mainImageView.setScaleType(ScaleType.FIT_XY); //mainImageView.setAdjustViewBounds(true); //with this line enabled, just scales image down addView(mainImageView,new LinearLayout.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); 

Lo logré con una vista personalizada. Establezca layout_width = “fill_parent” y layout_height = “wrap_content”, y apúntelo al drawable apropiado:

 public class Banner extends View { private final Drawable logo; public Banner(Context context) { super(context); logo = context.getResources().getDrawable(R.drawable.banner); setBackgroundDrawable(logo); } public Banner(Context context, AttributeSet attrs) { super(context, attrs); logo = context.getResources().getDrawable(R.drawable.banner); setBackgroundDrawable(logo); } public Banner(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); logo = context.getResources().getDrawable(R.drawable.banner); setBackgroundDrawable(logo); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = width * logo.getIntrinsicHeight() / logo.getIntrinsicWidth(); setMeasuredDimension(width, height); } } 

Al final, generé las dimensiones manualmente, lo que funciona muy bien:

 DisplayMetrics dm = new DisplayMetrics(); context.getWindowManager().getDefaultDisplay().getMetrics(dm); int width = dm.widthPixels; int height = width * mainImage.getHeight() / mainImage.getWidth(); //mainImage is the Bitmap I'm drawing addView(mainImageView,new LinearLayout.LayoutParams( width, height)); 

Acabo de leer el código fuente de ImageView y es básicamente imposible sin usar las soluciones de subclases en este hilo. En ImageView.onMeasure llegamos a estas líneas:

  // Get the max possible width given our constraints widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec); // Get the max possible height given our constraints heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec); 

Donde h y w son las dimensiones de la imagen, y p* es el relleno.

Y entonces:

 private int resolveAdjustedSize(int desiredSize, int maxSize, int measureSpec) { ... switch (specMode) { case MeasureSpec.UNSPECIFIED: /* Parent says we can be as big as we want. Just don't be larger than max size imposed on ourselves. */ result = Math.min(desiredSize, maxSize); 

Entonces, si tiene un layout_height="wrap_content" , establecerá widthSize = w + pleft + pright , o en otras palabras, el ancho máximo es igual al ancho de la imagen.

Esto significa que a menos que establezca un tamaño exacto, las imágenes NUNCA se agrandarán . Considero que esto es un error, pero buena suerte para que Google lo note o lo solucione. Editar: Comer mis propias palabras, envié un informe de error y dicen que se ha corregido en una versión futura.

Otra solución

Aquí hay otra solución alternativa de subclases, pero debería (en teoría, ¡no lo he probado realmente mucho!) Poder usarlo en cualquier lugar que tenga ImageView . Para usarlo, establece layout_width="match_parent" y layout_height="wrap_content" . Es bastante más general que la solución aceptada también. Por ejemplo, puede ajustar tanto a la altura como a la anchura.

 import android.content.Context; import android.util.AttributeSet; import android.widget.ImageView; // This works around the issue described here: http://stackoverflow.com/a/12675430/265521 public class StretchyImageView extends ImageView { public StretchyImageView(Context context) { super(context); } public StretchyImageView(Context context, AttributeSet attrs) { super(context, attrs); } public StretchyImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Call super() so that resolveUri() is called. super.onMeasure(widthMeasureSpec, heightMeasureSpec); // If there's no drawable we can just use the result from super. if (getDrawable() == null) return; final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int w = getDrawable().getIntrinsicWidth(); int h = getDrawable().getIntrinsicHeight(); if (w <= 0) w = 1; if (h <= 0) h = 1; // Desired aspect ratio of the view's contents (not including padding) float desiredAspect = (float) w / (float) h; // We are allowed to change the view's width boolean resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; // We are allowed to change the view's height boolean resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; int pleft = getPaddingLeft(); int pright = getPaddingRight(); int ptop = getPaddingTop(); int pbottom = getPaddingBottom(); // Get the sizes that ImageView decided on. int widthSize = getMeasuredWidth(); int heightSize = getMeasuredHeight(); if (resizeWidth && !resizeHeight) { // Resize the width to the height, maintaining aspect ratio. int newWidth = (int) (desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright; setMeasuredDimension(newWidth, heightSize); } else if (resizeHeight && !resizeWidth) { int newHeight = (int) ((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom; setMeasuredDimension(widthSize, newHeight); } } } 

Establecer adjustViewBounds en true y usar un grupo de vistas LinearLayout funcionó muy bien para mí. No es necesario crear una subclase o solicitar métricas del dispositivo:

 //NOTE: "this" is a subclass of LinearLayout ImageView splashImageView = new ImageView(context); splashImageView.setImageResource(R.drawable.splash); splashImageView.setAdjustViewBounds(true); addView(splashImageView); 

He estado luchando con este problema de una forma u otra para AGES, gracias, gracias, GRACIAS … 🙂

Solo quería señalar que puede obtener una solución generalizable de lo que hace Bob Lee simplemente ampliando View y reemplazando onMeasure. De esta forma, puedes usar esto con cualquier dibujo que quieras, y no se romperá si no hay imagen:

  public class CardImageView extends View { public CardImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public CardImageView(Context context, AttributeSet attrs) { super(context, attrs); } public CardImageView(Context context) { super(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Drawable bg = getBackground(); if (bg != null) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = width * bg.getIntrinsicHeight() / bg.getIntrinsicWidth(); setMeasuredDimension(width,height); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } } 

En algunos casos, esta fórmula mágica resuelve maravillosamente el problema.

Para cualquier persona que deba lidiar con esto desde otra plataforma, la opción “tamaño y forma de ajuste” se maneja muy bien en Android, pero es difícil de encontrar.

Por lo general, desea esta combinación:

  • el ancho coincide con el padre,
  • contenido de altura envolvente,
  • adjustViewBounds ENCENDIDO (sic)
  • ajuste de escalaCentro
  • cropToPadding desactivado (sic)

Entonces es automático e increíble.

Si eres un desarrollador de iOS, es increíble cómo puedes hacer “alturas de celda totalmente dinámicas” en una vista de tabla … me refiero a ListView. Disfrutar.

  

enter image description here

Lo hice con estos valores dentro de LinearLayout:

 Scale type: fitStart Layout gravity: fill_horizontal Layout height: wrap_content Layout weight: 1 Layout width: fill_parent 

Logré lograr esto usando solo este código XML. Puede ser que el eclipse no represente la altura para mostrar que se expande para adaptarse; sin embargo, cuando realmente ejecuta esto en un dispositivo, rinde y proporciona el resultado deseado. (Bueno, al menos para mi)

    

Todo el mundo está haciendo esto programmily así que pensé que esta respuesta encajaría perfectamente aquí. Este código funcionó para mi en el xml. Aún no estoy pensando en la proporción, pero aún así quería colocar esta respuesta si ayudaría a alguien.

 android:adjustViewBounds="true" 

Aclamaciones..

Una solución muy simple es usar las funciones proporcionadas por RelativeLayout .

Aquí está el xml que lo hace posible con las Views estándar de Android:

            

El truco es que configura ImageView para que ocupe toda la pantalla, pero tiene que estar encima de los otros diseños. De esta manera logras todo lo que necesitas.

¡Su simple cuestión de establecer adjustViewBounds="true" y scaleType="fitCenter" en el archivo XML para ImageView!

  

Nota: layout_width está establecido en match_parent

Está configurando ScaleType en ScaleType.FIT_XY. De acuerdo con los javadocs , esto estirará la imagen para que se ajuste a toda el área, cambiando la relación de aspecto si es necesario. Eso explicaría el comportamiento que estás viendo.

Para obtener el comportamiento que desea … FIT_CENTER, FIT_START o FIT_END están cerca, pero si la imagen es más estrecha que alta, no comenzará a llenar el ancho. Sin embargo, podría ver cómo se implementan, y probablemente debería ser capaz de descubrir cómo ajustarlo para su propósito.

ScaleType.CENTER_CROP hará lo que usted desee: estirar hasta el ancho completo y escalar la altura en consecuencia. si la altura de la escala excede los límites de la pantalla, la imagen se recortará.

Mire que hay una solución mucho más fácil para su problema:

 ImageView imageView; protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.your_layout); imageView =(ImageView)findViewById(R.id.your_imageView); Bitmap imageBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.your_image); Point screenSize = new Point(); getWindowManager().getDefaultDisplay().getSize(screenSize); Bitmap temp = Bitmap.createBitmap(screenSize.x, screenSize.x, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(temp); canvas.drawBitmap(imageBitmap,null, new Rect(0,0,screenSize.x,screenSize.x), null); imageView.setImageBitmap(temp); } 

Puedes usar mi StretchableImageView conservando la relación de aspecto (por ancho o por alto) dependiendo del ancho y alto de dibujable:

 import android.content.Context; import android.util.AttributeSet; import android.widget.ImageView; public class StretchableImageView extends ImageView{ public StretchableImageView(Context context) { super(context); } public StretchableImageView(Context context, AttributeSet attrs) { super(context, attrs); } public StretchableImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if(getDrawable()!=null){ if(getDrawable().getIntrinsicWidth()>=getDrawable().getIntrinsicHeight()){ int width = MeasureSpec.getSize(widthMeasureSpec); int height = width * getDrawable().getIntrinsicHeight() / getDrawable().getIntrinsicWidth(); setMeasuredDimension(width, height); }else{ int height = MeasureSpec.getSize(heightMeasureSpec); int width = height * getDrawable().getIntrinsicWidth() / getDrawable().getIntrinsicHeight(); setMeasuredDimension(width, height); } } } } 

Para mí, el android: scaleType = “centerCrop” no resolvió mi problema. De hecho, amplió la imagen mucho más. Así que probé con android: scaleType = “fitXY” y funcionó excelente.

Esto funciona bien según mi requisito