https://kotlinlang.org logo
Title
k

KotlinLeaner

02/12/2023, 7:00 PM
Hi another question, I have value in Percentage from
0
to
100%
which is in
Float
type . I am drawing vertical lines but in
Offset
which value is also in
Float
type. So how can I convert values in
pixel
values?
@Composable
fun DrawProgressBar() {
    val activity = LocalContext.current as AppCompatActivity
    val rangeComposition = RangeComposition()
    val itemLst = rangeComposition.bpExplained
    val boxSize = 30.dp
    Box(
        modifier = Modifier
            .background(Color.LightGray)
            .height(height = boxSize)
    ) {
        Canvas(
            modifier = Modifier
                .fillMaxWidth()
        ) {
            val canvasWidth = size.width
            drawLine(
                start = Offset(x = 0f, y = (boxSize / 2).toPx()),
                end = Offset(x = canvasWidth, y = (boxSize / 2).toPx()),
                color = Color.Black,
                strokeWidth = 8.dp.toPx(),
            )
            activity.logE("Pixel in size $canvasWidth")
            itemLst.forEach { rangeItem ->
                val endPointInPixel = rangeItem.endPoint
                activity.logE("name ${rangeItem.name} --++-- startPoint ${rangeItem.startPoint} --++-- endPoint ${rangeItem.endPoint} ")
                drawLine(
                    start = Offset(x = endPointInPixel, y = 0F),
                    end = Offset(x = endPointInPixel, y = boxSize.toPx()),
                    color = Color.Black,
                    strokeWidth = 4.dp.toPx(),
                )
            }
        }
    }
}
Pixel in size
996.0
in LogCat
Actual Output is like this
Expected Output
r

romainguy

02/12/2023, 8:00 PM
Are the x positions of the tick marks expressed in percentage? If so, multiply them by canvasWidth
k

KotlinLeaner

02/12/2023, 8:04 PM
yes x is in the percentage. It going out of width
fun DrawProgressBar() {
    val activity = LocalContext.current as AppCompatActivity
    val rangeComposition = RangeComposition()
    val itemLst = rangeComposition.bpExplained
    val boxSize = 30.dp
    Box(
        modifier = Modifier
            .background(Color.LightGray)
            .height(height = boxSize)
    ) {
        Canvas(
            modifier = Modifier.fillMaxSize()
        ) {
            val strokeWidth = 8.dp
            val canvasWidth = size.width
            val canvasHeight = size.height
            val strokeWidthPx = density.run { strokeWidth.toPx() }
            rangeComposition.initialiseList()
            drawLine(
                start = Offset(x = 0f, y = canvasHeight / 2),
                end = Offset(x = canvasWidth, y = canvasHeight / 2),
                color = Color.Black,
                strokeWidth = strokeWidthPx,
            )
            itemLst.forEachIndexed { index, rangeItem ->
                val endPointInFloat = rangeItem.endPoint
                activity.logE("name ${rangeItem.name} --++-- startPoint ${rangeItem.startPoint} --++-- endPoint ${rangeItem.endPoint} ")
                if (index != itemLst.lastIndex) {
                    drawLine(
                        start = Offset(x = endPointInFloat, y = 0F),
                        end = Offset(x = endPointInFloat, y = boxSize.toPx()),
                        color = Color.Black,
                        strokeWidth = 4.dp.toPx(),
                    )
                }
            }
        }
    }
}
r

romainguy

02/12/2023, 8:14 PM
Wait when you say percentage, is your float in 0..100? If so, divide by 100f and multiply by canvasWidth
k

KotlinLeaner

02/12/2023, 8:15 PM
yes, it's in 0..100. Perfect thanks a million. This works with your suggestion.
If we use
cap = StrokeCap.Round,
inside
drawLine
then, why this is going out of Canvas?
Box(
    modifier = Modifier
        .background(Color.LightGray)
        .height(height = boxSize)
) {
    Canvas(
        modifier = Modifier.fillMaxSize()
    ) {
        val strokeWidth = 8.dp
        val canvasWidth = size.width
        val canvasHeight = size.height
        val strokeWidthPx = density.run { strokeWidth.toPx() }
        drawLine(
            start = Offset(x = 0f, y = canvasHeight / 2),
            end = Offset(x = canvasWidth, y = canvasHeight / 2),
            color = Color.Black,
            strokeWidth = strokeWidthPx,
            cap = StrokeCap.Round,
        )
    }
}
r

romainguy

02/12/2023, 8:25 PM
Because round caps start at the line endpoints
k

KotlinLeaner

02/12/2023, 8:26 PM
Okk got it
Can we give color to cap?
r

romainguy

02/13/2023, 12:44 AM
So it has to be the same as the line
You can draw caps separately using a Path though
k

KotlinLeaner

02/13/2023, 1:02 AM
Actual output
Expected output
@Composable
    fun DrawProgressBar() {
        val activity = LocalContext.current as AppCompatActivity
        val rangeComposition = RangeComposition()
        val itemLst = rangeComposition.bpExplained
        val boxSize = 30.dp
        val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue))
        val progressBarPointer = rangeComposition.findReadingWithPointer(142, 90).second
        Box(
            modifier = Modifier
                .background(Color.White)
                .height(height = boxSize)
        ) {
            Canvas(
                modifier = Modifier.fillMaxSize()
            ) {
                val strokeWidth = 8.dp
                val canvasWidth = size.width
                val canvasHeight = size.height
                val strokeWidthPx = density.run { strokeWidth.toPx() }
                drawLine(
                    start = Offset(x = 0f, y = canvasHeight / 2),
                    end = Offset(x = canvasWidth, y = canvasHeight / 2),
                    color = Color.Gray,
                    strokeWidth = strokeWidthPx,
                    cap = StrokeCap.Round,
                )
                val progressBarPointerInPixel = (progressBarPointer / 100f) * canvasWidth
                activity.logE("progressBarPointerInPixel $progressBarPointerInPixel")
                drawLine(
                    brush = brush,
                    start = Offset(x = 0f, y = canvasHeight / 2),
                    end = Offset(x = progressBarPointerInPixel, y = canvasHeight / 2),
                    strokeWidth = strokeWidthPx,
                    cap = StrokeCap.Round,
                )
                drawArc(
                    topLeft = Offset(x = progressBarPointerInPixel, y = canvasHeight / 2),
                    size = Size(8.dp.toPx(), strokeWidthPx),
                    color = Color.Cyan,
                    startAngle = -90f,
                    sweepAngle = 180f,
                    useCenter = true
                )
                itemLst.forEachIndexed { index, rangeItem ->
                    val endPointInPixel = (rangeItem.endPoint / 100f) * canvasWidth
                    if (index != itemLst.lastIndex) {
                        drawLine(
                            start = Offset(x = endPointInPixel, y = 0F),
                            end = Offset(x = endPointInPixel, y = boxSize.toPx()),
                            color = Color.Black,
                            strokeWidth = 4.dp.toPx(),
                        )
                    }
                }
            }
        }
    }
I tried this, so how how can I do through path?
r

romainguy

02/13/2023, 1:15 AM
You need to shift your arc up and give it the same color as the background
You'll also need a rectangle. Or draw a line with a round cap using the background color to erase the part between the orange and blue lines (in your desired output screenshot)
k

KotlinLeaner

02/13/2023, 9:27 AM
Ok I'll try that one..