
In this article we will learn how to make Bar Chart using jetpack compose Canvas in Android. This article is in continuation with our Canvas series in Android using Kotlin and Jetpack Compose. You can read about how to make Line Chart here. If you want to watch full video on how to create Bar Chart then watch here.
To draw bar chart first we need some sample to draw chart. Our sample data is list of Pair of String and Integer. First part of the Pair is String which will contain the info about the Data point and Second part of Pair will contain the actual Integer value based on which we will draw the graph.
val chartData = listOf(
Pair("I", 90),
Pair("II", 110),
Pair("III", 70),
Pair("IV", 205),
Pair("V", 150),
Pair("VI", 175)
)
Now we need to create some variable for the our graph.
val spacingFromLeft = 100f
val spacingFromBottom = 40f
val upperValue = remember { (chartData.maxOfOrNull { it.second }?.plus(1)) ?: 0 }
val lowerValue = remember { (chartData.minOfOrNull { it.second }?.toInt() ?: 0) }
val density = LocalDensity.current
In jetpack compose canvas we need to define Text Paint for any type of text that we need to draw on the canvas. In this Paint Object we decide what property we want to give to our text like its color, text size, text font etc.
//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() }
}
}
Now we need to find canvas height and width to accurately display the content. Create a variable which maintain the space b/w each vertical bar along the horizontal axis. Then loop through data and each time draw text along vertical and horizontal axis.
val canvasHeight = size.height
val canvasWidth = size.width
val spacePerData = (canvasWidth - spacingFromLeft) / chartData.size
//loop through each index by step of 1
//data shown horizontally
(chartData.indices step 1).forEach { i ->
val text = chartData[i].first
drawContext.canvas.nativeCanvas.apply {
drawText(
text,
spacingFromLeft + 30f + i * spacePerData,
canvasHeight,
textPaint
)
}
}
Now we will show the vertical text at 5 points and also show a small line in front of text to accurately visualize the bar value. For this we will loop 5 times and each time draw text along with a small horizontal line along the vertical axis.
val valuesToShow = 5f //we will show 5 data values on vertical line
val eachStep = (upperValue - lowerValue) / valuesToShow
//data shown vertically
(0..4).forEach { i ->
drawContext.canvas.nativeCanvas.apply {
drawText(
round(lowerValue + eachStep * i).toString(),
20f,
canvasHeight - 30f - i * canvasHeight / 5f,
textPaint
)
}
//draw horizontal line at each value
drawLine(
start = Offset(
spacingFromLeft - 20f,
canvasHeight - spacingFromBottom - i * canvasHeight / 5f
),
end = Offset(
spacingFromLeft,
canvasHeight - spacingFromBottom - i * canvasHeight / 5f
),
color = Color.Black,
strokeWidth = 3f
)
}
Now draw the full length horizontal and vertical line along the X & Y axis using drawLine function.
//Vertical line
drawLine(
start = Offset(spacingFromLeft, canvasHeight - spacingFromBottom),
end = Offset(spacingFromLeft, 0f),
color = Color.Black,
strokeWidth = 3f
)
//Horizontal line
drawLine(
start = Offset(spacingFromLeft, canvasHeight - spacingFromBottom),
end = Offset(canvasWidth - 40f, canvasHeight - spacingFromBottom),
color = Color.Black,
strokeWidth = 3f
)
Now come to our final step and draw the bars. We will loop upon the our graph data and fetching its index and value. We are showing the round corner bars and a text above the bar which will show the bar value. The drawRoundRect function takes following arguments :
- topLeft – this is a Offset(top left corner location of Rect) which takes two coordinates points(X and Y)
- size – this is of Type Size(size in X, size in Y) define width and height of the rectangle.
- cornerRadius – here you define the radius of corner of the rectangle.
//draw bars
chartData.forEachIndexed { index, chartPair ->
//draw text at top of each bar
drawContext.canvas.nativeCanvas.apply {
drawText(
chartPair.second.toString(),
spacingFromLeft + 40f + index * spacePerData,
(upperValue - chartPair.second.toFloat()) / upperValue * canvasHeight - 10f,
textPaint
)
}
//draw Bar for each value
drawRoundRect(
color = Color.Magenta,
topLeft = Offset(
spacingFromLeft + 10f + index * spacePerData,
(upperValue - chartPair.second.toFloat()) / upperValue * canvasHeight
),
size = Size(
55f,
(chartPair.second.toFloat() / upperValue) * canvasHeight - spacingFromBottom
),
cornerRadius = CornerRadius(10f, 10f)
)
}
GitHub gist for this video: here