맞춤 페인터

Compose에서 Painter 객체는 그릴 수 있는 항목을 나타내고(Android에서 정의된 Drawable API의 대체 API) 객체를 사용하고 있는 컴포저블의 측정 및 레이아웃에 영향을 미치는 데 사용됩니다. BitmapPainterImageBitmap을 사용하여 화면에 Bitmap을 그릴 수 있습니다.

대부분의 사용 사례에서 위의 painterResource()를 사용하면 애셋에 맞는 올바른 페인터(예: BitmapPainter 또는 VectorPainter)가 반환됩니다. 둘의 차이점에 관한 자세한 내용은 ImageBitmap과 ImageVector 섹션을 참고하세요.

Painter는 지정된 경계 내에서 엄격하게 그려지고 컴포저블의 측정 또는 레이아웃에 영향을 주지 않는 DrawModifier와 다릅니다.

맞춤 페인터를 만들려면 Painter 클래스를 확장하고 onDraw 메서드를 구현합니다. 이렇게 하면 DrawScope에 액세스하여 맞춤 그래픽을 그릴 수 있습니다. 또한, 맞춤 페인터가 포함된 컴포저블에 영향을 미치는 데 사용되는 intrinsicSize를 재정의할 수 있습니다.

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
    }
}

이제 맞춤 Painter를 만들었으므로 다음과 같이 소스 이미지 위에 이미지를 오버레이할 수 있습니다.

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()
)

두 이미지를 맞춤 페인터와 결합한 출력은 다음과 같습니다.

두 이미지를 서로 오버레이하는 맞춤 페인터
그림 1: 두 이미지를 서로 오버레이하는 맞춤 페인터

맞춤 페인터를 Modifier.paint(customPainter)와 함께 사용하여 다음과 같이 컴포저블에 콘텐츠를 그릴 수도 있습니다.

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 **/ }