You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改MPAndroidChart中柱状图的柱子样式?(圆角/自定义Drawable)

MPAndroidChart柱状图样式修改方案

针对你的需求,既可以实现柱子的圆角处理,也可以设置自定义Drawable来定制柱子样式,以下是具体实现方式:


一、仅实现柱子圆角处理(推荐,适配目标样式)

1. 全局圆角(四个角均圆角)

如果不需要区分顶部/底部圆角,MPAndroidChart 3.0+版本可以直接通过BarDataSet的barBorderRadius属性快速设置:

val dataSet = BarDataSet(entries, "")
// 设置圆角半径,数值根据需求调整
dataSet.barBorderRadius = 8f

2. 仅顶部圆角(匹配目标样式)

如果需要仅顶部圆角,需要自定义柱子渲染器,替换默认的BarChartRenderer:

class RoundTopBarChartRenderer(chart: BarChart, animator: ChartAnimator, viewPortHandler: ViewPortHandler) : BarChartRenderer(chart, animator, viewPortHandler) {
    private val topRadius = 8f // 自定义顶部圆角半径

    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

        val barBuffers = mBarBuffers
        barBuffers[index].setPhases(phaseX, phaseY)
        barBuffers[index].feed(dataSet)
        trans.pointValuesToPixel(barBuffers[index].buffer)

        val isSingleColor = dataSet.colors.size == 1
        if (isSingleColor) {
            mRenderPaint.color = dataSet.color
        }

        var j = 0
        while (j < barBuffers[index].buffer.size step 4) {
            if (!mViewPortHandler.isInBoundsLeft(barBuffers[index].buffer[j + 2])) {
                j += 4
                continue
            }
            if (!mViewPortHandler.isInBoundsRight(barBuffers[index].buffer[j])) {
                break
            }
            if (!isSingleColor) {
                mRenderPaint.color = dataSet.getColor(j / 4)
            }

            // 绘制仅顶部圆角的柱子
            val left = barBuffers[index].buffer[j]
            val top = barBuffers[index].buffer[j + 1]
            val right = barBuffers[index].buffer[j + 2]
            val bottom = barBuffers[index].buffer[j + 3]

            val path = Path()
            // 顶部左圆角
            path.moveTo(left, bottom)
            path.lineTo(left, top + topRadius)
            path.arcTo(RectF(left, top, left + topRadius * 2, top + topRadius * 2), 180f, 90f)
            // 顶部右圆角
            path.lineTo(right - topRadius, top)
            path.arcTo(RectF(right - topRadius * 2, top, right, top + topRadius * 2), 270f, 90f)
            path.lineTo(right, bottom)
            path.close()

            c.drawPath(path, mRenderPaint)
            if (drawBorder) {
                c.drawPath(path, mBarBorderPaint)
            }
            j += 4
        }
    }
}

然后在你的barChart函数中,初始化chart后替换渲染器:

// 替换为自定义顶部圆角渲染器
chart.renderer = RoundTopBarChartRenderer(chart, chart.animator, chart.viewPortHandler)

二、设置自定义柱子Drawable

如果需要更复杂的柱子样式(比如渐变、纹理填充等),可以通过两种方式实现:

1. 单个柱子设置图标

给指定BarEntry绑定自定义Drawable:

// 创建BarEntry时设置图标
val entry = BarEntry(xValue, yValue, ContextCompat.getDrawable(activity, R.drawable.custom_bar_drawable))
// 开启图标绘制
dataSet.setDrawIcons(true)
// 调整图标位置(按需修改)
dataSet.iconOffset = MPPointF(0f, -20f)

2. 全局自定义Drawable渲染

如果需要所有柱子使用同一个自定义Drawable,修改自定义渲染器的drawDataSet方法,用drawable.draw(canvas)替代原有的矩形绘制逻辑即可。


修改后的完整示例代码

fun barChart(
    activity: Activity,
    chart: BarChart,
    entries: ArrayList<BarEntry>,
    columnsNames: ArrayList<String>,
    colors: ArrayList<Int>,
) {
    // 替换为自定义顶部圆角渲染器
    chart.renderer = RoundTopBarChartRenderer(chart, chart.animator, chart.viewPortHandler)

    val colorsTemplate = intArrayOf(
        Color.rgb(74, 160, 150), Color.rgb(74, 160, 150), Color.rgb(74, 160, 150),
        Color.rgb(74, 160, 150), Color.rgb(74, 160, 150)
    )
    val colorList: ArrayList<Int> = ArrayList()
    for (color in colorsTemplate) {
        colorList.add(color)
    }
    val dataSet = BarDataSet(entries, "")

    val decimalFormat = DecimalFormat("0.##")
    dataSet.setValueFormatter(object : ValueFormatter() {
        override fun getFormattedValue(value: Float): String {
            return decimalFormat.format(value)
        }
    })

    dataSet.colors = colorList
    val data = BarData(dataSet)
    data.barWidth = 0.5f
    data.setDrawValues(true)
    chart.setData(data)
    chart.setAutoScaleMinMaxEnabled(true)

    val xAxis = chart.xAxis
    xAxis.valueFormatter = IndexAxisValueFormatter(columnsNames)
    xAxis.position = XAxis.XAxisPosition.BOTTOM
    xAxis.setDrawGridLines(false)
    xAxis.setDrawAxisLine(false)
    xAxis.granularity = 1f
    xAxis.labelCount = columnsNames.size
    xAxis.labelRotationAngle = 0f // 360f等同于不旋转,改为0f更直观

    chart.legend.isEnabled = false
    chart.description.isEnabled = false
    chart.axisRight.isEnabled = false

    chart.axisLeft.labelCount = 3
    chart.axisLeft.axisMinimum = 0f

    chart.animateY(1000)
    chart.invalidate()
}

// 自定义顶部圆角柱子渲染器
class RoundTopBarChartRenderer(chart: BarChart, animator: ChartAnimator, viewPortHandler: ViewPortHandler) : BarChartRenderer(chart, animator, viewPortHandler) {
    private val topRadius = 8f // 自定义顶部圆角半径

    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

        val barBuffers = mBarBuffers
        barBuffers[index].setPhases(phaseX, phaseY)
        barBuffers[index].feed(dataSet)
        trans.pointValuesToPixel(barBuffers[index].buffer)

        val isSingleColor = dataSet.colors.size == 1
        if (isSingleColor) {
            mRenderPaint.color = dataSet.color
        }

        var j = 0
        while (j < barBuffers[index].buffer.size step 4) {
            if (!mViewPortHandler.isInBoundsLeft(barBuffers[index].buffer[j + 2])) {
                j += 4
                continue
            }
            if (!mViewPortHandler.isInBoundsRight(barBuffers[index].buffer[j])) {
                break
            }
            if (!isSingleColor) {
                mRenderPaint.color = dataSet.getColor(j / 4)
            }

            // 绘制仅顶部圆角的柱子
            val left = barBuffers[index].buffer[j]
            val top = barBuffers[index].buffer[j + 1]
            val right = barBuffers[index].buffer[j + 2]
            val bottom = barBuffers[index].buffer[j + 3]

            val path = Path()
            // 顶部左圆角
            path.moveTo(left, bottom)
            path.lineTo(left, top + topRadius)
            path.arcTo(RectF(left, top, left + topRadius * 2, top + topRadius * 2), 180f, 90f)
            // 顶部右圆角
            path.lineTo(right - topRadius, top)
            path.arcTo(RectF(right - topRadius * 2, top, right, top + topRadius * 2), 270f, 90f)
            path.lineTo(right, bottom)
            path.close()

            c.drawPath(path, mRenderPaint)
            if (drawBorder) {
                c.drawPath(path, mBarBorderPaint)
            }
            j += 4
        }
    }
}

内容的提问来源于stack exchange,提问作者pyroblinchik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 03:25:20