Как нарисовать прозрачную дугу с окончанием угла линии в компоновке джетпака
Я хочу нарисовать прозрачную дугу после окончания радиуса цветового градиента. Звучит запутанно. У меня есть индикатор выполнения, в котором я заканчиваю строку в позиции. После этого я хочу показать прозрачное дуговое пространство в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.
ОБНОВЛЯТЬ
После того, как user2016562 упомянул код, который я попробовал с белым цветом, и я вижу, что там есть крошечная белая вертикальная полоса
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
)
}
}
}
}
}
Результат
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
)
}