-1

I mean a Drawable generated dynamically, not a file from res/drawable.

I tried setting it as a background of the modifier, but it doesn't accept a Drawable instance object. In Java, it was done like this: layout.setBackground(bg.getDrawable()).

In this case, I have a LazyColumn, and it haves some items. Ok, I need the drawable to be the background of the LazyColumn. The background must fit the height and the width of the LazyColumn

I thought a possibility is to do it using a Box, placing a Image with the Drawable inside the Box, and over it, the LazyColumn, but the drawable must be the exact size of the LazyColumn.

What can I try next?

2 Answers 2

1

You could use drawBehind modifier which provides access to the the underlying Canvas with DrawScope.drawIntoCanvas. It's important to also add clipToBounds to avoid drawing outside of the element.

.clipToBounds()
.drawBehind {
    drawIntoCanvas { canvas ->
       drawable.bounds = canvas.nativeCanvas.clipBounds
       drawable.draw(canvas.nativeCanvas)
    }
}

screen grab

Full example:

@Composable
fun DrawableBackground() {
    val drawable = object : Drawable() {
        val paint: Paint = Paint().apply { setARGB(255, 0, 255, 0) }

        override fun draw(canvas: Canvas) {
            val top = bounds.top.toFloat()
            val bottom = bounds.bottom.toFloat()
            val left = bounds.left.toFloat()
            val right = bounds.right.toFloat()
            canvas.drawLine(right, top, left, bottom, paint)
            canvas.drawLine(left, top, right, bottom, paint)
        }

        override fun setAlpha(alpha: Int) {}
        override fun setColorFilter(colorFilter: ColorFilter?) {}
        override fun getOpacity(): Int = PixelFormat.OPAQUE
    }

    Column {
        var items by remember { mutableIntStateOf(5) }
        Row {
            Button(onClick = { ++items }) { Text("+") }
            Button(onClick = { --items }) { Text("-") }
        }
        LazyColumn(modifier = Modifier
            .fillMaxWidth()
            .clipToBounds()
            .drawBehind {
                drawIntoCanvas { canvas ->
                    drawable.bounds = canvas.nativeCanvas.clipBounds
                    drawable.draw(canvas.nativeCanvas)
                }
            }
        ) {
            items(items) { Text("Item $it") }
        }
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

It seems to work with your sample! but not with mine. I have an issue with my Drawable but I'm going to open a different question for it. Can you tell me how you reach this answer? are you very pro with Drawables or did you use any AI model for get it? Even you attached a GIF. If you have used an AI model, I have curiosity of which one
No, I didn't use any AI assistance. You could start by looking through drawing modifiers. drawBehind description says "Draw into a Canvas behind the modified content" which seems like what we need because we know that Drawable needs android.graphics.Canvas to draw on. drawBehind gives us DrawScope so we look for Canvas access through DrawScope docs and find drawIntoCanvas. @NullPointerException
0

To set a dynamically generated drawable as the background, you'll have to convert that drawable into a format that compose can use, such as a Bitmap. Then, you can use an Image composable with a BitmapPainter.

Here is the sample implementation: Updated

// convert drawable to bitmap
fun drawableToBitmap(drawable: Drawable, width: Int, height: Int): Bitmap {
    val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bitmap)
    drawable.setBounds(0, 0, width, height)
    drawable.draw(canvas)
    return bitmap
}

@Composable
fun LazyColumnWithDynamicBg(
    drawable: Drawable,
    items: List<String>
) {
    var contentHeight by remember {
        mutableStateOf(20.dp)
    }
    var contentWidth by remember {
        mutableStateOf(20.dp)
    }
    Box(modifier = Modifier.wrapContentSize(), contentAlignment = Alignment.TopCenter) {
        // get bitmap
        val bitmap = drawableToBitmap(drawable, width = contentWidth.value.toInt(), height = contentHeight.value.toInt()) // considering full screen size

        Image(
            painter = BitmapPainter(bitmap.asImageBitmap()),
            contentDescription = null,
            modifier = Modifier.matchParentSize()
        )

        LazyColumn(
            modifier = Modifier.fillMaxWidth().wrapContentHeight().onGloballyPositioned {
                contentHeight = it.size.height.dp
                contentWidth = it.size.width.dp
            }
        ) {
            items(items) { item ->
                Text(
                    text = item,
                    modifier = Modifier.padding(16.dp),
                    color = Color.White,
                    fontWeight = FontWeight.Bold
                )
            }
        }
    }
}

@Preview
@Composable
fun PreviewComposable() {
    // a simple dynamic Drawable for sample
    val dynamicDrawable = object : Drawable() {
        override fun draw(canvas: Canvas) {
            canvas.drawColor(android.graphics.Color.CYAN)  // Example: fill the background with cyan color
        }

        override fun setAlpha(alpha: Int) {}
        override fun getOpacity(): Int = android.graphics.PixelFormat.OPAQUE
        override fun setColorFilter(colorFilter: android.graphics.ColorFilter?) {}
    }

    val dummyItems = List(3) { "Item ${it + 1}" }

    LazyColumnWithDynamicBg(
        drawable = dynamicDrawable,
        items = dummyItems
    )
}

Output:

enter image description here

4 Comments

well, maybe I didn't explain myself correctly. Take your sample, and modify it to have just 3 items, how can you make the Image to have exactly the same height of the lazycolumn?
Yes, it will show full background even if there are 3 items as drawable's size is equivalent to screen's size
but I need that it only fits the LazyColumn height...
Oh, I see! Then you can resize the content/drawable once lazycolumn items are drawn using onGloballyPositioned. I'll update my answer accordingly!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.