Categories
Android

Custom bar chart for android – tutorial

In our Skenit app, used for gym management, we wanted to improve user experience by providing them with statistics that show monthly users per gym, the number of users per training type, financial reports for every month, etc. By doing this we would give our users more insight into the gyms they own, their employees, their business growth, and income.

One of the best ways to represent this data to users is through data visualization. Data visualization offers an easy approach to observing and analyzing trends, outliers, and patterns in data by utilizing visual components like charts, graphs, and maps. Data visualization is essential to analyze massive amounts of information and allow you to make data-driven decisions that can improve your business even more. 

Using MPAndroidChart for data visualization

For this task, I have chosen MPAndroidChart which allows us to create different kinds of data visualization. This library is easy to use and charts are highly customizable, the library is well documented and there is a vast amount of implementation examples online.

In this tutorial, we will give an example of a bar chart that shows income per month, as shown on the image.

Preparation

1. Import this library into build.gradle (Module: app):

<code class="language-kotlin">dependencies {
     implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
 }</code>

2. Import this library into build.gradle (Project: …):

<code class="language-kotlin">repositories {
    maven { url 'https://jitpack.io' }
}</code>

3. Go to fragment.xml and declare the BarChart view in the layout:

<code class="language-xml">
&lt;com.github.mikephil.charting.charts.BarChart
       android:id="@+id/bar_chart
       android:layout_height="150dp"/>
</code>

Be sure to add layout_height or you won’t be able to see anything because the chart will be collapsed.

The style/look of the chart has to be made programmatically, inside of a fragment or activity.

Stylize bar chart

1. Make a function with a parameter of BarChart type. This method will determine the look of a chart and will initialize it. You can make multiple functions for different styles.

<code class="language-kotlin">private fun initializeBarChart(barChart: BarChart) {}</code>

2. Stylize the chart by adding different parameters. I recommend checking out the documentation for the list of all the parameters you can adjust. You can experiment until you get the look you want. Feel free to copy these parameters if you want the same look we have:

<code class="language-kotlin">private fun initializeBarChart(barChart: BarChart) {
   barChart.description.isEnabled = false
   barChart.axisRight.isEnabled = false
   barChart.animateY(1500)
   barChart.legend.isEnabled = false
   barChart.setTouchEnabled(false)
   barChart.xAxis.setDrawGridLines(false)
   barChart.xAxis.position = XAxis.XAxisPosition.BOTTOM
   barChart.xAxis.setDrawLabels(true)
   barChart.xAxis.granularity = 1F
   barChart.xAxis.valueFormatter = AxisFormatter()
   barChart.axisLeft.isGranularityEnabled = true
   barChart.axisLeft.axisMinimum = 0F
   barChart.axisLeft.valueFormatter = LargeValueFormatter()
}</code>

Don’t worry if AxisFormatter() and LargeValueFormatter() are red, we will solve that next.

Formatters

1. X and Y-axis inputs can only be numbers, we cannot make January an input for the X-axis. That is why we have to make an axis value formatter, and add it as a value formatter for the X-axis (barChart.xAxis.valueFormatter = AxisFormatter()). Axis formatter will change the number on the X-axis (which represents one X-axis entry) to the corresponding name of the month.

Make an inner class that extends IndexAxisValueFormatter(), and override function getAxisLabel(). This function will take input from X-axis (value) in form of a number, and get a string for it from the list of strings (monthLabels). Variable monthLabels will be populated with month names when we start iterating through our data, later in this article.

<code class="language-kotlin">val monthLabels: ArrayList<String> = ArrayList()

inner class AxisFormatter : IndexAxisValueFormatter() {
    override fun getAxisLabel(value: Float, axis: AxisBase?): String {
        val indexOfBar = value.toInt()
        return if (indexOfBar - 1 < monthLabels.size) {
            monthLabels[index - 1]
        } else {
            ""
        }
    }
}</code>

2. We also want to show the value of each bar on top of it. Make another inner class that extends ValueFormatter(), that will take the value of each bar and create a string that can be shown on top of every bar:

<code class="language-kotlin">inner class IntValueFormatter : ValueFormatter() {
    override fun getFormattedValue(value: Float): String {
        return value.toInt().toString()
    }
}</code>

Create bar chart

1. Create a function that will take data and show it in form of a bar chart. This function will take a map of String and Int, where String stands for the name of the month and Int for the income amount for that month:

<code class="language-kotlin">private fun createIncomePerMonthChart (Map<String, Int>) {}</code>

2. Declare two variables. The first variable (barChartValues) will be the list of all the bar chart entries (containing X and Y-axis values) and the second one (xAxisBarEntry) will just increment after each entry creating X-axis entry values for the first variable (barChartValues).

Function forEach will iterate through data and for each entry it will add a new BarEntry to the list, using data of each month as Y-axis values, and xAxisBarEntry value for X-axis values. It will also store the names of months in the variable monthLabels, and the AxisFormatter that we made earlier will use that variable/list to swap X-axis values with the corresponding month names.

<code class="language-kotlin">private fun createIncomePerMonthChart (incomePerMonth:  Map<String, Int>) {
    val barChartValues: ArrayList<BarEntry> = ArrayList()
    var xAxisBarEntry = 0F

        incomePerMonth.forEach { month ->
                yAxisValues.add(BarEntry(xAxisBarEntry, month.value.toFloat()))
                monthLabels.add(month)
                xAxisBarEntry++
            }
}</code>

3. Now we will pass the data to the BarDataSet (set of bars in this chart). We also have to choose a color for the bars, and in this case, we used the same color for all the bars. We also added value formatter (IntValueFormatter()) to the barDataSet so that we can see numbers on top of the bars:

<code class="language-kotlin">private fun createChart (incomePerMonth:  Map<String, Int>) {
…
val barDataSet = BarDataSet(barChartValues, "")
barDataSet.color = getColor(requireContext(), R.color.orange)
barDataSet.valueFormatter = IntValueFormatter()

val data = BarData(barDataSet)
binding.bcIncomePerMonthsEUR.data = data
binding.bcIncomePerMonthsEUR.invalidate()
}</code>

4. Initialize the bar chart in onViewCreated method. This will assign the defined style to the view from the layout:

<code class="language-kotlin">initializeBarChart(binding.barChart)</code>

5. Finally, create the bar chart by providing data to the createChart method. This method should be also called in onViewCreated method below/after the initializeBarChart method:

<code class="language-kotlin">createChart(data)</code>