NullPointerException
NullPointerException

Reputation: 37721

How to set a drawable object instance as a background of a LazyColumn?

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?

Upvotes: -1

Views: 91

Answers (2)

Jan Itor
Jan Itor

Reputation: 4276

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

Upvotes: 1

Megh Lath
Megh Lath

Reputation: 2214

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

Upvotes: 0

Related Questions