繁体   English   中英

Compose 中带圆角的 CircularProgressIndicator

[英]CircularProgressIndicator with rounded corner in Compose

我正在尝试使用 jetpack compose 绕过 CircularProgressIndicator 的角落。 但我没有看到任何成员变量这样做。 以下是源代码,但它没有以 Stroke 作为参数。 如果可以,那么我们将能够创建带有圆帽的自定义 Stroke。

@Composable
fun CircularProgressIndicator(
    /*@FloatRange(from = 0.0, to = 1.0)*/
    progress: Float,
    modifier: Modifier = Modifier,
    color: Color = MaterialTheme.colors.primary,
    strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth
) {
    val stroke = with(LocalDensity.current) {
        Stroke(width = strokeWidth.toPx(), cap = StrokeCap.Butt)
    }
    Canvas(
        modifier
            .progressSemantics(progress)
            .size(CircularIndicatorDiameter)
            .focusable()
    ) {
        // Start at 12 O'clock
        val startAngle = 270f
        val sweep = progress * 360f
        drawDeterminateCircularIndicator(startAngle, sweep, color, stroke)
    }
}

我取得了这样的全面进展:

LinearProgressIndicator(
    modifier = Modifier
    .background(Color.Transparent)
    .clip(CircleShape)
    .height(16.dp),
    progress = 1f
)

如果您尝试将圆形边缘设置为包含进度的框,只需在修改器中为背景设置一个形状(您还需要设置颜色)。

CircularProgressIndicator(modifier = Modifier.background(Color.Black, shape = RoundedCornerShape(15.dp)))

cap 在源函数中被硬编码。 我使用的解决方案基本上是复制粘贴源功能和更改笔画上限。 同样对于线性指示器,您需要做一些额外的数学运算,这样圆顶帽就不会超过指定的宽度。

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.progressSemantics
import androidx.compose.material.MaterialTheme
import androidx.compose.material.ProgressIndicatorDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp

private val LinearIndicatorWidth = 240.dp
private val LinearIndicatorHeight = ProgressIndicatorDefaults.StrokeWidth

private val CircularIndicatorDiameter = 40.dp

@Composable
internal fun RoundedLinearProgressIndicator(
    /*@FloatRange(from = 0.0, to = 1.0)*/
    progress: Float,
    modifier: Modifier = Modifier,
    color: Color = MaterialTheme.colors.primary,
    backgroundColor: Color = color.copy(alpha = ProgressIndicatorDefaults.IndicatorBackgroundOpacity)
) {
    Canvas(
        modifier
            .progressSemantics(progress)
            .size(LinearIndicatorWidth, LinearIndicatorHeight)
            .focusable()
    ) {
        val strokeWidth = size.height
        drawRoundedLinearProgressIndicator(
            startFraction = 0f,
            endFraction = 1f,
            color = backgroundColor,
            strokeWidth = strokeWidth
        )
        drawRoundedLinearProgressIndicator(
            startFraction = 0f,
            endFraction = progress,
            color = color,
            strokeWidth = strokeWidth
        )
    }
}

@Composable
internal fun RoundedCircularProgressIndicator(
    /*@FloatRange(from = 0.0, to = 1.0)*/
    progress: Float,
    modifier: Modifier = Modifier,
    color: Color = MaterialTheme.colors.primary,
    strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth
) {
    val stroke = with(LocalDensity.current) {
        Stroke(width = strokeWidth.toPx(), cap = StrokeCap.Round)
    }
    Canvas(
        modifier
            .progressSemantics(progress)
            .size(CircularIndicatorDiameter)
            .focusable()
    ) {
        // Start at 12 O'clock
        val startAngle = 270f
        val sweep = progress * 360f
        drawRoundedCircularIndicator(startAngle, sweep, color, stroke)
    }
}

private fun DrawScope.drawRoundedCircularIndicator(
    startAngle: Float,
    sweep: Float,
    color: Color,
    stroke: Stroke
) {
    // To draw this circle we need a rect with edges that line up with the midpoint of the stroke.
    // To do this we need to remove half the stroke width from the total diameter for both sides.
    val diameterOffset = stroke.width / 2
    val arcDimen = size.width - 2 * diameterOffset
    drawArc(
        color = color,
        startAngle = startAngle,
        sweepAngle = sweep,
        useCenter = false,
        topLeft = Offset(diameterOffset, diameterOffset),
        size = Size(arcDimen, arcDimen),
        style = stroke
    )
}

private fun DrawScope.drawRoundedLinearProgressIndicator(
    startFraction: Float,
    endFraction: Float,
    color: Color,
    strokeWidth: Float,
) {
    val cap = StrokeCap.Round
    val width = size.width
    val height = size.height
    // Start drawing from the vertical center of the stroke
    val yOffset = height / 2

    val roundedCapOffset = size.height / 2

    val isLtr = layoutDirection == LayoutDirection.Ltr
    val barStart = (if (isLtr) startFraction else 1f - endFraction) * width + if (isLtr) roundedCapOffset else -roundedCapOffset
    val barEnd = (if (isLtr) endFraction else 1f - startFraction) * width - if (isLtr) roundedCapOffset else -roundedCapOffset

    // Progress line
    drawLine(
        color = color,
        start = Offset(barStart, yOffset),
        end = Offset(barEnd, yOffset),
        strokeWidth = strokeWidth,
        cap = cap,
    )
}

作为一个想法,您可以在进度上方画 2 个圆圈

@Composable
fun RoundedCircularProgress(
        progress: Float,
        strokeWidth: Dp,
        progressColor: Color,
        modifier: Modifier = Modifier
) {
    Box(modifier = modifier.size(100.dp)) {
        // circle progress
        CircularProgressIndicator(
                progress = progress,
                color = progressColor,
                strokeWidth = strokeWidth,
                modifier = Modifier.fillMaxSize()
        )
        // start circle
        Spacer(modifier = Modifier
                .fillMaxSize()
                .wrapContentSize(align = Alignment.TopCenter)
                .size(strokeWidth)
                .background(progressColor, CircleShape)
        )
        // end circle
        Spacer(modifier = Modifier
                .fillMaxSize()
                .rotate(360 * progress)
                .wrapContentSize(align = Alignment.TopCenter)
                .size(strokeWidth)
                .background(progressColor, CircleShape)
        )
    }
}

只需像这样将剪辑添加到LinearProgressIndicator

modifier = Modifier.fillMaxWidth().clip(RoundedCornerShape(14.dp))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM