自定义上下两个方向的柱形图

用compose绘制一个自定义的柱形图标,上面表示一个列表值,下方表示另外一个列表值

效果大概是这样的

@Composable
fun ProgramBarChartCompose(
    modifier: Modifier = Modifier,
    upValues: List<Float?>,
    downValues: List<Float?>,
    currentIndex: Int,

) {
    val colorUp = Color(0xFF5284FF)
    val colorDown = Color(0xFF8CD359)
    val colorDefault = Color(0xFF31426D)

    val maxUpValue = maxOf(6f, upValues.filterNotNull().maxOrNull() ?: 0f) + 1
    val maxDownValue = maxOf(6f, downValues.filterNotNull().maxOrNull() ?: 0f) + 1
    val barCount = upValues.size.coerceAtLeast(1)

    Canvas(modifier = modifier) {
        val totalWeight = 120f + 50f
        val upRegionHeight = size.height * (120f / totalWeight)
        val downRegionHeight = size.height * (50f / totalWeight)
        val splitBaseLine = upRegionHeight

        val totalParts = barCount * 4 + (barCount + 1) * 5
        val unit = size.width / totalParts
        val barWidth = unit * 4
        val barSpacing = unit * 5

        for (i in 0 until barCount) {
            val cx = i * (barWidth + barSpacing) + barSpacing
            val upVal = upValues.getOrNull(i)
            val downVal = downValues.getOrNull(i)
            val isActive = i <= currentIndex

            val upColor = if (isActive) colorUp else colorDefault
            val downColor = if (isActive) colorDown else colorDefault

            // --- Up Bar ---
            if (upVal != null && maxUpValue > 0) {
                val upHeight = (upVal / maxUpValue).coerceIn(0f, 1f) * upRegionHeight
                val upTop = splitBaseLine - upHeight

                val rect = Rect(
                    offset = Offset(cx, upTop),
                    size = Size(barWidth, upHeight)
                )

                val roundRect = RoundRect(
                    rect = rect,
                    topLeft = CornerRadius(barWidth / 2),
                    topRight = CornerRadius(barWidth / 2),
                    bottomLeft = CornerRadius(0f),
                    bottomRight = CornerRadius(0f)
                )

                val path = Path().apply {
                    addRoundRect(roundRect, Direction.Clockwise)
                }

                drawPath(path = path, color = upColor)
            }


            // --- Down Bar ---
            if (downVal != null && maxDownValue > 0) {
                val downHeight = (downVal / maxDownValue).coerceIn(0f, 1f) * downRegionHeight
                val rect = Rect(
                    offset = Offset(cx, splitBaseLine),
                    size = Size(barWidth, downHeight)
                )

                val roundRect = RoundRect(
                    rect = rect,
                    topLeft = CornerRadius(0f),
                    topRight = CornerRadius(0f),
                    bottomLeft = CornerRadius(barWidth / 2),
                    bottomRight = CornerRadius(barWidth / 2)
                )

                val path = Path().apply {
                    addRoundRect(roundRect, Direction.Clockwise)
                }

                drawPath(path = path, color = downColor)
            }

        }
    }
}

如果想要给某个view画上边框或者下边框


/**
 * 自定义边框
 * @param strokeWidthDp 边框宽度
 * @param cornerRadiusDp 圆角
 * @param borderColor 颜色
 * @param noTop 不要上面的线,只画两边
 */
fun Modifier.drawUpBorder(
    strokeWidthDp: Dp = 2.dp,
    cornerRadiusDp: Dp = 8.dp,
    borderColor: Color = Color_EE4343,
    noTop: Boolean = false
) =
    this.drawWithContent {
        drawContent()
        if (strokeWidthDp > 0.dp) {

            val strokeWidth = strokeWidthDp.toPx()
            val cornerRadius = cornerRadiusDp.toPx()

            // 创建绘制三边的路径
            val path = Path().apply {
                // 起始点:左上圆弧结束点 (右上角圆弧起始点)
                moveTo(0f, cornerRadius)

                // 绘制左边线 (从上到下)
                lineTo(0f, size.height)

                // 3. 左上圆角 (从下向上到左向右) - 顺时针90度
                arcTo(
                    rect = Rect(0f, 0f, cornerRadius * 2, cornerRadius * 2),
                    startAngleDegrees = 180f,
                    sweepAngleDegrees = 90f,
                    forceMoveTo = false
                )

                if (noTop) {
                    moveTo(size.width - cornerRadius, 0f)
                } else {
                    // 4. 上边线
                    lineTo(size.width - cornerRadius, 0f)
                }


                // 5. 右上圆角
                arcTo(
                    rect = Rect(
                        size.width - 2 * cornerRadius,
                        0f,
                        size.width,
                        cornerRadius * 2
                    ),
                    startAngleDegrees = 270f,
                    sweepAngleDegrees = 90f,
                    forceMoveTo = false
                )

                // 6. 右边线
                lineTo(size.width, size.height)
            }

            // 绘制边框路径
            drawPath(
                path = path, color = borderColor, style = Stroke(
                    width = strokeWidth,
                    cap = StrokeCap.Round,
                    join = StrokeJoin.Round
                )
            )
        }
    }



fun Modifier.drawDownBorder(
    strokeWidthDp: Dp = 2.dp,
    cornerRadiusDp: Dp = 8.dp,
    borderColor: Color = Color_EE4343,
    noBottom: Boolean = false
) =
    this.drawWithContent {
        drawContent()
        if (strokeWidthDp > 0.dp) {

            val strokeWidth = strokeWidthDp.toPx()
            val cornerRadius = cornerRadiusDp.toPx()

            // 创建绘制三边的路径
            val path = Path().apply {
                // 1. 左下角开始点
                moveTo(0f, size.height - cornerRadius)

                // 2. 左下圆角(逆时针,从上往下到左往右)
                arcTo(
                    rect = Rect(0f, size.height - 2 * cornerRadius, 2 * cornerRadius, size.height),
                    startAngleDegrees = 180f,
                    sweepAngleDegrees = 90f,
                    forceMoveTo = false
                )

                if (noBottom) {
                    // 只画左右边,跳过底边
                    moveTo(size.width, size.height - cornerRadius)
                } else {
                    // 3. 底部直线
                    lineTo(size.width - cornerRadius, size.height)
                }

                // 4. 右下圆角(顺时针,从下往上到右往左)
                arcTo(
                    rect = Rect(
                        size.width - 2 * cornerRadius,
                        size.height - 2 * cornerRadius,
                        size.width,
                        size.height
                    ),
                    startAngleDegrees = 90f,
                    sweepAngleDegrees = 90f,
                    forceMoveTo = false
                )

                // 5. 右边线往上
                lineTo(size.width, 0f)

                // 6. 左边线往上
                moveTo(0f, size.height)
                lineTo(0f, 0f)
            }

            drawPath(
                path = path,
                color = borderColor,
                style = Stroke(
                    width = strokeWidth,
                    cap = StrokeCap.Round,
                    join = StrokeJoin.Round
                )
            )

        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值