From 1d2853d136a4dacbbeee7ef5647f6cd56d5831be Mon Sep 17 00:00:00 2001 From: Mohini Katara Date: Wed, 4 Oct 2023 17:50:32 +0530 Subject: [PATCH 1/4] Added RoundedBarChartRenderer & RoundedHorizontalBarChartRenderer for corner radius positive/negative Bar Chart --- .../renderer/RoundedBarChartRenderer.java | 324 ++++++++++++++++++ .../RoundedHorizontalBarChartRenderer.java | 242 +++++++++++++ 2 files changed, 566 insertions(+) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedBarChartRenderer.java create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedHorizontalBarChartRenderer.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedBarChartRenderer.java new file mode 100644 index 0000000000..86d3ce4799 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedBarChartRenderer.java @@ -0,0 +1,324 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.Shader; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.buffer.BarBuffer; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.highlight.Range; +import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +public class RoundedBarChartRenderer extends BarChartRenderer { + + public RoundedBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(chart, animator, viewPortHandler); + } + private final RectF mBarShadowRectBuffer = new RectF(); + private float mRadius = 20f; + private float roundedShadowRadius = 0f; + private float roundedPositiveDataSetRadius = 0f; + private float roundedNegativeDataSetRadius = 0f; + public void setRoundedNegativeDataSetRadius(float roundedNegativeDataSet) { + roundedNegativeDataSetRadius = roundedNegativeDataSet; + } + public void setRoundedShadowRadius(float roundedShadow) { + roundedShadowRadius = roundedShadow; + } + public void setRoundedPositiveDataSetRadius(float roundedPositiveDataSet) { + roundedPositiveDataSetRadius = roundedPositiveDataSet; + } + + @Override + protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { + initBuffers(); + 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(); + + + if (roundedShadowRadius >0) { + c.drawRoundRect(mBarRect, roundedShadowRadius, roundedShadowRadius, mShadowPaint); + } else { + c.drawRect(mBarShadowRectBuffer, mShadowPaint); + } + i++; + } + } + + 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); + + // if multiple colors has been assigned to Bar Chart + if (dataSet.getColors().size() > 1) { + + for (int j = 0; j < buffer.size(); j += 4) { + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) + continue; + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) + break; + + if (mChart.isDrawBarShadowEnabled()) { + if (roundedShadowRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(), + buffer.buffer[j + 2], + mViewPortHandler.contentBottom()), roundedShadowRadius, roundedShadowRadius, mShadowPaint); + else + c.drawRect(buffer.buffer[j], mViewPortHandler.contentTop(), + buffer.buffer[j + 2], + mViewPortHandler.contentBottom(), mShadowPaint); + } + + // Set the color for the currently drawn value. If the index + mRenderPaint.setColor(dataSet.getColor(j / 4)); + + if (roundedPositiveDataSetRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, mRenderPaint); + else + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + } else { + + mRenderPaint.setColor(dataSet.getColor()); + + for (int j = 0; j < buffer.size(); j += 4) { + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) + continue; + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) + break; + + if (mChart.isDrawBarShadowEnabled()) { + if (roundedShadowRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(), + buffer.buffer[j + 2], + mViewPortHandler.contentBottom()), roundedShadowRadius, roundedShadowRadius, mShadowPaint); + else + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + + if (roundedPositiveDataSetRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, mRenderPaint); + else + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + } + + + boolean isSingleColor = dataSet.getColors().size() == 1; + if (isSingleColor) { + mRenderPaint.setColor(dataSet.getColor(index)); + } + + 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) { + mRenderPaint.setColor(dataSet.getColor(j/4)); + } + + mRenderPaint.setShader(new LinearGradient( + buffer.buffer[j], + buffer.buffer[j + 3], + buffer.buffer[j], + buffer.buffer[j + 1], + dataSet.getColor(j/4), + dataSet.getColor(j/4), + Shader.TileMode.MIRROR)); + + mRenderPaint.setShader(new LinearGradient( + buffer.buffer[j], + buffer.buffer[j + 3], + buffer.buffer[j], + buffer.buffer[j + 1], + dataSet.getColor(j/4), + dataSet.getColor(j/4), + Shader.TileMode.MIRROR)); + + + if((dataSet.getEntryForIndex(j/4).getY()<0 && roundedNegativeDataSetRadius >0)) { + Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedNegativeDataSetRadius, roundedNegativeDataSetRadius, true, true, true, true); + c.drawPath(path2, mRenderPaint); + } else if((dataSet.getEntryForIndex(j/4).getY()>0 && roundedPositiveDataSetRadius >0)){ + Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, true, true, true, true); + c.drawPath(path2, mRenderPaint); + } + else { + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + + 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, true, true); + + 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) + path.rQuadTo(0, -ry, -rx, -ry);//top-right corner + else { + path.rLineTo(0, -ry); + path.rLineTo(-rx, 0); + } + path.rLineTo(-widthMinusCorners, 0); + if (tl) + path.rQuadTo(-rx, 0, -rx, ry); //top-left corner + else { + path.rLineTo(-rx, 0); + path.rLineTo(0, ry); + } + path.rLineTo(0, heightMinusCorners); + + if (bl) + path.rQuadTo(0, ry, rx, ry);//bottom-left corner + else { + path.rLineTo(0, ry); + path.rLineTo(rx, 0); + } + + path.rLineTo(widthMinusCorners, 0); + if (br) + path.rQuadTo(rx, 0, rx, -ry); //bottom-right corner + else { + path.rLineTo(rx, 0); + path.rLineTo(0, -ry); + } + + path.rLineTo(0, -heightMinusCorners); + path.close(); + return path; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedHorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedHorizontalBarChartRenderer.java new file mode 100644 index 0000000000..9d14a321ba --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedHorizontalBarChartRenderer.java @@ -0,0 +1,242 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.buffer.BarBuffer; +import com.github.mikephil.charting.data.BarData; +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.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +public class RoundedHorizontalBarChartRenderer extends HorizontalBarChartRenderer { + + private final RectF mBarShadowRectBuffer = new RectF(); + private float roundedShadowRadius = 0f; + private float roundedPositiveDataSetRadius = 0f; + private float roundedNegativeDataSetRadius = 0f; + public void setRoundedNegativeDataSetRadius(float roundedNegativeDataSet) { + roundedNegativeDataSetRadius = roundedNegativeDataSet; + } + public void setRoundedShadowRadius(float roundedShadow) { + roundedShadowRadius = roundedShadow; + } + public void setRoundedPositiveDataSetRadius(float roundedPositiveDataSet) { + roundedPositiveDataSetRadius = roundedPositiveDataSet; + } + public RoundedHorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(chart, animator, viewPortHandler); + + mValuePaint.setTextAlign(Paint.Align.LEFT); + } + + @Override + protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { + initBuffers(); + 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.top = x - barWidthHalf; + mBarShadowRectBuffer.bottom = x + barWidthHalf; + trans.rectValueToPixel(mBarShadowRectBuffer); + if (!mViewPortHandler.isInBoundsTop(mBarShadowRectBuffer.bottom)) { + i++; + continue; + } + if (!mViewPortHandler.isInBoundsBottom(mBarShadowRectBuffer.top)) + break; + mBarShadowRectBuffer.left = mViewPortHandler.contentLeft(); + mBarShadowRectBuffer.right = mViewPortHandler.contentRight(); + + + if (roundedShadowRadius >0) { + c.drawRoundRect(mBarRect, roundedShadowRadius, roundedShadowRadius, mShadowPaint); + } else { + c.drawRect(mBarShadowRectBuffer, mShadowPaint); + } + i++; + } + } + + 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); + + // if multiple colors has been assigned to Bar Chart + if (dataSet.getColors().size() > 1) { + + for (int j = 0; j < buffer.size(); j += 4) { + + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) + continue; + + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j+1])) + break; + + if (mChart.isDrawBarShadowEnabled()) { + if (roundedShadowRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(), + buffer.buffer[j + 2], + mViewPortHandler.contentBottom()), roundedShadowRadius, roundedShadowRadius, mShadowPaint); + else + c.drawRect(buffer.buffer[j], mViewPortHandler.contentTop(), + buffer.buffer[j + 2], + mViewPortHandler.contentBottom(), mShadowPaint); + } + + // Set the color for the currently drawn value. If the index + mRenderPaint.setColor(dataSet.getColor(j / 4)); + + if (roundedPositiveDataSetRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, mRenderPaint); + else + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + } else { + + mRenderPaint.setColor(dataSet.getColor()); + + for (int j = 0; j < buffer.size(); j += 4) { + + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) + continue; + + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j+1])) + break; + + if (mChart.isDrawBarShadowEnabled()) { + if (roundedShadowRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(), + buffer.buffer[j + 2], + mViewPortHandler.contentBottom()), roundedShadowRadius, roundedShadowRadius, mShadowPaint); + else + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + + if (roundedPositiveDataSetRadius >0) + c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, mRenderPaint); + else + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + } + + + boolean isSingleColor = dataSet.getColors().size() == 1; + if (isSingleColor) { + mRenderPaint.setColor(dataSet.getColor(index)); + } + + int j = 0; + while (j < buffer.size()) { + + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) { + j += 4; + continue; + } + + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j+1])) + break; + + if (!isSingleColor) { + mRenderPaint.setColor(dataSet.getColor(j/4)); + } + + if((dataSet.getEntryForIndex(j/4).getY()<0 && roundedNegativeDataSetRadius >0)) { + Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedNegativeDataSetRadius, roundedNegativeDataSetRadius, true, true, true, true); + c.drawPath(path2, mRenderPaint); + } else if((dataSet.getEntryForIndex(j/4).getY()>0 && roundedPositiveDataSetRadius >0)){ + Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, true, true, true, true); + c.drawPath(path2, mRenderPaint); + } + else { + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + j += 4; + } + } + + 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) + path.rQuadTo(0, -ry, -rx, -ry);//top-right corner + else { + path.rLineTo(0, -ry); + path.rLineTo(-rx, 0); + } + path.rLineTo(-widthMinusCorners, 0); + if (tl) + path.rQuadTo(-rx, 0, -rx, ry); //top-left corner + else { + path.rLineTo(-rx, 0); + path.rLineTo(0, ry); + } + path.rLineTo(0, heightMinusCorners); + + if (bl) + path.rQuadTo(0, ry, rx, ry);//bottom-left corner + else { + path.rLineTo(0, ry); + path.rLineTo(rx, 0); + } + + path.rLineTo(widthMinusCorners, 0); + if (br) + path.rQuadTo(rx, 0, rx, -ry); //bottom-right corner + else { + path.rLineTo(rx, 0); + path.rLineTo(0, -ry); + } + + path.rLineTo(0, -heightMinusCorners); + path.close(); + return path; + } +} From 621d924e1b49337e15c6e20138d22d8072297f4e Mon Sep 17 00:00:00 2001 From: Mohini Katara Date: Wed, 11 Oct 2023 23:27:11 +0530 Subject: [PATCH 2/4] Added Rounded corner for Pie chart --- .../mpchartexample/PieChartActivity.java | 5 + .../charting/renderer/PieChartRenderer.java | 35 ++ .../renderer/RoundedBarChartRenderer.java | 324 ------------------ .../RoundedHorizontalBarChartRenderer.java | 242 ------------- 4 files changed, 40 insertions(+), 566 deletions(-) delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedBarChartRenderer.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedHorizontalBarChartRenderer.java diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index fbc1a35ae0..03371d3d57 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -32,6 +32,7 @@ import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.renderer.PieChartRenderer; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.MPPointF; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -170,6 +171,10 @@ private void setData(int count, float range) { // undo all highlights chart.highlightValues(null); + PieChartRenderer renderer =(PieChartRenderer) chart.getRenderer(); + renderer.setRoundedCornerRadius(10f); + dataSet.setSliceSpace(renderer.getRoundedCornerRadius()/2); + chart.invalidate(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java index b557b333dd..ef62590736 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -45,6 +45,11 @@ public class PieChartRenderer extends DataRenderer { protected Paint mTransparentCirclePaint; protected Paint mValueLinePaint; + /** + * paint object used for drawing the rounded corner slice + */ + protected Paint mRoundedCornerPaint; + /** * paint object for the text that can be displayed in the center of the * chart @@ -68,6 +73,23 @@ public class PieChartRenderer extends DataRenderer { protected Canvas mBitmapCanvas; + /** + * Setter for the rounded corner slice paint object + */ + public void setRoundedCornerRadius(float radius){ + mRoundedCornerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mRoundedCornerPaint.setStrokeWidth(radius); + mRoundedCornerPaint.setStyle(Style.STROKE); + mRoundedCornerPaint.setAntiAlias(true); + } + + /** + * Getter for the rounded corner slice paint object + */ + public float getRoundedCornerRadius(){ + return mRoundedCornerPaint.getStrokeWidth(); + } + public PieChartRenderer(PieChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { super(animator, viewPortHandler); @@ -245,6 +267,11 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { final float sliceSpace = visibleAngleCount <= 1 ? 0.f : getSliceSpace(dataSet); + if (getRoundedCornerRadius()>0) { + mRoundedCornerPaint.setStrokeCap(Paint.Cap.ROUND); + mRoundedCornerPaint.setStrokeJoin(Paint.Join.ROUND); + } + for (int j = 0; j < entryCount; j++) { float sliceAngle = drawAngles[j]; @@ -268,6 +295,9 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { mRenderPaint.setColor(dataSet.getColor(j)); + // Set current data set color to paint object + mRoundedCornerPaint.setColor(dataSet.getColor(j)); + final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? 0.f : sliceSpace / (Utils.FDEG2RAD * radius); @@ -398,6 +428,11 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint); + // Draw rounded corner path with paint object for the given radius + if (getRoundedCornerRadius()>0) { + mBitmapCanvas.drawPath(mPathBuffer, mRoundedCornerPaint); + } + angle += sliceAngle * phaseX; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedBarChartRenderer.java deleted file mode 100644 index 86d3ce4799..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedBarChartRenderer.java +++ /dev/null @@ -1,324 +0,0 @@ -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.LinearGradient; -import android.graphics.Path; -import android.graphics.RectF; -import android.graphics.Shader; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.buffer.BarBuffer; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.highlight.Range; -import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -public class RoundedBarChartRenderer extends BarChartRenderer { - - public RoundedBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { - super(chart, animator, viewPortHandler); - } - private final RectF mBarShadowRectBuffer = new RectF(); - private float mRadius = 20f; - private float roundedShadowRadius = 0f; - private float roundedPositiveDataSetRadius = 0f; - private float roundedNegativeDataSetRadius = 0f; - public void setRoundedNegativeDataSetRadius(float roundedNegativeDataSet) { - roundedNegativeDataSetRadius = roundedNegativeDataSet; - } - public void setRoundedShadowRadius(float roundedShadow) { - roundedShadowRadius = roundedShadow; - } - public void setRoundedPositiveDataSetRadius(float roundedPositiveDataSet) { - roundedPositiveDataSetRadius = roundedPositiveDataSet; - } - - @Override - protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { - initBuffers(); - 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(); - - - if (roundedShadowRadius >0) { - c.drawRoundRect(mBarRect, roundedShadowRadius, roundedShadowRadius, mShadowPaint); - } else { - c.drawRect(mBarShadowRectBuffer, mShadowPaint); - } - i++; - } - } - - 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); - - // if multiple colors has been assigned to Bar Chart - if (dataSet.getColors().size() > 1) { - - for (int j = 0; j < buffer.size(); j += 4) { - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) - continue; - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) - break; - - if (mChart.isDrawBarShadowEnabled()) { - if (roundedShadowRadius >0) - c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(), - buffer.buffer[j + 2], - mViewPortHandler.contentBottom()), roundedShadowRadius, roundedShadowRadius, mShadowPaint); - else - c.drawRect(buffer.buffer[j], mViewPortHandler.contentTop(), - buffer.buffer[j + 2], - mViewPortHandler.contentBottom(), mShadowPaint); - } - - // Set the color for the currently drawn value. If the index - mRenderPaint.setColor(dataSet.getColor(j / 4)); - - if (roundedPositiveDataSetRadius >0) - c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, mRenderPaint); - else - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - } - } else { - - mRenderPaint.setColor(dataSet.getColor()); - - for (int j = 0; j < buffer.size(); j += 4) { - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) - continue; - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) - break; - - if (mChart.isDrawBarShadowEnabled()) { - if (roundedShadowRadius >0) - c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(), - buffer.buffer[j + 2], - mViewPortHandler.contentBottom()), roundedShadowRadius, roundedShadowRadius, mShadowPaint); - else - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - } - - if (roundedPositiveDataSetRadius >0) - c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, mRenderPaint); - else - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - } - } - - - boolean isSingleColor = dataSet.getColors().size() == 1; - if (isSingleColor) { - mRenderPaint.setColor(dataSet.getColor(index)); - } - - 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) { - mRenderPaint.setColor(dataSet.getColor(j/4)); - } - - mRenderPaint.setShader(new LinearGradient( - buffer.buffer[j], - buffer.buffer[j + 3], - buffer.buffer[j], - buffer.buffer[j + 1], - dataSet.getColor(j/4), - dataSet.getColor(j/4), - Shader.TileMode.MIRROR)); - - mRenderPaint.setShader(new LinearGradient( - buffer.buffer[j], - buffer.buffer[j + 3], - buffer.buffer[j], - buffer.buffer[j + 1], - dataSet.getColor(j/4), - dataSet.getColor(j/4), - Shader.TileMode.MIRROR)); - - - if((dataSet.getEntryForIndex(j/4).getY()<0 && roundedNegativeDataSetRadius >0)) { - Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3]), roundedNegativeDataSetRadius, roundedNegativeDataSetRadius, true, true, true, true); - c.drawPath(path2, mRenderPaint); - } else if((dataSet.getEntryForIndex(j/4).getY()>0 && roundedPositiveDataSetRadius >0)){ - Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, true, true, true, true); - c.drawPath(path2, mRenderPaint); - } - else { - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - } - - 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, true, true); - - 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) - path.rQuadTo(0, -ry, -rx, -ry);//top-right corner - else { - path.rLineTo(0, -ry); - path.rLineTo(-rx, 0); - } - path.rLineTo(-widthMinusCorners, 0); - if (tl) - path.rQuadTo(-rx, 0, -rx, ry); //top-left corner - else { - path.rLineTo(-rx, 0); - path.rLineTo(0, ry); - } - path.rLineTo(0, heightMinusCorners); - - if (bl) - path.rQuadTo(0, ry, rx, ry);//bottom-left corner - else { - path.rLineTo(0, ry); - path.rLineTo(rx, 0); - } - - path.rLineTo(widthMinusCorners, 0); - if (br) - path.rQuadTo(rx, 0, rx, -ry); //bottom-right corner - else { - path.rLineTo(rx, 0); - path.rLineTo(0, -ry); - } - - path.rLineTo(0, -heightMinusCorners); - path.close(); - return path; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedHorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedHorizontalBarChartRenderer.java deleted file mode 100644 index 9d14a321ba..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedHorizontalBarChartRenderer.java +++ /dev/null @@ -1,242 +0,0 @@ -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.RectF; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.buffer.BarBuffer; -import com.github.mikephil.charting.data.BarData; -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.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -public class RoundedHorizontalBarChartRenderer extends HorizontalBarChartRenderer { - - private final RectF mBarShadowRectBuffer = new RectF(); - private float roundedShadowRadius = 0f; - private float roundedPositiveDataSetRadius = 0f; - private float roundedNegativeDataSetRadius = 0f; - public void setRoundedNegativeDataSetRadius(float roundedNegativeDataSet) { - roundedNegativeDataSetRadius = roundedNegativeDataSet; - } - public void setRoundedShadowRadius(float roundedShadow) { - roundedShadowRadius = roundedShadow; - } - public void setRoundedPositiveDataSetRadius(float roundedPositiveDataSet) { - roundedPositiveDataSetRadius = roundedPositiveDataSet; - } - public RoundedHorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { - super(chart, animator, viewPortHandler); - - mValuePaint.setTextAlign(Paint.Align.LEFT); - } - - @Override - protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { - initBuffers(); - 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.top = x - barWidthHalf; - mBarShadowRectBuffer.bottom = x + barWidthHalf; - trans.rectValueToPixel(mBarShadowRectBuffer); - if (!mViewPortHandler.isInBoundsTop(mBarShadowRectBuffer.bottom)) { - i++; - continue; - } - if (!mViewPortHandler.isInBoundsBottom(mBarShadowRectBuffer.top)) - break; - mBarShadowRectBuffer.left = mViewPortHandler.contentLeft(); - mBarShadowRectBuffer.right = mViewPortHandler.contentRight(); - - - if (roundedShadowRadius >0) { - c.drawRoundRect(mBarRect, roundedShadowRadius, roundedShadowRadius, mShadowPaint); - } else { - c.drawRect(mBarShadowRectBuffer, mShadowPaint); - } - i++; - } - } - - 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); - - // if multiple colors has been assigned to Bar Chart - if (dataSet.getColors().size() > 1) { - - for (int j = 0; j < buffer.size(); j += 4) { - - if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) - continue; - - if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j+1])) - break; - - if (mChart.isDrawBarShadowEnabled()) { - if (roundedShadowRadius >0) - c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(), - buffer.buffer[j + 2], - mViewPortHandler.contentBottom()), roundedShadowRadius, roundedShadowRadius, mShadowPaint); - else - c.drawRect(buffer.buffer[j], mViewPortHandler.contentTop(), - buffer.buffer[j + 2], - mViewPortHandler.contentBottom(), mShadowPaint); - } - - // Set the color for the currently drawn value. If the index - mRenderPaint.setColor(dataSet.getColor(j / 4)); - - if (roundedPositiveDataSetRadius >0) - c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, mRenderPaint); - else - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - } - } else { - - mRenderPaint.setColor(dataSet.getColor()); - - for (int j = 0; j < buffer.size(); j += 4) { - - if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) - continue; - - if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j+1])) - break; - - if (mChart.isDrawBarShadowEnabled()) { - if (roundedShadowRadius >0) - c.drawRoundRect(new RectF(buffer.buffer[j], mViewPortHandler.contentTop(), - buffer.buffer[j + 2], - mViewPortHandler.contentBottom()), roundedShadowRadius, roundedShadowRadius, mShadowPaint); - else - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - } - - if (roundedPositiveDataSetRadius >0) - c.drawRoundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, mRenderPaint); - else - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - } - } - - - boolean isSingleColor = dataSet.getColors().size() == 1; - if (isSingleColor) { - mRenderPaint.setColor(dataSet.getColor(index)); - } - - int j = 0; - while (j < buffer.size()) { - - if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) { - j += 4; - continue; - } - - if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j+1])) - break; - - if (!isSingleColor) { - mRenderPaint.setColor(dataSet.getColor(j/4)); - } - - if((dataSet.getEntryForIndex(j/4).getY()<0 && roundedNegativeDataSetRadius >0)) { - Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3]), roundedNegativeDataSetRadius, roundedNegativeDataSetRadius, true, true, true, true); - c.drawPath(path2, mRenderPaint); - } else if((dataSet.getEntryForIndex(j/4).getY()>0 && roundedPositiveDataSetRadius >0)){ - Path path2 = roundRect(new RectF(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3]), roundedPositiveDataSetRadius, roundedPositiveDataSetRadius, true, true, true, true); - c.drawPath(path2, mRenderPaint); - } - else { - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - } - j += 4; - } - } - - 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) - path.rQuadTo(0, -ry, -rx, -ry);//top-right corner - else { - path.rLineTo(0, -ry); - path.rLineTo(-rx, 0); - } - path.rLineTo(-widthMinusCorners, 0); - if (tl) - path.rQuadTo(-rx, 0, -rx, ry); //top-left corner - else { - path.rLineTo(-rx, 0); - path.rLineTo(0, ry); - } - path.rLineTo(0, heightMinusCorners); - - if (bl) - path.rQuadTo(0, ry, rx, ry);//bottom-left corner - else { - path.rLineTo(0, ry); - path.rLineTo(rx, 0); - } - - path.rLineTo(widthMinusCorners, 0); - if (br) - path.rQuadTo(rx, 0, rx, -ry); //bottom-right corner - else { - path.rLineTo(rx, 0); - path.rLineTo(0, -ry); - } - - path.rLineTo(0, -heightMinusCorners); - path.close(); - return path; - } -} From 57d1c73fa75d6723bb30501e1e91757c223ad523 Mon Sep 17 00:00:00 2001 From: Mohini Katara Date: Fri, 27 Oct 2023 17:44:46 +0530 Subject: [PATCH 3/4] Added Rounded corner for Pie chart --- .../mpchartexample/PieChartActivity.java | 11 ++++++----- .../mikephil/charting/renderer/PieChartRenderer.java | 5 +++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 03371d3d57..fa8e220288 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -75,13 +75,14 @@ protected void onCreate(Bundle savedInstanceState) { chart.setCenterText(generateCenterSpannableText()); chart.setDrawHoleEnabled(true); - chart.setHoleColor(Color.WHITE); + chart.setHoleColor(Color.TRANSPARENT); - chart.setTransparentCircleColor(Color.WHITE); + chart.setTransparentCircleColor(Color.TRANSPARENT); chart.setTransparentCircleAlpha(110); - chart.setHoleRadius(58f); - chart.setTransparentCircleRadius(61f); + chart.setHoleRadius(50f); + + chart.setTransparentCircleRadius(0f); chart.setDrawCenterText(true); @@ -172,7 +173,7 @@ private void setData(int count, float range) { chart.highlightValues(null); PieChartRenderer renderer =(PieChartRenderer) chart.getRenderer(); - renderer.setRoundedCornerRadius(10f); + renderer.setRoundedCornerRadius(30f); dataSet.setSliceSpace(renderer.getRoundedCornerRadius()/2); chart.invalidate(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java index ef62590736..86b6247005 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -78,9 +78,10 @@ public class PieChartRenderer extends DataRenderer { */ public void setRoundedCornerRadius(float radius){ mRoundedCornerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mRoundedCornerPaint.setStrokeWidth(radius); mRoundedCornerPaint.setStyle(Style.STROKE); mRoundedCornerPaint.setAntiAlias(true); + mRoundedCornerPaint.setStrokeWidth(radius); + } /** @@ -428,7 +429,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint); - // Draw rounded corner path with paint object for the given radius + // Draw rounded corner path with paint object slice with the given radius if (getRoundedCornerRadius()>0) { mBitmapCanvas.drawPath(mPathBuffer, mRoundedCornerPaint); } From 512be02ffa5852981637f68404fd7847d2990052 Mon Sep 17 00:00:00 2001 From: Hannes Achleitner Date: Sat, 22 Jun 2024 06:34:46 +0200 Subject: [PATCH 4/4] Code cleanup --- .../charting/renderer/PieChartRenderer.java | 396 +++++++++--------- 1 file changed, 190 insertions(+), 206 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java index 86b6247005..db8d28cfda 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.renderer; import android.graphics.Bitmap; @@ -10,13 +9,11 @@ import android.graphics.Path; import android.graphics.RectF; import android.graphics.drawable.Drawable; -import android.os.Build; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; @@ -54,17 +51,17 @@ public class PieChartRenderer extends DataRenderer { * paint object for the text that can be displayed in the center of the * chart */ - private TextPaint mCenterTextPaint; + private final TextPaint mCenterTextPaint; /** * paint object used for drwing the slice-text */ - private Paint mEntryLabelsPaint; + private final Paint mEntryLabelsPaint; private StaticLayout mCenterTextLayout; private CharSequence mCenterTextLastValue; - private RectF mCenterTextLastBounds = new RectF(); - private RectF[] mRectBuffer = {new RectF(), new RectF(), new RectF()}; + private final RectF mCenterTextLastBounds = new RectF(); + private final RectF[] mRectBuffer = {new RectF(), new RectF(), new RectF()}; /** * Bitmap for drawing the center hole @@ -170,12 +167,12 @@ public void drawData(Canvas c) { for (IPieDataSet set : pieData.getDataSets()) { if (set.isVisible() && set.getEntryCount() > 0) - drawDataSet(c, set); + drawDataSet(set); } } - private Path mPathBuffer = new Path(); - private RectF mInnerRectBuffer = new RectF(); + private final Path mPathBuffer = new Path(); + private final RectF mInnerRectBuffer = new RectF(); protected float calculateMinimumRadiusForSpacedSlice( MPPointF center, @@ -210,7 +207,7 @@ protected float calculateMinimumRadiusForSpacedSlice( float spacedRadius = radius - containedTriangleHeight; // And now subtract the height of the arc that's between the triangle and the outer circle - spacedRadius -= Math.sqrt( + spacedRadius -= (float) Math.sqrt( Math.pow(arcMidPointX - (arcEndPointX + arcStartPointX) / 2.f, 2) + Math.pow(arcMidPointY - (arcEndPointY + arcStartPointY) / 2.f, 2)); @@ -219,9 +216,6 @@ protected float calculateMinimumRadiusForSpacedSlice( /** * Calculates the sliceSpace to use based on visible values and their size compared to the set sliceSpace. - * - * @param dataSet - * @return */ protected float getSliceSpace(IPieDataSet dataSet) { @@ -231,12 +225,10 @@ protected float getSliceSpace(IPieDataSet dataSet) { float spaceSizeRatio = dataSet.getSliceSpace() / mViewPortHandler.getSmallestContentExtension(); float minValueRatio = dataSet.getYMin() / mChart.getData().getYValueSum() * 2; - float sliceSpace = spaceSizeRatio > minValueRatio ? 0f : dataSet.getSliceSpace(); - - return sliceSpace; + return spaceSizeRatio > minValueRatio ? 0f : dataSet.getSliceSpace(); } - protected void drawDataSet(Canvas c, IPieDataSet dataSet) { + protected void drawDataSet(IPieDataSet dataSet) { float angle = 0; float rotationAngle = mChart.getRotationAngle(); @@ -463,7 +455,7 @@ public void drawValues(Canvas c) { if (!mChart.isDrawSlicesUnderHoleEnabled() && mChart.isDrawRoundedSlicesEnabled()) { // Add curved circle slice and spacing to rotation angle, so that it sits nicely inside - rotationAngle += roundedRadius * 360 / (Math.PI * 2 * radius); + rotationAngle += (float) (roundedRadius * 360 / (Math.PI * 2 * radius)); } } @@ -666,7 +658,6 @@ else if (valueLineColor != ColorTemplate.COLOR_NONE) drawEntryLabel(c, entryLabel, x, y + lineHeight / 2f); } } else if (drawYInside) { - drawValue(c, formatter, value, entry, 0, x, y + lineHeight / 2f, dataSet.getValueTextColor(j)); } } @@ -700,10 +691,6 @@ else if (valueLineColor != ColorTemplate.COLOR_NONE) /** * Draws an entry label at the specified position. * - * @param c - * @param label - * @param x - * @param y */ protected void drawEntryLabel(Canvas c, String label, float x, float y) { c.drawText(label, x, y, mEntryLabelsPaint); @@ -711,18 +698,18 @@ protected void drawEntryLabel(Canvas c, String label, float x, float y) { @Override public void drawExtras(Canvas c) { - drawHole(c); + drawHole(); c.drawBitmap(mDrawBitmap.get(), 0, 0, null); drawCenterText(c); } - private Path mHoleCirclePath = new Path(); + private final Path mHoleCirclePath = new Path(); /** * draws the hole in the center of the chart and the transparent circle / * hole */ - protected void drawHole(Canvas c) { + protected void drawHole() { if (mChart.isDrawHoleEnabled() && mBitmapCanvas != null) { @@ -816,14 +803,12 @@ protected void drawCenterText(Canvas c) { float layoutHeight = mCenterTextLayout.getHeight(); c.save(); - if (Build.VERSION.SDK_INT >= 18) { - Path path = mDrawCenterTextPathBuffer; - path.reset(); - path.addOval(holeRect, Path.Direction.CW); - c.clipPath(path); - } + Path path = mDrawCenterTextPathBuffer; + path.reset(); + path.addOval(holeRect, Path.Direction.CW); + c.clipPath(path); - c.translate(boundingRect.left, boundingRect.top + (boundingRect.height() - layoutHeight) / 2.f); + c.translate(boundingRect.left, boundingRect.top + (boundingRect.height() - layoutHeight) / 2.f); mCenterTextLayout.draw(c); c.restore(); @@ -864,176 +849,176 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { final RectF highlightedCircleBox = mDrawHighlightedRectF; highlightedCircleBox.set(0, 0, 0, 0); - for (int i = 0; i < indices.length; i++) { - - // get the index to highlight - int index = (int) indices[i].getX(); - - if (index >= drawAngles.length) - continue; - - IPieDataSet set = mChart.getData() - .getDataSetByIndex(indices[i].getDataSetIndex()); - - if (set == null || !set.isHighlightEnabled()) - continue; - - final int entryCount = set.getEntryCount(); - int visibleAngleCount = 0; - for (int j = 0; j < entryCount; j++) { - // draw only if the value is greater than zero - if ((Math.abs(set.getEntryForIndex(j).getY()) > Utils.FLOAT_EPSILON)) { - visibleAngleCount++; - } - } - - if (index == 0) - angle = 0.f; - else - angle = absoluteAngles[index - 1] * phaseX; - - final float sliceSpace = visibleAngleCount <= 1 ? 0.f : set.getSliceSpace(); - - float sliceAngle = drawAngles[index]; - float innerRadius = userInnerRadius; - - float shift = set.getSelectionShift(); - final float highlightedRadius = radius + shift; - highlightedCircleBox.set(mChart.getCircleBox()); - highlightedCircleBox.inset(-shift, -shift); - - final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; - - Integer highlightColor = set.getHighlightColor(); - if (highlightColor == null) - highlightColor = set.getColor(index); - mRenderPaint.setColor(highlightColor); - - final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? - 0.f : - sliceSpace / (Utils.FDEG2RAD * radius); - - final float sliceSpaceAngleShifted = visibleAngleCount == 1 ? - 0.f : - sliceSpace / (Utils.FDEG2RAD * highlightedRadius); - - final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY; - float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY; - if (sweepAngleOuter < 0.f) { - sweepAngleOuter = 0.f; - } - - final float startAngleShifted = rotationAngle + (angle + sliceSpaceAngleShifted / 2.f) * phaseY; - float sweepAngleShifted = (sliceAngle - sliceSpaceAngleShifted) * phaseY; - if (sweepAngleShifted < 0.f) { - sweepAngleShifted = 0.f; - } - - mPathBuffer.reset(); - - if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { - // Android is doing "mod 360" - mPathBuffer.addCircle(center.x, center.y, highlightedRadius, Path.Direction.CW); - } else { - - mPathBuffer.moveTo( - center.x + highlightedRadius * (float) Math.cos(startAngleShifted * Utils.FDEG2RAD), - center.y + highlightedRadius * (float) Math.sin(startAngleShifted * Utils.FDEG2RAD)); - - mPathBuffer.arcTo( - highlightedCircleBox, - startAngleShifted, - sweepAngleShifted - ); - } - - float sliceSpaceRadius = 0.f; - if (accountForSliceSpacing) { - sliceSpaceRadius = - calculateMinimumRadiusForSpacedSlice( - center, radius, - sliceAngle * phaseY, - center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD), - center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD), - startAngleOuter, - sweepAngleOuter); - } - - // API < 21 does not receive floats in addArc, but a RectF - mInnerRectBuffer.set( - center.x - innerRadius, - center.y - innerRadius, - center.x + innerRadius, - center.y + innerRadius); - - if (drawInnerArc && - (innerRadius > 0.f || accountForSliceSpacing)) { - - if (accountForSliceSpacing) { - float minSpacedRadius = sliceSpaceRadius; - - if (minSpacedRadius < 0.f) - minSpacedRadius = -minSpacedRadius; - - innerRadius = Math.max(innerRadius, minSpacedRadius); - } - - final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ? - 0.f : - sliceSpace / (Utils.FDEG2RAD * innerRadius); - final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY; - float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY; - if (sweepAngleInner < 0.f) { - sweepAngleInner = 0.f; - } - final float endAngleInner = startAngleInner + sweepAngleInner; - - if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { - // Android is doing "mod 360" - mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW); - } else { - - mPathBuffer.lineTo( - center.x + innerRadius * (float) Math.cos(endAngleInner * Utils.FDEG2RAD), - center.y + innerRadius * (float) Math.sin(endAngleInner * Utils.FDEG2RAD)); - - mPathBuffer.arcTo( - mInnerRectBuffer, - endAngleInner, - -sweepAngleInner - ); - } - } else { - - if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) { - - if (accountForSliceSpacing) { - final float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f; - - final float arcEndPointX = center.x + - sliceSpaceRadius * (float) Math.cos(angleMiddle * Utils.FDEG2RAD); - final float arcEndPointY = center.y + - sliceSpaceRadius * (float) Math.sin(angleMiddle * Utils.FDEG2RAD); - - mPathBuffer.lineTo( - arcEndPointX, - arcEndPointY); - - } else { - - mPathBuffer.lineTo( - center.x, - center.y); - } - - } - - } - - mPathBuffer.close(); - - mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint); - } + for (Highlight highlight : indices) { + + // get the index to highlight + int index = (int) highlight.getX(); + + if (index >= drawAngles.length) + continue; + + IPieDataSet set = mChart.getData() + .getDataSetByIndex(highlight.getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + final int entryCount = set.getEntryCount(); + int visibleAngleCount = 0; + for (int j = 0; j < entryCount; j++) { + // draw only if the value is greater than zero + if ((Math.abs(set.getEntryForIndex(j).getY()) > Utils.FLOAT_EPSILON)) { + visibleAngleCount++; + } + } + + if (index == 0) + angle = 0.f; + else + angle = absoluteAngles[index - 1] * phaseX; + + final float sliceSpace = visibleAngleCount <= 1 ? 0.f : set.getSliceSpace(); + + float sliceAngle = drawAngles[index]; + float innerRadius = userInnerRadius; + + float shift = set.getSelectionShift(); + final float highlightedRadius = radius + shift; + highlightedCircleBox.set(mChart.getCircleBox()); + highlightedCircleBox.inset(-shift, -shift); + + final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; + + Integer highlightColor = set.getHighlightColor(); + if (highlightColor == null) + highlightColor = set.getColor(index); + mRenderPaint.setColor(highlightColor); + + final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * radius); + + final float sliceSpaceAngleShifted = visibleAngleCount == 1 ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * highlightedRadius); + + final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY; + float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY; + if (sweepAngleOuter < 0.f) { + sweepAngleOuter = 0.f; + } + + final float startAngleShifted = rotationAngle + (angle + sliceSpaceAngleShifted / 2.f) * phaseY; + float sweepAngleShifted = (sliceAngle - sliceSpaceAngleShifted) * phaseY; + if (sweepAngleShifted < 0.f) { + sweepAngleShifted = 0.f; + } + + mPathBuffer.reset(); + + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { + // Android is doing "mod 360" + mPathBuffer.addCircle(center.x, center.y, highlightedRadius, Path.Direction.CW); + } else { + + mPathBuffer.moveTo( + center.x + highlightedRadius * (float) Math.cos(startAngleShifted * Utils.FDEG2RAD), + center.y + highlightedRadius * (float) Math.sin(startAngleShifted * Utils.FDEG2RAD)); + + mPathBuffer.arcTo( + highlightedCircleBox, + startAngleShifted, + sweepAngleShifted + ); + } + + float sliceSpaceRadius = 0.f; + if (accountForSliceSpacing) { + sliceSpaceRadius = + calculateMinimumRadiusForSpacedSlice( + center, radius, + sliceAngle * phaseY, + center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD), + center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD), + startAngleOuter, + sweepAngleOuter); + } + + // API < 21 does not receive floats in addArc, but a RectF + mInnerRectBuffer.set( + center.x - innerRadius, + center.y - innerRadius, + center.x + innerRadius, + center.y + innerRadius); + + if (drawInnerArc && + (innerRadius > 0.f || accountForSliceSpacing)) { + + if (accountForSliceSpacing) { + float minSpacedRadius = sliceSpaceRadius; + + if (minSpacedRadius < 0.f) + minSpacedRadius = -minSpacedRadius; + + innerRadius = Math.max(innerRadius, minSpacedRadius); + } + + final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * innerRadius); + final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY; + float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY; + if (sweepAngleInner < 0.f) { + sweepAngleInner = 0.f; + } + final float endAngleInner = startAngleInner + sweepAngleInner; + + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { + // Android is doing "mod 360" + mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW); + } else { + + mPathBuffer.lineTo( + center.x + innerRadius * (float) Math.cos(endAngleInner * Utils.FDEG2RAD), + center.y + innerRadius * (float) Math.sin(endAngleInner * Utils.FDEG2RAD)); + + mPathBuffer.arcTo( + mInnerRectBuffer, + endAngleInner, + -sweepAngleInner + ); + } + } else { + + if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) { + + if (accountForSliceSpacing) { + final float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f; + + final float arcEndPointX = center.x + + sliceSpaceRadius * (float) Math.cos(angleMiddle * Utils.FDEG2RAD); + final float arcEndPointY = center.y + + sliceSpaceRadius * (float) Math.sin(angleMiddle * Utils.FDEG2RAD); + + mPathBuffer.lineTo( + arcEndPointX, + arcEndPointY); + + } else { + + mPathBuffer.lineTo( + center.x, + center.y); + } + + } + + } + + mPathBuffer.close(); + + mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint); + } MPPointF.recycleInstance(center); } @@ -1041,9 +1026,8 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { /** * This gives all pie-slices a rounded edge. * - * @param c */ - protected void drawRoundedSlices(Canvas c) { + protected void drawRoundedSlices() { if (!mChart.isDrawRoundedSlicesEnabled()) return;