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