MPAndroidchart 圆形边缘用于彩色 Horizo​​ntalBarChart

Posted

技术标签:

【中文标题】MPAndroidchart 圆形边缘用于彩色 Horizo​​ntalBarChart【英文标题】:MPAndroidchart rounded edges for colorized HorizontalBarChart 【发布时间】:2016-05-08 19:08:21 【问题描述】:

我想使用MPandroidChart 制作一个带有圆边的彩色Horizo​​ntalBarChart。我正在尝试为我的图表更改 Horizo​​ntalBarChartRenderer。这是我的代码:

HorizontalBarChart初始化:

List<Float> values = new ArrayList<>();
List<Integer> colors = new ArrayList<>();
// initializing

float[] valuesArray = new float[values.size()];
for (int i = 0; i < values.size(); i++) 
    valuesArray[i] = values.get(i);

List<BarEntry> yValues = valuesArray.length <= 0
    ? Collections.<BarEntry>emptyList()
    : Collections.singletonList(new BarEntry(valuesArray, 0));
BarDataSet barDataSet = new BarDataSet(yValues, "");
barDataSet.setColors(colors);
barDataSet.setValueFormatter(new ValueFormatter() 
    @Override
    public String getFormattedValue(float value, Entry entry, 
        int dataSetIndex, ViewPortHandler viewPortHandler) 
            return "";
        
);
chartView.setData(new BarData(new String[]"sleep", barDataSet));

并扩展HorizontalBarChartRenderer

@Override
protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) 
    // ...
    c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j + 2], buffer.buffer[j + 3] - 10), 20, 20, mRenderPaint);

结果是:

如何只为外侧制作圆边? 像这样:

【问题讨论】:

【参考方案1】:

我建议您在“正常”模式下绘制除最后一个之外的所有条形图,并首先绘制顶部(圆形)条形图,让它在所有条形图的整个长度上延伸。

这样,其他未圆角的条形将覆盖第一个绘制(圆形)条形底部不需要的圆角。

【讨论】:

【参考方案2】:

我找到了解决方案。我的想法是像往常一样绘制数据,并在清除一些区域以使边缘变圆之后。 我的代码:

public class CircleHorizontalBarChartRenderer extends HorizontalBarChartRenderer 

    public CircleHorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) 
        super(chart, animator, viewPortHandler);
    

    @Override
    protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) 
        Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());

        mShadowPaint.setColor(dataSet.getBarShadowColor());

        float phaseX = mAnimator.getPhaseX();
        float phaseY = mAnimator.getPhaseY();

        // initialize the buffer
        BarBuffer buffer = mBarBuffers[index];
        buffer.setPhases(phaseX, phaseY);
        buffer.setBarSpace(dataSet.getBarSpace());
        buffer.setDataSet(index);
        buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency()));

        buffer.feed(dataSet);

        trans.pointValuesToPixel(buffer.buffer);

        int length = buffer.buffer.length;
        float left = 0;
        float right = 0;
        float top = buffer.buffer[length - 3];
        float bot = buffer.buffer[length - 1];
        boolean leftSaved = false;

        for (int j = 0; j < buffer.size(); j += 4) 

            if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3]))
                break;

            if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1]))
                continue;

            // Set the color for the currently drawn value. 
            // If the index is
            // out of bounds, reuse colors.
            int color = dataSet.getColor(j / 4);
            mRenderPaint.setColor(color);
            if (color != 0 && !leftSaved) 
                leftSaved = true;
                left = buffer.buffer[j];
            
            if (j > 4)  // it works but its ugly
                right = buffer.buffer[j - 2];
            

            c.drawRect(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j + 2],
                    buffer.buffer[j + 3] - 10, mRenderPaint);
        

        Paint erasePaint = new Paint();
        erasePaint.setAntiAlias(true);
        erasePaint.setStyle(Paint.Style.STROKE);
        int paintWidth = 20;
        erasePaint.setStrokeWidth(paintWidth);
        erasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        c.drawRoundRect(new RectF(left - paintWidth / 2, top, right + paintWidth / 2, bot), 30, 30, erasePaint);
    


PorterDuff.Mode.CLEAR 仅在您将图层类型设置为软件时才有效。所以你需要打电话

setLayerType(View.LAYER_TYPE_SOFTWARE, null);

用于 chartView 或其父视图。如果您不需要透明背景,并且已知背景颜色,您可以为erasePaint 设置此颜色,并且不要使用会降低性能的软件层类型。在这种情况下也不需要erasePaint.setXfermode。

最终结果:

【讨论】:

【参考方案3】:

我编辑了 Nick Kober 的解决方案。这是代码;

public class CircleHorizontalBarChartRenderer extends HorizontalBarChartRenderer 

    public CircleHorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) 
        super(chart, animator, viewPortHandler);
    

    @Override
    protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) 
        Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());

        mShadowPaint.setColor(dataSet.getBarShadowColor());

        float phaseX = mAnimator.getPhaseX();
        float phaseY = mAnimator.getPhaseY();

        // initialize the buffer
        BarBuffer buffer = mBarBuffers[index];
        buffer.setPhases(phaseX, phaseY);
        buffer.setBarWidth(dataSet.getBarBorderWidth());
        buffer.setDataSet(index);
        buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency()));

        buffer.feed(dataSet);

        trans.pointValuesToPixel(buffer.buffer);

        int timeToChange = buffer.size() / 4;

        for (int j = 0; j < buffer.size(); j += 4) 
            if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3]))
                break;

            if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1]))
                continue;

            // Set the color for the currently drawn value.
            // If the index is
            // out of bounds, reuse colors.
            int color = dataSet.getColor(j / 4);
            mRenderPaint.setColor(color);

            if (j/4 == 0) 
                c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), 20, 20, mRenderPaint);
                c.drawRect(new RectF(buffer.buffer[j] + 10 , buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), mRenderPaint);
            
            else if (j/4 < timeToChange - 1) 
                c.drawRect(new RectF(buffer.buffer[j] , buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), mRenderPaint);
            
            else if (j/4 == timeToChange - 1) 
                c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j+2], buffer.buffer[j + 3] - 10), 20, 20, mRenderPaint);
                c.drawRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1] + 10, buffer.buffer[j+2] - 10, buffer.buffer[j + 3] - 10), mRenderPaint);
            
        
    

同样,您应该像 Nick 那样编写自己的自定义 Renderer 类。我添加了最后一部分(if-else if 块)。

我是这样想的,为第一条画圆角矩形。然后在上面通过将左侧移动到右侧来绘制一个矩形。通过移位,左侧保持圆形。对于内部的,将它们全部绘制为矩形。最后一个画圆角矩形。然后在其上方通过将右侧向左移动来绘制一个矩形。通过移位,右侧保持圆形。

我使用变量“timeToChange”来了解我当前正在绘制哪个条形图。

And the output is like this

【讨论】:

以上是关于MPAndroidchart 圆形边缘用于彩色 Horizo​​ntalBarChart的主要内容,如果未能解决你的问题,请参考以下文章

将圆形彩色背景应用于 Material PopupMenu 组件的正确方法是啥?

将图例中的 ggplot 彩色线条更改为正方形或圆形

具有多个彩色边框的圆形UIView像饼图一样工作

如何在 QT 中获得具有圆形边缘和圆形进度边缘的 QProgressBar?

用于背景的 Android 圆角边缘

彩色图像的边缘检测 CannyAlgorithm