MPAndroidChart——柱状图BarChart详细使用

目录

1.需求

2、多组/分组柱状图的具体实现

2.1 xml代码

2.2 代码实现

2.3 实现效果

 3.顶部圆角柱状图

3.1 代码实现

3.2 圆角代码

 4.堆叠柱状图

4.1 代码实现

 4.2 实现效果

5.分段/区间柱状图

5.1 需求

5.2 实现代码

5.3  实现效果

1.需求

实现柱状图,能够良好的反应用户某一段时间的各类数据,或者用于展示各个季度的各种财务相关的数据。

这次主要是app内需要实现三种柱状图,圆角柱状图,区间柱状图·,多组柱状图。

2、多组/分组柱状图的具体实现

2.1 xml代码

<com.github.mikephil.charting.charts.BarChart
                android:id="@+id/barChart"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/lineChartTest3" />

2.2 代码实现

具体的API调用和注释都在代码里了,我就不补充了。

   private fun initBarChart() {
        mBarChart = binding.barChart
        binding.barChart.apply {
            isHighlightPerTapEnabled = false
        }
        setChartData()
        updateCommonUI()
        updateChartUI()
    }

    /**
     * 设置数据源
     */
    private fun setChartData() {
        val barEntries1: MutableList<BarEntry> = ArrayList()
        barEntries1.add(BarEntry(0f, 1500f))
        barEntries1.add(BarEntry(1f, 1400f))
        barEntries1.add(BarEntry(2f, 800f))
        barEntries1.add(BarEntry(3f, 2000f))
        val barDataSet1 = BarDataSet(barEntries1, "Q1")

        val barEntries2: MutableList<BarEntry> = ArrayList()
        barEntries2.add(BarEntry(0f, 1000f))
        barEntries2.add(BarEntry(1f, 1200f))
        barEntries2.add(BarEntry(2f, 1500f))
        barEntries2.add(BarEntry(3f, 900f))
        val barDataSet2 = BarDataSet(barEntries2, "Q2")

        val barEntries3: MutableList<BarEntry> = ArrayList()
        barEntries3.add(BarEntry(0f, 900f))
        barEntries3.add(BarEntry(1f, 1000f))
        barEntries3.add(BarEntry(2f, 1200f))
        barEntries3.add(BarEntry(3f, 1500f))
        val barDataSet3 = BarDataSet(barEntries3, "Q3")

        val barEntries4: MutableList<BarEntry> = ArrayList()
        barEntries4.add(BarEntry(0f, 1400f))
        barEntries4.add(BarEntry(1f, 800f))
        barEntries4.add(BarEntry(2f, 1000f))
        barEntries4.add(BarEntry(3f, 500f))
        val barDataSet4 = BarDataSet(barEntries4, "Q4")

        val colors: MutableList<Int> = ArrayList()
        colors.add(Color.GREEN)
        colors.add(Color.BLUE)
        colors.add(Color.RED)
        colors.add(Color.YELLOW)

        barDataSet1.color = colors[0]
        barDataSet2.color = colors[1]
        barDataSet3.color = colors[2]
        barDataSet4.color = colors[3]

        val barDataSets: MutableList<IBarDataSet> = ArrayList()
        barDataSets.add(barDataSet1)
        barDataSets.add(barDataSet2)
        barDataSets.add(barDataSet3)
        barDataSets.add(barDataSet4)

        val ba = BarData(barDataSets)
        mBarChart.data = ba
    }
//基本设置和y轴设置
    private fun updateCommonUI() {
        // 不显示图例
        mBarChart.legend.isEnabled = false
        // 不显示描述
        mBarChart.description.text = ""
        // 不绘制网格
        mBarChart.setDrawGridBackground(false)
        //不允许缩放
        mBarChart.isScaleYEnabled = false
        mBarChart.isScaleXEnabled = false
        mBarChart.setScaleEnabled(false)
        // 设置左y轴
        val yAxis = mBarChart.axisLeft
        // 设置y-label显示在图表外
        yAxis.setPosition(YAxisLabelPosition.OUTSIDE_CHART)
        // Y轴从0开始,不然会上移一点
        yAxis.setAxisMinValue(0f)
        // 设置y轴不画线
        yAxis.setDrawGridLines(false)
        // 不显示右y轴
        val rightAxis = mBarChart.axisRight
        rightAxis.isEnabled = false
    }

//x轴设置
    private fun updateChartUI() {
        val labels = arrayOf("Q1", "Q2", "Q3", "Q4")
        val xAxis = mBarChart.xAxis
        // 设置自定义的ValueFormatter
        xAxis.valueFormatter = object : ValueFormatter() {
            override fun getFormattedValue(p0: Float): String {
                val index = p0.toInt()
                if (index >= 0 && index < labels.size) {
                    return labels[index]
                }
                return ""
            }

        }
        // 设置x轴显示在下方
        xAxis.position = XAxisPosition.BOTTOM
        // 设置x轴不画线
        xAxis.setDrawGridLines(false)
        // 设置x轴标签从0开始
        xAxis.setAxisMinValue(0f)
        // 设置x轴显示的最大标签数量
        xAxis.setAxisMaxValue(labels.size.toFloat())
        // x轴标签居中
        xAxis.setCenterAxisLabels(true)
        // 接下来这段代码尤其重要,网上几乎99%的资料说的都是错的,这里详解一下
        // 1、要想标签跟group中间对齐,必须保证:(barWidth + barSpace) * group + groupSpace = granularity
        // 2、granularity < 1,则右边会有空余;
        // 3、granularity = 1,则刚好在一个屏幕显示开;
        // 4、granularity > 1,则一个屏幕显示不开;
        val barSpace = 0.025f
        val groupSpace = 0.1f
        val barWidth = 0.2f//宽度
        val granularity = (barWidth + barSpace) * labels.size + groupSpace
        xAxis.granularity = granularity
        mBarChart.scaleX = granularity
        mBarChart.barData.barWidth = barWidth
        mBarChart.groupBars(0f, groupSpace, barSpace)//分组
    }

2.3 实现效果

 3.顶部圆角柱状图

3.1 代码实现

    fun initBarChart2() {
        mBarChart2 = binding.barChart2
        mBarChart2.apply {
            isHighlightPerTapEnabled = false
            isScaleYEnabled = false
            isHighlightPerDragEnabled = false
            isHighlightFullBarEnabled = false
            isScaleXEnabled = false
            setScaleEnabled(false)
        }
        binding.barChart2.renderer = RoundedBarChartRenderer(binding.barChart2, binding.barChart2.animator, binding.barChart2.viewPortHandler)
        val list: ArrayList<BarEntry> = ArrayList() //实例化一个List用来存储数据
        list.add(BarEntry(1f, 13f))
        list.add(BarEntry(2f, 28f))
        list.add(BarEntry(3f, 36f))
        list.add(BarEntry(4f, 19f))
        list.add(BarEntry(5f, 49f))
        list.add(BarEntry(6f, 59f))
        list.add(BarEntry(7f, 89f))

        val barDataSet = BarDataSet(list, "测试")
        barDataSet.color = getColor(R.color.color_ff8561)
        barDataSet.setDrawValues(false)//不展示值

        val barData = BarData(barDataSet)
        barData.barWidth = 0.5f
        mBarChart2.data = barData
        mBarChart2.isHighlightPerTapEnabled = false
        mBarChart2.setDescription(null)
        mBarChart2.axisRight.isEnabled = false;//隐藏右侧Y轴   默认是左右两侧都有Y轴

        mBarChart2.xAxis.axisLineColor = Color.TRANSPARENT  // 设置 X 轴线条为透明
        mBarChart2.axisLeft.axisLineColor = Color.TRANSPARENT  // 设置左侧 Y 轴线条为透明
        mBarChart2.xAxis.textSize = 11f
        mBarChart2.xAxis.textColor = getColor(R.color.color_cacaca)
        mBarChart2.axisLeft.textSize = 11f
        mBarChart2.axisLeft.textColor = getColor(R.color.color_cacaca)

        val labels = arrayOf("SUN", "MON", "Tue", "Wed", "Thu", "Fri", "Sat")

        // 设置自定义的ValueFormatter
        mBarChart2.xAxis.valueFormatter = object : ValueFormatter() {
            override fun getFormattedValue(p0: Float): String {
                val index = (p0.toInt() - 1)
                if (index >= 0 && index < labels.size) {
                    return labels[index]
                }
                return ""
            }
        }
        mBarChart2.xAxis.apply {
            position = XAxisPosition.BOTTOM //设置x轴位置
            setDrawGridLines(false)
            // 设置x轴标签从0开始
            setAxisMinValue(0.5f)// 让第一个柱子居中对齐
            // 设置x轴显示的最大标签数量,居中对齐
            setAxisMaxValue(labels.size.toFloat() + 0.5f)
            // x轴标签居中,分组柱状图需要
            granularity = 1f
        }

        mBarChart2.axisLeft.apply {
            setPosition(YAxisLabelPosition.OUTSIDE_CHART)// 设置y-label显示在图表外
            setAxisMaxValue(100f)
            setAxisMinValue(0f)
            setDrawGridLines(true)
            gridColor = getColor(R.color.ededed)
            enableGridDashedLine(12f, 12f, 0f)
            gridLineWidth = 1f
            labelCount = 5
            granularity = 25f
        }

    }

取消X,Y轴的展示,增加虚线,增加圆角

3.2 圆角代码

BarChartRendererMPAndroidChart 库中用于绘制 BarChart(柱状图)的渲染器。它继承自 DataRenderer,负责绘制 柱形条(Bar)、高亮(Highlight)以及 X/Y 轴值

我们需要去重写他的drawDataSet


class RoundedBarChartRenderer(
    chart: BarDataProvider,
    animator: ChartAnimator,
    viewPortHandler: ViewPortHandler,
    private val cornerRadius: Float = 20f // 设置圆角半径
) : BarChartRenderer(chart, animator, viewPortHandler) {

    override fun drawDataSet(c: Canvas, dataSet: IBarDataSet, index: Int) {
        val trans = mChart.getTransformer(dataSet.axisDependency)
        mShadowPaint.color = dataSet.barShadowColor
        mBarBorderPaint.color = dataSet.barBorderColor
        mBarBorderPaint.strokeWidth = Utils.convertDpToPixel(dataSet.barBorderWidth)
        val drawBorder = dataSet.barBorderWidth > 0.0f
        val phaseX = mAnimator.phaseX
        val phaseY = mAnimator.phaseY
        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)
        var j: Int
        if (dataSet.colors.size > 1) {
            j = 0
            while (j < buffer.size()) {
                if (mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) {
                    if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) {
                        break
                    }

                    mRenderPaint.color = dataSet.getColor(j / 4)

                    val rectF = RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], buffer.buffer[j + 3])
                    if (j % 4 == 0) {
                        c.drawRect(
                            buffer.buffer[j],
                            (buffer.buffer[j + 1] + buffer.buffer[j + 3]) / 2,
                            buffer.buffer[j + 2],
                            buffer.buffer[j + 3],
                            mRenderPaint
                        )
                    } else {
                        c.drawRect(rectF, mRenderPaint)
                    }
//绘制圆角矩形
                    c.drawRoundRect(rectF, cornerRadius, cornerRadius, mRenderPaint)
                    if (drawBorder) {//绘制圆角矩形
                        c.drawRoundRect(rectF, cornerRadius, cornerRadius, mBarBorderPaint)
                    }
                }
                j += 4
            }
        } else {
            mRenderPaint.color = dataSet.color
            j = 0
            while (j < buffer.size()) {
                if (mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) {
                    if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) {
                        break
                    }
                    val rectF = RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], buffer.buffer[j + 3])
                    if (j % 4 == 0) {
                        c.drawRect(
                            buffer.buffer[j],
                            (buffer.buffer[j + 1] + buffer.buffer[j + 3]) / 2,
                            buffer.buffer[j + 2],
                            buffer.buffer[j + 3],
                            mRenderPaint
                        )
                    } else {
                        c.drawRect(rectF, mRenderPaint)
                    }
//绘制圆角矩形
                    c.drawRoundRect(rectF, cornerRadius, cornerRadius, mRenderPaint)
                    if (drawBorder) {
//绘制圆角矩形
                        c.drawRoundRect(rectF, cornerRadius, cornerRadius, mBarBorderPaint)
                    }
                }
                j += 4
            }
        }
    }
}

3.3 实现效果

 4.堆叠柱状图

4.1 代码实现

  fun initBarChart4() {
        val barChart = binding.barChart4
        val entries = ArrayList<BarEntry>()
        // 仅存储 (minY, maxY)
        entries.add(BarEntry(0f, floatArrayOf(5f, 10f))) // X=0, 从 0-5 到5-10
        entries.add(BarEntry(1f, floatArrayOf(3f, 8f))) // X=1, 从 0-3 到 3-8

        val barDataSet = BarDataSet(entries, "Data Set").apply {
            setDrawValues(false) // 关闭数值显示
            setColors(intArrayOf(R.color.purple_200, R.color.color_ff8561), this@ChartTestAc) // 第一部分透明,第二部分填充颜色
        }

        val barData = BarData(barDataSet).apply {
            barWidth = 0.3f // 设置柱宽
        }

        barChart.data = barData
        binding.barChart4.renderer = RoundedBarChartRendererAll(binding.barChart4, binding.barChart4.animator, binding.barChart4.viewPortHandler)
        barChart.invalidate() // 刷新图表
    }

 4.2 实现效果

5.分段/区间柱状图

5.1 需求

需求是需要展示当天的温度区间,用户的血压区间

灵感来自于堆叠柱状图的实现

我只需要把下层数据不展示就可以了。 

5.2 实现代码

 fun initBarChart3() {
        val barChart = binding.barChart3
        val entries = ArrayList<BarEntry>()
        // 仅存储 (minY, maxY)
        entries.add(BarEntry(0f, floatArrayOf(2f, 8f-2f))) // 减去第一区间的值
        entries.add(BarEntry(1f, floatArrayOf(3f, 11f-3f))) 
        val barDataSet = BarDataSet(entries, "Data Set").apply {
            setDrawValues(false) // 关闭数值显示
            setColors(intArrayOf(R.color.trans, R.color.color_ff8561), this@ChartTestAc) // 第一部分透明,第二部分填充颜色
        }

        val barData = BarData(barDataSet).apply {
            barWidth = 0.3f // 设置柱宽
        }

        barChart.data = barData
        binding.barChart3.renderer = RoundedBarChartRendererAll(binding.barChart3, binding.barChart3.animator, binding.barChart3.viewPortHandler)
        barChart.invalidate() // 刷新图表
    }

关键点:因为自带的堆叠柱状图会加你的第一个值,如果不减去第一个值,就会绘制2到(2+8)的区间,也就是2-10都会绘制,这肯定不符合需求设计,所以记得减去上一段的Y值。 

四个方向的圆角代码


class RoundedBarChartRendererAll(
    chart: BarDataProvider,
    animator: ChartAnimator,
    viewPortHandler: ViewPortHandler,
    private val cornerRadius: Float = 20f // 设置圆角半径
) : BarChartRenderer(chart, animator, viewPortHandler) {

    override fun drawDataSet(c: Canvas, dataSet: IBarDataSet, index: Int) {
        val trans = mChart.getTransformer(dataSet.axisDependency)
        mShadowPaint.color = dataSet.barShadowColor
        mBarBorderPaint.color = dataSet.barBorderColor
        mBarBorderPaint.strokeWidth = Utils.convertDpToPixel(dataSet.barBorderWidth)
        val drawBorder = dataSet.barBorderWidth > 0.0f
        val phaseX = mAnimator.phaseX
        val phaseY = mAnimator.phaseY
        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)
        var j: Int
        if (dataSet.colors.size > 1) {
            j = 0
            while (j < buffer.size()) {
                if (mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) {
                    if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) {
                        break
                    }

                    mRenderPaint.color = dataSet.getColor(j / 4)

                    val rectF = RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], buffer.buffer[j + 3])

                    // 绘制圆角矩形(四个角都要圆角)
                    c.drawRoundRect(rectF, cornerRadius, cornerRadius, mRenderPaint)

                    if (drawBorder) {
                        c.drawRoundRect(rectF, cornerRadius, cornerRadius, mBarBorderPaint)
                    }
                }
                j += 4
            }
        } else {
            mRenderPaint.color = dataSet.color
            j = 0
            while (j < buffer.size()) {
                if (mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) {
                    if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) {
                        break
                    }
                    val rectF = RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], buffer.buffer[j + 3])

                    // 绘制圆角矩形(四个角都要圆角)
                    c.drawRoundRect(rectF, cornerRadius, cornerRadius, mRenderPaint)

                    if (drawBorder) {
                        c.drawRoundRect(rectF, cornerRadius, cornerRadius, mBarBorderPaint)
                    }
                }
                j += 4
            }
        }
    }
}

5.3  实现效果

在JavaScript中,当你遇到`index.js:210 Uncaught (in promise) DOMException: The element has no supported sources.` 这样的错误,通常是在处理HTML5媒体元素(如`<video>`或`<audio>`)时发生的。DOMException是DOM(Document Object Model)API抛出的一种异常,表明浏览器遇到了与DOM元素操作相关的错误。 具体到这个错误,错误信息说“元素没有支持的源”,这意味着你在尝试播放视频或音频时,指定的`src`(source)属性的URL不是一个浏览器支持的格式,或者是网络问题导致的资源无法加载,或者是元素的`type`属性设置不正确,使得浏览器无法识别为可播放的媒体类型。 解决这个问题,你可以做以下几个步骤: 1. **检查`src`和`type`**:确保视频或音频文件的URL是正确的,并且`type`属性指定了正确的MIME类型,例如对于MP4视频,可能是`type="video/mp4"`。 2. **测试媒体格式**:确认浏览器支持那种媒体格式,你可以查看MDN文档(https://developer.mozilla.org/zh-CN/docs/Web/HTML/Supported_media_formats)来确认。 3. **跨域问题**:如果文件不在同一域下,检查是否设置了正确的CORS(Cross-Origin Resource Sharing)策略。 4. **网络状况**:确保你的用户有权限访问该资源,或者网络连接正常。 5. **代码审查**:检查`index.js:210`的具体上下文,看是否有其他可能影响媒体加载的代码逻辑。 如果你能提供更具体的上下文或代码片段,我可以给出更精确的帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

&岁月不待人&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值