androidandroid-jetpack-composeandroid-jetpack-compose-listandroid-jetpack-compose-lazy-column

How to draw border around the LazyColumn items in Android Compose


There are items() {} sections inside LazyColumn. So I would like to draw a border with rounded corners around each section. Is there any method?

// need to draw a border around the items
LazyColumn {
    items(10) {
        Row {
            // content
        }
    }

    items(5) {
        Row {
            // content
        }
    }
}

Solution

  • If you want to add a border to single item just add in your item content a Composable with a border modifier:

    items(10) {
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(2.dp)
                    .border(width = 1.dp, color = Blue200, shape = RoundedCornerShape(8.dp))
                    .padding(2.dp)
           ){ /** ... */ }
    }
    

    enter image description here

    If you want to add a border around all the items block you can create different border modifiers to apply to each items.
    Something like:

    //border
    val strokeWidth: Dp = 2.dp
    val strokeColor: Color = Blue500
    val cornerRadius: Dp = 8.dp
    
    //background shape
    val topShape = RoundedCornerShape(topStart = cornerRadius, topEnd = cornerRadius)
    val bottomShape = RoundedCornerShape(bottomStart = cornerRadius, bottomEnd = cornerRadius)
    
    LazyColumn {
        val itemCount = 10
        var shape : Shape
        var borderModifier : Modifier
    
        items(itemCount) { index ->
            when (index) {
                0 -> {
                    //First item. Only top border
                    shape = topShape
                    borderModifier = Modifier.topBorder(strokeWidth,strokeColor,cornerRadius)
                }
                itemCount -1 -> {
                    //last item. Only bottom border
                    shape = bottomShape
                    borderModifier = Modifier.bottomBorder(strokeWidth,strokeColor,cornerRadius)
                }
                else -> {
                    //Other items. Only side border
                    shape = RectangleShape
                    borderModifier = Modifier.sideBorder(strokeWidth,strokeColor,cornerRadius)
                }
            }
    
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .clip(shape)
                    .background(Teal200)
                    .then(borderModifier)
                    .padding(4.dp)
            ) {
                Text(text = "Item: $index")
            }
        }
    }
    

    enter image description here

    where:

    fun Modifier.topBorder(strokeWidth: Dp, color: Color, cornerRadiusDp: Dp) = composed(
        factory = {
            val density = LocalDensity.current
            val strokeWidthPx = density.run { strokeWidth.toPx() }
            val cornerRadiusPx = density.run { cornerRadiusDp.toPx() }
    
            Modifier.drawBehind {
                val width = size.width
                val height = size.height
    
                drawLine(
                    color = color,
                    start = Offset(x = 0f, y = height),
                    end = Offset(x = 0f, y = cornerRadiusPx),
                    strokeWidth = strokeWidthPx
                )
    
                drawArc(
                    color = color,
                    startAngle = 180f,
                    sweepAngle = 90f,
                    useCenter = false,
                    topLeft = Offset.Zero,
                    size = Size(cornerRadiusPx * 2, cornerRadiusPx * 2),
                    style = Stroke(width = strokeWidthPx)
                )
    
                drawLine(
                    color = color,
                    start = Offset(x = cornerRadiusPx, y = 0f),
                    end = Offset(x = width - cornerRadiusPx, y = 0f),
                    strokeWidth = strokeWidthPx
                )
    
                drawArc(
                    color = color,
                    startAngle = 270f,
                    sweepAngle = 90f,
                    useCenter = false,
                    topLeft = Offset(x = width - cornerRadiusPx * 2, y = 0f),
                    size = Size(cornerRadiusPx * 2, cornerRadiusPx * 2),
                    style = Stroke(width = strokeWidthPx)
                )
    
                drawLine(
                    color = color,
                    start = Offset(x = width, y = height),
                    end = Offset(x = width, y = cornerRadiusPx),
                    strokeWidth = strokeWidthPx
                )
            }
        }
    )
    
    fun Modifier.bottomBorder(strokeWidth: Dp, color: Color, cornerRadiusDp: Dp) = composed(
        factory = {
            val density = LocalDensity.current
            val strokeWidthPx = density.run { strokeWidth.toPx() }
            val cornerRadiusPx = density.run { cornerRadiusDp.toPx() }
    
            Modifier.drawBehind {
                val width = size.width
                val height = size.height
    
                drawLine(
                    color = color,
                    start = Offset(x = 0f, y = 0f),
                    end = Offset(x = 0f, y = height-cornerRadiusPx),
                    strokeWidth = strokeWidthPx
                )
    
                drawArc(
                    color = color,
                    startAngle = 90f,
                    sweepAngle = 90f,
                    useCenter = false,
                    topLeft = Offset(x = 0f, y = height - cornerRadiusPx * 2),
                    size = Size(cornerRadiusPx * 2, cornerRadiusPx * 2),
                    style = Stroke(width = strokeWidthPx)
                )
    
                drawLine(
                    color = color,
                    start = Offset(x = cornerRadiusPx, y = height),
                    end = Offset(x = width - cornerRadiusPx, y = height),
                    strokeWidth = strokeWidthPx
                )
    
                drawArc(
                    color = color,
                    startAngle = 0f,
                    sweepAngle = 90f,
                    useCenter = false,
                    topLeft = Offset(x = width - cornerRadiusPx * 2, y = height - cornerRadiusPx * 2),
                    size = Size(cornerRadiusPx * 2, cornerRadiusPx * 2),
                    style = Stroke(width = strokeWidthPx)
                )
    
                drawLine(
                    color = color,
                    start = Offset(x = width, y = 0f),
                    end = Offset(x = width, y = height - cornerRadiusPx),
                    strokeWidth = strokeWidthPx
                )
            }
        }
    )
    
    fun Modifier.sideBorder(strokeWidth: Dp, color: Color, cornerRadiusDp: Dp) = composed(
        factory = {
            val density = LocalDensity.current
            val strokeWidthPx = density.run { strokeWidth.toPx() }
            val cornerRadiusPx = density.run { cornerRadiusDp.toPx() }
    
            Modifier.drawBehind {
                val width = size.width
                val height = size.height
    
                drawLine(
                    color = color,
                    start = Offset(x = 0f, y = 0f),
                    end = Offset(x = 0f, y = height),
                    strokeWidth = strokeWidthPx
                )
    
                drawLine(
                    color = color,
                    start = Offset(x = width, y = 0f),
                    end = Offset(x = width, y = height),
                    strokeWidth = strokeWidthPx
                )
            }
        }
    )