Search code examples
androidandroid-jetpack-composeandroid-jetpack-compose-layoutandroid-jetpack-compose-canvas

How to implement vertical likert scale with jetpack compose


I am attempting to create a vertical Likert scale using Jetpack Compose. Each field should include a vertical line that extends beyond its boundaries. I have also included an image to give an idea of what I am trying to achieve.

enter image description here


Solution

  • You can use a Box to put a Canvas on top of the row items.

    Something like:

    Box(Modifier.fillMaxWidth()){
    
        //Just a simple for the row items
        Column(
            modifier = Modifier.fillMaxWidth(),
            verticalArrangement = Arrangement.spacedBy(16.dp)
        ){
            for (i in 1..5) {
                RoundedIem()
            }
        }
    
        //vertical Likert scale
        Canvas(modifier = Modifier.fillMaxWidth()){
    
            val height = 60.dp.toPx() //height of items
            val verticalOffset = 76.dp.toPx() //height + vertical padding
    
            val strokeWidthCircle =1f //stroke circle width
            val radiusCircle = 30f    //radius circle
    
            for (i in 0..4) {
    
                val circleCenterX = size.width-100f 
                val circleCenterY = height/2+ i*verticalOffset
    
                //Inner white circle
                drawCircle(
                    color =  White,
                    radius =  radiusCircle,
                    center = Offset(circleCenterX ,circleCenterY),
                )
    
                //Stroke circle
                drawCircle(
                    color =  DarkGray,
                    radius =  radiusCircle,
                    center = Offset(circleCenterX, circleCenterY),
                    style = Stroke(width = strokeWidthCircle)
                )
    
                //vertical line
                if (i < 4) {
    
                    val startY = circleCenterY + radiusCircle + strokeWidthCircle
                    drawLine(
                        color = DarkGray,
                        start = Offset(
                            x = circleCenterX,
                            y = startY),
                        end = Offset(
                            x = circleCenterX,
                            y = startY + verticalOffset - strokeWidthCircle),
                        strokeWidth = strokeWidthCircle
                    )
                }
            }
        }
    }
    
    @Composable
    fun RoundedIem(){
        Row(
            modifier= Modifier
                .padding(horizontal = 16.dp)
                .fillMaxWidth()
                .height(60.dp)
                .clip(RoundedCornerShape(8.dp))
                .background(LightGray.copy(alpha = 0.5f))
                .padding(start = 10.dp),
            verticalAlignment = Alignment.CenterVertically,
        ){
            Text("Option")
        }
    }
    

    enter image description here

    If you want to add also an icon in the circle you can draw the icon in the Canvas using:

    //Icon
    val painter = rememberVectorPainter(Icons.Default.Done)
    
    Canvas(modifier = Modifier.fillMaxWidth()){
    
            //previous code
       
    
            val iconSize = 60f
            //circle center - iconSize/2
            translate(
                left = circleCenterX - iconSize/2,
                top = circleCenterY - iconSize/2
            ) {
                with(painter) {
                    draw(
                        size = Size(iconSize,iconSize),//painter.intrinsicSize,
                        colorFilter = ColorFilter.tint(Color.Blue)
                    )
                }
            }
    }
    

    enter image description here

    With a background:

                drawCircle(
                    color =  Blue,
                    radius =  radiusCircle-8f,
                    center = Offset(circleCenterX,circleCenterY),
                )
    
                //circle center - iconSize/2
                val iconSize = 48f
                translate(
                    left = circleCenterX- iconSize/2,
                    top = circleCenterY -iconSize/2
                ) {
                    with(painter) {
    
                        draw(
                            size = Size(iconSize,iconSize),//painter.intrinsicSize,
                            colorFilter = ColorFilter.tint(Color.White)
                        )
                    }
                }
    

    enter image description here