Cuando necesitas mostrar imágenes estáticas en tu app, puedes usar la clase Drawable
y sus subclases para dibujar imágenes y
formas. Una clase Drawable
es una abstracción general que representa
algo que se puede dibujar. Las diversas subclases ayudan en situaciones específicas
de imágenes, y puedes extenderlas para definir tus objetos propios de elementos de diseño
que tienen comportamientos únicos.
Hay dos formas de definir un Drawable
y crear una instancia de él, además de usar los constructores de clase:
- Aumenta un recurso de imagen (un archivo de mapa de bits) guardado en tu proyecto.
- Aumenta un recurso XML que define las propiedades del elemento de diseño.
Nota: Tal vez prefieras usar un elemento de diseño vectorial, que define una imagen con un conjunto de puntos, líneas y curvas, junto con la información de color asociada. Esto permite que se ajusten los elementos de diseño vectoriales a diferentes tamaños sin que se pierda la calidad. Para obtener más información, consulta Descripción general de los elementos de diseño vectoriales.
Cómo crear elementos de diseño a partir de recursos de imágenes
Puedes hacer referencia a un archivo de imagen desde los recursos de tu proyecto para agregar gráficos a tu app. Los tipos de archivo admitidos son PNG (preferido), JPG (aceptable) y GIF (desaconsejado). Los íconos de las apps, logotipos y otros gráficos, como los que se usan en los juegos, son ideales para esta técnica.
Para usar un recurso de imagen, agrega tu archivo al directorio res/drawable/
del proyecto. Cuando estés en el proyecto, puedes hacer referencia al recurso
de imagen desde el código o el diseño XML. De cualquier modo, se hace referencia a él con un
ID de recurso, es decir, el nombre del archivo sin la extensión del tipo. Por
ejemplo, haz referencia a my_image.png
con my_image
.
Nota: Los recursos de imagen que se colocan en el directorio res/drawable/
se pueden optimizar automáticamente con la compresión de imágenes sin pérdidas mediante la herramienta aapt
durante el proceso de compilación. Por ejemplo, un archivo PNG con color verdadero que no requiera más de 256 colores
se puede convertir a un archivo PNG de 8 bits con una paleta de colores. De ese modo, se genera una imagen
que tiene la misma calidad, pero que requiere menos memoria. Como resultado, los archivos binarios de imágenes
que se colocan en este directorio pueden cambiar en el momento de la compilación. Si planeas leer una
imagen como un flujo de bits para convertirla en un mapa de bits, coloca tus imágenes en la
carpeta res/raw/
, donde la herramienta aapt
no las
modifica.
En el siguiente fragmento de código, se muestra cómo compilar una ImageView
que utilice
una imagen creada desde un recurso de elemento de diseño y la agregue al diseño:
Kotlin
private lateinit var constraintLayout: ConstraintLayout override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Instantiate an ImageView and define its properties val i = ImageView(this).apply { setImageResource(R.drawable.my_image) contentDescription = resources.getString(R.string.my_image_desc) // set the ImageView bounds to match the Drawable's dimensions adjustViewBounds = true layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) } // Create a ConstraintLayout in which to add the ImageView constraintLayout = ConstraintLayout(this).apply { // Add the ImageView to the layout. addView(i) } // Set the layout as the content view. setContentView(constraintLayout) }
Java
ConstraintLayout constraintLayout; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create a ConstraintLayout in which to add the ImageView constraintLayout = new ConstraintLayout(this); // Instantiate an ImageView and define its properties ImageView i = new ImageView(this); i.setImageResource(R.drawable.my_image); i.setContentDescription(getResources().getString(R.string.my_image_desc)); // set the ImageView bounds to match the Drawable's dimensions i.setAdjustViewBounds(true); i.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); // Add the ImageView to the layout and set the layout as the content view. constraintLayout.addView(i); setContentView(constraintLayout); }
En otros casos, tal vez prefieras manejar tu recurso de imagen como si fuera un objeto Drawable
. Por ejemplo:
Kotlin
val myImage: Drawable = ResourcesCompat.getDrawable(context.resources, R.drawable.my_image, null)
Java
Resources res = context.getResources(); Drawable myImage = ResourcesCompat.getDrawable(res, R.drawable.my_image, null);
Nota: Cada recurso único de tu proyecto puede mantener un solo estado, sin importar de cuántos objetos diferentes sea la instancia que crees para él. Por ejemplo, si creas una instancia de dos objetos Drawable
a partir del mismo recurso de imagen y
cambias una propiedad (como el canal alfa) de uno de los objetos, el cambio también afectará
al otro. Cuando se trata de varias instancias de un recurso de imagen, en lugar de
transformar directamente el objeto Drawable
, debes realizar una
animación
de interpolación.
En el siguiente fragmento de XML, se muestra cómo agregar un recurso de elemento de diseño a una ImageView
en el diseño XML:
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/my_image" android:contentDescription="@string/my_image_desc" />
Para obtener más información sobre cómo usar los recursos del proyecto, consulta Recursos y elementos.
Nota: Cuando uses recursos de imagen como fuente de los elementos de diseño, asegúrate de que el tamaño de las imágenes sea adecuado para varias densidades de píxeles. Si las imágenes no son correctas, se ampliarán para ajustarse, lo que podría generar artefactos en los elementos de diseño. Para obtener más información, consulta Cómo brindar compatibilidad con diferentes densidades de píxeles.
Cómo crear elementos de diseño a partir de recursos de XML
Si hay un objeto Drawable
que
deseas crear, que en principio no depende de las variables definidas por tu
código o por la interacción del usuario, es una buena opción definir
el Drawable
en XML. Incluso si esperas que el Drawable
cambie sus
propiedades durante la interacción del usuario con tu app, sería conveniente
definir el objeto en XML, ya que puedes modificar las propiedades después de
crear las instancias correspondientes.
Después de definir tu Drawable
en XML,
guarda el archivo en el directorio res/drawable/
del proyecto. En
el siguiente ejemplo, se muestra el XML que define un recurso TransitionDrawable
, el cual se hereda de
Drawable
:
<!-- res/drawable/expand_collapse.xml --> <transition xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/image_expand"> <item android:drawable="@drawable/image_collapse"> </transition>
Luego, llama a Resources.getDrawable()
y pasa el ID de recurso a tu archivo XML para
recuperar el objeto y crear instancias de él. Cualquier
subclase Drawable
que admita el método inflate()
se puede definir en XML,
y tu app puede crear instancias de ella. Cada clase de elemento de diseño que admite el aumento XML
utiliza atributos XML específicos que ayudan a definir las propiedades del objeto. El
siguiente código crea una instancia del TransitionDrawable
y lo establece como el contenido de un
objeto ImageView
:
Kotlin
val transition= ResourcesCompat.getDrawable( context.resources, R.drawable.expand_collapse, null ) as TransitionDrawable val image: ImageView = findViewById(R.id.toggle_image) image.setImageDrawable(transition) // Description of the initial state that the drawable represents. image.contentDescription = resources.getString(R.string.collapsed) // Then you can call the TransitionDrawable object's methods. transition.startTransition(1000) // After the transition is complete, change the image's content description // to reflect the new state.
Java
Resources res = context.getResources(); TransitionDrawable transition = (TransitionDrawable) ResourcesCompat.getDrawable(res, R.drawable.expand_collapse, null); ImageView image = (ImageView) findViewById(R.id.toggle_image); image.setImageDrawable(transition); // Description of the initial state that the drawable represents. image.setContentDescription(getResources().getString(R.string.collapsed)); // Then you can call the TransitionDrawable object's methods. transition.startTransition(1000); // After the transition is complete, change the image's content description // to reflect the new state.
Para obtener más información sobre los atributos XML admitidos, consulta las clases que se mencionaron anteriormente.
Elementos de diseño de forma
Un objeto ShapeDrawable
puede ser una buena opción
cuando quieres dibujar de manera dinámica un gráfico bidimensional. Puedes
dibujar formas básicas de manera programática en un objeto ShapeDrawable
y aplicar los estilos que necesite tu app.
ShapeDrawable
es una subclase de Drawable
. Por esta razón, puedes usar un
ShapeDrawable
donde se espera un Drawable
. Por
ejemplo, puedes usar un objeto ShapeDrawable
para establecer el fondo
de una vista si lo pasas al método setBackgroundDrawable()
de la vista. También puedes dibujar la forma como su
propia vista personalizada y agregarla a un diseño en tu app.
Debido a que ShapeDrawable
tiene su propio método draw()
, puedes crear una
subclase de View
que dibuje el objeto
ShapeDrawable
durante el evento onDraw()
, como se muestra en
el siguiente ejemplo de código:
Kotlin
class CustomDrawableView(context: Context) : View(context) { private val drawable: ShapeDrawable = run { val x = 10 val y = 10 val width = 300 val height = 50 contentDescription = context.resources.getString(R.string.my_view_desc) ShapeDrawable(OvalShape()).apply { // If the color isn't set, the shape uses black as the default. paint.color = 0xff74AC23.toInt() // If the bounds aren't set, the shape can't be drawn. setBounds(x, y, x + width, y + height) } } override fun onDraw(canvas: Canvas) { drawable.draw(canvas) } }
Java
public class CustomDrawableView extends View { private ShapeDrawable drawable; public CustomDrawableView(Context context) { super(context); int x = 10; int y = 10; int width = 300; int height = 50; setContentDescription(context.getResources().getString( R.string.my_view_desc)); drawable = new ShapeDrawable(new OvalShape()); // If the color isn't set, the shape uses black as the default. drawable.getPaint().setColor(0xff74AC23); // If the bounds aren't set, the shape can't be drawn. drawable.setBounds(x, y, x + width, y + height); } protected void onDraw(Canvas canvas) { drawable.draw(canvas); } }
Puedes usar la clase CustomDrawableView
en el ejemplo de código
anterior como lo harías con cualquier otra vista personalizada. Por ejemplo, puedes
agregarla de manera programática a una actividad de tu app, como se muestra en el siguiente
ejemplo:
Kotlin
private lateinit var customDrawableView: CustomDrawableView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) customDrawableView = CustomDrawableView(this) setContentView(customDrawableView) }
Java
CustomDrawableView customDrawableView; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); customDrawableView = new CustomDrawableView(this); setContentView(customDrawableView); }
Si quieres usar la vista personalizada en el diseño XML, la
clase CustomDrawableView
debe anular el constructor View(Context, AttributeSet)
, al que se llama cuando la clase se
aumenta desde XML. En el siguiente ejemplo, se muestra cómo declarar la
CustomDrawableView
en el diseño XML:
<com.example.shapedrawable.CustomDrawableView android:layout_width="fill_parent" android:layout_height="wrap_content" />
La clase ShapeDrawable
, como muchos otros
tipos de elementos de diseño del paquete android.graphics.drawable
, te permite
definir varias propiedades del objeto mediante el uso de métodos públicos. Algunos ejemplos
de propiedades que tal vez quieras ajustar son la transparencia alfa, el filtro de color,
la interpolación, la opacidad y el color.
También puedes definir formas básicas de elementos de diseño con recursos XML. Para obtener más información, consulta Elemento de diseño de forma en Tipos de recursos de elementos de diseño.
Elementos de diseño de NinePatch
Un gráfico NinePatchDrawable
es una
imagen de mapa de bits extensible que puedes usar como fondo de una vista. Android
cambia automáticamente el tamaño del gráfico para que admita el contenido de la vista. Un
ejemplo de uso de una imagen de NinePatch es el fondo que usan los botones estándar
de Android: estos se deben extender para adaptarse a strings de diferentes longitudes. Un
gráfico de NinePatch es una imagen PNG estándar que incluye un borde adicional de 1 píxel.
Debe guardarse con la extensión 9.png
en el
directorio res/drawable/
de tu proyecto.
Usa el borde para definir las áreas extensibles y estáticas de la imagen. A fin de indicar una sección extensible, dibuja una o más líneas negras de 1 píxel de ancho en las partes izquierda y superior del borde (los píxeles de los otros bordes deben ser blancos o transparentes). Puedes tener todas las secciones extensibles que quieras. El tamaño relativo de las secciones extensibles permanece igual, por lo que la sección más grande siempre se mantiene así.
También puedes definir una sección opcional del elemento de diseño de la imagen (efectivamente,
las líneas de relleno) si dibujas una línea en la parte derecha y otra en la parte inferior. Si un
objeto View
establece el gráfico de NinePatch como fondo
y especifica el texto de la vista, se extiende de manera que todo el texto
ocupe solo el área designada por las líneas inferior y derecha (si se incluyeron).
Si no se incluyeron las líneas de relleno, Android usa las líneas izquierda y superior para
definir esta área del elemento de diseño.
Para aclarar la diferencia entre las líneas, la izquierda y la superior definen qué píxeles de la imagen se pueden reproducir para extender la imagen. Las líneas inferior y derecha definen el área relativa dentro de la imagen que puede ocupar el contenido de la vista.
En la figura 1, se muestra un ejemplo de un gráfico de NinePatch que se usa para definir un botón:

Figura 1: Ejemplo de un gráfico de NinePatch que define un botón
Este gráfico de NinePatch define un área extensible con las líneas izquierda y superior, y el área del elemento de diseño con las líneas inferior y derecha. En la imagen superior, las líneas grises punteadas identifican las regiones de la imagen que se reproducen para extenderla. El rectángulo rosado de la imagen inferior identifica la región en la que se permite el contenido de la vista. Si el contenido no cabe en esta región, la imagen se extiende para que quepa.
La herramienta Draw 9-patch ofrece una manera muy práctica de crear tus imágenes de NinePatch con un editor de gráficos WYSIWYG. Incluso muestra advertencias si la región que definiste para el área extensible corre el riesgo de generar artefactos de dibujo como resultado de la reproducción de píxeles.
En el siguiente ejemplo de XML de diseño, se muestra cómo agregar un gráfico de NinePatch
a dos botones. La imagen de NinePatch se guarda en
res/drawable/my_button_background.9.png
.
<Button android:id="@+id/tiny" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerInParent="true" android:text="Tiny" android:textSize="8sp" android:background="@drawable/my_button_background"/> <Button android:id="@+id/big" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerInParent="true" android:text="Biiiiiiig text!" android:textSize="30sp" android:background="@drawable/my_button_background"/>
Ten en cuenta que los atributos layout_width
y layout_height
están configurados en wrap_content
para que el botón se ajuste perfectamente
alrededor del texto.
En la figura 2, se muestran los dos botones renderizados a partir de la imagen XML y de NinePatch que aparece arriba. Observa cómo el ancho y la altura del botón varían con el texto, y la imagen de fondo se extiende para adaptarse a él.

Figura 2: Botones renderizados con un recurso XML y un gráfico de NinePatch
Elementos de diseño personalizados
Cuando quieras crear algunos dibujos personalizados, podrás hacerlo si extiendes la clase Drawable
(o cualquiera de sus subclases).
El método más importante para implementar es draw(Canvas)
porque proporciona el objeto Canvas
que debes usar a fin de brindar
tus instrucciones de dibujo.
En el siguiente código, se muestra una subclase simple de Drawable
que dibuja un círculo:
Kotlin
class MyDrawable : Drawable() { private val redPaint: Paint = Paint().apply { setARGB(255, 255, 0, 0) } override fun draw(canvas: Canvas) { // Get the drawable's bounds val width: Int = bounds.width() val height: Int = bounds.height() val radius: Float = Math.min(width, height).toFloat() / 2f // Draw a red circle in the center canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, redPaint) } override fun setAlpha(alpha: Int) { // This method is required } override fun setColorFilter(colorFilter: ColorFilter?) { // This method is required } override fun getOpacity(): Int = // Must be PixelFormat.UNKNOWN, TRANSLUCENT, TRANSPARENT, or OPAQUE PixelFormat.OPAQUE }
Java
public class MyDrawable extends Drawable { private final Paint redPaint; public MyDrawable() { // Set up color and text size redPaint = new Paint(); redPaint.setARGB(255, 255, 0, 0); } @Override public void draw(Canvas canvas) { // Get the drawable's bounds int width = getBounds().width(); int height = getBounds().height(); float radius = Math.min(width, height) / 2; // Draw a red circle in the center canvas.drawCircle(width/2, height/2, radius, redPaint); } @Override public void setAlpha(int alpha) { // This method is required } @Override public void setColorFilter(ColorFilter colorFilter) { // This method is required } @Override public int getOpacity() { // Must be PixelFormat.UNKNOWN, TRANSLUCENT, TRANSPARENT, or OPAQUE return PixelFormat.OPAQUE; } }
Luego, puedes agregar tu elemento de diseño donde quieras, por ejemplo, en una
ImageView
, como se muestra a continuación:
Kotlin
val myDrawing = MyDrawable() val image: ImageView = findViewById(R.id.imageView) image.setImageDrawable(myDrawing) image.contentDescription = resources.getString(R.string.my_image_desc)
Java
MyDrawable mydrawing = new MyDrawable(); ImageView image = findViewById(R.id.imageView); image.setImageDrawable(mydrawing); image.setContentDescription(getResources().getString(R.string.my_image_desc));
En Android 7.0 (API nivel 24) y versiones posteriores, también puedes definir instancias de tus elementos de diseño personalizados con XML de las siguientes maneras:
- Puedes usar el nombre completo de la clase como el nombre del elemento XML. Para este enfoque, la clase
del elemento de diseño personalizado debe ser una clase pública de nivel superior:
<com.myapp.MyDrawable xmlns:android="http://schemas.android.com/apk/res/android" android:color="#ffff0000" />
- Puedes usar
drawable
como el nombre de la etiqueta XML y especificar el nombre completo de la clase de su atributo. Este enfoque se puede utilizar tanto para las clases públicas de nivel superior como para las clases internas estáticas públicas:<drawable xmlns:android="http://schemas.android.com/apk/res/android" class="com.myapp.MyTopLevelClass$MyDrawable" android:color="#ffff0000" />
Cómo agregar un tono a los elementos de diseño
Con Android 5.0 (API nivel 21) y versiones posteriores, puedes aplicar tonos a los mapas de bits y a los mapas de bits ajustables definidos como
máscaras alfa. Puedes hacerlo con recursos de color o atributos de tema que se resuelven como recursos
de color (por ejemplo, ?android:attr/colorPrimary
). Por lo general, creas estos elementos
solo una vez y los coloreas automáticamente para que coincidan con tu tema.
Puedes aplicar un tono a los objetos BitmapDrawable
, NinePatchDrawable
o VectorDrawable
con el método setTint()
. También
puedes establecer el color y el modo del tono en tus diseños con los atributos
android:tint
y android:tintMode
.
Cómo extraer colores destacados de una imagen
La biblioteca de compatibilidad de Android incluye la clase Palette
, que te permite extraer colores destacados de una imagen.
Puedes cargar tus elementos de diseño como un Bitmap
y pasarlo a Palette
para acceder a sus colores.
Para obtener más información, consulta Cómo seleccionar colores con
la API de Palette.