Created
December 27, 2024 05:12
-
-
Save raipradeep/7e606c654854768fdccc6fedd4821456 to your computer and use it in GitHub Desktop.
Line Chart With Canvas In Jetpack Compose
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Composable | |
fun LineChart( | |
labelsXAxis: List<String>, | |
dataPointsYAxis: List<Float>, | |
modifier: Modifier = Modifier, | |
textColor: Color = AppTheme.Dark, | |
axisSpacing: Float = 60f, | |
maxDataPoints: Int = 35// Count for X Axis Data | |
) { | |
val trimmedDataPointsYAxis = if (dataPointsYAxis.size > maxDataPoints) { | |
dataPointsYAxis.takeLast(maxDataPoints) | |
} else { | |
dataPointsYAxis | |
} | |
val trimmedLabelsXAxis = if (labelsXAxis.size > maxDataPoints) { | |
labelsXAxis.takeLast(maxDataPoints) | |
} else { | |
labelsXAxis | |
} | |
val density = LocalDensity.current | |
val yAxisSpace = with(density) { 10.dp.toPx() } | |
val xAxisSpace = with(density) { 10.dp.toPx() } | |
val maxY = (trimmedDataPointsYAxis.maxOrNull() ?: 0f) | |
val minY = (trimmedDataPointsYAxis.minOrNull() ?: 0f) | |
val textSizeSp = 12.sp | |
val textSizePx = with(density) { textSizeSp.toPx() } | |
Canvas(modifier = modifier.padding(vertical = 16.dp, horizontal = 16.dp)) { | |
val chartWidth = size.width - yAxisSpace - axisSpacing | |
val chartHeight = size.height - xAxisSpace - axisSpacing | |
val xStep = chartWidth / (trimmedDataPointsYAxis.size - 1) | |
val yScale = chartHeight / (maxY - minY) | |
val path = Path().apply { | |
reset() | |
moveTo(0f, chartHeight) // Start from the left | |
for (i in trimmedDataPointsYAxis.indices) { | |
val x = i * xStep // | |
val y = chartHeight - (trimmedDataPointsYAxis[i] - minY) * yScale | |
lineTo(x, y) | |
} | |
lineTo((trimmedDataPointsYAxis.size - 1) * xStep, chartHeight) | |
close() | |
} | |
drawPath( | |
path = path, | |
brush = Brush.verticalGradient( | |
colors = listOf( | |
progressStartColor.copy(alpha = 0.5f), | |
Color.Transparent | |
), | |
startY = 0f, | |
endY = chartHeight | |
) | |
) | |
for (i in 0 until trimmedDataPointsYAxis.size - 1) { | |
val startX = i * xStep | |
val startY = chartHeight - (trimmedDataPointsYAxis[i] - minY) * yScale | |
val endX = (i + 1) * xStep | |
val endY = chartHeight - (trimmedDataPointsYAxis[i + 1] - minY) * yScale | |
drawLine( | |
color = progressStartColor, | |
start = Offset(startX, startY), | |
end = Offset(endX, endY), | |
strokeWidth = 4f | |
) | |
} | |
// Y-axis labels | |
for (i in 0..8) { | |
val value = minY + i * (maxY - minY) / 8 | |
val y = chartHeight - (value - minY) * yScale | |
drawContext.canvas.nativeCanvas.apply { | |
drawText( | |
String.format("%.1f", value), | |
size.width - yAxisSpace / 2, | |
y, | |
android.graphics.Paint().apply { | |
textSize = textSizePx | |
textAlign = android.graphics.Paint.Align.CENTER | |
color = textColor.toArgb() | |
} | |
) | |
} | |
} | |
// Dynamically skip X-axis labels | |
val labelSkipCount = (trimmedDataPointsYAxis.size / (chartWidth / (xStep * 3))).toInt() | |
.coerceAtLeast(1) // Adjust label skip logic | |
for (i in trimmedDataPointsYAxis.indices step labelSkipCount) { | |
val startX = i * xStep | |
drawContext.canvas.nativeCanvas.apply { | |
save() | |
rotate(-60f, startX, chartHeight + 20f + axisSpacing) | |
drawText( | |
trimmedLabelsXAxis.getOrElse(i) { "" }, | |
startX, | |
chartHeight + 20f + axisSpacing, | |
android.graphics.Paint().apply { | |
textSize = textSizePx | |
textAlign = android.graphics.Paint.Align.CENTER | |
color = textColor.toArgb() | |
} | |
) | |
restore() | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment