androidkotlinandroid-jetpack-composeandroid-jetpack-compose-canvasandroid-jetpack-compose-animation

Rectangle border progress bar


How i Draw Rectangle progress bar as a below in photo as CircleProgress indicator in android with JetpackCompose https://drive.google.com/file/d/1SXZcBp7uAesl-i6gHqFNXNpnc54Z6vdI/view?usp=share_link

CircularProgressIndicator(strokeWidth=10.dp,
                          progress = 1.0f,
                            color = Red,
                           modifier = Modifier.height(200.dp).width(200.dp).align(Alignment.Center))

Solution

  • You can change how to get progress for path progress i post an answer with 3 different alternatives

    Result

    enter image description here

    First create a path and add Rounded rectangle

     if (path.isEmpty) {
                        path.addRoundRect(
                            RoundRect(
                                Rect(offset = Offset.Zero, size),
                                cornerRadius = CornerRadius(100.dp.toPx(), 100.dp.toPx())
                            )
                        )
                    }
    

    Then measure path using

        val pathMeasure by remember { mutableStateOf(PathMeasure()) }
    
                pathWithProgress.reset()
    
                pathMeasure.setPath(path, forceClosed = false)
                pathMeasure.getSegment(
                    startDistance = 0f,
                    stopDistance = pathMeasure.length * progress / 100f,
                    pathWithProgress,
                    startWithMoveTo = true
                )
    

    Then draw this path and original Path as in implementation

    @Preview
    @Composable
    private fun BorderProgressBar() {
    
        val startDurationInSeconds = 10
        var currentTime by remember {
            mutableStateOf(startDurationInSeconds)
        }
    
        var targetValue by remember {
            mutableStateOf(100f)
        }
    
        var timerStarted by remember {
            mutableStateOf(false)
        }
        LaunchedEffect(key1 = timerStarted) {
            if (timerStarted) {
                while (currentTime > 0) {
                    delay(1000)
                    currentTime--
                }
            }
        }
    
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(40.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            // This is the progress path which wis changed using path measure
            val pathWithProgress by remember {
                mutableStateOf(Path())
            }
    
            // using path
            val pathMeasure by remember { mutableStateOf(PathMeasure()) }
    
            val path = remember {
                Path()
            }
    
            val progress by animateFloatAsState(
                targetValue = targetValue,
                animationSpec = tween(startDurationInSeconds * 1000, easing = LinearEasing)
            )
    
            Box(contentAlignment = Alignment.Center) {
                Canvas(modifier = Modifier.size(250.dp, 140.dp)) {
    
                    if (path.isEmpty) {
                        path.addRoundRect(
                            RoundRect(
                                Rect(offset = Offset.Zero, size),
                                cornerRadius = CornerRadius(100.dp.toPx(), 100.dp.toPx())
                            )
                        )
                    }
                    pathWithProgress.reset()
    
                    pathMeasure.setPath(path, forceClosed = false)
                    pathMeasure.getSegment(
                        startDistance = 0f,
                        stopDistance = pathMeasure.length * progress / 100f,
                        pathWithProgress,
                        startWithMoveTo = true
                    )
    
    
                    clipPath(path) {
                        drawRect(Color.LightGray)
                    }
    
                    drawPath(
                        path = path,
                        style = Stroke(
                            6.dp.toPx()
                        ),
                        color = Color.Gray
                    )
    
                    drawPath(
                        path = pathWithProgress,
                        style = Stroke(
                            6.dp.toPx()
                        ),
                        color = Color.Blue
                    )
                }
    
                Text(text = "$currentTime", fontSize = 40.sp, color = Color.Blue)
            }
    
            Spacer(modifier = Modifier.height(20.dp))
            Box(contentAlignment = Alignment.Center) {
                Canvas(modifier = Modifier.size(250.dp, 140.dp)) {
    
                    if (path.isEmpty) {
                        path.addRoundRect(
                            RoundRect(
                                Rect(offset = Offset.Zero, size),
                                cornerRadius = CornerRadius(100.dp.toPx(), 100.dp.toPx())
                            )
                        )
                    }
                    pathWithProgress.reset()
    
                    pathMeasure.setPath(path, forceClosed = false)
                    pathMeasure.getSegment(
                        startDistance = 0f,
                        stopDistance = pathMeasure.length * progress.toInt() / 100f,
                        pathWithProgress,
                        startWithMoveTo = true
                    )
    
    
                    clipPath(path) {
                        drawRect(Color.LightGray)
                    }
    
                    drawPath(
                        path = path,
                        style = Stroke(
                            6.dp.toPx()
                        ),
                        color = Color.Gray
                    )
    
                    drawPath(
                        path = pathWithProgress,
                        style = Stroke(
                            6.dp.toPx()
                        ),
                        color = Color.Blue
                    )
                }
    
                Text(text = "$currentTime", fontSize = 40.sp, color = Color.Blue)
            }
    
            Spacer(modifier = Modifier.height(20.dp))
            Box(contentAlignment = Alignment.Center) {
                Canvas(modifier = Modifier.size(250.dp, 140.dp)) {
    
                    if (path.isEmpty) {
                        path.addRoundRect(
                            RoundRect(
                                Rect(offset = Offset.Zero, size),
                                cornerRadius = CornerRadius(100.dp.toPx(), 100.dp.toPx())
                            )
                        )
                    }
                    pathWithProgress.reset()
    
                    pathMeasure.setPath(path, forceClosed = false)
                    pathMeasure.getSegment(
                        startDistance = 0f,
                        stopDistance = pathMeasure.length * ((currentTime.toFloat() / startDurationInSeconds)),
                        pathWithProgress,
                        startWithMoveTo = true
                    )
    
    
                    clipPath(path) {
                        drawRect(Color.LightGray)
                    }
    
                    drawPath(
                        path = path,
                        style = Stroke(
                            6.dp.toPx()
                        ),
                        color = Color.Gray
                    )
    
                    drawPath(
                        path = pathWithProgress,
                        style = Stroke(
                            6.dp.toPx()
                        ),
                        color = Color.Blue
                    )
                }
    
                Text(text = "$currentTime", fontSize = 40.sp, color = Color.Blue)
            }
    
            Button(
                modifier = Modifier.fillMaxWidth(),
                onClick = {
                    targetValue = 0f
                    timerStarted = true
                }) {
                Text(text = "Start Timer")
            }
    
            Text(
                text = "currentTime: $currentTime, " +
                        "progress: ${progress.toInt()}"
            )
    
        }
    }