【问题标题】:MPAndroidChart: add custom image inside barsMPAndroidChart:在条形图内添加自定义图像
【发布时间】:2016-12-27 07:23:18
【问题描述】:

我正在使用MPAndroidChart,我想在这个CombinedChart 中显示一个自定义可绘制对象,如下图所示:

如果条形值 >= 目标值,例如 50,那么我想在条形内添加星形图像。

谁能帮我定制条形图?

【问题讨论】:

    标签: android graph mpandroidchart


    【解决方案1】:

    要在条形图中获取星形图像,我们需要创建一个自定义渲染器。因为我们的条形图使用BarChartRenderer,我们将首先对其进行子类化并为我们的图像添加一个参数:

    public class ImageBarChartRenderer extends BarChartRenderer {
    
        private final Bitmap barImage;
    
        public ImageBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler, Bitmap barImage) {
            super(chart, animator, viewPortHandler);
            this.barImage = barImage;
        }
    

    如果我们检查BarChartRenderer 的源代码,我们可以看到它调用了名为drawData 的方法,然后遍历每个DataSet 并调用drawDataSetdrawDataSet 是动作发生的地方:它正在绘制阴影和条形。这是一个合适的地方添加逻辑来绘制额外的类似图像,所以让我们添加一个对方法的调用来在那里绘制我们的图像:

        @Override
        protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
            super.drawDataSet(c, dataSet, index);
            drawBarImages(c, dataSet, index);
        }
    

    我们现在需要一个方法来遍历 DataSet 并绘制星形图像。将用作模板的适当方法是drawValues,所以让我们复制并更改它,以便绘制图像而不是文本。理解这一点的关键是了解 BarBuffer 是如何工作的。 BarBuffer 在jj + 1j + 2j + 3 处保存给定条目的条形图的屏幕(像素)坐标。

    为了澄清,j 是左侧 x 坐标,j + 1 是顶部 y 坐标,依此类推,直到右侧 x 坐标 j + 3。我们会将这些提取到变量中以便于理解:

        protected void drawBarImages(Canvas c, IBarDataSet dataSet, int index) {
            BarBuffer buffer = mBarBuffers[index];
    
            float left; //avoid allocation inside loop
            float right;
            float top;
            float bottom;
    
            for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) {
                left = buffer.buffer[j];
                right = buffer.buffer[j + 2];
                top = buffer.buffer[j + 1];
                bottom = buffer.buffer[j + 3];
    
                float x = (left + right) / 2f;
    
                if (!mViewPortHandler.isInBoundsRight(x))
                    break;
    
                if (!mViewPortHandler.isInBoundsY(top)
                        || !mViewPortHandler.isInBoundsLeft(x))
                    continue;
    
                BarEntry entry = dataSet.getEntryForIndex(j / 4);
                float val = entry.getY();
    
                if (val > 50) {
                    drawStar(c, barImage, x, top);
                }
            }
        }
    

    渲染器的使用方法如下:

        Bitmap starBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.star);
        mChart.setRenderer(new ImageBarChartRenderer(mChart, mChart.getAnimator(), mChart.getViewPortHandler(), starBitmap));
    

    渲染器的最后一步是添加逻辑来缩放位图并正确定位它。这是自定义渲染器的最终概念验证:

    package com.xxmassdeveloper.mpchartexample;
    
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    
    import com.github.mikephil.charting.animation.ChartAnimator;
    import com.github.mikephil.charting.buffer.BarBuffer;
    import com.github.mikephil.charting.data.BarEntry;
    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.ViewPortHandler;
    
    /**
     * Created by David on 29/12/2016.
     */
    
    public class ImageBarChartRenderer extends BarChartRenderer {
    
        private final Bitmap barImage;
    
        public ImageBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler, Bitmap barImage) {
            super(chart, animator, viewPortHandler);
            this.barImage = barImage;
        }
    
        @Override
        public void drawData(Canvas c) {
            super.drawData(c);
        }
    
        @Override
        protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
            super.drawDataSet(c, dataSet, index);
            drawBarImages(c, dataSet, index);
        }
    
        protected void drawBarImages(Canvas c, IBarDataSet dataSet, int index) {
            BarBuffer buffer = mBarBuffers[index];
    
            float left; //avoid allocation inside loop
            float right;
            float top;
            float bottom;
    
            final Bitmap scaledBarImage = scaleBarImage(buffer);
    
            int starWidth = scaledBarImage.getWidth();
            int starOffset = starWidth / 2;
    
            for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) {
                left = buffer.buffer[j];
                right = buffer.buffer[j + 2];
                top = buffer.buffer[j + 1];
                bottom = buffer.buffer[j + 3];
    
                float x = (left + right) / 2f;
    
                if (!mViewPortHandler.isInBoundsRight(x))
                    break;
    
                if (!mViewPortHandler.isInBoundsY(top)
                        || !mViewPortHandler.isInBoundsLeft(x))
                    continue;
    
                BarEntry entry = dataSet.getEntryForIndex(j / 4);
                float val = entry.getY();
    
                if (val > 50) {
                    drawImage(c, scaledBarImage, x - starOffset, top);
                }
            }
        }
    
        private Bitmap scaleBarImage(BarBuffer buffer) {
            float firstLeft = buffer.buffer[0];
            float firstRight = buffer.buffer[2];
            int firstWidth = (int) Math.ceil(firstRight - firstLeft);
            return Bitmap.createScaledBitmap(barImage, firstWidth, firstWidth, false);
        }
    
        protected void drawImage(Canvas c, Bitmap image, float x, float y) {
            if (image != null) {
                c.drawBitmap(image, x, y, null);
            }
        }
    }
    

    这是一个屏幕截图 - 您可以看到超过 50 的值带有星号:

    【讨论】:

    • 感谢您的回答。
    • 你这里用的是什么版本的mpandroid图表?
    • @Amalo 版本 3.0.1
    • 这个问题了解更多关于编写自定义渲染器的信息:stackoverflow.com/q/43443787/5241933
    • 如何对折线图进行此操作并在最后一项上仅显示图像?
    【解决方案2】:
        for (i in 0 until values.size) {
            if (values[i].y.toInt() >= 8000) {
                values[i].icon = ContextCompat.getDrawable(this, R.drawable.oval_check)
            }
        }
    

    【讨论】:

    • "values" 类型是 ArrayList()
    • 虽然此代码可能会解决问题,但 including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提出问题的人。请edit您的答案添加解释并说明适用的限制和假设。
    【解决方案3】:

    您可以只使用 BarDataSet 上的 iconsOffset 属性,而不是使用自定义渲染器

    barDataSet.iconsOffset = MPPointF.getInstance(CHART_ICON_X_OFFSET, CHART_ICON_Y_OFFSET)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-15
      • 1970-01-01
      • 2013-12-18
      • 2018-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多