Skip to content

Instantly share code, notes, and snippets.

@raipradeep
Created December 27, 2024 05:12
Show Gist options
  • Save raipradeep/7e606c654854768fdccc6fedd4821456 to your computer and use it in GitHub Desktop.
Save raipradeep/7e606c654854768fdccc6fedd4821456 to your computer and use it in GitHub Desktop.
Line Chart With Canvas In Jetpack Compose
@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