Skip to content

Instantly share code, notes, and snippets.

@decodeandroid
Created May 23, 2024 06:41
Show Gist options
  • Select an option

  • Save decodeandroid/6d72d8c9cfea07288218681885c66ce5 to your computer and use it in GitHub Desktop.

Select an option

Save decodeandroid/6d72d8c9cfea07288218681885c66ce5 to your computer and use it in GitHub Desktop.
Line Chart Jetpack Compose Canvas
@Preview(showBackground = true, showSystemUi = true)
@Composable
fun ShowLineChartPreview() {
val chartData = listOf(
Pair(1, 1.5),
Pair(2, 1.75),
Pair(3, 3.45),
Pair(4, 2.25),
Pair(5, 6.45),
Pair(6, 3.35),
Pair(7, 8.65),
Pair(8, 0.15),
Pair(9, 3.05),
Pair(10, 4.25)
)
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.White)
.padding(16.dp),
verticalArrangement = Arrangement.Center
) {
LineChart(
data = chartData,
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
.align(CenterHorizontally)
)
}
}
@Composable
fun LineChart(
data: List<Pair<Int, Double>> = emptyList(),
modifier: Modifier = Modifier
) {
val spacingFromLeft = 80f
val graphColor = Color.Green //color for your graph
val transparentGraphColor = remember { graphColor.copy(alpha = 0.5f) }
val upperValue = remember { (data.maxOfOrNull { it.second }?.plus(1))?.roundToInt() ?: 0 }
val lowerValue = remember { (data.minOfOrNull { it.second }?.toInt() ?: 0) }
val density = LocalDensity.current
//paint for the text shown in data values
val textPaint = remember(density) {
Paint().apply {
color = android.graphics.Color.BLACK
textAlign = Paint.Align.CENTER
textSize = density.run { 12.sp.toPx() }
}
}
Canvas(modifier = modifier) {
val spacePerData = (size.width - spacingFromLeft) / data.size
//loop through each index by step of 1
//data shown horizontally
(data.indices step 1).forEach { i ->
val hour = data[i].first
drawContext.canvas.nativeCanvas.apply {
drawText(
hour.toString(),
spacingFromLeft + i * spacePerData,
size.height,
textPaint
)
}
}
val priceStep = (upperValue - lowerValue) / 5f
//data shown vertically
(0..4).forEach { i ->
drawContext.canvas.nativeCanvas.apply {
drawText(
round(lowerValue + priceStep * i).toString(),
30f,
size.height - spacingFromLeft - i * size.height / 5f,
textPaint
)
}
}
//Vertical line
drawLine(
start = Offset(spacingFromLeft, size.height - spacingFromLeft),
end = Offset(spacingFromLeft, 0f),
color = Color.Black,
strokeWidth = 3f
)
//Horizontal line
drawLine(
start = Offset(spacingFromLeft, size.height - spacingFromLeft),
end = Offset(size.width - 40f, size.height - spacingFromLeft),
color = Color.Black,
strokeWidth = 3f
)
//Use this to show straight line path
val straightLinePath = Path().apply {
val height = size.height
//loop through index only not value
data.indices.forEach { i ->
val info = data[i]
val x1 = spacingFromLeft + i * spacePerData
val y1 =
(upperValue - info.second).toFloat() / upperValue * height - spacingFromLeft
if (i == 0) {
moveTo(x1, y1)
}
lineTo(x1, y1)
//drawCircle(color = Color.Black, radius = 5f, center = Offset(x1,y1)) //Uncomment it to see the end points
}
}
//Use this to show curved path
var medX: Float
var medY: Float
val curvedLinePath = Path().apply {
val height = size.height
data.indices.forEach { i ->
val nextInfo = data.getOrNull(i + 1) ?: data.last()
val x1 = spacingFromLeft + i * spacePerData
val y1 =
(upperValue - data[i].second).toFloat() / upperValue * height - spacingFromLeft
val x2 = spacingFromLeft + (i + 1) * spacePerData
val y2 =
(upperValue - nextInfo.second).toFloat() / upperValue * height - spacingFromLeft
if (i == 0) {
moveTo(x1, y1)
} else {
medX = (x1 + x2) / 2f
medY = (y1 + y2) / 2f
quadraticBezierTo(x1 = x1, y1 = y1, x2 = medX, y2 = medY)
}
//drawCircle(color = Color.White, radius = 5f, center = Offset(x1,y1))
//drawCircle(color = Color.Magenta, radius = 9f, center = Offset(medX,medY))
//drawCircle(color = Color.Blue, radius = 7f, center = Offset(x2,y2)) //Uncomment these to see the control Points
}
}
//Now draw path on canvas
drawPath(
path = straightLinePath,
color = graphColor,
style = Stroke(
width = 1.dp.toPx(),
cap = StrokeCap.Round
)
)
//To show the background transparent gradient
val fillPath = android.graphics.Path(straightLinePath.asAndroidPath()).asComposePath().apply {
lineTo(size.width - spacePerData, size.height - spacingFromLeft)
lineTo(spacingFromLeft, size.height - spacingFromLeft)
close()
}
drawPath(
path = fillPath,
brush = Brush.verticalGradient(
colors = listOf(
transparentGraphColor,
Color.Transparent
),
endY = size.height - spacingFromLeft
)
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment