java - programa - relacion de aspecto del dvd 4 3 o 16 9
Android: ¿Cómo estirar una imagen al ancho de la pantalla mientras se mantiene la relación de aspecto? (17)
¡Su simple cuestión de establecer adjustViewBounds="true"
y scaleType="fitCenter"
en el archivo XML para ImageView!
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/image"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
/>
Nota: layout_width
está establecido en match_parent
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));
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://.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);
}
}
}
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));
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.
<com.parse.ParseImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/post_image"
android:src="@drawable/icon_192"
android:layout_margin="0dp"
android:cropToPadding="false"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:background="#eff2eb"/>
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.
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);
Esto funciona bien según mi requisito
<ImageView android:id="@+id/imgIssue" android:layout_width="fill_parent" android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="fitXY"/>
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);
}
}
}
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
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);
}
}
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)
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:src="@drawable/whatever" />
</FrameLayout>
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);
}
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.
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);
}
}
}
}
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á.
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:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:id="@+id/button_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentBottom="true"
>
<Button
android:text="button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:text="button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:text="button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<ImageView
android:src="@drawable/cat"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:layout_above="@id/button_container"/>
</RelativeLayout>
</ScrollView>
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.