简体   繁体   English

Compose 中带圆角的 CircularProgressIndicator

[英]CircularProgressIndicator with rounded corner in Compose

I'm trying to round corners of CircularProgressIndicator using jetpack compose.我正在尝试使用 jetpack compose 绕过 CircularProgressIndicator 的角落。 But I don't see any member variables to do so.但我没有看到任何成员变量这样做。 Following is the source code, but it doesn't take Stroke as a parameter.以下是源代码,但它没有以 Stroke 作为参数。 If it can then we'll be able to create our custom Stroke with a round cap.如果可以,那么我们将能够创建带有圆帽的自定义 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)
    }
}

I made my rounded progress like this:我取得了这样的全面进展:

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

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

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

cap is hardcoded in source functions. cap 在源函数中被硬编码。 Solution I use is basically copy-pasting source functions and changing stroke cap.我使用的解决方案基本上是复制粘贴源功能和更改笔画上限。 Also for linear indicator you need to do some additional math, so that rounded caps don't go over specified width.同样对于线性指示器,您需要做一些额外的数学运算,这样圆顶帽就不会超过指定的宽度。

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

As an idea, you can just draw 2 circles above your progress作为一个想法,您可以在进度上方画 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)
        )
    }
}

just add clip to LinearProgressIndicator like this:只需像这样将剪辑添加到LinearProgressIndicator

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

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

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