DEV Community

bright inventions
bright inventions

Posted on • Updated on • Originally published at brightinventions.pl

Adding features to your chart on Android

In the previous post I did show you how to make a basic setup of Android linear chart using MPAndroidChart library. Now I'll show you some features I found useful.

tuning

Custom handling of chart tap event

The library provides default highlight values behavior on tap or drag. You may want to handle it by yourself, so get familiar with OnChartValueSelectedListener. It contains 2 methods:

  • onNothingSelected
  • onValueSelected

onValueSelected provides Entry object which gives you an access to the data, that is FoodSearch in our case. Let's use it to something simple, like displaying some info in the toast, to see how it works:

lineChart.isHighlightPerTapEnabled = true

lineChart.setOnChartValueSelectedListener(object : OnChartValueSelectedListener {

    override fun onNothingSelected() {}

    override fun onValueSelected(e: Entry, h: Highlight) {
        val yearWeek = (e.data as FoodSearch).week_id
        Toast.makeText(baseContext, "date: $yearWeek", Toast.LENGTH_SHORT).show()
    }
})
Enter fullscreen mode Exit fullscreen mode

highlight chart

If you want to display some view over selected Entry you may take advantage of the Highlight object in the onValueSelected method and get Entry's coordinates on the screen by accsessing xPx and yPxproperties and set them to the view.

Live Data

MPAndroidChart does not officially support realtime data, **however* (...)*

As long as we keep reference to the specific DataSet object, we are able to add and remove entries dynamically. It might be a result of syncing data from the internet or partial progress some asynchronous task - doesn't matter. Let's add a random entry on the button click.

val rand = Random()
bananaDataSet.addEntry(Entry(bananaDataSet.entryCount.toFloat(), rand.nextFloat() * 100))
yogurtDataSet.addEntry(Entry(yogurtDataSet.entryCount.toFloat(), rand.nextFloat() * 100))
Enter fullscreen mode Exit fullscreen mode

When modifying data set, BOTH LineData object and the chart need to be notified about it. To do so, invoke proper method on them, then simply invalidate the chart like you would with any other view element.

lineChart.data.notifyDataChanged()
lineChart.notifyDataSetChanged()
lineChart.invalidate()
Enter fullscreen mode Exit fullscreen mode

I recommend you to check out other ways of updating chart data described in the docs.

Viewport

Let's say you want to put readability aside, remove labels and axes with all that white spaces and make a pure chart view fill the screen. Labels/ axis configuration is obvious, just disable them:

lineChart.axisLeft.isEnabled = false
lineChart.axisRight.isEnabled = false
lineChart.xAxis.isEnabled = false
lineChart.description.isEnabled = false
lineChart.legend.isEnabled = false
Enter fullscreen mode Exit fullscreen mode

Unfortunately, it does not make the job done. As you can see below there are still space between the chart and the parent view left.

default viewport

How to disable the chart padding? The library provides us a simple way to modify chart's Viewport. In this case call method setViewPortOffsets(0f,0f,0f,0f) on the chart object. It sets chart's offset (padding) to 0. Just remember all viewport modifications have to be called after data is set up.

BUT...!

It's a solution you need to be cautious about. What does the documentation say about using this approach?

USE THIS ONLY WHEN YOU KNOW WHAT YOU ARE DOING.

Why? During a short adventure with chart's viewport I came across one problematic issue. Offset seemed to be recalculated independently and every other view besides the chart itself didn't know about the change. This may cause some render problems, for example like this:

viewport issue

Displayed value is the mentioned "other view" that does not know about the offset change, so edge values are cut. Keep this in mind modifying viewport! For more viewport modifications check out the project's wiki page.

Drawable Values

If you need to mark any value in some special way you may define Entry's icon property. It's an instance of a Drawable class, so it's very easy to layout the icon in a resource file and then apply it to the Entry. In our example let's mark with a red dot each value that differs from the previous one more than 15 to see every bigger steeper slope on the chart.

Define any drawable resource, for example 20x20 red oval:

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <size android:width="20dp" android:height="20dp" />
    <solid android:color="#ff0000" />
</shape>
Enter fullscreen mode Exit fullscreen mode

Now add some code to getEntriesFromCSV method. Before adding a new Entry to an array, add the Drawable as the icon's property if the point fulfills the condition:


data?.mapIndexed { index, foodSearch ->
    val entry = Entry(index.toFloat(), foodSearch.value.toFloat(), foodSearch)
    val steeperSlope = 15

    if (index != 0 && (foodSearch.value > data?.get(index - 1)!!.value + steeperSlope ||
                    foodSearch.value < data?.get(index - 1)!!.value - steeperSlope)) {

        val icon = resources.getDrawable(R.drawable.ic_balloon, null)
        entry.icon = icon
    }

    entries.add(entry)
}

Enter fullscreen mode Exit fullscreen mode

In order to see the icons, set the drawIcons flag to true on each DataSet object:

bananaDataSet.setDrawIcons(true)
yogurtDataSet.setDrawIcons(true)
Enter fullscreen mode Exit fullscreen mode

Thank's to that icons are displayed over specified entries:

icons over values

Originally published at brightinventions.pl

By Radek Pieczątkiewicz, Software Engineer @ Bright Inventions
Email

Top comments (0)