En Compose, se usa un objeto Painter
para representar algo que se puede dibujar (un reemplazo de las APIs de Drawable
definidas en Android). Este objeto también influye en la medición y el diseño del elemento correspondiente componible que lo usa. Un BitmapPainter
toma una ImageBitmap
que puede dibujar un Bitmap
en la pantalla.
En la mayoría de los casos de uso, el uso de painterResource()
anterior muestra el pintor correcto para el recurso (es decir, BitmapPainter
o VectorPainter
). Para obtener más información sobre las diferencias entre los dos, lee la sección ImageBitmap vs. ImageVector.
Un Painter
es diferente de un DrawModifier
, que dibuja estrictamente dentro de los límites que se le otorgan y no influye en la medición ni en el diseño del elemento componible.
Para crear un pintor personalizado, extiende la clase Painter
e implementa el método onDraw
, que permite el acceso a DrawScope
para dibujar gráficos personalizados. También puedes anular intrinsicSize
, que se usará para influir en el elemento de componibilidad que contiene:
class OverlayImagePainter constructor( private val image: ImageBitmap, private val imageOverlay: ImageBitmap, private val srcOffset: IntOffset = IntOffset.Zero, private val srcSize: IntSize = IntSize(image.width, image.height), private val overlaySize: IntSize = IntSize(imageOverlay.width, imageOverlay.height) ) : Painter() { private val size: IntSize = validateSize(srcOffset, srcSize) override fun DrawScope.onDraw() { // draw the first image without any blend mode drawImage( image, srcOffset, srcSize, dstSize = IntSize( this@onDraw.size.width.roundToInt(), this@onDraw.size.height.roundToInt() ) ) // draw the second image with an Overlay blend mode to blend the two together drawImage( imageOverlay, srcOffset, overlaySize, dstSize = IntSize( this@onDraw.size.width.roundToInt(), this@onDraw.size.height.roundToInt() ), blendMode = BlendMode.Overlay ) } /** * Return the dimension of the underlying [ImageBitmap] as it's intrinsic width and height */ override val intrinsicSize: Size get() = size.toSize() private fun validateSize(srcOffset: IntOffset, srcSize: IntSize): IntSize { require( srcOffset.x >= 0 && srcOffset.y >= 0 && srcSize.width >= 0 && srcSize.height >= 0 && srcSize.width <= image.width && srcSize.height <= image.height ) return srcSize } }
Ahora que tenemos nuestro Painter
personalizado, podemos superponer cualquier imagen sobre nuestra imagen de origen de la siguiente manera:
val rainbowImage = ImageBitmap.imageResource(id = R.drawable.rainbow) val dogImage = ImageBitmap.imageResource(id = R.drawable.dog) val customPainter = remember { OverlayImagePainter(dogImage, rainbowImage) } Image( painter = customPainter, contentDescription = stringResource(id = R.string.dog_content_description), contentScale = ContentScale.Crop, modifier = Modifier.wrapContentSize() )
A continuación, se puede ver el resultado de la combinación de las dos imágenes con un pintor personalizado:
También se puede usar un pintor personalizado con Modifier.paint(customPainter)
para dibujar el contenido en un elemento que admite composición de la siguiente manera:
val rainbowImage = ImageBitmap.imageResource(id = R.drawable.rainbow) val dogImage = ImageBitmap.imageResource(id = R.drawable.dog) val customPainter = remember { OverlayImagePainter(dogImage, rainbowImage) } Box( modifier = Modifier.background(color = Color.Gray) .padding(30.dp) .background(color = Color.Yellow) .paint(customPainter) ) { /** intentionally empty **/ }
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- ImageBitmap vs. ImageVector {:#bitmap-vs-vector}
- Gráficos en Compose
- Carga de imágenes {:#loading-images}