Search code examples
android-viewandroid-jetpack-composekotlin-coroutinesmpandroidchartkotlin-flow

Jetpack Compose: UnsupportedOperationException when adding Entries to MPAndroidCharts dynamically


I try to display live data in an MPAndroidChart hosted in an AndroidView.

I get the graph but an UnsupportedOperationException happens when I call addEntry() to update the graph dynamically. Am I doing something wrong?

You find a demo repo in the comments.

@Composable
fun MyLineChart() {
    val mainViewModel = viewModel()
    val sensorData = mainViewModel.sensorFlow.collectAsState(SensorModel(0F,0F)).value

    AndroidView(modifier = Modifier.fillMaxSize(),
        factory = { context ->
            val lineChart = LineChart(context)
            var entries = listOf(Entry(1f,1f))

            val dataSet = LineDataSet(entries, "Label").apply { color = Color.Red.toArgb() }

            val lineData = LineData(dataSet)
            lineChart.data = lineData
            lineChart.invalidate()

            lineChart
        }){

        try {
            Log.d("TAG", "MyLineChart: Update --- Current thread id: ${Thread.currentThread()}")

            it.data.dataSets[0].addEntry(Entry(sensorData.x, sensorData.y))
            it.lineData.notifyDataChanged()
            it.notifyDataSetChanged()
            it.invalidate()
        } catch(ex: Exception) {
            Log.d("TAG", "MyLineChart: $ex")
        }
    }
}

The data is sent to the view via the following ViewModel:

@HiltViewModel
class MainViewModel @Inject constructor(@ApplicationContext var appContext: Context) : ViewModel()  {
    private var rand: Random = Random(1)

    val sensorFlow: Flow<SensorModel> = flow<SensorModel> {

        while (true) {
            delay(1000L)
            Log.d("TAG", "sensorFlow: Current thread id: ${Thread.currentThread()}")
            emit(SensorModel(rand.nextFloat(), rand.nextFloat()))
        }
    }
}

Solution

  • You pass entries to LineDataSet, which is an immutable list.

    This library seems to have a pretty bad API, because it doesn't ask for a modifiable list as a parameter, but at the same time it doesn't make it modifiable on its side. This causes you to try to modify the immutable list, which leads to an exception.

    Replace

    var entries = listOf(Entry(1f,1f))
    

    with

    val entries = mutableListOf(Entry(1f,1f))
    

    p.s. I can't advise you another graph library as I haven't worked with any, but I would advise you to look for a library with a better API.