diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java deleted file mode 100644 index 28a353a17..000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java +++ /dev/null @@ -1,301 +0,0 @@ -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.Log; - -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; -import com.github.mikephil.charting.highlight.BarHighlighter; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.renderer.BarChartRenderer; - -import java.util.Locale; - -/** - * Chart that draws bars. - * - * @author Philipp Jahoda - */ -public class BarChart extends BarLineChartBase implements BarDataProvider { - - /** - * flag that indicates whether the highlight should be full-bar oriented, or single-value? - */ - protected boolean mHighlightFullBarEnabled = false; - - /** - * if set to true, all values are drawn above their bars, instead of below their top - */ - private boolean mDrawValueAboveBar = true; - - /** - * if set to true, a grey area is drawn behind each bar that indicates the maximum value - */ - private boolean mDrawBarShadow = false; - - /** - * if set to true, the bar chart's bars would be round on all corners instead of rectangular - */ - private boolean mDrawRoundedBars; - - /** - * the radius of the rounded bar chart bars - */ - private float mRoundedBarRadius = 0f; - - private boolean mFitBars = false; - - public BarChart(Context context) { - super(context); - } - - public BarChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public BarChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mRenderer = new BarChartRenderer(this, mAnimator, mViewPortHandler, mDrawRoundedBars, mRoundedBarRadius); - - setHighlighter(new BarHighlighter(this)); - - getXAxis().setSpaceMin(0.5f); - getXAxis().setSpaceMax(0.5f); - } - - @Override - protected void calcMinMax() { - - if (mFitBars) { - mXAxis.calculate(mData.getXMin() - mData.getBarWidth() / 2f, mData.getXMax() + mData.getBarWidth() / 2f); - } else { - mXAxis.calculate(mData.getXMin(), mData.getXMax()); - } - - // calculate axis range (min / max) according to provided data - mAxisLeft.calculate(mData.getYMin(YAxis.AxisDependency.LEFT), mData.getYMax(YAxis.AxisDependency.LEFT)); - mAxisRight.calculate(mData.getYMin(YAxis.AxisDependency.RIGHT), mData.getYMax(YAxis.AxisDependency - .RIGHT)); - } - - /** - * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch - * point - * inside the BarChart. - */ - @Override - public Highlight getHighlightByTouchPoint(float x, float y) { - - if (mData == null) { - Log.e(LOG_TAG, "Can't select by touch. No data set."); - return null; - } else { - Highlight h = getHighlighter().getHighlight(x, y); - if (h == null || !isHighlightFullBarEnabled()) return h; - - // For isHighlightFullBarEnabled, remove stackIndex - return new Highlight(h.getX(), h.getY(), - h.getXPx(), h.getYPx(), - h.getDataSetIndex(), -1, h.getAxis()); - } - } - - /** - * Returns the bounding box of the specified Entry in the specified DataSet. Returns null if the Entry could not be - * found in the charts data. Performance-intensive code should use void getBarBounds(BarEntry, RectF) instead. - */ - public RectF getBarBounds(BarEntry barEntry) { - - RectF bounds = new RectF(); - getBarBounds(barEntry, bounds); - - return bounds; - } - - /** - * The passed outputRect will be assigned the values of the bounding box of the specified Entry in the specified DataSet. - * The rect will be assigned Float.MIN_VALUE in all locations if the Entry could not be found in the charts data. - */ - public void getBarBounds(BarEntry barEntry, RectF outputRect) { - - IBarDataSet set = mData.getDataSetForEntry(barEntry); - - if (set == null) { - outputRect.set(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE); - return; - } - - float y = barEntry.getY(); - float x = barEntry.getX(); - - float barWidth = mData.getBarWidth(); - - float left = x - barWidth / 2f; - float right = x + barWidth / 2f; - float top = y >= 0 ? y : 0; - float bottom = y <= 0 ? y : 0; - - outputRect.set(left, top, right, bottom); - - getTransformer(set.getAxisDependency()).rectValueToPixel(outputRect); - } - - /** - * If set to true, all values are drawn above their bars, instead of below their top. - * - */ - public void setDrawValueAboveBar(boolean enabled) { - mDrawValueAboveBar = enabled; - } - - /** - * returns true if drawing values above bars is enabled, false if not - * - */ - public boolean isDrawValueAboveBarEnabled() { - return mDrawValueAboveBar; - } - - /** - * If set to true, a grey area is drawn behind each bar that indicates the maximum value. Enabling his will reduce - * performance by about 50%. - * - */ - public void setDrawBarShadow(boolean enabled) { - mDrawBarShadow = enabled; - } - - /** - * returns true if drawing shadows (maxvalue) for each bar is enabled, false if not - * - */ - public boolean isDrawBarShadowEnabled() { - return mDrawBarShadow; - } - - @Override - public void setDrawBarShadowEnabled(boolean value) { - mDrawBarShadow = value; - - } - - @Override - public void setDrawValueAboveBarEnabled(boolean value) { - mDrawValueAboveBar = value; - } - - /** - * Set this to true to make the highlight operation full-bar oriented, false to make it highlight single values (relevant - * only for stacked). If enabled, highlighting operations will highlight the whole bar, even if only a single stack entry - * was tapped. - * Default: false - * - */ - public void setHighlightFullBarEnabled(boolean enabled) { - mHighlightFullBarEnabled = enabled; - } - - /** - * @return true the highlight operation is be full-bar oriented, false if single-value - */ - @Override - public boolean isHighlightFullBarEnabled() { - return mHighlightFullBarEnabled; - } - - /** - * Highlights the value at the given x-value in the given DataSet. Provide - * -1 as the dataSetIndex to undo all highlighting. - * @param stackIndex the index inside the stack - only relevant for stacked entries - */ - public void highlightValue(float x, int dataSetIndex, int stackIndex) { - highlightValue(new Highlight(x, dataSetIndex, stackIndex), false); - } - - @Override - public BarData getBarData() { - return mData; - } - - /** - * Adds half of the bar width to each side of the x-axis range in order to allow the bars of the barchart to be - * fully displayed. - * Default: false - * - */ - public void setFitBars(boolean enabled) { - mFitBars = enabled; - } - - /** - * Groups all BarDataSet objects this data object holds together by modifying the x-value of their entries. - * Previously set x-values of entries will be overwritten. Leaves space between bars and groups as specified - * by the parameters. - * Calls notifyDataSetChanged() afterwards. - * - * @param fromX the starting point on the x-axis where the grouping should begin - * @param groupSpace the space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f - * @param barSpace the space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f - */ - public void groupBars(float fromX, float groupSpace, float barSpace) { - - if (getBarData() == null) { - throw new RuntimeException("You need to set data for the chart before grouping bars."); - } else { - getBarData().groupBars(fromX, groupSpace, barSpace); - notifyDataSetChanged(); - } - } - /** - * Used to enable rounded bar chart bars and set the radius of the rounded bars - * - * @param mRoundedBarRadius - the radius of the rounded bars - */ - public void setRoundedBarRadius(float mRoundedBarRadius) { - this.mRoundedBarRadius = mRoundedBarRadius; - this.mDrawRoundedBars = true; - init(); - } - - @Override - public String getAccessibilityDescription() { - - BarData barData = getBarData(); - if (barData == null) { - return ""; - } - - int entryCount = barData.getEntryCount(); - - // Find the min and max index - IAxisValueFormatter yAxisValueFormatter = getAxisLeft().getValueFormatter(); - String minVal = yAxisValueFormatter.getFormattedValue(barData.getYMin(), null); - String maxVal = yAxisValueFormatter.getFormattedValue(barData.getYMax(), null); - - // Data range... - IAxisValueFormatter xAxisValueFormatter = getXAxis().getValueFormatter(); - String minRange = xAxisValueFormatter.getFormattedValue(barData.getXMin(), null); - String maxRange = xAxisValueFormatter.getFormattedValue(barData.getXMax(), null); - - String entries = entryCount == 1 ? "entry" : "entries"; - - // Format the values of min and max; to recite them back - - return String.format(Locale.getDefault(), "The bar chart has %d %s. " + - "The minimum value is %s and maximum value is %s." + - "Data ranges from %s to %s.", - entryCount, entries, minVal, maxVal, minRange, maxRange); - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.kt new file mode 100644 index 000000000..aa990e1e1 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.kt @@ -0,0 +1,249 @@ +package com.github.mikephil.charting.charts + +import android.content.Context +import android.graphics.RectF +import android.util.AttributeSet +import android.util.Log +import com.github.mikephil.charting.components.YAxis +import com.github.mikephil.charting.data.BarData +import com.github.mikephil.charting.data.BarEntry +import com.github.mikephil.charting.highlight.BarHighlighter +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider +import com.github.mikephil.charting.renderer.BarChartRenderer +import java.util.Locale + +/** + * Chart that draws bars. + */ +open class BarChart : BarLineChartBase, BarDataProvider { + /** + * @return true the highlight operation is be full-bar oriented, false if single-value + */ + /** + * Set this to true to make the highlight operation full-bar oriented, false to make it highlight single values (relevant + * only for stacked). If enabled, highlighting operations will highlight the whole bar, even if only a single stack entry + * was tapped. + * Default: false + * + */ + /** + * flag that indicates whether the highlight should be full-bar oriented, or single-value? + */ + override var isHighlightFullBarEnabled: Boolean = false + + /** + * returns true if drawing values above bars is enabled, false if not + * + */ + /** + * if set to true, all values are drawn above their bars, instead of below their top + */ + override var isDrawValueAboveBarEnabled: Boolean = true + + /** + * returns true if drawing shadows (maxvalue) for each bar is enabled, false if not + * + */ + /** + * if set to true, a grey area is drawn behind each bar that indicates the maximum value + */ + override var isDrawBarShadowEnabled: Boolean = false + + /** + * if set to true, the bar chart's bars would be round on all corners instead of rectangular + */ + private var mDrawRoundedBars = false + + /** + * the radius of the rounded bar chart bars + */ + private var mRoundedBarRadius = 0f + + private var mFitBars = false + + constructor(context: Context?) : super(context) + + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + + constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) + + protected override fun init() { + super.init() + + mRenderer = BarChartRenderer(this, mAnimator, mViewPortHandler, mDrawRoundedBars, mRoundedBarRadius) + + setHighlighter(BarHighlighter(this)) + + xAxis.spaceMin = 0.5f + xAxis.spaceMax = 0.5f + } + + protected override fun calcMinMax() { + if (mFitBars) { + mXAxis.calculate(mData.xMin - mData.barWidth / 2f, mData.xMax + mData.barWidth / 2f) + } else { + mXAxis.calculate(mData.xMin, mData.xMax) + } + + // calculate axis range (min / max) according to provided data + mAxisLeft.calculate(mData.getYMin(YAxis.AxisDependency.LEFT), mData.getYMax(YAxis.AxisDependency.LEFT)) + mAxisRight.calculate( + mData.getYMin(YAxis.AxisDependency.RIGHT), mData.getYMax( + YAxis.AxisDependency + .RIGHT + ) + ) + } + + /** + * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch + * point + * inside the BarChart. + */ + override fun getHighlightByTouchPoint(x: Float, y: Float): Highlight? { + if (mData == null) { + Log.e(LOG_TAG, "Can't select by touch. No data set.") + return null + } else { + val h = highlighter.getHighlight(x, y) + if (h == null || !isHighlightFullBarEnabled) return h + + // For isHighlightFullBarEnabled, remove stackIndex + return Highlight( + h.x, h.y, + h.xPx, h.yPx, + h.dataSetIndex, -1, h.axis + ) + } + } + + /** + * Returns the bounding box of the specified Entry in the specified DataSet. Returns null if the Entry could not be + * found in the charts data. Performance-intensive code should use void getBarBounds(BarEntry, RectF) instead. + */ + fun getBarBounds(barEntry: BarEntry): RectF { + val bounds = RectF() + getBarBounds(barEntry, bounds) + + return bounds + } + + /** + * The passed outputRect will be assigned the values of the bounding box of the specified Entry in the specified DataSet. + * The rect will be assigned Float.MIN_VALUE in all locations if the Entry could not be found in the charts data. + */ + open fun getBarBounds(barEntry: BarEntry, outputRect: RectF) { + val set = mData.getDataSetForEntry(barEntry) + + if (set == null) { + outputRect.set(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE) + return + } + + val y = barEntry.y + val x = barEntry.x + + val barWidth = mData.barWidth + + val left = x - barWidth / 2f + val right = x + barWidth / 2f + val top = if (y >= 0) y else 0f + val bottom = if (y <= 0) y else 0f + + outputRect.set(left, top, right, bottom) + + getTransformer(set.axisDependency)!!.rectValueToPixel(outputRect) + } + + /** + * If set to true, all values are drawn above their bars, instead of below their top. + * + */ + fun setDrawValueAboveBar(enabled: Boolean) { + this.isDrawValueAboveBarEnabled = enabled + } + + /** + * If set to true, a grey area is drawn behind each bar that indicates the maximum value. Enabling his will reduce + * performance by about 50%. + * + */ + fun setDrawBarShadow(enabled: Boolean) { + this.isDrawBarShadowEnabled = enabled + } + + /** + * Highlights the value at the given x-value in the given DataSet. Provide + * -1 as the dataSetIndex to undo all highlighting. + * @param stackIndex the index inside the stack - only relevant for stacked entries + */ + override fun highlightValue(x: Float, dataSetIndex: Int, stackIndex: Int) { + highlightValue(Highlight(x, dataSetIndex, stackIndex), false) + } + + override val barData: BarData + get() = mData + + /** + * Adds half of the bar width to each side of the x-axis range in order to allow the bars of the barchart to be + * fully displayed. + * Default: false + * + */ + fun setFitBars(enabled: Boolean) { + mFitBars = enabled + } + + /** + * Groups all BarDataSet objects this data object holds together by modifying the x-value of their entries. + * Previously set x-values of entries will be overwritten. Leaves space between bars and groups as specified + * by the parameters. + * Calls notifyDataSetChanged() afterwards. + * + * @param fromX the starting point on the x-axis where the grouping should begin + * @param groupSpace the space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f + * @param barSpace the space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f + */ + fun groupBars(fromX: Float, groupSpace: Float, barSpace: Float) { + barData.groupBars(fromX, groupSpace, barSpace) + notifyDataSetChanged() + } + + /** + * Used to enable rounded bar chart bars and set the radius of the rounded bars + * + * @param mRoundedBarRadius - the radius of the rounded bars + */ + fun setRoundedBarRadius(mRoundedBarRadius: Float) { + this.mRoundedBarRadius = mRoundedBarRadius + this.mDrawRoundedBars = true + init() + } + + override fun getAccessibilityDescription(): String { + val barData = barData + + val entryCount = barData.entryCount + + // Find the min and max index + val yAxisValueFormatter = axisLeft.valueFormatter + val minVal = yAxisValueFormatter!!.getFormattedValue(barData.yMin, null) + val maxVal = yAxisValueFormatter.getFormattedValue(barData.yMax, null) + + // Data range... + val xAxisValueFormatter = xAxis.valueFormatter + val minRange = xAxisValueFormatter!!.getFormattedValue(barData.xMin, null) + val maxRange = xAxisValueFormatter.getFormattedValue(barData.xMax, null) + + val entries = if (entryCount == 1) "entry" else "entries" + + // Format the values of min and max; to recite them back + return String.format( + Locale.getDefault(), "The bar chart has %d %s. " + + "The minimum value is %s and maximum value is %s." + + "Data ranges from %s to %s.", + entryCount, entries, minVal, maxVal, minRange, maxRange + ) + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java deleted file mode 100644 index 1510d85f0..000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java +++ /dev/null @@ -1,48 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.util.AttributeSet; - -import com.github.mikephil.charting.data.BubbleData; -import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; -import com.github.mikephil.charting.renderer.BubbleChartRenderer; - -/** - * The BubbleChart. Draws bubbles. Bubble chart implementation: Copyright 2015 - * Pierre-Marc Airoldi Licensed under Apache License 2.0. In the BubbleChart, it - * is the area of the bubble, not the radius or diameter of the bubble that - * conveys the data. - * - * @author Philipp Jahoda - */ -public class BubbleChart extends BarLineChartBase implements BubbleDataProvider { - - public BubbleChart(Context context) { - super(context); - } - - public BubbleChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public BubbleChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mRenderer = new BubbleChartRenderer(this, mAnimator, mViewPortHandler); - } - - public BubbleData getBubbleData() { - return mData; - } - - @Override - public String getAccessibilityDescription() { - return "This is bubble chart"; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.kt new file mode 100644 index 000000000..d11ada742 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.kt @@ -0,0 +1,34 @@ +package com.github.mikephil.charting.charts + +import android.content.Context +import android.util.AttributeSet +import com.github.mikephil.charting.data.BubbleData +import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider +import com.github.mikephil.charting.renderer.BubbleChartRenderer + +/** + * The BubbleChart. Draws bubbles. Bubble chart implementation: Copyright 2015 + * Pierre-Marc Airoldi Licensed under Apache License 2.0. In the BubbleChart, it + * is the area of the bubble, not the radius or diameter of the bubble that + * conveys the data. + */ +class BubbleChart : BarLineChartBase, BubbleDataProvider { + constructor(context: Context?) : super(context) + + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + + constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) + + protected override fun init() { + super.init() + + mRenderer = BubbleChartRenderer(this, mAnimator, mViewPortHandler) + } + + override val bubbleData: BubbleData? + get() = mData + + override fun getAccessibilityDescription(): String { + return "This is bubble chart" + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java deleted file mode 100644 index 29ba92d56..000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java +++ /dev/null @@ -1,49 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.util.AttributeSet; - -import com.github.mikephil.charting.data.CandleData; -import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; -import com.github.mikephil.charting.renderer.CandleStickChartRenderer; - -/** - * Financial chart type that draws candle-sticks (OHCL chart). - * - * @author Philipp Jahoda - */ -public class CandleStickChart extends BarLineChartBase implements CandleDataProvider { - - public CandleStickChart(Context context) { - super(context); - } - - public CandleStickChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public CandleStickChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mRenderer = new CandleStickChartRenderer(this, mAnimator, mViewPortHandler); - - getXAxis().setSpaceMin(0.5f); - getXAxis().setSpaceMax(0.5f); - } - - @Override - public CandleData getCandleData() { - return mData; - } - - @Override - public String getAccessibilityDescription() { - return "This is a candlestick chart"; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.kt new file mode 100644 index 000000000..3f36fb43c --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.kt @@ -0,0 +1,34 @@ +package com.github.mikephil.charting.charts + +import android.content.Context +import android.util.AttributeSet +import com.github.mikephil.charting.data.CandleData +import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider +import com.github.mikephil.charting.renderer.CandleStickChartRenderer + +/** + * Financial chart type that draws candle-sticks (OHCL chart). + */ +class CandleStickChart : BarLineChartBase, CandleDataProvider { + constructor(context: Context?) : super(context) + + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + + constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) + + protected override fun init() { + super.init() + + mRenderer = CandleStickChartRenderer(this, mAnimator, mViewPortHandler) + + xAxis.spaceMin = 0.5f + xAxis.spaceMax = 0.5f + } + + override val candleData: CandleData? + get() = mData + + override fun getAccessibilityDescription(): String { + return "This is a candlestick chart" + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java deleted file mode 100644 index 93c78295d..000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java +++ /dev/null @@ -1,336 +0,0 @@ -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.Log; - -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.highlight.HorizontalBarHighlighter; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer; -import com.github.mikephil.charting.renderer.XAxisRendererHorizontalBarChart; -import com.github.mikephil.charting.renderer.YAxisRendererHorizontalBarChart; -import com.github.mikephil.charting.utils.HorizontalViewPortHandler; -import com.github.mikephil.charting.utils.MPPointF; -import com.github.mikephil.charting.utils.TransformerHorizontalBarChart; -import com.github.mikephil.charting.utils.UtilsKtKt; - -/** - * BarChart with horizontal bar orientation. In this implementation, x- and y-axis are switched, meaning the YAxis class - * represents the horizontal values and the XAxis class represents the vertical values. - * - * @author Philipp Jahoda - */ -public class HorizontalBarChart extends BarChart { - - public HorizontalBarChart(Context context) { - super(context); - } - - public HorizontalBarChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public HorizontalBarChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - - mViewPortHandler = new HorizontalViewPortHandler(); - - super.init(); - - mLeftAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler); - mRightAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler); - - mRenderer = new HorizontalBarChartRenderer(this, mAnimator, mViewPortHandler); - setHighlighter(new HorizontalBarHighlighter(this)); - - mAxisRendererLeft = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisLeft, mLeftAxisTransformer); - mAxisRendererRight = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisRight, mRightAxisTransformer); - mXAxisRenderer = new XAxisRendererHorizontalBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer); - } - - private final RectF mOffsetsBuffer = new RectF(); - - protected void calculateLegendOffsets(RectF offsets) { - - offsets.left = 0.f; - offsets.right = 0.f; - offsets.top = 0.f; - offsets.bottom = 0.f; - - if (mLegend == null || !mLegend.isEnabled() || mLegend.isDrawInsideEnabled()) { - return; - } - - switch (mLegend.getOrientation()) { - case VERTICAL: - - switch (mLegend.getHorizontalAlignment()) { - case LEFT: - offsets.left += Math.min(mLegend.mNeededWidth, - mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) - + mLegend.getXOffset(); - break; - - case RIGHT: - offsets.right += Math.min(mLegend.mNeededWidth, - mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) - + mLegend.getXOffset(); - break; - - case CENTER: - - switch (mLegend.getVerticalAlignment()) { - case TOP: - offsets.top += Math.min(mLegend.mNeededHeight, - mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) - + mLegend.getYOffset(); - break; - - case BOTTOM: - offsets.bottom += Math.min(mLegend.mNeededHeight, - mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) - + mLegend.getYOffset(); - break; - - default: - break; - } - } - - break; - - case HORIZONTAL: - - switch (mLegend.getVerticalAlignment()) { - case TOP: - offsets.top += Math.min(mLegend.mNeededHeight, - mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) - + mLegend.getYOffset(); - - if (mAxisLeft.isEnabled() && mAxisLeft.isDrawLabelsEnabled()) { - offsets.top += mAxisLeft.getRequiredHeightSpace( - mAxisRendererLeft.getPaintAxisLabels()); - } - break; - - case BOTTOM: - offsets.bottom += Math.min(mLegend.mNeededHeight, - mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) - + mLegend.getYOffset(); - - if (mAxisRight.isEnabled() && mAxisRight.isDrawLabelsEnabled()) { - offsets.bottom += mAxisRight.getRequiredHeightSpace( - mAxisRendererRight.getPaintAxisLabels()); - } - break; - - default: - break; - } - break; - } - } - - @Override - public void calculateOffsets() { - - float offsetLeft = 0f, offsetRight = 0f, offsetTop = 0f, offsetBottom = 0f; - - calculateLegendOffsets(mOffsetsBuffer); - - offsetLeft += mOffsetsBuffer.left; - offsetTop += mOffsetsBuffer.top; - offsetRight += mOffsetsBuffer.right; - offsetBottom += mOffsetsBuffer.bottom; - - // offsets for y-labels - if (mAxisLeft.needsOffset()) { - offsetTop += mAxisLeft.getRequiredHeightSpace(mAxisRendererLeft.getPaintAxisLabels()); - } - - if (mAxisRight.needsOffset()) { - offsetBottom += mAxisRight.getRequiredHeightSpace(mAxisRendererRight.getPaintAxisLabels()); - } - - float xLabelWidth = mXAxis.mLabelWidth; - - if (mXAxis.isEnabled()) { - - // offsets for x-labels - if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - - offsetLeft += xLabelWidth; - - } else if (mXAxis.getPosition() == XAxisPosition.TOP) { - - offsetRight += xLabelWidth; - - } else if (mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - - offsetLeft += xLabelWidth; - offsetRight += xLabelWidth; - } - } - - offsetTop += getExtraTopOffset(); - offsetRight += getExtraRightOffset(); - offsetBottom += getExtraBottomOffset(); - offsetLeft += getExtraLeftOffset(); - - float minOffset = UtilsKtKt.convertDpToPixel(mMinOffset); - - mViewPortHandler.restrainViewPort( - Math.max(minOffset, offsetLeft), - Math.max(minOffset, offsetTop), - Math.max(minOffset, offsetRight), - Math.max(minOffset, offsetBottom)); - - if (mLogEnabled) { - Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop + ", offsetRight: " + - offsetRight + ", offsetBottom: " + offsetBottom); - Log.i(LOG_TAG, "Content: " + mViewPortHandler.getContentRect()); - } - - prepareOffsetMatrix(); - prepareValuePxMatrix(); - } - - @Override - protected void prepareValuePxMatrix() { - mRightAxisTransformer.prepareMatrixValuePx(mAxisRight.mAxisMinimum, mAxisRight.mAxisRange, mXAxis.mAxisRange, - mXAxis.mAxisMinimum); - mLeftAxisTransformer.prepareMatrixValuePx(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisRange, mXAxis.mAxisRange, - mXAxis.mAxisMinimum); - } - - @Override - protected float[] getMarkerPosition(Highlight high) { - return new float[]{high.getDrawY(), high.getDrawX()}; - } - - @Override - public void getBarBounds(BarEntry barEntry, RectF outputRect) { - - RectF bounds = outputRect; - IBarDataSet set = mData.getDataSetForEntry(barEntry); - - float y = barEntry.getY(); - float x = barEntry.getX(); - - float barWidth = mData.getBarWidth(); - - float top = x - barWidth / 2f; - float bottom = x + barWidth / 2f; - float left = y >= 0 ? y : 0; - float right = y <= 0 ? y : 0; - - bounds.set(left, top, right, bottom); - - getTransformer(set.getAxisDependency()).rectValueToPixel(bounds); - - } - - protected float[] mGetPositionBuffer = new float[2]; - - /** - * Returns a recyclable MPPointF instance. - */ - @Override - public MPPointF getPosition(Entry e, AxisDependency axis) { - - if (e == null) { - return null; - } - - float[] vals = mGetPositionBuffer; - vals[0] = e.getY(); - vals[1] = e.getX(); - - getTransformer(axis).pointValuesToPixel(vals); - - return MPPointF.Companion.getInstance(vals[0], vals[1]); - } - - /** - * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point - * inside the BarChart. - */ - @Override - public Highlight getHighlightByTouchPoint(float x, float y) { - - if (mData == null) { - if (mLogEnabled) { - Log.e(LOG_TAG, "Can't select by touch. No data set."); - } - return null; - } else { - return getHighlighter().getHighlight(y, x); // switch x and y - } - } - - @Override - public float getLowestVisibleX() { - getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), - mViewPortHandler.contentBottom(), posForGetLowestVisibleX); - return (float) Math.max(mXAxis.mAxisMinimum, posForGetLowestVisibleX.getY()); - } - - @Override - public float getHighestVisibleX() { - getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), - mViewPortHandler.contentTop(), posForGetHighestVisibleX); - return (float) Math.min(mXAxis.mAxisMaximum, posForGetHighestVisibleX.getY()); - } - - /** - * ###### VIEWPORT METHODS BELOW THIS ###### - */ - - @Override - public void setVisibleXRangeMaximum(float maxXRange) { - float xScale = mXAxis.mAxisRange / (maxXRange); - mViewPortHandler.setMinimumScaleY(xScale); - } - - @Override - public void setVisibleXRangeMinimum(float minXRange) { - float xScale = mXAxis.mAxisRange / (minXRange); - mViewPortHandler.setMaximumScaleY(xScale); - } - - @Override - public void setVisibleXRange(float minXRange, float maxXRange) { - float minScale = mXAxis.mAxisRange / minXRange; - float maxScale = mXAxis.mAxisRange / maxXRange; - mViewPortHandler.setMinMaxScaleY(minScale, maxScale); - } - - @Override - public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { - float yScale = getAxisRange(axis) / maxYRange; - mViewPortHandler.setMinimumScaleX(yScale); - } - - @Override - public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) { - float yScale = getAxisRange(axis) / minYRange; - mViewPortHandler.setMaximumScaleX(yScale); - } - - @Override - public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis) { - float minScale = getAxisRange(axis) / minYRange; - float maxScale = getAxisRange(axis) / maxYRange; - mViewPortHandler.setMinMaxScaleX(minScale, maxScale); - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.kt new file mode 100644 index 000000000..68877e44a --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.kt @@ -0,0 +1,310 @@ +package com.github.mikephil.charting.charts + +import android.content.Context +import android.graphics.RectF +import android.util.AttributeSet +import android.util.Log +import com.github.mikephil.charting.components.Legend.LegendHorizontalAlignment +import com.github.mikephil.charting.components.Legend.LegendOrientation +import com.github.mikephil.charting.components.Legend.LegendVerticalAlignment +import com.github.mikephil.charting.components.XAxis.XAxisPosition +import com.github.mikephil.charting.components.YAxis.AxisDependency +import com.github.mikephil.charting.data.BarEntry +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.highlight.HorizontalBarHighlighter +import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer +import com.github.mikephil.charting.renderer.XAxisRendererHorizontalBarChart +import com.github.mikephil.charting.renderer.YAxisRendererHorizontalBarChart +import com.github.mikephil.charting.utils.HorizontalViewPortHandler +import com.github.mikephil.charting.utils.MPPointF +import com.github.mikephil.charting.utils.MPPointF.Companion.getInstance +import com.github.mikephil.charting.utils.TransformerHorizontalBarChart +import com.github.mikephil.charting.utils.convertDpToPixel +import kotlin.math.max +import kotlin.math.min + +/** + * BarChart with horizontal bar orientation. In this implementation, x- and y-axis are switched, meaning the YAxis class + * represents the horizontal values and the XAxis class represents the vertical values. + * + * @author Philipp Jahoda + */ +class HorizontalBarChart : BarChart { + constructor(context: Context?) : super(context) + + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + + constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) + + override fun init() { + mViewPortHandler = HorizontalViewPortHandler() + + super.init() + + mLeftAxisTransformer = TransformerHorizontalBarChart(mViewPortHandler) + mRightAxisTransformer = TransformerHorizontalBarChart(mViewPortHandler) + + mRenderer = HorizontalBarChartRenderer(this, mAnimator, mViewPortHandler) + setHighlighter(HorizontalBarHighlighter(this)) + + mAxisRendererLeft = YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisLeft, mLeftAxisTransformer) + mAxisRendererRight = YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisRight, mRightAxisTransformer) + mXAxisRenderer = XAxisRendererHorizontalBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer) + } + + private val mOffsetsBuffer = RectF() + + override fun calculateLegendOffsets(offsets: RectF) { + offsets.left = 0f + offsets.right = 0f + offsets.top = 0f + offsets.bottom = 0f + + if (mLegend == null || !mLegend.isEnabled || mLegend.isDrawInsideEnabled) { + return + } + + when (mLegend.orientation) { + LegendOrientation.VERTICAL -> when (mLegend.horizontalAlignment) { + LegendHorizontalAlignment.LEFT -> offsets.left += min( + mLegend.mNeededWidth, + mViewPortHandler.chartWidth * mLegend.maxSizePercent + ) + mLegend.xOffset + + LegendHorizontalAlignment.RIGHT -> offsets.right += min( + mLegend.mNeededWidth, + mViewPortHandler.chartWidth * mLegend.maxSizePercent + ) + mLegend.xOffset + + LegendHorizontalAlignment.CENTER -> when (mLegend.verticalAlignment) { + LegendVerticalAlignment.TOP -> offsets.top += min( + mLegend.mNeededHeight, + mViewPortHandler.chartHeight * mLegend.maxSizePercent + ) + mLegend.yOffset + + LegendVerticalAlignment.BOTTOM -> offsets.bottom += min( + mLegend.mNeededHeight, + mViewPortHandler.chartHeight * mLegend.maxSizePercent + ) + mLegend.yOffset + + else -> {} + } + } + + LegendOrientation.HORIZONTAL -> when (mLegend.verticalAlignment) { + LegendVerticalAlignment.TOP -> { + offsets.top += min( + mLegend.mNeededHeight, + mViewPortHandler.chartHeight * mLegend.maxSizePercent + ) + mLegend.yOffset + + if (mAxisLeft.isEnabled && mAxisLeft.isDrawLabelsEnabled) { + offsets.top += mAxisLeft.getRequiredHeightSpace( + mAxisRendererLeft.paintAxisLabels + ) + } + } + + LegendVerticalAlignment.BOTTOM -> { + offsets.bottom += min( + mLegend.mNeededHeight, + mViewPortHandler.chartHeight * mLegend.maxSizePercent + ) + mLegend.yOffset + + if (mAxisRight.isEnabled && mAxisRight.isDrawLabelsEnabled) { + offsets.bottom += mAxisRight.getRequiredHeightSpace( + mAxisRendererRight.paintAxisLabels + ) + } + } + + else -> {} + } + } + } + + override fun calculateOffsets() { + var offsetLeft = 0f + var offsetRight = 0f + var offsetTop = 0f + var offsetBottom = 0f + + calculateLegendOffsets(mOffsetsBuffer) + + offsetLeft += mOffsetsBuffer.left + offsetTop += mOffsetsBuffer.top + offsetRight += mOffsetsBuffer.right + offsetBottom += mOffsetsBuffer.bottom + + // offsets for y-labels + if (mAxisLeft.needsOffset()) { + offsetTop += mAxisLeft.getRequiredHeightSpace(mAxisRendererLeft.paintAxisLabels) + } + + if (mAxisRight.needsOffset()) { + offsetBottom += mAxisRight.getRequiredHeightSpace(mAxisRendererRight.paintAxisLabels) + } + + val xLabelWidth = mXAxis.mLabelWidth.toFloat() + + if (mXAxis.isEnabled) { + // offsets for x-labels + + if (mXAxis.position == XAxisPosition.BOTTOM) { + offsetLeft += xLabelWidth + } else if (mXAxis.position == XAxisPosition.TOP) { + offsetRight += xLabelWidth + } else if (mXAxis.position == XAxisPosition.BOTH_SIDED) { + offsetLeft += xLabelWidth + offsetRight += xLabelWidth + } + } + + offsetTop += extraTopOffset + offsetRight += extraRightOffset + offsetBottom += extraBottomOffset + offsetLeft += extraLeftOffset + + val minOffset = mMinOffset.convertDpToPixel() + + mViewPortHandler.restrainViewPort( + max(minOffset, offsetLeft), + max(minOffset, offsetTop), + max(minOffset, offsetRight), + max(minOffset, offsetBottom) + ) + + if (mLogEnabled) { + Log.i( + LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop + ", offsetRight: " + + offsetRight + ", offsetBottom: " + offsetBottom + ) + Log.i(LOG_TAG, "Content: " + mViewPortHandler.contentRect) + } + + prepareOffsetMatrix() + prepareValuePxMatrix() + } + + override fun prepareValuePxMatrix() { + mRightAxisTransformer.prepareMatrixValuePx( + mAxisRight.mAxisMinimum, mAxisRight.mAxisRange, mXAxis.mAxisRange, + mXAxis.mAxisMinimum + ) + mLeftAxisTransformer.prepareMatrixValuePx( + mAxisLeft.mAxisMinimum, mAxisLeft.mAxisRange, mXAxis.mAxisRange, + mXAxis.mAxisMinimum + ) + } + + override fun getMarkerPosition(high: Highlight): FloatArray { + return floatArrayOf(high.drawY, high.drawX) + } + + override fun getBarBounds(barEntry: BarEntry, outputRect: RectF) { + val bounds = outputRect + val set = mData.getDataSetForEntry(barEntry) + + val y = barEntry.y + val x = barEntry.x + + val barWidth = mData.barWidth + + val top = x - barWidth / 2f + val bottom = x + barWidth / 2f + val left = if (y >= 0) y else 0f + val right = if (y <= 0) y else 0f + + bounds.set(left, top, right, bottom) + + getTransformer(set!!.axisDependency)!!.rectValueToPixel(bounds) + } + + protected var mGetPositionBuffer: FloatArray = FloatArray(2) + + /** + * Returns a recyclable MPPointF instance. + */ + override fun getPosition(e: Entry?, axis: AxisDependency?): MPPointF? { + if (e == null) { + return null + } + + val vals = mGetPositionBuffer + vals[0] = e.y + vals[1] = e.x + + getTransformer(axis)!!.pointValuesToPixel(vals) + + return getInstance(vals[0], vals[1]) + } + + /** + * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point + * inside the BarChart. + */ + override fun getHighlightByTouchPoint(x: Float, y: Float): Highlight? { + if (mData == null) { + if (mLogEnabled) { + Log.e(LOG_TAG, "Can't select by touch. No data set.") + } + return null + } else { + return highlighter.getHighlight(y, x) // switch x and y + } + } + + override val lowestVisibleX: Float + get() { + getTransformer(AxisDependency.LEFT)!!.getValuesByTouchPoint( + mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), posForGetLowestVisibleX + ) + return max(mXAxis.mAxisMinimum.toDouble(), posForGetLowestVisibleX.y).toFloat() + } + + override val highestVisibleX: Float + get() { + getTransformer(AxisDependency.LEFT)!!.getValuesByTouchPoint( + mViewPortHandler.contentLeft(), + mViewPortHandler.contentTop(), posForGetHighestVisibleX + ) + return min(mXAxis.mAxisMaximum.toDouble(), posForGetHighestVisibleX.y).toFloat() + } + + /** + * ###### VIEWPORT METHODS BELOW THIS ###### + */ + override fun setVisibleXRangeMaximum(maxXRange: Float) { + val xScale = mXAxis.mAxisRange / (maxXRange) + mViewPortHandler.setMinimumScaleY(xScale) + } + + override fun setVisibleXRangeMinimum(minXRange: Float) { + val xScale = mXAxis.mAxisRange / (minXRange) + mViewPortHandler.setMaximumScaleY(xScale) + } + + override fun setVisibleXRange(minXRange: Float, maxXRange: Float) { + val minScale = mXAxis.mAxisRange / minXRange + val maxScale = mXAxis.mAxisRange / maxXRange + mViewPortHandler.setMinMaxScaleY(minScale, maxScale) + } + + override fun setVisibleYRangeMaximum(maxYRange: Float, axis: AxisDependency?) { + val yScale = getAxisRange(axis) / maxYRange + mViewPortHandler.setMinimumScaleX(yScale) + } + + override fun setVisibleYRangeMinimum(minYRange: Float, axis: AxisDependency?) { + val yScale = getAxisRange(axis) / minYRange + mViewPortHandler.setMaximumScaleX(yScale) + } + + override fun setVisibleYRange(minYRange: Float, maxYRange: Float, axis: AxisDependency?) { + val minScale = getAxisRange(axis) / minYRange + val maxScale = getAxisRange(axis) / maxYRange + mViewPortHandler.setMinMaxScaleX(minScale, maxScale) + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java deleted file mode 100644 index 3dcaa9521..000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java +++ /dev/null @@ -1,791 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.RectF; -import android.graphics.Typeface; -import android.text.TextUtils; -import android.util.AttributeSet; - -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.data.PieData; -import com.github.mikephil.charting.data.PieEntry; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.highlight.PieHighlighter; -import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; -import com.github.mikephil.charting.renderer.PieChartRenderer; -import com.github.mikephil.charting.utils.MPPointF; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.UtilsKtKt; - -import java.util.List; -import java.util.Locale; -import java.util.Objects; - -import androidx.annotation.NonNull; - -/** - * View that represents a pie chart. Draws cake like slices. - * - * @author Philipp Jahoda - */ -@SuppressWarnings("unused") -public class PieChart extends PieRadarChartBase { - - /** - * rect object that represents the bounds of the piechart, needed for - * drawing the circle - */ - private final RectF mCircleBox = new RectF(); - - /** - * flag indicating if entry labels should be drawn or not - */ - private boolean mDrawEntryLabels = true; - - /** - * array that holds the width of each pie-slice in degrees - */ - private float[] mDrawAngles = new float[1]; - - /** - * array that holds the absolute angle in degrees of each slice - */ - private float[] mAbsoluteAngles = new float[1]; - - /** - * if true, the white hole inside the chart will be drawn - */ - private boolean mDrawHole = true; - - /** - * if true, the hole will see-through to the inner tips of the slices - */ - private boolean mDrawSlicesUnderHole = false; - - /** - * if true, the values inside the piechart are drawn as percent values - */ - private boolean mUsePercentValues = false; - - /** - * if true, the slices of the piechart are rounded - */ - private boolean mDrawRoundedSlices = false; - - /** - * variable for the text that is drawn in the center of the pie-chart - */ - private CharSequence mCenterText = ""; - - private final MPPointF mCenterTextOffset = MPPointF.Companion.getInstance(0, 0); - - /** - * indicates the size of the hole in the center of the piechart, - * default: radius / 2 - */ - private float mHoleRadiusPercent = 50f; - - /** - * the radius of the transparent circle next to the chart-hole in the center - */ - protected float mTransparentCircleRadiusPercent = 55f; - - /** - * if enabled, centertext is drawn - */ - private boolean mDrawCenterText = true; - - private float mCenterTextRadiusPercent = 100.f; - - protected float mMaxAngle = 360f; - - /** - * Minimum angle to draw slices, this only works if there is enough room for all slices to have - * the minimum angle, default 0f. - */ - private float mMinAngleForSlices = 0f; - - public PieChart(Context context) { - super(context); - } - - public PieChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public PieChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mRenderer = new PieChartRenderer(this, mAnimator, mViewPortHandler); - mXAxis = null; - - mHighlighter = new PieHighlighter(this); - } - - @Override - protected void onDraw(@NonNull Canvas canvas) { - super.onDraw(canvas); - - if (mData == null) - return; - - mRenderer.drawData(canvas); - - if (valuesToHighlight()) - mRenderer.drawHighlighted(canvas, mIndicesToHighlight); - - mRenderer.drawExtras(canvas); - - mRenderer.drawValues(canvas); - - mLegendRenderer.renderLegend(canvas); - - drawDescription(canvas); - - drawMarkers(canvas); - } - - @Override - public void calculateOffsets() { - super.calculateOffsets(); - - // prevent nullpointer when no data set - if (mData == null) - return; - - float diameter = getDiameter(); - float radius = diameter / 2f; - - MPPointF c = getCenterOffsets(); - - float shift = mData.getDataSet().getSelectionShift(); - - // create the circle box that will contain the pie-chart (the bounds of - // the pie-chart) - mCircleBox.set(c.getX() - radius + shift, - c.getY() - radius + shift, - c.getX() + radius - shift, - c.getY() + radius - shift); - - MPPointF.recycleInstance(c); - } - - @Override - protected void calcMinMax() { - calcAngles(); - } - - @Override - protected float[] getMarkerPosition(Highlight highlight) { - - MPPointF center = getCenterCircleBox(); - float r = getRadius(); - - float off = r / 10f * 3.6f; - - if (isDrawHoleEnabled()) { - off = (r - (r / 100f * getHoleRadius())) / 2f; - } - - r -= off; // offset to keep things inside the chart - - float rotationAngle = getRotationAngle(); - - int entryIndex = (int) highlight.getX(); - - // offset needed to center the drawn text in the slice - float offset = mDrawAngles[entryIndex] / 2; - - // calculate the text position - float x = (float) (r - * Math.cos(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset) - * mAnimator.getPhaseY())) + center.getX()); - float y = (float) (r - * Math.sin(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset) - * mAnimator.getPhaseY())) + center.getY()); - - MPPointF.recycleInstance(center); - return new float[]{x, y}; - } - - /** - * calculates the needed angles for the chart slices - */ - private void calcAngles() { - - int entryCount = mData.getEntryCount(); - - if (mDrawAngles.length != entryCount) { - mDrawAngles = new float[entryCount]; - } else { - for (int i = 0; i < entryCount; i++) { - mDrawAngles[i] = 0; - } - } - if (mAbsoluteAngles.length != entryCount) { - mAbsoluteAngles = new float[entryCount]; - } else { - for (int i = 0; i < entryCount; i++) { - mAbsoluteAngles[i] = 0; - } - } - - float yValueSum = mData.getYValueSum(); - - List dataSets = mData.getDataSets(); - - boolean hasMinAngle = mMinAngleForSlices != 0f && entryCount * mMinAngleForSlices <= mMaxAngle; - float[] minAngles = new float[entryCount]; - - int cnt = 0; - float offset = 0f; - float diff = 0f; - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - IPieDataSet set = dataSets.get(i); - - for (int j = 0; j < set.getEntryCount(); j++) { - - float drawAngle = calcAngle(Math.abs(set.getEntryForIndex(j).getY()), yValueSum); - - if (hasMinAngle) { - float temp = drawAngle - mMinAngleForSlices; - if (temp <= 0) { - minAngles[cnt] = mMinAngleForSlices; - offset -= temp; - } else { - minAngles[cnt] = drawAngle; - diff += temp; - } - } - - mDrawAngles[cnt] = drawAngle; - - if (cnt == 0) { - mAbsoluteAngles[cnt] = mDrawAngles[cnt]; - } else { - mAbsoluteAngles[cnt] = mAbsoluteAngles[cnt - 1] + mDrawAngles[cnt]; - } - - cnt++; - } - } - - if (hasMinAngle) { - // Correct bigger slices by relatively reducing their angles based on the total angle needed to subtract - // This requires that `entryCount * mMinAngleForSlices <= mMaxAngle` be true to properly work! - for (int i = 0; i < entryCount; i++) { - minAngles[i] -= (minAngles[i] - mMinAngleForSlices) / diff * offset; - if (i == 0) { - mAbsoluteAngles[0] = minAngles[0]; - } else { - mAbsoluteAngles[i] = mAbsoluteAngles[i - 1] + minAngles[i]; - } - } - - mDrawAngles = minAngles; - } - } - - /** - * Checks if the given index is set to be highlighted. - * - */ - public boolean needsHighlight(int index) { - - // no highlight - if (!valuesToHighlight()) - return false; - - // check if the xvalue for the given dataset needs highlight - for (Highlight highlight : mIndicesToHighlight) - if ((int) highlight.getX() == index) - return true; - - return false; - } - - /** - * calculates the needed angle for a given value - * - */ - private float calcAngle(float value) { - return calcAngle(value, mData.getYValueSum()); - } - - /** - * calculates the needed angle for a given value - * - */ - private float calcAngle(float value, float yValueSum) { - return value / yValueSum * mMaxAngle; - } - - /** - * This will throw an exception, PieChart has no XAxis object. - * - */ - @Deprecated - @Override - public XAxis getXAxis() { - throw new RuntimeException("PieChart has no XAxis"); - } - - @Override - public int getIndexForAngle(float angle) { - - // take the current angle of the chart into consideration - float a = Utils.getNormalizedAngle(angle - getRotationAngle()); - - for (int i = 0; i < mAbsoluteAngles.length; i++) { - if (mAbsoluteAngles[i] > a) - return i; - } - - return -1; // return -1 if no index found - } - - /** - * Returns the index of the DataSet this x-index belongs to. - * - */ - public int getDataSetIndexForIndex(int xIndex) { - - List dataSets = mData.getDataSets(); - - for (int i = 0; i < dataSets.size(); i++) { - if (dataSets.get(i).getEntryForXValue(xIndex, Float.NaN) != null) - return i; - } - - return -1; - } - - /** - * returns an integer array of all the different angles the chart slices - * have the angles in the returned array determine how much space (of 360°) - * each slice takes - * - */ - public float[] getDrawAngles() { - return mDrawAngles; - } - - /** - * returns the absolute angles of the different chart slices (where the - * slices end) - * - */ - public float[] getAbsoluteAngles() { - return mAbsoluteAngles; - } - - /** - * Sets the color for the hole that is drawn in the center of the PieChart - * (if enabled). - * - */ - public void setHoleColor(int color) { - ((PieChartRenderer) mRenderer).getPaintHole().setColor(color); - } - - /** - * Enable or disable the visibility of the inner tips of the slices behind the hole - */ - public void setDrawSlicesUnderHole(boolean enable) { - mDrawSlicesUnderHole = enable; - } - - /** - * Returns true if the inner tips of the slices are visible behind the hole, - * false if not. - * - * @return true if slices are visible behind the hole. - */ - public boolean isDrawSlicesUnderHoleEnabled() { - return mDrawSlicesUnderHole; - } - - /** - * set this to true to draw the pie center empty - * - */ - public void setDrawHoleEnabled(boolean enabled) { - this.mDrawHole = enabled; - } - - /** - * returns true if the hole in the center of the pie-chart is set to be - * visible, false if not - * - */ - public boolean isDrawHoleEnabled() { - return mDrawHole; - } - - /** - * Sets the text String that is displayed in the center of the PieChart. - * - */ - public void setCenterText(CharSequence text) { - mCenterText = Objects.requireNonNullElse(text, ""); - } - - /** - * returns the text that is drawn in the center of the pie-chart - * - */ - public CharSequence getCenterText() { - return mCenterText; - } - - /** - * set this to true to draw the text that is displayed in the center of the - * pie chart - * - */ - public void setDrawCenterText(boolean enabled) { - this.mDrawCenterText = enabled; - } - - /** - * returns true if drawing the center text is enabled - * - */ - public boolean isDrawCenterTextEnabled() { - return mDrawCenterText; - } - - @Override - protected float getRequiredLegendOffset() { - return mLegendRenderer.getLabelPaint().getTextSize() * 2.f; - } - - @Override - protected float getRequiredBaseOffset() { - return 0; - } - - @Override - public float getRadius() { - if (mCircleBox == null) - return 0; - else - return Math.min(mCircleBox.width() / 2f, mCircleBox.height() / 2f); - } - - /** - * returns the circlebox, the boundingbox of the pie-chart slices - * - */ - public RectF getCircleBox() { - return mCircleBox; - } - - /** - * returns the center of the circlebox - * - */ - public MPPointF getCenterCircleBox() { - return MPPointF.Companion.getInstance(mCircleBox.centerX(), mCircleBox.centerY()); - } - - /** - * sets the typeface for the center-text paint - * - */ - public void setCenterTextTypeface(Typeface t) { - ((PieChartRenderer) mRenderer).getPaintCenterText().setTypeface(t); - } - - /** - * Sets the size of the center text of the PieChart in dp. - * - */ - public void setCenterTextSize(float sizeDp) { - ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize( - UtilsKtKt.convertDpToPixel(sizeDp)); - } - - /** - * Sets the size of the center text of the PieChart in pixels. - * - */ - public void setCenterTextSizePixels(float sizePixels) { - ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize(sizePixels); - } - - /** - * Sets the offset the center text should have from it's original position in dp. Default x = 0, y = 0 - * - */ - public void setCenterTextOffset(float x, float y) { - mCenterTextOffset.setX(UtilsKtKt.convertDpToPixel(x)); - mCenterTextOffset.setY(UtilsKtKt.convertDpToPixel(y)); - } - - /** - * Returns the offset on the x- and y-axis the center text has in dp. - * - */ - public MPPointF getCenterTextOffset() { - return MPPointF.Companion.getInstance(mCenterTextOffset.getX(), mCenterTextOffset.getY()); - } - - /** - * Sets the color of the center text of the PieChart. - * - */ - public void setCenterTextColor(int color) { - ((PieChartRenderer) mRenderer).getPaintCenterText().setColor(color); - } - - /** - * sets the radius of the hole in the center of the piechart in percent of - * the maximum radius (max = the radius of the whole chart), default 50% - * - */ - public void setHoleRadius(final float percent) { - mHoleRadiusPercent = percent; - } - - /** - * Returns the size of the hole radius in percent of the total radius. - * - */ - public float getHoleRadius() { - return mHoleRadiusPercent; - } - - /** - * Sets the color the transparent-circle should have. - * - */ - public void setTransparentCircleColor(int color) { - - Paint p = ((PieChartRenderer) mRenderer).getPaintTransparentCircle(); - int alpha = p.getAlpha(); - p.setColor(color); - p.setAlpha(alpha); - } - - /** - * sets the radius of the transparent circle that is drawn next to the hole - * in the piechart in percent of the maximum radius (max = the radius of the - * whole chart), default 55% -> means 5% larger than the center-hole by - * default - * - */ - public void setTransparentCircleRadius(final float percent) { - mTransparentCircleRadiusPercent = percent; - } - - public float getTransparentCircleRadius() { - return mTransparentCircleRadiusPercent; - } - - /** - * Sets the amount of transparency the transparent circle should have 0 = fully transparent, - * 255 = fully opaque. - * Default value is 100. - * - * @param alpha 0-255 - */ - public void setTransparentCircleAlpha(int alpha) { - ((PieChartRenderer) mRenderer).getPaintTransparentCircle().setAlpha(alpha); - } - - /** - * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class). - * Deprecated -> use setDrawEntryLabels(...) instead. - * - */ - @Deprecated - public void setDrawSliceText(boolean enabled) { - mDrawEntryLabels = enabled; - } - - /** - * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class). - * - */ - public void setDrawEntryLabels(boolean enabled) { - mDrawEntryLabels = enabled; - } - - /** - * Returns true if drawing the entry labels is enabled, false if not. - * - */ - public boolean isDrawEntryLabelsEnabled() { - return mDrawEntryLabels; - } - - /** - * Sets the color the entry labels are drawn with. - * - */ - public void setEntryLabelColor(int color) { - ((PieChartRenderer) mRenderer).getPaintEntryLabels().setColor(color); - } - - /** - * Sets a custom Typeface for the drawing of the entry labels. - * - */ - public void setEntryLabelTypeface(Typeface tf) { - ((PieChartRenderer) mRenderer).getPaintEntryLabels().setTypeface(tf); - } - - /** - * Sets the size of the entry labels in dp. Default: 13dp - * - */ - public void setEntryLabelTextSize(float size) { - ((PieChartRenderer) mRenderer).getPaintEntryLabels().setTextSize(UtilsKtKt.convertDpToPixel(size)); - } - - /** - * Sets whether to draw slices in a curved fashion, only works if drawing the hole is enabled - * and if the slices are not drawn under the hole. - * - * @param enabled draw curved ends of slices - */ - public void setDrawRoundedSlices(boolean enabled) { - mDrawRoundedSlices = enabled; - } - - /** - * Returns true if the chart is set to draw each end of a pie-slice - * "rounded". - * - */ - public boolean isDrawRoundedSlicesEnabled() { - return mDrawRoundedSlices; - } - - /** - * If this is enabled, values inside the PieChart are drawn in percent and - * not with their original value. Values provided for the IValueFormatter to - * format are then provided in percent. - * - */ - public void setUsePercentValues(boolean enabled) { - mUsePercentValues = enabled; - } - - /** - * Returns true if using percentage values is enabled for the chart. - * - */ - public boolean isUsePercentValuesEnabled() { - return mUsePercentValues; - } - - /** - * the rectangular radius of the bounding box for the center text, as a percentage of the pie - * hole - * default 1.f (100%) - */ - public void setCenterTextRadiusPercent(float percent) { - mCenterTextRadiusPercent = percent; - } - - /** - * the rectangular radius of the bounding box for the center text, as a percentage of the pie - * hole - * default 1.f (100%) - */ - public float getCenterTextRadiusPercent() { - return mCenterTextRadiusPercent; - } - - public float getMaxAngle() { - return mMaxAngle; - } - - /** - * Sets the max angle that is used for calculating the pie-circle. 360f means - * it's a full PieChart, 180f results in a half-pie-chart. Default: 360f - * - * @param maxangle min 90, max 360 - */ - public void setMaxAngle(float maxangle) { - - if (maxangle > 360) - maxangle = 360f; - - if (maxangle < 90) - maxangle = 90f; - - this.mMaxAngle = maxangle; - } - - /** - * The minimum angle slices on the chart are rendered with, default is 0f. - * - * @return minimum angle for slices - */ - public float getMinAngleForSlices() { - return mMinAngleForSlices; - } - - /** - * Set the angle to set minimum size for slices, you must call {@link #notifyDataSetChanged()} - * and {@link #invalidate()} when changing this, only works if there is enough room for all - * slices to have the minimum angle. - * - * @param minAngle minimum 0, maximum is half of {@link #setMaxAngle(float)} - */ - public void setMinAngleForSlices(float minAngle) { - - if (minAngle > (mMaxAngle / 2f)) - minAngle = mMaxAngle / 2f; - else if (minAngle < 0) - minAngle = 0f; - - this.mMinAngleForSlices = minAngle; - } - - @Override - protected void onDetachedFromWindow() { - // releases the bitmap in the renderer to avoid oom error - if (mRenderer != null && mRenderer instanceof PieChartRenderer) { - ((PieChartRenderer) mRenderer).releaseBitmap(); - } - super.onDetachedFromWindow(); - } - - @Override - public String getAccessibilityDescription() { - - PieData pieData = getData(); - - int entryCount = 0; - if (pieData != null) - entryCount = pieData.getEntryCount(); - - StringBuilder builder = new StringBuilder(); - - builder.append(String.format(Locale.getDefault(), "The pie chart has %d entries.", entryCount)); - - for (int i = 0; i < entryCount; i++) { - PieEntry entry = pieData.getDataSet().getEntryForIndex(i); - float percentage = (entry.getValue() / pieData.getYValueSum()) * 100; - builder.append(String.format(Locale.getDefault(), "%s has %.2f percent pie taken", - (TextUtils.isEmpty(entry.label) ? "No Label" : entry.label), - percentage)); - } - - return builder.toString(); - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.kt new file mode 100644 index 000000000..6c77a1fb9 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.kt @@ -0,0 +1,616 @@ +package com.github.mikephil.charting.charts + +import android.content.Context +import android.graphics.Canvas +import android.graphics.RectF +import android.graphics.Typeface +import android.text.TextUtils +import android.util.AttributeSet +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.data.PieData +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.highlight.PieHighlighter +import com.github.mikephil.charting.renderer.PieChartRenderer +import com.github.mikephil.charting.utils.MPPointF +import com.github.mikephil.charting.utils.MPPointF.Companion.getInstance +import com.github.mikephil.charting.utils.MPPointF.Companion.recycleInstance +import com.github.mikephil.charting.utils.Utils +import com.github.mikephil.charting.utils.convertDpToPixel +import java.util.Locale +import java.util.Objects +import kotlin.math.abs +import kotlin.math.cos +import kotlin.math.min +import kotlin.math.sin + +/** + * View that represents a pie chart. Draws cake like slices. + */ +@Suppress("unused") +class PieChart : PieRadarChartBase { + /** + * rect object that represents the bounds of the piechart, needed for + * drawing the circle + */ + val circleBox: RectF = RectF() + + /** + * flag indicating if entry labels should be drawn or not + */ + var isDrawEntryLabelsEnabled: Boolean = true + private set + + /** + * returns an integer array of all the different angles the chart slices + * have the angles in the returned array determine how much space (of 360°) + * each slice takes + * + */ + var drawAngles: FloatArray = FloatArray(1) + private set + + /** + * returns the absolute angles of the different chart slices (where the + * slices end) + */ + var absoluteAngles: FloatArray = FloatArray(1) + private set + + /** + * returns true if the hole in the center of the pie-chart is set to be + * visible, false if not + */ + var isDrawHoleEnabled: Boolean = true + + /** + * Returns true if the inner tips of the slices are visible behind the hole, + * false if not. + * + * @return true if slices are visible behind the hole. + */ + var isDrawSlicesUnderHoleEnabled: Boolean = false + private set + + /** + * Returns true if using percentage values is enabled for the chart. + */ + var isUsePercentValuesEnabled: Boolean = false + private set + + /** + * Returns true if the chart is set to draw each end of a pie-slice + * "rounded". + */ + var isDrawRoundedSlicesEnabled: Boolean = false + private set + + /** + * variable for the text that is drawn in the center of the pie-chart + */ + private var mCenterText: CharSequence = "" + + private val mCenterTextOffset = getInstance(0f, 0f) + + /** + * sets the radius of the hole in the center of the piechart in percent of + * the maximum radius (max = the radius of the whole chart), default 50% + * + * indicates the size of the hole in the center of the piechart, + * default: radius / 2 + */ + var holeRadius: Float = 50f + + /** + * sets the radius of the transparent circle that is drawn next to the hole + * in the piechart in percent of the maximum radius (max = the radius of the + * whole chart), default 55% -> means 5% larger than the center-hole by + * default + */ + var transparentCircleRadius: Float = 55f + + /** + * returns true if drawing the center text is enabled + */ + var isDrawCenterTextEnabled: Boolean = true + private set + + /** + * the rectangular radius of the bounding box for the center text, as a percentage of the pie + * hole + * default 1.f (100%) + */ + /** + * the rectangular radius of the bounding box for the center text, as a percentage of the pie + * hole + * default 1.f (100%) + */ + var centerTextRadiusPercent: Float = 100f + + protected var mMaxAngle: Float = 360f + + /** + * Minimum angle to draw slices, this only works if there is enough room for all slices to have + * the minimum angle, default 0f. + */ + private var mMinAngleForSlices = 0f + + constructor(context: Context?) : super(context) + + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + + constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) + + override fun init() { + super.init() + + mRenderer = PieChartRenderer(this, mAnimator, mViewPortHandler) + mXAxis = null + + mHighlighter = PieHighlighter(this) + } + + protected override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + if (mData == null) return + + mRenderer.drawData(canvas) + + if (valuesToHighlight()) mRenderer.drawHighlighted(canvas, mIndicesToHighlight) + + mRenderer.drawExtras(canvas) + + mRenderer.drawValues(canvas) + + mLegendRenderer.renderLegend(canvas) + + drawDescription(canvas) + + drawMarkers(canvas) + } + + override fun calculateOffsets() { + super.calculateOffsets() + + // prevent nullpointer when no data set + if (mData == null) return + + val diameter = diameter + val radius = diameter / 2f + + val c = centerOffsets + + val shift = mData.dataSet.selectionShift + + // create the circle box that will contain the pie-chart (the bounds of + // the pie-chart) + circleBox.set( + c.x - radius + shift, + c.y - radius + shift, + c.x + radius - shift, + c.y + radius - shift + ) + + recycleInstance(c) + } + + override fun calcMinMax() { + calcAngles() + } + + override fun getMarkerPosition(highlight: Highlight): FloatArray { + val center = this.centerCircleBox + var r = radius + + var off = r / 10f * 3.6f + + if (this.isDrawHoleEnabled) { + off = (r - (r / 100f * this.holeRadius)) / 2f + } + + r -= off // offset to keep things inside the chart + + val rotationAngle = rotationAngle + + val entryIndex = highlight.x.toInt() + + // offset needed to center the drawn text in the slice + val offset = this.drawAngles[entryIndex] / 2 + + // calculate the text position + val x = (r + * cos( + Math.toRadians( + ((rotationAngle + this.absoluteAngles[entryIndex] - offset) + * mAnimator.phaseY).toDouble() + ) + ) + center.x).toFloat() + val y = (r + * sin( + Math.toRadians( + ((rotationAngle + this.absoluteAngles[entryIndex] - offset) + * mAnimator.phaseY).toDouble() + ) + ) + center.y).toFloat() + + recycleInstance(center) + return floatArrayOf(x, y) + } + + /** + * calculates the needed angles for the chart slices + */ + private fun calcAngles() { + val entryCount = mData.entryCount + + if (drawAngles.size != entryCount) { + this.drawAngles = FloatArray(entryCount) + } else { + for (i in 0.. a) return i + } + + return -1 // return -1 if no index found + } + + /** + * Returns the index of the DataSet this x-index belongs to. + */ + fun getDataSetIndexForIndex(xIndex: Int): Int { + val dataSets = mData.dataSets + + for (i in dataSets!!.indices) { + if (dataSets.get(i).getEntryForXValue(xIndex.toFloat(), Float.NaN) != null) return i + } + + return -1 + } + + /** + * Sets the color for the hole that is drawn in the center of the PieChart + * (if enabled). + */ + fun setHoleColor(color: Int) { + (mRenderer as PieChartRenderer).paintHole.color = color + } + + /** + * Enable or disable the visibility of the inner tips of the slices behind the hole + */ + fun setDrawSlicesUnderHole(enable: Boolean) { + this.isDrawSlicesUnderHoleEnabled = enable + } + + var centerText: CharSequence? + /** + * returns the text that is drawn in the center of the pie-chart + */ + get() = mCenterText + /** + * Sets the text String that is displayed in the center of the PieChart. + */ + set(text) { + mCenterText = Objects.requireNonNullElse(text, "") + } + + /** + * set this to true to draw the text that is displayed in the center of the + * pie chart + */ + fun setDrawCenterText(enabled: Boolean) { + this.isDrawCenterTextEnabled = enabled + } + + override val requiredLegendOffset: Float + get() = mLegendRenderer.labelPaint.textSize * 2f + + override val requiredBaseOffset: Float + get() = 0f + + override val radius: Float + get() { + return min(circleBox.width() / 2f, circleBox.height() / 2f) + } + + val centerCircleBox: MPPointF + get() = getInstance(circleBox.centerX(), circleBox.centerY()) + + fun setCenterTextTypeface(t: Typeface?) { + (mRenderer as PieChartRenderer).paintCenterText.typeface = t + } + + /** + * Sets the size of the center text of the PieChart in dp. + */ + fun setCenterTextSize(sizeDp: Float) { + (mRenderer as PieChartRenderer).paintCenterText.textSize = sizeDp.convertDpToPixel() + } + + /** + * Sets the size of the center text of the PieChart in pixels. + */ + fun setCenterTextSizePixels(sizePixels: Float) { + (mRenderer as PieChartRenderer).paintCenterText.textSize = sizePixels + } + + /** + * Sets the offset the center text should have from it's original position in dp. Default x = 0, y = 0 + */ + fun setCenterTextOffset(x: Float, y: Float) { + mCenterTextOffset.x = x.convertDpToPixel() + mCenterTextOffset.y = y.convertDpToPixel() + } + + val centerTextOffset: MPPointF + /** + * Returns the offset on the x- and y-axis the center text has in dp. + */ + get() = getInstance(mCenterTextOffset.x, mCenterTextOffset.y) + + /** + * Sets the color of the center text of the PieChart. + */ + fun setCenterTextColor(color: Int) { + (mRenderer as PieChartRenderer).paintCenterText.color = color + } + + /** + * Sets the color the transparent-circle should have. + */ + fun setTransparentCircleColor(color: Int) { + val p = (mRenderer as PieChartRenderer).paintTransparentCircle + val alpha = p.alpha + p.color = color + p.alpha = alpha + } + + /** + * Sets the amount of transparency the transparent circle should have 0 = fully transparent, + * 255 = fully opaque. + * Default value is 100. + * + * @param alpha 0-255 + */ + fun setTransparentCircleAlpha(alpha: Int) { + (mRenderer as PieChartRenderer).paintTransparentCircle.alpha = alpha + } + + /** + * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class). + * Deprecated -> use setDrawEntryLabels(...) instead. + * + */ + @Deprecated("") + fun setDrawSliceText(enabled: Boolean) { + this.isDrawEntryLabelsEnabled = enabled + } + + /** + * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class). + * + */ + fun setDrawEntryLabels(enabled: Boolean) { + this.isDrawEntryLabelsEnabled = enabled + } + + /** + * Sets the color the entry labels are drawn with. + * + */ + fun setEntryLabelColor(color: Int) { + (mRenderer as PieChartRenderer).paintEntryLabels.color = color + } + + /** + * Sets a custom Typeface for the drawing of the entry labels. + * + */ + fun setEntryLabelTypeface(tf: Typeface?) { + (mRenderer as PieChartRenderer).paintEntryLabels.typeface = tf + } + + /** + * Sets the size of the entry labels in dp. Default: 13dp + * + */ + fun setEntryLabelTextSize(size: Float) { + (mRenderer as PieChartRenderer).paintEntryLabels.textSize = size.convertDpToPixel() + } + + /** + * Sets whether to draw slices in a curved fashion, only works if drawing the hole is enabled + * and if the slices are not drawn under the hole. + * + * @param enabled draw curved ends of slices + */ + fun setDrawRoundedSlices(enabled: Boolean) { + this.isDrawRoundedSlicesEnabled = enabled + } + + /** + * If this is enabled, values inside the PieChart are drawn in percent and + * not with their original value. Values provided for the IValueFormatter to + * format are then provided in percent. + * + */ + fun setUsePercentValues(enabled: Boolean) { + this.isUsePercentValuesEnabled = enabled + } + + var maxAngle: Float + get() = mMaxAngle + /** + * Sets the max angle that is used for calculating the pie-circle. 360f means + * it's a full PieChart, 180f results in a half-pie-chart. Default: 360f + * + * @param maxAngle min 90, max 360 + */ + set(value) { + var maxangle = value + if (maxangle > 360) maxangle = 360f + + if (maxangle < 90) maxangle = 90f + + this.mMaxAngle = maxangle + } + + var minAngleForSlices: Float + /** + * The minimum angle slices on the chart are rendered with, default is 0f. + * + * @return minimum angle for slices + */ + get() = mMinAngleForSlices + /** + * Set the angle to set minimum size for slices, you must call [.notifyDataSetChanged] + * and [.invalidate] when changing this, only works if there is enough room for all + * slices to have the minimum angle. + * + * @param minAngle minimum 0, maximum is half of [.setMaxAngle] + */ + set(minAngle) { + var minAngle = minAngle + if (minAngle > (mMaxAngle / 2f)) minAngle = mMaxAngle / 2f + else if (minAngle < 0) minAngle = 0f + + this.mMinAngleForSlices = minAngle + } + + protected override fun onDetachedFromWindow() { + // releases the bitmap in the renderer to avoid oom error + if (mRenderer != null && mRenderer is PieChartRenderer) { + (mRenderer as PieChartRenderer).releaseBitmap() + } + super.onDetachedFromWindow() + } + + override fun getAccessibilityDescription(): String { + val pieData = data + + var entryCount = 0 + if (pieData != null) entryCount = pieData.entryCount + + val builder = StringBuilder() + + builder.append(String.format(Locale.getDefault(), "The pie chart has %d entries.", entryCount)) + + for (i in 0.. implements ScatterDataProvider { - - public ScatterChart(Context context) { - super(context); - } - - public ScatterChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public ScatterChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - - @Override - protected void init() { - super.init(); - - mRenderer = new ScatterChartRenderer(this, mAnimator, mViewPortHandler); - - getXAxis().setSpaceMin(0.5f); - getXAxis().setSpaceMax(0.5f); - } - - @Override - public ScatterData getScatterData() { - return mData; - } - - /** - * Predefined ScatterShapes that allow the specification of a shape a ScatterDataSet should be drawn with. - * If a ScatterShape is specified for a ScatterDataSet, the required renderer is set. - */ - public enum ScatterShape { - - SQUARE("SQUARE"), - CIRCLE("CIRCLE"), - TRIANGLE("TRIANGLE"), - CROSS("CROSS"), - X("X"), - CHEVRON_UP("CHEVRON_UP"), - CHEVRON_DOWN("CHEVRON_DOWN"); - - private final String shapeIdentifier; - - ScatterShape(final String shapeIdentifier) { - this.shapeIdentifier = shapeIdentifier; - } - - @Override - public String toString() { - return shapeIdentifier; - } - - public static ScatterShape[] getAllDefaultShapes() { - return new ScatterShape[]{SQUARE, CIRCLE, TRIANGLE, CROSS, X, CHEVRON_UP, CHEVRON_DOWN}; - } - } - - @Override - public String getAccessibilityDescription() { - return "This is scatter chart"; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.kt new file mode 100644 index 000000000..9e5bfd9fe --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.kt @@ -0,0 +1,68 @@ +package com.github.mikephil.charting.charts + +import android.content.Context +import android.util.AttributeSet +import com.github.mikephil.charting.data.ScatterData +import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider +import com.github.mikephil.charting.renderer.ScatterChartRenderer + +/** + * The ScatterChart. Draws dots, triangles, squares and custom shapes into the + * Chart-View. CIRCLE and SCQUARE offer the best performance, TRIANGLE has the + * worst performance. + */ +class ScatterChart : BarLineChartBase, ScatterDataProvider { + constructor(context: Context?) : super(context) + + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + + constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle) + + + protected override fun init() { + super.init() + + mRenderer = ScatterChartRenderer(this, mAnimator, mViewPortHandler) + + xAxis.spaceMin = 0.5f + xAxis.spaceMax = 0.5f + } + + override val scatterData: ScatterData + get() = mData + + /** + * Predefined ScatterShapes that allow the specification of a shape a ScatterDataSet should be drawn with. + * If a ScatterShape is specified for a ScatterDataSet, the required renderer is set. + */ + enum class ScatterShape(private val shapeIdentifier: String) { + SQUARE("SQUARE"), + CIRCLE("CIRCLE"), + TRIANGLE("TRIANGLE"), + CROSS("CROSS"), + X("X"), + CHEVRON_UP("CHEVRON_UP"), + CHEVRON_DOWN("CHEVRON_DOWN"); + + override fun toString(): String { + return shapeIdentifier + } + + companion object { + val allDefaultShapes: Array + get() = arrayOf( + SQUARE, + CIRCLE, + TRIANGLE, + CROSS, + X, + CHEVRON_UP, + CHEVRON_DOWN + ) + } + } + + override fun getAccessibilityDescription(): String { + return "This is scatter chart" + } +} diff --git a/app/src/main/kotlin/info/appdev/chartexample/HalfPieChartActivity.kt b/app/src/main/kotlin/info/appdev/chartexample/HalfPieChartActivity.kt index 8090747bd..102d50997 100644 --- a/app/src/main/kotlin/info/appdev/chartexample/HalfPieChartActivity.kt +++ b/app/src/main/kotlin/info/appdev/chartexample/HalfPieChartActivity.kt @@ -57,7 +57,7 @@ class HalfPieChartActivity : DemoBase() { binding.chart1.isRotationEnabled = false binding.chart1.isHighlightPerTapEnabled = true - binding.chart1.setMaxAngle(180f) // HALF CHART + binding.chart1.maxAngle = 180f // HALF CHART binding.chart1.rotationAngle = 180f binding.chart1.setCenterTextOffset(0f, -20f) diff --git a/app/src/main/kotlin/info/appdev/chartexample/PieChartActivity.kt b/app/src/main/kotlin/info/appdev/chartexample/PieChartActivity.kt index 683772ac9..15f99a2a9 100644 --- a/app/src/main/kotlin/info/appdev/chartexample/PieChartActivity.kt +++ b/app/src/main/kotlin/info/appdev/chartexample/PieChartActivity.kt @@ -186,8 +186,10 @@ class PieChartActivity : DemoBase(), OnSeekBarChangeListener, OnChartValueSelect } R.id.actionToggleMinAngles -> { - if (binding.chart1.minAngleForSlices == 0f) binding.chart1.setMinAngleForSlices(36f) - else binding.chart1.setMinAngleForSlices(0f) + if (binding.chart1.minAngleForSlices == 0f) + binding.chart1.minAngleForSlices = 36f + else + binding.chart1.minAngleForSlices = 0f binding.chart1.notifyDataSetChanged() binding.chart1.invalidate() } diff --git a/app/src/main/kotlin/info/appdev/chartexample/PieChartRoundedActivity.kt b/app/src/main/kotlin/info/appdev/chartexample/PieChartRoundedActivity.kt index 8a1b856c7..c6dc0e976 100644 --- a/app/src/main/kotlin/info/appdev/chartexample/PieChartRoundedActivity.kt +++ b/app/src/main/kotlin/info/appdev/chartexample/PieChartRoundedActivity.kt @@ -190,8 +190,10 @@ class PieChartRoundedActivity : DemoBase(), OnSeekBarChangeListener, OnChartValu } R.id.actionToggleMinAngles -> { - if (binding.chart1.minAngleForSlices == 0f) binding.chart1.setMinAngleForSlices(36f) - else binding.chart1.setMinAngleForSlices(0f) + if (binding.chart1.minAngleForSlices == 0f) + binding.chart1.minAngleForSlices =36f + else + binding.chart1.minAngleForSlices = 0f binding.chart1.notifyDataSetChanged() binding.chart1.invalidate() } diff --git a/app/src/main/kotlin/info/appdev/chartexample/PiePolylineChartActivity.kt b/app/src/main/kotlin/info/appdev/chartexample/PiePolylineChartActivity.kt index 9094ff3d5..e9e654e7b 100644 --- a/app/src/main/kotlin/info/appdev/chartexample/PiePolylineChartActivity.kt +++ b/app/src/main/kotlin/info/appdev/chartexample/PiePolylineChartActivity.kt @@ -172,8 +172,10 @@ class PiePolylineChartActivity : DemoBase(), OnSeekBarChangeListener, OnChartVal } R.id.actionToggleMinAngles -> { - if (binding.chart1.minAngleForSlices == 0f) binding.chart1.setMinAngleForSlices(36f) - else binding.chart1.setMinAngleForSlices(0f) + if (binding.chart1.minAngleForSlices == 0f) + binding.chart1.minAngleForSlices =36f + else + binding.chart1.minAngleForSlices = 0f binding.chart1.notifyDataSetChanged() binding.chart1.invalidate() } diff --git a/app/src/main/kotlin/info/appdev/chartexample/fragments/SimpleFragment.kt b/app/src/main/kotlin/info/appdev/chartexample/fragments/SimpleFragment.kt index 085124de2..5c63fcb58 100644 --- a/app/src/main/kotlin/info/appdev/chartexample/fragments/SimpleFragment.kt +++ b/app/src/main/kotlin/info/appdev/chartexample/fragments/SimpleFragment.kt @@ -63,7 +63,7 @@ abstract class SimpleFragment : Fragment() { val values = getValues(count) val sets = ArrayList() - val shapes = ScatterShape.getAllDefaultShapes() + val shapes = ScatterShape.allDefaultShapes for (i in 0..()