diff --git a/MPChartExample/src/main/AndroidManifest.xml b/MPChartExample/src/main/AndroidManifest.xml
index 14eeed1810..d357bdf1b7 100644
--- a/MPChartExample/src/main/AndroidManifest.xml
+++ b/MPChartExample/src/main/AndroidManifest.xml
@@ -25,6 +25,7 @@
+
diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartRoundedActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartRoundedActivity.java
new file mode 100644
index 0000000000..6c3ddd216a
--- /dev/null
+++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartRoundedActivity.java
@@ -0,0 +1,334 @@
+
+package com.xxmassdeveloper.mpchartexample;
+
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.SpannableString;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StyleSpan;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.WindowManager;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+import com.github.mikephil.charting.animation.Easing;
+import com.github.mikephil.charting.charts.PieChart;
+import com.github.mikephil.charting.components.Legend;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.data.PieData;
+import com.github.mikephil.charting.data.PieDataSet;
+import com.github.mikephil.charting.data.PieEntry;
+import com.github.mikephil.charting.formatter.PercentFormatter;
+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;
+
+import java.util.ArrayList;
+
+import androidx.core.content.ContextCompat;
+
+public class PieChartRoundedActivity extends DemoBase implements OnSeekBarChangeListener,
+ OnChartValueSelectedListener {
+
+ private PieChart chart;
+ private SeekBar seekBarX, seekBarY;
+ private TextView tvX, tvY;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ setContentView(R.layout.activity_piechart);
+
+ setTitle("PieChartActivity");
+
+ tvX = findViewById(R.id.tvXMax);
+ tvY = findViewById(R.id.tvYMax);
+
+ seekBarX = findViewById(R.id.seekBarX);
+ seekBarY = findViewById(R.id.seekBarY);
+
+ seekBarX.setOnSeekBarChangeListener(this);
+ seekBarY.setOnSeekBarChangeListener(this);
+
+ chart = findViewById(R.id.chart1);
+ chart.setUsePercentValues(true);
+ chart.getDescription().setEnabled(false);
+ chart.setExtraOffsets(5, 10, 5, 5);
+
+ chart.setDragDecelerationFrictionCoef(0.95f);
+
+ chart.setCenterTextTypeface(tfLight);
+ chart.setCenterText(generateCenterSpannableText());
+
+ chart.setDrawHoleEnabled(true);
+ chart.setHoleColor(Color.TRANSPARENT);
+
+ chart.setTransparentCircleColor(Color.TRANSPARENT);
+ chart.setTransparentCircleAlpha(110);
+
+ chart.setHoleRadius(50f);
+
+ chart.setTransparentCircleRadius(0f);
+
+ chart.setDrawCenterText(true);
+
+ chart.setRotationAngle(0);
+ // enable rotation of the chart by touch
+ chart.setRotationEnabled(true);
+ chart.setHighlightPerTapEnabled(true);
+
+ // chart.setUnit(" €");
+ // chart.setDrawUnitsInChart(true);
+
+ // add a selection listener
+ chart.setOnChartValueSelectedListener(this);
+
+ seekBarX.setProgress(4);
+ seekBarY.setProgress(10);
+
+ chart.animateY(1400, Easing.EaseInOutQuad);
+ // chart.spin(2000, 0, 360);
+
+ Legend l = chart.getLegend();
+ l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP);
+ l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT);
+ l.setOrientation(Legend.LegendOrientation.VERTICAL);
+ l.setDrawInside(false);
+ l.setXEntrySpace(7f);
+ l.setYEntrySpace(0f);
+ l.setYOffset(0f);
+
+ // entry label styling
+ chart.setEntryLabelColor(Color.WHITE);
+ chart.setEntryLabelTypeface(tfRegular);
+ chart.setEntryLabelTextSize(12f);
+ }
+
+ private void setData(int count, float range) {
+ ArrayList entries = new ArrayList<>();
+ Double[] sampleValues = DataTools.Companion.getValues(100);
+
+ // NOTE: The order of the entries when being added to the entries array determines their position around the center of
+ // the chart.
+ for (int i = 0; i < count ; i++) {
+ entries.add(new PieEntry((float) ((sampleValues[i].floatValue() * range) + range / 5),
+ parties[i % parties.length],
+ getResources().getDrawable(R.drawable.star)));
+ }
+
+ PieDataSet dataSet = new PieDataSet(entries, "Election Results");
+
+ dataSet.setDrawIcons(false);
+
+ dataSet.setSliceSpace(3f);
+ dataSet.setIconsOffset(new MPPointF(0, 40));
+ dataSet.setSelectionShift(5f);
+
+ // add a lot of colors
+
+ ArrayList colors = new ArrayList<>();
+
+ for (int c : ColorTemplate.VORDIPLOM_COLORS)
+ colors.add(c);
+
+ for (int c : ColorTemplate.JOYFUL_COLORS)
+ colors.add(c);
+
+ for (int c : ColorTemplate.COLORFUL_COLORS)
+ colors.add(c);
+
+ for (int c : ColorTemplate.LIBERTY_COLORS)
+ colors.add(c);
+
+ for (int c : ColorTemplate.PASTEL_COLORS)
+ colors.add(c);
+
+ colors.add(ColorTemplate.getHoloBlue());
+
+ dataSet.setColors(colors);
+ //dataSet.setSelectionShift(0f);
+
+ PieData data = new PieData(dataSet);
+ data.setValueFormatter(new PercentFormatter());
+ data.setValueTextSize(11f);
+ data.setValueTextColor(Color.WHITE);
+ data.setValueTypeface(tfLight);
+ chart.setData(data);
+
+ // undo all highlights
+ chart.highlightValues(null);
+
+ PieChartRenderer renderer =(PieChartRenderer) chart.getRenderer();
+ renderer.setRoundedCornerRadius(30f);
+ dataSet.setSliceSpace(renderer.getRoundedCornerRadius()/2);
+
+ chart.invalidate();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.pie, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+
+ switch (item.getItemId()) {
+ case R.id.viewGithub: {
+ Intent i = new Intent(Intent.ACTION_VIEW);
+ i.setData(Uri.parse("https://github.com/AppDevNext/AndroidChart/blob/master/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java"));
+ startActivity(i);
+ break;
+ }
+ case R.id.actionToggleValues: {
+ for (IDataSet> set : chart.getData().getDataSets())
+ set.setDrawValues(!set.isDrawValuesEnabled());
+
+ chart.invalidate();
+ break;
+ }
+ case R.id.actionToggleIcons: {
+ for (IDataSet> set : chart.getData().getDataSets())
+ set.setDrawIcons(!set.isDrawIconsEnabled());
+
+ chart.invalidate();
+ break;
+ }
+ case R.id.actionToggleHole: {
+ if (chart.isDrawHoleEnabled())
+ chart.setDrawHoleEnabled(false);
+ else
+ chart.setDrawHoleEnabled(true);
+ chart.invalidate();
+ break;
+ }
+ case R.id.actionToggleMinAngles: {
+ if (chart.getMinAngleForSlices() == 0f)
+ chart.setMinAngleForSlices(36f);
+ else
+ chart.setMinAngleForSlices(0f);
+ chart.notifyDataSetChanged();
+ chart.invalidate();
+ break;
+ }
+ case R.id.actionToggleCurvedSlices: {
+ boolean toSet = !chart.isDrawRoundedSlicesEnabled() || !chart.isDrawHoleEnabled();
+ chart.setDrawRoundedSlices(toSet);
+ if (toSet && !chart.isDrawHoleEnabled()) {
+ chart.setDrawHoleEnabled(true);
+ }
+ if (toSet && chart.isDrawSlicesUnderHoleEnabled()) {
+ chart.setDrawSlicesUnderHole(false);
+ }
+ chart.invalidate();
+ break;
+ }
+ case R.id.actionDrawCenter: {
+ if (chart.isDrawCenterTextEnabled())
+ chart.setDrawCenterText(false);
+ else
+ chart.setDrawCenterText(true);
+ chart.invalidate();
+ break;
+ }
+ case R.id.actionToggleXValues: {
+
+ chart.setDrawEntryLabels(!chart.isDrawEntryLabelsEnabled());
+ chart.invalidate();
+ break;
+ }
+ case R.id.actionTogglePercent:
+ chart.setUsePercentValues(!chart.isUsePercentValuesEnabled());
+ chart.invalidate();
+ break;
+ case R.id.animateX: {
+ chart.animateX(1400);
+ break;
+ }
+ case R.id.animateY: {
+ chart.animateY(1400);
+ break;
+ }
+ case R.id.animateXY: {
+ chart.animateXY(1400, 1400);
+ break;
+ }
+ case R.id.actionToggleSpin: {
+ chart.spin(1000, chart.getRotationAngle(), chart.getRotationAngle() + 360, Easing.EaseInOutCubic);
+ break;
+ }
+ case R.id.actionSave: {
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
+ saveToGallery();
+ } else {
+ requestStoragePermission(chart);
+ }
+ break;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+
+ tvX.setText(String.valueOf(seekBarX.getProgress()));
+ tvY.setText(String.valueOf(seekBarY.getProgress()));
+
+ setData(seekBarX.getProgress(), seekBarY.getProgress());
+ }
+
+ @Override
+ protected void saveToGallery() {
+ saveToGallery(chart, "PieChartActivity");
+ }
+
+ private SpannableString generateCenterSpannableText() {
+
+ SpannableString s = new SpannableString("MPAndroidChart\ndeveloped by Philipp Jahoda");
+ s.setSpan(new RelativeSizeSpan(1.7f), 0, 14, 0);
+ s.setSpan(new StyleSpan(Typeface.NORMAL), 14, s.length() - 15, 0);
+ s.setSpan(new ForegroundColorSpan(Color.GRAY), 14, s.length() - 15, 0);
+ s.setSpan(new RelativeSizeSpan(.8f), 14, s.length() - 15, 0);
+ s.setSpan(new StyleSpan(Typeface.ITALIC), s.length() - 14, s.length(), 0);
+ s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), s.length() - 14, s.length(), 0);
+ return s;
+ }
+
+ @Override
+ public void onValueSelected(Entry e, Highlight h) {
+
+ if (e == null)
+ return;
+ Log.i("VAL SELECTED",
+ "Value: " + e.getY() + ", index: " + h.getX()
+ + ", DataSet index: " + h.getDataSetIndex());
+ }
+
+ @Override
+ public void onNothingSelected() {
+ Log.i("PieChart", "nothing selected");
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {}
+}
diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.kt b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.kt
index 602f5df198..2d9f2da5da 100644
--- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.kt
+++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.kt
@@ -91,6 +91,7 @@ class MainActivity : AppCompatActivity(), OnItemClickListener {
add(19, ContentItem("Pie Charts"))
add(20, ContentItem("Basic", "Simple pie chart.", PieChartActivity::class.java))
+ add(20, ContentItem("Basic", "Rounded pie chart.", PieChartRoundedActivity::class.java))
add(21, ContentItem("Value Lines", "Stylish lines drawn outward from slices.", PiePolylineChartActivity::class.java))
add(22, ContentItem("Half Pie", "180° (half) pie chart.", HalfPieChartActivity::class.java))
add(23, ContentItem("Specific positions", "This demonstrates how to pass a list of specific positions for lines and labels on x and y axis", SpecificPositionsLineChartActivity::class.java))
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..653eb4e296 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,20 @@ public class PieChartRenderer extends DataRenderer {
protected Canvas mBitmapCanvas;
+ /**
+ * Setter for the rounded corner slice paint object
+ */
+ public void setRoundedCornerRadius(float radius){
+ mRoundedCornerPaint.setStrokeWidth(radius);
+ }
+
+ /**
+ * 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);
@@ -97,6 +116,10 @@ public PieChartRenderer(PieChart chart, ChartAnimator animator,
mValueLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mValueLinePaint.setStyle(Style.STROKE);
+
+ mRoundedCornerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mRoundedCornerPaint.setStyle(Style.STROKE);
+ mRoundedCornerPaint.setAntiAlias(true);
}
public Paint getPaintHole() {
@@ -245,6 +268,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 +296,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 +429,11 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) {
mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint);
+ // Draw rounded corner path with paint object slice with 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
new file mode 100644
index 0000000000..e06228c6df
--- /dev/null
+++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedBarChartRenderer.java
@@ -0,0 +1,346 @@
+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;
+
+/** @noinspection unused*/
+public class RoundedBarChartRenderer extends BarChartRenderer {
+
+ public RoundedBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
+ super(chart, animator, viewPortHandler);
+ }
+
+ private final RectF mBarShadowRectBuffer = new RectF();
+ private final 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((double) (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..7921480cb9
--- /dev/null
+++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RoundedHorizontalBarChartRenderer.java
@@ -0,0 +1,264 @@
+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;
+
+/** @noinspection unused*/
+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((double) (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;
+ }
+}
diff --git a/screenshotsToCompare9/StartTest_smokeTestStart-20-PieChartRoundedActivity.png b/screenshotsToCompare9/StartTest_smokeTestStart-20-PieChartRoundedActivity.png
new file mode 100644
index 0000000000..336bb7e788
Binary files /dev/null and b/screenshotsToCompare9/StartTest_smokeTestStart-20-PieChartRoundedActivity.png differ
diff --git a/screenshotsToCompare9/StartTest_smokeTestStart-39-PieChartActivity.png b/screenshotsToCompare9/StartTest_smokeTestStart-39-PieChartActivity.png
new file mode 100644
index 0000000000..c99634fa55
Binary files /dev/null and b/screenshotsToCompare9/StartTest_smokeTestStart-39-PieChartActivity.png differ
diff --git a/screenshotsToCompare9/StartTest_smokeTestStart.png b/screenshotsToCompare9/StartTest_smokeTestStart.png
index 06af3ead24..0ba82ed4c4 100644
Binary files a/screenshotsToCompare9/StartTest_smokeTestStart.png and b/screenshotsToCompare9/StartTest_smokeTestStart.png differ