Как нарисовать прозрачную дугу с окончанием угла линии в компоновке джетпака

Я хочу нарисовать прозрачную дугу после окончания радиуса цветового градиента. Звучит запутанно. У меня есть индикатор выполнения, в котором я заканчиваю строку в позиции X. После этого я хочу показать прозрачное дуговое пространство в позиции X с помощью линейного градиента. Я пытаюсь использовать drawArc, но это работает неправильно.

@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(),
                    )
                }
            }
        }
    }
}

Фактический результат

Ожидаемый результат

Вы можете найти исходный код RangeComposition.kt.

ОБНОВЛЯТЬ

После того, как @GabrieleMariotti упомянул код, который я попробовал с белым цветом, и я вижу, что есть белая крошечная вертикальная полоса.

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() }
            val pathEffect = PathEffect.dashPathEffect(floatArrayOf(canvasHeight / 19, canvasHeight / 19), 0f)
            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
            drawLine(
                color = Color.White,
                start = Offset(x = progressBarPointerInPixel, y = canvasHeight / 2),
                end = Offset(x = progressBarPointerInPixel + strokeWidthPx / 2, y = canvasHeight / 2),
                strokeWidth = strokeWidthPx,
            )
            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 - strokeWidthPx / 2),
                size = Size(strokeWidthPx, strokeWidthPx),
                color = Color.White,
                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 = 1.2.dp.toPx(),
                        pathEffect = pathEffect
                    )
                }
            }
        }
    }
}

Результат

0
0
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В вашей дуге вы должны изменить смещение topLeft, учитывая также смещение высоты из-за strokeWidthPx. Что-то вроде:

topLeft = Offset(x = progressBarPointerInPixel, y = canvasHeight / 2 - strokeWidthPx/2),

Также вы должны добавить линию от progressBarPointerInPixel до progressBarPointerInPixel + strokeWidthPx/2 из-за закругленных углов.

Что-то вроде:

    drawLine(
      //gray line
    )

    drawLine(
        color = Color.Cyan,
        start = Offset(x = progressBarPointerInPixel , y = canvasHeight / 2),
        end = Offset(x = progressBarPointerInPixel + strokeWidthPx/2, y = canvasHeight / 2),
        strokeWidth = strokeWidthPx,
    )

    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 - strokeWidthPx/2),
        size = Size(strokeWidthPx,strokeWidthPx),
        color = Color.Cyan,
        startAngle = -90f,
        sweepAngle = 180f,
        useCenter = true
    )

Чтобы получить прозрачную дугу, вы можете добавить blendMode = BlendMode.DstOut к линии + дуге. Также требуется применить alpha !=1F к Canvas с помощью graphicsLayer(alpha = 0.99f) Проверьте документ для получения более подробной информации о blendMode.

Canvas(
    modifier = Modifier.fillMaxSize().graphicsLayer(alpha = 0.99f)
    ) {
       
    drawLine(
       //gray line
    )

    
    drawLine(
        //...
        color = Color.Cyan,
        blendMode = BlendMode.DstOut
    )

    drawLine(
       //gradient
    )

    drawArc(
        blendMode = BlendMode.DstOut
    )

}

ваш код добавляет arc после строки. Но мы добавляем Прозрачный цвет, он показывает меня за серым цветом. это отдельный вопрос, я должен открыть для этого?

Vivek Modi 13.02.2023 14:55

@VivekModi Здесь довольно сложно добиться прозрачного раздела. Вам действительно нужна прозрачная дуга или вам достаточно дуги с тем же цветом фона вокруг бара?

Gabriele Mariotti 13.02.2023 15:10

Я думаю, фона вокруг машины будет достаточно, и он работает как положено. Я поставил arc белого цвета и получил очень тонкую прямую вертикальную линию. Можем ли мы удалить это?

Vivek Modi 13.02.2023 15:18

Я добавил белый цвет в arc и добавил код в раздел ОБНОВЛЕНИЕ.

Vivek Modi 13.02.2023 15:21

Я должен понять, почему эта тонкая граница из-за серой линии

Gabriele Mariotti 13.02.2023 15:33

Пожалуйста, дайте мне знать, если вы найдете правильный способ решения проблемы.

Vivek Modi 13.02.2023 15:43

@VivekModi добавил код для создания прозрачного раздела. Я должен понять, почему существует тонкая граница.

Gabriele Mariotti 13.02.2023 15:58

Без проблем. @GabrieleMariotti благодарит миллион за поддержку. Пожалуйста, дайте мне знать, если вы найдете что-то.

Vivek Modi 13.02.2023 16:53

Другие вопросы по теме