Mpandroidchart: Custom BarChartRenderer and recycler Attempt to read from null array

Created on 18 Jul 2018  路  2Comments  路  Source: PhilJay/MPAndroidChart

Summary
I have made a custom BarChartRenderer to display icons below each column(code below). it is implemented inside recycle viewHolder in setData method triggered from addapter's onBind().
Now when i scroll the recycler fast i get an exception thrown from the library. note that when i disable the custom renderer there is not error.

java.lang.NullPointerException: Attempt to read from null array at com.github.mikephil.charting.renderer.BarChartRenderer.drawDataSet(BarChartRenderer.java:136) at com.github.mikephil.charting.renderer.BarChartRenderer.drawData(BarChartRenderer.java:80) at com.github.mikephil.charting.charts.BarLineChartBase.onDraw(BarLineChartBase.java:231) at android.view.View.draw(View.java:16178) at android.view.View.updateDisplayListIfDirty(View.java:15174) at android.view.View.draw(View.java:15948) at android.view.ViewGroup.drawChild(ViewGroup.java:3609) at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399) at android.view.View.updateDisplayListIfDirty(View.java:15169) at android.view.View.draw(View.java:15948) at android.view.ViewGroup.drawChild(ViewGroup.java:3609) at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399) at android.view.View.draw(View.java:16181) at android.view.View.updateDisplayListIfDirty(View.java:15174) at android.view.View.draw(View.java:15948) at android.view.ViewGroup.drawChild(ViewGroup.java:3609) at android.support.v7.widget.RecyclerView.drawChild(RecyclerView.java:4703) at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399) at android.view.View.draw(View.java:16181) at android.support.v7.widget.RecyclerView.draw(RecyclerView.java:4107) at android.view.View.updateDisplayListIfDirty(View.java:15174) at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593) at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573) at android.view.View.updateDisplayListIfDirty(View.java:15134) at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593) at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573) at android.view.View.updateDisplayListIfDirty(View.java:15134) at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593) at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573) at android.view.View.updateDisplayListIfDirty(View.java:15134) at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593) at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573) at android.view.View.updateDisplayListIfDirty(View.java:15134) at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593) at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573) at android.view.View.updateDisplayListIfDirty(View.java:15134) at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593) at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573) at android.view.View.updateDisplayListIfDirty(View.java:15134) at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593) at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573) at android.view.View.updateDisplayListIfDirty(View.java:15134) at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3593) at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3573) at android.view.View.updateDisplayListIfDirty(View.java:15134) at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:281) at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:287) at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:322) at android.view.ViewRootImpl.draw(ViewRootImpl.java:2615) at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2434) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2067) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858) at android.view.Choreographer.doCallbacks(Choreographer.java:670) at android.view.Choreographer.doFrame(Choreographer.java:606) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.ja

my custom render

`class BarChartCustomRenderer(chart: BarDataProvider, animator: ChartAnimator, viewPortHandler: ViewPortHandler, private val imageList: ArrayList, private val context: Context) : BarChartRenderer(chart, animator, viewPortHandler) {

override fun drawValues(canvas: Canvas) {
    val dataSets = mChart.barData.dataSets
    val valueOffsetPlus = Utils.convertDpToPixel(OFF_SET_VALUE)
    var negOffset: Float

    for (i in 0 until mChart.barData.dataSetCount) {

        val dataSet = dataSets[i]
        applyValueTextStyle(dataSet)
        val valueTextHeight = Utils.calcTextHeight(mValuePaint, "8").toFloat()
        negOffset = valueTextHeight + valueOffsetPlus

        val buffer = mBarBuffers[i]

        var left: Float
        var right: Float
        var top: Float
        var bottom: Float

        var columnIndex = 0
        while (columnIndex < buffer.buffer.size * mAnimator.phaseX) {

            left = buffer.buffer[columnIndex]
            top = buffer.buffer[columnIndex + 1]
            right = buffer.buffer[columnIndex + 2]
            bottom = buffer.buffer[columnIndex + 3]

            val xCenter = (left + right) / 2

            if (!mViewPortHandler.isInBoundsRight(xCenter))
                break

            if (!mViewPortHandler.isInBoundsY(top) || !mViewPortHandler.isInBoundsLeft(xCenter)) {
                columnIndex += 4
                continue
            }

            val entry = dataSet.getEntryForIndex(columnIndex / 4)
            val yValue = entry.y
            mValuePaint.textAlign = Paint.Align.CENTER

            if (yValue > 0) {
                drawValue(canvas, dataSet.valueFormatter, yValue, entry, i, xCenter, top - 4, dataSet.getValueTextColor(columnIndex / 4))
            }

            val bitmap = imageList[columnIndex / 4]

            val scaledBitmap = getScaledBitmap(bitmap)
            canvas.drawBitmap(scaledBitmap, xCenter - scaledBitmap.width / 2, bottom + 0.5F * negOffset - scaledBitmap.width / 2F, null)
            scaledBitmap.recycle()
            columnIndex += 4
        }
    }
}

private fun getScaledBitmap(bitmap: Bitmap): Bitmap {
    val width = context.resources.getDimension(R.dimen.dimen_18).toInt()
    val height = context.resources.getDimension(R.dimen.dimen_18).toInt()
    return Bitmap.createScaledBitmap(bitmap, width, height, true)
}

}`

Expected Behavior
The viewHolder is properly binded and no error on draw happens

Possible Solution
not a clue :/

Device (please complete the following information):

  • nexus 5; android 6

Most helpful comment

@czerwix Thanks for this helped me out.

initBuffers() needs to be called in the drawData() method now though, there is no onDraw() to override.

All 2 comments

for anybody else who encounters this problem. override onDraw()and initBuffers() there before super.onDraw()

@czerwix Thanks for this helped me out.

initBuffers() needs to be called in the drawData() method now though, there is no onDraw() to override.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vishvendu picture vishvendu  路  3Comments

DarkHelmet67 picture DarkHelmet67  路  3Comments

JungYongWook picture JungYongWook  路  3Comments

OnlyInAmerica picture OnlyInAmerica  路  3Comments

manucheri picture manucheri  路  3Comments