highcharts / highcharts-android

Android wrapper for Highcharts usage
Other
126 stars 30 forks source link

HIChart Not Loading Sometimes on Android #218

Closed KissloveDewangan closed 5 months ago

KissloveDewangan commented 2 years ago
Screenshot 2021-12-02 at 6 40 41 PM

I have integerated highcharts-android natively in android. Above, is the issue screenshot happening sometimes on my android device where the chart is not loading and getting blank screen. And the above screenshot is taken by debugging the HIChartView webview using chrome dev tools. My highchart implementation as given below:-

  1. At runtime, dynamically creating HIChartView object (not attaching HIChartView on XML)
  2. set all the series data, and setting all the required HIOptions to HIChartView
  3. Attaching the HIChartView object on FrameLayout using addview() method

Most of the time highchart is working great. But sometimes in an app session it is not working at all (all highchart HIChartView suddenly stops working) and I have to kill the app entirely and then cold starting the app then the highchart starts working again.

Any help would be highly appreciated. Please let me know if you need any further information

soommy12 commented 2 years ago

Hi @KissloveDewangan ! Thank you for the question. At start, I'm not sure what do you mean by nativaly implementing Highcharts in Android. For me it sounds like the library was used not in android project but in some other solution which let you create an android app. The wrapper wasn't created for such purposes and tested on any other platforms than native android so I cannot guarantee that it will behave correctly on those.

Anyways, for me it looks like a problem with dynamic chart size calculation. Please make sure that one of the sizes (height or width) will be set to match_parent and the other one has set a specific value in dp. This is known problem and that's the only solution I can provide. At this moment I cannot think of any other way of helping you. You can provide more info such as platform you are using the native code on, chart initialization code and so on.

KissloveDewangan commented 2 years ago

Hi @soommy12 , Thank you for your quick response. Few things I want to clarify further. We have integrated the Android Highcharts library only and attaching the HIChartView at runtime as given below (written in Kotlin, Android Studio):


val chartView = view.inflate(R.layout.item_highchartview) as HIChartView
val lp = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
lp.gravity = Gravity.CENTER
chartView.layoutParams = lp
val options = HIOptions()

        options.exporting = HIExporting()
        options.exporting.enabled = false

        options.credits = HICredits()
        options.credits.enabled = false

        val legend = HILegend()
        legend.enabled = false

        options.legend = legend

        val chart = HIChart()
        if (type?.contains("area") == true) {
            chart.type = "area"
        }
        options.chart = chart
        options.chart.marginRight = 8
        options.chart.marginLeft = 8
        options.chart.marginTop = 0
        options.chart.spacingTop = 4
        options.chart.spacingBottom = 4
        options.chart.backgroundColor = HIColor.initWithHexValue("00000000")
        options.chart.events = HIEvents()
        options.chart.events.load = HIFunction(object : Runnable {
            override fun run() {
                events?.fireChartLoadEvent()
            }
        })

        val title = HITitle()
        title.text = ""
        options.title = title

        // Setting Up X axis

        val xAxis = HIXAxis()
        xAxis.categories = ArrayList(xAxisLabelsList)

        xAxis.tickInterval = xAxisLabelsList.size / 3

        xAxis.labels = HILabels()
        xAxis.labels.style = HICSSObject()
        xAxis.labels.style.fontSize = FONT_SIZE
        xAxis.visible = true
        xAxis.labels.enabled = true
        xAxis.lineWidth = 0

        options.xAxis = object : ArrayList<HIXAxis?>() {
            init {
                add(xAxis)
            }
        }

        // Setting Up Y axis

        val yAxis = HIYAxis()
        yAxis.visible = false
        yAxis.title = HITitle()
        yAxis.title.text = ""

        options.yAxis = object : ArrayList<HIYAxis?>() {
            init {
                add(yAxis)
            }
        }

        options.series = ArrayList(mainList)

        val tooltip = HITooltip()
        tooltip.split = true
        tooltip.valuePrefix = "₹"
        options.tooltip = tooltip

        val plotOptions = HIPlotOptions()

        if (type?.contains("stacked-area") == true) {
            plotOptions.area = HIArea()
            plotOptions.area.stacking = "normal"
            plotOptions.area.lineWidth = 1
            plotOptions.area.marker = HIMarker()
            plotOptions.area.marker.enabled = false
//            plotOptions.area.fillOpacity = 1
            val linkedList = LinkedList<HIStop>()
            linkedList.add(HIStop(0f, HIColor.initWithHexValue("CFD4FF")))
            linkedList.add(HIStop(1f, HIColor.initWithHexValue("CFD4FF")))
            plotOptions.area.fillColor = HIColor.initWithLinearGradient(HIGradient(), linkedList)
        } else {
            plotOptions.line = HILine()
            plotOptions.line.lineWidth = 1
            plotOptions.line.marker = HIMarker()
            plotOptions.line.marker.enabled = false
        }
        options.plotOptions = plotOptions
        chartView.options = options
view.addView(chartView)

Please go through above code and let me know if I'm missing something.
soommy12 commented 2 years ago

Hi @KissloveDewangan ! Thanks for additional info. You didn't clraify what framework are you using for develop you app (debugging via chrome dev tools doesn't look like typical android app, are you using react native or something else? If yes, this library was not designed neither tested for such solutions).

Anyway, as I mentioned from my previous post make sure that HIChartView class has fixed height or width in dp. In your code you are setting both dimensions to MATCH_PARENT. Please, use fixed value for one of those dimensions (preferably height), let's say 400dp. That way it should work. This is happening because HC view is in WebView under the hood which has some problem with dynamic dimension calculaction of the view which in turn may cause that the view is not visible in the end.

warnyul commented 2 years ago

I experience something similar. In our case HIChartView in a ConstraintLayout, and we have set a fixed height for the chart. And sometimes when we open the screen the WebView on the HIChartView has 0 height, however when I checked the layout with layout inspector the HiChartView has 249dp height.

Also I saw an error message on the log: E/HIChartView: javascript:(function update(options) { chart.update(options); })({ ... E/Highcharts: Uncaught ReferenceError: chart is not defined

<com.highsoft.highcharts.core.HIChartView
  android:id="@+id/chart"
  android:layout_width="0dp"
  android:layout_height="250dp"
  android:layout_marginHorizontal="16dp"
  android:layout_marginTop="8dp"
  app:layout_constraintEnd_toEndOf="parent"
  app:layout_constraintStart_toStartOf="parent"
  app:layout_constraintTop_toBottomOf="@id/some_label" />
Screenshot 2022-08-31 at 18 07 58 Screenshot 2022-08-31 at 18 08 12

We draw and redraw the chart with this code:

viewModel.chartModel.observe(viewLifecycleOwner) { options: HIOptions ->
    if (chart.options == null) {
        chart.options = options
    } else {
        chart.update(options)
    }
}
zangico commented 2 years ago

I'm facing a similar problem. I'm trying to implement HighCharts in an activity wich retrieves dinamycally new data with an asynctask. If i try to add these new data to the chart I get this error:

A WebView method was called on thread 'AsyncTask #1'. All WebView methods must be called on the same thread.

If I use

runOnUiThread(new Runnable() {
            @Override
            public void run() {
                             hiChartView.redraw();
            }
        });

chart doesn't appear and in the log you'll find

E/Highcharts: Uncaught ReferenceError: chart is not defined

MikolajMichalczak commented 6 months ago

Hi there, Thank you for sharing your experience. This behavior can occur when asynchronously fetched data is delivered very quickly. Conceptually, on the Android side, all objects have been created and the JavaScript request to create the chart has been sent, but on the JavaScript side, everything hasn't been fully initialized yet. Since the wrapper is based on WebView, the JavaScript code still needs to execute. I found that introducing a delay of around 500 milliseconds resolved the issue in my case. An ideal solution would be to defer the update until the chart has fully loaded, which can be detected using the load event.

Regarding the code provided by @warnyul with the observer, it's hard to imagine a scenario where options is null, as they are necessary for rendering the chart.

Furthermore, there is a workaround to address this issue. It arises with the update() method. Therefore, if the data that updates the chart are the series, they can be updated by direct assignment, in which case this problem does not occur:

chart.options.series = ArrayList(listOf(series))
MikolajMichalczak commented 5 months ago

Closing due to inactivity. Please feel free to reopen the ticket if you have any further questions.