MPAndroidChart 세로 막대형 차트 둥근 모서리 문제 솔루션

배경 및 문제점

프로젝트에서는 차트를 많이 사용하기 때문에 솔루션으로 MPAndroidChart를 사용합니다. 이 라이브러리는 매우 강력합니다.프로젝트의 요구 사항 대부분은 프로젝트에서 제공하는 방법을 사용하여 직접 해결할 수 있지만 이 기사의 주제와 같이 자체 사용자 정의가 필요한 일부 요구 사항도 있습니다. 히스토그램의 둥근 모서리 문제.

해결책

프로젝트 요구 사항을 달성하기 위해 소스 코드를 더 잘 수정하기 위해 모듈을 가져와서 프로젝트를 가져옵니다. 세부 정보는 나열되지 않습니다.

우리의 디자인 여성은 매우 얇습니다. 디자인된 세로 막대형 차트는 이와 같이 아래쪽이 직각이고 위쪽이 둥근 모서리입니다.

이미지.png

처음에는 둥근 모서리에 대한 이 솔루션을 찾았습니다. github.com/WW-Digital/…

여기의 코드는 네 모서리가 모두 둥글다는 요구 사항을 해결할 수 있습니다.핵심은 그리기 방법을 수정하고 캔버스의 그리기 방법을 drawRect에서 drawRoundRect로 변경하는 것입니다. 네 모서리를 모두 둥글게 처리해야 하는 경우 이 솔루션을 사용할 수 있으며 코드 수정량이 많지 않습니다.

나에게 문제는 둥근 모서리를 그릴 수 있는 방법이 없으므로 이 솔루션이 내 요구 사항을 충족하지 못한다는 것입니다. 계속해서 다른 솔루션을 시도하고 이 질문에서 부분적으로 둥근 모서리에 대한 솔루션을 찾았습니다: stackoverflow.com/questions/3… @duc tan 에서 제공한 코드 에 따르면 부분적으로 둥근 모서리에 대한 요구 사항이 성공적으로 해결되었습니다. 핵심은 Rect를 Path로 변환하고 drawPath를 통해 그립니다. 둥근 모서리는 다음 메소드 매개변수를 수정하여 임의로 사용자 정의할 수 있습니다.

roundRect(RectF rect, float rx, float ry, boolean tl, boolean tr, boolean br, boolean bl) 
复制代码

문제는 해결된듯 한데 하이라이트 하면 여전히 정사각형인데 이건 절대 불가능해서 다시 drawHighlighted메소드를 다시 작성하고 하이라이트 히스토그램도 부분적으로 반올림해서 이 문제를 최종적으로 해결한 코드는 다음과 같습니다. :

@Override
public void drawHighlighted(Canvas c, Highlight[] indices) {
    BarData barData = mChart.getBarData();

    for (Highlight high : indices) {

        IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex());

        if (set == null || !set.isHighlightEnabled()) {
            continue;
        }

        BarEntry e = set.getEntryForXValue(high.getX(), high.getY());

        if (!isInBoundsX(e, set)) {
            continue;
        }

        Transformer trans = mChart.getTransformer(set.getAxisDependency());

        mHighlightPaint.setColor(set.getHighLightColor());
        mHighlightPaint.setAlpha(set.getHighLightAlpha());

        boolean isStack = high.getStackIndex() >= 0 && e.isStacked();

        final float y1;
        final float y2;

        if (isStack) {

            if (mChart.isHighlightFullBarEnabled()) {

                y1 = e.getPositiveSum();
                y2 = -e.getNegativeSum();

            } else {

                Range range = e.getRanges()[high.getStackIndex()];

                y1 = range.from;
                y2 = range.to;
            }

        } else {
            y1 = e.getY();
            y2 = 0.f;
        }

        prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans);

        setHighlightDrawPos(high, mBarRect);

        Path path2 = roundRect(new RectF(mBarRect.left, mBarRect.top, mBarRect.right,
                mBarRect.bottom), mRadius, mRadius, true, true, false, false);

        c.drawPath(path2, mHighlightPaint);
    }
}
复制代码

마지막으로 최종 솔루션 렌더러 클래스 코드와 사용법을 게시합니다.

public class CustomBarChartRender extends BarChartRenderer {
    private final RectF mBarShadowRectBuffer = new RectF();

    private int mRadius;

    public CustomBarChartRender(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
        super(chart, animator, viewPortHandler);
    }

    public void setRadius(int mRadius) {
        this.mRadius = mRadius;
    }

    @Override
    protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {

        Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
        mBarBorderPaint.setColor(dataSet.getBarBorderColor());
        mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth()));
        mShadowPaint.setColor(dataSet.getBarShadowColor());
        boolean drawBorder = dataSet.getBarBorderWidth() > 0f;

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

        if (mChart.isDrawBarShadowEnabled()) {
            mShadowPaint.setColor(dataSet.getBarShadowColor());

            BarData barData = mChart.getBarData();

            float barWidth = barData.getBarWidth();
            float barWidthHalf = barWidth / 2.0f;
            float x;

            int i = 0;
            double count = Math.min(Math.ceil((int) (double) ((float) dataSet.getEntryCount() * phaseX)), dataSet.getEntryCount());
            while (i < count) {

                BarEntry e = dataSet.getEntryForIndex(i);

                x = e.getX();

                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.drawRoundRect(mBarRect, mRadius, mRadius, mShadowPaint);
                i++;
            }
        }

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

        buffer.feed(dataSet);

        trans.pointValuesToPixel(buffer.buffer);

        boolean isSingleColor = dataSet.getColors().size() == 1;

        if (isSingleColor) {
            mRenderPaint.setColor(dataSet.getColor());
        }

        int 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.setColor(dataSet.getColor(j / 4));
            }

            Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
                    buffer.buffer[j + 3]), mRadius, mRadius, true, true, false, false);
            c.drawPath(path2, mRenderPaint);

            if (drawBorder) {
                Path path = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
                        buffer.buffer[j + 3]), mRadius, mRadius, true, true, false, false);
                c.drawPath(path, mBarBorderPaint);
            }
            j += 4;
        }

    }

    @Override
    public void drawHighlighted(Canvas c, Highlight[] indices) {
        BarData barData = mChart.getBarData();

        for (Highlight high : indices) {

            IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex());

            if (set == null || !set.isHighlightEnabled()) {
                continue;
            }

            BarEntry e = set.getEntryForXValue(high.getX(), high.getY());

            if (!isInBoundsX(e, set)) {
                continue;
            }

            Transformer trans = mChart.getTransformer(set.getAxisDependency());

            mHighlightPaint.setColor(set.getHighLightColor());
            mHighlightPaint.setAlpha(set.getHighLightAlpha());

            boolean isStack = high.getStackIndex() >= 0 && e.isStacked();

            final float y1;
            final float y2;

            if (isStack) {

                if (mChart.isHighlightFullBarEnabled()) {

                    y1 = e.getPositiveSum();
                    y2 = -e.getNegativeSum();

                } else {

                    Range range = e.getRanges()[high.getStackIndex()];

                    y1 = range.from;
                    y2 = range.to;
                }

            } else {
                y1 = e.getY();
                y2 = 0.f;
            }

            prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans);

            setHighlightDrawPos(high, mBarRect);

            Path path2 = roundRect(new RectF(mBarRect.left, mBarRect.top, mBarRect.right,
                    mBarRect.bottom), mRadius, mRadius, true, true, false, false);

            c.drawPath(path2, mHighlightPaint);
        }
    }

    private Path roundRect(RectF rect, float rx, float ry, boolean tl, boolean tr, boolean br, boolean bl) {
        float top = rect.top;
        float left = rect.left;
        float right = rect.right;
        float bottom = rect.bottom;
        Path path = new Path();
        if (rx < 0) {
            rx = 0;
        }
        if (ry < 0) {
            ry = 0;
        }
        float width = right - left;
        float height = bottom - top;
        if (rx > width / 2) {
            rx = width / 2;
        }
        if (ry > height / 2) {
            ry = height / 2;
        }
        float widthMinusCorners = (width - (2 * rx));
        float heightMinusCorners = (height - (2 * ry));

        path.moveTo(right, top + ry);
        if (tr) {
            //top-right corner
            path.rQuadTo(0, -ry, -rx, -ry);
        } else {
            path.rLineTo(0, -ry);
            path.rLineTo(-rx, 0);
        }
        path.rLineTo(-widthMinusCorners, 0);
        if (tl) {
            //top-left corner
            path.rQuadTo(-rx, 0, -rx, ry);
        } else {
            path.rLineTo(-rx, 0);
            path.rLineTo(0, ry);
        }
        path.rLineTo(0, heightMinusCorners);

        if (bl) {
            //bottom-left corner
            path.rQuadTo(0, ry, rx, ry);
        } else {
            path.rLineTo(0, ry);
            path.rLineTo(rx, 0);
        }

        path.rLineTo(widthMinusCorners, 0);
        if (br) {
            //bottom-right corner
            path.rQuadTo(rx, 0, rx, -ry);
        } else {
            path.rLineTo(rx, 0);
            path.rLineTo(0, -ry);
        }

        path.rLineTo(0, -heightMinusCorners);

        path.close();//Given close, last lineto can be removed.

        return path;
    }
}
复制代码

사용법(코틀린):

val barChartRender = CustomBarChartRender(chart,chart.animator,chart.viewPortHandler)
barChartRender.setRadius(20)
barChart.renderer = barChartRender
复制代码

달성된 효과의 실제 기계 스크린샷은 다음과 같습니다.

이미지.png

추천

출처juejin.im/post/7100863388850503693