android MPAndroidChart 圆角柱状图的实现

2,782 阅读2分钟

一开始。我的条形图是这样的 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201214175526433.jpeg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zOTk0NjkzMQ==,size_16,color_FFFFFF,t_70

但是产品 给的原型图是这样的 在这里插入图片描述 条形图有的顶端有一个圆角 当时。所有的需求都做完了 只剩下这个圆角了

然后网上一顿搜,github csdn 简书 其他的一些博客 发现特别好的方法没有 我一脸懵逼。难道真要改源码吗? 然后继续一顿搜。 最后绝望了。 改源码吧

网上各种找。最后定位到绘制条形图的是BarChartRenderer类下的drawDataSet方法,那么就需要重写这个方法 那么就定义一个自己的类。用来重写源码

新建SuddleBarChartRenderer类 在里面这样写

import android.graphics.Canvas
import android.graphics.Path
import android.graphics.RectF
import com.github.mikephil.charting.animation.ChartAnimator
import com.github.mikephil.charting.highlight.Highlight
import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet
import com.github.mikephil.charting.renderer.BarChartRenderer
import com.github.mikephil.charting.utils.Utils
import com.github.mikephil.charting.utils.ViewPortHandler


class SuddleBarChartRenderer(
    chart: BarDataProvider?,
    animator: ChartAnimator?,
    viewPortHandler: ViewPortHandler?
) :
    BarChartRenderer(chart, animator, viewPortHandler) {
    val mBarShadowRectBuffer = RectF()
    override fun drawDataSet(c: Canvas, dataSet: IBarDataSet, index: Int) {
        val trans = mChart.getTransformer(dataSet.axisDependency)

        mBarBorderPaint.color = dataSet.barBorderColor
        mBarBorderPaint.strokeWidth = Utils.convertDpToPixel(dataSet.barBorderWidth)

        val drawBorder = dataSet.barBorderWidth > 0f

        val phaseX = mAnimator.phaseX
        val phaseY = mAnimator.phaseY

        // draw the bar shadow before the values

        // draw the bar shadow before the values
        if (mChart.isDrawBarShadowEnabled) {
            mShadowPaint.color = dataSet.barShadowColor
            val barData = mChart.barData
            val barWidth = barData.barWidth
            val barWidthHalf = barWidth / 2.0f
            var x: Float
            var i = 0
            val count = Math.min(
                Math.ceil((dataSet.entryCount.toFloat() * phaseX).toDouble()).toInt(),
                dataSet.entryCount
            )
            while (i < count) {
                val e = dataSet.getEntryForIndex(i)
                x = e.x
                mBarShadowRectBuffer.left = x - barWidthHalf
                mBarShadowRectBuffer.right = x + barWidthHalf
                trans.rectValueToPixel(mBarShadowRectBuffer)
                if (!mViewPortHandler.isInBoundsLeft(mBarShadowRectBuffer.right)) {
                    i++
                    continue
                }
                if (!mViewPortHandler.isInBoundsRight(mBarShadowRectBuffer.left)) break
                mBarShadowRectBuffer.top = mViewPortHandler.contentTop()
                mBarShadowRectBuffer.bottom = mViewPortHandler.contentBottom()
                c.drawRect(mBarShadowRectBuffer, mShadowPaint)
                i++
            }
        }

        // initialize the buffer

        // initialize the buffer
        val buffer = mBarBuffers[index]
        buffer.setPhases(phaseX, phaseY)
        buffer.setDataSet(index)
        buffer.setInverted(mChart.isInverted(dataSet.axisDependency))
        buffer.setBarWidth(mChart.barData.barWidth)

        buffer.feed(dataSet)

        trans.pointValuesToPixel(buffer.buffer)

        val isSingleColor = dataSet.colors.size == 1

        if (isSingleColor) {
            mRenderPaint.color = dataSet.color
        }

        var j = 0
        while (j < buffer.size()) {
            if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) {
                j += 4
                continue
            }
            if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) break
            if (!isSingleColor) {
                // Set the color for the currently drawn value. If the index
                // is out of bounds, reuse colors.
                mRenderPaint.color = dataSet.getColor(j / 4)
            }
//            c.drawRect(//以前的源码
//                buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
//                buffer.buffer[j + 3], mRenderPaint
//            )
            val rectF = RectF(
                buffer.buffer[j],
                buffer.buffer[j + 1], buffer.buffer[j + 2], buffer.buffer[j + 3]
            )
            val path = Path()
            path.addRoundRect(
                rectF,
                floatArrayOf(14f, 14f, 14f, 14f, 14f, 0f, 0f, 0f),
                Path.Direction.CCW
            )
            c.drawPath(path, mRenderPaint)
            if (drawBorder) {
                c.drawPath(path, mRenderPaint)
            }
            j += 4
        }
    }
}
源码里是这样写的 
c.drawRect(//以前的源码
buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
buffer.buffer[j + 3], mRenderPaint
)
使用了canvas的drawRect方法 绘制了矩形,所以要把绘制方法改成绘制有圆角的矩形
所以我改成了这样
val rectF = RectF(buffer.buffer[j],buffer.buffer[j + 1],buffer.buffer[j + 2], buffer.buffer[j + 3])
val path = Path()
path.addRoundRect(rectF,floatArrayOf(14f, 14f, 14f, 14f, 14f, 0f, 0f, 0f),Path.Direction.CCW)
那么。我调用了path方法 这个方法是用来绘制几何图形的
然后绘制了圆角矩形
哈哈 

然后呢。要定义一个类CustomBarChart 用来传值

import android.content.Context
import android.util.AttributeSet
import com.github.mikephil.charting.charts.BarChart


class CustomBarChart : BarChart {
    constructor(context: Context?) : super(context) {}
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {}
    constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(context,
        attrs,
        defStyle) {
    }

    override fun init() {
        super.init()
        mRenderer = SuddleBarChartRenderer(this, mAnimator, mViewPortHandler)
    }
}

传值以后,要把改写后的源码绑定到你的xml结构里,代码如下

 <com.example.你的路径.CustomBarChart
            android:id="@+id/chart"
            android:layout_width="match_parent"
            android:layout_height="180dp"
            android:background="@color/white"
            android:padding="3dp" />

然后呢,加载试一下如何, 结果发现,有bug 点击到条形图上以后,有两种框,一种是修改的圆角,另一种是点击后加上的矩形, 这样不行 后来又找方法,发现在源码里,BarChartRenderer类中drawHighlighted这个方法是用来设置点击条形图选中的矩形框的 那就好办了,也改了

在SuddleBarChartRenderer类中,增加如下代码

override fun drawHighlighted(c: Canvas, indices: Array<Highlight>) {
        val barData = mChart.barData
        for (high in indices) {
            val set = barData.getDataSetByIndex(high.dataSetIndex)
            if (set == null || !set.isHighlightEnabled) continue
            val e = set.getEntryForXValue(high.x, high.y)
            if (!isInBoundsX(e, set)) continue
            val trans = mChart.getTransformer(set.axisDependency)
            mHighlightPaint.color = set.highLightColor
            mHighlightPaint.alpha = set.highLightAlpha
            val isStack = if (high.stackIndex >= 0 && e.isStacked) true else false
            val y1: Float
            val y2: Float
            if (isStack) {
                if (mChart.isHighlightFullBarEnabled) {
                    y1 = e.positiveSum
                    y2 = -e.negativeSum
                } else {
                    val range = e.ranges[high.stackIndex]
                    y1 = range.from
                    y2 = range.to
                }
            } else {
                y1 = e.y
                y2 = 0f
            }
            prepareBarHighlight(e.x, y1, y2, barData.barWidth / 2f, trans)
            setHighlightDrawPos(high, mBarRect)
            val path = Path()
//            path.addRoundRect(
//                mBarRect,
//                floatArrayOf(30f, 30f, 30f, 30f, 30f, 0f, 0f, 0f),
//                Path.Direction.CCW
//            )
            c.drawRoundRect(mBarRect,25.toFloat(),25.toFloat(),mHighlightPaint)
//            c.drawRect(mBarRect, mHighlightPaint)//源码
        }
    }

那么 加载一下,大功告成。