Android: ¿Escalar una imagen dibujable o de fondo?

134

En un diseño, quiero escalar la imagen de fondo (manteniendo su relación de aspecto) al espacio asignado cuando se crea la página. ¿Alguien tiene una idea de cómo hacer esto?

Estoy usando layout.setBackgroundDrawable()y a BitmapDrawable()para configurar el gravityrecorte y el relleno, pero no veo ninguna opción para escalar.

finnmglas
fuente
Tuve este problema y seguí la recomendación de Dweebo con el cambio sugerido por Anke: cambió fitXY a fitCenter. Funcionado bien.

Respuestas:

90

Para personalizar la escala de la imagen de fondo, cree un recurso como este:

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:gravity="center"
    android:src="@drawable/list_bkgnd" />

Luego se centrará en la vista si se usa como fondo. También hay otras banderas: http://developer.android.com/guide/topics/resources/drawable-resource.html

Aleks N.
fuente
66
Intenté usar un mapa de bits, pero la imagen está simplemente centrada, no escalada como Sam pregunta. Cuando uso ImageView, funciona de maravilla, sin complicaciones. (Nota: tampoco estoy desplazándome en mi caso). ¿Con qué aumentaría el mapa de bits para escalar la imagen?
Joe D'Andrea
77
Los valores posibles son: "top" | "fondo" | "izquierda" | "correcto" | "centro_vertical" | "fill_vertical" | "center_horizontal" | "fill_horizontal" | "centro" | "llenar" | "clip_vertical" | "clip_horizontal"
Aleks N.
30
ninguno de estos parámetros escala el mapa de bits, manteniendo su relación de aspecto, por lo que la respuesta es incorrecta.
Malachiasz
77
Querido Malachiasz, 'centro' respeta la relación de aspecto. Pero no reducirá la imagen. Lo que significa que es una característica algo peligrosa de usar. Dependiendo de la resolución del objetivo, nunca se sabe cuán escalado saldrá la cosa exactamente. Otra solución a medias de Google.
2
tiene una manera de agregar el atributo scaleType?
ademar111190
57

No he intentado hacer exactamente lo que desea, pero puede escalar un ImageView usando android:scaleType="fitXY"
y se ajustará al tamaño que le dé al ImageView.

Por lo tanto, puede crear un FrameLayout para su diseño, colocar ImageView dentro de él y luego cualquier otro contenido que necesite en FrameLayout también con un fondo transparente.

<FrameLayout  
android:layout_width="fill_parent" android:layout_height="fill_parent">

  <ImageView 
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:src="@drawable/back" android:scaleType="fitXY" />

  <LinearLayout>your views</LinearLayout>
</FrameLayout>
dweebo
fuente
1
¿Es posible hacer esto en una superficie? Si no, ¿puedo mostrar la vista previa (para una aplicación de grabadora) usando FrameLayout?
Namratha
1
@dweebo: ¿Has probado esto? No parece estar funcionando para mí.
Speedplane
3
Votado negativamente ya que usar dos componentes de UI en lugar de uno es una mala idea, especialmente para componentes tan ocupados como ListView. Lo más probable es que tengas problemas mientras te desplazas. Solución adecuada: stackoverflow.com/a/9362168/145046
Aleks N.
Probé un scaleType de fitCenter y centerCrop (junto con algunas variaciones del dibujo / imagen para diferentes densidades) y funcionó. Cualquier tipo de escala tenía sentido para mí, sospecho que es más una preferencia personal basada en la imagen. Tenga en cuenta que usé merge en lugar de FrameLayout, y no hay desplazamiento en mi caso particular.
Joe D'Andrea
2
centerInside es correcto, no fitXY, ya que fitXY no mantiene la relación de aspecto de la imagen
Malachiasz
35

Hay una manera fácil de hacer esto desde el sorteo:

your_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:drawable="@color/bg_color"/>
    <item>
        <bitmap
            android:gravity="center|bottom|clip_vertical"
            android:src="@drawable/your_image" />
    </item>

</layer-list>

El único inconveniente es que si no hay suficiente espacio, su imagen no se mostrará completamente, pero se recortará, no pude encontrar una manera de hacerlo directamente desde un dibujo. Pero a partir de las pruebas que hice, funciona bastante bien y no recorta gran parte de la imagen. Podrías jugar más con las opciones de gravedad.

Otra forma será la de simplemente crear un diseño, donde usará una ImageViewy establecer la scaleTypea fitCenter.

Ionut Negru
fuente
2
Esta solución es definitivamente lo que estaba buscando durante mucho tiempo para reemplazar la propiedad de fondo de mi tema y evitar una escala de imagen diferente cuando la vista tiene pestañas o no. Gracias por compartir esto.
Bibu
1
Muestra error:error: <item> must have a 'name' attribute.
Dmitry
gracias @lonutNegru, +1 por salvar mi día :)
Ravi Vaniya
18

Para mantener la relación de aspecto que tiene que usar android:scaleType=fitCentero fitStartetc. UsofitXY no mantener la relación de aspecto original de la imagen!

Tenga en cuenta que esto solo funciona para imágenes con un srcatributo, no para la imagen de fondo.

Anke
fuente
18

Use la imagen como fondo dimensionado para el diseño:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <ImageView
        android:id="@+id/imgPlaylistItemBg"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:adjustViewBounds="true"
        android:maxHeight="0dp"
        android:scaleType="fitXY"
        android:src="@drawable/img_dsh" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >


    </LinearLayout>

</FrameLayout>
Konstantin Konopko
fuente
¡Gracias! También android:scaleType="centerCrop"funciona
CoolMind
5

Cuando establece el Drawable de un ImageView usando el setBackgroundDrawablemétodo, la imagen siempre se escalará. Los parámetros como adjustViewBoundso diferentes ScaleTypessimplemente serán ignorados. La única solución para mantener la relación de aspecto que encontré es cambiar el tamaño ImageViewdespués de cargar su dibujo. Aquí está el fragmento de código que utilicé:

// bmp is your Bitmap object
int imgHeight = bmp.getHeight();
int imgWidth = bmp.getWidth();
int containerHeight = imageView.getHeight();
int containerWidth = imageView.getWidth();
boolean ch2cw = containerHeight > containerWidth;
float h2w = (float) imgHeight / (float) imgWidth;
float newContainerHeight, newContainerWidth;

if (h2w > 1) {
    // height is greater than width
    if (ch2cw) {
        newContainerWidth = (float) containerWidth;
        newContainerHeight = newContainerWidth * h2w;
    } else {
        newContainerHeight = (float) containerHeight;
        newContainerWidth = newContainerHeight / h2w;
    }
} else {
    // width is greater than height
    if (ch2cw) {
        newContainerWidth = (float) containerWidth;
        newContainerHeight = newContainerWidth / h2w; 
    } else {
        newContainerWidth = (float) containerHeight;
        newContainerHeight = newContainerWidth * h2w;       
    }
}
Bitmap copy = Bitmap.createScaledBitmap(bmp, (int) newContainerWidth, (int) newContainerHeight, false);
imageView.setBackgroundDrawable(new BitmapDrawable(copy));
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
imageView.setLayoutParams(params);
imageView.setMaxHeight((int) newContainerHeight);
imageView.setMaxWidth((int) newContainerWidth);

En el fragmento de código anterior, bmp es el objeto de mapa de bits que se debe mostrar e imageView es el ImageViewobjeto

Una cosa importante a tener en cuenta es el cambio de los parámetros de diseño . Esto es necesario porque setMaxHeighty setMaxWidthsolo hará una diferencia si el ancho y la altura están definidos para envolver el contenido, no para llenar el padre. Los padres de llenado en el otro lado es el ajuste deseado al principio, porque de lo contrario containerWidthy containerHeightambos tienen valores iguales a 0. Por lo tanto, en su archivo de diseño va a tener algo como esto para su ImageView:

...
<ImageView android:id="@+id/my_image_view"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"/>
...
maddob
fuente
4

Esta no es la solución más eficaz, pero como alguien sugirió en lugar de fondo, puede crear FrameLayout o RelativeLayout y usar ImageView como pseudo fondo; otros elementos se colocarán simplemente encima:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

    <ImageView
        android:id="@+id/ivBackground"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:scaleType="fitStart"
        android:src="@drawable/menu_icon_exit" />

    <Button
        android:id="@+id/bSomeButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="61dp"
        android:layout_marginTop="122dp"
        android:text="Button" />

</RelativeLayout>

El problema con ImageView es que solo los tipos de escalas disponibles son: CENTER, CENTER_CROP, CENTER_INSIDE, FIT_CENTER, FIT_END, FIT_START, FIT_XY, MATRIX ( http://etcodehome.blogspot.de/2011/05/android-imageview-scaletype-samples.html )

y para "escalar la imagen de fondo (manteniendo su relación de aspecto)" en algunos casos, cuando desea que una imagen llene toda la pantalla (por ejemplo, imagen de fondo) y la relación de aspecto de la pantalla es diferente a la de la imagen, el tipo de escala necesario es amable de TOP_CROP, porque:

CENTER_CROP centra la imagen a escala en lugar de alinear el borde superior con el borde superior de la vista de imagen y FIT_START se ajusta a la altura de la pantalla y no llena el ancho. Y como el usuario Anke notó que FIT_XY no mantiene la relación de aspecto.

Con mucho gusto alguien ha extendido ImageView para soportar TOP_CROP

public class ImageViewScaleTypeTopCrop extends ImageView {
    public ImageViewScaleTypeTopCrop(Context context) {
        super(context);
        setup();
    }

    public ImageViewScaleTypeTopCrop(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup();
    }

    public ImageViewScaleTypeTopCrop(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setup();
    }

    private void setup() {
        setScaleType(ScaleType.MATRIX);
    }

    @Override
    protected boolean setFrame(int frameLeft, int frameTop, int frameRight, int frameBottom) {

        float frameWidth = frameRight - frameLeft;
        float frameHeight = frameBottom - frameTop;

        if (getDrawable() != null) {

            Matrix matrix = getImageMatrix();
            float scaleFactor, scaleFactorWidth, scaleFactorHeight;

            scaleFactorWidth = (float) frameWidth / (float) getDrawable().getIntrinsicWidth();
            scaleFactorHeight = (float) frameHeight / (float) getDrawable().getIntrinsicHeight();

            if (scaleFactorHeight > scaleFactorWidth) {
                scaleFactor = scaleFactorHeight;
            } else {
                scaleFactor = scaleFactorWidth;
            }

            matrix.setScale(scaleFactor, scaleFactor, 0, 0);
            setImageMatrix(matrix);
        }

        return super.setFrame(frameLeft, frameTop, frameRight, frameBottom);
    }

}

https://stackoverflow.com/a/14815588/2075875

Ahora, en mi humilde opinión, sería perfecto si alguien escribiera Drawable personalizado que escalara una imagen como esa. Entonces podría usarse como parámetro de fondo.


Reflog sugiere preescalar dibujable antes de usarlo. Aquí hay instrucciones de cómo hacerlo: Java (Android): ¿Cómo escalar un dibujo sin Bitmap? Aunque tiene una desventaja, ese mapa de bits / drawable escalado utilizará más RAM, mientras que el escalado sobre la marcha utilizado por ImageView no requiere más memoria. La ventaja podría ser una menor carga del procesador.

Malaquiasz
fuente
¡Buena esa! Hace exactamente lo que buscaba: imagen de fondo para llenar toda la pantalla aumentando el ancho proporcionalmente y recortando la parte inferior de la imagen.
CMash
4

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 bwidth=bitMap1.getWidth();
int bheight=bitMap1.getHeight();
int swidth=imageView_location.getWidth();
int sheight=imageView_location.getHeight();
new_width=swidth;
new_height = (int) Math.floor((double) bheight *( (double) new_width / (double) bwidth));
Bitmap newbitMap = Bitmap.createScaledBitmap(bitMap1,new_width,new_height, true);
imageView_location.setImageBitmap(newbitMap)
Naresh Sharma
fuente
2

Lo que Dweebo propuso funciona. Pero en mi humilde opinión es innecesario. Un fondo dibujable se escala bien solo. La vista debe tener ancho y alto fijos, como en el siguiente ejemplo:

 < RelativeLayout 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@android:color/black">

    <LinearLayout
        android:layout_width="500dip"
        android:layout_height="450dip"
        android:layout_centerInParent="true"
        android:background="@drawable/my_drawable"
        android:orientation="vertical"
        android:padding="30dip"
        >
        ...
     </LinearLayout>
 < / RelativeLayout>
Yar
fuente
2
Tengo entendido que un fondo se expandirá para llenar una vista, pero no se reducirá.
Edward Falk
1

Una opción para intentar es colocar la imagen en la drawable-nodpicarpeta y establecer el fondo de un diseño en la identificación del recurso dibujable.

Esto definitivamente funciona con la reducción, no he probado con la ampliación sin embargo.

Sababado
fuente
0

Kotlin:

Si necesita dibujar un mapa de bits en una Vista, ajuste a FIT.

Puede hacer los cálculos adecuados para establecer bm la altura igual al contenedor y ajustar el ancho, en el caso de que la relación ancho / altura bm sea menor que la relación ancho / altura del contenedor, o lo inverso en el escenario opuesto.

Imágenes:

Imagen de ejemplo 1

Imagen de ejemplo 2

// binding.fragPhotoEditDrawCont is the RelativeLayout where is your view
// bm is the Bitmap
val ch = binding.fragPhotoEditDrawCont.height
val cw = binding.fragPhotoEditDrawCont.width
val bh = bm.height
val bw = bm.width
val rc = cw.toFloat() / ch.toFloat()
val rb = bw.toFloat() / bh.toFloat()

if (rb < rc) {
    // Bitmap Width to Height ratio is less than Container ratio
    // Means, bitmap should pin top and bottom, and have some space on sides.
    //              _____          ___
    // container = |_____|   bm = |___| 
    val bmHeight = ch - 4 //4 for container border
    val bmWidth = rb * bmHeight //new width is bm_ratio * bm_height
    binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth.toInt(), bmHeight)
}
else {
    val bmWidth = cw - 4 //4 for container border
    val bmHeight = 1f/rb * cw
    binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth, bmHeight.toInt())
}
danitinez
fuente
-4

tendrás que preescalar ese dibujo antes de usarlo como fondo

volver a registrar
fuente
¿Es esta realmente la única opción? ¿No hay ningún tipo de contenedor para colocar ImageView en el que se pueda especificar el tamaño de visualización?
GrkEngineer
También tengo problemas con SurfaceView
AZ_
3
¿No hay nada como ajuste de aspecto, relleno de aspecto, arriba a la izquierda, arriba, etc., como en iOS? Eso apesta ..
Maciej Swic
Como dijo @Aleksej, una respuesta falsa.
alumno
-5

Puede usar uno de los siguientes:

android: gravity = "fill_horizontal | clip_vertical"

O

android: gravity = "fill_vertical | clip_horizontal"

Eli Baynesay
fuente