From a91de26a5291471d9e705d0836771636bc2725b8 Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Sun, 7 Aug 2016 10:17:18 +0200 Subject: [PATCH 001/291] axis line dash effect --- .../charting/components/AxisBase.java | 57 +++++++++++++++++++ .../charting/renderer/XAxisRenderer.java | 1 + 2 files changed, 58 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index 378a3eb49e..d21675d613 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -92,6 +92,11 @@ public abstract class AxisBase extends ComponentBase { protected boolean mCenterAxisLabels = false; + /** + * the path effect of the axis line that makes dashed lines possible + */ + private DashPathEffect mAxisLineDashPathEffect = null; + /** * the path effect of the grid lines that makes dashed lines possible */ @@ -524,6 +529,58 @@ public boolean isGridDashedLineEnabled() { public DashPathEffect getGridDashPathEffect() { return mGridDashPathEffect; } + + + /** + * Enables the axis line to be drawn in dashed mode, e.g. like this + * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF. + * Keep in mind that hardware acceleration boosts performance. + * + * @param lineLength the length of the line pieces + * @param spaceLength the length of space in between the pieces + * @param phase offset, in degrees (normally, use 0) + */ + public void enableAxisLineDashedLine(float lineLength, float spaceLength, float phase) { + mAxisLineDashPathEffect = new DashPathEffect(new float[]{ + lineLength, spaceLength + }, phase); + } + + /** + * Enables the axis line to be drawn in dashed mode, e.g. like this + * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF. + * Keep in mind that hardware acceleration boosts performance. + * + * @param effect the DashPathEffect + */ + public void setAxisLineDashedLine(DashPathEffect effect) { + mAxisLineDashPathEffect = effect; + } + + /** + * Disables the axis line to be drawn in dashed mode. + */ + public void disableAxisLineDashedLine() { + mAxisLineDashPathEffect = null; + } + + /** + * Returns true if the axis dashed-line effect is enabled, false if not. + * + * @return + */ + public boolean isAxisLineDashedLineEnabled() { + return mAxisLineDashPathEffect == null ? false : true; + } + + /** + * returns the DashPathEffect that is set for axis line + * + * @return + */ + public DashPathEffect getAxisLineDashPathEffect() { + return mAxisLineDashPathEffect; + } /** * ###### BELOW CODE RELATED TO CUSTOM AXIS VALUES ###### diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java index 1973aa3a47..4410ba1122 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -152,6 +152,7 @@ public void renderAxisLine(Canvas c) { mAxisLinePaint.setColor(mXAxis.getAxisLineColor()); mAxisLinePaint.setStrokeWidth(mXAxis.getAxisLineWidth()); + mAxisLinePaint.setPathEffect(mXAxis.getAxisLineDashPathEffect()); if (mXAxis.getPosition() == XAxisPosition.TOP || mXAxis.getPosition() == XAxisPosition.TOP_INSIDE From 2f6fb103a239a291b6e55d4fdebf4cd6b028d97d Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Sun, 7 Aug 2016 10:17:32 +0200 Subject: [PATCH 002/291] method to set the dash effect --- .../github/mikephil/charting/components/AxisBase.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index d21675d613..02ff48a397 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -504,6 +504,17 @@ public void enableGridDashedLine(float lineLength, float spaceLength, float phas lineLength, spaceLength }, phase); } + + /** + * Enables the grid line to be drawn in dashed mode, e.g. like this + * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF. + * Keep in mind that hardware acceleration boosts performance. + * + * @param effect the DashPathEffect + */ + public void setGridDashedLine(DashPathEffect effect) { + mGridDashPathEffect = effect; + } /** * Disables the grid line to be drawn in dashed mode. From 099a9cf8c19ea695865229e339fa9fb013959d7c Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 7 Aug 2016 10:55:42 +0200 Subject: [PATCH 003/291] Minor changes --- .../main/java/com/github/mikephil/charting/charts/Chart.java | 2 +- .../src/main/java/com/github/mikephil/charting/data/Entry.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index f6596aaccc..ec4d38402b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -497,7 +497,7 @@ public float getMaxHighlightDistance() { /** * Sets the maximum distance in screen dp a touch can be away from an entry to cause it to get highlighted. - * Default: 100dp + * Default: 500dp * * @param distDp */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java index f5847a029d..6848540aaa 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java @@ -101,7 +101,7 @@ public boolean equalTo(Entry e) { */ @Override public String toString() { - return "Entry, x: " + x + " y (sum): " + getY(); + return "Entry, x: " + x + " y: " + getY(); } @Override From 946475b6d5317f1fcbdf071214d6676c56c0463b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 7 Aug 2016 22:34:41 +0200 Subject: [PATCH 004/291] Add grouped barchart wiki image --- screenshots/grouped_barchart_wiki.png | Bin 0 -> 46857 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 screenshots/grouped_barchart_wiki.png diff --git a/screenshots/grouped_barchart_wiki.png b/screenshots/grouped_barchart_wiki.png new file mode 100644 index 0000000000000000000000000000000000000000..c4d9e66836c57a4a54383d2007402343979d6794 GIT binary patch literal 46857 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 Date: Sun, 7 Aug 2016 22:52:40 +0200 Subject: [PATCH 005/291] Add wiki piechart --- screenshots/piechart_wiki.png | Bin 0 -> 152866 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 screenshots/piechart_wiki.png diff --git a/screenshots/piechart_wiki.png b/screenshots/piechart_wiki.png new file mode 100644 index 0000000000000000000000000000000000000000..deaed3613705c5e6baebd24c07c1e8536251ecda GIT binary patch literal 152866 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

GI)49?Pm6G++f_nAd=^o^*A2^5HiXQVi&Of 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

-Bk|AMFAfZh5J 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 Date: Sun, 7 Aug 2016 23:17:11 +0200 Subject: [PATCH 006/291] Add linechart wiki --- screenshots/linechart_wiki.png | Bin 0 -> 73507 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 screenshots/linechart_wiki.png diff --git a/screenshots/linechart_wiki.png b/screenshots/linechart_wiki.png new file mode 100644 index 0000000000000000000000000000000000000000..b81f5baf02127dbb2157018e5c79a2cbf5bc580d GIT binary patch literal 73507 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 C;Z>sm literal 0 HcmV?d00001 From f9108b9ad9023674657affcf73676bae3021be1c Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 7 Aug 2016 15:00:31 +0300 Subject: [PATCH 007/291] Groundwork for future support of highlight for multiple bubbles per x --- .../renderer/BubbleChartRenderer.java | 79 ++++++++++--------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index 458a46f884..e4fc5cef5a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -193,57 +193,64 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - final BubbleEntry entry = set.getEntryForXPos(high.getX()); + // In bubble charts - it makes sense to have multiple bubbles on the same X value in the same dataset. + final List entries = set.getEntriesForXPos(high.getX()); - if (!isInBoundsX(entry, set)) - continue; + for (BubbleEntry entry : entries) { - Transformer trans = mChart.getTransformer(set.getAxisDependency()); + if (entry.getY() != high.getY()) + continue; - sizeBuffer[0] = 0f; - sizeBuffer[2] = 1f; + if (!isInBoundsX(entry, set)) + continue; - trans.pointValuesToPixel(sizeBuffer); + Transformer trans = mChart.getTransformer(set.getAxisDependency()); - boolean normalizeSize = set.isNormalizeSizeEnabled(); + sizeBuffer[0] = 0f; + sizeBuffer[2] = 1f; - // calcualte the full width of 1 step on the x-axis - final float maxBubbleWidth = Math.abs(sizeBuffer[2] - sizeBuffer[0]); - final float maxBubbleHeight = Math.abs( - mViewPortHandler.contentBottom() - mViewPortHandler.contentTop()); - final float referenceSize = Math.min(maxBubbleHeight, maxBubbleWidth); + trans.pointValuesToPixel(sizeBuffer); - pointBuffer[0] = entry.getX(); - pointBuffer[1] = (entry.getY()) * phaseY; - trans.pointValuesToPixel(pointBuffer); + boolean normalizeSize = set.isNormalizeSizeEnabled(); - high.setDraw(pointBuffer[0], pointBuffer[1]); + // calcualte the full width of 1 step on the x-axis + final float maxBubbleWidth = Math.abs(sizeBuffer[2] - sizeBuffer[0]); + final float maxBubbleHeight = Math.abs( + mViewPortHandler.contentBottom() - mViewPortHandler.contentTop()); + final float referenceSize = Math.min(maxBubbleHeight, maxBubbleWidth); - float shapeHalf = getShapeSize(entry.getSize(), - set.getMaxSize(), - referenceSize, - normalizeSize) / 2f; + pointBuffer[0] = entry.getX(); + pointBuffer[1] = (entry.getY()) * phaseY; + trans.pointValuesToPixel(pointBuffer); - if (!mViewPortHandler.isInBoundsTop(pointBuffer[1] + shapeHalf) - || !mViewPortHandler.isInBoundsBottom(pointBuffer[1] - shapeHalf)) - continue; + high.setDraw(pointBuffer[0], pointBuffer[1]); - if (!mViewPortHandler.isInBoundsLeft(pointBuffer[0] + shapeHalf)) - continue; + float shapeHalf = getShapeSize(entry.getSize(), + set.getMaxSize(), + referenceSize, + normalizeSize) / 2f; - if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) - break; + if (!mViewPortHandler.isInBoundsTop(pointBuffer[1] + shapeHalf) + || !mViewPortHandler.isInBoundsBottom(pointBuffer[1] - shapeHalf)) + continue; - final int originalColor = set.getColor((int) entry.getX()); + if (!mViewPortHandler.isInBoundsLeft(pointBuffer[0] + shapeHalf)) + continue; + + if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) + break; - Color.RGBToHSV(Color.red(originalColor), Color.green(originalColor), - Color.blue(originalColor), _hsvBuffer); - _hsvBuffer[2] *= 0.5f; - final int color = Color.HSVToColor(Color.alpha(originalColor), _hsvBuffer); + final int originalColor = set.getColor((int) entry.getX()); - mHighlightPaint.setColor(color); - mHighlightPaint.setStrokeWidth(set.getHighlightCircleWidth()); - c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mHighlightPaint); + Color.RGBToHSV(Color.red(originalColor), Color.green(originalColor), + Color.blue(originalColor), _hsvBuffer); + _hsvBuffer[2] *= 0.5f; + final int color = Color.HSVToColor(Color.alpha(originalColor), _hsvBuffer); + + mHighlightPaint.setColor(color); + mHighlightPaint.setStrokeWidth(set.getHighlightCircleWidth()); + c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mHighlightPaint); + } } } } From 164bfd7fcab94b0a4595d8e0117a9703a14173c2 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 7 Aug 2016 16:50:37 +0300 Subject: [PATCH 008/291] Fixed legend anchor for TOP --- .../com/github/mikephil/charting/renderer/XAxisRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java index 1973aa3a47..2a93910d72 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -115,7 +115,7 @@ public void renderAxisLabels(Canvas c) { MPPointF pointF = MPPointF.getInstance(0,0); if (mXAxis.getPosition() == XAxisPosition.TOP) { pointF.x = 0.5f; - pointF.y = 0.9f; + pointF.y = 1.0f; drawLabels(c, mViewPortHandler.contentTop() - yoffset, pointF); } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { From b2250e01558080c8af558bdc19a5a2cd1230813a Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 7 Aug 2016 17:10:50 +0300 Subject: [PATCH 009/291] Use scientific EPSILON --- .../main/java/com/github/mikephil/charting/data/Entry.java | 6 ++++-- .../main/java/com/github/mikephil/charting/utils/Utils.java | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java index 6848540aaa..f7a7ca32f0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java @@ -5,6 +5,8 @@ import android.os.ParcelFormatException; import android.os.Parcelable; +import com.github.mikephil.charting.utils.Utils; + /** * Class representing one entry in the chart. Might contain multiple values. * Might only contain a single value depending on the used constructor. @@ -87,10 +89,10 @@ public boolean equalTo(Entry e) { if (e.getData() != this.getData()) return false; - if (Math.abs(e.x - this.x) > 0.000001f) + if (Math.abs(e.x - this.x) > Utils.FLOAT_EPSILON) return false; - if (Math.abs(e.getY() - this.getY()) > 0.000001f) + if (Math.abs(e.getY() - this.getY()) > Utils.FLOAT_EPSILON) return false; return true; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java index bee377dcf9..11cffc5d1a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java @@ -36,6 +36,12 @@ public abstract class Utils { public final static double DEG2RAD = (Math.PI / 180.0); public final static float FDEG2RAD = ((float) Math.PI / 180.f); + @SuppressWarnings("unused") + public final static double DOUBLE_EPSILON = Double.longBitsToDouble(Double.doubleToLongBits(1.0) + 1); + + @SuppressWarnings("unused") + public final static float FLOAT_EPSILON = Float.intBitsToFloat(Float.floatToIntBits(1f) + 1); + /** * initialize method, called inside the Chart.init() method. * From 1eae18b657cea825a6186475a0991526a155d7e6 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 7 Aug 2016 17:24:25 +0300 Subject: [PATCH 010/291] Keep that in a variable --- .../github/mikephil/charting/renderer/BarChartRenderer.java | 4 +++- .../charting/renderer/HorizontalBarChartRenderer.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index 3260d4c470..00ab5e8d1e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -217,6 +217,8 @@ public void drawValues(Canvas c) { // get the buffer BarBuffer buffer = mBarBuffers[i]; + final float phaseY = mAnimator.getPhaseY(); + // if only single values are drawn (sum) if (!dataSet.isStacked()) { @@ -293,7 +295,7 @@ public void drawValues(Canvas c) { negY -= value; } - transformed[k + 1] = y * mAnimator.getPhaseY(); + transformed[k + 1] = y * phaseY; } trans.pointValuesToPixel(transformed); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index 2162a28b06..26101970e9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -130,6 +130,8 @@ public void drawValues(Canvas c) { // get the buffer BarBuffer buffer = mBarBuffers[i]; + final float phaseY = mAnimator.getPhaseY(); + // if only single values are drawn (sum) if (!dataSet.isStacked()) { @@ -230,7 +232,7 @@ public void drawValues(Canvas c) { negY -= value; } - transformed[k] = y * mAnimator.getPhaseY(); + transformed[k] = y * phaseY; } trans.pointValuesToPixel(transformed); From 0c00b09bdb00eb1916e72e042d296bb1867369e9 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 7 Aug 2016 17:28:24 +0300 Subject: [PATCH 011/291] DRYed that code --- .../charting/renderer/BarChartRenderer.java | 45 +++++++------------ .../renderer/HorizontalBarChartRenderer.java | 15 +++++-- 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index 00ab5e8d1e..6cf208a451 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -121,47 +121,32 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { } } - // if multiple colors - if (dataSet.getColors().size() > 1) { + final boolean isSingleColor = dataSet.getColors().size() == 1; - for (int j = 0; j < buffer.size(); j += 4) { + if (isSingleColor) { + mRenderPaint.setColor(dataSet.getColor()); + } - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) - continue; + for (int j = 0; j < buffer.size(); j += 4) { - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) - break; + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) + continue; + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) + break; + if (!isSingleColor) { // Set the color for the currently drawn value. If the index // is out of bounds, reuse colors. mRenderPaint.setColor(dataSet.getColor(j / 4)); - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - - if (drawBorder) { - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mBarBorderPaint); - } } - } 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; + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + if (drawBorder) { c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - - if (drawBorder) { - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mBarBorderPaint); - } + buffer.buffer[j + 3], mBarBorderPaint); } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index 26101970e9..6f369b75b4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -73,6 +73,12 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { trans.pointValuesToPixel(buffer.buffer); + final boolean isSingleColor = dataSet.getColors().size() == 1; + + if (isSingleColor) { + mRenderPaint.setColor(dataSet.getColor()); + } + for (int j = 0; j < buffer.size(); j += 4) { if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) @@ -87,9 +93,12 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { buffer.buffer[j + 3], mShadowPaint); } - // Set the color for the currently drawn value. If the index - // is out of bounds, reuse colors. - mRenderPaint.setColor(dataSet.getColor(j / 4)); + if (!isSingleColor) { + // Set the color for the currently drawn value. If the index + // is out of bounds, reuse colors. + mRenderPaint.setColor(dataSet.getColor(j / 4)); + } + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], buffer.buffer[j + 3], mRenderPaint); From 1096719e8f66f976723f7641f6c0b0b662c108d5 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 7 Aug 2016 18:10:30 +0300 Subject: [PATCH 012/291] `drawBarShadowEnabled` is working again --- .../charting/renderer/BarChartRenderer.java | 54 +++++++++++++------ .../renderer/HorizontalBarChartRenderer.java | 45 +++++++++++++--- 2 files changed, 75 insertions(+), 24 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index 6cf208a451..7402f3f2a2 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -80,11 +80,12 @@ public void drawData(Canvas c) { } } + private RectF mBarShadowRectBuffer = new RectF(); + protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - mShadowPaint.setColor(dataSet.getBarShadowColor()); mBarBorderPaint.setColor(dataSet.getBarBorderColor()); mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth())); @@ -93,34 +94,53 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); - // initialize the buffer - BarBuffer buffer = mBarBuffers[index]; - buffer.setPhases(phaseX, phaseY); - buffer.setDataSet(index); - buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); - buffer.setBarWidth(mChart.getBarData().getBarWidth()); + // draw the bar shadow before the values + if (mChart.isDrawBarShadowEnabled()) { + mShadowPaint.setColor(dataSet.getBarShadowColor()); - buffer.feed(dataSet); + BarData barData = mChart.getBarData(); - trans.pointValuesToPixel(buffer.buffer); + final float barWidth = barData.getBarWidth(); + final float barWidthHalf = barWidth / 2.0f; + float x; - // draw the bar shadow before the values - if (mChart.isDrawBarShadowEnabled()) { + for (int i = 0, count = Math.min((int)(Math.ceil((float)(dataSet.getEntryCount()) * phaseX)), dataSet.getEntryCount()); + i < count; + i++) { + + BarEntry e = dataSet.getEntryForIndex(i); - for (int j = 0; j < buffer.size(); j += 4) { + x = e.getX(); - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) + mBarShadowRectBuffer.left = x - barWidthHalf; + mBarShadowRectBuffer.right = x + barWidthHalf; + + trans.rectValueToPixel(mBarShadowRectBuffer); + + if (!mViewPortHandler.isInBoundsLeft(mBarShadowRectBuffer.right)) continue; - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) + if (!mViewPortHandler.isInBoundsRight(mBarShadowRectBuffer.left)) break; - c.drawRect(buffer.buffer[j], mViewPortHandler.contentTop(), - buffer.buffer[j + 2], - mViewPortHandler.contentBottom(), mShadowPaint); + mBarShadowRectBuffer.top = mViewPortHandler.contentTop(); + mBarShadowRectBuffer.bottom = mViewPortHandler.contentBottom(); + + c.drawRect(mBarShadowRectBuffer, mShadowPaint); } } + // initialize the buffer + BarBuffer buffer = mBarBuffers[index]; + buffer.setPhases(phaseX, phaseY); + buffer.setDataSet(index); + buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); + buffer.setBarWidth(mChart.getBarData().getBarWidth()); + + buffer.feed(dataSet); + + trans.pointValuesToPixel(buffer.buffer); + final boolean isSingleColor = dataSet.getColors().size() == 1; if (isSingleColor) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index 6f369b75b4..1519364a95 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -48,12 +48,13 @@ public void initBuffers() { } } + private RectF mBarShadowRectBuffer = new RectF(); + @Override protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - mShadowPaint.setColor(dataSet.getBarShadowColor()); mBarBorderPaint.setColor(dataSet.getBarBorderColor()); mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth())); @@ -62,6 +63,42 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); + // draw the bar shadow before the values + if (mChart.isDrawBarShadowEnabled()) { + mShadowPaint.setColor(dataSet.getBarShadowColor()); + + BarData barData = mChart.getBarData(); + + final float barWidth = barData.getBarWidth(); + final float barWidthHalf = barWidth / 2.0f; + float x; + + for (int i = 0, count = Math.min((int)(Math.ceil((float)(dataSet.getEntryCount()) * phaseX)), dataSet.getEntryCount()); + i < count; + i++) { + + BarEntry e = dataSet.getEntryForIndex(i); + + x = e.getX(); + + mBarShadowRectBuffer.top = x - barWidthHalf; + mBarShadowRectBuffer.bottom = x + barWidthHalf; + + trans.rectValueToPixel(mBarShadowRectBuffer); + + if (!mViewPortHandler.isInBoundsTop(mBarShadowRectBuffer.bottom)) + continue; + + if (!mViewPortHandler.isInBoundsBottom(mBarShadowRectBuffer.top)) + break; + + mBarShadowRectBuffer.left = mViewPortHandler.contentLeft(); + mBarShadowRectBuffer.right = mViewPortHandler.contentRight(); + + c.drawRect(mBarShadowRectBuffer, mShadowPaint); + } + } + // initialize the buffer BarBuffer buffer = mBarBuffers[index]; buffer.setPhases(phaseX, phaseY); @@ -87,12 +124,6 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1])) continue; - if (mChart.isDrawBarShadowEnabled()) { - c.drawRect(mViewPortHandler.contentLeft(), buffer.buffer[j + 1], - mViewPortHandler.contentRight(), - buffer.buffer[j + 3], mShadowPaint); - } - if (!isSingleColor) { // Set the color for the currently drawn value. If the index // is out of bounds, reuse colors. From 9c5a46c9f8532bd8f9f6d142431614af45061450 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 7 Aug 2016 18:16:34 +0300 Subject: [PATCH 013/291] Gradle required 2048 heap for multidex --- gradle.properties | 1 + 1 file changed, 1 insertion(+) create mode 100644 gradle.properties diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000000..4a9594aeec --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx2048M \ No newline at end of file From d2e8ee220a75f46517f833be741a52c9b18ed923 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 8 Aug 2016 09:24:33 +0300 Subject: [PATCH 014/291] Renamed `zoomCenter` to `zoomToCenter` --- .../com/github/mikephil/charting/charts/BarLineChartBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index a208e73837..bf659468aa 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -657,7 +657,7 @@ public void zoom(float scaleX, float scaleY, float xValue, float yValue, AxisDep * @param scaleX * @param scaleY */ - public void zoomCenter(float scaleX, float scaleY) { + public void zoomToCenter(float scaleX, float scaleY) { MPPointF center = getCenterOffsets(); From 99379d7bcffd3247bde40fbe06bab8ec58066586 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 8 Aug 2016 09:28:22 +0300 Subject: [PATCH 015/291] Renamed `ShapeRenderer` -> `IShapeRenderer` --- .../custom/CustomScatterShapeRenderer.java | 5 +++-- .../mikephil/charting/data/ScatterDataSet.java | 16 +++++++--------- .../dataprovider/ScatterDataProvider.java | 1 - .../interfaces/datasets/IScatterDataSet.java | 6 +++--- .../charting/renderer/ScatterChartRenderer.java | 6 +++--- .../scatter/ChevronDownShapeRenderer.java | 3 ++- .../renderer/scatter/ChevronUpShapeRenderer.java | 3 ++- .../renderer/scatter/CircleShapeRenderer.java | 3 ++- .../renderer/scatter/CrossShapeRenderer.java | 3 ++- .../{ShapeRenderer.java => IShapeRenderer.java} | 3 ++- .../renderer/scatter/SquareShapeRenderer.java | 3 ++- .../renderer/scatter/TriangleShapeRenderer.java | 3 ++- .../renderer/scatter/XShapeRenderer.java | 3 ++- .../charting/utils/ShapeRendererHandler.java | 14 +++++++------- 14 files changed, 39 insertions(+), 33 deletions(-) rename MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/{ShapeRenderer.java => IShapeRenderer.java} (96%) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java index 25cd41b1a2..77ffe2759e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java @@ -5,14 +5,15 @@ import com.github.mikephil.charting.buffer.ScatterBuffer; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; -import com.github.mikephil.charting.renderer.scatter.ShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; import com.github.mikephil.charting.utils.ViewPortHandler; /** * Custom shape renderer that draws a single line. * Created by philipp on 26/06/16. */ -public class CustomScatterShapeRenderer implements ShapeRenderer { +public class CustomScatterShapeRenderer implements IShapeRenderer +{ @Override public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java index 01f2933d3f..446bce3b52 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java @@ -1,11 +1,9 @@ package com.github.mikephil.charting.data; -import android.graphics.drawable.shapes.Shape; - import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; -import com.github.mikephil.charting.renderer.scatter.ShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; import com.github.mikephil.charting.renderer.scatter.SquareShapeRenderer; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.ShapeRendererHandler; @@ -23,7 +21,7 @@ public class ScatterDataSet extends LineScatterCandleRadarDataSet impleme /** * Renderer responsible for rendering this DataSet, default: square */ - protected ShapeRenderer mShapeRenderer = new SquareShapeRenderer(); + protected IShapeRenderer mShapeRenderer = new SquareShapeRenderer(); /** * The radius of the hole in the shape (applies to Square, Circle and Triangle) @@ -87,7 +85,7 @@ public float getScatterShapeSize() { } /** - * Sets the ScatterShape this DataSet should be drawn with. This will search for an available ShapeRenderer and set this + * Sets the ScatterShape this DataSet should be drawn with. This will search for an available IShapeRenderer and set this * renderer for the DataSet. * * @param shape @@ -99,17 +97,17 @@ public void setScatterShape(ScatterChart.ScatterShape shape) { } /** - * Sets a new ShapeRenderer responsible for drawing this DataSet. - * This can also be used to set a custom ShapeRenderer aside from the default ones. + * Sets a new IShapeRenderer responsible for drawing this DataSet. + * This can also be used to set a custom IShapeRenderer aside from the default ones. * * @param shapeRenderer */ - public void setShapeRenderer(ShapeRenderer shapeRenderer) { + public void setShapeRenderer(IShapeRenderer shapeRenderer) { mShapeRenderer = shapeRenderer; } @Override - public ShapeRenderer getShapeRenderer() { + public IShapeRenderer getShapeRenderer() { return mShapeRenderer; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java index e89110cdb9..b58d5af95d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java @@ -1,7 +1,6 @@ package com.github.mikephil.charting.interfaces.dataprovider; import com.github.mikephil.charting.data.ScatterData; -import com.github.mikephil.charting.renderer.scatter.ShapeRenderer; public interface ScatterDataProvider extends BarLineScatterCandleBubbleDataProvider { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java index ac838a4d2e..ac6122742a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java @@ -1,7 +1,7 @@ package com.github.mikephil.charting.interfaces.datasets; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.renderer.scatter.ShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; /** * Created by philipp on 21/10/15. @@ -30,9 +30,9 @@ public interface IScatterDataSet extends ILineScatterCandleRadarDataSet { int getScatterShapeHoleColor(); /** - * Returns the ShapeRenderer responsible for rendering this DataSet. + * Returns the IShapeRenderer responsible for rendering this DataSet. * * @return */ - ShapeRenderer getShapeRenderer(); + IShapeRenderer getShapeRenderer(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index 484bcba551..417e553b65 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -10,7 +10,7 @@ import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; -import com.github.mikephil.charting.renderer.scatter.ShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; @@ -72,12 +72,12 @@ protected void drawDataSet(Canvas c, IScatterDataSet dataSet) { trans.pointValuesToPixel(buffer.buffer); - ShapeRenderer renderer = dataSet.getShapeRenderer(); + IShapeRenderer renderer = dataSet.getShapeRenderer(); if (renderer != null) { renderer.renderShape(c, dataSet, mViewPortHandler, buffer, mRenderPaint, shapeSize); } else { - throw new RuntimeException("No ShapeRenderer found for provided identifier. Please make sure to add a ShapeRenderer" + + throw new RuntimeException("No IShapeRenderer found for provided identifier. Please make sure to add a IShapeRenderer" + " capable of rendering the provided shape."); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java index d293b28481..cdc3f2af6d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java @@ -12,7 +12,8 @@ * Created by wajdic on 15/06/2016. * Created at Time 09:08 */ -public class ChevronDownShapeRenderer implements ShapeRenderer { +public class ChevronDownShapeRenderer implements IShapeRenderer +{ @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java index 4eb9db7942..f4185bfd8e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java @@ -12,7 +12,8 @@ * Created by wajdic on 15/06/2016. * Created at Time 09:08 */ -public class ChevronUpShapeRenderer implements ShapeRenderer { +public class ChevronUpShapeRenderer implements IShapeRenderer +{ @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java index 449bd9fe96..98ed8c1459 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java @@ -13,7 +13,8 @@ * Created by wajdic on 15/06/2016. * Created at Time 09:08 */ -public class CircleShapeRenderer implements ShapeRenderer { +public class CircleShapeRenderer implements IShapeRenderer +{ @Override public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java index 45db656677..3273490a46 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java @@ -12,7 +12,8 @@ * Created by wajdic on 15/06/2016. * Created at Time 09:08 */ -public class CrossShapeRenderer implements ShapeRenderer { +public class CrossShapeRenderer implements IShapeRenderer +{ @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/IShapeRenderer.java similarity index 96% rename from MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ShapeRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/IShapeRenderer.java index 3fb23f7718..f89d6f6b08 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/IShapeRenderer.java @@ -12,7 +12,8 @@ * Created by wajdic on 15/06/2016. * Created at Time 09:07 */ -public interface ShapeRenderer { +public interface IShapeRenderer +{ /** * Renders the provided ScatterDataSet with a shape. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java index 184e77a449..74670473ef 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java @@ -13,7 +13,8 @@ * Created by wajdic on 15/06/2016. * Created at Time 09:08 */ -public class SquareShapeRenderer implements ShapeRenderer { +public class SquareShapeRenderer implements IShapeRenderer +{ @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java index 789297d170..f8e2f30b47 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java @@ -14,7 +14,8 @@ * Created by wajdic on 15/06/2016. * Created at Time 09:08 */ -public class TriangleShapeRenderer implements ShapeRenderer { +public class TriangleShapeRenderer implements IShapeRenderer +{ protected Path mTrianglePathBuffer = new Path(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java index 2dbac6a974..3c37174e2f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java @@ -12,7 +12,8 @@ * Created by wajdic on 15/06/2016. * Created at Time 09:08 */ -public class XShapeRenderer implements ShapeRenderer { +public class XShapeRenderer implements IShapeRenderer +{ @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ShapeRendererHandler.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ShapeRendererHandler.java index 0cef75f151..c2093e6e76 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ShapeRendererHandler.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ShapeRendererHandler.java @@ -5,7 +5,7 @@ import com.github.mikephil.charting.renderer.scatter.ChevronUpShapeRenderer; import com.github.mikephil.charting.renderer.scatter.CircleShapeRenderer; import com.github.mikephil.charting.renderer.scatter.CrossShapeRenderer; -import com.github.mikephil.charting.renderer.scatter.ShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; import com.github.mikephil.charting.renderer.scatter.SquareShapeRenderer; import com.github.mikephil.charting.renderer.scatter.TriangleShapeRenderer; import com.github.mikephil.charting.renderer.scatter.XShapeRenderer; @@ -14,16 +14,16 @@ /** * Created by Philipp Jahoda on 27/06/16. - * Class allowing to determine the corresponding ShapeRenderer for a given ScatterShape. + * Class allowing to determine the corresponding IShapeRenderer for a given ScatterShape. */ public final class ShapeRendererHandler { /** - * Dictionary of ShapeRenderer which are responsible for drawing custom shapes. + * Dictionary of IShapeRenderer which are responsible for drawing custom shapes. * Can add to it your custom shapes. - * CustomShapeRenderer Implements ShapeRenderer{} + * CustomShapeRenderer Implements IShapeRenderer{} */ - protected HashMap shapeRendererList; + protected HashMap shapeRendererList; /** * Constructor @@ -33,12 +33,12 @@ public ShapeRendererHandler() { } /** - * Returns the corresponding ShapeRenderer for a given ScatterShape. + * Returns the corresponding IShapeRenderer for a given ScatterShape. * * @param shape * @return */ - public ShapeRenderer getShapeRenderer(ScatterChart.ScatterShape shape) { + public IShapeRenderer getShapeRenderer(ScatterChart.ScatterShape shape) { return shapeRendererList.get(shape.toString()); } From 79f5ed02004e8a80f1c4ecfc5cefa50d75dc9703 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 8 Aug 2016 09:46:06 +0300 Subject: [PATCH 016/291] Simplified scatter shape enum model (Why would anyone try to subclass an enum?) --- .../charting/charts/ScatterChart.java | 7 ++- .../charting/data/ScatterDataSet.java | 31 +++++++--- .../charting/utils/ShapeRendererHandler.java | 59 ------------------- 3 files changed, 28 insertions(+), 69 deletions(-) delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/utils/ShapeRendererHandler.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java index 0f68457b55..decf1e855a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java @@ -48,7 +48,12 @@ public ScatterData getScatterData() { */ public enum ScatterShape { - SQUARE("SQUARE"), CIRCLE("CIRCLE"), TRIANGLE("TRIANGLE"), CROSS("CROSS"), X("X"), CHEVRON_UP("CHEVRON_UP"), + SQUARE("SQUARE"), + CIRCLE("CIRCLE"), + TRIANGLE("TRIANGLE"), + CROSS("CROSS"), + X("X"), + CHEVRON_UP("CHEVRON_UP"), CHEVRON_DOWN("CHEVRON_DOWN"); private final String shapeIdentifier; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java index 446bce3b52..a9d73885b5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java @@ -3,10 +3,15 @@ import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.renderer.scatter.ChevronDownShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.ChevronUpShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.CircleShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.CrossShapeRenderer; import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; import com.github.mikephil.charting.renderer.scatter.SquareShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.TriangleShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.XShapeRenderer; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.ShapeRendererHandler; import java.util.ArrayList; import java.util.List; @@ -36,11 +41,6 @@ public class ScatterDataSet extends LineScatterCandleRadarDataSet impleme */ private int mScatterShapeHoleColor = ColorTemplate.COLOR_NONE; - /** - * Custom path object the user can provide that is drawn where the values - * are at. This is used when ScatterShape.CUSTOM is set for a DataSet. - */ - //private Path mCustomScatterPath = null; public ScatterDataSet(List yVals, String label) { super(yVals, label); } @@ -91,9 +91,7 @@ public float getScatterShapeSize() { * @param shape */ public void setScatterShape(ScatterChart.ScatterShape shape) { - - ShapeRendererHandler handler = new ShapeRendererHandler(); - mShapeRenderer = handler.getShapeRenderer(shape); + mShapeRenderer = getRendererForShape(shape); } /** @@ -139,4 +137,19 @@ public void setScatterShapeHoleColor(int holeColor) { public int getScatterShapeHoleColor() { return mScatterShapeHoleColor; } + + public static IShapeRenderer getRendererForShape(ScatterChart.ScatterShape shape) { + + switch (shape) { + case SQUARE: return new SquareShapeRenderer(); + case CIRCLE: return new CircleShapeRenderer(); + case TRIANGLE: return new TriangleShapeRenderer(); + case CROSS: return new CrossShapeRenderer(); + case X: return new XShapeRenderer(); + case CHEVRON_UP: return new ChevronUpShapeRenderer(); + case CHEVRON_DOWN: return new ChevronDownShapeRenderer(); + } + + return null; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ShapeRendererHandler.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ShapeRendererHandler.java deleted file mode 100644 index c2093e6e76..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ShapeRendererHandler.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.github.mikephil.charting.utils; - -import com.github.mikephil.charting.charts.ScatterChart; -import com.github.mikephil.charting.renderer.scatter.ChevronDownShapeRenderer; -import com.github.mikephil.charting.renderer.scatter.ChevronUpShapeRenderer; -import com.github.mikephil.charting.renderer.scatter.CircleShapeRenderer; -import com.github.mikephil.charting.renderer.scatter.CrossShapeRenderer; -import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; -import com.github.mikephil.charting.renderer.scatter.SquareShapeRenderer; -import com.github.mikephil.charting.renderer.scatter.TriangleShapeRenderer; -import com.github.mikephil.charting.renderer.scatter.XShapeRenderer; - -import java.util.HashMap; - -/** - * Created by Philipp Jahoda on 27/06/16. - * Class allowing to determine the corresponding IShapeRenderer for a given ScatterShape. - */ -public final class ShapeRendererHandler { - - /** - * Dictionary of IShapeRenderer which are responsible for drawing custom shapes. - * Can add to it your custom shapes. - * CustomShapeRenderer Implements IShapeRenderer{} - */ - protected HashMap shapeRendererList; - - /** - * Constructor - */ - public ShapeRendererHandler() { - initShapeRenderers(); - } - - /** - * Returns the corresponding IShapeRenderer for a given ScatterShape. - * - * @param shape - * @return - */ - public IShapeRenderer getShapeRenderer(ScatterChart.ScatterShape shape) { - return shapeRendererList.get(shape.toString()); - } - - /** - * Init default ShapeRenderers. - */ - protected void initShapeRenderers() { - shapeRendererList = new HashMap<>(); - - shapeRendererList.put(ScatterChart.ScatterShape.SQUARE.toString(), new SquareShapeRenderer()); - shapeRendererList.put(ScatterChart.ScatterShape.CIRCLE.toString(), new CircleShapeRenderer()); - shapeRendererList.put(ScatterChart.ScatterShape.TRIANGLE.toString(), new TriangleShapeRenderer()); - shapeRendererList.put(ScatterChart.ScatterShape.CROSS.toString(), new CrossShapeRenderer()); - shapeRendererList.put(ScatterChart.ScatterShape.X.toString(), new XShapeRenderer()); - shapeRendererList.put(ScatterChart.ScatterShape.CHEVRON_UP.toString(), new ChevronUpShapeRenderer()); - shapeRendererList.put(ScatterChart.ScatterShape.CHEVRON_DOWN.toString(), new ChevronDownShapeRenderer()); - } -} From 2d54fd1f3f0aeba4ffd65b8ad8cd1d11d47cadb1 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 8 Aug 2016 09:52:26 +0300 Subject: [PATCH 017/291] Call createRenderers() instead of recreating the renderer each time --- .../charting/charts/CombinedChart.java | 4 +--- .../renderer/CombinedChartRenderer.java | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java index 5f578c5d83..ba0cfcb0ae 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java @@ -81,11 +81,9 @@ public CombinedData getCombinedData() { @Override public void setData(CombinedData data) { - mData = null; - mRenderer = null; super.setData(data); setHighlighter(new CombinedHighlighter(this, this)); - mRenderer = new CombinedChartRenderer(this, mAnimator, mViewPortHandler); + ((CombinedChartRenderer)mRenderer).createRenderers(); mRenderer.initBuffers(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java index 142dff4346..6d0d4d3da0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java @@ -9,6 +9,7 @@ import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.CombinedData; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; import com.github.mikephil.charting.utils.ViewPortHandler; import java.lang.ref.WeakReference; @@ -30,21 +31,21 @@ public class CombinedChartRenderer extends DataRenderer { public CombinedChartRenderer(CombinedChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { super(animator, viewPortHandler); mChart = new WeakReference(chart); - createRenderers(chart, animator, viewPortHandler); + createRenderers(); } /** * Creates the renderers needed for this combined-renderer in the required order. Also takes the DrawOrder into * consideration. - * - * @param chart - * @param animator - * @param viewPortHandler */ - protected void createRenderers(CombinedChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { + public void createRenderers() { mRenderers.clear(); + CombinedChart chart = (CombinedChart)mChart.get(); + if (chart == null) + return; + DrawOrder[] orders = chart.getDrawOrder(); for (DrawOrder order : orders) { @@ -52,23 +53,23 @@ protected void createRenderers(CombinedChart chart, ChartAnimator animator, View switch (order) { case BAR: if (chart.getBarData() != null) - mRenderers.add(new BarChartRenderer(chart, animator, viewPortHandler)); + mRenderers.add(new BarChartRenderer(chart, mAnimator, mViewPortHandler)); break; case BUBBLE: if (chart.getBubbleData() != null) - mRenderers.add(new BubbleChartRenderer(chart, animator, viewPortHandler)); + mRenderers.add(new BubbleChartRenderer(chart, mAnimator, mViewPortHandler)); break; case LINE: if (chart.getLineData() != null) - mRenderers.add(new LineChartRenderer(chart, animator, viewPortHandler)); + mRenderers.add(new LineChartRenderer(chart, mAnimator, mViewPortHandler)); break; case CANDLE: if (chart.getCandleData() != null) - mRenderers.add(new CandleStickChartRenderer(chart, animator, viewPortHandler)); + mRenderers.add(new CandleStickChartRenderer(chart, mAnimator, mViewPortHandler)); break; case SCATTER: if (chart.getScatterData() != null) - mRenderers.add(new ScatterChartRenderer(chart, animator, viewPortHandler)); + mRenderers.add(new ScatterChartRenderer(chart, mAnimator, mViewPortHandler)); break; } } From 77c10fc9e56f9d9538773af9e5af06c072f2318c Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 8 Aug 2016 10:03:51 +0300 Subject: [PATCH 018/291] These return a single pixel (x/y), for values (x value and y value) --- .../github/mikephil/charting/charts/BarLineChartBase.java | 6 +++--- .../mikephil/charting/highlight/BarHighlighter.java | 2 +- .../mikephil/charting/highlight/ChartHighlighter.java | 2 +- .../charting/highlight/HorizontalBarHighlighter.java | 2 +- .../charting/renderer/CandleStickChartRenderer.java | 2 +- .../mikephil/charting/renderer/LineChartRenderer.java | 2 +- .../mikephil/charting/renderer/ScatterChartRenderer.java | 2 +- .../github/mikephil/charting/renderer/YAxisRenderer.java | 2 +- .../renderer/YAxisRendererHorizontalBarChart.java | 2 +- .../com/github/mikephil/charting/utils/Transformer.java | 8 ++++---- 10 files changed, 15 insertions(+), 15 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index bf659468aa..fbc20a44b3 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -1206,7 +1206,7 @@ public void setKeepPositionOnRotation(boolean keepPositionOnRotation) { * Returns the x and y values in the chart at the given touch point * (encapsulated in a MPPointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to - * getPixelsForValues(...). + * getPixelForValues(...). * * @param x * @param y @@ -1231,8 +1231,8 @@ public void getValuesByTouchPoint(float x, float y, AxisDependency axis, MPPoint * @param y * @return */ - public MPPointD getPixelsForValues(float x, float y, AxisDependency axis) { - return getTransformer(axis).getPixelsForValues(x, y); + public MPPointD getPixelForValues(float x, float y, AxisDependency axis) { + return getTransformer(axis).getPixelForValues(x, y); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java index 1adad89de1..0a5fb25023 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java @@ -68,7 +68,7 @@ public Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal if (ranges.length > 0) { int stackIndex = getClosestStackIndex(ranges, yVal); - MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(high.getX(), ranges[stackIndex].to); + MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(high.getX(), ranges[stackIndex].to); Highlight stackedHigh = new Highlight( entry.getX(), diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java index c3aa4560d8..59a38f6132 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java @@ -167,7 +167,7 @@ protected Highlight buildHighlight(IDataSet set, int dataSetIndex, float xVal, D if (e == null) return null; - MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), e.getY()); + MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), e.getY()); return new Highlight(e.getX(), e.getY(), (float) pixels.x, (float) pixels.y, dataSetIndex, set.getAxisDependency()); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java index 626868c3de..97cb11bff9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java @@ -47,7 +47,7 @@ protected Highlight buildHighlight(IDataSet set, int dataSetIndex, float xVal, D final Entry e = set.getEntryForXPos(xVal, rounding); - MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getY(), e.getX()); + MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getY(), e.getX()); return new Highlight(e.getX(), e.getY(), (float) pixels.x, (float) pixels.y, dataSetIndex, set.getAxisDependency()); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index 734b4301e5..cdd79f41c3 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -326,7 +326,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { float highValue = e.getHigh() * mAnimator.getPhaseY(); float y = (lowValue + highValue) / 2f; - MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), y); + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), y); high.setDraw((float) pix.x, (float) pix.y); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 48c7a4be3c..85e95a7621 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -668,7 +668,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (!isInBoundsX(e, set)) continue; - MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), e.getY() * mAnimator + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), e.getY() * mAnimator .getPhaseY()); high.setDraw((float) pix.x, (float) pix.y); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index 417e553b65..2341ca5b59 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -148,7 +148,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (!isInBoundsX(e, set)) continue; - MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), e.getY() * mAnimator + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), e.getY() * mAnimator .getPhaseY()); high.setDraw((float) pix.x, (float) pix.y); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java index 71cfbf324e..09e25d1e1e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -203,7 +203,7 @@ protected float[] getTransformedPositions() { protected void drawZeroLine(Canvas c) { // draw zero line - MPPointD pos = mTrans.getPixelsForValues(0f, 0f); + MPPointD pos = mTrans.getPixelForValues(0f, 0f); mZeroLinePaint.setColor(mYAxis.getZeroLineColor()); mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth()); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java index 3f9f36a965..6cf7985ea2 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java @@ -179,7 +179,7 @@ protected Path linePath(Path p, int i, float[] positions) { protected void drawZeroLine(Canvas c) { // draw zero line - MPPointD pos = mTrans.getPixelsForValues(0f, 0f); + MPPointD pos = mTrans.getPixelForValues(0f, 0f); mZeroLinePaint.setColor(mYAxis.getZeroLineColor()); mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth()); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java index d9a57c3f82..6848bc58d1 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java @@ -353,7 +353,7 @@ public void rectValuesToPixel(List rects) { m.mapRect(rects.get(i)); } - protected Matrix mPixelsToValueMatrixBuffer = new Matrix(); + protected Matrix mPixelToValueMatrixBuffer = new Matrix(); /** * Transforms the given array of touch positions (pixels) (x, y, x, y, ...) @@ -363,7 +363,7 @@ public void rectValuesToPixel(List rects) { */ public void pixelsToValue(float[] pixels) { - Matrix tmp = mPixelsToValueMatrixBuffer; + Matrix tmp = mPixelToValueMatrixBuffer; tmp.reset(); // invert all matrixes to convert back to the original value @@ -387,7 +387,7 @@ public void pixelsToValue(float[] pixels) { * returns the x and y values in the chart at the given touch point * (encapsulated in a MPPointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to - * getPixelsForValues(...). + * getPixelForValues(...). * * @param x * @param y @@ -419,7 +419,7 @@ public void getValuesByTouchPoint(float x, float y, MPPointD outputPoint) { * @param y * @return */ - public MPPointD getPixelsForValues(float x, float y) { + public MPPointD getPixelForValues(float x, float y) { ptsBuffer[0] = x; ptsBuffer[1] = y; From f63002e196fb2c86393c018061163c84b2d110a9 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 8 Aug 2016 10:14:58 +0300 Subject: [PATCH 019/291] Renamed x-pos to x-value where appropriate --- .../mikephil/charting/charts/BarChart.java | 6 +-- .../mikephil/charting/charts/Chart.java | 4 +- .../mikephil/charting/charts/PieChart.java | 2 +- .../mikephil/charting/data/BarData.java | 4 +- .../mikephil/charting/data/BaseDataSet.java | 4 +- .../mikephil/charting/data/ChartData.java | 10 ++--- .../mikephil/charting/data/CombinedData.java | 6 +-- .../mikephil/charting/data/DataSet.java | 32 +++++++------- .../charting/highlight/BarHighlighter.java | 2 +- .../charting/highlight/ChartHighlighter.java | 6 +-- .../highlight/CombinedHighlighter.java | 2 +- .../highlight/HorizontalBarHighlighter.java | 2 +- .../interfaces/datasets/IDataSet.java | 44 +++++++++---------- .../charting/renderer/BarChartRenderer.java | 2 +- .../BarLineScatterCandleBubbleRenderer.java | 4 +- .../renderer/BubbleChartRenderer.java | 2 +- .../renderer/CandleStickChartRenderer.java | 2 +- .../charting/renderer/LineChartRenderer.java | 2 +- .../renderer/ScatterChartRenderer.java | 2 +- .../mikephil/charting/test/DataSetTest.java | 16 +++---- 20 files changed, 76 insertions(+), 78 deletions(-) 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 index 6f3692c2f3..a149adf909 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java @@ -202,7 +202,7 @@ public boolean isHighlightFullBarEnabled() { } /** - * Highlights the value at the given x-position in the given DataSet. Provide + * Highlights the value at the given x-value in the given DataSet. Provide * -1 as the dataSetIndex to undo all highlighting. * * @param x @@ -230,8 +230,8 @@ public void setFitBars(boolean enabled) { } /** - * Groups all BarDataSet objects this data object holds together by modifying the x-position of their entries. - * Previously set x-positions of entries will be overwritten. Leaves space between bars and groups as specified + * 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. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index ec4d38402b..74823825c8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -581,7 +581,7 @@ public void highlightValues(Highlight[] highs) { } /** - * Highlights the value at the given x-position in the given DataSet. Provide + * Highlights the value at the given x-value in the given DataSet. Provide * -1 as the dataSetIndex to undo all highlighting. This will trigger a callback to the OnChartValueSelectedListener. * * @param x @@ -592,7 +592,7 @@ public void highlightValue(float x, int dataSetIndex) { } /** - * Highlights the value at the given x position in the given DataSet. Provide + * Highlights the value at the given x-value in the given DataSet. Provide * -1 as the dataSetIndex to undo all highlighting. * * @param x 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 index bad10dbbb4..d2acf19b2d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java @@ -328,7 +328,7 @@ public int getDataSetIndexForIndex(int xIndex) { List dataSets = mData.getDataSets(); for (int i = 0; i < dataSets.size(); i++) { - if (dataSets.get(i).getEntryForXPos(xIndex) != null) + if (dataSets.get(i).getEntryForXValue(xIndex) != null) return i; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java index 609d480d32..601364dbc4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java @@ -44,8 +44,8 @@ public float getBarWidth() { } /** - * Groups all BarDataSet objects this data object holds together by modifying the x-position of their entries. - * Previously set x-positions of entries will be overwritten. Leaves space between bars and groups as specified + * 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. * Do not forget to call notifyDataSetChanged() on your BarChart object after calling this method. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java index 28dd77ceb4..55d7309c9a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -387,9 +387,9 @@ public boolean removeLast() { } @Override - public boolean removeEntryByXPos(float xPos) { + public boolean removeEntryByXValue(float xValue) { - T e = getEntryForXPos(xPos); + T e = getEntryForXValue(xValue); return removeEntry(e); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java index a8496b3bfb..0d8c52bd37 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java @@ -335,7 +335,7 @@ public Entry getEntryForHighlight(Highlight highlight) { if (highlight.getDataSetIndex() >= mDataSets.size()) return null; else { - return mDataSets.get(highlight.getDataSetIndex()).getEntryForXPos(highlight.getX()); + return mDataSets.get(highlight.getDataSetIndex()).getEntryForXValue(highlight.getX()); } } @@ -526,17 +526,17 @@ public boolean removeEntry(Entry e, int dataSetIndex) { * specified index. Returns true if an Entry was removed, false if no Entry * was found that meets the specified requirements. * - * @param xPos + * @param xValue * @param dataSetIndex * @return */ - public boolean removeEntry(float xPos, int dataSetIndex) { + public boolean removeEntry(float xValue, int dataSetIndex) { if (dataSetIndex >= mDataSets.size()) return false; IDataSet dataSet = mDataSets.get(dataSetIndex); - Entry e = dataSet.getEntryForXPos(xPos); + Entry e = dataSet.getEntryForXValue(xValue); if (e == null) return false; @@ -561,7 +561,7 @@ public T getDataSetForEntry(Entry e) { T set = mDataSets.get(i); for (int j = 0; j < set.getEntryCount(); j++) { - if (e.equalTo(set.getEntryForXPos(e.getX()))) + if (e.equalTo(set.getEntryForXValue(e.getX()))) return set; } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java index 0b6b94437a..035ad9e0b3 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java @@ -3,10 +3,8 @@ import android.util.Log; -import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; import java.util.ArrayList; import java.util.List; @@ -194,7 +192,7 @@ public Entry getEntryForHighlight(Highlight highlight) { // if we are not interested in highlighting a specific value. List entries = data.getDataSetByIndex(highlight.getDataSetIndex()) - .getEntriesForXPos(highlight.getX()); + .getEntriesForXValue(highlight.getX()); for (Entry entry : entries) if (entry.getY() == highlight.getY() || Float.isNaN(highlight.getY())) @@ -243,7 +241,7 @@ public boolean removeEntry(Entry e, int dataSetIndex) { @Deprecated @Override - public boolean removeEntry(float xPos, int dataSetIndex) { + public boolean removeEntry(float xValue, int dataSetIndex) { Log.e("MPAndroidChart", "removeEntry(...) not supported for CombinedData"); return false; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java index 42571e95de..d337a0482b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -239,17 +239,17 @@ public int getEntryIndex(Entry e) { } @Override - public T getEntryForXPos(float xPos, Rounding rounding) { + public T getEntryForXValue(float xValue, Rounding rounding) { - int index = getEntryIndex(xPos, rounding); + int index = getEntryIndex(xValue, rounding); if (index > -1) return mValues.get(index); return null; } @Override - public T getEntryForXPos(float xPos) { - return getEntryForXPos(xPos, Rounding.CLOSEST); + public T getEntryForXValue(float xValue) { + return getEntryForXValue(xValue, Rounding.CLOSEST); } @Override @@ -258,7 +258,7 @@ public T getEntryForIndex(int index) { } @Override - public int getEntryIndex(float xPos, Rounding rounding) { + public int getEntryIndex(float xValue, Rounding rounding) { if (mValues == null || mValues.isEmpty()) return -1; @@ -269,8 +269,8 @@ public int getEntryIndex(float xPos, Rounding rounding) { while (low < high) { int m = (low + high) / 2; - float d1 = Math.abs(mValues.get(m).getX() - xPos); - float d2 = Math.abs(mValues.get(m + 1).getX() - xPos); + float d1 = Math.abs(mValues.get(m).getX() - xValue); + float d2 = Math.abs(mValues.get(m + 1).getX() - xValue); if (d2 <= d1) { low = m + 1; @@ -280,13 +280,13 @@ public int getEntryIndex(float xPos, Rounding rounding) { } if (high != -1) { - float closestXPos = mValues.get(high).getX(); + float closestXValue = mValues.get(high).getX(); if (rounding == Rounding.UP) { - if (closestXPos < xPos && high < mValues.size() - 1) { + if (closestXValue < xValue && high < mValues.size() - 1) { ++high; } } else if (rounding == Rounding.DOWN) { - if (closestXPos > xPos && high > 0) { + if (closestXValue > xValue && high > 0) { --high; } } @@ -300,11 +300,11 @@ public int getEntryIndex(float xPos, Rounding rounding) { * does calculations at runtime. Do not over-use in performance critical * situations. * - * @param xVal + * @param xValue * @return */ @Override - public List getEntriesForXPos(float xVal) { + public List getEntriesForXValue(float xValue) { List entries = new ArrayList(); @@ -315,14 +315,14 @@ public List getEntriesForXPos(float xVal) { int m = (high + low) / 2; T entry = mValues.get(m); - if (xVal == entry.getX()) { - while (m > 0 && mValues.get(m - 1).getX() == xVal) + if (xValue == entry.getX()) { + while (m > 0 && mValues.get(m - 1).getX() == xValue) m--; high = mValues.size(); for (; m < high; m++) { entry = mValues.get(m); - if (entry.getX() == xVal) { + if (entry.getX() == xValue) { entries.add(entry); } else { break; @@ -331,7 +331,7 @@ public List getEntriesForXPos(float xVal) { break; } else { - if (xVal > entry.getX()) + if (xValue > entry.getX()) low = m + 1; else high = m - 1; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java index 0a5fb25023..b60eeabfe9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java @@ -54,7 +54,7 @@ public Highlight getHighlight(float x, float y) { */ public Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal, float yVal) { - BarEntry entry = set.getEntryForXPos(xVal); + BarEntry entry = set.getEntryForXValue(xVal); if (entry == null) return null; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java index 59a38f6132..03491877db 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java @@ -66,7 +66,7 @@ protected MPPointD getValsForTouch(float x, float y) { */ protected Highlight getHighlightForX(float xVal, float x, float y) { - List closestValues = getHighlightsAtXPos(xVal, x, y); + List closestValues = getHighlightsAtXValue(xVal, x, y); if(closestValues.isEmpty()) { return null; @@ -124,7 +124,7 @@ protected float getHighlightPos(Highlight h) { * @param y touch position * @return */ - protected List getHighlightsAtXPos(float xVal, float x, float y) { + protected List getHighlightsAtXValue(float xVal, float x, float y) { mHighlightBuffer.clear(); @@ -162,7 +162,7 @@ protected List getHighlightsAtXPos(float xVal, float x, float y) { */ protected Highlight buildHighlight(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { - final Entry e = set.getEntryForXPos(xVal, rounding); + final Entry e = set.getEntryForXValue(xVal, rounding); if (e == null) return null; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java index eaff38c27c..d2275187e9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java @@ -28,7 +28,7 @@ public CombinedHighlighter(CombinedDataProvider chart, BarDataProvider barChart) } @Override - protected List getHighlightsAtXPos(float xVal, float x, float y) { + protected List getHighlightsAtXValue(float xVal, float x, float y) { mHighlightBuffer.clear(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java index 97cb11bff9..60ec66ba9d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java @@ -45,7 +45,7 @@ public Highlight getHighlight(float x, float y) { @Override protected Highlight buildHighlight(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { - final Entry e = set.getEntryForXPos(xVal, rounding); + final Entry e = set.getEntryForXValue(xVal, rounding); MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getY(), e.getX()); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index aadd6efcba..f36cee928e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -58,40 +58,40 @@ public interface IDataSet { void calcMinMax(); /** - * Returns the first Entry object found at the given xPos with binary - * search. If the no Entry at the specified xPos is found, this method - * returns the Entry at the xPos according to the rounding. + * Returns the first Entry object found at the given x-value with binary + * search. If the no Entry at the specified x-value is found, this method + * returns the Entry at the x-value according to the rounding. * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xPos + * @param xValue * @param rounding determine to round up/down/closest if there is no Entry matching the provided x-index * @return */ - T getEntryForXPos(float xPos, DataSet.Rounding rounding); + T getEntryForXValue(float xValue, DataSet.Rounding rounding); /** - * Returns the first Entry object found at the given xPos with binary - * search. If the no Entry at the specified xPos is found, this method - * returns the index at the closest xPos. + * Returns the first Entry object found at the given x-value with binary + * search. If the no Entry at the specified x-value is found, this method + * returns the index at the closest x-value. * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xPos + * @param xValue * @return */ - T getEntryForXPos(float xPos); + T getEntryForXValue(float xValue); /** - * Returns all Entry objects found at the given xPos with binary - * search. An empty array if no Entry object at that xPos. + * Returns all Entry objects found at the given x-value with binary + * search. An empty array if no Entry object at that x-value. * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xPos + * @param xValue * @return */ - List getEntriesForXPos(float xPos); + List getEntriesForXValue(float xValue); /** * Returns the Entry object found at the given index (NOT xIndex) in the values array. @@ -102,17 +102,17 @@ public interface IDataSet { T getEntryForIndex(int index); /** - * Returns the first Entry index found at the given xPos with binary - * search. If the no Entry at the specified xPos is found, this method - * returns the Entry at the closest xPos. + * Returns the first Entry index found at the given x-value with binary + * search. If the no Entry at the specified x-value is found, this method + * returns the Entry at the closest x-value. * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xPos + * @param xValue * @param rounding determine to round up/down/closest if there is no Entry matching the provided x-index * @return */ - int getEntryIndex(float xPos, DataSet.Rounding rounding); + int getEntryIndex(float xValue, DataSet.Rounding rounding); /** * Returns the position of the provided entry in the DataSets Entry array. @@ -183,12 +183,12 @@ public interface IDataSet { boolean removeEntry(T e); /** - * Removes the Entry object closest to the given xPos from the DataSet. + * Removes the Entry object closest to the given x-value from the DataSet. * Returns true if an Entry was removed, false if no Entry could be removed. * - * @param xPos + * @param xValue */ - boolean removeEntryByXPos(float xPos); + boolean removeEntryByXValue(float xValue); /** * Removes the Entry object at the given index in the values array from the DataSet. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index 7402f3f2a2..cf41858f46 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -341,7 +341,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - BarEntry e = set.getEntryForXPos(high.getX()); + BarEntry e = set.getEntryForXValue(high.getX()); if (!isInBoundsX(e, set)) continue; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java index 8f2447d960..5b58a01deb 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java @@ -85,8 +85,8 @@ public void set(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCan float low = chart.getLowestVisibleX(); float high = chart.getHighestVisibleX(); - Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); + Entry entryFrom = dataSet.getEntryForXValue(low, DataSet.Rounding.DOWN); + Entry entryTo = dataSet.getEntryForXValue(high, DataSet.Rounding.UP); min = dataSet.getEntryIndex(entryFrom); max = dataSet.getEntryIndex(entryTo); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index e4fc5cef5a..2a0bfcbb76 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -194,7 +194,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { continue; // In bubble charts - it makes sense to have multiple bubbles on the same X value in the same dataset. - final List entries = set.getEntriesForXPos(high.getX()); + final List entries = set.getEntriesForXValue(high.getX()); for (BubbleEntry entry : entries) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index cdd79f41c3..ffc7479c8b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -317,7 +317,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - CandleEntry e = set.getEntryForXPos(high.getX()); + CandleEntry e = set.getEntryForXValue(high.getX()); if (!isInBoundsX(e, set)) continue; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 85e95a7621..e29686bf63 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -663,7 +663,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - Entry e = set.getEntryForXPos(high.getX()); + Entry e = set.getEntryForXValue(high.getX()); if (!isInBoundsX(e, set)) continue; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index 2341ca5b59..e3da3cce7a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -143,7 +143,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - Entry e = set.getEntryForXPos(high.getX()); + Entry e = set.getEntryForXValue(high.getX()); if (!isInBoundsX(e, set)) continue; diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java index 4ade7977b1..381c2b23da 100644 --- a/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java @@ -141,7 +141,7 @@ public void testAddRemoveEntry() { } @Test - public void testGetEntryForXPos() { + public void testGetEntryForXValue() { List entries = new ArrayList(); entries.add(new Entry(10, 10)); @@ -150,31 +150,31 @@ public void testGetEntryForXPos() { ScatterDataSet set = new ScatterDataSet(entries, ""); - Entry closest = set.getEntryForXPos(17, DataSet.Rounding.CLOSEST); + Entry closest = set.getEntryForXValue(17, DataSet.Rounding.CLOSEST); assertEquals(15, closest.getX(), 0.01f); assertEquals(5, closest.getY(), 0.01f); - closest = set.getEntryForXPos(17, DataSet.Rounding.DOWN); + closest = set.getEntryForXValue(17, DataSet.Rounding.DOWN); assertEquals(15, closest.getX(), 0.01f); assertEquals(5, closest.getY(), 0.01f); - closest = set.getEntryForXPos(15, DataSet.Rounding.DOWN); + closest = set.getEntryForXValue(15, DataSet.Rounding.DOWN); assertEquals(15, closest.getX(), 0.01f); assertEquals(5, closest.getY(), 0.01f); - closest = set.getEntryForXPos(14, DataSet.Rounding.DOWN); + closest = set.getEntryForXValue(14, DataSet.Rounding.DOWN); assertEquals(10, closest.getX(), 0.01f); assertEquals(10, closest.getY(), 0.01f); - closest = set.getEntryForXPos(17, DataSet.Rounding.UP); + closest = set.getEntryForXValue(17, DataSet.Rounding.UP); assertEquals(21, closest.getX(), 0.01f); assertEquals(5, closest.getY(), 0.01f); - closest = set.getEntryForXPos(21, DataSet.Rounding.UP); + closest = set.getEntryForXValue(21, DataSet.Rounding.UP); assertEquals(21, closest.getX(), 0.01f); assertEquals(5, closest.getY(), 0.01f); - closest = set.getEntryForXPos(21, DataSet.Rounding.CLOSEST); + closest = set.getEntryForXValue(21, DataSet.Rounding.CLOSEST); assertEquals(21, closest.getX(), 0.01f); assertEquals(5, closest.getY(), 0.01f); } From 51f0e53a7f1de86bd0bc2fc2e3f2087ce54fe1bc Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 8 Aug 2016 18:25:44 +0300 Subject: [PATCH 020/291] Reinvented Markers - interface based, enhanced default behaviour Two default implementations exist now: MarkerView, MarkerImage The default behaviour constraints the marker to the view's bounds. --- MPChartExample/AndroidManifest.xml | 10 - MPChartExample/build.gradle | 2 +- .../mpchartexample/BarChartActivity.java | 4 +- .../BarChartActivityMultiDataset.java | 5 +- .../DynamicalAddingActivity.java | 4 +- .../InvertedLineChartActivity.java | 5 +- .../mpchartexample/LineChartActivity1.java | 5 +- .../mpchartexample/RadarChartActivitry.java | 6 +- .../mpchartexample/custom/MyMarkerView.java | 12 +- .../custom/RadarMarkerView.java | 12 +- .../mpchartexample/custom/RealmDemoData.java | 180 ---------------- .../mpchartexample/custom/RealmFloat.java | 27 --- .../custom/StackedBarsMarkerView.java | 12 +- .../mpchartexample/custom/XYMarkerView.java | 12 +- .../fragments/BarChartFrag.java | 4 +- .../fragments/ScatterChartFrag.java | 4 +- .../notimportant/MainActivity.java | 9 - .../realm/RealmBaseActivity.java | 203 ------------------ .../realm/RealmDatabaseActivityBar.java | 69 ------ .../realm/RealmDatabaseActivityBubble.java | 71 ------ .../realm/RealmDatabaseActivityCandle.java | 77 ------- .../RealmDatabaseActivityHorizontalBar.java | 74 ------- .../realm/RealmDatabaseActivityLine.java | 77 ------- .../realm/RealmDatabaseActivityPie.java | 83 ------- .../realm/RealmDatabaseActivityRadar.java | 78 ------- .../realm/RealmDatabaseActivityScatter.java | 73 ------- .../realm/RealmMainActivity.java | 131 ----------- .../realm/RealmWikiExample.java | 137 ------------ .../mpchartexample/realm/Score.java | 51 ----- .../mikephil/charting/charts/Chart.java | 75 ++++--- .../mikephil/charting/components/IMarker.java | 52 +++++ .../charting/components/MarkerImage.java | 167 ++++++++++++++ .../charting/components/MarkerView.java | 118 ++++++---- .../github/mikephil/charting/utils/FSize.java | 5 +- .../mikephil/charting/utils/MPPointF.java | 7 +- build.gradle | 2 +- 36 files changed, 384 insertions(+), 1479 deletions(-) delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index ab730cd2bd..4131688a8d 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -47,22 +47,12 @@ - - - - - - - - - - diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index c0d4745dcc..752d3fd321 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -1,5 +1,5 @@ apply plugin: 'com.android.application' -apply plugin: 'realm-android' +//apply plugin: 'realm-android' android { compileSdkVersion 23 diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index b51735f1e6..36dbd042b6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -119,7 +119,9 @@ protected void onCreate(Bundle savedInstanceState) { // l.setCustom(ColorTemplate.VORDIPLOM_COLORS, new String[] { "abc", // "def", "ghj", "ikl", "mno" }); - mChart.setMarkerView(new XYMarkerView(this, xAxisFormatter)); + XYMarkerView mv = new XYMarkerView(this, xAxisFormatter); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); // Set the marker to the chart setData(12, 50); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index a75182bce8..f27de86b53 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -70,9 +70,8 @@ protected void onCreate(Bundle savedInstanceState) { // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - - // set the marker to the chart - mChart.setMarkerView(mv); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); // Set the marker to the chart mSeekBarX.setProgress(10); mSeekBarY.setProgress(100); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index 2d1cbcd907..3462a74af8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -87,11 +87,11 @@ private void removeLastEntry() { if (set != null) { - Entry e = set.getEntryForXPos(set.getEntryCount() - 1); + Entry e = set.getEntryForXValue(set.getEntryCount() - 1); data.removeEntry(e, 0); // or remove by index - // mData.removeEntryByXPos(xIndex, dataSetIndex); + // mData.removeEntryByXValue(xIndex, dataSetIndex); data.notifyDataChanged(); mChart.notifyDataSetChanged(); mChart.invalidate(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index 1f760f871e..dc41160eb2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -79,9 +79,8 @@ protected void onCreate(Bundle savedInstanceState) { // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - - // set the marker to the chart - mChart.setMarkerView(mv); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); // Set the marker to the chart XAxis xl = mChart.getXAxis(); xl.setAvoidFirstLastClipping(true); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index d727370ff0..2de91ee64f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -92,9 +92,8 @@ protected void onCreate(Bundle savedInstanceState) { // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - - // set the marker to the chart - mChart.setMarkerView(mv); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); // Set the marker to the chart // x-axis limit line LimitLine llXAxis = new LimitLine(10f, "Index 10"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index 97b871a0da..3326442a87 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -14,6 +14,7 @@ import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.Legend.LegendPosition; +import com.github.mikephil.charting.components.MarkerImage; import com.github.mikephil.charting.components.MarkerView; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; @@ -58,9 +59,8 @@ protected void onCreate(Bundle savedInstanceState) { // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it MarkerView mv = new RadarMarkerView(this, R.layout.radar_markerview); - - // set the marker to the chart - mChart.setMarkerView(mv); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); // Set the marker to the chart setData(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java index 53a3bc47f4..8d97346195 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java @@ -8,6 +8,7 @@ import com.github.mikephil.charting.data.CandleEntry; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.xxmassdeveloper.mpchartexample.R; @@ -40,17 +41,12 @@ public void refreshContent(Entry e, Highlight highlight) { tvContent.setText("" + Utils.formatNumber(e.getY(), 0, true)); } - } - @Override - public int getXOffset(float xpos) { - // this will center the marker-view horizontally - return -(getWidth() / 2); + super.refreshContent(e, highlight); } @Override - public int getYOffset(float ypos) { - // this will cause the marker-view to be above the selected value - return -getHeight(); + public MPPointF getOffset() { + return new MPPointF(-(getWidth() / 2), -getHeight()); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java index 61f7209e44..b5aa1960d1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java @@ -10,6 +10,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.formatter.FormattedStringCache; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.xxmassdeveloper.mpchartexample.R; @@ -38,17 +39,12 @@ public RadarMarkerView(Context context, int layoutResource) { public void refreshContent(Entry e, Highlight highlight) { float value = e.getY(); tvContent.setText(mFormattedStringCache.getFormattedValue(value) + " %"); - } - @Override - public int getXOffset(float xpos) { - // this will center the marker-view horizontally - return -(getWidth() / 2); + super.refreshContent(e, highlight); } @Override - public int getYOffset(float ypos) { - // this will cause the marker-view to be above the selected value - return -getHeight()-10; + public MPPointF getOffset() { + return new MPPointF(-(getWidth() / 2), -getHeight() - 10); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java deleted file mode 100644 index 7becc88c90..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java +++ /dev/null @@ -1,180 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.custom; - - -import io.realm.RealmList; -import io.realm.RealmObject; - -/** - * Demo class that encapsulates data stored in realm.io database. - * This class represents data suitable for all chart-types. - */ -public class RealmDemoData extends RealmObject { - - private float yValue; - private float xValue; - - private float open, close, high, low; - - private float bubbleSize; - - private RealmList stackValues; - - private String someStringField; - - /** - * label for pie entries - */ - private String label; - - // ofc there could me more fields here... - - public RealmDemoData() { - - } - - public RealmDemoData(float yValue) { - this.yValue = yValue; - } - - public RealmDemoData(float xValue, float yValue) { - this.xValue = xValue; - this.yValue = yValue; - } - - /** - * Constructor for stacked bars. - * - * @param xValue - * @param stackValues - */ - public RealmDemoData(float xValue, float[] stackValues) { - this.xValue = xValue; - this.stackValues = new RealmList(); - - for (float val : stackValues) { - this.stackValues.add(new RealmFloat(val)); - } - } - - /** - * Constructor for candles. - * - * @param xValue - * @param high - * @param low - * @param open - * @param close - */ - public RealmDemoData(float xValue, float high, float low, float open, float close) { - this.yValue = (high + low) / 2f; - this.high = high; - this.low = low; - this.open = open; - this.close = close; - this.xValue = xValue; - } - - /** - * Constructor for bubbles. - * - * @param xValue - * @param yValue - * @param bubbleSize - */ - public RealmDemoData(float xValue, float yValue, float bubbleSize) { - this.xValue = xValue; - this.yValue = yValue; - this.bubbleSize = bubbleSize; - } - - /** - * Constructor for pie chart. - * - * @param yValue - * @param label - */ - public RealmDemoData(float yValue, String label) { - this.yValue = yValue; - this.label = label; - } - - public float getyValue() { - return yValue; - } - - public void setyValue(float yValue) { - this.yValue = yValue; - } - - public float getxValue() { - return xValue; - } - - public void setxValue(float xValue) { - this.xValue = xValue; - } - - public RealmList getStackValues() { - return stackValues; - } - - public void setStackValues(RealmList stackValues) { - this.stackValues = stackValues; - } - - public float getOpen() { - return open; - } - - public void setOpen(float open) { - this.open = open; - } - - public float getClose() { - return close; - } - - public void setClose(float close) { - this.close = close; - } - - public float getHigh() { - return high; - } - - public void setHigh(float high) { - this.high = high; - } - - public float getLow() { - return low; - } - - public void setLow(float low) { - this.low = low; - } - - public float getBubbleSize() { - return bubbleSize; - } - - public void setBubbleSize(float bubbleSize) { - this.bubbleSize = bubbleSize; - } - - public String getSomeStringField() { - return someStringField; - } - - public void setSomeStringField(String someStringField) { - this.someStringField = someStringField; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } -} \ No newline at end of file diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java deleted file mode 100644 index 15b027b3ff..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.custom; - -import io.realm.RealmObject; - -/** - * Created by Philipp Jahoda on 09/11/15. - */ -public class RealmFloat extends RealmObject { - - private float floatValue; - - public RealmFloat() { - - } - - public RealmFloat(float floatValue) { - this.floatValue = floatValue; - } - - public float getFloatValue() { - return floatValue; - } - - public void setFloatValue(float value) { - this.floatValue = value; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java index 0b8938778d..d0781b2ee2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java @@ -8,6 +8,7 @@ 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.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.xxmassdeveloper.mpchartexample.R; @@ -46,17 +47,12 @@ public void refreshContent(Entry e, Highlight highlight) { tvContent.setText("" + Utils.formatNumber(e.getY(), 0, true)); } - } - @Override - public int getXOffset(float xpos) { - // this will center the marker-view horizontally - return -(getWidth() / 2); + super.refreshContent(e, highlight); } @Override - public int getYOffset(float ypos) { - // this will cause the marker-view to be above the selected value - return -getHeight(); + public MPPointF getOffset() { + return new MPPointF(-(getWidth() / 2), -getHeight()); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java index 6bfa313c77..7a7fe726d5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java @@ -9,6 +9,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.formatter.AxisValueFormatter; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.xxmassdeveloper.mpchartexample.R; @@ -40,17 +41,12 @@ public XYMarkerView(Context context, AxisValueFormatter xAxisValueFormatter) { public void refreshContent(Entry e, Highlight highlight) { tvContent.setText("x: " + xAxisValueFormatter.getFormattedValue(e.getX(), null) + ", y: " + format.format(e.getY())); - } - @Override - public int getXOffset(float xpos) { - // this will center the marker-view horizontally - return -(getWidth() / 2); + super.refreshContent(e, highlight); } @Override - public int getYOffset(float ypos) { - // this will cause the marker-view to be above the selected value - return -getHeight(); + public MPPointF getOffset() { + return new MPPointF(-(getWidth() / 2), -getHeight()); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java index 29b378d44b..b77c0b815f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java @@ -37,8 +37,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa mChart.setOnChartGestureListener(this); MyMarkerView mv = new MyMarkerView(getActivity(), R.layout.custom_marker_view); - - mChart.setMarkerView(mv); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); mChart.setDrawGridBackground(false); mChart.setDrawBarShadow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java index 652a796321..cb1bd1c7e7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java @@ -34,8 +34,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); MyMarkerView mv = new MyMarkerView(getActivity(), R.layout.custom_marker_view); - - mChart.setMarkerView(mv); + mv.setChartView(mChart); // For bounds control + mChart.setMarker(mv); mChart.setDrawGridBackground(false); mChart.setData(generateScatterData(6, 10000, 200)); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index 617e43c021..512f4baa14 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -46,7 +46,6 @@ import com.xxmassdeveloper.mpchartexample.StackedBarActivity; import com.xxmassdeveloper.mpchartexample.StackedBarActivityNegative; import com.xxmassdeveloper.mpchartexample.fragments.SimpleChartDemo; -import com.xxmassdeveloper.mpchartexample.realm.RealmMainActivity; import java.util.ArrayList; @@ -131,10 +130,6 @@ protected void onCreate(Bundle savedInstanceState) { "BarChart positive / negative", "This demonstrates how to create a BarChart with positive and negative values in different colors.")); - ContentItem realm = new ContentItem( - "Realm.io Database", - "This demonstrates how to use this library with Realm.io mobile database."); - objects.add(realm); ContentItem time = new ContentItem( "Time Chart", @@ -274,10 +269,6 @@ public void onItemClick(AdapterView av, View v, int pos, long arg3) { i = new Intent(this, BarChartPositiveNegative.class); startActivity(i); break; - case 28: - i = new Intent(this, RealmMainActivity.class); - startActivity(i); - break; case 29: i = new Intent(this, LineChartTime.class); startActivity(i); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java deleted file mode 100644 index d4fef69576..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ /dev/null @@ -1,203 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.os.Bundle; - -import com.github.mikephil.charting.charts.BarLineChartBase; -import com.github.mikephil.charting.charts.Chart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.formatter.PercentFormatter; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import io.realm.Realm; -import io.realm.RealmConfiguration; - -/** - * Created by Philipp Jahoda on 05/11/15. - */ -public abstract class RealmBaseActivity extends DemoBase { - - protected Realm mRealm; - - protected Typeface mTf; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setTitle("Realm.io Examples"); - } - - protected void setup(Chart chart) { - - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - // no description text - chart.setDescription(""); - chart.setNoDataTextDescription("You need to provide data for the chart."); - - // enable touch gestures - chart.setTouchEnabled(true); - - if (chart instanceof BarLineChartBase) { - - BarLineChartBase mChart = (BarLineChartBase) chart; - - mChart.setDrawGridBackground(false); - - // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - - // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(false); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.removeAllLimitLines(); // reset all limit lines to avoid overlapping lines - leftAxis.setTypeface(mTf); - leftAxis.setTextSize(8f); - leftAxis.setTextColor(Color.DKGRAY); - leftAxis.setValueFormatter(new PercentFormatter()); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setTypeface(mTf); - xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); - xAxis.setTextSize(8f); - xAxis.setTextColor(Color.DKGRAY); - - mChart.getAxisRight().setEnabled(false); - } - } - - protected void styleData(ChartData data) { - data.setValueTypeface(mTf); - data.setValueTextSize(8f); - data.setValueTextColor(Color.DKGRAY); - data.setValueFormatter(new PercentFormatter()); - } - - @Override - protected void onResume() { - super.onResume(); - - // Create a RealmConfiguration that saves the Realm file in the app's "files" directory. - RealmConfiguration realmConfig = new RealmConfiguration.Builder(getApplicationContext()).build(); - Realm.setDefaultConfiguration(realmConfig); - - mRealm = Realm.getDefaultInstance(); - } - - @Override - protected void onPause() { - super.onPause(); - mRealm.close(); - } - - protected void writeToDB(int objectCount) { - - mRealm.beginTransaction(); - - mRealm.delete(RealmDemoData.class); - - for (int i = 0; i < objectCount; i++) { - - float value = 40f + (float) (Math.random() * 60f); - - RealmDemoData d = new RealmDemoData(i, value); - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } - - protected void writeToDBStack(int objectCount) { - - mRealm.beginTransaction(); - - mRealm.delete(RealmDemoData.class); - - for (int i = 0; i < objectCount; i++) { - - float val1 = 34f + (float) (Math.random() * 12.0f); - float val2 = 34f + (float) (Math.random() * 12.0f); - float[] stack = new float[]{val1, val2, 100 - val1 - val2}; - - RealmDemoData d = new RealmDemoData(i, stack); - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } - - protected void writeToDBCandle(int objectCount) { - - mRealm.beginTransaction(); - - mRealm.delete(RealmDemoData.class); - - for (int i = 0; i < objectCount; i++) { - - float mult = 50; - float val = (float) (Math.random() * 40) + mult; - - float high = (float) (Math.random() * 9) + 8f; - float low = (float) (Math.random() * 9) + 8f; - - float open = (float) (Math.random() * 6) + 1f; - float close = (float) (Math.random() * 6) + 1f; - - boolean even = i % 2 == 0; - - RealmDemoData d = new RealmDemoData(i, val + high, val - low, even ? val + open : val - open, - even ? val - close : val + close); - - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } - - protected void writeToDBBubble(int objectCount) { - - mRealm.beginTransaction(); - - mRealm.delete(RealmDemoData.class); - - for (int i = 0; i < objectCount; i++) { - - float value = 30f + (float) (Math.random() * 100.0); - float size = 15f + (float) (Math.random() * 20.0); - - RealmDemoData d = new RealmDemoData(i, value, size); - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } - - protected void writeToDBPie() { - - mRealm.beginTransaction(); - - mRealm.delete(RealmDemoData.class); - - float value1 = 15f + (float) (Math.random() * 8f); - float value2 = 15f + (float) (Math.random() * 8f); - float value3 = 15f + (float) (Math.random() * 8f); - float value4 = 15f + (float) (Math.random() * 8f); - float value5 = 100f - value1 - value2 - value3 - value4; - - float[] values = new float[] { value1, value2, value3, value4, value5 }; - String[] labels = new String[]{ "iOS", "Android", "WP 10", "BlackBerry", "Other"}; - - for (int i = 0; i < values.length; i++) { - RealmDemoData d = new RealmDemoData(values[i], labels[i]); - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java deleted file mode 100644 index c87290050d..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityBar extends RealmBaseActivity { - - private BarChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_barchart_noseekbar); - - mChart = (BarChart) findViewById(R.id.chart1); - setup(mChart); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDB(20); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet(result, "xValue", "yValue"); // stacked entries - set.setColors(new int[] {ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); - set.setLabel("Realm BarDataSet"); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - BarData data = new BarData(dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.setFitBars(true); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java deleted file mode 100644 index d0aa25b864..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.BubbleChart; -import com.github.mikephil.charting.data.BubbleData; -import com.github.mikephil.charting.data.realm.implementation.RealmBubbleDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityBubble extends RealmBaseActivity { - - private BubbleChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_bubblechart_noseekbar); - - mChart = (BubbleChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.getXAxis().setDrawGridLines(false); - mChart.getAxisLeft().setDrawGridLines(false); - mChart.setPinchZoom(true); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDBBubble(10); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - RealmBubbleDataSet set = new RealmBubbleDataSet(result, "xValue", "yValue", "bubbleSize"); - set.setLabel("Realm BubbleDataSet"); - set.setColors(ColorTemplate.COLORFUL_COLORS, 110); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - BubbleData data = new BubbleData(dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java deleted file mode 100644 index a388df3741..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.graphics.Paint; -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.CandleStickChart; -import com.github.mikephil.charting.data.CandleData; -import com.github.mikephil.charting.data.realm.implementation.RealmCandleDataSet; -import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityCandle extends RealmBaseActivity { - - private CandleStickChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_candlechart_noseekbar); - - mChart = (CandleStickChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.getAxisLeft().setDrawGridLines(false); - mChart.getXAxis().setDrawGridLines(false); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDBCandle(50); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - RealmCandleDataSet set = new RealmCandleDataSet(result, "xValue", "high", "low", "open", "close"); - set.setLabel("Realm CandleDataSet"); - set.setShadowColor(Color.DKGRAY); - set.setShadowWidth(0.7f); - set.setDecreasingColor(Color.RED); - set.setDecreasingPaintStyle(Paint.Style.FILL); - set.setIncreasingColor(Color.rgb(122, 242, 84)); - set.setIncreasingPaintStyle(Paint.Style.STROKE); - set.setNeutralColor(Color.BLUE); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - CandleData data = new CandleData(dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java deleted file mode 100644 index 32d1234fe2..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.HorizontalBarChart; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityHorizontalBar extends RealmBaseActivity { - - private HorizontalBarChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_horizontalbarchart_noseekbar); - - mChart = (HorizontalBarChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.getAxisLeft().setAxisMinValue(0f); - mChart.setDrawValueAboveBar(false); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDBStack(50); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet(result, "xValue", "stackValues", "floatValue"); // stacked entries - set.setColors(new int[]{ColorTemplate.rgb("#8BC34A"), ColorTemplate.rgb("#FFC107"), ColorTemplate.rgb("#9E9E9E")}); - set.setLabel("Mobile OS distribution"); - set.setStackLabels(new String[]{"iOS", "Android", "Other"}); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - BarData data = new BarData(dataSets); - styleData(data); - data.setValueTextColor(Color.WHITE); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java deleted file mode 100644 index 6d2396c22b..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityLine extends RealmBaseActivity { - - private LineChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_linechart_noseekbar); - - mChart = (LineChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.getAxisLeft().setAxisMaxValue(150f); - mChart.getAxisLeft().setAxisMinValue(0f); - mChart.getAxisLeft().setDrawGridLines(false); - mChart.getXAxis().setDrawGridLines(false); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDB(40); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - RealmLineDataSet set = new RealmLineDataSet(result, "xValue", "yValue"); - set.setDrawCubic(false); - set.setLabel("Realm LineDataSet"); - set.setDrawCircleHole(false); - set.setColor(ColorTemplate.rgb("#FF5722")); - set.setCircleColor(ColorTemplate.rgb("#FF5722")); - set.setLineWidth(1.8f); - set.setCircleSize(3.6f); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - LineData data = new LineData(dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java deleted file mode 100644 index 7b1eb675d9..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.graphics.Typeface; -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.view.WindowManager; - -import com.github.mikephil.charting.charts.PieChart; -import com.github.mikephil.charting.data.PieData; -import com.github.mikephil.charting.data.realm.implementation.RealmPieDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityPie extends RealmBaseActivity { - - private PieChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_piechart_noseekbar); - - mChart = (PieChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.setCenterText(generateCenterSpannableText()); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDBPie(); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmPieDataSet set = new RealmPieDataSet(result, "yValue", "label"); // stacked entries - set.setColors(ColorTemplate.VORDIPLOM_COLORS); - set.setLabel("Example market share"); - set.setSliceSpace(2); - - // create a data object with the dataset list - PieData data = new PieData(set); - styleData(data); - data.setValueTextColor(Color.WHITE); - data.setValueTextSize(12f); - - // set data - mChart.setData(data); - mChart.animateY(1400); - } - - private SpannableString generateCenterSpannableText() { - - SpannableString s = new SpannableString("Realm.io\nmobile database"); - s.setSpan(new ForegroundColorSpan(Color.rgb(240, 115, 126)), 0, 8, 0); - s.setSpan(new RelativeSizeSpan(2.2f), 0, 8, 0); - s.setSpan(new StyleSpan(Typeface.ITALIC), 9, s.length(), 0); - s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), 9, s.length(), 0); - s.setSpan(new RelativeSizeSpan(0.85f), 9, s.length(), 0); - return s; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java deleted file mode 100644 index 411f4b6ac9..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.data.RadarData; -import com.github.mikephil.charting.data.realm.implementation.RealmRadarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityRadar extends RealmBaseActivity { - - private RadarChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_radarchart_noseekbar); - - mChart = (RadarChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.getYAxis().setEnabled(false); - mChart.getXAxis().setEnabled(false); - mChart.setWebAlpha(180); - mChart.setWebColorInner(Color.DKGRAY); - mChart.setWebColor(Color.GRAY); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDB(7); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmRadarDataSet set = new RealmRadarDataSet(result, "yValue"); // stacked entries - set.setLabel("Realm RadarDataSet"); - set.setDrawFilled(true); - set.setColor(ColorTemplate.rgb("#009688")); - set.setFillColor(ColorTemplate.rgb("#009688")); - set.setFillAlpha(130); - set.setLineWidth(2f); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - RadarData data = new RadarData(dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java deleted file mode 100644 index 14175ac73a..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.ScatterChart; -import com.github.mikephil.charting.data.ScatterData; -import com.github.mikephil.charting.data.realm.implementation.RealmScatterDataSet; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityScatter extends RealmBaseActivity { - - private ScatterChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_scatterchart_noseekbar); - - mChart = (ScatterChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.getAxisLeft().setDrawGridLines(false); - mChart.getXAxis().setDrawGridLines(false); - mChart.setPinchZoom(true); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDB(45); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - RealmScatterDataSet set = new RealmScatterDataSet(result, "xValue", "yValue"); - set.setLabel("Realm ScatterDataSet"); - set.setScatterShapeSize(9f); - set.setColor(ColorTemplate.rgb("#CDDC39")); - set.setScatterShape(ScatterChart.ScatterShape.CIRCLE); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - ScatterData data = new ScatterData(dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java deleted file mode 100644 index 3198320272..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.WindowManager; -import android.widget.AdapterView; -import android.widget.ListView; - -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.notimportant.ContentItem; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; -import com.xxmassdeveloper.mpchartexample.notimportant.MyAdapter; - -import java.util.ArrayList; - -import io.realm.Realm; -import io.realm.RealmConfiguration; - -/** - * Created by Philipp Jahoda on 07/12/15. - */ -public class RealmMainActivity extends DemoBase implements AdapterView.OnItemClickListener { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_main); - - setTitle("Realm.io Examples"); - - ArrayList objects = new ArrayList(); - - objects.add(new ContentItem("Line Chart", "Creating a LineChart with Realm.io database")); - objects.add(new ContentItem("Bar Chart", - "Creating a BarChart with Realm.io database")); - objects.add(new ContentItem("Horizontal Bar Chart", - "Creating a HorizontalBarChart with Realm.io database")); - objects.add(new ContentItem("Scatter Chart", - "Creating a ScatterChart with Realm.io database")); - objects.add(new ContentItem("Candle Stick Chart", "Creating a CandleStickChart with Realm.io database")); - objects.add(new ContentItem("Bubble Chart", "Creating a BubbleChart with Realm.io database")); - objects.add(new ContentItem("Pie Chart", "Creating a PieChart with Realm.io database")); - objects.add(new ContentItem("Radar Chart", "Creating a RadarChart with Realm.io database")); - objects.add(new ContentItem("Realm Wiki", "This is the code related to the wiki entry about realm.io on the MPAndroidChart github page.")); - - MyAdapter adapter = new MyAdapter(this, objects); - - ListView lv = (ListView) findViewById(R.id.listView1); - lv.setAdapter(adapter); - - lv.setOnItemClickListener(this); - - // Create a RealmConfiguration that saves the Realm file in the app's "files" directory. - RealmConfiguration realmConfig = new RealmConfiguration.Builder(getApplicationContext()).build(); - Realm.setDefaultConfiguration(realmConfig); - - Realm realm = Realm.getDefaultInstance(); - realm.beginTransaction(); - realm.deleteAll(); - realm.commitTransaction(); - } - - @Override - public void onItemClick(AdapterView av, View v, int pos, long arg3) { - - Intent i; - - switch (pos) { - case 0: - i = new Intent(this, RealmDatabaseActivityLine.class); - startActivity(i); - break; - case 1: - i = new Intent(this, RealmDatabaseActivityBar.class); - startActivity(i); - break; - case 2: - i = new Intent(this, RealmDatabaseActivityHorizontalBar.class); - startActivity(i); - break; - case 3: - i = new Intent(this, RealmDatabaseActivityScatter.class); - startActivity(i); - break; - case 4: - i = new Intent(this, RealmDatabaseActivityCandle.class); - startActivity(i); - break; - case 5: - i = new Intent(this, RealmDatabaseActivityBubble.class); - startActivity(i); - break; - case 6: - i = new Intent(this, RealmDatabaseActivityPie.class); - startActivity(i); - break; - case 7: - i = new Intent(this, RealmDatabaseActivityRadar.class); - startActivity(i); - break; - case 8: - i = new Intent(this, RealmWikiExample.class); - startActivity(i); - break; - } - - overridePendingTransition(R.anim.move_right_in_activity, R.anim.move_left_out_activity); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.realm, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse("https://realm.io/")); - startActivity(i); - - return super.onOptionsItemSelected(item); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java deleted file mode 100644 index 7682bca957..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; -import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; -import com.github.mikephil.charting.formatter.AxisValueFormatter; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 18/12/15. - */ -public class RealmWikiExample extends RealmBaseActivity { - - private LineChart lineChart; - private BarChart barChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_realm_wiki); - - lineChart = (LineChart) findViewById(R.id.lineChart); - barChart = (BarChart) findViewById(R.id.barChart); - setup(lineChart); - setup(barChart); - - lineChart.setExtraBottomOffset(5f); - barChart.setExtraBottomOffset(5f); - - lineChart.getAxisLeft().setDrawGridLines(false); - lineChart.getXAxis().setDrawGridLines(false); - lineChart.getXAxis().setLabelCount(5); - lineChart.getXAxis().setGranularity(1f); - barChart.getAxisLeft().setDrawGridLines(false); - barChart.getXAxis().setDrawGridLines(false); - barChart.getXAxis().setLabelCount(5); - barChart.getXAxis().setGranularity(1f); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - mRealm.beginTransaction(); - - // write some demo-data into the realm.io database - Score score1 = new Score(100f, 0f, "Peter"); - mRealm.copyToRealm(score1); - Score score2 = new Score(110f, 1f, "Lisa"); - mRealm.copyToRealm(score2); - Score score3 = new Score(130f, 2f, "Dennis"); - mRealm.copyToRealm(score3); - Score score4 = new Score(70f, 3f, "Luke"); - mRealm.copyToRealm(score4); - Score score5 = new Score(80f, 4f, "Sarah"); - mRealm.copyToRealm(score5); - - mRealm.commitTransaction(); - - // add data to the chart - setData(); - } - - private void setData() { - - // LINE-CHART - final RealmResults results = mRealm.where(Score.class).findAll(); - - - AxisValueFormatter formatter = new AxisValueFormatter() { - @Override - public String getFormattedValue(float value, AxisBase axis) { - return results.get((int) value).getPlayerName(); - } - - @Override - public int getDecimalDigits() { - return 0; - } - }; - - lineChart.getXAxis().setValueFormatter(formatter); - barChart.getXAxis().setValueFormatter(formatter); - - RealmLineDataSet lineDataSet = new RealmLineDataSet(results, "scoreNr", "totalScore"); - lineDataSet.setDrawCubic(false); - lineDataSet.setLabel("Result Scores"); - lineDataSet.setDrawCircleHole(false); - lineDataSet.setColor(ColorTemplate.rgb("#FF5722")); - lineDataSet.setCircleColor(ColorTemplate.rgb("#FF5722")); - lineDataSet.setLineWidth(1.8f); - lineDataSet.setCircleSize(3.6f); - - ArrayList dataSets = new ArrayList(); - dataSets.add(lineDataSet); - - LineData lineData = new LineData(dataSets); - styleData(lineData); - - // set data - lineChart.setData(lineData); - lineChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - - - // BAR-CHART - RealmBarDataSet barDataSet = new RealmBarDataSet(results, "scoreNr", "totalScore"); - barDataSet.setColors(new int[]{ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); - barDataSet.setLabel("Realm BarDataSet"); - - ArrayList barDataSets = new ArrayList(); - barDataSets.add(barDataSet); - - BarData barData = new BarData(barDataSets); - styleData(barData); - - barChart.setData(barData); - barChart.setFitBars(true); - barChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java deleted file mode 100644 index 870e371491..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - - -import io.realm.RealmObject; - -/** - * our data object - */ -public class Score extends RealmObject { - - private float totalScore; - - private float scoreNr; - - private String playerName; - - public Score() { - } - - public Score(float totalScore, float scoreNr, String playerName) { - this.scoreNr = scoreNr; - this.playerName = playerName; - this.totalScore = totalScore; - } - - // all getters and setters... - - public float getTotalScore() { - return totalScore; - } - - public void setTotalScore(float totalScore) { - this.totalScore = totalScore; - } - - public float getScoreNr() { - return scoreNr; - } - - public void setScoreNr(float scoreNr) { - this.scoreNr = scoreNr; - } - - public String getPlayerName() { - return playerName; - } - - public void setPlayerName(String playerName) { - this.playerName = playerName; - } -} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 74823825c8..93d07572ae 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -27,8 +27,8 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.animation.EasingFunction; +import com.github.mikephil.charting.components.IMarker; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.MarkerView; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.Entry; @@ -710,12 +710,12 @@ public ChartTouchListener getOnTouchListener() { /** * if set to true, the marker view is drawn when a value is clicked */ - protected boolean mDrawMarkerViews = true; + protected boolean mDrawMarkers = true; /** * the view that represents the marker */ - protected MarkerView mMarkerView; + protected IMarker mMarker; /** * draws all MarkerViews on the highlighted positions @@ -723,7 +723,7 @@ public ChartTouchListener getOnTouchListener() { protected void drawMarkers(Canvas canvas) { // if there is no marker view or drawing marker is disabled - if (mMarkerView == null || !mDrawMarkerViews || !valuesToHighlight()) + if (mMarker == null || !isDrawMarkersEnabled() || !valuesToHighlight()) return; for (int i = 0; i < mIndicesToHighlight.length; i++) { @@ -746,19 +746,10 @@ protected void drawMarkers(Canvas canvas) { continue; // callbacks to update the content - mMarkerView.refreshContent(e, highlight); + mMarker.refreshContent(e, highlight); - mMarkerView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - mMarkerView.layout(0, 0, mMarkerView.getMeasuredWidth(), - mMarkerView.getMeasuredHeight()); - - if (pos[1] - mMarkerView.getHeight() <= 0) { - float y = mMarkerView.getHeight() - pos[1]; - mMarkerView.draw(canvas, pos[0], pos[1] + y); - } else { - mMarkerView.draw(canvas, pos[0], pos[1]); - } + // draw the marker + mMarker.draw(canvas, pos[0], pos[1]); } } @@ -1272,21 +1263,31 @@ public void setTouchEnabled(boolean enabled) { } /** - * sets the view that is displayed when a value is clicked on the chart + * sets the marker that is displayed when a value is clicked on the chart * - * @param v + * @param marker */ - public void setMarkerView(MarkerView v) { - mMarkerView = v; + public void setMarker(IMarker marker) { + mMarker = marker; } /** - * returns the view that is set as a marker view for the chart + * returns the marker that is set as a marker view for the chart * * @return */ - public MarkerView getMarkerView() { - return mMarkerView; + public IMarker getMarker() { + return mMarker; + } + + @Deprecated + public void setMarkerView(IMarker v) { + setMarker(v); + } + + @Deprecated + public IMarker getMarkerView() { + return getMarker(); } /** @@ -1407,25 +1408,35 @@ public Paint getPaint(int which) { return null; } + @Deprecated + public boolean isDrawMarkerViewsEnabled() { + return isDrawMarkersEnabled(); + } + + @Deprecated + public void setDrawMarkerViews(boolean enabled) { + setDrawMarkers(enabled); + } + /** - * returns true if drawing the marker-view is enabled when tapping on values - * (use the setMarkerView(View v) method to specify a marker view) + * returns true if drawing the marker is enabled when tapping on values + * (use the setMarker(IMarker marker) method to specify a marker) * * @return */ - public boolean isDrawMarkerViewEnabled() { - return mDrawMarkerViews; + public boolean isDrawMarkersEnabled() { + return mDrawMarkers; } /** - * Set this to true to draw a user specified marker-view when tapping on - * chart values (use the setMarkerView(MarkerView mv) method to specify a - * marker view). Default: true + * Set this to true to draw a user specified marker when tapping on + * chart values (use the setMarker(IMarker marker) method to specify a + * marker). Default: true * * @param enabled */ - public void setDrawMarkerViews(boolean enabled) { - mDrawMarkerViews = enabled; + public void setDrawMarkers(boolean enabled) { + mDrawMarkers = enabled; } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java new file mode 100644 index 0000000000..d8ee7452ab --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java @@ -0,0 +1,52 @@ +package com.github.mikephil.charting.components; + +import android.content.Context; +import android.graphics.Canvas; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.RelativeLayout; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.MPPointF; + +public interface IMarker { + + /** + * @return The desired offset you wish the IMarker to have on the x-axis. + * By returning x: -(width / 2) you will center the IMarker horizontally. + * By returning y: -(height / 2) you will center the IMarker vertically. + */ + MPPointF getOffset(); + + /** + * @return The offset for drawing at the specific `point` + * If you have no adjustments to make, return getOffset(). + * + * @param posX This is the X position at which the marker wants to be drawn. + * You can adjust the offset conditionally based on this argument. + * @param posY This is the X position at which the marker wants to be drawn. + * You can adjust the offset conditionally based on this argument. + */ + MPPointF getOffsetForDrawingAtPos(float posX, float posY); + + /** + * This method enables a specified custom IMarker to update it's content every time the IMarker is redrawn. + * + * @param e The Entry the IMarker belongs to. This can also be any subclass of Entry, like BarEntry or + * CandleEntry, simply cast it at runtime. + * @param highlight the highlight object contains information about the highlighted value such as it's dataset-index, the + * selected range or stack-index (only stacked bar entries). + */ + void refreshContent(Entry e, Highlight highlight); + + /** + * Draws the IMarker on the given position on the screen with the given Canvas object. + * + * @param canvas + * @param posX + * @param posY + */ + void draw(Canvas canvas, float posX, float posY); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java new file mode 100644 index 0000000000..5c952bb056 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java @@ -0,0 +1,167 @@ +package com.github.mikephil.charting.components; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.RelativeLayout; + +import com.github.mikephil.charting.charts.Chart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.MPPointF; + +import java.lang.ref.WeakReference; + +/** + * View that can be displayed when selecting values in the chart. Extend this class to provide custom layouts for your + * markers. + * + * @author Philipp Jahoda + */ +public class MarkerImage implements IMarker { + + private Context mContext; + private Drawable mDrawable; + + private MPPointF mOffset = new MPPointF(); + private MPPointF mOffset2 = new MPPointF(); + private WeakReference mWeakChart; + + private FSize mSize = new FSize(); + private Rect mDrawableBoundsCache = new Rect(); + + /** + * Constructor. Sets up the MarkerView with a custom layout resource. + * + * @param context + * @param drawableResourceId the drawable resource to render + */ + public MarkerImage(Context context, int drawableResourceId) { + mContext = context; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + { + mDrawable = mContext.getResources().getDrawable(drawableResourceId, null); + } + else + { + mDrawable = mContext.getResources().getDrawable(drawableResourceId); + } + } + + public void setOffset(MPPointF offset) { + mOffset = offset; + + if (mOffset == null) { + mOffset = new MPPointF(); + } + } + + public void setOffset(float offsetX, float offsetY) { + mOffset.x = offsetX; + mOffset.y = offsetY; + } + + @Override + public MPPointF getOffset() { + return mOffset; + } + + public void setSize(FSize size) { + mSize = size; + + if (mSize == null) { + mSize = new FSize(); + } + } + + public FSize getSize() { + return mSize; + } + + public void setChartView(Chart chart) { + mWeakChart = new WeakReference<>(chart); + } + + public Chart getChartView() { + return mWeakChart == null ? null : mWeakChart.get(); + } + + @Override + public MPPointF getOffsetForDrawingAtPos(float posX, float posY) { + + MPPointF offset = getOffset(); + mOffset2.x = offset.x; + mOffset2.y = offset.y; + + Chart chart = getChartView(); + + float width = mSize.width; + float height = mSize.height; + + if (width == 0.f && mDrawable != null) { + width = mDrawable.getIntrinsicWidth(); + } + if (height == 0.f && mDrawable != null) { + height = mDrawable.getIntrinsicHeight(); + } + + if (posX + mOffset2.x < 0) { + mOffset2.x = - posX; + } else if (chart != null && posX + width + mOffset2.x > chart.getWidth()) { + mOffset2.x = chart.getWidth() - posX - width; + } + + if (posY + mOffset2.y < 0) { + mOffset2.y = - posY; + } else if (chart != null && posY + height + mOffset2.y > chart.getHeight()) { + mOffset2.y = chart.getHeight() - posY - height; + } + + return mOffset2; + } + + @Override + public void refreshContent(Entry e, Highlight highlight) { + + } + + @Override + public void draw(Canvas canvas, float posX, float posY) { + + if (mDrawable == null) return; + + MPPointF offset = getOffsetForDrawingAtPos(posX, posY); + + float width = mSize.width; + float height = mSize.height; + + if (width == 0.f && mDrawable != null) { + width = mDrawable.getIntrinsicWidth(); + } + if (height == 0.f && mDrawable != null) { + height = mDrawable.getIntrinsicHeight(); + } + + mDrawable.copyBounds(mDrawableBoundsCache); + mDrawable.setBounds( + mDrawableBoundsCache.left, + mDrawableBoundsCache.top, + mDrawableBoundsCache.left + (int)width, + mDrawableBoundsCache.top + (int)height); + + int saveId = canvas.save(); + // translate to the correct position and draw + canvas.translate(posX + offset.x, posY + offset.y); + mDrawable.draw(canvas); + canvas.restoreToCount(saveId); + + mDrawable.setBounds(mDrawableBoundsCache); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java index 523376c786..478527caee 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java @@ -6,8 +6,13 @@ import android.view.View; import android.widget.RelativeLayout; +import com.github.mikephil.charting.charts.Chart; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.MPPointF; + +import java.lang.ref.WeakReference; /** * View that can be displayed when selecting values in the chart. Extend this class to provide custom layouts for your @@ -15,7 +20,11 @@ * * @author Philipp Jahoda */ -public abstract class MarkerView extends RelativeLayout { +public class MarkerView extends RelativeLayout implements IMarker { + + private MPPointF mOffset = new MPPointF(); + private MPPointF mOffset2 = new MPPointF(); + private WeakReference mWeakChart; /** * Constructor. Sets up the MarkerView with a custom layout resource. @@ -44,50 +53,77 @@ private void setupLayoutResource(int layoutResource) { inflated.layout(0, 0, inflated.getMeasuredWidth(), inflated.getMeasuredHeight()); } - /** - * Draws the MarkerView on the given position on the screen with the given Canvas object. - * - * @param canvas - * @param posx - * @param posy - */ - public void draw(Canvas canvas, float posx, float posy) { + public void setOffset(MPPointF offset) { + mOffset = offset; - // take offsets into consideration - posx += getXOffset(posx); - posy += getYOffset(posy); + if (mOffset == null) { + mOffset = new MPPointF(); + } + } - // translate to the correct position and draw - canvas.translate(posx, posy); - draw(canvas); - canvas.translate(-posx, -posy); + public void setOffset(float offsetX, float offsetY) { + mOffset.x = offsetX; + mOffset.y = offsetY; } - /** - * This method enables a specified custom MarkerView to update it's content everytime the MarkerView is redrawn. - * - * @param e The Entry the MarkerView belongs to. This can also be any subclass of Entry, like BarEntry or - * CandleEntry, simply cast it at runtime. - * @param highlight the highlight object contains information about the highlighted value such as it's dataset-index, the - * selected range or stack-index (only stacked bar entries). - */ - public abstract void refreshContent(Entry e, Highlight highlight); + @Override + public MPPointF getOffset() { + return mOffset; + } - /** - * Use this to return the desired offset you wish the MarkerView to have on the x-axis. By returning -(getWidth() / - * 2) you will center the MarkerView horizontally. - * - * @param xpos the position on the x-axis in pixels where the marker is drawn - * @return - */ - public abstract int getXOffset(float xpos); + public void setChartView(Chart chart) { + mWeakChart = new WeakReference<>(chart); + } - /** - * Use this to return the desired position offset you wish the MarkerView to have on the y-axis. By returning - * -getHeight() you will cause the MarkerView to be above the selected value. - * - * @param ypos the position on the y-axis in pixels where the marker is drawn - * @return - */ - public abstract int getYOffset(float ypos); + public Chart getChartView() { + return mWeakChart == null ? null : mWeakChart.get(); + } + + @Override + public MPPointF getOffsetForDrawingAtPos(float posX, float posY) { + + MPPointF offset = getOffset(); + mOffset2.x = offset.x; + mOffset2.y = offset.y; + + Chart chart = getChartView(); + + float width = getWidth(); + float height = getHeight(); + + if (posX + mOffset2.x < 0) { + mOffset2.x = - posX; + } else if (chart != null && posX + width + mOffset2.x > chart.getWidth()) { + mOffset2.x = chart.getWidth() - posX - width; + } + + if (posY + mOffset2.y < 0) { + mOffset2.y = - posY; + } else if (chart != null && posY + height + mOffset2.y > chart.getHeight()) { + mOffset2.y = chart.getHeight() - posY - height; + } + + return mOffset2; + } + + @Override + public void refreshContent(Entry e, Highlight highlight) { + + measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); + + } + + @Override + public void draw(Canvas canvas, float posX, float posY) { + + MPPointF offset = getOffsetForDrawingAtPos(posX, posY); + + int saveId = canvas.save(); + // translate to the correct position and draw + canvas.translate(posX + offset.x, posY + offset.y); + draw(canvas); + canvas.restoreToCount(saveId); + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FSize.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FSize.java index d4ff8974d4..a12bc45918 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FSize.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FSize.java @@ -41,7 +41,10 @@ public static void recycleInstances(List instances){ pool.recycle(instances); } - private FSize(final float width, final float height) { + public FSize() { + } + + public FSize(final float width, final float height) { this.width = width; this.height = height; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java index 2e3fc58934..36dd6d33ea 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java @@ -20,12 +20,15 @@ public class MPPointF extends ObjectPool.Poolable { pool.setReplenishPercentage(0.5f); } - private MPPointF(float x, float y){ + public MPPointF() { + } + + public MPPointF(float x, float y) { this.x = x; this.y = y; } - public static MPPointF getInstance(float x, float y){ + public static MPPointF getInstance(float x, float y) { MPPointF result = pool.get(); result.x = x; result.y = y; diff --git a/build.gradle b/build.gradle index 3dfff3b287..339dc2007c 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath "io.realm:realm-gradle-plugin:1.1.0" + //classpath "io.realm:realm-gradle-plugin:1.1.0" classpath 'com.android.tools.build:gradle:2.1.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } From aaec1f5d2cbd8546eb2b5df73b512f75fd21004e Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 8 Aug 2016 22:15:57 +0300 Subject: [PATCH 021/291] DRYed scatter code, simplified IShapeRenderer implementations --- .../custom/CustomScatterShapeRenderer.java | 31 +++----- .../charting/buffer/ScatterBuffer.java | 31 -------- .../renderer/ScatterChartRenderer.java | 56 ++++++++------ .../scatter/ChevronDownShapeRenderer.java | 46 ++++-------- .../scatter/ChevronUpShapeRenderer.java | 45 ++++------- .../renderer/scatter/CircleShapeRenderer.java | 60 ++++++--------- .../renderer/scatter/CrossShapeRenderer.java | 44 ++++------- .../renderer/scatter/IShapeRenderer.java | 14 ++-- .../renderer/scatter/SquareShapeRenderer.java | 64 +++++++--------- .../scatter/TriangleShapeRenderer.java | 74 ++++++++----------- .../renderer/scatter/XShapeRenderer.java | 43 ++++------- 11 files changed, 190 insertions(+), 318 deletions(-) delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/buffer/ScatterBuffer.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java index 77ffe2759e..6279e395f6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java @@ -3,7 +3,6 @@ import android.graphics.Canvas; import android.graphics.Paint; -import com.github.mikephil.charting.buffer.ScatterBuffer; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -16,28 +15,16 @@ public class CustomScatterShapeRenderer implements IShapeRenderer { @Override - public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint - renderPaint, float shapeSize) { + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { - final float shapeHalf = shapeSize / 2f; + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; - for (int i = 0; i < buffer.size(); i += 2) { - - if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - renderPaint.setColor(dataSet.getColor(i / 2)); - - c.drawLine( - buffer.buffer[i] - shapeHalf, - buffer.buffer[i + 1] - shapeHalf, - buffer.buffer[i] + shapeHalf, - buffer.buffer[i + 1] + shapeHalf, - renderPaint); - } + c.drawLine( + posX - shapeHalf, + posY - shapeHalf, + posX + shapeHalf, + posY + shapeHalf, + renderPaint); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/ScatterBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/ScatterBuffer.java deleted file mode 100644 index 14b75fb1f7..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/ScatterBuffer.java +++ /dev/null @@ -1,31 +0,0 @@ - -package com.github.mikephil.charting.buffer; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; - -public class ScatterBuffer extends AbstractBuffer { - - public ScatterBuffer(int size) { - super(size); - } - - protected void addForm(float x, float y) { - buffer[index++] = x; - buffer[index++] = y; - } - - @Override - public void feed(IScatterDataSet data) { - - float size = data.getEntryCount() * phaseX; - - for (int i = 0; i < size; i++) { - - Entry e = data.getEntryForIndex(i); - addForm(e.getX(), e.getY() * phaseY); - } - - reset(); - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index e3da3cce7a..faf2395626 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -2,9 +2,9 @@ package com.github.mikephil.charting.renderer; import android.graphics.Canvas; +import android.util.Log; import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.buffer.ScatterBuffer; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.highlight.Highlight; @@ -22,8 +22,6 @@ public class ScatterChartRenderer extends LineScatterCandleRadarRenderer { protected ScatterDataProvider mChart; - protected ScatterBuffer[] mScatterBuffers; - public ScatterChartRenderer(ScatterDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { super(animator, viewPortHandler); mChart = chart; @@ -31,15 +29,6 @@ public ScatterChartRenderer(ScatterDataProvider chart, ChartAnimator animator, V @Override public void initBuffers() { - - ScatterData scatterData = mChart.getScatterData(); - - mScatterBuffers = new ScatterBuffer[scatterData.getDataSetCount()]; - - for (int i = 0; i < mScatterBuffers.length; i++) { - IScatterDataSet set = scatterData.getDataSetByIndex(i); - mScatterBuffers[i] = new ScatterBuffer(set.getEntryCount() * 2); - } } @Override @@ -57,28 +46,47 @@ public void drawData(Canvas c) { } } + float[] mPixelBuffer = new float[2]; + protected void drawDataSet(Canvas c, IScatterDataSet dataSet) { + ViewPortHandler viewPortHandler = mViewPortHandler; + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); - final float shapeSize = Utils.convertDpToPixel(dataSet.getScatterShapeSize()); + IShapeRenderer renderer = dataSet.getShapeRenderer(); + if (renderer == null) { + Log.i("MISSING", "There's no IShapeRenderer specified for ScatterDataSet"); + return; + } - ScatterBuffer buffer = mScatterBuffers[mChart.getScatterData().getIndexOfDataSet(dataSet)]; - buffer.setPhases(phaseX, phaseY); - buffer.feed(dataSet); + int max = (int)(Math.min( + Math.ceil((float)dataSet.getEntryCount() * mAnimator.getPhaseX()), + (float)dataSet.getEntryCount())); - trans.pointValuesToPixel(buffer.buffer); + for (int i = 0; i < max; i++) { - IShapeRenderer renderer = dataSet.getShapeRenderer(); + Entry e = dataSet.getEntryForIndex(i); + + mPixelBuffer[0] = e.getX(); + mPixelBuffer[1] = e.getY() * phaseY; + + trans.pointValuesToPixel(mPixelBuffer); + + if (!viewPortHandler.isInBoundsRight(mPixelBuffer[0])) + break; + + if (!viewPortHandler.isInBoundsLeft(mPixelBuffer[0]) + || !viewPortHandler.isInBoundsY(mPixelBuffer[1])) + continue; - if (renderer != null) { - renderer.renderShape(c, dataSet, mViewPortHandler, buffer, mRenderPaint, shapeSize); - } else { - throw new RuntimeException("No IShapeRenderer found for provided identifier. Please make sure to add a IShapeRenderer" + - " capable of rendering the provided shape."); + mRenderPaint.setColor(dataSet.getColor(i / 2)); + renderer.renderShape( + c, dataSet, mViewPortHandler, + mPixelBuffer[0], mPixelBuffer[1], + mRenderPaint); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java index cdc3f2af6d..9328b276c4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java @@ -3,7 +3,6 @@ import android.graphics.Canvas; import android.graphics.Paint; -import com.github.mikephil.charting.buffer.ScatterBuffer; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -17,39 +16,26 @@ public class ChevronDownShapeRenderer implements IShapeRenderer @Override - public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint - renderPaint, final float shapeSize) { + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { - final float shapeHalf = shapeSize / 2f; + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; renderPaint.setStyle(Paint.Style.STROKE); renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); - for (int i = 0; i < buffer.size(); i += 2) { - - if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - renderPaint.setColor(dataSet.getColor(i / 2)); - - c.drawLine( - buffer.buffer[i], - buffer.buffer[i + 1] + (2 * shapeHalf), - buffer.buffer[i] + (2 * shapeHalf), - buffer.buffer[i + 1], - renderPaint); - - c.drawLine( - buffer.buffer[i], - buffer.buffer[i + 1] + (2 * shapeHalf), - buffer.buffer[i] - (2 * shapeHalf), - buffer.buffer[i + 1], - renderPaint); - } - + c.drawLine( + posX, + posY + (2 * shapeHalf), + posX + (2 * shapeHalf), + posY, + renderPaint); + + c.drawLine( + posX, + posY + (2 * shapeHalf), + posX - (2 * shapeHalf), + posY, + renderPaint); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java index f4185bfd8e..6dea0abf7b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java @@ -3,7 +3,6 @@ import android.graphics.Canvas; import android.graphics.Paint; -import com.github.mikephil.charting.buffer.ScatterBuffer; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -17,39 +16,27 @@ public class ChevronUpShapeRenderer implements IShapeRenderer @Override - public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint - renderPaint, final float shapeSize) { + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { - final float shapeHalf = shapeSize / 2f; + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; renderPaint.setStyle(Paint.Style.STROKE); renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); - for (int i = 0; i < buffer.size(); i += 2) { - - if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - renderPaint.setColor(dataSet.getColor(i / 2)); - - c.drawLine( - buffer.buffer[i], - buffer.buffer[i + 1] - (2 * shapeHalf), - buffer.buffer[i] + (2 * shapeHalf), - buffer.buffer[i + 1], - renderPaint); - - c.drawLine( - buffer.buffer[i], - buffer.buffer[i + 1] - (2 * shapeHalf), - buffer.buffer[i] - (2 * shapeHalf), - buffer.buffer[i + 1], - renderPaint); - } + c.drawLine( + posX, + posY - (2 * shapeHalf), + posX + (2 * shapeHalf), + posY, + renderPaint); + + c.drawLine( + posX, + posY - (2 * shapeHalf), + posX - (2 * shapeHalf), + posY, + renderPaint); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java index 98ed8c1459..ac7abb92de 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java @@ -3,7 +3,6 @@ import android.graphics.Canvas; import android.graphics.Paint; -import com.github.mikephil.charting.buffer.ScatterBuffer; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.Utils; @@ -17,9 +16,10 @@ public class CircleShapeRenderer implements IShapeRenderer { @Override - public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint - renderPaint, final float shapeSize) { + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + final float shapeSize = dataSet.getScatterShapeSize(); final float shapeHalf = shapeSize / 2f; final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); final float shapeHoleSize = shapeHoleSizeHalf * 2.f; @@ -28,46 +28,34 @@ public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewP final int shapeHoleColor = dataSet.getScatterShapeHoleColor(); - for (int i = 0; i < buffer.size(); i += 2) { + if (shapeSize > 0.0) { + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(shapeStrokeSize); - if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; + c.drawCircle( + posX, + posY, + shapeHoleSizeHalf + shapeStrokeSizeHalf, + renderPaint); - if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - renderPaint.setColor(dataSet.getColor(i / 2)); - - if (shapeSize > 0.0) { - renderPaint.setStyle(Paint.Style.STROKE); - renderPaint.setStrokeWidth(shapeStrokeSize); - - c.drawCircle( - buffer.buffer[i], - buffer.buffer[i + 1], - shapeHoleSizeHalf + shapeStrokeSizeHalf, - renderPaint); - - if (shapeHoleColor != ColorTemplate.COLOR_NONE) { - renderPaint.setStyle(Paint.Style.FILL); - - renderPaint.setColor(shapeHoleColor); - c.drawCircle( - buffer.buffer[i], - buffer.buffer[i + 1], - shapeHoleSizeHalf, - renderPaint); - } - } else { + if (shapeHoleColor != ColorTemplate.COLOR_NONE) { renderPaint.setStyle(Paint.Style.FILL); + renderPaint.setColor(shapeHoleColor); c.drawCircle( - buffer.buffer[i], - buffer.buffer[i + 1], - shapeHalf, + posX, + posY, + shapeHoleSizeHalf, renderPaint); } + } else { + renderPaint.setStyle(Paint.Style.FILL); + + c.drawCircle( + posX, + posY, + shapeHalf, + renderPaint); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java index 3273490a46..202670d6ba 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java @@ -3,7 +3,6 @@ import android.graphics.Canvas; import android.graphics.Paint; -import com.github.mikephil.charting.buffer.ScatterBuffer; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -17,39 +16,26 @@ public class CrossShapeRenderer implements IShapeRenderer @Override - public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint - renderPaint, final float shapeSize) { - - final float shapeHalf = shapeSize / 2f; + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; renderPaint.setStyle(Paint.Style.STROKE); renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); - for (int i = 0; i < buffer.size(); i += 2) { - - if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - renderPaint.setColor(dataSet.getColor(i / 2)); - - c.drawLine( - buffer.buffer[i] - shapeHalf, - buffer.buffer[i + 1], - buffer.buffer[i] + shapeHalf, - buffer.buffer[i + 1], - renderPaint); - c.drawLine( - buffer.buffer[i], - buffer.buffer[i + 1] - shapeHalf, - buffer.buffer[i], - buffer.buffer[i + 1] + shapeHalf, - renderPaint); - } + c.drawLine( + posX - shapeHalf, + posY, + posX + shapeHalf, + posY, + renderPaint); + c.drawLine( + posX, + posY - shapeHalf, + posX, + posY + shapeHalf, + renderPaint); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/IShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/IShapeRenderer.java index f89d6f6b08..20b57a900d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/IShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/IShapeRenderer.java @@ -3,8 +3,6 @@ import android.graphics.Canvas; import android.graphics.Paint; -import com.github.mikephil.charting.buffer.ScatterBuffer; -import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -19,12 +17,12 @@ public interface IShapeRenderer * Renders the provided ScatterDataSet with a shape. * * @param c Canvas object for drawing the shape - * @param dataSet the DataSet to be drawn - * @param viewPortHandler contains information about the current state of the view - * @param buffer buffer containing the transformed values of all entries in the DataSet + * @param dataSet The DataSet to be drawn + * @param viewPortHandler Contains information about the current state of the view + * @param posX Position to draw the shape at + * @param posY Position to draw the shape at * @param renderPaint Paint object used for styling and drawing - * @param shapeSize */ - void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint - renderPaint, final float shapeSize); + void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java index 74670473ef..ac98679233 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java @@ -3,7 +3,6 @@ import android.graphics.Canvas; import android.graphics.Paint; -import com.github.mikephil.charting.buffer.ScatterBuffer; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.Utils; @@ -18,9 +17,10 @@ public class SquareShapeRenderer implements IShapeRenderer @Override - public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint - renderPaint, final float shapeSize) { + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + final float shapeSize = dataSet.getScatterShapeSize(); final float shapeHalf = shapeSize / 2f; final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); final float shapeHoleSize = shapeHoleSizeHalf * 2.f; @@ -29,47 +29,35 @@ public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewP final int shapeHoleColor = dataSet.getScatterShapeHoleColor(); - for (int i = 0; i < buffer.size(); i += 2) { + if (shapeSize > 0.0) { + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(shapeStrokeSize); - if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; + c.drawRect(posX - shapeHoleSizeHalf - shapeStrokeSizeHalf, + posY - shapeHoleSizeHalf - shapeStrokeSizeHalf, + posX + shapeHoleSizeHalf + shapeStrokeSizeHalf, + posY + shapeHoleSizeHalf + shapeStrokeSizeHalf, + renderPaint); - if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - renderPaint.setColor(dataSet.getColor(i / 2)); - - if (shapeSize > 0.0) { - renderPaint.setStyle(Paint.Style.STROKE); - renderPaint.setStrokeWidth(shapeStrokeSize); - - c.drawRect(buffer.buffer[i] - shapeHoleSizeHalf - shapeStrokeSizeHalf, - buffer.buffer[i + 1] - shapeHoleSizeHalf - shapeStrokeSizeHalf, - buffer.buffer[i] + shapeHoleSizeHalf + shapeStrokeSizeHalf, - buffer.buffer[i + 1] + shapeHoleSizeHalf + shapeStrokeSizeHalf, - renderPaint); - - if (shapeHoleColor != ColorTemplate.COLOR_NONE) { - renderPaint.setStyle(Paint.Style.FILL); - - renderPaint.setColor(shapeHoleColor); - c.drawRect(buffer.buffer[i] - shapeHoleSizeHalf, - buffer.buffer[i + 1] - shapeHoleSizeHalf, - buffer.buffer[i] + shapeHoleSizeHalf, - buffer.buffer[i + 1] + shapeHoleSizeHalf, - renderPaint); - } - - } else { + if (shapeHoleColor != ColorTemplate.COLOR_NONE) { renderPaint.setStyle(Paint.Style.FILL); - c.drawRect(buffer.buffer[i] - shapeHalf, - buffer.buffer[i + 1] - shapeHalf, - buffer.buffer[i] + shapeHalf, - buffer.buffer[i + 1] + shapeHalf, + renderPaint.setColor(shapeHoleColor); + c.drawRect(posX - shapeHoleSizeHalf, + posY - shapeHoleSizeHalf, + posX + shapeHoleSizeHalf, + posY + shapeHoleSizeHalf, renderPaint); } + + } else { + renderPaint.setStyle(Paint.Style.FILL); + + c.drawRect(posX - shapeHalf, + posY - shapeHalf, + posX + shapeHalf, + posY + shapeHalf, + renderPaint); } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java index f8e2f30b47..5343454bbb 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java @@ -4,7 +4,6 @@ import android.graphics.Paint; import android.graphics.Path; -import com.github.mikephil.charting.buffer.ScatterBuffer; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.Utils; @@ -20,9 +19,10 @@ public class TriangleShapeRenderer implements IShapeRenderer protected Path mTrianglePathBuffer = new Path(); @Override - public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint - renderPaint, final float shapeSize) { + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + final float shapeSize = dataSet.getScatterShapeSize(); final float shapeHalf = shapeSize / 2f; final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); final float shapeHoleSize = shapeHoleSizeHalf * 2.f; @@ -36,55 +36,43 @@ public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewP Path tri = mTrianglePathBuffer; tri.reset(); - for (int i = 0; i < buffer.size(); i += 2) { - - if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; + tri.moveTo(posX, posY - shapeHalf); + tri.lineTo(posX + shapeHalf, posY + shapeHalf); + tri.lineTo(posX - shapeHalf, posY + shapeHalf); + + if (shapeSize > 0.0) { + tri.lineTo(posX, posY - shapeHalf); + + tri.moveTo(posX - shapeHalf + shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + tri.lineTo(posX + shapeHalf - shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + tri.lineTo(posX, + posY - shapeHalf + shapeStrokeSize); + tri.lineTo(posX - shapeHalf + shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + } - renderPaint.setColor(dataSet.getColor(i / 2)); + tri.close(); - tri.moveTo(buffer.buffer[i], buffer.buffer[i + 1] - shapeHalf); - tri.lineTo(buffer.buffer[i] + shapeHalf, buffer.buffer[i + 1] + shapeHalf); - tri.lineTo(buffer.buffer[i] - shapeHalf, buffer.buffer[i + 1] + shapeHalf); + c.drawPath(tri, renderPaint); + tri.reset(); - if (shapeSize > 0.0) { - tri.lineTo(buffer.buffer[i], buffer.buffer[i + 1] - shapeHalf); + if (shapeSize > 0.0 && + shapeHoleColor != ColorTemplate.COLOR_NONE) { - tri.moveTo(buffer.buffer[i] - shapeHalf + shapeStrokeSize, - buffer.buffer[i + 1] + shapeHalf - shapeStrokeSize); - tri.lineTo(buffer.buffer[i] + shapeHalf - shapeStrokeSize, - buffer.buffer[i + 1] + shapeHalf - shapeStrokeSize); - tri.lineTo(buffer.buffer[i], - buffer.buffer[i + 1] - shapeHalf + shapeStrokeSize); - tri.lineTo(buffer.buffer[i] - shapeHalf + shapeStrokeSize, - buffer.buffer[i + 1] + shapeHalf - shapeStrokeSize); - } + renderPaint.setColor(shapeHoleColor); + tri.moveTo(posX, + posY - shapeHalf + shapeStrokeSize); + tri.lineTo(posX + shapeHalf - shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + tri.lineTo(posX - shapeHalf + shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); tri.close(); c.drawPath(tri, renderPaint); tri.reset(); - - if (shapeSize > 0.0 && - shapeHoleColor != ColorTemplate.COLOR_NONE) { - - renderPaint.setColor(shapeHoleColor); - - tri.moveTo(buffer.buffer[i], - buffer.buffer[i + 1] - shapeHalf + shapeStrokeSize); - tri.lineTo(buffer.buffer[i] + shapeHalf - shapeStrokeSize, - buffer.buffer[i + 1] + shapeHalf - shapeStrokeSize); - tri.lineTo(buffer.buffer[i] - shapeHalf + shapeStrokeSize, - buffer.buffer[i + 1] + shapeHalf - shapeStrokeSize); - tri.close(); - - c.drawPath(tri, renderPaint); - tri.reset(); - } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java index 3c37174e2f..225640ec8e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java @@ -3,7 +3,6 @@ import android.graphics.Canvas; import android.graphics.Paint; -import com.github.mikephil.charting.buffer.ScatterBuffer; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -17,38 +16,26 @@ public class XShapeRenderer implements IShapeRenderer @Override - public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint - renderPaint, final float shapeSize) { + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { - final float shapeHalf = shapeSize / 2f; + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; renderPaint.setStyle(Paint.Style.STROKE); renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); - for (int i = 0; i < buffer.size(); i += 2) { - - if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - renderPaint.setColor(dataSet.getColor(i / 2)); - - c.drawLine( - buffer.buffer[i] - shapeHalf, - buffer.buffer[i + 1] - shapeHalf, - buffer.buffer[i] + shapeHalf, - buffer.buffer[i + 1] + shapeHalf, - renderPaint); - c.drawLine( - buffer.buffer[i] + shapeHalf, - buffer.buffer[i + 1] - shapeHalf, - buffer.buffer[i] - shapeHalf, - buffer.buffer[i + 1] + shapeHalf, - renderPaint); - } + c.drawLine( + posX - shapeHalf, + posY - shapeHalf, + posX + shapeHalf, + posY + shapeHalf, + renderPaint); + c.drawLine( + posX + shapeHalf, + posY - shapeHalf, + posX - shapeHalf, + posY + shapeHalf, + renderPaint); } From 339fa16324fa59e092eeaa78bebd5218eeaa036b Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 8 Aug 2016 22:19:25 +0300 Subject: [PATCH 022/291] Added back missing `new CombinedChartRenderer` --- .../mikephil/charting/charts/CombinedChart.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java index ba0cfcb0ae..65ce85ceb8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java @@ -40,9 +40,7 @@ public class CombinedChart extends BarLineChartBase implements Com */ private boolean mDrawBarShadow = false; - protected DrawOrder[] mDrawOrder = new DrawOrder[]{ - DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.LINE, DrawOrder.CANDLE, DrawOrder.SCATTER - }; + protected DrawOrder[] mDrawOrder; /** * enum that allows to specify the order in which the different data objects @@ -68,10 +66,17 @@ public CombinedChart(Context context, AttributeSet attrs, int defStyle) { protected void init() { super.init(); + // Default values are not ready here yet + mDrawOrder = new DrawOrder[]{ + DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.LINE, DrawOrder.CANDLE, DrawOrder.SCATTER + }; + setHighlighter(new CombinedHighlighter(this, this)); // Old default behaviour setHighlightFullBarEnabled(true); + + mRenderer = new CombinedChartRenderer(this, mAnimator, mViewPortHandler); } @Override From 4d9dc0aa06a1684e26a6e76b72b838dcf7f5e4e5 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 8 Aug 2016 23:32:11 +0300 Subject: [PATCH 023/291] Safeguard for cubic bezier drawing --- .../github/mikephil/charting/renderer/LineChartRenderer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index e29686bf63..75516be93e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -206,6 +206,8 @@ protected void drawCubicBezier(ILineDataSet dataSet) { Entry cur = prev; Entry next = dataSet.getEntryForIndex(mXBounds.min + 1); + if (cur == null || next == null) return; + // let the spline start cubicPath.moveTo(cur.getX(), cur.getY() * phaseY); From 713f467ba839b9811b631c67db6c8c4bf65e3c71 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 9 Aug 2016 11:16:58 +0300 Subject: [PATCH 024/291] Renamed Highlighter -> IHighlighter --- .../java/com/github/mikephil/charting/charts/Chart.java | 6 +++--- .../mikephil/charting/highlight/ChartHighlighter.java | 3 ++- .../mikephil/charting/highlight/CombinedHighlighter.java | 3 ++- .../highlight/{Highlighter.java => IHighlighter.java} | 3 ++- .../mikephil/charting/highlight/PieRadarHighlighter.java | 3 ++- 5 files changed, 11 insertions(+), 7 deletions(-) rename MPChartLib/src/main/java/com/github/mikephil/charting/highlight/{Highlighter.java => IHighlighter.java} (90%) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 93d07572ae..e6ef4c6598 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -36,7 +36,7 @@ import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.ChartHighlighter; import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.highlight.Highlighter; +import com.github.mikephil.charting.highlight.IHighlighter; import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.ChartTouchListener; @@ -163,7 +163,7 @@ public abstract class Chart implements Highlighter { +public class ChartHighlighter implements IHighlighter +{ /** * instance of the data-provider diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java index d2275187e9..f75b0e925d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java @@ -13,7 +13,8 @@ /** * Created by Philipp Jahoda on 12/09/15. */ -public class CombinedHighlighter extends ChartHighlighter implements Highlighter { +public class CombinedHighlighter extends ChartHighlighter implements IHighlighter +{ /** * bar highlighter for supporting stacked highlighting diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/IHighlighter.java similarity index 90% rename from MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlighter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/highlight/IHighlighter.java index cee50a9e57..d0ca0cfe57 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/IHighlighter.java @@ -3,7 +3,8 @@ /** * Created by philipp on 10/06/16. */ -public interface Highlighter { +public interface IHighlighter +{ /** * Returns a Highlight object corresponding to the given x- and y- touch positions in pixels. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieRadarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieRadarHighlighter.java index 9c067de808..a19906d75c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieRadarHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieRadarHighlighter.java @@ -9,7 +9,8 @@ /** * Created by philipp on 12/06/16. */ -public abstract class PieRadarHighlighter implements Highlighter { +public abstract class PieRadarHighlighter implements IHighlighter +{ protected T mChart; From 8cf2a6c3d027356c1ef251452183936dbe508a41 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 9 Aug 2016 11:33:09 +0300 Subject: [PATCH 025/291] Removed the extra offset that messed up legend location --- .../com/github/mikephil/charting/renderer/LegendRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java index f621afcf35..e4b0b81ffc 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -273,7 +273,7 @@ public void renderLegend(Canvas c) { List calculatedLabelSizes = mLegend.getCalculatedLabelSizes(); List calculatedLabelBreakPoints = mLegend.getCalculatedLabelBreakPoints(); - float posX = originPosX + xoffset; + float posX = originPosX; float posY = 0.f; switch (verticalAlignment) { @@ -372,7 +372,7 @@ public void renderLegend(Canvas c) { for (int i = 0; i < labels.length; i++) { Boolean drawingForm = colors[i] != ColorTemplate.COLOR_SKIP; - float posX = originPosX + xoffset; + float posX = originPosX; if (drawingForm) { if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) From e841b697be0de4179fb23a4ed7e0e05e580ced9a Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 9 Aug 2016 14:26:52 +0300 Subject: [PATCH 026/291] Reversed "array access optimization" A `for-each` loop allocates only one iterator for the whole loop, not for each iteration. So this "optimization" is not needed, and just introduces additional clutter. http://stackoverflow.com/questions/14640184/does-the-java-foreach-loop-create-a-new-object --- .../mikephil/charting/charts/Chart.java | 6 +- .../mikephil/charting/data/BarData.java | 5 +- .../mikephil/charting/data/BubbleData.java | 4 +- .../mikephil/charting/data/ChartData.java | 65 +++++-------------- .../mikephil/charting/data/ScatterData.java | 3 +- .../renderer/BubbleChartRenderer.java | 6 +- .../renderer/CandleStickChartRenderer.java | 6 +- .../charting/renderer/LineChartRenderer.java | 5 +- .../charting/renderer/PieChartRenderer.java | 7 +- .../charting/renderer/RadarChartRenderer.java | 6 +- .../renderer/ScatterChartRenderer.java | 5 +- 11 files changed, 30 insertions(+), 88 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index e6ef4c6598..12ce891a98 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -305,11 +305,7 @@ public void setData(T data) { // calculate how many digits are needed setupDefaultFormatter(data.getYMin(), data.getYMax()); - IDataSet set; - final List sets = mData.getDataSets(); - final int count = sets.size(); - for (int i = 0; i < count; i++) { - set = (IDataSet) sets.get(i); + for (IDataSet set : mData.getDataSets()) { if (set.needsFormatter() || set.getValueFormatter() == mDefaultFormatter) set.setValueFormatter(mDefaultFormatter); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java index 601364dbc4..20a27a5fec 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java @@ -74,10 +74,7 @@ public void groupBars(float fromX, float groupSpace, float barSpace) { float start = fromX; fromX += groupSpaceWidthHalf; - IBarDataSet set; - final int setCountJ = mDataSets.size(); - for(int j = 0 ; j < setCountJ ; j++){ - set = mDataSets.get(j); + for (IBarDataSet set : mDataSets) { fromX += barSpaceHalf; fromX += barWidthHalf; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleData.java index bf1be94a86..8fb72f17c5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleData.java @@ -27,8 +27,8 @@ public BubbleData(List dataSets) { * @param width */ public void setHighlightCircleWidth(float width) { - for(int i = 0 ; i < mDataSets.size() ; i++){ - mDataSets.get(i).setHighlightCircleWidth(width); + for (IBubbleDataSet set : mDataSets) { + set.setHighlightCircleWidth(width); } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java index 0d8c52bd37..8c63b3130a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java @@ -79,10 +79,9 @@ public ChartData(T... dataSets) { */ private List arrayToList(T[] array) { - List list = new ArrayList(); + List list = new ArrayList<>(); - for (int i = 0; i < array.length; i++) { - T set = array[i]; + for (T set : array) { list.add(set); } @@ -129,8 +128,7 @@ public void calcMinMax() { mXMax = -Float.MAX_VALUE; mXMin = Float.MAX_VALUE; - for (int i = 0; i < mDataSets.size(); i++) { - T set = mDataSets.get(i); + for (T set : mDataSets) { calcMinMax(set); } @@ -147,9 +145,7 @@ public void calcMinMax() { mLeftAxisMax = firstLeft.getYMax(); mLeftAxisMin = firstLeft.getYMin(); - for (int i = 0; i < mDataSets.size(); i++) { - - T dataSet = mDataSets.get(i); + for (IDataSet dataSet : mDataSets) { if (dataSet.getAxisDependency() == AxisDependency.LEFT) { if (dataSet.getYMin() < mLeftAxisMin) mLeftAxisMin = dataSet.getYMin(); @@ -168,9 +164,7 @@ public void calcMinMax() { mRightAxisMax = firstRight.getYMax(); mRightAxisMin = firstRight.getYMin(); - for (int i = 0; i < mDataSets.size(); i++) { - - T dataSet = mDataSets.get(i); + for (IDataSet dataSet : mDataSets) { if (dataSet.getAxisDependency() == AxisDependency.RIGHT) { if (dataSet.getYMin() < mRightAxisMin) mRightAxisMin = dataSet.getYMin(); @@ -619,9 +613,7 @@ public int getIndexOfDataSet(T dataSet) { * @return */ protected T getFirstLeft(List sets) { - - for (int i = 0; i < sets.size(); i++) { - T dataSet = sets.get(i); + for (T dataSet : sets) { if (dataSet.getAxisDependency() == AxisDependency.LEFT) return dataSet; } @@ -635,9 +627,7 @@ protected T getFirstLeft(List sets) { * @return */ public T getFirstRight(List sets) { - - for (int i = 0; i < sets.size(); i++) { - T dataSet = sets.get(i); + for (T dataSet : sets) { if (dataSet.getAxisDependency() == AxisDependency.RIGHT) return dataSet; } @@ -653,9 +643,7 @@ public void setValueFormatter(ValueFormatter f) { if (f == null) return; else { - - for (int i = 0; i < mDataSets.size(); i++) { - T set = mDataSets.get(i); + for (IDataSet set : mDataSets) { set.setValueFormatter(f); } } @@ -668,9 +656,7 @@ public void setValueFormatter(ValueFormatter f) { * @param color */ public void setValueTextColor(int color) { - - for (int i = 0; i < mDataSets.size(); i++) { - T set = mDataSets.get(i); + for (IDataSet set : mDataSets) { set.setValueTextColor(color); } } @@ -682,9 +668,7 @@ public void setValueTextColor(int color) { * @param colors */ public void setValueTextColors(List colors) { - - for (int i = 0; i < mDataSets.size(); i++) { - T set = mDataSets.get(i); + for (IDataSet set : mDataSets) { set.setValueTextColors(colors); } } @@ -696,9 +680,7 @@ public void setValueTextColors(List colors) { * @param tf */ public void setValueTypeface(Typeface tf) { - - for (int i = 0; i < mDataSets.size(); i++) { - T set = mDataSets.get(i); + for (IDataSet set : mDataSets) { set.setValueTypeface(tf); } } @@ -710,9 +692,7 @@ public void setValueTypeface(Typeface tf) { * @param size */ public void setValueTextSize(float size) { - - for (int i = 0; i < mDataSets.size(); i++) { - T set = mDataSets.get(i); + for (IDataSet set : mDataSets) { set.setValueTextSize(size); } } @@ -724,9 +704,7 @@ public void setValueTextSize(float size) { * @param enabled */ public void setDrawValues(boolean enabled) { - - for (int i = 0; i < mDataSets.size(); i++) { - T set = mDataSets.get(i); + for (IDataSet set : mDataSets) { set.setDrawValues(enabled); } } @@ -737,9 +715,7 @@ public void setDrawValues(boolean enabled) { * be highlighted programmatically or by touch gesture. */ public void setHighlightEnabled(boolean enabled) { - - for (int i = 0; i < mDataSets.size(); i++) { - T set = mDataSets.get(i); + for (IDataSet set : mDataSets) { set.setHighlightEnabled(enabled); } } @@ -751,9 +727,7 @@ public void setHighlightEnabled(boolean enabled) { * @return */ public boolean isHighlightEnabled() { - - for (int i = 0; i < mDataSets.size(); i++) { - T set = mDataSets.get(i); + for (IDataSet set : mDataSets) { if (!set.isHighlightEnabled()) return false; } @@ -780,8 +754,7 @@ public void clearValues() { */ public boolean contains(T dataSet) { - for (int i = 0; i < mDataSets.size(); i++) { - T set = mDataSets.get(i); + for (T set : mDataSets) { if (set.equals(dataSet)) return true; } @@ -798,8 +771,7 @@ public int getEntryCount() { int count = 0; - for (int i = 0; i < mDataSets.size(); i++) { - T set = mDataSets.get(i); + for (T set : mDataSets) { count += set.getEntryCount(); } @@ -818,8 +790,7 @@ public T getMaxEntryCountSet() { T max = mDataSets.get(0); - for (int i = 0; i < mDataSets.size(); i++) { - T set = mDataSets.get(i); + for (T set : mDataSets) { if (set.getEntryCount() > max.getEntryCount()) max = set; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterData.java index 930eb04198..ba142360f9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterData.java @@ -28,8 +28,7 @@ public float getGreatestShapeSize() { float max = 0f; - for (int i = 0; i < mDataSets.size(); i++) { - IScatterDataSet set = mDataSets.get(i); + for (IScatterDataSet set : mDataSets) { float size = set.getScatterShapeSize(); if (size > max) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index 2a0bfcbb76..f21f03d3e3 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -46,11 +46,7 @@ public void drawData(Canvas c) { BubbleData bubbleData = mChart.getBubbleData(); - IBubbleDataSet set; - List dataSets = bubbleData.getDataSets(); - int setCount = dataSets.size(); - for (int i = 0; i < setCount; i++) { - set = dataSets.get(i); + for (IBubbleDataSet set : bubbleData.getDataSets()) { if (set.isVisible()) drawDataSet(c, set); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index ffc7479c8b..8809c0f44b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -44,11 +44,7 @@ public void drawData(Canvas c) { CandleData candleData = mChart.getCandleData(); - ICandleDataSet set; - List dataSets = candleData.getDataSets(); - int setCount = dataSets.size(); - for (int i = 0; i < setCount; i++) { - set = dataSets.get(i); + for (ICandleDataSet set : candleData.getDataSets()) { if (set.isVisible()) drawDataSet(c, set); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 75516be93e..bf55c32cf0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -89,10 +89,7 @@ public void drawData(Canvas c) { LineData lineData = mChart.getLineData(); - ILineDataSet set; - int setCount = lineData.getDataSets().size(); - for (int i = 0; i < setCount; i++) { - set = lineData.getDataSets().get(i); + for (ILineDataSet set : lineData.getDataSets()) { if (set.isVisible()) drawDataSet(c, set); 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 d479737c04..a489a18c32 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 @@ -142,11 +142,8 @@ public void drawData(Canvas c) { PieData pieData = mChart.getData(); - IPieDataSet set; - int setCount = pieData.getDataSets().size(); - List dataSet = pieData.getDataSets(); - for(int i = 0 ; i < setCount ; i++){ - set = dataSet.get(i); + for (IPieDataSet set : pieData.getDataSets()) { + if (set.isVisible() && set.getEntryCount() > 0) drawDataSet(c, set); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java index 4d0a8d9b0f..dd0918d559 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java @@ -63,11 +63,7 @@ public void drawData(Canvas c) { int mostEntries = radarData.getMaxEntryCountSet().getEntryCount(); - IRadarDataSet set; - List dataSets = radarData.getDataSets(); - int setCount = dataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = dataSets.get(i); + for (IRadarDataSet set : radarData.getDataSets()) { if (set.isVisible()) { drawDataSet(c, set, mostEntries); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index faf2395626..91dc40fc38 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -36,10 +36,7 @@ public void drawData(Canvas c) { ScatterData scatterData = mChart.getScatterData(); - IScatterDataSet set; - int setCount = scatterData.getDataSets().size(); - for(int i = 0 ; i < setCount ; i++){ - set = scatterData.getDataSets().get(i); + for (IScatterDataSet set : scatterData.getDataSets()) { if (set.isVisible()) drawDataSet(c, set); From 02bf21df50f2d31f3f9c7276b24aba9a3dae163e Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 9 Aug 2016 15:33:26 +0300 Subject: [PATCH 027/291] Reverse string formatter caches These *will* cause trouble. We know of users who format the strings based on the entry's location, view port scroll/scale, etc. The cache will repeat irrelevant values, and gain no benefit for us. --- .../BarChartPositiveNegative.java | 7 +- .../mpchartexample/LineChartTime.java | 6 +- .../StackedBarActivityNegative.java | 16 +- .../custom/MyAxisValueFormatter.java | 6 +- .../custom/MyCustomXAxisValueFormatter.java | 7 +- .../custom/MyValueFormatter.java | 7 +- .../custom/RadarMarkerView.java | 6 +- .../formatter/DefaultAxisValueFormatter.java | 12 +- .../formatter/DefaultValueFormatter.java | 13 +- .../formatter/FormattedStringCache.java | 194 -------- .../formatter/LargeValueFormatter.java | 12 +- .../charting/formatter/PercentFormatter.java | 14 +- .../formatter/StackedValueFormatter.java | 22 +- .../test/FormattedStringCacheTest.java | 433 ------------------ 14 files changed, 48 insertions(+), 707 deletions(-) delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FormattedStringCache.java delete mode 100644 MPChartLib/src/test/java/com/github/mikephil/charting/test/FormattedStringCacheTest.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index efe51d2ffd..1c22ae41c6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -16,7 +16,6 @@ import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.formatter.AxisValueFormatter; -import com.github.mikephil.charting.formatter.FormattedStringCache; import com.github.mikephil.charting.utils.ViewPortHandler; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -165,15 +164,15 @@ public Data(float xValue, float yValue, String xAxisValue) { private class ValueFormatter implements com.github.mikephil.charting.formatter.ValueFormatter { - private FormattedStringCache.PrimIntFloat mFormattedStringCache; + private DecimalFormat mFormat; public ValueFormatter() { - mFormattedStringCache = new FormattedStringCache.PrimIntFloat(new DecimalFormat("######.0")); + mFormat = new DecimalFormat("######.0"); } @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormattedStringCache.getFormattedValue(value, dataSetIndex); + return mFormat.format(value); } } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 77f9ecb834..7d833625aa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -21,7 +21,6 @@ import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.formatter.AxisValueFormatter; -import com.github.mikephil.charting.formatter.FormattedStringCache; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -92,12 +91,11 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setGranularity(60000L); // one minute in millis xAxis.setValueFormatter(new AxisValueFormatter() { - private FormattedStringCache.Generic mFormattedStringCache = new FormattedStringCache.Generic<>(new SimpleDateFormat("dd MMM HH:mm")); + private SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm"); @Override public String getFormattedValue(float value, AxisBase axis) { - Long v = (long) value; - return mFormattedStringCache.getFormattedValue(new Date(v), v); + return mFormat.format(new Date((long) value)); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 4f0e16f5fa..200c012771 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -20,7 +20,6 @@ import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.FormattedStringCache; import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.formatter.AxisValueFormatter; import com.github.mikephil.charting.highlight.Highlight; @@ -80,11 +79,11 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setGranularity(10f); xAxis.setValueFormatter(new AxisValueFormatter() { - private FormattedStringCache.PrimFloat format = new FormattedStringCache.PrimFloat(new DecimalFormat("###")); + private DecimalFormat format = new DecimalFormat("###"); @Override public String getFormattedValue(float value, AxisBase axis) { - return format.getFormattedValue(value) + "-" + format.getFormattedValue(value+10); + return format.format(value) + "-" + format.format(value + 10); } @Override @@ -223,25 +222,22 @@ public void onNothingSelected() { private class CustomFormatter implements ValueFormatter, AxisValueFormatter { - private FormattedStringCache.Generic mFormatValue; - private FormattedStringCache.PrimFloat mFormatAxis; + private DecimalFormat mFormat; public CustomFormatter() { - mFormatValue = new FormattedStringCache.Generic<>(new DecimalFormat("###")); - mFormatAxis = new FormattedStringCache.PrimFloat(new DecimalFormat("###")); + mFormat = new DecimalFormat("###"); } // data @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormatValue.getFormattedValue(value, dataSetIndex) + "m"; + return mFormat.format(Math.abs(value)) + "m"; } // YAxis @Override public String getFormattedValue(float value, AxisBase axis) { - Float v = Math.abs(value); - return mFormatAxis.getFormattedValue(v) + "m"; + return mFormat.format(Math.abs(value)) + "m"; } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java index 2eeff56971..ea50fd8fad 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java @@ -2,22 +2,20 @@ import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.formatter.AxisValueFormatter; -import com.github.mikephil.charting.formatter.FormattedStringCache; import java.text.DecimalFormat; public class MyAxisValueFormatter implements AxisValueFormatter { private DecimalFormat mFormat; - private FormattedStringCache.PrimFloat mFormattedStringCache; public MyAxisValueFormatter() { - mFormattedStringCache = new FormattedStringCache.PrimFloat(new DecimalFormat("###,###,###,##0.0")); + mFormat = new DecimalFormat("###,###,###,##0.0"); } @Override public String getFormattedValue(float value, AxisBase axis) { - return mFormattedStringCache.getFormattedValue(value) + " $"; + return mFormat.format(value) + " $"; } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index 9b5f4c921c..7d698ef0e7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -2,7 +2,6 @@ import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.formatter.AxisValueFormatter; -import com.github.mikephil.charting.formatter.FormattedStringCache; import com.github.mikephil.charting.utils.ViewPortHandler; import java.text.DecimalFormat; @@ -12,13 +11,13 @@ */ public class MyCustomXAxisValueFormatter implements AxisValueFormatter { - private FormattedStringCache.PrimFloat mFormattedStringCache; + private DecimalFormat mFormat; private ViewPortHandler mViewPortHandler; public MyCustomXAxisValueFormatter(ViewPortHandler viewPortHandler) { mViewPortHandler = viewPortHandler; // maybe do something here or provide parameters in constructor - mFormattedStringCache = new FormattedStringCache.PrimFloat(new DecimalFormat("###,###,###,##0.0")); + mFormat = new DecimalFormat("###,###,###,##0.0"); } @Override @@ -35,7 +34,7 @@ else if (xScale > 3) else if (xScale > 1) return "2"; else - return mFormattedStringCache.getFormattedValue(value); + return mFormat.format(value); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java index 5ca05d92de..a374b60e7f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java @@ -1,7 +1,6 @@ package com.xxmassdeveloper.mpchartexample.custom; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.FormattedStringCache; import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -9,14 +8,14 @@ public class MyValueFormatter implements ValueFormatter { - private FormattedStringCache.Generic mFormattedStringCache; + private DecimalFormat mFormat; public MyValueFormatter() { - mFormattedStringCache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,###,##0.0")); + mFormat = new DecimalFormat("###,###,###,##0.0"); } @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormattedStringCache.getFormattedValue(value, dataSetIndex) + " $"; + return mFormat.format(value) + " $"; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java index b5aa1960d1..1a350f45ac 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java @@ -8,7 +8,6 @@ import com.github.mikephil.charting.components.MarkerView; import com.github.mikephil.charting.data.CandleEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.FormattedStringCache; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; @@ -24,7 +23,7 @@ public class RadarMarkerView extends MarkerView { private TextView tvContent; - private FormattedStringCache.PrimFloat mFormattedStringCache = new FormattedStringCache.PrimFloat(new DecimalFormat("##0")); + private DecimalFormat format = new DecimalFormat("##0"); public RadarMarkerView(Context context, int layoutResource) { super(context, layoutResource); @@ -37,8 +36,7 @@ public RadarMarkerView(Context context, int layoutResource) { // content (user-interface) @Override public void refreshContent(Entry e, Highlight highlight) { - float value = e.getY(); - tvContent.setText(mFormattedStringCache.getFormattedValue(value) + " %"); + tvContent.setText(format.format(e.getY()) + " %"); super.refreshContent(e, highlight); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java index f55fc8175e..ad3bb783ee 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java @@ -10,9 +10,9 @@ public class DefaultAxisValueFormatter implements AxisValueFormatter { /** - * FormattedStringFormat for formatting and caching + * decimalformat for formatting */ - protected FormattedStringCache.PrimFloat mFormattedStringCache; + protected DecimalFormat mFormat; /** * the number of decimal digits this formatter uses @@ -35,15 +35,13 @@ public DefaultAxisValueFormatter(int digits) { b.append("0"); } - mFormattedStringCache = new FormattedStringCache.PrimFloat(new DecimalFormat("###,###,###,##0" + b.toString())); + mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); } @Override public String getFormattedValue(float value, AxisBase axis) { - - // TODO: There should be a better way to do this. Floats are not the best keys... - return mFormattedStringCache.getFormattedValue(value); - + // avoid memory allocations here (for performance) + return mFormat.format(value); } @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java index 8d169b1405..5d23d25f7e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java @@ -15,9 +15,9 @@ public class DefaultValueFormatter implements ValueFormatter { /** - * FormattedStringCache for formatting and caching. + * DecimalFormat for formatting */ - protected FormattedStringCache.Generic mFormattedStringCache; + protected DecimalFormat mFormat; protected int mDecimalDigits; @@ -47,13 +47,16 @@ public void setup(int digits) { b.append("0"); } - mFormattedStringCache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,###,##0" + b.toString())); - + mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); } @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormattedStringCache.getFormattedValue(value, dataSetIndex); + + // put more logic here ... + // avoid memory allocations here (for performance reasons) + + return mFormat.format(value); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FormattedStringCache.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FormattedStringCache.java deleted file mode 100644 index c6b4ef3e6f..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FormattedStringCache.java +++ /dev/null @@ -1,194 +0,0 @@ -package com.github.mikephil.charting.formatter; - -import java.text.Format; -import java.util.ArrayList; -import java.util.HashMap; - -/** - * Created by Tony Patino on 6/29/16. - * - * COST : Frequently the V type, and the K type will often be passed as primitives (float / int / double) - * This will incur an auto-boxing penalty, and an instantiation on each call. - * - * BENEFIT : Formatting of Strings is one of the costliest operations remaining, and it is larger than the boxing penalty. - * Eliminating redundant formats helps more than boxing hurts. - * - * Possibly want to make some explicit primitive enabled caches, though they can be ugly. - * - */ -public class FormattedStringCache { - - protected Format mFormat; - - public Format getFormat(){ - return mFormat; - } - - public FormattedStringCache(Format format){ - this.mFormat = format; - } - - - /** - * Cache a Formatted String, derived from formatting value V in the Format passed to the constructor. - * Use the object K as a way to quickly look up the string in the cache. - * If value of V has changed since the last time the cache was accessed, re-format the string. - * - * NOTE: This version relies on auto-boxing of primitive values. As a result, it has worse memory performance - * than the Prim versions. - * - * @param - * @param - */ - public static class Generic extends FormattedStringCache{ - - private HashMap mCachedStringsHashMap = new HashMap<>(); - private HashMap mCachedValuesHashMap = new HashMap<>(); - - public Generic(Format format){ - super(format); - } - - public String getFormattedValue(V value, K key){ - - // If we can't find the value at all, create an entry for it, and format the string. - if(!mCachedValuesHashMap.containsKey(key)){ - mCachedStringsHashMap.put(key, mFormat.format(value)); - mCachedValuesHashMap.put(key, value); - } - - // If the old value and the new one don't match, format the string and store it, because the string's value will be different now. - if(!value.equals(mCachedValuesHashMap.get(key))){ - mCachedStringsHashMap.put(key, mFormat.format(value)); - mCachedValuesHashMap.put(key, value); - } - - String result = mCachedStringsHashMap.get(key); - - return result; - } - } - - /** - * Cache a Formatted String, derived from formatting float value in the Format passed to the constructor. - * Use the integer as a way to quickly look up the string in the cache. - * If value of V has changed since the last time the cache was accessed, re-format the string. - * - */ - public static class PrimIntFloat extends FormattedStringCache{ - - public PrimIntFloat(Format format){ - super(format); - } - - private ArrayList cachedValues = new ArrayList<>(); - private ArrayList cachedStrings = new ArrayList<>(); - - public String getFormattedValue(float value, int key) { - - boolean hasValueAtdataSetIndex = true; - if(cachedValues.size() <= key){ - int p = key; - while(p >= 0){ - if(p == 0){ - cachedValues.add(value); - cachedStrings.add(""); - }else{ - cachedValues.add(Float.NaN); - cachedStrings.add(""); - } - p--; - } - hasValueAtdataSetIndex = false; - } - - if(hasValueAtdataSetIndex) { - Float cachedValue = cachedValues.get(key); - hasValueAtdataSetIndex = !(cachedValue == null || cachedValue != value); - } - - if(!hasValueAtdataSetIndex){ - cachedValues.set(key, value); - cachedStrings.set(key, mFormat.format(value)); - } - - return cachedStrings.get(key); - } - - } - - /** - * Cache a Formatted String, derived from formatting float value in the Format passed to the constructor. - */ - public static class PrimFloat extends FormattedStringCache{ - - public PrimFloat(Format format){ - super(format); - } - - - private ArrayList cachedValues = new ArrayList<>(); - private ArrayList cachedStrings = new ArrayList<>(); - - public String getFormattedValue(float value) { - - boolean alreadyHasValue = false; - int vCount = cachedValues.size(); - int sIndex = -1; - for(int i = 0 ; i < vCount ; i++){ - if(cachedValues.get(i) == value){ - sIndex = i; - alreadyHasValue = true; - break; - } - } - if(!alreadyHasValue) { - cachedValues.add(value); - cachedStrings.add(mFormat.format(value)); - sIndex = cachedValues.size() - 1; - } - - return cachedStrings.get(sIndex); - } - - } - - /** - * Cache a Formatted String, derived from formatting double value in the Format passed to the constructor. - */ - public static class PrimDouble extends FormattedStringCache{ - - public PrimDouble(Format format){ - super(format); - } - - - private ArrayList cachedValues = new ArrayList<>(); - private ArrayList cachedStrings = new ArrayList<>(); - - public String getFormattedValue(double value) { - - boolean alreadyHasValue = false; - int vCount = cachedValues.size(); - int sIndex = -1; - for(int i = 0 ; i < vCount ; i++){ - if(cachedValues.get(i) == value){ - sIndex = i; - alreadyHasValue = true; - break; - } - } - if(!alreadyHasValue) { - cachedValues.add(value); - cachedStrings.add(mFormat.format(value)); - sIndex = cachedValues.size() - 1; - } - - return cachedStrings.get(sIndex); - } - - } - - - -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java index 4e6e77a6bf..ef8dc4077a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java @@ -23,16 +23,11 @@ public class LargeValueFormatter implements ValueFormatter, AxisValueFormatter { "", "k", "m", "b", "t" }; private static final int MAX_LENGTH = 5; + private DecimalFormat mFormat; private String mText = ""; - - /** - * FormattedStringCache for formatting and caching. - */ - protected FormattedStringCache.PrimDouble mFormattedStringCache; - public LargeValueFormatter() { - mFormattedStringCache = new FormattedStringCache.PrimDouble(new DecimalFormat("###E00")); + mFormat = new DecimalFormat("###E00"); } /** @@ -82,8 +77,7 @@ public void setSuffix(String[] suff) { */ private String makePretty(double number) { - // TODO : Should be better way to do this. Double isn't the best key... - String r = mFormattedStringCache.getFormattedValue(number); + String r = mFormat.format(number); int numericValue1 = Character.getNumericValue(r.charAt(r.length() - 1)); int numericValue2 = Character.getNumericValue(r.charAt(r.length() - 2)); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java index 1ad1ba1c0d..209da1fa83 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java @@ -15,12 +15,10 @@ */ public class PercentFormatter implements ValueFormatter, AxisValueFormatter { - protected FormattedStringCache.Generic mFormattedStringCache; - protected FormattedStringCache.PrimFloat mFormattedStringCacheAxis; + protected DecimalFormat mFormat; public PercentFormatter() { - mFormattedStringCache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,##0.0")); - mFormattedStringCacheAxis = new FormattedStringCache.PrimFloat(new DecimalFormat("###,###,##0.0")); + mFormat = new DecimalFormat("###,###,##0.0"); } /** @@ -29,21 +27,19 @@ public PercentFormatter() { * @param format */ public PercentFormatter(DecimalFormat format) { - mFormattedStringCache = new FormattedStringCache.Generic<>(format); - mFormattedStringCacheAxis = new FormattedStringCache.PrimFloat(format); + this.mFormat = format; } // ValueFormatter @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormattedStringCache.getFormattedValue(value, dataSetIndex) + " %"; + return mFormat.format(value) + " %"; } // AxisValueFormatter @Override public String getFormattedValue(float value, AxisBase axis) { - // TODO: Find a better way to do this. Float isn't the best key... - return mFormattedStringCacheAxis.getFormattedValue(value) + " %"; + return mFormat.format(value) + " %"; } @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java index bc020d5637..b13dd8d907 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java @@ -18,8 +18,6 @@ public class StackedValueFormatter implements ValueFormatter { * if true, all stack values of the stacked bar entry are drawn, else only top */ private boolean mDrawWholeStack; - private FormattedStringCache.Generic mFormattedStringCacheWholeStack; - private FormattedStringCache.Generic mFormattedStringCache; /** * a string that should be appended behind the value @@ -46,17 +44,12 @@ public StackedValueFormatter(boolean drawWholeStack, String appendix, int decima b.append("0"); } - this.mFormattedStringCache = new FormattedStringCache.Generic(new DecimalFormat("###,###,###,##0" + b.toString())); - this.mFormattedStringCacheWholeStack = new FormattedStringCache.Generic(new DecimalFormat("###,###,###,##0" + b.toString())); + this.mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); } @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - FormattedStringCache.Generic chosenCache = mFormattedStringCache; - int chosenIndex = dataSetIndex; - float chosenValue = value; - if (!mDrawWholeStack && entry instanceof BarEntry) { BarEntry barEntry = (BarEntry) entry; @@ -66,19 +59,16 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View // find out if we are on top of the stack if (vals[vals.length - 1] == value) { - chosenCache = mFormattedStringCacheWholeStack; - chosenValue = barEntry.getY(); + + // return the "sum" across all stack values + return mFormat.format(barEntry.getY()) + mAppendix; } else { - chosenCache = null; + return ""; // return empty } } } - if(chosenCache == null){ - return ""; - } - // return the "proposed" value - return chosenCache.getFormattedValue(chosenValue, chosenIndex) + mAppendix; + return mFormat.format(value) + mAppendix; } } diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/FormattedStringCacheTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/FormattedStringCacheTest.java deleted file mode 100644 index bd33008ea6..0000000000 --- a/MPChartLib/src/test/java/com/github/mikephil/charting/test/FormattedStringCacheTest.java +++ /dev/null @@ -1,433 +0,0 @@ -package com.github.mikephil.charting.test; - -import com.github.mikephil.charting.formatter.FormattedStringCache; - -import junit.framework.Assert; - -import org.junit.Test; - -import java.text.DecimalFormat; - -/** - * Created by Tony Patino on 6/30/16. - */ -public class FormattedStringCacheTest { - - @Test - public void testPrimFloat(){ - int digits = 2; - - StringBuffer b = new StringBuffer(); - for (int i = 0; i < digits; i++) { - if (i == 0) - b.append("."); - b.append("0"); - } - - FormattedStringCache.PrimFloat cache = new FormattedStringCache.PrimFloat(new DecimalFormat("###,###,###,##0" + b.toString())); - - String s = null; - - s = cache.getFormattedValue(1.0f); - - Assert.assertEquals("1.00", s); - - s = cache.getFormattedValue(1.0f); - - Assert.assertEquals("1.00", s); - - - s = cache.getFormattedValue(1.3f); - - Assert.assertEquals("1.30", s); - - - s = cache.getFormattedValue(1.3f); - - Assert.assertEquals("1.30", s); - - - s = cache.getFormattedValue(1.0f); - - Assert.assertEquals("1.00", s); - - for(int i = 0 ; i < 100 ; i++){ - float f = 0.75f + i; - s = cache.getFormattedValue(f); - Assert.assertEquals(i+".75", s); - } - - - s = cache.getFormattedValue(1.5323234f); - Assert.assertEquals("1.53", s); - - - s = cache.getFormattedValue(1.31f); - Assert.assertEquals("1.31", s); - - s = cache.getFormattedValue(1.3111111f); - Assert.assertEquals("1.31", s); - - } - - @Test - public void testPrimDouble(){ - int digits = 2; - - StringBuffer b = new StringBuffer(); - for (int i = 0; i < digits; i++) { - if (i == 0) - b.append("."); - b.append("0"); - } - - FormattedStringCache.PrimDouble cache = new FormattedStringCache.PrimDouble(new DecimalFormat("###,###,###,##0" + b.toString())); - - String s = null; - - s = cache.getFormattedValue(1.0d); - - Assert.assertEquals("1.00", s); - - s = cache.getFormattedValue(1.0d); - - Assert.assertEquals("1.00", s); - - - s = cache.getFormattedValue(1.3d); - - Assert.assertEquals("1.30", s); - - - s = cache.getFormattedValue(1.3d); - - Assert.assertEquals("1.30", s); - - - s = cache.getFormattedValue(1.0d); - - Assert.assertEquals("1.00", s); - - for(int i = 0 ; i < 100 ; i++){ - double f = 0.75f + i; - s = cache.getFormattedValue(f); - Assert.assertEquals(i+".75", s); - } - - - s = cache.getFormattedValue(1.5323234d); - Assert.assertEquals("1.53", s); - - - s = cache.getFormattedValue(1.31d); - Assert.assertEquals("1.31", s); - - s = cache.getFormattedValue(1.3111111d); - Assert.assertEquals("1.31", s); - - } - - @Test - public void testPrimIntFloat(){ - - int digits = 2; - - StringBuffer b = new StringBuffer(); - for (int i = 0; i < digits; i++) { - if (i == 0) - b.append("."); - b.append("0"); - } - - FormattedStringCache.PrimIntFloat cache = new FormattedStringCache.PrimIntFloat(new DecimalFormat("###,###,###,##0" + b.toString())); - - String s = null; - - s = cache.getFormattedValue(1.0f, 0); - - Assert.assertEquals("1.00", s); - - s = cache.getFormattedValue(1.0f, 0); - - Assert.assertEquals("1.00", s); - - - s = cache.getFormattedValue(1.3f ,1); - - Assert.assertEquals("1.30", s); - - - s = cache.getFormattedValue(1.3f, 1); - - Assert.assertEquals("1.30", s); - - - s = cache.getFormattedValue(1.3f, 0); - - Assert.assertEquals("1.30", s); - - - s = cache.getFormattedValue(1.0f, 1); - - Assert.assertEquals("1.00", s); - - for(int i = 0 ; i < 100 ; i++){ - float f = 0.75f + i; - s = cache.getFormattedValue(f, i); - Assert.assertEquals(i+".75", s); - } - - - s = cache.getFormattedValue(1.5323234f, 200); - Assert.assertEquals("1.53", s); - - - s = cache.getFormattedValue(1.31f, 300); - Assert.assertEquals("1.31", s); - - s = cache.getFormattedValue(1.3111111f, 300); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.3111111f, 400); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.3111111f, 500); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.3111111f, 5000); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.31f, 0); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.31f, 1); - Assert.assertEquals("1.31", s); - } - - @Test - public void testGenericKV(){ - - this.genericIntFloat(); - this.genericDoubleFloat(); - this.genericObjectFloat(); - } - - private void genericObjectFloat() { - - - int digits = 2; - - StringBuffer b = new StringBuffer(); - for (int i = 0; i < digits; i++) { - if (i == 0) - b.append("."); - b.append("0"); - } - - FormattedStringCache.Generic cache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,###,##0" + b.toString())); - - String s = null; - - - Object obj0 = new Object(); - Object obj1 = new Object(); - Object obj2 = new Object(); - - s = cache.getFormattedValue(10f, obj0); - - Assert.assertEquals("10.00", s); - - - s = cache.getFormattedValue(10f, obj0); - - Assert.assertEquals("10.00", s); - - - s = cache.getFormattedValue(11f, obj1); - - Assert.assertEquals("11.00", s); - - s = cache.getFormattedValue(10f, obj2); - - Assert.assertEquals("10.00", s); - - - s = cache.getFormattedValue(11f, obj0); - - Assert.assertEquals("11.00", s); - - - } - - private void genericDoubleFloat() { - - - - int digits = 2; - - StringBuffer b = new StringBuffer(); - for (int i = 0; i < digits; i++) { - if (i == 0) - b.append("."); - b.append("0"); - } - - FormattedStringCache.Generic cache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,###,##0" + b.toString())); - - String s = null; - - s = cache.getFormattedValue(1.0f, 0d); - - Assert.assertEquals("1.00", s); - - s = cache.getFormattedValue(1.0f, 0d); - - Assert.assertEquals("1.00", s); - - - s = cache.getFormattedValue(1.3f ,1d); - - Assert.assertEquals("1.30", s); - - - s = cache.getFormattedValue(1.3f, 1d); - - Assert.assertEquals("1.30", s); - - - s = cache.getFormattedValue(1.3f, 0d); - - Assert.assertEquals("1.30", s); - - - s = cache.getFormattedValue(1.0f, 1d); - - Assert.assertEquals("1.00", s); - - for(int i = 0 ; i < 100 ; i++){ - float f = 0.75f + i; - s = cache.getFormattedValue(f, (double)i); - Assert.assertEquals(i+".75", s); - } - - - s = cache.getFormattedValue(1.5323234f, 200d); - Assert.assertEquals("1.53", s); - - - s = cache.getFormattedValue(1.31f, 300d); - Assert.assertEquals("1.31", s); - - s = cache.getFormattedValue(1.3111111f, 300d); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.3111111f, 400d); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.3111111f, 500d); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.3111111f, 5000d); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.31f, 0d); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.31f, 1d); - Assert.assertEquals("1.31", s); - - } - - private void genericIntFloat() { - - - int digits = 2; - - StringBuffer b = new StringBuffer(); - for (int i = 0; i < digits; i++) { - if (i == 0) - b.append("."); - b.append("0"); - } - - FormattedStringCache.Generic cache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,###,##0" + b.toString())); - - String s = null; - - s = cache.getFormattedValue(1.0f, 0); - - Assert.assertEquals("1.00", s); - - s = cache.getFormattedValue(1.0f, 0); - - Assert.assertEquals("1.00", s); - - - s = cache.getFormattedValue(1.3f ,1); - - Assert.assertEquals("1.30", s); - - - s = cache.getFormattedValue(1.3f, 1); - - Assert.assertEquals("1.30", s); - - - s = cache.getFormattedValue(1.3f, 0); - - Assert.assertEquals("1.30", s); - - - s = cache.getFormattedValue(1.0f, 1); - - Assert.assertEquals("1.00", s); - - for(int i = 0 ; i < 100 ; i++){ - float f = 0.75f + i; - s = cache.getFormattedValue(f, i); - Assert.assertEquals(i+".75", s); - } - - - s = cache.getFormattedValue(1.5323234f, 200); - Assert.assertEquals("1.53", s); - - - s = cache.getFormattedValue(1.31f, 300); - Assert.assertEquals("1.31", s); - - s = cache.getFormattedValue(1.3111111f, 300); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.3111111f, 400); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.3111111f, 500); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.3111111f, 5000); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.31f, 0); - Assert.assertEquals("1.31", s); - - - s = cache.getFormattedValue(1.31f, 1); - Assert.assertEquals("1.31", s); - } - -} From 18eec8f0e6bf2571732ad319e9beb0a7ddcfc6c7 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 9 Aug 2016 15:44:03 +0300 Subject: [PATCH 028/291] If we are at it - rename those interfaces too FillFormatter -> IFillFormatter ValueFormatter -> IValueFormatter AxisValueFormatter -> IAxisValueFormatter --- .../mpchartexample/BarChartActivity.java | 8 +++----- .../mpchartexample/BarChartActivityMultiDataset.java | 4 ++-- .../mpchartexample/BarChartPositiveNegative.java | 8 +++++--- .../mpchartexample/CombinedChartActivity.java | 6 ++---- .../mpchartexample/CubicLineChartActivity.java | 4 ++-- .../mpchartexample/FilledLineActivity.java | 6 +++--- .../xxmassdeveloper/mpchartexample/LineChartTime.java | 4 ++-- .../mpchartexample/RadarChartActivitry.java | 5 ++--- .../mpchartexample/StackedBarActivityNegative.java | 9 +++++---- .../mpchartexample/custom/DayAxisValueFormatter.java | 7 +++---- .../mpchartexample/custom/MyAxisValueFormatter.java | 5 +++-- .../custom/MyCustomXAxisValueFormatter.java | 5 +++-- .../mpchartexample/custom/MyFillFormatter.java | 5 +++-- .../mpchartexample/custom/MyValueFormatter.java | 5 +++-- .../mpchartexample/custom/XYMarkerView.java | 8 +++----- .../mpchartexample/custom/YearXAxisFormatter.java | 5 +++-- .../com/github/mikephil/charting/charts/Chart.java | 7 +++---- .../com/github/mikephil/charting/charts/PieChart.java | 2 +- .../github/mikephil/charting/components/AxisBase.java | 8 ++++---- .../com/github/mikephil/charting/data/BaseDataSet.java | 8 ++++---- .../com/github/mikephil/charting/data/ChartData.java | 6 +++--- .../com/github/mikephil/charting/data/LineDataSet.java | 10 +++++----- .../charting/formatter/DefaultAxisValueFormatter.java | 3 ++- .../charting/formatter/DefaultFillFormatter.java | 3 ++- .../charting/formatter/DefaultValueFormatter.java | 3 ++- ...xisValueFormatter.java => IAxisValueFormatter.java} | 3 ++- .../{FillFormatter.java => IFillFormatter.java} | 3 ++- .../{ValueFormatter.java => IValueFormatter.java} | 5 +++-- .../charting/formatter/LargeValueFormatter.java | 7 ++++--- .../mikephil/charting/formatter/PercentFormatter.java | 9 +++++---- .../charting/formatter/StackedValueFormatter.java | 3 ++- .../interfaces/dataprovider/ChartInterface.java | 6 ++---- .../charting/interfaces/datasets/IDataSet.java | 6 +++--- .../charting/interfaces/datasets/ILineDataSet.java | 7 +++---- .../mikephil/charting/renderer/DataRenderer.java | 6 +++--- .../charting/renderer/HorizontalBarChartRenderer.java | 4 ++-- .../mikephil/charting/renderer/PieChartRenderer.java | 5 ++--- 37 files changed, 106 insertions(+), 102 deletions(-) rename MPChartLib/src/main/java/com/github/mikephil/charting/formatter/{AxisValueFormatter.java => IAxisValueFormatter.java} (95%) rename MPChartLib/src/main/java/com/github/mikephil/charting/formatter/{FillFormatter.java => IFillFormatter.java} (95%) rename MPChartLib/src/main/java/com/github/mikephil/charting/formatter/{ValueFormatter.java => IValueFormatter.java} (90%) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 36dbd042b6..7718cd12ea 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -2,8 +2,6 @@ package com.xxmassdeveloper.mpchartexample; import android.annotation.SuppressLint; -import android.graphics.PointF; -import android.graphics.Rect; import android.graphics.RectF; import android.os.Bundle; import android.util.Log; @@ -28,7 +26,7 @@ import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.IDataSet; @@ -80,7 +78,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); // mChart.setDrawYLabels(false); - AxisValueFormatter xAxisFormatter = new DayAxisValueFormatter(mChart); + IAxisValueFormatter xAxisFormatter = new DayAxisValueFormatter(mChart); XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); @@ -90,7 +88,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setLabelCount(7); xAxis.setValueFormatter(xAxisFormatter); - AxisValueFormatter custom = new MyAxisValueFormatter(); + IAxisValueFormatter custom = new MyAxisValueFormatter(); YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(mTfLight); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index f27de86b53..fe63f075d0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -21,7 +21,7 @@ import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.formatter.LargeValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; @@ -87,7 +87,7 @@ protected void onCreate(Bundle savedInstanceState) { xl.setTypeface(mTfLight); xl.setGranularity(1f); xl.setCenterAxisLabels(true); - xl.setValueFormatter(new AxisValueFormatter() { + xl.setValueFormatter(new IAxisValueFormatter() { @Override public String getFormattedValue(float value, AxisBase axis) { return String.valueOf((int) value); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 1c22ae41c6..424873e72a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -15,7 +15,8 @@ import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.utils.ViewPortHandler; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -86,7 +87,7 @@ protected void onCreate(Bundle savedInstanceState) { data.add(new Data(3.5f, -442.3f, "01-01")); data.add(new Data(4.5f, -2280.1f, "01-02")); - xAxis.setValueFormatter(new AxisValueFormatter() { + xAxis.setValueFormatter(new IAxisValueFormatter() { @Override public String getFormattedValue(float value, AxisBase axis) { return data.get(Math.min(Math.max((int) value, 0), data.size()-1)).xAxisValue; @@ -162,7 +163,8 @@ public Data(float xValue, float yValue, String xAxisValue) { } } - private class ValueFormatter implements com.github.mikephil.charting.formatter.ValueFormatter { + private class ValueFormatter implements IValueFormatter + { private DecimalFormat mFormat; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 519a15d219..599143e38b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -2,7 +2,6 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; -import android.graphics.Paint; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; @@ -30,13 +29,12 @@ import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; -import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; -import java.util.Arrays; public class CombinedChartActivity extends DemoBase { @@ -78,7 +76,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setPosition(XAxisPosition.BOTH_SIDED); xAxis.setAxisMinValue(0f); xAxis.setGranularity(1f); - xAxis.setValueFormatter(new AxisValueFormatter() { + xAxis.setValueFormatter(new IAxisValueFormatter() { @Override public String getFormattedValue(float value, AxisBase axis) { return mMonths[(int) value % mMonths.length]; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 2fc7d2b210..2752af37f9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -17,7 +17,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.formatter.FillFormatter; +import com.github.mikephil.charting.formatter.IFillFormatter; import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; @@ -294,7 +294,7 @@ private void setData(int count, float range) { set1.setFillColor(Color.WHITE); set1.setFillAlpha(100); set1.setDrawHorizontalHighlightIndicator(false); - set1.setFillFormatter(new FillFormatter() { + set1.setFillFormatter(new IFillFormatter() { @Override public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { return -10; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java index ece33d8964..a9d7210d62 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java @@ -12,7 +12,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.formatter.FillFormatter; +import com.github.mikephil.charting.formatter.IFillFormatter; import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -109,7 +109,7 @@ private void setData(int count, float range) { set1.setFillColor(Color.WHITE); set1.setHighLightColor(Color.rgb(244, 117, 117)); set1.setDrawCircleHole(false); - set1.setFillFormatter(new FillFormatter() { + set1.setFillFormatter(new IFillFormatter() { @Override public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { return mChart.getAxisLeft().getAxisMinimum(); @@ -128,7 +128,7 @@ public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProv set2.setFillColor(Color.WHITE); set2.setDrawCircleHole(false); set2.setHighLightColor(Color.rgb(244, 117, 117)); - set2.setFillFormatter(new FillFormatter() { + set2.setFillFormatter(new IFillFormatter() { @Override public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { return mChart.getAxisLeft().getAxisMaximum(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 7d833625aa..afe8fa2ef0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -20,7 +20,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -89,7 +89,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTextColor(Color.rgb(255, 192, 56)); xAxis.setCenterAxisLabels(true); xAxis.setGranularity(60000L); // one minute in millis - xAxis.setValueFormatter(new AxisValueFormatter() { + xAxis.setValueFormatter(new IAxisValueFormatter() { private SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index 3326442a87..fcc1b2f55c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -14,14 +14,13 @@ import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.MarkerImage; import com.github.mikephil.charting.components.MarkerView; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.RadarData; import com.github.mikephil.charting.data.RadarDataSet; import com.github.mikephil.charting.data.RadarEntry; -import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; import com.xxmassdeveloper.mpchartexample.custom.RadarMarkerView; @@ -74,7 +73,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTextSize(9f); xAxis.setYOffset(0f); xAxis.setXOffset(0f); - xAxis.setValueFormatter(new AxisValueFormatter() { + xAxis.setValueFormatter(new IAxisValueFormatter() { private String[] mActivities = new String[]{"Burger", "Steak", "Salad", "Pasta", "Pizza"}; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 200c012771..1569f574ee 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -20,8 +20,8 @@ import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.ValueFormatter; -import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; @@ -77,7 +77,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setCenterAxisLabels(true); xAxis.setLabelCount(12); xAxis.setGranularity(10f); - xAxis.setValueFormatter(new AxisValueFormatter() { + xAxis.setValueFormatter(new IAxisValueFormatter() { private DecimalFormat format = new DecimalFormat("###"); @@ -220,7 +220,8 @@ public void onNothingSelected() { Log.i("NOTING SELECTED", ""); } - private class CustomFormatter implements ValueFormatter, AxisValueFormatter { + private class CustomFormatter implements IValueFormatter, IAxisValueFormatter + { private DecimalFormat mFormat; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java index d989104e48..f6b2908f72 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -1,15 +1,14 @@ package com.xxmassdeveloper.mpchartexample.custom; import com.github.mikephil.charting.charts.BarLineChartBase; -import com.github.mikephil.charting.charts.Chart; import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.formatter.AxisValueFormatter; -import com.github.mikephil.charting.formatter.DefaultAxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; /** * Created by philipp on 02/06/16. */ -public class DayAxisValueFormatter implements AxisValueFormatter { +public class DayAxisValueFormatter implements IAxisValueFormatter +{ protected String[] mMonths = new String[]{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java index ea50fd8fad..137097b84e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java @@ -1,11 +1,12 @@ package com.xxmassdeveloper.mpchartexample.custom; import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import java.text.DecimalFormat; -public class MyAxisValueFormatter implements AxisValueFormatter { +public class MyAxisValueFormatter implements IAxisValueFormatter +{ private DecimalFormat mFormat; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index 7d698ef0e7..23f5785c11 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -1,7 +1,7 @@ package com.xxmassdeveloper.mpchartexample.custom; import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.utils.ViewPortHandler; import java.text.DecimalFormat; @@ -9,7 +9,8 @@ /** * Created by Philipp Jahoda on 14/09/15. */ -public class MyCustomXAxisValueFormatter implements AxisValueFormatter { +public class MyCustomXAxisValueFormatter implements IAxisValueFormatter +{ private DecimalFormat mFormat; private ViewPortHandler mViewPortHandler; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java index 1c4d450167..e4b94c331f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java @@ -1,13 +1,14 @@ package com.xxmassdeveloper.mpchartexample.custom; -import com.github.mikephil.charting.formatter.FillFormatter; +import com.github.mikephil.charting.formatter.IFillFormatter; import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; /** * Created by Philipp Jahoda on 12/09/15. */ -public class MyFillFormatter implements FillFormatter { +public class MyFillFormatter implements IFillFormatter +{ private float mFillPos = 0f; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java index a374b60e7f..cbf5fd56c8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java @@ -1,12 +1,13 @@ package com.xxmassdeveloper.mpchartexample.custom; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.utils.ViewPortHandler; import java.text.DecimalFormat; -public class MyValueFormatter implements ValueFormatter { +public class MyValueFormatter implements IValueFormatter +{ private DecimalFormat mFormat; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java index 7a7fe726d5..177527b3bf 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java @@ -5,12 +5,10 @@ import android.widget.TextView; import com.github.mikephil.charting.components.MarkerView; -import com.github.mikephil.charting.data.CandleEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.utils.MPPointF; -import com.github.mikephil.charting.utils.Utils; import com.xxmassdeveloper.mpchartexample.R; import java.text.DecimalFormat; @@ -23,11 +21,11 @@ public class XYMarkerView extends MarkerView { private TextView tvContent; - private AxisValueFormatter xAxisValueFormatter; + private IAxisValueFormatter xAxisValueFormatter; private DecimalFormat format; - public XYMarkerView(Context context, AxisValueFormatter xAxisValueFormatter) { + public XYMarkerView(Context context, IAxisValueFormatter xAxisValueFormatter) { super(context, R.layout.custom_marker_view); this.xAxisValueFormatter = xAxisValueFormatter; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java index e9b31bb6c0..0b1245cd77 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java @@ -1,12 +1,13 @@ package com.xxmassdeveloper.mpchartexample.custom; import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; /** * Created by Philipp Jahoda on 14/09/15. */ -public class YearXAxisFormatter implements AxisValueFormatter { +public class YearXAxisFormatter implements IAxisValueFormatter +{ protected String[] mMonths = new String[]{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 12ce891a98..c34a8a013b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -33,7 +33,7 @@ import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.formatter.DefaultValueFormatter; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.ChartHighlighter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.highlight.IHighlighter; @@ -53,7 +53,6 @@ import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; -import java.util.List; /** * Baseclass of all Chart-Views. @@ -976,12 +975,12 @@ public XAxis getXAxis() { } /** - * Returns the default ValueFormatter that has been determined by the chart + * Returns the default IValueFormatter that has been determined by the chart * considering the provided minimum and maximum values. * * @return */ - public ValueFormatter getDefaultValueFormatter() { + public IValueFormatter getDefaultValueFormatter() { return mDefaultFormatter; } 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 index d2acf19b2d..db327fa8b9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java @@ -662,7 +662,7 @@ public boolean isDrawRoundedSlicesEnabled() { /** * If this is enabled, values inside the PieChart are drawn in percent and - * not with their original value. Values provided for the ValueFormatter to + * not with their original value. Values provided for the IValueFormatter to * format are then provided in percent. * * @param enabled diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index 378a3eb49e..bc729f1148 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -5,7 +5,7 @@ import android.graphics.DashPathEffect; import android.util.Log; -import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.formatter.DefaultAxisValueFormatter; import com.github.mikephil.charting.utils.Utils; @@ -22,7 +22,7 @@ public abstract class AxisBase extends ComponentBase { /** * custom formatter that is used instead of the auto-formatter if set */ - protected AxisValueFormatter mAxisValueFormatter; + protected IAxisValueFormatter mAxisValueFormatter; private int mGridColor = Color.GRAY; @@ -460,7 +460,7 @@ public String getFormattedLabel(int index) { * * @param f */ - public void setValueFormatter(AxisValueFormatter f) { + public void setValueFormatter(IAxisValueFormatter f) { if (f == null) mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals); @@ -473,7 +473,7 @@ public void setValueFormatter(AxisValueFormatter f) { * * @return */ - public AxisValueFormatter getValueFormatter() { + public IAxisValueFormatter getValueFormatter() { if (mAxisValueFormatter == null) { mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java index 55d7309c9a..662f831d9f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -6,7 +6,7 @@ import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.formatter.DefaultValueFormatter; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.Utils; @@ -49,7 +49,7 @@ public abstract class BaseDataSet implements IDataSet { /** * custom formatter that is used instead of the auto-formatter if set */ - protected transient ValueFormatter mValueFormatter; + protected transient IValueFormatter mValueFormatter; /** * the typeface used for the value text @@ -257,7 +257,7 @@ public boolean isHighlightEnabled() { } @Override - public void setValueFormatter(ValueFormatter f) { + public void setValueFormatter(IValueFormatter f) { if (f == null) return; @@ -266,7 +266,7 @@ public void setValueFormatter(ValueFormatter f) { } @Override - public ValueFormatter getValueFormatter() { + public IValueFormatter getValueFormatter() { if (needsFormatter()) return new DefaultValueFormatter(1); return mValueFormatter; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java index 8c63b3130a..23bd58759b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java @@ -5,7 +5,7 @@ import android.util.Log; import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IDataSet; @@ -635,11 +635,11 @@ public T getFirstRight(List sets) { } /** - * Sets a custom ValueFormatter for all DataSets this data object contains. + * Sets a custom IValueFormatter for all DataSets this data object contains. * * @param f */ - public void setValueFormatter(ValueFormatter f) { + public void setValueFormatter(IValueFormatter f) { if (f == null) return; else { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java index 2f1af59d1d..e987707cef 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java @@ -6,7 +6,7 @@ import android.graphics.DashPathEffect; import com.github.mikephil.charting.formatter.DefaultFillFormatter; -import com.github.mikephil.charting.formatter.FillFormatter; +import com.github.mikephil.charting.formatter.IFillFormatter; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.Utils; @@ -54,7 +54,7 @@ public class LineDataSet extends LineRadarDataSet implements ILineDataSet /** * formatter for customizing the position of the fill-line */ - private FillFormatter mFillFormatter = new DefaultFillFormatter(); + private IFillFormatter mFillFormatter = new DefaultFillFormatter(); /** * if true, drawing circles is enabled @@ -377,12 +377,12 @@ public boolean isDrawCircleHoleEnabled() { } /** - * Sets a custom FillFormatter to the chart that handles the position of the + * Sets a custom IFillFormatter to the chart that handles the position of the * filled-line for each DataSet. Set this to null to use the default logic. * * @param formatter */ - public void setFillFormatter(FillFormatter formatter) { + public void setFillFormatter(IFillFormatter formatter) { if (formatter == null) mFillFormatter = new DefaultFillFormatter(); @@ -391,7 +391,7 @@ public void setFillFormatter(FillFormatter formatter) { } @Override - public FillFormatter getFillFormatter() { + public IFillFormatter getFillFormatter() { return mFillFormatter; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java index ad3bb783ee..f29c554059 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java @@ -7,7 +7,8 @@ /** * Created by philipp on 02/06/16. */ -public class DefaultAxisValueFormatter implements AxisValueFormatter { +public class DefaultAxisValueFormatter implements IAxisValueFormatter +{ /** * decimalformat for formatting diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java index 54e9cca8b3..851faeb333 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java @@ -10,7 +10,8 @@ * * @author Philipp Jahoda */ -public class DefaultFillFormatter implements FillFormatter { +public class DefaultFillFormatter implements IFillFormatter +{ @Override public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java index 5d23d25f7e..e2fea4b079 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java @@ -12,7 +12,8 @@ * * @author Philipp Jahoda */ -public class DefaultValueFormatter implements ValueFormatter { +public class DefaultValueFormatter implements IValueFormatter +{ /** * DecimalFormat for formatting diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/AxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java similarity index 95% rename from MPChartLib/src/main/java/com/github/mikephil/charting/formatter/AxisValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java index ec248cace6..5be1d28752 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/AxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java @@ -7,7 +7,8 @@ * Custom formatter interface that allows formatting of * axis labels before they are being drawn. */ -public interface AxisValueFormatter { +public interface IAxisValueFormatter +{ /** * Called when a value from an axis is to be formatted diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FillFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java similarity index 95% rename from MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FillFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java index 66895d77e7..e096cc6528 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FillFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java @@ -9,7 +9,8 @@ * * @author Philipp Jahoda */ -public interface FillFormatter { +public interface IFillFormatter +{ /** * Returns the vertical (y-axis) position where the filled-line of the diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java similarity index 90% rename from MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java index 1f056bf786..75d2363f26 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java @@ -6,12 +6,13 @@ /** * Interface that allows custom formatting of all values inside the chart before they are * being drawn to the screen. Simply create your own formatting class and let - * it implement ValueFormatter. Then override the getFormattedValue(...) method + * it implement IValueFormatter. Then override the getFormattedValue(...) method * and return whatever you want. * * @author Philipp Jahoda */ -public interface ValueFormatter { +public interface IValueFormatter +{ /** * Called when a value (from labels inside the chart) is formatted diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java index ef8dc4077a..0b4e8306c1 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java @@ -17,7 +17,8 @@ * @author Philipp Jahoda * @author Oleksandr Tyshkovets */ -public class LargeValueFormatter implements ValueFormatter, AxisValueFormatter { +public class LargeValueFormatter implements IValueFormatter, IAxisValueFormatter +{ private static String[] SUFFIX = new String[]{ "", "k", "m", "b", "t" @@ -40,13 +41,13 @@ public LargeValueFormatter(String appendix) { mText = appendix; } - // ValueFormatter + // IValueFormatter @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { return makePretty(value) + mText; } - // AxisValueFormatter + // IAxisValueFormatter @Override public String getFormattedValue(float value, AxisBase axis) { return makePretty(value) + mText; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java index 209da1fa83..d321b5f03e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java @@ -8,12 +8,13 @@ import java.text.DecimalFormat; /** - * This ValueFormatter is just for convenience and simply puts a "%" sign after + * This IValueFormatter is just for convenience and simply puts a "%" sign after * each value. (Recommeded for PieChart) * * @author Philipp Jahoda */ -public class PercentFormatter implements ValueFormatter, AxisValueFormatter { +public class PercentFormatter implements IValueFormatter, IAxisValueFormatter +{ protected DecimalFormat mFormat; @@ -30,13 +31,13 @@ public PercentFormatter(DecimalFormat format) { this.mFormat = format; } - // ValueFormatter + // IValueFormatter @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { return mFormat.format(value) + " %"; } - // AxisValueFormatter + // IAxisValueFormatter @Override public String getFormattedValue(float value, AxisBase axis) { return mFormat.format(value) + " %"; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java index b13dd8d907..0e8351634f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java @@ -12,7 +12,8 @@ * A formatter specifically for stacked BarChart that allows to specify whether the all stack values * or just the top value should be drawn. */ -public class StackedValueFormatter implements ValueFormatter { +public class StackedValueFormatter implements IValueFormatter +{ /** * if true, all stack values of the stacked bar entry are drawn, else only top diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java index a4618554bb..219b46bd82 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java @@ -1,12 +1,10 @@ package com.github.mikephil.charting.interfaces.dataprovider; -import android.graphics.PointF; import android.graphics.RectF; import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.utils.MPPointF; -import com.github.mikephil.charting.utils.Utils; /** * Interface that provides everything there is to know about the dimensions, @@ -63,7 +61,7 @@ public interface ChartInterface { RectF getContentRect(); - ValueFormatter getDefaultValueFormatter(); + IValueFormatter getDefaultValueFormatter(); ChartData getData(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index f36cee928e..f2f2eac203 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -5,7 +5,7 @@ import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import java.util.List; @@ -294,14 +294,14 @@ public interface IDataSet { * * @param f */ - void setValueFormatter(ValueFormatter f); + void setValueFormatter(IValueFormatter f); /** * Returns the formatter used for drawing the values inside the chart. * * @return */ - ValueFormatter getValueFormatter(); + IValueFormatter getValueFormatter(); /** * Returns true if the valueFormatter object of this DataSet is null. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java index bccf6984d5..3f534fe848 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java @@ -1,11 +1,10 @@ package com.github.mikephil.charting.interfaces.datasets; import android.graphics.DashPathEffect; -import android.graphics.drawable.Drawable; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.formatter.FillFormatter; +import com.github.mikephil.charting.formatter.IFillFormatter; /** * Created by Philpp Jahoda on 21/10/15. @@ -96,9 +95,9 @@ public interface ILineDataSet extends ILineRadarDataSet { boolean isDashedLineEnabled(); /** - * Returns the FillFormatter that is set for this DataSet. + * Returns the IFillFormatter that is set for this DataSet. * * @return */ - FillFormatter getFillFormatter(); + IFillFormatter getFillFormatter(); } \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java index 39afe9389a..905c6449cb 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java @@ -9,7 +9,7 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; import com.github.mikephil.charting.interfaces.datasets.IDataSet; @@ -134,7 +134,7 @@ protected void applyValueTextStyle(IDataSet set) { public abstract void drawValues(Canvas c); /** - * Draws the value of the given entry by using the provided ValueFormatter. + * Draws the value of the given entry by using the provided IValueFormatter. * * @param c canvas * @param formatter formatter for custom value-formatting @@ -145,7 +145,7 @@ protected void applyValueTextStyle(IDataSet set) { * @param y position * @param color */ - public void drawValue(Canvas c, ValueFormatter formatter, float value, Entry entry, int dataSetIndex, float x, float y, int color) { + public void drawValue(Canvas c, IValueFormatter formatter, float value, Entry entry, int dataSetIndex, float x, float y, int color) { mValuePaint.setColor(color); c.drawText(formatter.getFormattedValue(value, entry, dataSetIndex, mViewPortHandler), x, y, mValuePaint); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index 1519364a95..00cce881f2 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -10,7 +10,7 @@ import com.github.mikephil.charting.buffer.HorizontalBarBuffer; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; @@ -165,7 +165,7 @@ public void drawValues(Canvas c) { applyValueTextStyle(dataSet); final float halfTextHeight = Utils.calcTextHeight(mValuePaint, "10") / 2f; - ValueFormatter formatter = dataSet.getValueFormatter(); + IValueFormatter formatter = dataSet.getValueFormatter(); // get the buffer BarBuffer buffer = mBarBuffers[i]; 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 a489a18c32..36d18e055f 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 @@ -8,7 +8,6 @@ import android.graphics.Paint.Align; import android.graphics.Paint.Style; import android.graphics.Path; -import android.graphics.PointF; import android.graphics.RectF; import android.os.Build; import android.text.Layout; @@ -22,7 +21,7 @@ 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.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -433,7 +432,7 @@ public void drawValues(Canvas c) { float lineHeight = Utils.calcTextHeight(mValuePaint, "Q") + Utils.convertDpToPixel(4f); - ValueFormatter formatter = dataSet.getValueFormatter(); + IValueFormatter formatter = dataSet.getValueFormatter(); int entryCount = dataSet.getEntryCount(); From 2176873271fd6a54511acb80e84d38f681096006 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 9 Aug 2016 16:02:07 +0300 Subject: [PATCH 029/291] Avoid creating a new formatter if null This can improve performance in certain cases, and has the advantage of controlling the "global" default formatter that is used for null cases. --- .../github/mikephil/charting/charts/Chart.java | 10 +++++----- .../mikephil/charting/data/BaseDataSet.java | 2 +- .../github/mikephil/charting/utils/Utils.java | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index c34a8a013b..9b409baa00 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -98,7 +98,7 @@ public abstract class Chart Date: Tue, 9 Aug 2016 16:17:19 +0300 Subject: [PATCH 030/291] Pie's x is deprecated - log it out, and avoid in calcMinMax --- .../github/mikephil/charting/data/PieDataSet.java | 13 +++++++++++++ .../com/github/mikephil/charting/data/PieEntry.java | 3 +++ 2 files changed, 16 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java index 4d8b33b964..316aab4773 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java @@ -45,6 +45,19 @@ public DataSet copy() { return copied; } + @Override + protected void calcMinMax(PieEntry e) { + + if (e == null) + return; + + if (e.getY() < mYMin) + mYMin = e.getY(); + + if (e.getY() > mYMax) + mYMax = e.getY(); + } + /** * Sets the space that is left out between the piechart-slices in dp. * Default: 0 --> no space, maximum 20f diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java index 6ab31f140c..f5c40804c1 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.data; import android.annotation.SuppressLint; +import android.util.Log; /** * Created by Philipp Jahoda on 31/05/16. @@ -49,11 +50,13 @@ public void setLabel(String label) { @Override public void setX(float x) { super.setX(x); + Log.i("DEPRECATED", "Pie entries do not have x values"); } @Deprecated @Override public float getX() { + Log.i("DEPRECATED", "Pie entries do not have x values"); return super.getX(); } From 86d05e989ad0601e98ac31d0b1fa29eb81a582f9 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 9 Aug 2016 20:46:04 +0300 Subject: [PATCH 031/291] Corrected formula for 29-feb :-) --- .../custom/DayAxisValueFormatter.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java index f6b2908f72..6d58892398 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -70,12 +70,17 @@ public String getFormattedValue(float value, AxisBase axis) { private int getDaysForMonth(int month, int year) { + // month is 0-based + if (month == 1) { + int x400 = month % 400; + if (x400 < 0) + { + x400 = -x400; + } + boolean is29 = (month % 4) == 0 && x400 != 100 && x400 != 200 && x400 != 300; - if (year == 2016 || year == 2020) - return 29; - else - return 28; + return is29 ? 29 : 28; } if (month == 3 || month == 5 || month == 8 || month == 10) From 30cb069974a482a4f9a27d243079b286f99faabb Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 10 Aug 2016 00:15:34 +0300 Subject: [PATCH 032/291] Brought back the Realm demos, were removed by mistake --- MPChartExample/AndroidManifest.xml | 10 + MPChartExample/build.gradle | 2 +- .../mpchartexample/custom/RealmDemoData.java | 180 ++++++++++++++++ .../mpchartexample/custom/RealmFloat.java | 27 +++ .../notimportant/MainActivity.java | 9 + .../realm/RealmBaseActivity.java | 203 ++++++++++++++++++ .../realm/RealmDatabaseActivityBar.java | 69 ++++++ .../realm/RealmDatabaseActivityBubble.java | 71 ++++++ .../realm/RealmDatabaseActivityCandle.java | 77 +++++++ .../RealmDatabaseActivityHorizontalBar.java | 74 +++++++ .../realm/RealmDatabaseActivityLine.java | 77 +++++++ .../realm/RealmDatabaseActivityPie.java | 83 +++++++ .../realm/RealmDatabaseActivityRadar.java | 78 +++++++ .../realm/RealmDatabaseActivityScatter.java | 73 +++++++ .../realm/RealmMainActivity.java | 131 +++++++++++ .../realm/RealmWikiExample.java | 137 ++++++++++++ .../mpchartexample/realm/Score.java | 51 +++++ build.gradle | 2 +- 18 files changed, 1352 insertions(+), 2 deletions(-) create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index 4131688a8d..ab730cd2bd 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -47,12 +47,22 @@ + + + + + + + + + + diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 752d3fd321..c0d4745dcc 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -1,5 +1,5 @@ apply plugin: 'com.android.application' -//apply plugin: 'realm-android' +apply plugin: 'realm-android' android { compileSdkVersion 23 diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java new file mode 100644 index 0000000000..7becc88c90 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java @@ -0,0 +1,180 @@ +package com.xxmassdeveloper.mpchartexample.custom; + + +import io.realm.RealmList; +import io.realm.RealmObject; + +/** + * Demo class that encapsulates data stored in realm.io database. + * This class represents data suitable for all chart-types. + */ +public class RealmDemoData extends RealmObject { + + private float yValue; + private float xValue; + + private float open, close, high, low; + + private float bubbleSize; + + private RealmList stackValues; + + private String someStringField; + + /** + * label for pie entries + */ + private String label; + + // ofc there could me more fields here... + + public RealmDemoData() { + + } + + public RealmDemoData(float yValue) { + this.yValue = yValue; + } + + public RealmDemoData(float xValue, float yValue) { + this.xValue = xValue; + this.yValue = yValue; + } + + /** + * Constructor for stacked bars. + * + * @param xValue + * @param stackValues + */ + public RealmDemoData(float xValue, float[] stackValues) { + this.xValue = xValue; + this.stackValues = new RealmList(); + + for (float val : stackValues) { + this.stackValues.add(new RealmFloat(val)); + } + } + + /** + * Constructor for candles. + * + * @param xValue + * @param high + * @param low + * @param open + * @param close + */ + public RealmDemoData(float xValue, float high, float low, float open, float close) { + this.yValue = (high + low) / 2f; + this.high = high; + this.low = low; + this.open = open; + this.close = close; + this.xValue = xValue; + } + + /** + * Constructor for bubbles. + * + * @param xValue + * @param yValue + * @param bubbleSize + */ + public RealmDemoData(float xValue, float yValue, float bubbleSize) { + this.xValue = xValue; + this.yValue = yValue; + this.bubbleSize = bubbleSize; + } + + /** + * Constructor for pie chart. + * + * @param yValue + * @param label + */ + public RealmDemoData(float yValue, String label) { + this.yValue = yValue; + this.label = label; + } + + public float getyValue() { + return yValue; + } + + public void setyValue(float yValue) { + this.yValue = yValue; + } + + public float getxValue() { + return xValue; + } + + public void setxValue(float xValue) { + this.xValue = xValue; + } + + public RealmList getStackValues() { + return stackValues; + } + + public void setStackValues(RealmList stackValues) { + this.stackValues = stackValues; + } + + public float getOpen() { + return open; + } + + public void setOpen(float open) { + this.open = open; + } + + public float getClose() { + return close; + } + + public void setClose(float close) { + this.close = close; + } + + public float getHigh() { + return high; + } + + public void setHigh(float high) { + this.high = high; + } + + public float getLow() { + return low; + } + + public void setLow(float low) { + this.low = low; + } + + public float getBubbleSize() { + return bubbleSize; + } + + public void setBubbleSize(float bubbleSize) { + this.bubbleSize = bubbleSize; + } + + public String getSomeStringField() { + return someStringField; + } + + public void setSomeStringField(String someStringField) { + this.someStringField = someStringField; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } +} \ No newline at end of file diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java new file mode 100644 index 0000000000..15b027b3ff --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java @@ -0,0 +1,27 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import io.realm.RealmObject; + +/** + * Created by Philipp Jahoda on 09/11/15. + */ +public class RealmFloat extends RealmObject { + + private float floatValue; + + public RealmFloat() { + + } + + public RealmFloat(float floatValue) { + this.floatValue = floatValue; + } + + public float getFloatValue() { + return floatValue; + } + + public void setFloatValue(float value) { + this.floatValue = value; + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index 512f4baa14..617e43c021 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -46,6 +46,7 @@ import com.xxmassdeveloper.mpchartexample.StackedBarActivity; import com.xxmassdeveloper.mpchartexample.StackedBarActivityNegative; import com.xxmassdeveloper.mpchartexample.fragments.SimpleChartDemo; +import com.xxmassdeveloper.mpchartexample.realm.RealmMainActivity; import java.util.ArrayList; @@ -130,6 +131,10 @@ protected void onCreate(Bundle savedInstanceState) { "BarChart positive / negative", "This demonstrates how to create a BarChart with positive and negative values in different colors.")); + ContentItem realm = new ContentItem( + "Realm.io Database", + "This demonstrates how to use this library with Realm.io mobile database."); + objects.add(realm); ContentItem time = new ContentItem( "Time Chart", @@ -269,6 +274,10 @@ public void onItemClick(AdapterView av, View v, int pos, long arg3) { i = new Intent(this, BarChartPositiveNegative.class); startActivity(i); break; + case 28: + i = new Intent(this, RealmMainActivity.class); + startActivity(i); + break; case 29: i = new Intent(this, LineChartTime.class); startActivity(i); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java new file mode 100644 index 0000000000..d4fef69576 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -0,0 +1,203 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.graphics.Color; +import android.graphics.Typeface; +import android.os.Bundle; + +import com.github.mikephil.charting.charts.BarLineChartBase; +import com.github.mikephil.charting.charts.Chart; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.formatter.PercentFormatter; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import io.realm.Realm; +import io.realm.RealmConfiguration; + +/** + * Created by Philipp Jahoda on 05/11/15. + */ +public abstract class RealmBaseActivity extends DemoBase { + + protected Realm mRealm; + + protected Typeface mTf; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setTitle("Realm.io Examples"); + } + + protected void setup(Chart chart) { + + mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + + // no description text + chart.setDescription(""); + chart.setNoDataTextDescription("You need to provide data for the chart."); + + // enable touch gestures + chart.setTouchEnabled(true); + + if (chart instanceof BarLineChartBase) { + + BarLineChartBase mChart = (BarLineChartBase) chart; + + mChart.setDrawGridBackground(false); + + // enable scaling and dragging + mChart.setDragEnabled(true); + mChart.setScaleEnabled(true); + + // if disabled, scaling can be done on x- and y-axis separately + mChart.setPinchZoom(false); + + YAxis leftAxis = mChart.getAxisLeft(); + leftAxis.removeAllLimitLines(); // reset all limit lines to avoid overlapping lines + leftAxis.setTypeface(mTf); + leftAxis.setTextSize(8f); + leftAxis.setTextColor(Color.DKGRAY); + leftAxis.setValueFormatter(new PercentFormatter()); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setTypeface(mTf); + xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); + xAxis.setTextSize(8f); + xAxis.setTextColor(Color.DKGRAY); + + mChart.getAxisRight().setEnabled(false); + } + } + + protected void styleData(ChartData data) { + data.setValueTypeface(mTf); + data.setValueTextSize(8f); + data.setValueTextColor(Color.DKGRAY); + data.setValueFormatter(new PercentFormatter()); + } + + @Override + protected void onResume() { + super.onResume(); + + // Create a RealmConfiguration that saves the Realm file in the app's "files" directory. + RealmConfiguration realmConfig = new RealmConfiguration.Builder(getApplicationContext()).build(); + Realm.setDefaultConfiguration(realmConfig); + + mRealm = Realm.getDefaultInstance(); + } + + @Override + protected void onPause() { + super.onPause(); + mRealm.close(); + } + + protected void writeToDB(int objectCount) { + + mRealm.beginTransaction(); + + mRealm.delete(RealmDemoData.class); + + for (int i = 0; i < objectCount; i++) { + + float value = 40f + (float) (Math.random() * 60f); + + RealmDemoData d = new RealmDemoData(i, value); + mRealm.copyToRealm(d); + } + + mRealm.commitTransaction(); + } + + protected void writeToDBStack(int objectCount) { + + mRealm.beginTransaction(); + + mRealm.delete(RealmDemoData.class); + + for (int i = 0; i < objectCount; i++) { + + float val1 = 34f + (float) (Math.random() * 12.0f); + float val2 = 34f + (float) (Math.random() * 12.0f); + float[] stack = new float[]{val1, val2, 100 - val1 - val2}; + + RealmDemoData d = new RealmDemoData(i, stack); + mRealm.copyToRealm(d); + } + + mRealm.commitTransaction(); + } + + protected void writeToDBCandle(int objectCount) { + + mRealm.beginTransaction(); + + mRealm.delete(RealmDemoData.class); + + for (int i = 0; i < objectCount; i++) { + + float mult = 50; + float val = (float) (Math.random() * 40) + mult; + + float high = (float) (Math.random() * 9) + 8f; + float low = (float) (Math.random() * 9) + 8f; + + float open = (float) (Math.random() * 6) + 1f; + float close = (float) (Math.random() * 6) + 1f; + + boolean even = i % 2 == 0; + + RealmDemoData d = new RealmDemoData(i, val + high, val - low, even ? val + open : val - open, + even ? val - close : val + close); + + mRealm.copyToRealm(d); + } + + mRealm.commitTransaction(); + } + + protected void writeToDBBubble(int objectCount) { + + mRealm.beginTransaction(); + + mRealm.delete(RealmDemoData.class); + + for (int i = 0; i < objectCount; i++) { + + float value = 30f + (float) (Math.random() * 100.0); + float size = 15f + (float) (Math.random() * 20.0); + + RealmDemoData d = new RealmDemoData(i, value, size); + mRealm.copyToRealm(d); + } + + mRealm.commitTransaction(); + } + + protected void writeToDBPie() { + + mRealm.beginTransaction(); + + mRealm.delete(RealmDemoData.class); + + float value1 = 15f + (float) (Math.random() * 8f); + float value2 = 15f + (float) (Math.random() * 8f); + float value3 = 15f + (float) (Math.random() * 8f); + float value4 = 15f + (float) (Math.random() * 8f); + float value5 = 100f - value1 - value2 - value3 - value4; + + float[] values = new float[] { value1, value2, value3, value4, value5 }; + String[] labels = new String[]{ "iOS", "Android", "WP 10", "BlackBerry", "Other"}; + + for (int i = 0; i < values.length; i++) { + RealmDemoData d = new RealmDemoData(values[i], labels[i]); + mRealm.copyToRealm(d); + } + + mRealm.commitTransaction(); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java new file mode 100644 index 0000000000..c87290050d --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java @@ -0,0 +1,69 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityBar extends RealmBaseActivity { + + private BarChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_barchart_noseekbar); + + mChart = (BarChart) findViewById(R.id.chart1); + setup(mChart); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDB(20); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries + RealmBarDataSet set = new RealmBarDataSet(result, "xValue", "yValue"); // stacked entries + set.setColors(new int[] {ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); + set.setLabel("Realm BarDataSet"); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + BarData data = new BarData(dataSets); + styleData(data); + + // set data + mChart.setData(data); + mChart.setFitBars(true); + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java new file mode 100644 index 0000000000..d0aa25b864 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java @@ -0,0 +1,71 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.BubbleChart; +import com.github.mikephil.charting.data.BubbleData; +import com.github.mikephil.charting.data.realm.implementation.RealmBubbleDataSet; +import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityBubble extends RealmBaseActivity { + + private BubbleChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_bubblechart_noseekbar); + + mChart = (BubbleChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.getXAxis().setDrawGridLines(false); + mChart.getAxisLeft().setDrawGridLines(false); + mChart.setPinchZoom(true); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDBBubble(10); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + RealmBubbleDataSet set = new RealmBubbleDataSet(result, "xValue", "yValue", "bubbleSize"); + set.setLabel("Realm BubbleDataSet"); + set.setColors(ColorTemplate.COLORFUL_COLORS, 110); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + BubbleData data = new BubbleData(dataSets); + styleData(data); + + // set data + mChart.setData(data); + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java new file mode 100644 index 0000000000..a388df3741 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java @@ -0,0 +1,77 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.graphics.Color; +import android.graphics.Paint; +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.CandleStickChart; +import com.github.mikephil.charting.data.CandleData; +import com.github.mikephil.charting.data.realm.implementation.RealmCandleDataSet; +import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityCandle extends RealmBaseActivity { + + private CandleStickChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_candlechart_noseekbar); + + mChart = (CandleStickChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.getAxisLeft().setDrawGridLines(false); + mChart.getXAxis().setDrawGridLines(false); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDBCandle(50); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + RealmCandleDataSet set = new RealmCandleDataSet(result, "xValue", "high", "low", "open", "close"); + set.setLabel("Realm CandleDataSet"); + set.setShadowColor(Color.DKGRAY); + set.setShadowWidth(0.7f); + set.setDecreasingColor(Color.RED); + set.setDecreasingPaintStyle(Paint.Style.FILL); + set.setIncreasingColor(Color.rgb(122, 242, 84)); + set.setIncreasingPaintStyle(Paint.Style.STROKE); + set.setNeutralColor(Color.BLUE); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + CandleData data = new CandleData(dataSets); + styleData(data); + + // set data + mChart.setData(data); + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java new file mode 100644 index 0000000000..32d1234fe2 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java @@ -0,0 +1,74 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.graphics.Color; +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityHorizontalBar extends RealmBaseActivity { + + private HorizontalBarChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_horizontalbarchart_noseekbar); + + mChart = (HorizontalBarChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.getAxisLeft().setAxisMinValue(0f); + mChart.setDrawValueAboveBar(false); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDBStack(50); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries + RealmBarDataSet set = new RealmBarDataSet(result, "xValue", "stackValues", "floatValue"); // stacked entries + set.setColors(new int[]{ColorTemplate.rgb("#8BC34A"), ColorTemplate.rgb("#FFC107"), ColorTemplate.rgb("#9E9E9E")}); + set.setLabel("Mobile OS distribution"); + set.setStackLabels(new String[]{"iOS", "Android", "Other"}); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + BarData data = new BarData(dataSets); + styleData(data); + data.setValueTextColor(Color.WHITE); + + // set data + mChart.setData(data); + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java new file mode 100644 index 0000000000..6d2396c22b --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java @@ -0,0 +1,77 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityLine extends RealmBaseActivity { + + private LineChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_linechart_noseekbar); + + mChart = (LineChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.getAxisLeft().setAxisMaxValue(150f); + mChart.getAxisLeft().setAxisMinValue(0f); + mChart.getAxisLeft().setDrawGridLines(false); + mChart.getXAxis().setDrawGridLines(false); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDB(40); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + RealmLineDataSet set = new RealmLineDataSet(result, "xValue", "yValue"); + set.setDrawCubic(false); + set.setLabel("Realm LineDataSet"); + set.setDrawCircleHole(false); + set.setColor(ColorTemplate.rgb("#FF5722")); + set.setCircleColor(ColorTemplate.rgb("#FF5722")); + set.setLineWidth(1.8f); + set.setCircleSize(3.6f); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + LineData data = new LineData(dataSets); + styleData(data); + + // set data + mChart.setData(data); + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java new file mode 100644 index 0000000000..7b1eb675d9 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java @@ -0,0 +1,83 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.graphics.Color; +import android.graphics.Typeface; +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.view.WindowManager; + +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.realm.implementation.RealmPieDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityPie extends RealmBaseActivity { + + private PieChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_piechart_noseekbar); + + mChart = (PieChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.setCenterText(generateCenterSpannableText()); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDBPie(); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries + RealmPieDataSet set = new RealmPieDataSet(result, "yValue", "label"); // stacked entries + set.setColors(ColorTemplate.VORDIPLOM_COLORS); + set.setLabel("Example market share"); + set.setSliceSpace(2); + + // create a data object with the dataset list + PieData data = new PieData(set); + styleData(data); + data.setValueTextColor(Color.WHITE); + data.setValueTextSize(12f); + + // set data + mChart.setData(data); + mChart.animateY(1400); + } + + private SpannableString generateCenterSpannableText() { + + SpannableString s = new SpannableString("Realm.io\nmobile database"); + s.setSpan(new ForegroundColorSpan(Color.rgb(240, 115, 126)), 0, 8, 0); + s.setSpan(new RelativeSizeSpan(2.2f), 0, 8, 0); + s.setSpan(new StyleSpan(Typeface.ITALIC), 9, s.length(), 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), 9, s.length(), 0); + s.setSpan(new RelativeSizeSpan(0.85f), 9, s.length(), 0); + return s; + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java new file mode 100644 index 0000000000..411f4b6ac9 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java @@ -0,0 +1,78 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.graphics.Color; +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.data.RadarData; +import com.github.mikephil.charting.data.realm.implementation.RealmRadarDataSet; +import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityRadar extends RealmBaseActivity { + + private RadarChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_radarchart_noseekbar); + + mChart = (RadarChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.getYAxis().setEnabled(false); + mChart.getXAxis().setEnabled(false); + mChart.setWebAlpha(180); + mChart.setWebColorInner(Color.DKGRAY); + mChart.setWebColor(Color.GRAY); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDB(7); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries + RealmRadarDataSet set = new RealmRadarDataSet(result, "yValue"); // stacked entries + set.setLabel("Realm RadarDataSet"); + set.setDrawFilled(true); + set.setColor(ColorTemplate.rgb("#009688")); + set.setFillColor(ColorTemplate.rgb("#009688")); + set.setFillAlpha(130); + set.setLineWidth(2f); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + RadarData data = new RadarData(dataSets); + styleData(data); + + // set data + mChart.setData(data); + mChart.animateY(1400); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java new file mode 100644 index 0000000000..14175ac73a --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java @@ -0,0 +1,73 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.ScatterChart; +import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.data.realm.implementation.RealmScatterDataSet; +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public class RealmDatabaseActivityScatter extends RealmBaseActivity { + + private ScatterChart mChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_scatterchart_noseekbar); + + mChart = (ScatterChart) findViewById(R.id.chart1); + setup(mChart); + + mChart.getAxisLeft().setDrawGridLines(false); + mChart.getXAxis().setDrawGridLines(false); + mChart.setPinchZoom(true); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + // write some demo-data into the realm.io database + writeToDB(45); + + // add data to the chart + setData(); + } + + private void setData() { + + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); + + RealmScatterDataSet set = new RealmScatterDataSet(result, "xValue", "yValue"); + set.setLabel("Realm ScatterDataSet"); + set.setScatterShapeSize(9f); + set.setColor(ColorTemplate.rgb("#CDDC39")); + set.setScatterShape(ScatterChart.ScatterShape.CIRCLE); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set); // add the dataset + + // create a data object with the dataset list + ScatterData data = new ScatterData(dataSets); + styleData(data); + + // set data + mChart.setData(data); + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java new file mode 100644 index 0000000000..3198320272 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java @@ -0,0 +1,131 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.WindowManager; +import android.widget.AdapterView; +import android.widget.ListView; + +import com.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.notimportant.ContentItem; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; +import com.xxmassdeveloper.mpchartexample.notimportant.MyAdapter; + +import java.util.ArrayList; + +import io.realm.Realm; +import io.realm.RealmConfiguration; + +/** + * Created by Philipp Jahoda on 07/12/15. + */ +public class RealmMainActivity extends DemoBase implements AdapterView.OnItemClickListener { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_main); + + setTitle("Realm.io Examples"); + + ArrayList objects = new ArrayList(); + + objects.add(new ContentItem("Line Chart", "Creating a LineChart with Realm.io database")); + objects.add(new ContentItem("Bar Chart", + "Creating a BarChart with Realm.io database")); + objects.add(new ContentItem("Horizontal Bar Chart", + "Creating a HorizontalBarChart with Realm.io database")); + objects.add(new ContentItem("Scatter Chart", + "Creating a ScatterChart with Realm.io database")); + objects.add(new ContentItem("Candle Stick Chart", "Creating a CandleStickChart with Realm.io database")); + objects.add(new ContentItem("Bubble Chart", "Creating a BubbleChart with Realm.io database")); + objects.add(new ContentItem("Pie Chart", "Creating a PieChart with Realm.io database")); + objects.add(new ContentItem("Radar Chart", "Creating a RadarChart with Realm.io database")); + objects.add(new ContentItem("Realm Wiki", "This is the code related to the wiki entry about realm.io on the MPAndroidChart github page.")); + + MyAdapter adapter = new MyAdapter(this, objects); + + ListView lv = (ListView) findViewById(R.id.listView1); + lv.setAdapter(adapter); + + lv.setOnItemClickListener(this); + + // Create a RealmConfiguration that saves the Realm file in the app's "files" directory. + RealmConfiguration realmConfig = new RealmConfiguration.Builder(getApplicationContext()).build(); + Realm.setDefaultConfiguration(realmConfig); + + Realm realm = Realm.getDefaultInstance(); + realm.beginTransaction(); + realm.deleteAll(); + realm.commitTransaction(); + } + + @Override + public void onItemClick(AdapterView av, View v, int pos, long arg3) { + + Intent i; + + switch (pos) { + case 0: + i = new Intent(this, RealmDatabaseActivityLine.class); + startActivity(i); + break; + case 1: + i = new Intent(this, RealmDatabaseActivityBar.class); + startActivity(i); + break; + case 2: + i = new Intent(this, RealmDatabaseActivityHorizontalBar.class); + startActivity(i); + break; + case 3: + i = new Intent(this, RealmDatabaseActivityScatter.class); + startActivity(i); + break; + case 4: + i = new Intent(this, RealmDatabaseActivityCandle.class); + startActivity(i); + break; + case 5: + i = new Intent(this, RealmDatabaseActivityBubble.class); + startActivity(i); + break; + case 6: + i = new Intent(this, RealmDatabaseActivityPie.class); + startActivity(i); + break; + case 7: + i = new Intent(this, RealmDatabaseActivityRadar.class); + startActivity(i); + break; + case 8: + i = new Intent(this, RealmWikiExample.class); + startActivity(i); + break; + } + + overridePendingTransition(R.anim.move_right_in_activity, R.anim.move_left_out_activity); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.realm, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://realm.io/")); + startActivity(i); + + return super.onOptionsItemSelected(item); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java new file mode 100644 index 0000000000..7682bca957 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java @@ -0,0 +1,137 @@ +package com.xxmassdeveloper.mpchartexample.realm; + +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; +import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; +import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.R; + +import java.util.ArrayList; + +import io.realm.RealmResults; + +/** + * Created by Philipp Jahoda on 18/12/15. + */ +public class RealmWikiExample extends RealmBaseActivity { + + private LineChart lineChart; + private BarChart barChart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_realm_wiki); + + lineChart = (LineChart) findViewById(R.id.lineChart); + barChart = (BarChart) findViewById(R.id.barChart); + setup(lineChart); + setup(barChart); + + lineChart.setExtraBottomOffset(5f); + barChart.setExtraBottomOffset(5f); + + lineChart.getAxisLeft().setDrawGridLines(false); + lineChart.getXAxis().setDrawGridLines(false); + lineChart.getXAxis().setLabelCount(5); + lineChart.getXAxis().setGranularity(1f); + barChart.getAxisLeft().setDrawGridLines(false); + barChart.getXAxis().setDrawGridLines(false); + barChart.getXAxis().setLabelCount(5); + barChart.getXAxis().setGranularity(1f); + } + + @Override + protected void onResume() { + super.onResume(); // setup realm + + mRealm.beginTransaction(); + + // write some demo-data into the realm.io database + Score score1 = new Score(100f, 0f, "Peter"); + mRealm.copyToRealm(score1); + Score score2 = new Score(110f, 1f, "Lisa"); + mRealm.copyToRealm(score2); + Score score3 = new Score(130f, 2f, "Dennis"); + mRealm.copyToRealm(score3); + Score score4 = new Score(70f, 3f, "Luke"); + mRealm.copyToRealm(score4); + Score score5 = new Score(80f, 4f, "Sarah"); + mRealm.copyToRealm(score5); + + mRealm.commitTransaction(); + + // add data to the chart + setData(); + } + + private void setData() { + + // LINE-CHART + final RealmResults results = mRealm.where(Score.class).findAll(); + + + AxisValueFormatter formatter = new AxisValueFormatter() { + @Override + public String getFormattedValue(float value, AxisBase axis) { + return results.get((int) value).getPlayerName(); + } + + @Override + public int getDecimalDigits() { + return 0; + } + }; + + lineChart.getXAxis().setValueFormatter(formatter); + barChart.getXAxis().setValueFormatter(formatter); + + RealmLineDataSet lineDataSet = new RealmLineDataSet(results, "scoreNr", "totalScore"); + lineDataSet.setDrawCubic(false); + lineDataSet.setLabel("Result Scores"); + lineDataSet.setDrawCircleHole(false); + lineDataSet.setColor(ColorTemplate.rgb("#FF5722")); + lineDataSet.setCircleColor(ColorTemplate.rgb("#FF5722")); + lineDataSet.setLineWidth(1.8f); + lineDataSet.setCircleSize(3.6f); + + ArrayList dataSets = new ArrayList(); + dataSets.add(lineDataSet); + + LineData lineData = new LineData(dataSets); + styleData(lineData); + + // set data + lineChart.setData(lineData); + lineChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + + + // BAR-CHART + RealmBarDataSet barDataSet = new RealmBarDataSet(results, "scoreNr", "totalScore"); + barDataSet.setColors(new int[]{ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); + barDataSet.setLabel("Realm BarDataSet"); + + ArrayList barDataSets = new ArrayList(); + barDataSets.add(barDataSet); + + BarData barData = new BarData(barDataSets); + styleData(barData); + + barChart.setData(barData); + barChart.setFitBars(true); + barChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java new file mode 100644 index 0000000000..870e371491 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java @@ -0,0 +1,51 @@ +package com.xxmassdeveloper.mpchartexample.realm; + + +import io.realm.RealmObject; + +/** + * our data object + */ +public class Score extends RealmObject { + + private float totalScore; + + private float scoreNr; + + private String playerName; + + public Score() { + } + + public Score(float totalScore, float scoreNr, String playerName) { + this.scoreNr = scoreNr; + this.playerName = playerName; + this.totalScore = totalScore; + } + + // all getters and setters... + + public float getTotalScore() { + return totalScore; + } + + public void setTotalScore(float totalScore) { + this.totalScore = totalScore; + } + + public float getScoreNr() { + return scoreNr; + } + + public void setScoreNr(float scoreNr) { + this.scoreNr = scoreNr; + } + + public String getPlayerName() { + return playerName; + } + + public void setPlayerName(String playerName) { + this.playerName = playerName; + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 339dc2007c..3dfff3b287 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - //classpath "io.realm:realm-gradle-plugin:1.1.0" + classpath "io.realm:realm-gradle-plugin:1.1.0" classpath 'com.android.tools.build:gradle:2.1.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } From 31b0fd9ff94765e68022232099fe02b06113b182 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 10 Aug 2016 00:24:41 +0300 Subject: [PATCH 033/291] Updated Realm sample --- .../mpchartexample/realm/RealmWikiExample.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java index 7682bca957..a2b9e05880 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java @@ -11,7 +11,7 @@ import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; -import com.github.mikephil.charting.formatter.AxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -84,7 +84,7 @@ private void setData() { final RealmResults results = mRealm.where(Score.class).findAll(); - AxisValueFormatter formatter = new AxisValueFormatter() { + IAxisValueFormatter formatter = new IAxisValueFormatter() { @Override public String getFormattedValue(float value, AxisBase axis) { return results.get((int) value).getPlayerName(); From 788539001ff66550fe895d2868c3a4b1a56df02c Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 10 Aug 2016 22:43:54 +0300 Subject: [PATCH 034/291] Choose a default that matches the default v2 behavior To avoid hurting existing stylings drastically --- .../main/java/com/github/mikephil/charting/data/BarData.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java index 20a27a5fec..16d60f6f9c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java @@ -15,7 +15,7 @@ public class BarData extends BarLineScatterCandleBubbleData { /** * the width of the bars on the x-axis, in values (not pixels) */ - private float mBarWidth = 1f; + private float mBarWidth = 0.85f; public BarData() { super(); @@ -31,7 +31,7 @@ public BarData(List dataSets) { /** * Sets the width each bar should have on the x-axis (in values, not pixels). - * Default 1f + * Default 0.85f * * @param mBarWidth */ From e158ef15e23bb8f00f4cf064a181bd1a018d604b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 11 Aug 2016 12:46:52 +0200 Subject: [PATCH 035/291] Renaming of setAxisMinValue etc to setAxisMinimum for consistency --- .../mpchartexample/BarChartActivity.java | 8 ++--- .../BarChartActivityMultiDataset.java | 6 ++-- .../mpchartexample/BarChartActivitySinus.java | 8 ++--- .../BarChartPositiveNegative.java | 4 +-- .../mpchartexample/CombinedChartActivity.java | 8 ++--- .../mpchartexample/FilledLineActivity.java | 4 +-- .../HorizontalBarChartActivity.java | 6 ++-- .../InvertedLineChartActivity.java | 4 +-- .../mpchartexample/LineChartActivity1.java | 4 +-- .../mpchartexample/LineChartActivity2.java | 8 ++--- .../mpchartexample/LineChartTime.java | 4 +-- .../mpchartexample/RadarChartActivitry.java | 4 +-- .../RealtimeLineChartActivity.java | 5 ++-- .../mpchartexample/ScatterChartActivity.java | 3 +- .../mpchartexample/StackedBarActivity.java | 2 +- .../StackedBarActivityNegative.java | 8 ++--- .../fragments/BarChartFrag.java | 2 +- .../fragments/SineCosineFragment.java | 4 +-- .../listviewitems/BarChartItem.java | 4 +-- .../listviewitems/LineChartItem.java | 4 +-- .../RealmDatabaseActivityHorizontalBar.java | 2 +- .../realm/RealmDatabaseActivityLine.java | 4 +-- .../charting/components/AxisBase.java | 30 +++++++++++++++---- .../mikephil/charting/components/YAxis.java | 6 ++-- 24 files changed, 79 insertions(+), 63 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 7718cd12ea..71aba6f1dd 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -96,7 +96,7 @@ protected void onCreate(Bundle savedInstanceState) { leftAxis.setValueFormatter(custom); leftAxis.setPosition(YAxisLabelPosition.OUTSIDE_CHART); leftAxis.setSpaceTop(15f); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) YAxis rightAxis = mChart.getAxisRight(); rightAxis.setDrawGridLines(false); @@ -104,7 +104,7 @@ protected void onCreate(Bundle savedInstanceState) { rightAxis.setLabelCount(8, false); rightAxis.setValueFormatter(custom); rightAxis.setSpaceTop(15f); - rightAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) Legend l = mChart.getLegend(); l.setPosition(LegendPosition.BELOW_CHART_LEFT); @@ -228,8 +228,8 @@ private void setData(int count, float range) { float start = 0f; - mChart.getXAxis().setAxisMinValue(start); - mChart.getXAxis().setAxisMaxValue(start + count + 2); + mChart.getXAxis().setAxisMinimum(start); + mChart.getXAxis().setAxisMaximum(start + count + 2); ArrayList yVals1 = new ArrayList(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index fe63f075d0..4024260cc6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -104,7 +104,7 @@ public int getDecimalDigits() { leftAxis.setValueFormatter(new LargeValueFormatter()); leftAxis.setDrawGridLines(false); leftAxis.setSpaceTop(30f); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) mChart.getAxisRight().setEnabled(false); } @@ -249,8 +249,8 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } mChart.getBarData().setBarWidth(barWidth); - mChart.getXAxis().setAxisMinValue(startYear); - mChart.getXAxis().setAxisMaxValue(mChart.getBarData().getGroupWidth(groupSpace, barSpace) * mSeekBarX.getProgress() + startYear); + mChart.getXAxis().setAxisMinimum(startYear); + mChart.getXAxis().setAxisMaximum(mChart.getBarData().getGroupWidth(groupSpace, barSpace) * mSeekBarX.getProgress() + startYear); mChart.groupBars(startYear, groupSpace, barSpace); mChart.invalidate(); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index a61c357b13..31f9fa1e56 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -76,8 +76,8 @@ protected void onCreate(Bundle savedInstanceState) { YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(mTfLight); leftAxis.setLabelCount(6, false); - leftAxis.setAxisMinValue(-2.5f); - leftAxis.setAxisMaxValue(2.5f); + leftAxis.setAxisMinimum(-2.5f); + leftAxis.setAxisMaximum(2.5f); leftAxis.setGranularityEnabled(true); leftAxis.setGranularity(0.1f); @@ -85,8 +85,8 @@ protected void onCreate(Bundle savedInstanceState) { rightAxis.setDrawGridLines(false); rightAxis.setTypeface(mTfLight); rightAxis.setLabelCount(6, false); - rightAxis.setAxisMinValue(-2.5f); - rightAxis.setAxisMaxValue(2.5f); + rightAxis.setAxisMinimum(-2.5f); + rightAxis.setAxisMaximum(2.5f); rightAxis.setGranularity(0.1f); mSeekBarX.setOnSeekBarChangeListener(this); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 424873e72a..e9b7fc7a50 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -61,8 +61,8 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setDrawAxisLine(false); xAxis.setTextColor(Color.LTGRAY); xAxis.setTextSize(13f); - xAxis.setAxisMinValue(0f); - xAxis.setAxisMaxValue(5f); + xAxis.setAxisMinimum(0f); + xAxis.setAxisMaximum(5f); xAxis.setLabelCount(5); xAxis.setCenterAxisLabels(true); xAxis.setGranularity(1f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 599143e38b..224b1f1fe6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -66,15 +66,15 @@ protected void onCreate(Bundle savedInstanceState) { YAxis rightAxis = mChart.getAxisRight(); rightAxis.setDrawGridLines(false); - rightAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setDrawGridLines(false); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTH_SIDED); - xAxis.setAxisMinValue(0f); + xAxis.setAxisMinimum(0f); xAxis.setGranularity(1f); xAxis.setValueFormatter(new IAxisValueFormatter() { @Override @@ -97,7 +97,7 @@ public int getDecimalDigits() { data.setData(generateCandleData()); data.setValueTypeface(mTfLight); - xAxis.setAxisMaxValue(data.getXMax() + 0.25f); + xAxis.setAxisMaximum(data.getXMax() + 0.25f); mChart.setData(data); mChart.invalidate(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java index a9d7210d62..dbc44a34f8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java @@ -51,8 +51,8 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setEnabled(false); YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setAxisMaxValue(900f); - leftAxis.setAxisMinValue(-250f); + leftAxis.setAxisMaximum(900f); + leftAxis.setAxisMinimum(-250f); leftAxis.setDrawAxisLine(false); leftAxis.setDrawZeroLine(false); leftAxis.setDrawGridLines(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index bab2e085a0..316ef4ecac 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -2,8 +2,6 @@ package com.xxmassdeveloper.mpchartexample; import android.annotation.SuppressLint; -import android.graphics.PointF; -import android.graphics.Rect; import android.graphics.RectF; import android.os.Bundle; import android.util.Log; @@ -87,14 +85,14 @@ protected void onCreate(Bundle savedInstanceState) { yl.setTypeface(mTfLight); yl.setDrawAxisLine(true); yl.setDrawGridLines(true); - yl.setAxisMinValue(0f); // this replaces setStartAtZero(true) + yl.setAxisMinimum(0f); // this replaces setStartAtZero(true) // yl.setInverted(true); YAxis yr = mChart.getAxisRight(); yr.setTypeface(mTfLight); yr.setDrawAxisLine(true); yr.setDrawGridLines(false); - yr.setAxisMinValue(0f); // this replaces setStartAtZero(true) + yr.setAxisMinimum(0f); // this replaces setStartAtZero(true) // yr.setInverted(true); setData(12, 50); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index dc41160eb2..9360db623f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -84,11 +84,11 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xl = mChart.getXAxis(); xl.setAvoidFirstLastClipping(true); - xl.setAxisMinValue(0f); + xl.setAxisMinimum(0f); YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setInverted(true); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) YAxis rightAxis = mChart.getAxisRight(); rightAxis.setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 2de91ee64f..44fcfc036f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -128,8 +128,8 @@ protected void onCreate(Bundle savedInstanceState) { leftAxis.removeAllLimitLines(); // reset all limit lines to avoid overlapping lines leftAxis.addLimitLine(ll1); leftAxis.addLimitLine(ll2); - leftAxis.setAxisMaxValue(200f); - leftAxis.setAxisMinValue(-50f); + leftAxis.setAxisMaximum(200f); + leftAxis.setAxisMinimum(-50f); //leftAxis.setYOffset(20f); leftAxis.enableGridDashedLine(10f, 10f, 0f); leftAxis.setDrawZeroLine(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 81097debea..e46d582e6a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -107,16 +107,16 @@ protected void onCreate(Bundle savedInstanceState) { YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(mTfLight); leftAxis.setTextColor(ColorTemplate.getHoloBlue()); - leftAxis.setAxisMaxValue(200f); - leftAxis.setAxisMinValue(0f); + leftAxis.setAxisMaximum(200f); + leftAxis.setAxisMinimum(0f); leftAxis.setDrawGridLines(true); leftAxis.setGranularityEnabled(true); YAxis rightAxis = mChart.getAxisRight(); rightAxis.setTypeface(mTfLight); rightAxis.setTextColor(Color.RED); - rightAxis.setAxisMaxValue(900); - rightAxis.setAxisMinValue(-200); + rightAxis.setAxisMaximum(900); + rightAxis.setAxisMinimum(-200); rightAxis.setDrawGridLines(false); rightAxis.setDrawZeroLine(false); rightAxis.setGranularityEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index afe8fa2ef0..9380835ed8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -110,8 +110,8 @@ public int getDecimalDigits() { leftAxis.setTextColor(ColorTemplate.getHoloBlue()); leftAxis.setDrawGridLines(true); leftAxis.setGranularityEnabled(true); - leftAxis.setAxisMinValue(0f); - leftAxis.setAxisMaxValue(170f); + leftAxis.setAxisMinimum(0f); + leftAxis.setAxisMaximum(170f); leftAxis.setYOffset(-9f); leftAxis.setTextColor(Color.rgb(255, 192, 56)); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index fcc1b2f55c..51bd19dfad 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -93,8 +93,8 @@ public int getDecimalDigits() { yAxis.setTypeface(mTfLight); yAxis.setLabelCount(5, false); yAxis.setTextSize(9f); - yAxis.setAxisMinValue(0f); - yAxis.setAxisMaxValue(80f); + yAxis.setAxisMinimum(0f); + yAxis.setAxisMaximum(80f); yAxis.setDrawLabels(false); Legend l = mChart.getLegend(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 75675d1ac1..97410b0ca8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -2,7 +2,6 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; -import android.graphics.Typeface; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -83,8 +82,8 @@ protected void onCreate(Bundle savedInstanceState) { YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(mTfLight); leftAxis.setTextColor(Color.WHITE); - leftAxis.setAxisMaxValue(100f); - leftAxis.setAxisMinValue(0f); + leftAxis.setAxisMaximum(100f); + leftAxis.setAxisMinimum(0f); leftAxis.setDrawGridLines(true); YAxis rightAxis = mChart.getAxisRight(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index fbcea85d2f..c6b457eb1a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -1,7 +1,6 @@ package com.xxmassdeveloper.mpchartexample; -import android.graphics.Typeface; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -78,7 +77,7 @@ protected void onCreate(Bundle savedInstanceState) { YAxis yl = mChart.getAxisLeft(); yl.setTypeface(mTfLight); - yl.setAxisMinValue(0f); // this replaces setStartAtZero(true) + yl.setAxisMinimum(0f); // this replaces setStartAtZero(true) mChart.getAxisRight().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 5c562190bc..ff4fde35d0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -74,7 +74,7 @@ protected void onCreate(Bundle savedInstanceState) { // change the position of the y-labels YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setValueFormatter(new MyAxisValueFormatter()); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) mChart.getAxisRight().setEnabled(false); XAxis xLabels = mChart.getXAxis(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 1569f574ee..af04d3ae26 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -59,8 +59,8 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setHighlightFullBarEnabled(false); mChart.getAxisLeft().setEnabled(false); - mChart.getAxisRight().setAxisMaxValue(25f); - mChart.getAxisRight().setAxisMinValue(-25f); + mChart.getAxisRight().setAxisMaximum(25f); + mChart.getAxisRight().setAxisMinimum(-25f); mChart.getAxisRight().setDrawGridLines(false); mChart.getAxisRight().setDrawZeroLine(true); mChart.getAxisRight().setLabelCount(7, false); @@ -72,8 +72,8 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setDrawGridLines(false); xAxis.setDrawAxisLine(false); xAxis.setTextSize(9f); - xAxis.setAxisMinValue(0f); - xAxis.setAxisMaxValue(110f); + xAxis.setAxisMinimum(0f); + xAxis.setAxisMaximum(110f); xAxis.setCenterAxisLabels(true); xAxis.setLabelCount(12); xAxis.setGranularity(10f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java index b77c0b815f..286f0d58e4 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java @@ -52,7 +52,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(tf); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) mChart.getAxisRight().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java index 418a856f5f..648a0f01d0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java @@ -41,8 +41,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(tf); - leftAxis.setAxisMaxValue(1.2f); - leftAxis.setAxisMinValue(-1.2f); + leftAxis.setAxisMaximum(1.2f); + leftAxis.setAxisMinimum(-1.2f); mChart.getAxisRight().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java index 008c849786..ab3c99b7c8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java @@ -62,13 +62,13 @@ public View getView(int position, View convertView, Context c) { leftAxis.setTypeface(mTf); leftAxis.setLabelCount(5, false); leftAxis.setSpaceTop(20f); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) YAxis rightAxis = holder.chart.getAxisRight(); rightAxis.setTypeface(mTf); rightAxis.setLabelCount(5, false); rightAxis.setSpaceTop(20f); - rightAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) mChartData.setValueTypeface(mTf); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java index e43b64d1bd..c1796258f4 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java @@ -62,13 +62,13 @@ public View getView(int position, View convertView, Context c) { YAxis leftAxis = holder.chart.getAxisLeft(); leftAxis.setTypeface(mTf); leftAxis.setLabelCount(5, false); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) YAxis rightAxis = holder.chart.getAxisRight(); rightAxis.setTypeface(mTf); rightAxis.setLabelCount(5, false); rightAxis.setDrawGridLines(false); - rightAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) // set data holder.chart.setData((LineData) mChartData); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java index 32d1234fe2..5fcfa76bff 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java @@ -34,7 +34,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (HorizontalBarChart) findViewById(R.id.chart1); setup(mChart); - mChart.getAxisLeft().setAxisMinValue(0f); + mChart.getAxisLeft().setAxisMinimum(0f); mChart.setDrawValueAboveBar(false); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java index 6d2396c22b..13513fd50e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java @@ -33,8 +33,8 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (LineChart) findViewById(R.id.chart1); setup(mChart); - mChart.getAxisLeft().setAxisMaxValue(150f); - mChart.getAxisLeft().setAxisMinValue(0f); + mChart.getAxisLeft().setAxisMaximum(150f); + mChart.getAxisLeft().setAxisMinimum(0f); mChart.getAxisLeft().setDrawGridLines(false); mChart.getXAxis().setDrawGridLines(false); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index bc729f1148..a579549f37 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -5,8 +5,8 @@ import android.graphics.DashPathEffect; import android.util.Log; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.formatter.DefaultAxisValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; @@ -542,7 +542,7 @@ public float getAxisMinimum() { * and the calculation is * done automatically. */ - public void resetAxisMaxValue() { + public void resetAxisMaximum() { mCustomAxisMax = false; } @@ -560,7 +560,7 @@ public boolean isAxisMaxCustom() { * and the calculation is * done automatically. */ - public void resetAxisMinValue() { + public void resetAxisMinimum() { mCustomAxisMin = false; } @@ -582,12 +582,22 @@ public boolean isAxisMinCustom() { * * @param min */ - public void setAxisMinValue(float min) { + public void setAxisMinimum(float min) { mCustomAxisMin = true; mAxisMinimum = min; this.mAxisRange = Math.abs(mAxisMaximum - min); } + /** + * Use setAxisMinimum(...) instead. + * + * @param min + */ + @Deprecated + public void setAxisMinValue(float min) { + setAxisMinimum(min); + } + /** * Set a custom maximum value for this axis. If set, this value will not be calculated * automatically depending on @@ -595,12 +605,22 @@ public void setAxisMinValue(float min) { * * @param max */ - public void setAxisMaxValue(float max) { + public void setAxisMaximum(float max) { mCustomAxisMax = true; mAxisMaximum = max; this.mAxisRange = Math.abs(max - mAxisMinimum); } + /** + * Use setAxisMaximum(...) instead. + * + * @param max + */ + @Deprecated + public void setAxisMaxValue(float max) { + setAxisMaximum(max); + } + /** * Calculates the minimum / maximum and range values of the axis with the given * minimum and maximum values from the chart data. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java index 21dc5f7367..82d77c4c8f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java @@ -201,16 +201,16 @@ public boolean isInverted() { /** * This method is deprecated. - * Use setAxisMinValue(...) / setAxisMaxValue(...) instead. + * Use setAxisMinimum(...) / setAxisMaximum(...) instead. * * @param startAtZero */ @Deprecated public void setStartAtZero(boolean startAtZero) { if (startAtZero) - setAxisMinValue(0f); + setAxisMinimum(0f); else - resetAxisMinValue(); + resetAxisMinimum(); } /** From 077365c9af2004cbe99116a7183735ae206b6de9 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 11 Aug 2016 18:25:29 +0200 Subject: [PATCH 036/291] Add barchart wiki image --- screenshots/normal_barchart_wiki.png | Bin 0 -> 40671 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 screenshots/normal_barchart_wiki.png diff --git a/screenshots/normal_barchart_wiki.png b/screenshots/normal_barchart_wiki.png new file mode 100644 index 0000000000000000000000000000000000000000..aa5676f9d3058b393c27a23c919f2658d0d1d048 GIT binary patch literal 40671 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 Date: Thu, 11 Aug 2016 18:28:34 +0200 Subject: [PATCH 037/291] Update image --- screenshots/normal_barchart_wiki.png | Bin 40671 -> 36513 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/screenshots/normal_barchart_wiki.png b/screenshots/normal_barchart_wiki.png index aa5676f9d3058b393c27a23c919f2658d0d1d048..93479eb37d4d810e1a6dbd986b991311a19c6907 100644 GIT binary patch literal 36513 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³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 literal 40671 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 Date: Fri, 12 Aug 2016 20:50:57 +0200 Subject: [PATCH 038/291] Fix crash related to circle size (#2115) --- .../mikephil/charting/data/LineDataSet.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java index e987707cef..5eced95e43 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java @@ -4,6 +4,7 @@ import android.content.Context; import android.graphics.Color; import android.graphics.DashPathEffect; +import android.util.Log; import com.github.mikephil.charting.formatter.DefaultFillFormatter; import com.github.mikephil.charting.formatter.IFillFormatter; @@ -146,13 +147,18 @@ public float getCubicIntensity() { /** - * sets the radius of the drawn circles. - * Default radius = 4f + * Sets the radius of the drawn circles. + * Default radius = 4f, Min = 1f * * @param radius */ public void setCircleRadius(float radius) { - mCircleRadius = Utils.convertDpToPixel(radius); + + if (radius >= 1f) { + mCircleRadius = Utils.convertDpToPixel(radius); + } else { + Log.e("LineDataSet", "Circle radius cannot be < 1"); + } } @Override @@ -161,13 +167,18 @@ public float getCircleRadius() { } /** - * sets the hole radius of the drawn circles. - * Default radius = 2f + * Sets the hole radius of the drawn circles. + * Default radius = 2f, Min = 0.5f * * @param holeRadius */ public void setCircleHoleRadius(float holeRadius) { - mCircleHoleRadius = Utils.convertDpToPixel(holeRadius); + + if (holeRadius >= 0.5f) { + mCircleHoleRadius = Utils.convertDpToPixel(holeRadius); + } else { + Log.e("LineDataSet", "Circle radius cannot be < 0.5"); + } } @Override From 0f04e9c6f5e19f3003c5114383f480345067a905 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 12 Aug 2016 20:55:59 +0200 Subject: [PATCH 039/291] Documentation update --- .../github/mikephil/charting/components/IMarker.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java index d8ee7452ab..c15dae3a0c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java @@ -1,27 +1,22 @@ package com.github.mikephil.charting.components; -import android.content.Context; import android.graphics.Canvas; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.RelativeLayout; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.utils.FSize; import com.github.mikephil.charting.utils.MPPointF; public interface IMarker { /** - * @return The desired offset you wish the IMarker to have on the x-axis. + * @return The desired (general) offset you wish the IMarker to have on the x- and y-axis. * By returning x: -(width / 2) you will center the IMarker horizontally. * By returning y: -(height / 2) you will center the IMarker vertically. */ MPPointF getOffset(); /** - * @return The offset for drawing at the specific `point` + * @return The offset for drawing at the specific `point`. This allows conditional adjusting of the Marker position. * If you have no adjustments to make, return getOffset(). * * @param posX This is the X position at which the marker wants to be drawn. @@ -36,7 +31,7 @@ public interface IMarker { * * @param e The Entry the IMarker belongs to. This can also be any subclass of Entry, like BarEntry or * CandleEntry, simply cast it at runtime. - * @param highlight the highlight object contains information about the highlighted value such as it's dataset-index, the + * @param highlight The highlight object contains information about the highlighted value such as it's dataset-index, the * selected range or stack-index (only stacked bar entries). */ void refreshContent(Entry e, Highlight highlight); From 7462c083f9c71c908589ec358543cb9fbe58e0a1 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 12 Aug 2016 21:02:25 +0200 Subject: [PATCH 040/291] Fix #2119 --- .../com/github/mikephil/charting/renderer/PieChartRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 36d18e055f..1e94b5540c 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 @@ -265,7 +265,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { float arcStartPointX = 0.f, arcStartPointY = 0.f; - if (sweepAngleOuter % 360f == 0.f) { + if (sweepAngleOuter % 360f < 0.00001f) { // Android is doing "mod 360" mPathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW); } else { From d912964dd31d92f88327c6772655184aa66a7c0f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 13 Aug 2016 15:27:40 +0200 Subject: [PATCH 041/291] Cleanup --- .../mikephil/charting/renderer/LineChartRenderer.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index bf55c32cf0..516b3b7391 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -65,7 +65,8 @@ public LineChartRenderer(LineDataProvider chart, ChartAnimator animator, } @Override - public void initBuffers() { } + public void initBuffers() { + } @Override public void drawData(Canvas c) { @@ -231,7 +232,7 @@ protected void drawCubicBezier(ILineDataSet dataSet) { cubicFillPath.reset(); cubicFillPath.addPath(cubicPath); - // create a new path, this is bad for performance + drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, mXBounds); } @@ -440,7 +441,6 @@ protected void drawLinearFill(Canvas c, ILineDataSet dataSet, Transformer trans, if (currentStartIndex <= currentEndIndex) { generateFilledPath(dataSet, currentStartIndex, currentEndIndex, filled); - trans.pathValueToPixel(filled); final Drawable drawable = dataSet.getFillDrawable(); @@ -504,7 +504,6 @@ private void generateFilledPath(final ILineDataSet dataSet, final int startIndex } filled.close(); - } @Override From d342760357eec29154c4f0ca6917398a79036621 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 13 Aug 2016 15:38:40 +0200 Subject: [PATCH 042/291] Fix issue #2102 --- .../mpchartexample/PiePolylineChartActivity.java | 2 +- .../charting/renderer/PieChartRenderer.java | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index 6db7f80e29..d84301632f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -223,7 +223,7 @@ private void setData(int count, float range) { dataSet.setValueLinePart1OffsetPercentage(80.f); dataSet.setValueLinePart1Length(0.2f); dataSet.setValueLinePart2Length(0.4f); - // dataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); + //dataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); dataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); PieData data = new PieData(dataSet); 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 1e94b5540c..9535cc1fb9 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 @@ -414,6 +414,8 @@ public void drawValues(Canvas c) { c.save(); + float offset = Utils.convertDpToPixel(5.f); + for (int i = 0; i < dataSets.size(); i++) { IPieDataSet dataSet = dataSets.get(i); @@ -439,8 +441,6 @@ public void drawValues(Canvas c) { mValueLinePaint.setColor(dataSet.getValueLineColor()); mValueLinePaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getValueLineWidth())); - float offset = Utils.convertDpToPixel(5.f); - final float sliceSpace = getSliceSpace(dataSet); for (int j = 0; j < entryCount; j++) { @@ -509,13 +509,22 @@ public void drawValues(Canvas c) { if (transformedAngle % 360.0 >= 90.0 && transformedAngle % 360.0 <= 270.0) { pt2x = pt1x - polyline2Width; pt2y = pt1y; + mValuePaint.setTextAlign(Align.RIGHT); + + if(drawXOutside) + mEntryLabelsPaint.setTextAlign(Align.RIGHT); + labelPtx = pt2x - offset; labelPty = pt2y; } else { pt2x = pt1x + polyline2Width; pt2y = pt1y; mValuePaint.setTextAlign(Align.LEFT); + + if(drawXOutside) + mEntryLabelsPaint.setTextAlign(Align.LEFT); + labelPtx = pt2x + offset; labelPty = pt2y; } From 3c8bf8cb94a796406e3dc17340f430009b3ea246 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 14 Aug 2016 08:54:20 +0300 Subject: [PATCH 043/291] Use an actual Epsilon here --- .../mikephil/charting/renderer/PieChartRenderer.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java index 9535cc1fb9..a37fb0c390 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 @@ -229,7 +229,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { int visibleAngleCount = 0; for (int j = 0; j < entryCount; j++) { // draw only if the value is greater than zero - if ((Math.abs(dataSet.getEntryForIndex(j).getY()) > 0.000001)) { + if ((Math.abs(dataSet.getEntryForIndex(j).getY()) > Utils.FLOAT_EPSILON)) { visibleAngleCount++; } } @@ -244,7 +244,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { Entry e = dataSet.getEntryForIndex(j); // draw only if the value is greater than zero - if ((Math.abs(e.getY()) > 0.000001)) { + if ((Math.abs(e.getY()) > Utils.FLOAT_EPSILON)) { if (!mChart.needsHighlight(j)) { @@ -265,7 +265,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { float arcStartPointX = 0.f, arcStartPointY = 0.f; - if (sweepAngleOuter % 360f < 0.00001f) { + if (sweepAngleOuter % 360f < Utils.FLOAT_EPSILON) { // Android is doing "mod 360" mPathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW); } else { @@ -771,7 +771,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { int visibleAngleCount = 0; for (int j = 0; j < entryCount; j++) { // draw only if the value is greater than zero - if ((Math.abs(set.getEntryForIndex(j).getY()) > 0.000001)) { + if ((Math.abs(set.getEntryForIndex(j).getY()) > Utils.FLOAT_EPSILON)) { visibleAngleCount++; } } @@ -958,7 +958,7 @@ protected void drawRoundedSlices(Canvas c) { Entry e = dataSet.getEntryForIndex(j); // draw only if the value is greater than zero - if ((Math.abs(e.getY()) > 0.000001)) { + if ((Math.abs(e.getY()) > Utils.FLOAT_EPSILON)) { float x = (float) ((r - circleRadius) * Math.cos(Math.toRadians((angle + sliceAngle) From bab5b9dd6cb93c86ffb620252170448141372ca9 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 14 Aug 2016 09:37:45 +0300 Subject: [PATCH 044/291] Refined scaleXEnabled/scaleYEnabled conditioning --- .../charting/listener/BarLineChartTouchListener.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java index f1a8241dc4..e041e5ad97 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java @@ -149,10 +149,11 @@ public boolean onTouch(View v, MotionEvent event) { if (mChart.isPinchZoomEnabled()) { mTouchMode = PINCH_ZOOM; } else { - if (mSavedXDist > mSavedYDist) - mTouchMode = X_ZOOM; - else - mTouchMode = Y_ZOOM; + if (mChart.isScaleXEnabled() != mChart.isScaleYEnabled()) { + mTouchMode = mChart.isScaleXEnabled() ? X_ZOOM : Y_ZOOM; + } else { + mTouchMode = mSavedXDist > mSavedYDist ? X_ZOOM : Y_ZOOM; + } } } From be12f155baeb147222dcfe6c37bd8b0bc77dff70 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 14 Aug 2016 10:10:53 +0300 Subject: [PATCH 045/291] We need an "equals or less" here --- .../com/github/mikephil/charting/renderer/PieChartRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a37fb0c390..530e282a26 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 @@ -265,7 +265,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { float arcStartPointX = 0.f, arcStartPointY = 0.f; - if (sweepAngleOuter % 360f < Utils.FLOAT_EPSILON) { + if (sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { // Android is doing "mod 360" mPathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW); } else { From 092e8a80f7514d25ec28919fbe38539b334b4064 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 14 Aug 2016 09:51:47 +0200 Subject: [PATCH 046/291] Changes related to #2119 --- .../com/github/mikephil/charting/renderer/PieChartRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9535cc1fb9..8f1b41a7dd 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 @@ -265,7 +265,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { float arcStartPointX = 0.f, arcStartPointY = 0.f; - if (sweepAngleOuter % 360f < 0.00001f) { + if (sweepAngleOuter % 360f < Utils.FLOAT_EPSILON) { // Android is doing "mod 360" mPathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW); } else { From 7747c345523f355e807451601a7fbb4d4d5910b1 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 14 Aug 2016 11:07:14 +0300 Subject: [PATCH 047/291] Add half line width to clipping rect of grid/limit lines (Closes #2081) https://github.com/danielgindi/Charts/issues/1204 --- .../charting/charts/BarLineChartBase.java | 16 ++++--------- .../charting/renderer/XAxisRenderer.java | 23 +++++++++++++++++++ .../XAxisRendererHorizontalBarChart.java | 15 ++++++++++++ .../charting/renderer/YAxisRenderer.java | 22 ++++++++++++++++++ .../YAxisRendererHorizontalBarChart.java | 15 ++++++++++++ 5 files changed, 80 insertions(+), 11 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index fbc20a44b3..494c8bcedb 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -222,11 +222,6 @@ protected void onDraw(Canvas canvas) { } } - // make sure the graph values and grid cannot be drawn outside the - // content-rect - int clipRestoreCount = canvas.save(); - canvas.clipRect(mViewPortHandler.getContentRect()); - mXAxisRenderer.renderGridLines(canvas); mAxisRendererLeft.renderGridLines(canvas); mAxisRendererRight.renderGridLines(canvas); @@ -240,6 +235,10 @@ protected void onDraw(Canvas canvas) { if (mAxisRight.isDrawLimitLinesBehindDataEnabled()) mAxisRendererRight.renderLimitLines(canvas); + // make sure the data cannot be drawn outside the content-rect + int clipRestoreCount = canvas.save(); + canvas.clipRect(mViewPortHandler.getContentRect()); + mRenderer.drawData(canvas); // if highlighting is enabled @@ -251,9 +250,6 @@ protected void onDraw(Canvas canvas) { mRenderer.drawExtras(canvas); - clipRestoreCount = canvas.save(); - canvas.clipRect(mViewPortHandler.getContentRect()); - if (!mXAxis.isDrawLimitLinesBehindDataEnabled()) mXAxisRenderer.renderLimitLines(canvas); @@ -262,9 +258,7 @@ protected void onDraw(Canvas canvas) { if (!mAxisRight.isDrawLimitLinesBehindDataEnabled()) mAxisRendererRight.renderLimitLines(canvas); - - canvas.restoreToCount(clipRestoreCount); - + mXAxisRenderer.renderAxisLabels(canvas); mAxisRendererLeft.renderAxisLabels(canvas); mAxisRendererRight.renderAxisLabels(canvas); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java index 2a93910d72..43ce279e8e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -6,6 +6,7 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Path; +import android.graphics.RectF; import com.github.mikephil.charting.components.LimitLine; import com.github.mikephil.charting.components.XAxis; @@ -236,6 +237,9 @@ public void renderGridLines(Canvas c) { if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) return; + int clipRestoreCount = c.save(); + c.clipRect(getGridClippingRect()); + if(mRenderGridLinesBuffer.length != mAxis.mEntryCount * 2){ mRenderGridLinesBuffer = new float[mXAxis.mEntryCount * 2]; } @@ -257,6 +261,16 @@ public void renderGridLines(Canvas c) { drawGridLine(c, positions[i], positions[i + 1], gridLinePath); } + + c.restoreToCount(clipRestoreCount); + } + + protected RectF mGridClippingRect = new RectF(); + + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(-mAxis.getGridLineWidth() / 2.f, 0.f); + return mGridClippingRect; } /** @@ -279,6 +293,8 @@ protected void drawGridLine(Canvas c, float x, float y, Path gridLinePath) { } protected float[] mRenderLimitLinesBuffer = new float[2]; + protected RectF mLimitLineClippingRect = new RectF(); + /** * Draws the LimitLines associated with this axis to the screen. * @@ -303,6 +319,11 @@ public void renderLimitLines(Canvas c) { if (!l.isEnabled()) continue; + int clipRestoreCount = c.save(); + mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); + mLimitLineClippingRect.inset(-l.getLineWidth() / 2.f, 0.f); + c.clipRect(mLimitLineClippingRect); + position[0] = l.getLimit(); position[1] = 0.f; @@ -310,6 +331,8 @@ public void renderLimitLines(Canvas c) { renderLimitLineLine(c, l, position); renderLimitLineLabel(c, l, position, 2.f + l.getYOffset()); + + c.restoreToCount(clipRestoreCount); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java index 02192f5a47..48e1ece8b5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java @@ -5,6 +5,7 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Path; +import android.graphics.RectF; import com.github.mikephil.charting.charts.BarChart; import com.github.mikephil.charting.components.LimitLine; @@ -161,6 +162,13 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { } } + @Override + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(0.f, -mAxis.getGridLineWidth() / 2.f); + return mGridClippingRect; + } + @Override protected void drawGridLine(Canvas c, float x, float y, Path gridLinePath) { @@ -228,6 +236,11 @@ public void renderLimitLines(Canvas c) { if(!l.isEnabled()) continue; + int clipRestoreCount = c.save(); + mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); + mLimitLineClippingRect.inset(0.f, -l.getLineWidth() / 2.f); + c.clipRect(mLimitLineClippingRect); + mLimitLinePaint.setStyle(Paint.Style.STROKE); mLimitLinePaint.setColor(l.getLineColor()); mLimitLinePaint.setStrokeWidth(l.getLineWidth()); @@ -290,6 +303,8 @@ public void renderLimitLines(Canvas c) { pts[1] + yOffset, mLimitLinePaint); } } + + c.restoreToCount(clipRestoreCount); } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java index 09e25d1e1e..092714788a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -5,6 +5,7 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Path; +import android.graphics.RectF; import com.github.mikephil.charting.components.LimitLine; import com.github.mikephil.charting.components.YAxis; @@ -134,6 +135,9 @@ public void renderGridLines(Canvas c) { if (mYAxis.isDrawGridLinesEnabled()) { + int clipRestoreCount = c.save(); + c.clipRect(getGridClippingRect()); + float[] positions = getTransformedPositions(); mGridPaint.setColor(mYAxis.getGridColor()); @@ -150,6 +154,8 @@ public void renderGridLines(Canvas c) { c.drawPath(linePath(gridLinePath, i, positions), mGridPaint); gridLinePath.reset(); } + + c.restoreToCount(clipRestoreCount); } if (mYAxis.isDrawZeroLineEnabled()) { @@ -157,6 +163,14 @@ public void renderGridLines(Canvas c) { } } + protected RectF mGridClippingRect = new RectF(); + + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(0.f, -mAxis.getGridLineWidth() / 2.f); + return mGridClippingRect; + } + /** * Calculates the path for a grid line. * @@ -220,6 +234,7 @@ protected void drawZeroLine(Canvas c) { protected Path mRenderLimitLines = new Path(); protected float[] mRenderLimitLinesBuffer = new float[2]; + protected RectF mLimitLineClippingRect = new RectF(); /** * Draws the LimitLines associated with this axis to the screen. * @@ -246,6 +261,11 @@ public void renderLimitLines(Canvas c) { if (!l.isEnabled()) continue; + int clipRestoreCount = c.save(); + mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); + mLimitLineClippingRect.inset(0.f, -l.getLineWidth() / 2.f); + c.clipRect(mLimitLineClippingRect); + mLimitLinePaint.setStyle(Paint.Style.STROKE); mLimitLinePaint.setColor(l.getLineColor()); mLimitLinePaint.setStrokeWidth(l.getLineWidth()); @@ -309,6 +329,8 @@ public void renderLimitLines(Canvas c) { pts[1] + yOffset, mLimitLinePaint); } } + + c.restoreToCount(clipRestoreCount); } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java index 6cf7985ea2..f867cf5f3a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java @@ -5,6 +5,7 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Path; +import android.graphics.RectF; import com.github.mikephil.charting.components.LimitLine; import com.github.mikephil.charting.components.YAxis; @@ -164,6 +165,13 @@ protected float[] getTransformedPositions() { return positions; } + @Override + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(-mAxis.getGridLineWidth() / 2.f, 0.f); + return mGridClippingRect; + } + @Override protected Path linePath(Path p, int i, float[] positions) { @@ -225,6 +233,11 @@ public void renderLimitLines(Canvas c) { if (!l.isEnabled()) continue; + int clipRestoreCount = c.save(); + mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); + mLimitLineClippingRect.inset(-l.getLineWidth() / 2.f, 0.f); + c.clipRect(mLimitLineClippingRect); + pts[0] = l.getLimit(); pts[2] = l.getLimit(); @@ -281,6 +294,8 @@ public void renderLimitLines(Canvas c) { c.drawText(label, pts[0] - xOffset, mViewPortHandler.contentBottom() - yOffset, mLimitLinePaint); } } + + c.restoreToCount(clipRestoreCount); } } } From ea6b0e8e1c3f2567ad5724807a31493b7b88e629 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 14 Aug 2016 16:09:06 +0300 Subject: [PATCH 048/291] Simplified legend entries configuration. Option to style dataset form... --- .../mikephil/charting/components/Legend.java | 428 ++++++++++-------- .../charting/components/LegendEntry.java | 66 +++ .../mikephil/charting/data/BaseDataSet.java | 32 ++ .../interfaces/datasets/IDataSet.java | 22 + .../charting/renderer/LegendRenderer.java | 180 +++++--- 5 files changed, 484 insertions(+), 244 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java index b12a39b394..8e521f5adc 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java @@ -3,6 +3,7 @@ import android.graphics.Paint; +import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.FSize; import com.github.mikephil.charting.utils.Utils; @@ -33,7 +34,35 @@ public enum LegendPosition { } public enum LegendForm { - SQUARE, CIRCLE, LINE + /** + * Avoid drawing a form + */ + NONE, + + /** + * Do not draw the a form, but leave space for it + */ + EMPTY, + + /** + * Use default (default dataset's form to the legend's form) + */ + DEFAULT, + + /** + * Draw a square + */ + SQUARE, + + /** + * Draw a circle + */ + CIRCLE, + + /** + * Draw a horizontal line + */ + LINE } public enum LegendHorizontalAlignment { @@ -53,27 +82,15 @@ public enum LegendDirection { } /** - * the legend colors array, each color is for the form drawn at the same - * index - */ - private int[] mColors; - - /** - * the legend text array. a null label will start a group. - */ - private String[] mLabels; - - /** - * colors that will be appended to the end of the colors array after - * calculating the legend. + * The legend entries array */ - private int[] mExtraColors; + private LegendEntry[] mEntries = new LegendEntry[]{}; /** - * labels that will be appended to the end of the labels array after - * calculating the legend. a null label will start a group. + * Entries that will be appended to the end of the auto calculated entries after calculating the legend. + * (if the legend has already been calculated, you will need to call notifyDataSetChanged() to let the changes take effect) */ - private String[] mExtraLabels; + private LegendEntry[] mExtraEntries; /** * Are the legend labels/colors a custom value or auto calculated? If false, @@ -101,6 +118,11 @@ public enum LegendDirection { */ private float mFormSize = 8f; + /** + * the size of the legend forms/shapes + */ + private float mFormLineWidth = 3f; + /** * the space between the legend entries on a horizontal axis, default 6f */ @@ -134,6 +156,7 @@ public enum LegendDirection { public Legend() { mFormSize = Utils.convertDpToPixel(8f); + mFormLineWidth = Utils.convertDpToPixel(3f); mXEntrySpace = Utils.convertDpToPixel(6f); mYEntrySpace = Utils.convertDpToPixel(0f); mFormToTextSpace = Utils.convertDpToPixel(5f); @@ -144,11 +167,21 @@ public Legend() { } /** - * Constructor. Provide colors and labels for the legend. + * Constructor. Provide entries for the legend. * - * @param colors - * @param labels + * @param entries */ + public Legend(LegendEntry[] entries) { + this(); + + if (entries == null) { + throw new IllegalArgumentException("entries array is NULL"); + } + + this.mEntries = entries; + } + + @Deprecated public Legend(int[] colors, String[] labels) { this(); @@ -161,56 +194,41 @@ public Legend(int[] colors, String[] labels) { "colors array and labels array need to be of same size"); } - this.mColors = colors; - this.mLabels = labels; - } + List entries = new ArrayList<>(); - /** - * Constructor. Provide colors and labels for the legend. - * - * @param colors - * @param labels - */ - public Legend(List colors, List labels) { - this(); + for (int i = 0; i < Math.min(colors.length, labels.length); i++) { + final LegendEntry entry = new LegendEntry(); + entry.formColor = colors[i]; + entry.label = labels[i]; - if (colors == null || labels == null) { - throw new IllegalArgumentException("colors array or labels array is NULL"); - } + if (entry.formColor == ColorTemplate.COLOR_SKIP) + entry.form = LegendForm.NONE; + else if (entry.formColor == ColorTemplate.COLOR_NONE || + entry.formColor == 0) + entry.form = LegendForm.EMPTY; - if (colors.size() != labels.size()) { - throw new IllegalArgumentException( - "colors array and labels array need to be of same size"); + entries.add(entry); } - this.mColors = Utils.convertIntegers(colors); - this.mLabels = Utils.convertStrings(labels); + mEntries = entries.toArray(new LegendEntry[entries.size()]); + } + + @Deprecated + public Legend(List colors, List labels) { + this(Utils.convertIntegers(colors), Utils.convertStrings(labels)); } /** * This method sets the automatically computed colors for the legend. Use setCustom(...) to set custom colors. * - * @param colors + * @param entries */ - public void setComputedColors(List colors) { - if (mColors != null && colors.size() == mColors.length) { - Utils.copyIntegers(colors, mColors); - } else { - mColors = Utils.convertIntegers(colors); - } + public void setEntries(List entries) { + mEntries = entries.toArray(new LegendEntry[entries.size()]); } - /** - * This method sets the automatically computed labels for the legend. Use setCustom(...) to set custom labels. - * - * @param labels - */ - public void setComputedLabels(List labels) { - if (mLabels != null && mLabels.length == labels.size()) { - Utils.copyStrings(labels, mLabels); - } else { - mLabels = Utils.convertStrings(labels); - } + public LegendEntry[] getEntries() { + return mEntries; } /** @@ -223,19 +241,24 @@ public void setComputedLabels(List labels) { public float getMaximumEntryWidth(Paint p) { float max = 0f; + float maxFormSize = 0f; - for (int i = 0; i < mLabels.length; i++) { + for (LegendEntry entry : mEntries) { + final float formSize = Float.isNaN(entry.formSize) + ? mFormSize : entry.formSize; + if (formSize > maxFormSize) + maxFormSize = formSize; - if (mLabels[i] != null) { + String label = entry.label; + if (label == null) continue; - float length = (float) Utils.calcTextWidth(p, mLabels[i]); + float length = (float) Utils.calcTextWidth(p, label); - if (length > max) - max = length; - } + if (length > max) + max = length; } - return max + mFormSize + mFormToTextSpace; + return max + maxFormSize + mFormToTextSpace; } /** @@ -248,140 +271,141 @@ public float getMaximumEntryHeight(Paint p) { float max = 0f; - for (int i = 0; i < mLabels.length; i++) { - - if (mLabels[i] != null) { + for (LegendEntry entry : mEntries) { + String label = entry.label; + if (label == null) continue; - float length = (float) Utils.calcTextHeight(p, mLabels[i]); + float length = (float) Utils.calcTextHeight(p, label); - if (length > max) - max = length; - } + if (length > max) + max = length; } return max; } - /** - * returns all the colors the legend uses - * - * @return - */ + @Deprecated public int[] getColors() { - return mColors; + + int[] old = new int[mEntries.length]; + for (int i = 0; i < mEntries.length; i++) { + old[i] = mEntries[i].form == LegendForm.NONE ? ColorTemplate.COLOR_SKIP : + (mEntries[i].form == LegendForm.EMPTY ? ColorTemplate.COLOR_NONE : + mEntries[i].formColor); + } + return old; } - /** - * returns all the labels the legend uses - * - * @return - */ + @Deprecated public String[] getLabels() { - return mLabels; - } - /** - * Returns the legend-label at the given index. - * - * @param index - * @return - */ - public String getLabel(int index) { - return mLabels[index]; + String[] old = new String[mEntries.length]; + for (int i = 0; i < mEntries.length; i++) { + old[i] = mEntries[i].label; + } + return old; } - /** - * colors that will be appended to the end of the colors array after - * calculating the legend. - */ + @Deprecated public int[] getExtraColors() { - return mExtraColors; + + int[] old = new int[mExtraEntries.length]; + for (int i = 0; i < mExtraEntries.length; i++) { + old[i] = mExtraEntries[i].form == LegendForm.NONE ? ColorTemplate.COLOR_SKIP : + (mExtraEntries[i].form == LegendForm.EMPTY ? ColorTemplate.COLOR_NONE : + mExtraEntries[i].formColor); + } + return old; } - /** - * labels that will be appended to the end of the labels array after - * calculating the legend. a null label will start a group. - */ + @Deprecated public String[] getExtraLabels() { - return mExtraLabels; - } - /** - * Colors and labels that will be appended to the end of the auto calculated - * colors and labels arrays after calculating the legend. (if the legend has - * already been calculated, you will need to call notifyDataSetChanged() to - * let the changes take effect) - */ - public void setExtra(List colors, List labels) { - if (mExtraColors != null && mExtraColors.length == colors.size()) { - Utils.copyIntegers(colors, mExtraColors); - } else { - this.mExtraColors = Utils.convertIntegers(colors); + String[] old = new String[mExtraEntries.length]; + for (int i = 0; i < mExtraEntries.length; i++) { + old[i] = mExtraEntries[i].label; } + return old; + } - if (mExtraLabels != null && mExtraLabels.length == labels.size()) { - Utils.copyStrings(labels, mExtraLabels); - } else { - this.mExtraLabels = Utils.convertStrings(labels); - } + public LegendEntry[] getExtraEntries() { + + return mExtraEntries; + } + + public void setExtra(List entries) { + mExtraEntries = entries.toArray(new LegendEntry[entries.size()]); + } + + public void setExtra(LegendEntry[] entries) { + if (entries == null) + entries = new LegendEntry[]{}; + mExtraEntries = entries; + } + + @Deprecated + public void setExtra(List colors, List labels) { + setExtra(Utils.convertIntegers(colors), Utils.convertStrings(labels)); } /** - * Colors and labels that will be appended to the end of the auto calculated - * colors and labels arrays after calculating the legend. (if the legend has - * already been calculated, you will need to call notifyDataSetChanged() to - * let the changes take effect) + * Entries that will be appended to the end of the auto calculated + * entries after calculating the legend. + * (if the legend has already been calculated, you will need to call notifyDataSetChanged() + * to let the changes take effect) */ public void setExtra(int[] colors, String[] labels) { - this.mExtraColors = colors; - this.mExtraLabels = labels; + + List entries = new ArrayList<>(); + + for (int i = 0; i < Math.min(colors.length, labels.length); i++) { + final LegendEntry entry = new LegendEntry(); + entry.formColor = colors[i]; + entry.label = labels[i]; + + if (entry.formColor == ColorTemplate.COLOR_SKIP || + entry.formColor == 0) + entry.form = LegendForm.NONE; + else if (entry.formColor == ColorTemplate.COLOR_NONE) + entry.form = LegendForm.EMPTY; + + entries.add(entry); + } + + mExtraEntries = entries.toArray(new LegendEntry[entries.size()]); } /** - * Sets a custom legend's labels and colors arrays. The colors count should - * match the labels count. * Each color is for the form drawn at the same - * index. * A null label will start a group. * A ColorTemplate.COLOR_SKIP - * color will avoid drawing a form This will disable the feature that - * automatically calculates the legend labels and colors from the datasets. + * Sets a custom legend's entries array. + * * A null label will start a group. + * This will disable the feature that automatically calculates the legend + * entries from the datasets. * Call resetCustom() to re-enable automatic calculation (and then - * notifyDataSetChanged() is needed to auto-calculate the legend again) + * notifyDataSetChanged() is needed to auto-calculate the legend again) */ - public void setCustom(int[] colors, String[] labels) { - - if (colors.length != labels.length) { - throw new IllegalArgumentException( - "colors array and labels array need to be of same size"); - } + public void setCustom(LegendEntry[] entries) { - mLabels = labels; - mColors = colors; + mEntries = entries; mIsLegendCustom = true; } /** - * Sets a custom legend's labels and colors arrays. The colors count should - * match the labels count. * Each color is for the form drawn at the same - * index. * A null label will start a group. * A ColorTemplate.COLOR_SKIP - * color will avoid drawing a form This will disable the feature that - * automatically calculates the legend labels and colors from the datasets. + * Sets a custom legend's entries array. + * * A null label will start a group. + * This will disable the feature that automatically calculates the legend + * entries from the datasets. * Call resetCustom() to re-enable automatic calculation (and then - * notifyDataSetChanged() is needed to auto-calculate the legend again) + * notifyDataSetChanged() is needed to auto-calculate the legend again) */ - public void setCustom(List colors, List labels) { - - if (colors.size() != labels.size()) { - throw new IllegalArgumentException( - "colors array and labels array need to be of same size"); - } + public void setCustom(List entries) { - mColors = Utils.convertIntegers(colors); - mLabels = Utils.convertStrings(labels); + mEntries = entries.toArray(new LegendEntry[entries.size()]); mIsLegendCustom = true; } /** - * Calling this will disable the custom legend labels (set by - * setCustom(...)). Instead, the labels will again be calculated + * Calling this will disable the custom legend entries (set by + * setCustom(...)). Instead, the entries will again be calculated * automatically (after notifyDataSetChanged() is called). */ public void resetCustom() { @@ -389,7 +413,7 @@ public void resetCustom() { } /** - * @return true if a custom legend labels and colors has been set default + * @return true if a custom legend entries has been set default * false (automatic legend) */ public boolean isLegendCustom() { @@ -608,8 +632,7 @@ public void setForm(LegendForm shape) { } /** - * sets the size in pixels of the legend forms, this is internally converted - * in dp, default 8f + * sets the size in dp of the legend forms, default 8f * * @param size */ @@ -626,6 +649,24 @@ public float getFormSize() { return mFormSize; } + /** + * sets the line width in dp for forms that consist of lines, default 3f + * + * @param size + */ + public void setFormLineWidth(float size) { + mFormLineWidth = Utils.convertDpToPixel(size); + } + + /** + * returns the line width in dp for drawing forms that consist of lines + * + * @return + */ + public float getFormLineWidth() { + return mFormLineWidth; + } + /** * returns the space between the legend entries on a horizontal axis in * pixels @@ -795,6 +836,15 @@ public List getCalculatedLineSizes() { */ public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandler) { + float defaultFormSize = mFormSize; + float stackSpace = mStackSpace; + float formToTextSpace = mFormToTextSpace; + float xEntrySpace = mXEntrySpace; + float yEntrySpace = mYEntrySpace; + boolean wordWrapEnabled = mWordWrapEnabled; + LegendEntry[] entries = mEntries; + int entryCount = entries.length; + mTextWidthMax = getMaximumEntryWidth(labelpaint); mTextHeightMax = getMaximumEntryHeight(labelpaint); @@ -803,44 +853,46 @@ public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandle float maxWidth = 0f, maxHeight = 0f, width = 0f; float labelLineHeight = Utils.getLineHeight(labelpaint); - final int count = mLabels.length; boolean wasStacked = false; - for (int i = 0; i < count; i++) { + for (int i = 0; i < entryCount; i++) { - boolean drawingForm = mColors[i] != ColorTemplate.COLOR_SKIP; + LegendEntry e = entries[i]; + boolean drawingForm = e.form != LegendForm.NONE; + float formSize = Float.isNaN(e.formSize) ? defaultFormSize : e.formSize; + String label = e.label; if (!wasStacked) width = 0.f; if (drawingForm) { if (wasStacked) - width += mStackSpace; - width += mFormSize; + width += stackSpace; + width += formSize; } // grouped forms have null labels - if (mLabels[i] != null) { + if (label != null) { // make a step to the left if (drawingForm && !wasStacked) - width += mFormToTextSpace; + width += formToTextSpace; else if (wasStacked) { maxWidth = Math.max(maxWidth, width); - maxHeight += labelLineHeight + mYEntrySpace; + maxHeight += labelLineHeight + yEntrySpace; width = 0.f; wasStacked = false; } - width += Utils.calcTextWidth(labelpaint, mLabels[i]); + width += Utils.calcTextWidth(labelpaint, label); - if (i < count - 1) - maxHeight += labelLineHeight + mYEntrySpace; + if (i < entryCount - 1) + maxHeight += labelLineHeight + yEntrySpace; } else { wasStacked = true; - width += mFormSize; - if (i < count - 1) - width += mStackSpace; + width += formSize; + if (i < entryCount - 1) + width += stackSpace; } maxWidth = Math.max(maxWidth, width); @@ -853,9 +905,8 @@ else if (wasStacked) { } case HORIZONTAL: { - int labelCount = mLabels.length; float labelLineHeight = Utils.getLineHeight(labelpaint); - float labelLineSpacing = Utils.getLineSpacing(labelpaint) + mYEntrySpace; + float labelLineSpacing = Utils.getLineSpacing(labelpaint) + yEntrySpace; float contentWidth = viewPortHandler.contentWidth() * mMaxSizePercent; // Start calculating layout @@ -868,9 +919,12 @@ else if (wasStacked) { mCalculatedLabelSizes.clear(); mCalculatedLineSizes.clear(); - for (int i = 0; i < labelCount; i++) { + for (int i = 0; i < entryCount; i++) { - boolean drawingForm = mColors[i] != ColorTemplate.COLOR_SKIP; + LegendEntry e = entries[i]; + boolean drawingForm = e.form != LegendForm.NONE; + float formSize = Float.isNaN(e.formSize) ? defaultFormSize : e.formSize; + String label = e.label; mCalculatedLabelBreakPoints.add(false); @@ -880,19 +934,19 @@ else if (wasStacked) { requiredWidth = 0.f; } else { // add the spacing appropriate for stacked labels/forms - requiredWidth += mStackSpace; + requiredWidth += stackSpace; } // grouped forms have null labels - if (mLabels[i] != null) { + if (label != null) { - mCalculatedLabelSizes.add(Utils.calcTextSize(labelpaint, mLabels[i])); - requiredWidth += drawingForm ? mFormToTextSpace + mFormSize : 0.f; + mCalculatedLabelSizes.add(Utils.calcTextSize(labelpaint, label)); + requiredWidth += drawingForm ? mFormToTextSpace + formSize : 0.f; requiredWidth += mCalculatedLabelSizes.get(i).width; } else { mCalculatedLabelSizes.add(FSize.getInstance(0.f, 0.f)); - requiredWidth += drawingForm ? mFormSize : 0.f; + requiredWidth += drawingForm ? formSize : 0.f; if (stackedStartIndex == -1) { // mark this index as we might want to break here later @@ -900,11 +954,11 @@ else if (wasStacked) { } } - if (mLabels[i] != null || i == labelCount - 1) { + if (label != null || i == entryCount - 1) { - float requiredSpacing = currentLineWidth == 0.f ? 0.f : mXEntrySpace; + float requiredSpacing = currentLineWidth == 0.f ? 0.f : xEntrySpace; - if (!mWordWrapEnabled // No word wrapping, it must fit. + if (!wordWrapEnabled // No word wrapping, it must fit. // The line is empty, it must fit || currentLineWidth == 0.f // It simply fits @@ -925,14 +979,14 @@ else if (wasStacked) { currentLineWidth = requiredWidth; } - if (i == labelCount - 1) { + if (i == entryCount - 1) { // Add last line size to array mCalculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight)); maxLineWidth = Math.max(maxLineWidth, currentLineWidth); } } - stackedStartIndex = mLabels[i] != null ? -1 : stackedStartIndex; + stackedStartIndex = label != null ? -1 : stackedStartIndex; } mNeededWidth = maxLineWidth; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java new file mode 100644 index 0000000000..50300b6d7f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java @@ -0,0 +1,66 @@ +package com.github.mikephil.charting.components; + + +import com.github.mikephil.charting.utils.ColorTemplate; + +public class LegendEntry { + public LegendEntry() { + + } + + /** + * + * @param label The legend entry text. A `null` label will start a group. + * @param form The form to draw for this entry. + * @param formSize Set as NaN to use the legend's default + * @param formLineWidth Set as NaN to use the legend's default + * @param formColor The color for drawing the form + */ + public LegendEntry(String label, + Legend.LegendForm form, + float formSize, + float formLineWidth, + int formColor) + { + this.label = label; + this.form = form; + this.formSize = formSize; + this.formLineWidth = formLineWidth; + this.formColor = formColor; + } + + /** + * The legend entry text. + * A `null` label will start a group. + */ + public String label; + + /** + * The form to draw for this entry. + * + * `NONE` will avoid drawing a form, and any related space. + * `EMPTY` will avoid drawing a form, but keep its space. + * `DEFAULT` will use the Legend's default. + */ + public Legend.LegendForm form = Legend.LegendForm.DEFAULT; + + /** + * Form size will be considered except for when .None is used + * + * Set as NaN to use the legend's default + */ + public float formSize = Float.NaN; + + /** + * Line width used for shapes that consist of lines. + * + * Set as NaN to use the legend's default + */ + public float formLineWidth = Float.NaN; + + /** + * The color for drawing the form + */ + public int formColor = ColorTemplate.COLOR_NONE; + +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java index fe19c80ff9..42212f4423 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -4,6 +4,7 @@ import android.graphics.Color; import android.graphics.Typeface; +import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.formatter.DefaultValueFormatter; import com.github.mikephil.charting.formatter.IValueFormatter; @@ -56,6 +57,10 @@ public abstract class BaseDataSet implements IDataSet { */ protected Typeface mValueTypeface; + private Legend.LegendForm mForm = Legend.LegendForm.DEFAULT; + private float mFormSize = Float.NaN; + private float mFormLineWidth = Float.NaN; + /** * if true, y-values are drawn on the chart */ @@ -318,6 +323,33 @@ public float getValueTextSize() { return mValueTextSize; } + public void setForm(Legend.LegendForm form) { + mForm = form; + } + + @Override + public Legend.LegendForm getForm() { + return mForm; + } + + public void setFormSize(float formSize) { + mFormSize = formSize; + } + + @Override + public float getFormSize() { + return mFormSize; + } + + public void setFormLineWidth(float formLineWidth) { + mFormLineWidth = formLineWidth; + } + + @Override + public float getFormLineWidth() { + return mFormLineWidth; + } + @Override public void setDrawValues(boolean enabled) { this.mDrawValues = enabled; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index f2f2eac203..ad43237730 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -2,6 +2,7 @@ import android.graphics.Typeface; +import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; @@ -368,6 +369,27 @@ public interface IDataSet { */ float getValueTextSize(); + /** + * The form to draw for this dataset in the legend. + * + * Return `DEFAULT` to use the default legend form. + */ + Legend.LegendForm getForm(); + + /** + * The form size to draw for this dataset in the legend. + * + * Return `Float.NaN` to use the default legend form size. + */ + float getFormSize(); + + /** + * The line width for drawing the form of this dataset in the legend + * + * Return `Float.NaN` to use the default legend form line width. + */ + float getFormLineWidth(); + /** * set this to true to draw y-values on the chart NOTE (for bar and * linechart): if "maxvisiblecount" is reached, no values will be drawn even diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java index e4b0b81ffc..14cf91ecd1 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -7,6 +7,7 @@ import android.graphics.Typeface; import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.LegendEntry; import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; @@ -71,8 +72,7 @@ public Paint getFormPaint() { } - protected List computedLabels = new ArrayList<>(16); - protected List computedColors = new ArrayList<>(16); + protected List computedEntries = new ArrayList<>(16); /** * Prepares the legend and calculates all needed forms, labels and colors. @@ -83,8 +83,7 @@ public void computeLegend(ChartData data) { if (!mLegend.isLegendCustom()) { - computedLabels.clear(); - computedColors.clear(); + computedEntries.clear(); // loop for building up the colors and labels used in the legend for (int i = 0; i < data.getDataSetCount(); i++) { @@ -102,14 +101,24 @@ public void computeLegend(ChartData data) { for (int j = 0; j < clrs.size() && j < bds.getStackSize(); j++) { - computedLabels.add(sLabels[j % sLabels.length]); - computedColors.add(clrs.get(j)); + computedEntries.add(new LegendEntry( + sLabels[j % sLabels.length], + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + clrs.get(j) + )); } if (bds.getLabel() != null) { // add the legend description label - computedColors.add(ColorTemplate.COLOR_SKIP); - computedLabels.add(bds.getLabel()); + computedEntries.add(new LegendEntry( + dataSet.getLabel(), + Legend.LegendForm.NONE, + Float.NaN, + Float.NaN, + ColorTemplate.COLOR_NONE + )); } } else if (dataSet instanceof IPieDataSet) { @@ -118,55 +127,77 @@ public void computeLegend(ChartData data) { for (int j = 0; j < clrs.size() && j < entryCount; j++) { - computedLabels.add(pds.getEntryForIndex(j).getLabel()); - computedColors.add(clrs.get(j)); + computedEntries.add(new LegendEntry( + pds.getEntryForIndex(j).getLabel(), + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + clrs.get(j) + )); } if (pds.getLabel() != null) { // add the legend description label - computedColors.add(ColorTemplate.COLOR_SKIP); - computedLabels.add(pds.getLabel()); + computedEntries.add(new LegendEntry( + dataSet.getLabel(), + Legend.LegendForm.NONE, + Float.NaN, + Float.NaN, + ColorTemplate.COLOR_NONE + )); } } else if (dataSet instanceof ICandleDataSet && ((ICandleDataSet) dataSet).getDecreasingColor() != ColorTemplate.COLOR_NONE) { int decreasingColor = ((ICandleDataSet) dataSet).getDecreasingColor(); - computedColors.add(decreasingColor); - int increasingColor = ((ICandleDataSet) dataSet).getIncreasingColor(); - computedColors.add(increasingColor); - computedLabels.add(null); - computedLabels.add(dataSet.getLabel()); + computedEntries.add(new LegendEntry( + null, + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + decreasingColor + )); + + computedEntries.add(new LegendEntry( + dataSet.getLabel(), + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + increasingColor + )); } else { // all others for (int j = 0; j < clrs.size() && j < entryCount; j++) { + String label; + // if multiple colors are set for a DataSet, group them if (j < clrs.size() - 1 && j < entryCount - 1) { - - computedLabels.add(null); + label = null; } else { // add label to the last entry - - String label = data.getDataSetByIndex(i).getLabel(); - computedLabels.add(label); + label = data.getDataSetByIndex(i).getLabel(); } - computedColors.add(clrs.get(j)); + computedEntries.add(new LegendEntry( + label, + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + clrs.get(j) + )); } } } - if (mLegend.getExtraColors() != null && mLegend.getExtraLabels() != null) { - for (int color : mLegend.getExtraColors()) - computedColors.add(color); - Collections.addAll(computedLabels, mLegend.getExtraLabels()); + if (mLegend.getExtraEntries() != null) { + Collections.addAll(computedEntries, mLegend.getExtraEntries()); } - mLegend.setComputedColors(computedColors); - mLegend.setComputedLabels(computedLabels); + mLegend.setEntries(computedEntries); } Typeface tf = mLegend.getTypeface(); @@ -200,8 +231,7 @@ public void renderLegend(Canvas c) { float labelLineSpacing = Utils.getLineSpacing(mLegendLabelPaint, legendFontMetrics) + mLegend.getYEntrySpace(); float formYOffset = labelLineHeight - Utils.calcTextHeight(mLegendLabelPaint, "ABC") / 2.f; - String[] labels = mLegend.getLabels(); - int[] colors = mLegend.getColors(); + LegendEntry[] entries = mLegend.getEntries(); float formToTextSpace = mLegend.getFormToTextSpace(); float xEntrySpace = mLegend.getXEntrySpace(); @@ -209,7 +239,7 @@ public void renderLegend(Canvas c) { Legend.LegendHorizontalAlignment horizontalAlignment = mLegend.getHorizontalAlignment(); Legend.LegendVerticalAlignment verticalAlignment = mLegend.getVerticalAlignment(); Legend.LegendDirection direction = mLegend.getDirection(); - float formSize = mLegend.getFormSize(); + float defaultFormSize = mLegend.getFormSize(); // space between the entries float stackSpace = mLegend.getStackSpace(); @@ -292,7 +322,12 @@ public void renderLegend(Canvas c) { int lineIndex = 0; - for (int i = 0, count = labels.length; i < count; i++) { + for (int i = 0, count = entries.length; i < count; i++) { + + LegendEntry e = entries[i]; + boolean drawingForm = e.form != Legend.LegendForm.NONE; + float formSize = Float.isNaN(e.formSize) ? defaultFormSize : e.formSize; + if (i < calculatedLabelBreakPoints.size() && calculatedLabelBreakPoints.get(i)) { posX = originPosX; posY += labelLineHeight + labelLineSpacing; @@ -307,14 +342,13 @@ public void renderLegend(Canvas c) { lineIndex++; } - boolean drawingForm = colors[i] != ColorTemplate.COLOR_SKIP; - boolean isStacked = labels[i] == null; // grouped forms have null labels + boolean isStacked = e.label == null; // grouped forms have null labels if (drawingForm) { if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) posX -= formSize; - drawForm(c, posX, posY + formYOffset, i, mLegend); + drawForm(c, posX, posY + formYOffset, e, mLegend); if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) posX += formSize; @@ -328,7 +362,7 @@ public void renderLegend(Canvas c) { if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) posX -= calculatedLabelSizes.get(i).width; - drawLabel(c, posX, posY + labelLineHeight, labels[i]); + drawLabel(c, posX, posY + labelLineHeight, e.label); if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) posX += calculatedLabelSizes.get(i).width; @@ -369,9 +403,12 @@ public void renderLegend(Canvas c) { break; } - for (int i = 0; i < labels.length; i++) { + for (int i = 0; i < entries.length; i++) { + + LegendEntry e = entries[i]; + boolean drawingForm = e.form != Legend.LegendForm.NONE; + float formSize = Float.isNaN(e.formSize) ? defaultFormSize : e.formSize; - Boolean drawingForm = colors[i] != ColorTemplate.COLOR_SKIP; float posX = originPosX; if (drawingForm) { @@ -380,13 +417,13 @@ public void renderLegend(Canvas c) { else posX -= formSize - stack; - drawForm(c, posX, posY + formYOffset, i, mLegend); + drawForm(c, posX, posY + formYOffset, e, mLegend); if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) posX += formSize; } - if (labels[i] != null) { + if (e.label != null) { if (drawingForm && !wasStacked) posX += direction == Legend.LegendDirection.LEFT_TO_RIGHT ? formToTextSpace @@ -395,13 +432,13 @@ else if (wasStacked) posX = originPosX; if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - posX -= Utils.calcTextWidth(mLegendLabelPaint, labels[i]); + posX -= Utils.calcTextWidth(mLegendLabelPaint, e.label); if (!wasStacked) { - drawLabel(c, posX, posY + labelLineHeight, labels[i]); + drawLabel(c, posX, posY + labelLineHeight, e.label); } else { posY += labelLineHeight + labelLineSpacing; - drawLabel(c, posX, posY + labelLineHeight, labels[i]); + drawLabel(c, posX, posY + labelLineHeight, e.label); } // make a step down @@ -423,30 +460,59 @@ else if (wasStacked) * Draws the Legend-form at the given position with the color at the given * index. * - * @param c canvas to draw with - * @param x position - * @param y position - * @param index the index of the color to use (in the colors array) + * @param c canvas to draw with + * @param x position + * @param y position + * @param entry the entry to render + * @param legend the legend context */ - protected void drawForm(Canvas c, float x, float y, int index, Legend legend) { - - if (legend.getColors()[index] == ColorTemplate.COLOR_SKIP) + protected void drawForm( + Canvas c, + float x, float y, + LegendEntry entry, + Legend legend) { + + if (entry.formColor == ColorTemplate.COLOR_SKIP || + entry.formColor == ColorTemplate.COLOR_NONE || + entry.formColor == 0) return; - mLegendFormPaint.setColor(legend.getColors()[index]); + Legend.LegendForm form = entry.form; + if (form == Legend.LegendForm.DEFAULT) + form = legend.getForm(); - float formsize = legend.getFormSize(); - float half = formsize / 2f; + mLegendFormPaint.setColor(entry.formColor); - switch (legend.getForm()) { + final float formSize = Float.isNaN(entry.formSize) ? legend.getFormSize() : entry.formSize; + final float half = formSize / 2f; + + switch (form) { + case NONE: + // Do nothing + break; + + case EMPTY: + // Do not draw, but keep space for the form + break; + + case DEFAULT: case CIRCLE: c.drawCircle(x + half, y, half, mLegendFormPaint); break; + case SQUARE: - c.drawRect(x, y - half, x + formsize, y + half, mLegendFormPaint); + c.drawRect(x, y - half, x + formSize, y + half, mLegendFormPaint); break; + case LINE: - c.drawLine(x, y, x + formsize, y, mLegendFormPaint); + { + final float formLineWidth = Float.isNaN(entry.formLineWidth) + ? legend.getFormLineWidth() + : entry.formLineWidth; + mLegendFormPaint.setStrokeWidth(formLineWidth); + + c.drawLine(x, y, x + formSize, y, mLegendFormPaint); + } break; } } From 7a10c05e3a2a1f2d7549b1ff42c565e1ce291937 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 14 Aug 2016 16:40:55 +0300 Subject: [PATCH 049/291] Added feature for dashing legend line forms (Closes #1843) --- .../mpchartexample/LineChartActivity1.java | 4 +++ .../mikephil/charting/components/Legend.java | 22 +++++++++++++++ .../charting/components/LegendEntry.java | 18 ++++++++++-- .../mikephil/charting/data/BaseDataSet.java | 11 ++++++++ .../interfaces/datasets/IDataSet.java | 8 ++++++ .../charting/renderer/LegendRenderer.java | 28 +++++++++++++++++-- 6 files changed, 86 insertions(+), 5 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 44fcfc036f..ab2f226efa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -2,6 +2,7 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; +import android.graphics.DashPathEffect; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -368,6 +369,9 @@ private void setData(int count, float range) { set1.setDrawCircleHole(false); set1.setValueTextSize(9f); set1.setDrawFilled(true); + set1.setFormLineWidth(1f); + set1.setFormLineDashEffect(new DashPathEffect(new float[]{10f, 5f}, 0f)); + set1.setFormSize(15.f); if (Utils.getSDKInt() >= 18) { // fill drawable only supported on api level 18 and above diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java index 8e521f5adc..bdcc024f6e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.components; +import android.graphics.DashPathEffect; import android.graphics.Paint; import com.github.mikephil.charting.data.Entry; @@ -123,6 +124,11 @@ public enum LegendDirection { */ private float mFormLineWidth = 3f; + /** + * Line dash path effect used for shapes that consist of lines. + */ + private DashPathEffect mFormLineDashEffect = null; + /** * the space between the legend entries on a horizontal axis, default 6f */ @@ -667,6 +673,22 @@ public float getFormLineWidth() { return mFormLineWidth; } + /** + * Sets the line dash path effect used for shapes that consist of lines. + * + * @param dashPathEffect + */ + public void setFormLineDashEffect(DashPathEffect dashPathEffect) { + mFormLineDashEffect = dashPathEffect; + } + + /** + * @return The line dash path effect used for shapes that consist of lines. + */ + public DashPathEffect getFormLineDashEffect() { + return mFormLineDashEffect; + } + /** * returns the space between the legend entries on a horizontal axis in * pixels diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java index 50300b6d7f..3acec0f461 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java @@ -1,6 +1,8 @@ package com.github.mikephil.charting.components; +import android.graphics.DashPathEffect; + import com.github.mikephil.charting.utils.ColorTemplate; public class LegendEntry { @@ -12,20 +14,23 @@ public LegendEntry() { * * @param label The legend entry text. A `null` label will start a group. * @param form The form to draw for this entry. - * @param formSize Set as NaN to use the legend's default - * @param formLineWidth Set as NaN to use the legend's default - * @param formColor The color for drawing the form + * @param formSize Set to NaN to use the legend's default. + * @param formLineWidth Set to NaN to use the legend's default. + * @param formLineDashEffect Set to nil to use the legend's default. + * @param formColor The color for drawing the form. */ public LegendEntry(String label, Legend.LegendForm form, float formSize, float formLineWidth, + DashPathEffect formLineDashEffect, int formColor) { this.label = label; this.form = form; this.formSize = formSize; this.formLineWidth = formLineWidth; + this.formLineDashEffect = formLineDashEffect; this.formColor = formColor; } @@ -58,6 +63,13 @@ public LegendEntry(String label, */ public float formLineWidth = Float.NaN; + /** + * Line dash path effect used for shapes that consist of lines. + * + * Set to null to use the legend's default + */ + public DashPathEffect formLineDashEffect = null; + /** * The color for drawing the form */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java index 42212f4423..f3107ebed7 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -2,6 +2,7 @@ import android.content.Context; import android.graphics.Color; +import android.graphics.DashPathEffect; import android.graphics.Typeface; import com.github.mikephil.charting.components.Legend; @@ -60,6 +61,7 @@ public abstract class BaseDataSet implements IDataSet { private Legend.LegendForm mForm = Legend.LegendForm.DEFAULT; private float mFormSize = Float.NaN; private float mFormLineWidth = Float.NaN; + private DashPathEffect mFormLineDashEffect = null; /** * if true, y-values are drawn on the chart @@ -350,6 +352,15 @@ public float getFormLineWidth() { return mFormLineWidth; } + public void setFormLineDashEffect(DashPathEffect dashPathEffect) { + mFormLineDashEffect = dashPathEffect; + } + + @Override + public DashPathEffect getFormLineDashEffect() { + return mFormLineDashEffect; + } + @Override public void setDrawValues(boolean enabled) { this.mDrawValues = enabled; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index ad43237730..62c10b3f32 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -1,5 +1,6 @@ package com.github.mikephil.charting.interfaces.datasets; +import android.graphics.DashPathEffect; import android.graphics.Typeface; import com.github.mikephil.charting.components.Legend; @@ -390,6 +391,13 @@ public interface IDataSet { */ float getFormLineWidth(); + /** + * The line dash path effect used for shapes that consist of lines. + * + * Return `null` to use the default legend form line dash effect. + */ + DashPathEffect getFormLineDashEffect(); + /** * set this to true to draw y-values on the chart NOTE (for bar and * linechart): if "maxvisiblecount" is reached, no values will be drawn even diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java index 14cf91ecd1..06dc0b60d6 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -2,8 +2,10 @@ package com.github.mikephil.charting.renderer; import android.graphics.Canvas; +import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.Paint.Align; +import android.graphics.Path; import android.graphics.Typeface; import com.github.mikephil.charting.components.Legend; @@ -50,7 +52,6 @@ public LegendRenderer(ViewPortHandler viewPortHandler, Legend legend) { mLegendFormPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mLegendFormPaint.setStyle(Paint.Style.FILL); - mLegendFormPaint.setStrokeWidth(3f); } /** @@ -106,6 +107,7 @@ public void computeLegend(ChartData data) { dataSet.getForm(), dataSet.getFormSize(), dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), clrs.get(j) )); } @@ -117,6 +119,7 @@ public void computeLegend(ChartData data) { Legend.LegendForm.NONE, Float.NaN, Float.NaN, + null, ColorTemplate.COLOR_NONE )); } @@ -132,6 +135,7 @@ public void computeLegend(ChartData data) { dataSet.getForm(), dataSet.getFormSize(), dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), clrs.get(j) )); } @@ -143,6 +147,7 @@ public void computeLegend(ChartData data) { Legend.LegendForm.NONE, Float.NaN, Float.NaN, + null, ColorTemplate.COLOR_NONE )); } @@ -158,6 +163,7 @@ public void computeLegend(ChartData data) { dataSet.getForm(), dataSet.getFormSize(), dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), decreasingColor )); @@ -166,6 +172,7 @@ public void computeLegend(ChartData data) { dataSet.getForm(), dataSet.getFormSize(), dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), increasingColor )); @@ -187,6 +194,7 @@ public void computeLegend(ChartData data) { dataSet.getForm(), dataSet.getFormSize(), dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), clrs.get(j) )); } @@ -456,6 +464,8 @@ else if (wasStacked) } } + private Path mLineFormPath = new Path(); + /** * Draws the Legend-form at the given position with the color at the given * index. @@ -477,6 +487,8 @@ protected void drawForm( entry.formColor == 0) return; + int restoreCount = c.save(); + Legend.LegendForm form = entry.form; if (form == Legend.LegendForm.DEFAULT) form = legend.getForm(); @@ -497,10 +509,12 @@ protected void drawForm( case DEFAULT: case CIRCLE: + mLegendFormPaint.setStyle(Paint.Style.FILL); c.drawCircle(x + half, y, half, mLegendFormPaint); break; case SQUARE: + mLegendFormPaint.setStyle(Paint.Style.FILL); c.drawRect(x, y - half, x + formSize, y + half, mLegendFormPaint); break; @@ -509,12 +523,22 @@ protected void drawForm( final float formLineWidth = Float.isNaN(entry.formLineWidth) ? legend.getFormLineWidth() : entry.formLineWidth; + final DashPathEffect formLineDashEffect = entry.formLineDashEffect == null + ? legend.getFormLineDashEffect() + : entry.formLineDashEffect; + mLegendFormPaint.setStyle(Paint.Style.STROKE); mLegendFormPaint.setStrokeWidth(formLineWidth); + mLegendFormPaint.setPathEffect(formLineDashEffect); - c.drawLine(x, y, x + formSize, y, mLegendFormPaint); + mLineFormPath.reset(); + mLineFormPath.moveTo(x, y); + mLineFormPath.lineTo(x + formSize, y); + c.drawPath(mLineFormPath, mLegendFormPaint); } break; } + + c.restoreToCount(restoreCount); } /** From 38cdf1b206f025bf8f68edfe067ef768cd6677e2 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 14 Aug 2016 16:50:26 +0300 Subject: [PATCH 050/291] Set those sizes to dps, convert internally. Until now it was set-dps, get-pixels. --- .../mikephil/charting/components/Legend.java | 46 +++++++++---------- .../charting/renderer/LegendRenderer.java | 27 ++++++----- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java index bdcc024f6e..aeba757cc2 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java @@ -161,13 +161,7 @@ public enum LegendDirection { */ public Legend() { - mFormSize = Utils.convertDpToPixel(8f); - mFormLineWidth = Utils.convertDpToPixel(3f); - mXEntrySpace = Utils.convertDpToPixel(6f); - mYEntrySpace = Utils.convertDpToPixel(0f); - mFormToTextSpace = Utils.convertDpToPixel(5f); - mTextSize = Utils.convertDpToPixel(10f); - mStackSpace = Utils.convertDpToPixel(3f); + this.mTextSize = Utils.convertDpToPixel(10f); this.mXOffset = Utils.convertDpToPixel(5f); this.mYOffset = Utils.convertDpToPixel(3f); // 2 } @@ -248,10 +242,12 @@ public float getMaximumEntryWidth(Paint p) { float max = 0f; float maxFormSize = 0f; + float formToTextSpace = Utils.convertDpToPixel(mFormToTextSpace); for (LegendEntry entry : mEntries) { - final float formSize = Float.isNaN(entry.formSize) - ? mFormSize : entry.formSize; + final float formSize = Utils.convertDpToPixel( + Float.isNaN(entry.formSize) + ? mFormSize : entry.formSize); if (formSize > maxFormSize) maxFormSize = formSize; @@ -264,7 +260,7 @@ public float getMaximumEntryWidth(Paint p) { max = length; } - return max + maxFormSize + mFormToTextSpace; + return max + maxFormSize + formToTextSpace; } /** @@ -643,7 +639,7 @@ public void setForm(LegendForm shape) { * @param size */ public void setFormSize(float size) { - mFormSize = Utils.convertDpToPixel(size); + mFormSize = size; } /** @@ -661,7 +657,7 @@ public float getFormSize() { * @param size */ public void setFormLineWidth(float size) { - mFormLineWidth = Utils.convertDpToPixel(size); + mFormLineWidth = size; } /** @@ -706,7 +702,7 @@ public float getXEntrySpace() { * @param space */ public void setXEntrySpace(float space) { - mXEntrySpace = Utils.convertDpToPixel(space); + mXEntrySpace = space; } /** @@ -725,7 +721,7 @@ public float getYEntrySpace() { * @param space */ public void setYEntrySpace(float space) { - mYEntrySpace = Utils.convertDpToPixel(space); + mYEntrySpace = space; } /** @@ -744,7 +740,7 @@ public float getFormToTextSpace() { * @param space */ public void setFormToTextSpace(float space) { - this.mFormToTextSpace = Utils.convertDpToPixel(space); + this.mFormToTextSpace = space; } /** @@ -858,11 +854,11 @@ public List getCalculatedLineSizes() { */ public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandler) { - float defaultFormSize = mFormSize; - float stackSpace = mStackSpace; - float formToTextSpace = mFormToTextSpace; - float xEntrySpace = mXEntrySpace; - float yEntrySpace = mYEntrySpace; + float defaultFormSize = Utils.convertDpToPixel(mFormSize); + float stackSpace = Utils.convertDpToPixel(mStackSpace); + float formToTextSpace = Utils.convertDpToPixel(mFormToTextSpace); + float xEntrySpace = Utils.convertDpToPixel(mXEntrySpace); + float yEntrySpace = Utils.convertDpToPixel(mYEntrySpace); boolean wordWrapEnabled = mWordWrapEnabled; LegendEntry[] entries = mEntries; int entryCount = entries.length; @@ -881,7 +877,9 @@ public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandle LegendEntry e = entries[i]; boolean drawingForm = e.form != LegendForm.NONE; - float formSize = Float.isNaN(e.formSize) ? defaultFormSize : e.formSize; + float formSize = Float.isNaN(e.formSize) + ? defaultFormSize + : Utils.convertDpToPixel(e.formSize); String label = e.label; if (!wasStacked) @@ -945,7 +943,9 @@ else if (wasStacked) { LegendEntry e = entries[i]; boolean drawingForm = e.form != LegendForm.NONE; - float formSize = Float.isNaN(e.formSize) ? defaultFormSize : e.formSize; + float formSize = Float.isNaN(e.formSize) + ? defaultFormSize + : Utils.convertDpToPixel(e.formSize); String label = e.label; mCalculatedLabelBreakPoints.add(false); @@ -963,7 +963,7 @@ else if (wasStacked) { if (label != null) { mCalculatedLabelSizes.add(Utils.calcTextSize(labelpaint, label)); - requiredWidth += drawingForm ? mFormToTextSpace + formSize : 0.f; + requiredWidth += drawingForm ? formToTextSpace + formSize : 0.f; requiredWidth += mCalculatedLabelSizes.get(i).width; } else { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java index 06dc0b60d6..85597db6a1 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -236,21 +236,22 @@ public void renderLegend(Canvas c) { mLegendLabelPaint.setColor(mLegend.getTextColor()); float labelLineHeight = Utils.getLineHeight(mLegendLabelPaint, legendFontMetrics); - float labelLineSpacing = Utils.getLineSpacing(mLegendLabelPaint, legendFontMetrics) + mLegend.getYEntrySpace(); + float labelLineSpacing = Utils.getLineSpacing(mLegendLabelPaint, legendFontMetrics) + + Utils.convertDpToPixel(mLegend.getYEntrySpace()); float formYOffset = labelLineHeight - Utils.calcTextHeight(mLegendLabelPaint, "ABC") / 2.f; LegendEntry[] entries = mLegend.getEntries(); - float formToTextSpace = mLegend.getFormToTextSpace(); - float xEntrySpace = mLegend.getXEntrySpace(); + float formToTextSpace = Utils.convertDpToPixel(mLegend.getFormToTextSpace()); + float xEntrySpace = Utils.convertDpToPixel(mLegend.getXEntrySpace()); Legend.LegendOrientation orientation = mLegend.getOrientation(); Legend.LegendHorizontalAlignment horizontalAlignment = mLegend.getHorizontalAlignment(); Legend.LegendVerticalAlignment verticalAlignment = mLegend.getVerticalAlignment(); Legend.LegendDirection direction = mLegend.getDirection(); - float defaultFormSize = mLegend.getFormSize(); + float defaultFormSize = Utils.convertDpToPixel(mLegend.getFormSize()); // space between the entries - float stackSpace = mLegend.getStackSpace(); + float stackSpace = Utils.convertDpToPixel(mLegend.getStackSpace()); float yoffset = mLegend.getYOffset(); float xoffset = mLegend.getXOffset(); @@ -334,7 +335,7 @@ public void renderLegend(Canvas c) { LegendEntry e = entries[i]; boolean drawingForm = e.form != Legend.LegendForm.NONE; - float formSize = Float.isNaN(e.formSize) ? defaultFormSize : e.formSize; + float formSize = Float.isNaN(e.formSize) ? defaultFormSize : Utils.convertDpToPixel(e.formSize); if (i < calculatedLabelBreakPoints.size() && calculatedLabelBreakPoints.get(i)) { posX = originPosX; @@ -415,7 +416,7 @@ public void renderLegend(Canvas c) { LegendEntry e = entries[i]; boolean drawingForm = e.form != Legend.LegendForm.NONE; - float formSize = Float.isNaN(e.formSize) ? defaultFormSize : e.formSize; + float formSize = Float.isNaN(e.formSize) ? defaultFormSize : Utils.convertDpToPixel(e.formSize); float posX = originPosX; @@ -495,7 +496,10 @@ protected void drawForm( mLegendFormPaint.setColor(entry.formColor); - final float formSize = Float.isNaN(entry.formSize) ? legend.getFormSize() : entry.formSize; + final float formSize = Utils.convertDpToPixel( + Float.isNaN(entry.formSize) + ? legend.getFormSize() + : entry.formSize); final float half = formSize / 2f; switch (form) { @@ -520,9 +524,10 @@ protected void drawForm( case LINE: { - final float formLineWidth = Float.isNaN(entry.formLineWidth) - ? legend.getFormLineWidth() - : entry.formLineWidth; + final float formLineWidth = Utils.convertDpToPixel( + Float.isNaN(entry.formLineWidth) + ? legend.getFormLineWidth() + : entry.formLineWidth); final DashPathEffect formLineDashEffect = entry.formLineDashEffect == null ? legend.getFormLineDashEffect() : entry.formLineDashEffect; From dab8db488f66c6958da541f4596acbcf5e7307b5 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 14 Aug 2016 16:52:09 +0300 Subject: [PATCH 051/291] neededWidth should be enough here. formSize has no meaning globally. If more offset is needed - use extraOffsets, or adjust the maxSizePercent). --- .../com/github/mikephil/charting/charts/PieRadarChartBase.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java index 637be47cab..d403a752cc 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java @@ -115,8 +115,7 @@ public void calculateOffsets() { if (mLegend != null && mLegend.isEnabled() && !mLegend.isDrawInsideEnabled()) { float fullLegendWidth = Math.min(mLegend.mNeededWidth, - mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) + - mLegend.getFormSize() + mLegend.getFormToTextSpace(); + mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()); switch (mLegend.getOrientation()) { case VERTICAL: { From 62bc0de28ac1125597887eaff6046f21b02d5f3b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 14 Aug 2016 21:25:41 +0200 Subject: [PATCH 052/291] Cleanup, preparations for autoScaleMinMax fix --- .../mikephil/charting/data/ChartData.java | 31 ++++++++++--------- .../mikephil/charting/data/CombinedData.java | 13 ++++---- .../mikephil/charting/data/PieData.java | 2 +- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java index 23bd58759b..9bb8dbf107 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java @@ -68,7 +68,7 @@ public ChartData() { */ public ChartData(T... dataSets) { mDataSets = arrayToList(dataSets); - init(); + notifyDataChanged(); } /** @@ -95,15 +95,7 @@ private List arrayToList(T[] array) { */ public ChartData(List sets) { this.mDataSets = sets; - init(); - } - - /** - * performs all kinds of initialization calculations, such as min-max and - * value count and sum - */ - protected void init() { - calcMinMax(); + notifyDataChanged(); } /** @@ -112,11 +104,11 @@ protected void init() { * the contained data has changed. */ public void notifyDataChanged() { - init(); + calcMinMax(); } /** - * calc minimum and maximum values (both x and y) over all DataSets + * Calc minimum and maximum values (both x and y) over all DataSets. */ public void calcMinMax() { @@ -145,7 +137,7 @@ public void calcMinMax() { mLeftAxisMax = firstLeft.getYMax(); mLeftAxisMin = firstLeft.getYMin(); - for (IDataSet dataSet : mDataSets) { + for (T dataSet : mDataSets) { if (dataSet.getAxisDependency() == AxisDependency.LEFT) { if (dataSet.getYMin() < mLeftAxisMin) mLeftAxisMin = dataSet.getYMin(); @@ -164,7 +156,7 @@ public void calcMinMax() { mRightAxisMax = firstRight.getYMax(); mRightAxisMin = firstRight.getYMin(); - for (IDataSet dataSet : mDataSets) { + for (T dataSet : mDataSets) { if (dataSet.getAxisDependency() == AxisDependency.RIGHT) { if (dataSet.getYMin() < mRightAxisMin) mRightAxisMin = dataSet.getYMin(); @@ -436,6 +428,12 @@ public void addEntry(Entry e, int dataSetIndex) { } } + /** + * Adjusts the current minimum and maximum values based on the provided Entry object. + * + * @param e + * @param axis + */ protected void calcMinMax(Entry e, AxisDependency axis) { if (mYMax < e.getY()) @@ -462,6 +460,11 @@ protected void calcMinMax(Entry e, AxisDependency axis) { } } + /** + * Adjusts the minimum and maximum values based on the given DataSet. + * + * @param d + */ protected void calcMinMax(T d) { if (mYMax < d.getYMax()) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java index 035ad9e0b3..c81097da90 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java @@ -29,27 +29,27 @@ public CombinedData() { public void setData(LineData data) { mLineData = data; - init(); + notifyDataChanged(); } public void setData(BarData data) { mBarData = data; - init(); + notifyDataChanged(); } public void setData(ScatterData data) { mScatterData = data; - init(); + notifyDataChanged(); } public void setData(CandleData data) { mCandleData = data; - init(); + notifyDataChanged(); } public void setData(BubbleData data) { mBubbleData = data; - init(); + notifyDataChanged(); } @Override @@ -104,7 +104,6 @@ public void calcMinMax() { mRightAxisMin = data.mRightAxisMin; } - } public BubbleData getBubbleData() { @@ -166,7 +165,7 @@ public void notifyDataChanged() { if (mBubbleData != null) mBubbleData.notifyDataChanged(); - init(); // recalculate everything + calcMinMax(); // recalculate everything } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java index dc920bae50..db7972a3fb 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java @@ -33,7 +33,7 @@ public PieData(IPieDataSet dataSet) { public void setDataSet(IPieDataSet dataSet) { mDataSets.clear(); mDataSets.add(dataSet); - init(); + notifyDataChanged(); } /** From 336dfb93e5c6cb681aa8c660de780617a90480be Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 14 Aug 2016 21:48:05 +0200 Subject: [PATCH 053/291] Fix autoScaleMinMax --- .../mikephil/charting/charts/BarChart.java | 2 +- .../charting/charts/BarLineChartBase.java | 21 ++------- .../mikephil/charting/data/BarDataSet.java | 45 +++++++------------ .../mikephil/charting/data/BubbleDataSet.java | 22 +++------ .../mikephil/charting/data/CandleDataSet.java | 31 +++++-------- .../mikephil/charting/data/ChartData.java | 18 +++++++- .../mikephil/charting/data/DataSet.java | 19 ++++++++ .../interfaces/datasets/IDataSet.java | 16 +++++-- 8 files changed, 85 insertions(+), 89 deletions(-) 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 index a149adf909..8fa91bf5c9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java @@ -63,7 +63,7 @@ protected void init() { protected void calcMinMax() { if (mAutoScaleMinMaxEnabled) - mData.calcMinMax(); + mData.calcMinMax(getLowestVisibleX(), getHighestVisibleX()); if (mFitBars) { mXAxis.calculate(mData.getXMin() - mData.getBarWidth() / 2f, mData.getXMax() + mData.getBarWidth() / 2f); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index 494c8bcedb..a80530bdae 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -56,8 +56,6 @@ public abstract class BarLineChartBase yVals) { } @Override - public void calcMinMax() { + protected void calcMinMax(BarEntry e) { - if (mValues == null || mValues.isEmpty()) - return; + if (e != null && !Float.isNaN(e.getY())) { - mYMax = -Float.MAX_VALUE; - mYMin = Float.MAX_VALUE; - mXMax = -Float.MAX_VALUE; - mXMin = Float.MAX_VALUE; + if (e.getYVals() == null) { - for (BarEntry e : mValues) { + if (e.getY() < mYMin) + mYMin = e.getY(); - if (e != null && !Float.isNaN(e.getY())) { + if (e.getY() > mYMax) + mYMax = e.getY(); + } else { - if (e.getYVals() == null) { + if (-e.getNegativeSum() < mYMin) + mYMin = -e.getNegativeSum(); - if (e.getY() < mYMin) - mYMin = e.getY(); - - if (e.getY() > mYMax) - mYMax = e.getY(); - } else { - - if (-e.getNegativeSum() < mYMin) - mYMin = -e.getNegativeSum(); - - if (e.getPositiveSum() > mYMax) - mYMax = e.getPositiveSum(); - } + if (e.getPositiveSum() > mYMax) + mYMax = e.getPositiveSum(); + } - if (e.getX() < mXMin) - mXMin = e.getX(); + if (e.getX() < mXMin) + mXMin = e.getX(); - if (e.getX() > mXMax) - mXMax = e.getX(); - } + if (e.getX() > mXMax) + mXMax = e.getX(); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java index ec288123e2..d8c0c13013 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java @@ -29,25 +29,13 @@ public float getHighlightCircleWidth() { } @Override - public void calcMinMax() { + protected void calcMinMax(BubbleEntry e) { + super.calcMinMax(e); - if (mValues == null || mValues.isEmpty()) - return; + final float size = e.getSize(); - mYMax = -Float.MAX_VALUE; - mYMin = Float.MAX_VALUE; - mXMax = -Float.MAX_VALUE; - mXMin = Float.MAX_VALUE; - - for (BubbleEntry e : mValues) { - - calcMinMax(e); - - final float size = e.getSize(); - - if (size > mMaxSize) { - mMaxSize = size; - } + if (size > mMaxSize) { + mMaxSize = size; } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java index 139a1bad42..8d883f0575 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java @@ -25,7 +25,7 @@ public class CandleDataSet extends LineScatterCandleRadarDataSet im /** * should the candle bars show? * when false, only "ticks" will show - * + *

* - default: true */ private boolean mShowCandleBar = true; @@ -101,30 +101,19 @@ public DataSet copy() { } @Override - public void calcMinMax() { - - if (mValues == null || mValues.isEmpty()) - return; - - mYMax = -Float.MAX_VALUE; - mYMin = Float.MAX_VALUE; - mXMax = -Float.MAX_VALUE; - mXMin = Float.MAX_VALUE; + protected void calcMinMax(CandleEntry e) { - for (CandleEntry e : mValues) { + if (e.getLow() < mYMin) + mYMin = e.getLow(); - if (e.getLow() < mYMin) - mYMin = e.getLow(); + if (e.getHigh() > mYMax) + mYMax = e.getHigh(); - if (e.getHigh() > mYMax) - mYMax = e.getHigh(); + if (e.getX() < mXMin) + mXMin = e.getX(); - if (e.getX() < mXMin) - mXMin = e.getX(); - - if (e.getX() > mXMax) - mXMax = e.getX(); - } + if (e.getX() > mXMax) + mXMax = e.getX(); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java index 9bb8dbf107..866f258e16 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java @@ -107,10 +107,26 @@ public void notifyDataChanged() { calcMinMax(); } + /** + * Calc minimum and maximum values (both x and y) over all DataSets. + * Tell DataSets to recalculate their min and max values, this is needed for autoScaleMinMax. + * + * @param fromX the x-value to start the calculation from + * @param toX the x-value to which the calculation should be performed + */ + public void calcMinMax(float fromX, float toX) { + + for (T set : mDataSets) { + set.calcMinMax(fromX, toX); + } + + calcMinMax(); + } + /** * Calc minimum and maximum values (both x and y) over all DataSets. */ - public void calcMinMax() { + protected void calcMinMax() { if (mDataSets == null) return; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java index d337a0482b..6bd5a78c72 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -74,6 +74,25 @@ public void calcMinMax() { } } + @Override + public void calcMinMax(float fromX, float toX) { + + if (mValues == null || mValues.isEmpty()) + return; + + mYMax = -Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; + + int indexFrom = getEntryIndex(fromX, Rounding.CLOSEST); + int indexTo = getEntryIndex(toX, Rounding.CLOSEST); + + for (int i = indexFrom; i <= indexTo; i++) { + calcMinMax(mValues.get(i)); + } + } + /** * Updates the min and max x and y value of this DataSet based on the given Entry. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index 62c10b3f32..8130909b95 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -59,6 +59,14 @@ public interface IDataSet { */ void calcMinMax(); + /** + * Calculates the min and max values from the given x-value to the given x-value. + * + * @param fromX + * @param toX + */ + void calcMinMax(float fromX, float toX); + /** * Returns the first Entry object found at the given x-value with binary * search. If the no Entry at the specified x-value is found, this method @@ -372,28 +380,28 @@ public interface IDataSet { /** * The form to draw for this dataset in the legend. - * + *

* Return `DEFAULT` to use the default legend form. */ Legend.LegendForm getForm(); /** * The form size to draw for this dataset in the legend. - * + *

* Return `Float.NaN` to use the default legend form size. */ float getFormSize(); /** * The line width for drawing the form of this dataset in the legend - * + *

* Return `Float.NaN` to use the default legend form line width. */ float getFormLineWidth(); /** * The line dash path effect used for shapes that consist of lines. - * + *

* Return `null` to use the default legend form line dash effect. */ DashPathEffect getFormLineDashEffect(); From 1bbf4be1eed520a1da075d2e5a5d4cfb686ff4bd Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 14 Aug 2016 22:22:48 +0200 Subject: [PATCH 054/291] Fixes and finishing up of autoScaleMinMax feature --- .../mikephil/charting/charts/BarChart.java | 3 -- .../charting/charts/BarLineChartBase.java | 27 +++++++++++++---- .../mikephil/charting/data/BarDataSet.java | 6 +--- .../mikephil/charting/data/CandleDataSet.java | 6 +--- .../mikephil/charting/data/ChartData.java | 9 +++--- .../mikephil/charting/data/DataSet.java | 30 ++++++++++++------- .../mikephil/charting/data/PieDataSet.java | 6 +--- .../interfaces/datasets/IDataSet.java | 5 ++-- 8 files changed, 52 insertions(+), 40 deletions(-) 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 index 8fa91bf5c9..a805815952 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java @@ -62,9 +62,6 @@ protected void init() { @Override protected void calcMinMax() { - if (mAutoScaleMinMaxEnabled) - mData.calcMinMax(getLowestVisibleX(), getHighestVisibleX()); - if (mFitBars) { mXAxis.calculate(mData.getXMin() - mData.getBarWidth() / 2f, mData.getXMax() + mData.getBarWidth() / 2f); } else { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index a80530bdae..817c70b6c9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -204,9 +204,7 @@ protected void onDraw(Canvas canvas) { mAxisRendererRight.renderAxisLine(canvas); if (mAutoScaleMinMaxEnabled) { - - calcMinMax(); - calculateOffsets(); + autoScale(); } mXAxisRenderer.renderGridLines(canvas); @@ -325,12 +323,29 @@ public void notifyDataSetChanged() { calculateOffsets(); } + /** + * Performs auto scaling of the axis by recalculating the minimum and maximum y-values based on the entries currently in view. + */ + protected void autoScale() { + + final float fromX = getLowestVisibleX(); + final float toX = getHighestVisibleX(); + + mData.calcMinMaxY(fromX, toX); + + mXAxis.calculate(mData.getXMin(), mData.getXMax()); + + // calculate axis range (min / max) according to provided data + mAxisLeft.calculate(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); + mAxisRight.calculate(mData.getYMin(AxisDependency.RIGHT), mData.getYMax(AxisDependency + .RIGHT)); + + calculateOffsets(); + } + @Override protected void calcMinMax() { - if (mAutoScaleMinMaxEnabled) - mData.calcMinMax(getLowestVisibleX(), getHighestVisibleX()); - mXAxis.calculate(mData.getXMin(), mData.getXMax()); // calculate axis range (min / max) according to provided data diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java index c11eb2b9cc..ae11c97b3c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java @@ -127,11 +127,7 @@ protected void calcMinMax(BarEntry e) { mYMax = e.getPositiveSum(); } - if (e.getX() < mXMin) - mXMin = e.getX(); - - if (e.getX() > mXMax) - mXMax = e.getX(); + calcMinMaxX(e); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java index 8d883f0575..13bf6062d4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java @@ -109,11 +109,7 @@ protected void calcMinMax(CandleEntry e) { if (e.getHigh() > mYMax) mYMax = e.getHigh(); - if (e.getX() < mXMin) - mXMin = e.getX(); - - if (e.getX() > mXMax) - mXMax = e.getX(); + calcMinMaxX(e); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java index 866f258e16..1ffc1dbcc4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java @@ -108,18 +108,19 @@ public void notifyDataChanged() { } /** - * Calc minimum and maximum values (both x and y) over all DataSets. - * Tell DataSets to recalculate their min and max values, this is needed for autoScaleMinMax. + * Calc minimum and maximum y-values over all DataSets. + * Tell DataSets to recalculate their min and max y-values, this is only needed for autoScaleMinMax. * * @param fromX the x-value to start the calculation from * @param toX the x-value to which the calculation should be performed */ - public void calcMinMax(float fromX, float toX) { + public void calcMinMaxY(float fromX, float toX) { for (T set : mDataSets) { - set.calcMinMax(fromX, toX); + set.calcMinMaxY(fromX, toX); } + // apply the new data calcMinMax(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java index 6bd5a78c72..4aa8a17409 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -75,21 +75,21 @@ public void calcMinMax() { } @Override - public void calcMinMax(float fromX, float toX) { + public void calcMinMaxY(float fromX, float toX) { if (mValues == null || mValues.isEmpty()) return; mYMax = -Float.MAX_VALUE; mYMin = Float.MAX_VALUE; - mXMax = -Float.MAX_VALUE; - mXMin = Float.MAX_VALUE; - int indexFrom = getEntryIndex(fromX, Rounding.CLOSEST); - int indexTo = getEntryIndex(toX, Rounding.CLOSEST); + int indexFrom = getEntryIndex(fromX, Rounding.DOWN); + int indexTo = getEntryIndex(toX, Rounding.UP); for (int i = indexFrom; i <= indexTo; i++) { - calcMinMax(mValues.get(i)); + + // only recalculate y + calcMinMaxY(mValues.get(i)); } } @@ -103,11 +103,12 @@ protected void calcMinMax(T e) { if (e == null) return; - if (e.getY() < mYMin) - mYMin = e.getY(); + calcMinMaxX(e); - if (e.getY() > mYMax) - mYMax = e.getY(); + calcMinMaxY(e); + } + + protected void calcMinMaxX(T e) { if (e.getX() < mXMin) mXMin = e.getX(); @@ -116,6 +117,15 @@ protected void calcMinMax(T e) { mXMax = e.getX(); } + protected void calcMinMaxY(T e) { + + if (e.getY() < mYMin) + mYMin = e.getY(); + + if (e.getY() > mYMax) + mYMax = e.getY(); + } + @Override public int getEntryCount() { return mValues.size(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java index 316aab4773..3e864a25d8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java @@ -51,11 +51,7 @@ protected void calcMinMax(PieEntry e) { if (e == null) return; - if (e.getY() < mYMin) - mYMin = e.getY(); - - if (e.getY() > mYMax) - mYMax = e.getY(); + calcMinMaxY(e); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index 8130909b95..337adc35d2 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -60,12 +60,13 @@ public interface IDataSet { void calcMinMax(); /** - * Calculates the min and max values from the given x-value to the given x-value. + * Calculates the min and max y-values from the Entry closest to the given fromX to the Entry closest to the given toX value. + * This is only needed for the autoScaleMinMax feature. * * @param fromX * @param toX */ - void calcMinMax(float fromX, float toX); + void calcMinMaxY(float fromX, float toX); /** * Returns the first Entry object found at the given x-value with binary From 6c17ca45de833f1f66493eee36f2190dbe703497 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 15 Aug 2016 15:21:33 +0300 Subject: [PATCH 055/291] Take care of edge cases when calculating intervals (labelCount == 0) --- .../com/github/mikephil/charting/renderer/AxisRenderer.java | 4 ++++ .../mikephil/charting/renderer/YAxisRendererRadarChart.java | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java index 1b9c82364a..33198f7198 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java @@ -162,6 +162,10 @@ protected void computeAxisValues(float min, float max) { // Find out how much spacing (in y value space) between axis values double rawInterval = range / labelCount; + if (Double.isInfinite(rawInterval)) + { + rawInterval = range > 0.0 ? range : 1.0; + } double interval = Utils.roundToNextSignificant(rawInterval); // If granularity is enabled, then do not allow the interval to go below specified granularity. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java index 9a5fd1cd44..fdef622cc8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -40,6 +40,10 @@ protected void computeAxisValues(float min, float max) { // Find out how much spacing (in y value space) between axis values double rawInterval = range / labelCount; + if (Double.isInfinite(rawInterval)) + { + rawInterval = range > 0.0 ? range : 1.0; + } double interval = Utils.roundToNextSignificant(rawInterval); // If granularity is enabled, then do not allow the interval to go below specified granularity. From e556275e292a226c6bd8655c4f5ab1757e45049f Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 15 Aug 2016 15:23:44 +0300 Subject: [PATCH 056/291] range may also be Infinite when xMin/xMax are MAX --- .../com/github/mikephil/charting/renderer/AxisRenderer.java | 2 +- .../mikephil/charting/renderer/YAxisRendererRadarChart.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java index 33198f7198..c96a4d45fe 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java @@ -164,7 +164,7 @@ protected void computeAxisValues(float min, float max) { double rawInterval = range / labelCount; if (Double.isInfinite(rawInterval)) { - rawInterval = range > 0.0 ? range : 1.0; + rawInterval = range > 0.0 && !Double.isInfinite(range) ? range : 1.0; } double interval = Utils.roundToNextSignificant(rawInterval); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java index fdef622cc8..83d835755c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -42,7 +42,7 @@ protected void computeAxisValues(float min, float max) { double rawInterval = range / labelCount; if (Double.isInfinite(rawInterval)) { - rawInterval = range > 0.0 ? range : 1.0; + rawInterval = range > 0.0 && !Double.isInfinite(range) ? range : 1.0; } double interval = Utils.roundToNextSignificant(rawInterval); From 73148a6aa4ad590b02d7307dd31968ec2d4ae918 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 15 Aug 2016 15:50:08 +0300 Subject: [PATCH 057/291] Simplified some code --- .../charting/utils/ViewPortHandler.java | 46 ++++++------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java index eedd190456..541c726eeb 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java @@ -576,42 +576,33 @@ public Matrix getMatrixTouch() { */ public boolean isInBoundsX(float x) { - if (isInBoundsLeft(x) && isInBoundsRight(x)) - return true; - else - return false; + return isInBoundsLeft(x) && isInBoundsRight(x); } public boolean isInBoundsY(float y) { - if (isInBoundsTop(y) && isInBoundsBottom(y)) - return true; - else - return false; + return isInBoundsTop(y) && isInBoundsBottom(y); } public boolean isInBounds(float x, float y) { - if (isInBoundsX(x) && isInBoundsY(y)) - return true; - else - return false; + return isInBoundsX(x) && isInBoundsY(y); } public boolean isInBoundsLeft(float x) { - return mContentRect.left <= x + 1 ? true : false; + return mContentRect.left <= x + 1; } public boolean isInBoundsRight(float x) { x = (float) ((int) (x * 100.f)) / 100.f; - return mContentRect.right >= x - 1 ? true : false; + return mContentRect.right >= x - 1; } public boolean isInBoundsTop(float y) { - return mContentRect.top <= y ? true : false; + return mContentRect.top <= y; } public boolean isInBoundsBottom(float y) { y = (float) ((int) (y * 100.f)) / 100.f; - return mContentRect.bottom >= y ? true : false; + return mContentRect.bottom >= y; } /** @@ -669,10 +660,7 @@ public float getTransY() { */ public boolean isFullyZoomedOut() { - if (isFullyZoomedOutX() && isFullyZoomedOutY()) - return true; - else - return false; + return isFullyZoomedOutX() && isFullyZoomedOutY(); } /** @@ -681,10 +669,7 @@ public boolean isFullyZoomedOut() { * @return */ public boolean isFullyZoomedOutY() { - if (mScaleY > mMinScaleY || mMinScaleY > 1f) - return false; - else - return true; + return !(mScaleY > mMinScaleY || mMinScaleY > 1f); } /** @@ -694,10 +679,7 @@ public boolean isFullyZoomedOutY() { * @return */ public boolean isFullyZoomedOutX() { - if (mScaleX > mMinScaleX || mMinScaleX > 1f) - return false; - else - return true; + return !(mScaleX > mMinScaleX || mMinScaleX > 1f); } /** @@ -735,7 +717,7 @@ public boolean hasNoDragOffset() { * @return */ public boolean canZoomOutMoreX() { - return (mScaleX > mMinScaleX); + return mScaleX > mMinScaleX; } /** @@ -744,7 +726,7 @@ public boolean canZoomOutMoreX() { * @return */ public boolean canZoomInMoreX() { - return (mScaleX < mMaxScaleX); + return mScaleX < mMaxScaleX; } /** @@ -753,7 +735,7 @@ public boolean canZoomInMoreX() { * @return */ public boolean canZoomOutMoreY() { - return (mScaleY > mMinScaleY); + return mScaleY > mMinScaleY; } /** @@ -762,6 +744,6 @@ public boolean canZoomOutMoreY() { * @return */ public boolean canZoomInMoreY() { - return (mScaleY < mMaxScaleY); + return mScaleY < mMaxScaleY; } } From 5daa6dc23cf95fe62251a9ddf1197e6b8532e639 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 15 Aug 2016 15:59:33 +0300 Subject: [PATCH 058/291] Fill before stroke - because the fill may cover half of a thick stroke --- .../mikephil/charting/renderer/LineChartRenderer.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 516b3b7391..ada9c37715 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -300,6 +300,11 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { mXBounds.set(mChart, dataSet); + // if drawing filled is enabled + if (dataSet.isDrawFilledEnabled() && entryCount > 0) { + drawLinearFill(c, dataSet, trans, mXBounds); + } + // more than 1 color if (dataSet.getColors().size() > 1) { @@ -403,11 +408,6 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { } mRenderPaint.setPathEffect(null); - - // if drawing filled is enabled - if (dataSet.isDrawFilledEnabled() && entryCount > 0) { - drawLinearFill(c, dataSet, trans, mXBounds); - } } protected Path mGenerateFilledPathBuffer = new Path(); From cc971a7d02210b6ae6b2836d55e5241dad7595bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6pke?= Date: Wed, 17 Aug 2016 19:44:48 +0200 Subject: [PATCH 059/291] fixed LineChartRenderer to get correct x values when using cubicFill --- .../github/mikephil/charting/renderer/LineChartRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index ada9c37715..74af4d04ef 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -252,8 +252,8 @@ protected void drawCubicFill(Canvas c, ILineDataSet dataSet, Path spline, Transf float fillMin = dataSet.getFillFormatter() .getFillLinePosition(dataSet, mChart); - spline.lineTo(bounds.min + bounds.range, fillMin); - spline.lineTo(bounds.min, fillMin); + spline.lineTo(dataSet.getEntryForIndex(mXBounds.max).getX(), fillMin); + spline.lineTo(dataSet.getEntryForIndex(mXBounds.min).getX(), fillMin); spline.close(); trans.pathValueToPixel(spline); From 918950b6ad8109a182a96320a50bf13f73a27de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20K=C3=B6pke?= Date: Wed, 17 Aug 2016 19:44:48 +0200 Subject: [PATCH 060/291] respect phaseX when drawing cubicFill --- .../github/mikephil/charting/renderer/LineChartRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 74af4d04ef..63b0ad5c8b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -252,8 +252,8 @@ protected void drawCubicFill(Canvas c, ILineDataSet dataSet, Path spline, Transf float fillMin = dataSet.getFillFormatter() .getFillLinePosition(dataSet, mChart); - spline.lineTo(dataSet.getEntryForIndex(mXBounds.max).getX(), fillMin); - spline.lineTo(dataSet.getEntryForIndex(mXBounds.min).getX(), fillMin); + spline.lineTo(dataSet.getEntryForIndex(bounds.min + bounds.range).getX(), fillMin); + spline.lineTo(dataSet.getEntryForIndex(bounds.min).getX(), fillMin); spline.close(); trans.pathValueToPixel(spline); From 9d0b0fd955ffe0ecff43fa5173c2a20369941a9c Mon Sep 17 00:00:00 2001 From: Benoit Vermont Date: Thu, 18 Aug 2016 16:48:28 +0200 Subject: [PATCH 061/291] Update ComponentBase.java Update the documentation for setTextSize, to reflect that the parameter size is in DP, and not in pixel. --- .../github/mikephil/charting/components/ComponentBase.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java index 99e9c7a41b..159b6506f4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java @@ -107,10 +107,10 @@ public void setTypeface(Typeface tf) { } /** - * sets the size of the label text in pixels min = 6f, max = 24f, default + * sets the size of the label text in density pixels min = 6f, max = 24f, default * 10f * - * @param size + * @param size the text size, in DP */ public void setTextSize(float size) { @@ -123,7 +123,7 @@ public void setTextSize(float size) { } /** - * returns the text size that is currently set for the labels + * returns the text size that is currently set for the labels, in pixels * * @return */ From e5d03ba6409fde7f796f056345212293b5a0a1ee Mon Sep 17 00:00:00 2001 From: Voicu Date: Mon, 22 Aug 2016 23:30:43 -0700 Subject: [PATCH 062/291] Remove nullcheck for known non-null value --- .../com/github/mikephil/charting/components/MarkerImage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java index 5c952bb056..1d65436e72 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java @@ -142,10 +142,10 @@ public void draw(Canvas canvas, float posX, float posY) { float width = mSize.width; float height = mSize.height; - if (width == 0.f && mDrawable != null) { + if (width == 0.f) { width = mDrawable.getIntrinsicWidth(); } - if (height == 0.f && mDrawable != null) { + if (height == 0.f) { height = mDrawable.getIntrinsicHeight(); } From afcb72f50f3fcc886f423c16e76581acb6bd8fc1 Mon Sep 17 00:00:00 2001 From: Marcin Date: Wed, 24 Aug 2016 14:52:42 +0100 Subject: [PATCH 063/291] Fix for pie chart, so small values won't appear as "background" (which really is a huge circle) --- .../github/mikephil/charting/renderer/PieChartRenderer.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java index 530e282a26..1681d5baae 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 @@ -263,16 +263,14 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { mPathBuffer.reset(); - float arcStartPointX = 0.f, arcStartPointY = 0.f; + float arcStartPointX = center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD); + float arcStartPointY = center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD); if (sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { // Android is doing "mod 360" mPathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW); } else { - arcStartPointX = center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD); - arcStartPointY = center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD); - mPathBuffer.moveTo(arcStartPointX, arcStartPointY); mPathBuffer.arcTo( From fd3b71f78053fe57c5ff0297c6f4be6722835144 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 29 Aug 2016 22:21:59 +0300 Subject: [PATCH 064/291] Reuse index --- .../github/mikephil/charting/renderer/LineChartRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 63b0ad5c8b..5a7da70377 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -146,7 +146,7 @@ protected void drawHorizontalBezier(ILineDataSet dataSet) { for (int j = mXBounds.min + 1; j <= mXBounds.range + mXBounds.min; j++) { - prev = dataSet.getEntryForIndex(j - 1); + prev = cur; cur = dataSet.getEntryForIndex(j); final float cpx = (prev.getX()) From 01419f795bf87ecdbc90c1f61f9bffbad6c79952 Mon Sep 17 00:00:00 2001 From: PhilJay Date: Wed, 31 Aug 2016 09:19:05 +0200 Subject: [PATCH 065/291] Remove psd --- design/video_thumbnail.psd | Bin 263006 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 design/video_thumbnail.psd diff --git a/design/video_thumbnail.psd b/design/video_thumbnail.psd deleted file mode 100644 index 4695da67291fbfaf3988c045009388e26ae01223..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 263006 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 From 7b33554d5be299ff922a182b06704fa95f950dfa Mon Sep 17 00:00:00 2001 From: PhilJay Date: Sun, 4 Sep 2016 10:44:23 +0200 Subject: [PATCH 066/291] Minor changes & documentation --- MPChartExample/build.gradle | 2 +- .../mikephil/charting/components/AxisBase.java | 16 +++++++++++----- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 3 ++- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index c0d4745dcc..a7e8efa03b 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -39,7 +39,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' + classpath 'com.android.tools.build:gradle:2.1.3' //classpath 'io.realm:realm-gradle-plugin:0.88.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index 1805db001a..c2e04fc0a6 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -183,6 +183,12 @@ public boolean isDrawAxisLineEnabled() { return mDrawAxisLine; } + /** + * Centers the axis labels instead of drawing them at their original position. + * This is useful especially for grouped BarChart. + * + * @param enabled + */ public void setCenterAxisLabels(boolean enabled) { mCenterAxisLabels = enabled; } @@ -504,13 +510,13 @@ public void enableGridDashedLine(float lineLength, float spaceLength, float phas lineLength, spaceLength }, phase); } - + /** * Enables the grid line to be drawn in dashed mode, e.g. like this * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF. * Keep in mind that hardware acceleration boosts performance. * - * @param effect the DashPathEffect + * @param effect the DashPathEffect */ public void setGridDashedLine(DashPathEffect effect) { mGridDashPathEffect = effect; @@ -540,7 +546,7 @@ public boolean isGridDashedLineEnabled() { public DashPathEffect getGridDashPathEffect() { return mGridDashPathEffect; } - + /** * Enables the axis line to be drawn in dashed mode, e.g. like this @@ -556,13 +562,13 @@ public void enableAxisLineDashedLine(float lineLength, float spaceLength, float lineLength, spaceLength }, phase); } - + /** * Enables the axis line to be drawn in dashed mode, e.g. like this * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF. * Keep in mind that hardware acceleration boosts performance. * - * @param effect the DashPathEffect + * @param effect the DashPathEffect */ public void setAxisLineDashedLine(DashPathEffect effect) { mAxisLineDashPathEffect = effect; diff --git a/build.gradle b/build.gradle index 3dfff3b287..588bad10b1 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { classpath "io.realm:realm-gradle-plugin:1.1.0" - classpath 'com.android.tools.build:gradle:2.1.2' + classpath 'com.android.tools.build:gradle:2.1.3' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9851e51576..aecf1e6c37 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Sat Sep 03 21:01:56 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip From 8f5a898a95bee7fbb5b24b9ee00084e94e36dd2c Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 14 Sep 2016 22:22:32 +0300 Subject: [PATCH 067/291] Improved naming --- .../java/com/github/mikephil/charting/data/BarEntry.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java index 023a159750..dbf1e945db 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java @@ -142,7 +142,15 @@ public boolean isStacked() { return mYVals != null; } + /** + * Use `getSumBelow(stackIndex)` instead. + */ + @Deprecated public float getBelowSum(int stackIndex) { + return getSumBelow(stackIndex); + } + + public float getSumBelow(int stackIndex) { if (mYVals == null) return 0; From a6dd5bb4a127ed72c11b184cf14aa908a8d78e05 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 14 Sep 2016 23:47:55 +0300 Subject: [PATCH 068/291] Improved naming before the big release --- .../java/com/github/mikephil/charting/components/IMarker.java | 2 +- .../com/github/mikephil/charting/components/MarkerImage.java | 4 ++-- .../com/github/mikephil/charting/components/MarkerView.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java index c15dae3a0c..3b8ca43c81 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java @@ -24,7 +24,7 @@ public interface IMarker { * @param posY This is the X position at which the marker wants to be drawn. * You can adjust the offset conditionally based on this argument. */ - MPPointF getOffsetForDrawingAtPos(float posX, float posY); + MPPointF getOffsetForDrawingAtPoint(float posX, float posY); /** * This method enables a specified custom IMarker to update it's content every time the IMarker is redrawn. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java index 5c952bb056..f164d8341f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java @@ -94,7 +94,7 @@ public Chart getChartView() { } @Override - public MPPointF getOffsetForDrawingAtPos(float posX, float posY) { + public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) { MPPointF offset = getOffset(); mOffset2.x = offset.x; @@ -137,7 +137,7 @@ public void draw(Canvas canvas, float posX, float posY) { if (mDrawable == null) return; - MPPointF offset = getOffsetForDrawingAtPos(posX, posY); + MPPointF offset = getOffsetForDrawingAtPoint(posX, posY); float width = mSize.width; float height = mSize.height; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java index 478527caee..162e88e33c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java @@ -80,7 +80,7 @@ public Chart getChartView() { } @Override - public MPPointF getOffsetForDrawingAtPos(float posX, float posY) { + public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) { MPPointF offset = getOffset(); mOffset2.x = offset.x; @@ -118,7 +118,7 @@ public void refreshContent(Entry e, Highlight highlight) { @Override public void draw(Canvas canvas, float posX, float posY) { - MPPointF offset = getOffsetForDrawingAtPos(posX, posY); + MPPointF offset = getOffsetForDrawingAtPoint(posX, posY); int saveId = canvas.save(); // translate to the correct position and draw From a3c1fe6bbf7d48b79b42b3a447f946af850d86f0 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 15 Sep 2016 09:12:59 +0300 Subject: [PATCH 069/291] Added missing clip rect for zero line --- .../github/mikephil/charting/renderer/YAxisRenderer.java | 9 +++++++++ .../renderer/YAxisRendererHorizontalBarChart.java | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java index 092714788a..46f581732c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -211,11 +211,18 @@ protected float[] getTransformedPositions() { } protected Path mDrawZeroLinePath = new Path(); + protected RectF mZeroLineClippingRect = new RectF(); + /** * Draws the zero line. */ protected void drawZeroLine(Canvas c) { + int clipRestoreCount = c.save(); + mZeroLineClippingRect.set(mViewPortHandler.getContentRect()); + mZeroLineClippingRect.inset(0.f, -mYAxis.getZeroLineWidth() / 2.f); + c.clipRect(mLimitLineClippingRect); + // draw zero line MPPointD pos = mTrans.getPixelForValues(0f, 0f); @@ -230,6 +237,8 @@ protected void drawZeroLine(Canvas c) { // draw a path because lines don't support dashing on lower android versions c.drawPath(zeroLinePath, mZeroLinePaint); + + c.restoreToCount(clipRestoreCount); } protected Path mRenderLimitLines = new Path(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java index f867cf5f3a..8822ca5a23 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java @@ -186,6 +186,11 @@ protected Path linePath(Path p, int i, float[] positions) { @Override protected void drawZeroLine(Canvas c) { + int clipRestoreCount = c.save(); + mZeroLineClippingRect.set(mViewPortHandler.getContentRect()); + mZeroLineClippingRect.inset(-mYAxis.getZeroLineWidth() / 2.f, 0.f); + c.clipRect(mLimitLineClippingRect); + // draw zero line MPPointD pos = mTrans.getPixelForValues(0f, 0f); @@ -200,6 +205,10 @@ protected void drawZeroLine(Canvas c) { // draw a path because lines don't support dashing on lower android versions c.drawPath(zeroLinePath, mZeroLinePaint); + + c.restoreToCount(clipRestoreCount); + + c.restoreToCount(clipRestoreCount); } protected Path mRenderLimitLinesPathBuffer = new Path(); From 643f901b157625ebfc5120207e794939e5570872 Mon Sep 17 00:00:00 2001 From: PhilJay Date: Sat, 17 Sep 2016 21:36:59 +0200 Subject: [PATCH 070/291] Make chart description a Component which allows to treat description similar to Legend or Axis (issue #2249) --- .../mpchartexample/AnotherBarActivity.java | 2 +- .../mpchartexample/BarChartActivity.java | 2 +- .../BarChartActivityMultiDataset.java | 2 +- .../mpchartexample/BarChartActivitySinus.java | 2 +- .../BarChartPositiveNegative.java | 2 +- .../mpchartexample/BubbleChartActivity.java | 2 +- .../CandleStickChartActivity.java | 2 +- .../mpchartexample/CombinedChartActivity.java | 2 +- .../CubicLineChartActivity.java | 2 +- .../DynamicalAddingActivity.java | 2 +- .../mpchartexample/FilledLineActivity.java | 2 +- .../mpchartexample/HalfPieChartActivity.java | 2 +- .../HorizontalBarChartActivity.java | 2 +- .../InvertedLineChartActivity.java | 2 +- .../mpchartexample/LineChartActivity1.java | 2 +- .../mpchartexample/LineChartActivity2.java | 3 +- .../LineChartActivityColored.java | 2 +- .../mpchartexample/LineChartTime.java | 2 +- .../ListViewBarChartActivity.java | 2 +- .../MultiLineChartActivity.java | 2 +- .../mpchartexample/PerformanceLineChart.java | 2 +- .../mpchartexample/PieChartActivity.java | 2 +- .../PiePolylineChartActivity.java | 2 +- .../mpchartexample/RadarChartActivitry.java | 2 +- .../RealtimeLineChartActivity.java | 2 +- .../mpchartexample/ScatterChartActivity.java | 3 +- .../mpchartexample/ScrollViewActivity.java | 2 +- .../mpchartexample/StackedBarActivity.java | 2 +- .../StackedBarActivityNegative.java | 2 +- .../fragments/BarChartFrag.java | 2 +- .../fragments/ComplexityFragment.java | 4 +- .../fragments/PieChartFrag.java | 2 +- .../fragments/ScatterChartFrag.java | 2 +- .../fragments/SineCosineFragment.java | 4 +- .../listviewitems/BarChartItem.java | 2 +- .../listviewitems/LineChartItem.java | 2 +- .../listviewitems/PieChartItem.java | 2 +- .../realm/RealmBaseActivity.java | 2 +- .../mikephil/charting/charts/Chart.java | 123 ++++++------------ .../charting/components/ComponentBase.java | 4 +- .../charting/components/Description.java | 95 ++++++++++++++ 41 files changed, 180 insertions(+), 124 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/components/Description.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 7e867af488..ba01acd794 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -47,7 +47,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (BarChart) findViewById(R.id.chart1); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 71aba6f1dd..53e33c969b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -66,7 +66,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawBarShadow(false); mChart.setDrawValueAboveBar(true); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 4024260cc6..3d3efa7076 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -56,7 +56,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (BarChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // mChart.setDrawBorders(true); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index 31f9fa1e56..a7177ff054 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -53,7 +53,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawBarShadow(false); mChart.setDrawValueAboveBar(true); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index e9b7fc7a50..6fe6bc8e8c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -47,7 +47,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawBarShadow(false); mChart.setDrawValueAboveBar(true); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java index aabd600026..22f4afe3c9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -53,7 +53,7 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setOnSeekBarChangeListener(this); mChart = (BubbleChart) findViewById(R.id.chart1); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); mChart.setOnChartValueSelectedListener(this); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java index 1a9d899624..3921215be6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -50,7 +50,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (CandleStickChart) findViewById(R.id.chart1); mChart.setBackgroundColor(Color.WHITE); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 224b1f1fe6..ad3d7e01ea 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -49,7 +49,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_combined); mChart = (CombinedChart) findViewById(R.id.chart1); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); mChart.setBackgroundColor(Color.WHITE); mChart.setDrawGridBackground(false); mChart.setDrawBarShadow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 2752af37f9..cb979e80e9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -56,7 +56,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setBackgroundColor(Color.rgb(104, 241, 175)); // no description text - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // enable touch gestures mChart.setTouchEnabled(true); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index 3462a74af8..7d3e5f431f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -35,7 +35,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (LineChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // add an empty data object mChart.setData(new LineData()); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java index dbc44a34f8..d824167d6b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java @@ -39,7 +39,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawBorders(true); // no description text - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java index 487da96b7e..0b2f44e04f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java @@ -43,7 +43,7 @@ protected void onCreate(Bundle savedInstanceState) { moveOffScreen(); mChart.setUsePercentValues(true); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); mChart.setCenterTextTypeface(mTfLight); mChart.setCenterText(generateCenterSpannableText()); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 316ef4ecac..71c278fb77 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -60,7 +60,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawValueAboveBar(true); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index 9360db623f..a1be487e05 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -61,7 +61,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); // no description text - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // enable touch gestures mChart.setTouchEnabled(true); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index ab2f226efa..72d8638a59 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -72,7 +72,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); // no description text - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); mChart.setNoDataTextDescription("You need to provide data for the chart."); // enable touch gestures diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index e46d582e6a..eb9c754e1d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -2,6 +2,7 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; +import android.graphics.Paint; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -60,7 +61,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setOnChartValueSelectedListener(this); // no description text - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); mChart.setNoDataTextDescription("You need to provide data for the chart."); // enable touch gestures diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index b66b166320..e3c35c75a2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -56,7 +56,7 @@ private void setupChart(LineChart chart, LineData data, int color) { ((LineDataSet) data.getDataSetByIndex(0)).setCircleColorHole(color); // no description text - chart.setDescription(""); + chart.getDescription().setEnabled(false); chart.setNoDataTextDescription("You need to provide data for the chart."); // mChart.setDrawHorizontalGrid(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 9380835ed8..84140938e1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -53,7 +53,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (LineChart) findViewById(R.id.chart1); // no description text - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); mChart.setNoDataTextDescription("You need to provide data for the chart."); // enable touch gestures diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index c3eb7fbf5e..7ee212ff60 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java @@ -84,7 +84,7 @@ public View getView(int position, View convertView, ViewGroup parent) { // apply styling data.setValueTypeface(mTfLight); data.setValueTextColor(Color.BLACK); - holder.chart.setDescription(""); + holder.chart.getDescription().setEnabled(false); holder.chart.setDrawGridBackground(false); XAxis xAxis = holder.chart.getXAxis(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index 8d9060a2b6..e8f6182da2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -52,7 +52,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); mChart.setDrawBorders(false); mChart.getAxisLeft().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java index 8d2fc7f170..681d241c47 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -42,7 +42,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); // no description text - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); mChart.setNoDataTextDescription("You need to provide data for the chart."); // enable touch gestures diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index f514110d59..a33f8e4a01 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -57,7 +57,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (PieChart) findViewById(R.id.chart1); mChart.setUsePercentValues(true); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); mChart.setExtraOffsets(5, 10, 5, 5); mChart.setDragDecelerationFrictionCoef(0.95f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index d84301632f..6583129505 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -62,7 +62,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (PieChart) findViewById(R.id.chart1); mChart.setUsePercentValues(true); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); mChart.setExtraOffsets(5, 10, 5, 5); mChart.setDragDecelerationFrictionCoef(0.95f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index 51bd19dfad..804df1ec2a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -47,7 +47,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (RadarChart) findViewById(R.id.chart1); mChart.setBackgroundColor(Color.rgb(60, 65, 82)); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); mChart.setWebLineWidth(1f); mChart.setWebColor(Color.LTGRAY); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 97410b0ca8..4ca946530e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -40,7 +40,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setOnChartValueSelectedListener(this); // no description text - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); mChart.setNoDataTextDescription("You need to provide data for the chart."); // enable touch gestures diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index c6b457eb1a..ebf43a831d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -52,8 +52,7 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setOnSeekBarChangeListener(this); mChart = (ScatterChart) findViewById(R.id.chart1); - mChart.setDescription(""); - + mChart.getDescription().setEnabled(false); mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java index 617a25b2e0..ff098c32e0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java @@ -28,7 +28,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (BarChart) findViewById(R.id.chart1); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index ff4fde35d0..b6c10d700e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -56,7 +56,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (BarChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index af04d3ae26..4758490a77 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -49,7 +49,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (HorizontalBarChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java index 286f0d58e4..1cdba2735f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java @@ -33,7 +33,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa // create a new chart object mChart = new BarChart(getActivity()); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); mChart.setOnChartGestureListener(this); MyMarkerView mv = new MyMarkerView(getActivity(), R.layout.custom_marker_view); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java index 98b904b2b7..caf9e3e295 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java @@ -26,8 +26,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa View v = inflater.inflate(R.layout.frag_simple_line, container, false); mChart = (LineChart) v.findViewById(R.id.lineChart1); - - mChart.setDescription(""); + + mChart.getDescription().setEnabled(false); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java index ae7ca9f19c..dc30f303ec 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java @@ -29,7 +29,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa View v = inflater.inflate(R.layout.frag_simple_pie, container, false); mChart = (PieChart) v.findViewById(R.id.pieChart1); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); Typeface tf = Typeface.createFromAsset(getActivity().getAssets(), "OpenSans-Light.ttf"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java index cb1bd1c7e7..90aba051d3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java @@ -29,7 +29,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa View v = inflater.inflate(R.layout.frag_simple_scatter, container, false); mChart = (ScatterChart) v.findViewById(R.id.scatterChart1); - mChart.setDescription(""); + mChart.getDescription().setEnabled(false); Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java index 648a0f01d0..60173c368f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java @@ -26,8 +26,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa View v = inflater.inflate(R.layout.frag_simple_line, container, false); mChart = (LineChart) v.findViewById(R.id.lineChart1); - - mChart.setDescription(""); + + mChart.getDescription().setEnabled(false); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java index ab3c99b7c8..cf9b4b553d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java @@ -48,7 +48,7 @@ public View getView(int position, View convertView, Context c) { } // apply styling - holder.chart.setDescription(""); + holder.chart.getDescription().setEnabled(false); holder.chart.setDrawGridBackground(false); holder.chart.setDrawBarShadow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java index c1796258f4..f988844fce 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java @@ -50,7 +50,7 @@ public View getView(int position, View convertView, Context c) { // apply styling // holder.chart.setValueTypeface(mTf); - holder.chart.setDescription(""); + holder.chart.getDescription().setEnabled(false); holder.chart.setDrawGridBackground(false); XAxis xAxis = holder.chart.getXAxis(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java index f803eb8072..8a2ff8bda6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java @@ -56,7 +56,7 @@ public View getView(int position, View convertView, Context c) { } // apply styling - holder.chart.setDescription(""); + holder.chart.getDescription().setEnabled(false); holder.chart.setHoleRadius(52f); holder.chart.setTransparentCircleRadius(57f); holder.chart.setCenterText(mCenterText); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java index d4fef69576..27bdff9ce3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -36,7 +36,7 @@ protected void setup(Chart chart) { mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); // no description text - chart.setDescription(""); + chart.getDescription().setEnabled(false); chart.setNoDataTextDescription("You need to provide data for the chart."); // enable touch gestures diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 9b409baa00..6a666b38e5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -13,7 +13,6 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.RectF; -import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Environment; import android.provider.MediaStore.Images; @@ -27,6 +26,7 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.animation.EasingFunction; +import com.github.mikephil.charting.components.Description; import com.github.mikephil.charting.components.IMarker; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; @@ -112,11 +112,6 @@ public abstract class Chart 16f) - size = 16f; - if (size < 6f) - size = 6f; - - mDescPaint.setTextSize(Utils.convertDpToPixel(size)); - } - - /** - * Sets the color of the description text. - * - * @param color - */ - public void setDescriptionColor(int color) { - mDescPaint.setColor(color); - } - /** * Sets extra offsets (around the chart view) to be appended to the * auto-calculated offsets. @@ -1285,6 +1228,24 @@ public IMarker getMarkerView() { return getMarker(); } + /** + * Sets a new Description object for the chart. + * + * @param desc + */ + public void setDescription(Description desc) { + this.mDescription = desc; + } + + /** + * Returns the Description object of the chart. + * + * @return + */ + public Description getDescription() { + return mDescription; + } + /** * Returns the Legend object of the chart. This method can be used to get an * instance of the legend in order to customize the automatically generated diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java index 159b6506f4..7d31d0350a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java @@ -19,12 +19,12 @@ public abstract class ComponentBase { protected boolean mEnabled = true; /** - * the offset in pixels this axis labels have on the x-axis + * the offset in pixels this component has on the x-axis */ protected float mXOffset = 5f; /** - * the offset in pixels this axis labels have on the Y-axis + * the offset in pixels this component has on the Y-axis */ protected float mYOffset = 5f; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Description.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Description.java new file mode 100644 index 0000000000..18294a3270 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Description.java @@ -0,0 +1,95 @@ +package com.github.mikephil.charting.components; + +import android.graphics.Paint; + +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; + +/** + * Created by Philipp Jahoda on 17/09/16. + */ +public class Description extends ComponentBase { + + /** + * the text used in the description + */ + private String text = "Description Label"; + + /** + * the custom position of the description text + */ + private MPPointF mPosition; + + /** + * the alignment of the description text + */ + private Paint.Align mTextAlign = Paint.Align.RIGHT; + + public Description() { + super(); + + // default size + mTextSize = Utils.convertDpToPixel(8f); + } + + /** + * Sets the text to be shown as the description. + * Never set this to null as this will cause nullpointer exception when drawing with Android Canvas. + * + * @param text + */ + public void setText(String text) { + this.text = text; + } + + /** + * Returns the description text. + * + * @return + */ + public String getText() { + return text; + } + + /** + * Sets a custom position for the description text in pixels on the screen. + * + * @param x - xcoordinate + * @param y - ycoordinate + */ + public void setPosition(float x, float y) { + if (mPosition == null) { + mPosition = MPPointF.getInstance(x, y); + } else { + mPosition.x = x; + mPosition.y = y; + } + } + + /** + * Returns the customized position of the description, or null if none set. + * + * @return + */ + public MPPointF getPosition() { + return mPosition; + } + + /** + * Sets the text alignment of the description text. Default RIGHT. + * + * @param align + */ + public void setTextAlign(Paint.Align align) { + this.mTextAlign = align; + } + + /** + * Returns the text alignment of the description. + * + * @return + */ + public Paint.Align getTextAlign() { + return mTextAlign; + } +} From d34670fa620af9353da4fedc78d2c8653fe5c5b1 Mon Sep 17 00:00:00 2001 From: PhilJay Date: Sat, 17 Sep 2016 23:23:12 +0200 Subject: [PATCH 071/291] Remove noDataTextDescription - this can be done via noDataText as well --- .../mpchartexample/LineChartActivity1.java | 1 - .../mpchartexample/LineChartActivity2.java | 1 - .../LineChartActivityColored.java | 1 - .../mpchartexample/LineChartTime.java | 1 - .../mpchartexample/PerformanceLineChart.java | 1 - .../RealtimeLineChartActivity.java | 5 +-- .../realm/RealmBaseActivity.java | 1 - .../mikephil/charting/charts/Chart.java | 37 ++++--------------- 8 files changed, 9 insertions(+), 39 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 72d8638a59..bfb14090ab 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -73,7 +73,6 @@ protected void onCreate(Bundle savedInstanceState) { // no description text mChart.getDescription().setEnabled(false); - mChart.setNoDataTextDescription("You need to provide data for the chart."); // enable touch gestures mChart.setTouchEnabled(true); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index eb9c754e1d..5cdeacf3a9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -62,7 +62,6 @@ protected void onCreate(Bundle savedInstanceState) { // no description text mChart.getDescription().setEnabled(false); - mChart.setNoDataTextDescription("You need to provide data for the chart."); // enable touch gestures mChart.setTouchEnabled(true); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index e3c35c75a2..68bba4b458 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -57,7 +57,6 @@ private void setupChart(LineChart chart, LineData data, int color) { // no description text chart.getDescription().setEnabled(false); - chart.setNoDataTextDescription("You need to provide data for the chart."); // mChart.setDrawHorizontalGrid(false); // diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 84140938e1..7208f3f0da 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -54,7 +54,6 @@ protected void onCreate(Bundle savedInstanceState) { // no description text mChart.getDescription().setEnabled(false); - mChart.setNoDataTextDescription("You need to provide data for the chart."); // enable touch gestures mChart.setTouchEnabled(true); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java index 681d241c47..0763f7f88a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -43,7 +43,6 @@ protected void onCreate(Bundle savedInstanceState) { // no description text mChart.getDescription().setEnabled(false); - mChart.setNoDataTextDescription("You need to provide data for the chart."); // enable touch gestures mChart.setTouchEnabled(true); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 4ca946530e..30f38126ed 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -39,9 +39,8 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (LineChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); - // no description text - mChart.getDescription().setEnabled(false); - mChart.setNoDataTextDescription("You need to provide data for the chart."); + // enable description text + mChart.getDescription().setEnabled(true); // enable touch gestures mChart.setTouchEnabled(true); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java index 27bdff9ce3..f7d7233d88 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -37,7 +37,6 @@ protected void setup(Chart chart) { // no description text chart.getDescription().setEnabled(false); - chart.setNoDataTextDescription("You need to provide data for the chart."); // enable touch gestures chart.setTouchEnabled(true); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 6a666b38e5..48e206a438 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -13,6 +13,7 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.RectF; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Environment; import android.provider.MediaStore.Images; @@ -149,12 +150,6 @@ public abstract class Chart Date: Sat, 17 Sep 2016 23:23:59 +0200 Subject: [PATCH 072/291] Remove unused mDrawPaint instance --- .../java/com/github/mikephil/charting/charts/Chart.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 48e206a438..e0c94a8f6d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -239,8 +239,6 @@ public void onAnimationUpdate(ValueAnimator animation) { mInfoPaint.setTextAlign(Align.CENTER); mInfoPaint.setTextSize(Utils.convertDpToPixel(12f)); - mDrawPaint = new Paint(Paint.DITHER_FLAG); - if (mLogEnabled) Log.i("", "Chart.init()"); } @@ -394,11 +392,6 @@ protected void setupDefaultFormatter(float min, float max) { */ private boolean mOffsetsCalculated = false; - /** - * paint object used for drawing the bitmap - */ - protected Paint mDrawPaint; - @Override protected void onDraw(Canvas canvas) { // super.onDraw(canvas); From 0aeddb025efdff11e1f061ecb2645707bbb88219 Mon Sep 17 00:00:00 2001 From: PhilJay Date: Sat, 17 Sep 2016 23:47:47 +0200 Subject: [PATCH 073/291] Fix issue causing ANR for empty charts --- .../github/mikephil/charting/renderer/AxisRenderer.java | 7 ++----- .../charting/renderer/YAxisRendererRadarChart.java | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java index c96a4d45fe..7043e4dd63 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java @@ -154,18 +154,15 @@ protected void computeAxisValues(float min, float max) { int labelCount = mAxis.getLabelCount(); double range = Math.abs(yMax - yMin); - if (labelCount == 0 || range <= 0) { + if (labelCount == 0 || range <= 0 || Double.isInfinite(range)) { mAxis.mEntries = new float[]{}; + mAxis.mCenteredEntries = new float[]{}; mAxis.mEntryCount = 0; return; } // Find out how much spacing (in y value space) between axis values double rawInterval = range / labelCount; - if (Double.isInfinite(rawInterval)) - { - rawInterval = range > 0.0 && !Double.isInfinite(range) ? range : 1.0; - } double interval = Utils.roundToNextSignificant(rawInterval); // If granularity is enabled, then do not allow the interval to go below specified granularity. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java index 83d835755c..64bbb1e085 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -32,18 +32,15 @@ protected void computeAxisValues(float min, float max) { int labelCount = mAxis.getLabelCount(); double range = Math.abs(yMax - yMin); - if (labelCount == 0 || range <= 0) { + if (labelCount == 0 || range <= 0 || Double.isInfinite(range)) { mAxis.mEntries = new float[]{}; + mAxis.mCenteredEntries = new float[]{}; mAxis.mEntryCount = 0; return; } // Find out how much spacing (in y value space) between axis values double rawInterval = range / labelCount; - if (Double.isInfinite(rawInterval)) - { - rawInterval = range > 0.0 && !Double.isInfinite(range) ? range : 1.0; - } double interval = Utils.roundToNextSignificant(rawInterval); // If granularity is enabled, then do not allow the interval to go below specified granularity. From 5b9bd6e633f2f084ea8064af64f8f72402964cd4 Mon Sep 17 00:00:00 2001 From: PhilJay Date: Sun, 18 Sep 2016 11:13:25 +0200 Subject: [PATCH 074/291] Fix time chart example --- .../mpchartexample/LineChartTime.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 7208f3f0da..be1cadc27a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -25,10 +25,12 @@ import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; +import java.sql.Time; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.concurrent.TimeUnit; public class LineChartTime extends DemoBase implements OnSeekBarChangeListener { @@ -87,14 +89,16 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setDrawGridLines(true); xAxis.setTextColor(Color.rgb(255, 192, 56)); xAxis.setCenterAxisLabels(true); - xAxis.setGranularity(60000L); // one minute in millis + xAxis.setGranularity(1f); // one hour xAxis.setValueFormatter(new IAxisValueFormatter() { private SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm"); @Override public String getFormattedValue(float value, AxisBase axis) { - return mFormat.format(new Date((long) value)); + + long millis = TimeUnit.HOURS.toMillis((long) value); + return mFormat.format(new Date(millis)); } @Override @@ -264,15 +268,18 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { private void setData(int count, float range) { - long now = System.currentTimeMillis(); - long hourMillis = 3600000L; + // now in hours + long now = TimeUnit.MILLISECONDS.toHours(System.currentTimeMillis()); ArrayList values = new ArrayList(); - float from = now - (count / 2) * hourMillis; - float to = now + (count / 2) * hourMillis; + float from = now; + + // count = hours + float to = now + count; - for (float x = from; x < to; x += hourMillis) { + // increment by 1 hour + for (float x = from; x < to; x++) { float y = getRandom(range, 50); values.add(new Entry(x, y)); // add one entry per hour From 865c3dae1839dc1ba743159484a70086fcfed682 Mon Sep 17 00:00:00 2001 From: PhilJay Date: Sun, 18 Sep 2016 17:33:04 +0200 Subject: [PATCH 075/291] Extend test cases (for bugfix) --- .../mpchartexample/LineChartTime.java | 2 +- .../mikephil/charting/charts/Chart.java | 3 +- .../mikephil/charting/test/DataSetTest.java | 41 +++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index be1cadc27a..04070d2c26 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -318,4 +318,4 @@ public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } -} +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index e0c94a8f6d..c8b360b1ab 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -1208,7 +1208,8 @@ public void setDescription(Description desc) { } /** - * Returns the Description object of the chart. + * Returns the Description object of the chart that is responsible for holding all information related + * to the description text that is displayed in the bottom right corner of the chart (by default). * * @return */ diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java index 381c2b23da..2311dc7838 100644 --- a/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java @@ -178,4 +178,45 @@ public void testGetEntryForXValue() { assertEquals(21, closest.getX(), 0.01f); assertEquals(5, closest.getY(), 0.01f); } + + @Test + public void testGetEntryForXValueWithDuplicates() { + + // sorted list of values (by x position) + List values = new ArrayList(); + values.add(new Entry(0, 10)); + values.add(new Entry(1, 20)); + values.add(new Entry(2, 30)); + values.add(new Entry(3, 40)); + values.add(new Entry(3, 50)); // duplicate + values.add(new Entry(4, 60)); + values.add(new Entry(4, 70)); // duplicate + values.add(new Entry(5, 80)); + values.add(new Entry(6, 90)); + values.add(new Entry(7, 100)); + values.add(new Entry(8, 110)); + values.add(new Entry(8, 120)); // duplicate + + ScatterDataSet set = new ScatterDataSet(values, ""); + + Entry closest = set.getEntryForXValue(0, DataSet.Rounding.CLOSEST); + assertEquals(0, closest.getX(), 0.01f); + assertEquals(10, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(5, DataSet.Rounding.CLOSEST); + assertEquals(5, closest.getX(), 0.01f); + assertEquals(80, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(5.4f, DataSet.Rounding.CLOSEST); + assertEquals(5, closest.getX(), 0.01f); + assertEquals(80, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(4.6f, DataSet.Rounding.CLOSEST); + assertEquals(5, closest.getX(), 0.01f); + assertEquals(80, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(7, DataSet.Rounding.CLOSEST); + assertEquals(7, closest.getX(), 0.01f); + assertEquals(100, closest.getY(), 0.01f); + } } From 89f10e928e846080f4c9ee239ecdc04812b884e9 Mon Sep 17 00:00:00 2001 From: Voicu Date: Sun, 18 Sep 2016 09:32:34 -0700 Subject: [PATCH 076/291] Remove unused local variable --- .../com/github/mikephil/charting/renderer/LineChartRenderer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 5a7da70377..3184031bce 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -181,7 +181,6 @@ protected void drawHorizontalBezier(ILineDataSet dataSet) { protected void drawCubicBezier(ILineDataSet dataSet) { - float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); From b53c8cdbd1f747adea53a5854878353aa595547c Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 19 Sep 2016 22:15:26 +0300 Subject: [PATCH 077/291] Removed redundancies --- .../com/github/mikephil/charting/charts/Chart.java | 14 ++++++++------ .../renderer/YAxisRendererHorizontalBarChart.java | 2 -- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index e0c94a8f6d..66b349e8e6 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -430,16 +430,18 @@ protected void drawDescription(Canvas c) { mDescPaint.setColor(mDescription.getTextColor()); mDescPaint.setTextAlign(mDescription.getTextAlign()); + float x, y; + // if no position specified, draw on default position if (position == null) { - - float x = getWidth() - mViewPortHandler.offsetRight() - mDescription.getXOffset(); - float y = getHeight() - mViewPortHandler.offsetBottom() - mDescription.getYOffset(); - - c.drawText(mDescription.getText(), x, y, mDescPaint); + x = getWidth() - mViewPortHandler.offsetRight() - mDescription.getXOffset(); + y = getHeight() - mViewPortHandler.offsetBottom() - mDescription.getYOffset(); } else { - c.drawText(mDescription.getText(), position.x, position.y, mDescPaint); + x = position.x; + y = position.y; } + + c.drawText(mDescription.getText(), x, y, mDescPaint); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java index 8822ca5a23..2f96fa71f6 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java @@ -207,8 +207,6 @@ protected void drawZeroLine(Canvas c) { c.drawPath(zeroLinePath, mZeroLinePaint); c.restoreToCount(clipRestoreCount); - - c.restoreToCount(clipRestoreCount); } protected Path mRenderLimitLinesPathBuffer = new Path(); From 7ffa9caaa103af79207b56d15d2d239cdfddfea3 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 21 Sep 2016 07:30:46 +0300 Subject: [PATCH 078/291] Fixed entry searching algorithm to handle sequential same values --- .../mikephil/charting/data/DataSet.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java index 4aa8a17409..7496a50396 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -298,13 +298,28 @@ public int getEntryIndex(float xValue, Rounding rounding) { while (low < high) { int m = (low + high) / 2; - float d1 = Math.abs(mValues.get(m).getX() - xValue); - float d2 = Math.abs(mValues.get(m + 1).getX() - xValue); + final float d1 = mValues.get(m).getX() - xValue, + d2 = mValues.get(m + 1).getX() - xValue, + ad1 = Math.abs(d1), ad2 = Math.abs(d2); - if (d2 <= d1) { + if (ad2 < ad1) { + // [m + 1] is closer to xValue + // Search in an higher place low = m + 1; - } else { + } else if (ad1 < ad2) { + // [m] is closer to xValue + // Search in a lower place high = m; + } else { + // We have multiple sequential x-value with same distance + + if (d1 >= 0.0) { + // Search in a lower place + high = m; + } else if (d1 < 0.0) { + // Search in an higher place + low = m + 1; + } } } From 21e778cb1fae280f53725ce035227a2504c00316 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 21 Sep 2016 07:53:06 +0300 Subject: [PATCH 079/291] Move on from the deprecated property in the demos --- .../mpchartexample/BarChartActivity.java | 5 ++++- .../BarChartActivityMultiDataset.java | 5 ++++- .../mpchartexample/BarChartActivitySinus.java | 5 ++++- .../mpchartexample/BubbleChartActivity.java | 5 ++++- .../mpchartexample/CombinedChartActivity.java | 5 ++++- .../mpchartexample/HalfPieChartActivity.java | 5 ++++- .../HorizontalBarChartActivity.java | 5 ++++- .../mpchartexample/InvertedLineChartActivity.java | 1 - .../mpchartexample/LineChartActivity1.java | 1 - .../mpchartexample/LineChartActivity2.java | 6 ++++-- .../mpchartexample/MultiLineChartActivity.java | 5 ++++- .../mpchartexample/PieChartActivity.java | 5 ++++- .../mpchartexample/PiePolylineChartActivity.java | 5 ++++- .../mpchartexample/RadarChartActivitry.java | 5 ++++- .../mpchartexample/RealtimeLineChartActivity.java | 1 - .../mpchartexample/ScatterChartActivity.java | 5 ++++- .../mpchartexample/StackedBarActivity.java | 5 ++++- .../StackedBarActivityNegative.java | 5 ++++- .../mpchartexample/fragments/PieChartFrag.java | 5 ++++- .../listviewitems/PieChartItem.java | 5 ++++- .../mikephil/charting/components/Legend.java | 15 ++++++++------- 21 files changed, 76 insertions(+), 28 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 53e33c969b..5eb67e7c41 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -107,7 +107,10 @@ protected void onCreate(Bundle savedInstanceState) { rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); l.setForm(LegendForm.SQUARE); l.setFormSize(9f); l.setTextSize(11f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 3d3efa7076..da2ba2c8bc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -77,7 +77,10 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setProgress(100); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART_INSIDE); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(true); l.setTypeface(mTfLight); l.setYOffset(0f); l.setYEntrySpace(0f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index a7177ff054..56add4458e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -93,7 +93,10 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarX.setProgress(150); // set data Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); l.setForm(LegendForm.SQUARE); l.setFormSize(9f); l.setTextSize(11f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java index 22f4afe3c9..db72f29495 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -72,7 +72,10 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setProgress(50); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); l.setTypeface(mTfLight); YAxis yl = mChart.getAxisLeft(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index ad3d7e01ea..f221dcf3b7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -62,7 +62,10 @@ protected void onCreate(Bundle savedInstanceState) { Legend l = mChart.getLegend(); l.setWordWrapEnabled(true); - l.setPosition(Legend.LegendPosition.BELOW_CHART_CENTER); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); YAxis rightAxis = mChart.getAxisRight(); rightAxis.setDrawGridLines(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java index 0b2f44e04f..15da39a8f3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java @@ -71,7 +71,10 @@ protected void onCreate(Bundle savedInstanceState) { mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.ABOVE_CHART_CENTER); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); l.setXEntrySpace(7f); l.setYEntrySpace(0f); l.setYOffset(0f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 71c278fb77..f5ea95098b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -107,7 +107,10 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarX.setOnSeekBarChangeListener(this); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); l.setFormSize(8f); l.setXEntrySpace(4f); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index a1be487e05..f87a9a8098 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -106,7 +106,6 @@ protected void onCreate(Bundle savedInstanceState) { Legend l = mChart.getLegend(); // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); l.setForm(LegendForm.LINE); // dont forget to refresh the drawing diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index bfb14090ab..f6747bbf6f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -156,7 +156,6 @@ protected void onCreate(Bundle savedInstanceState) { Legend l = mChart.getLegend(); // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); l.setForm(LegendForm.LINE); // // dont forget to refresh the drawing diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 5cdeacf3a9..72977c26fa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -89,12 +89,14 @@ protected void onCreate(Bundle savedInstanceState) { Legend l = mChart.getLegend(); // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); l.setForm(LegendForm.LINE); l.setTypeface(mTfLight); l.setTextSize(11f); l.setTextColor(Color.WHITE); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); // l.setYOffset(11f); XAxis xAxis = mChart.getXAxis(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index e8f6182da2..19f7bae938 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -75,7 +75,10 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setProgress(100); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index a33f8e4a01..4493da1f97 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -96,7 +96,10 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setOnSeekBarChangeListener(this); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); + 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); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index 6583129505..49fc4959e9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -102,7 +102,10 @@ protected void onCreate(Bundle savedInstanceState) { // mChart.spin(2000, 0, 360); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); l.setEnabled(false); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index 804df1ec2a..b787f17e85 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -98,7 +98,10 @@ public int getDecimalDigits() { yAxis.setDrawLabels(false); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.ABOVE_CHART_CENTER); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); l.setTypeface(mTfLight); l.setXEntrySpace(7f); l.setYEntrySpace(5f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 30f38126ed..13346bf631 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -66,7 +66,6 @@ protected void onCreate(Bundle savedInstanceState) { Legend l = mChart.getLegend(); // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); l.setForm(LegendForm.LINE); l.setTypeface(mTfLight); l.setTextColor(Color.WHITE); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index ebf43a831d..29904a95c9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -70,7 +70,10 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setProgress(100); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); l.setTypeface(mTfLight); l.setXOffset(5f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index b6c10d700e..31ad84e5ac 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -88,7 +88,10 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setProgress(100); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_RIGHT); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); l.setFormSize(8f); l.setFormToTextSpace(4f); l.setXEntrySpace(6f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 4758490a77..8b9819092a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -93,7 +93,10 @@ public int getDecimalDigits() { }); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_RIGHT); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); l.setFormSize(8f); l.setFormToTextSpace(4f); l.setXEntrySpace(6f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java index dc30f303ec..7888aa632f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java @@ -43,7 +43,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa mChart.setTransparentCircleRadius(50f); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); mChart.setData(generatePieData()); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java index 8a2ff8bda6..db6ba32416 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java @@ -73,7 +73,10 @@ public View getView(int position, View convertView, Context c) { holder.chart.setData((PieData) mChartData); Legend l = holder.chart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); l.setYEntrySpace(0f); l.setYOffset(0f); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java index aeba757cc2..fb1afc8bba 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java @@ -23,9 +23,10 @@ public class Legend extends ComponentBase { /** - * This property is deprecated - Use `position`, `horizontalAlignment`, `verticalAlignment`, `orientation`, `drawInside`, + * This property is deprecated - Use `horizontalAlignment`, `verticalAlignment`, `orientation`, `drawInside`, * `direction`. */ + @Deprecated public enum LegendPosition { RIGHT_OF_CHART, RIGHT_OF_CHART_CENTER, RIGHT_OF_CHART_INSIDE, LEFT_OF_CHART, LEFT_OF_CHART_CENTER, LEFT_OF_CHART_INSIDE, @@ -423,10 +424,10 @@ public boolean isLegendCustom() { } /** - * returns the position of the legend relative to the chart - * - * @return + * This property is deprecated - Use `horizontalAlignment`, `verticalAlignment`, `orientation`, `drawInside`, + * `direction`. */ + @Deprecated public LegendPosition getPosition() { if (mOrientation == LegendOrientation.VERTICAL @@ -463,10 +464,10 @@ public LegendPosition getPosition() { } /** - * sets the position of the legend relative to the whole chart - * - * @param newValue + * This property is deprecated - Use `horizontalAlignment`, `verticalAlignment`, `orientation`, `drawInside`, + * `direction`. */ + @Deprecated public void setPosition(LegendPosition newValue) { switch (newValue) { From 63684f02ca686d38b835f39fc86a5263f86034a3 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 21 Sep 2016 17:56:00 +0300 Subject: [PATCH 080/291] Avoid crash for `centerAxisLabelsEnabled` when entry count == 1 --- .../com/github/mikephil/charting/renderer/AxisRenderer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java index 7043e4dd63..2cade932c5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java @@ -250,7 +250,9 @@ protected void computeAxisValues(float min, float max) { mAxis.mCenteredEntries = new float[n]; } - float offset = (mAxis.mEntries[1] - mAxis.mEntries[0]) / 2f; + float offset = 0.f; + if (mAxis.mEntries.length > 1) + offset = (mAxis.mEntries[1] - mAxis.mEntries[0]) / 2f; for (int i = 0; i < n; i++) { mAxis.mCenteredEntries[i] = mAxis.mEntries[i] + offset; From b1fe859ce88a6de3777d67c3ad1f120ba4e84e6c Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 21 Sep 2016 19:55:58 +0300 Subject: [PATCH 081/291] Fixes for cubic bezier edges --- .../charting/renderer/LineChartRenderer.java | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 5a7da70377..8688ab37ea 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -199,22 +199,33 @@ protected void drawCubicBezier(ILineDataSet dataSet) { float curDx = 0f; float curDy = 0f; - Entry prevPrev = dataSet.getEntryForIndex(mXBounds.min); - Entry prev = prevPrev; - Entry cur = prev; - Entry next = dataSet.getEntryForIndex(mXBounds.min + 1); + // Take an extra point from the left, and an extra from the right. + // That's because we need 4 points for a cubic bezier (cubic=4), otherwise we get lines moving and doing weird stuff on the edges of the chart. + // So in the starting `prev` and `cur`, go -2, -1 + // And in the `lastIndex`, add +1 + + final int firstIndex = mXBounds.min + 1; + final int lastIndex = mXBounds.min + mXBounds.range; + + Entry prevPrev; + Entry prev = dataSet.getEntryForIndex(Math.max(firstIndex - 2, 0)); + Entry cur = dataSet.getEntryForIndex(Math.max(firstIndex - 1, 0)); + Entry next = cur; + int nextIndex = -1; - if (cur == null || next == null) return; + if (cur == null) return; // let the spline start cubicPath.moveTo(cur.getX(), cur.getY() * phaseY); for (int j = mXBounds.min + 1; j <= mXBounds.range + mXBounds.min; j++) { - prevPrev = dataSet.getEntryForIndex(j == 1 ? 0 : j - 2); - prev = dataSet.getEntryForIndex(j - 1); - cur = dataSet.getEntryForIndex(j); - next = mXBounds.max > j + 1 ? dataSet.getEntryForIndex(j + 1) : cur; + prevPrev = prev; + prev = cur; + cur = nextIndex == j ? next : dataSet.getEntryForIndex(j); + + nextIndex = j + 1 < dataSet.getEntryCount() ? j + 1 : j; + next = dataSet.getEntryForIndex(nextIndex); prevDx = (cur.getX() - prevPrev.getX()) * intensity; prevDy = (cur.getY() - prevPrev.getY()) * intensity; From 6c0b9c1aabc41a0a80e21ff042e4514cea57ce82 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 21 Sep 2016 20:02:56 +0300 Subject: [PATCH 082/291] Removed redundant condition --- .../github/mikephil/charting/renderer/LineChartRenderer.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 8688ab37ea..9cab2208fb 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -361,8 +361,6 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { // make sure the lines don't do shitty things outside // bounds if (!mViewPortHandler.isInBoundsLeft(mLineBuffer[2]) - || (!mViewPortHandler.isInBoundsTop(mLineBuffer[1]) && !mViewPortHandler - .isInBoundsBottom(mLineBuffer[3])) || (!mViewPortHandler.isInBoundsTop(mLineBuffer[1]) && !mViewPortHandler .isInBoundsBottom(mLineBuffer[3]))) continue; From 01d8b246869f3d9517b615814d8ecb30cbfebdf0 Mon Sep 17 00:00:00 2001 From: PhilJay Date: Sat, 24 Sep 2016 18:21:50 +0200 Subject: [PATCH 083/291] Update gradle --- MPChartExample/build.gradle | 2 +- build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index a7e8efa03b..84513580e4 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -39,7 +39,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.3' + classpath 'com.android.tools.build:gradle:2.2.0' //classpath 'io.realm:realm-gradle-plugin:0.88.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/build.gradle b/build.gradle index 588bad10b1..8e5194d52e 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { classpath "io.realm:realm-gradle-plugin:1.1.0" - classpath 'com.android.tools.build:gradle:2.1.3' + classpath 'com.android.tools.build:gradle:2.2.0' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } } From 028328b98647ac22624680508bd1b9f42ec2433b Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 29 Sep 2016 08:12:05 +0300 Subject: [PATCH 084/291] Guard `roundToNextSignificant` and `decimal` from invalids --- .../java/com/github/mikephil/charting/utils/Utils.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java index c7c1884ff6..b555b44917 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java @@ -360,6 +360,11 @@ public static String formatNumber(float number, int digitCount, boolean separate * @return */ public static float roundToNextSignificant(double number) { + if (Double.isInfinite(number) || + Double.isNaN(number) || + number == 0.0) + return 0; + final float d = (float) Math.ceil((float) Math.log10(number < 0 ? -number : number)); final int pw = 1 - (int) d; final float magnitude = (float) Math.pow(10, pw); @@ -377,6 +382,10 @@ public static float roundToNextSignificant(double number) { public static int getDecimals(float number) { float i = roundToNextSignificant(number); + + if (Float.isInfinite(i)) + return 0; + return (int) Math.ceil(-Math.log10(i)) + 2; } From aa14ab262b61da575d0591f0851ff7fccb42e81e Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 29 Sep 2016 08:59:33 +0300 Subject: [PATCH 085/291] Minor fixes for interval in axis labels --- .../mikephil/charting/renderer/AxisRenderer.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java index 2cade932c5..4c8ce4c8ba 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java @@ -179,13 +179,12 @@ protected void computeAxisValues(float min, float max) { interval = Math.floor(10 * intervalMagnitude); } - boolean centeringEnabled = mAxis.isCenterAxisLabelsEnabled(); int n = centeringEnabled ? 1 : 0; // force label count if (mAxis.isForceLabelsEnabled()) { - float step = (float) range / (float) (labelCount - 1); + interval = (float) range / (float) (labelCount - 1); mAxis.mEntryCount = labelCount; if (mAxis.mEntries.length < labelCount) { @@ -197,7 +196,7 @@ protected void computeAxisValues(float min, float max) { for (int i = 0; i < labelCount; i++) { mAxis.mEntries[i] = v; - v += step; + v += interval; } n = labelCount; @@ -206,7 +205,7 @@ protected void computeAxisValues(float min, float max) { } else { double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; - if(centeringEnabled) { + if(mAxis.isCenterAxisLabelsEnabled()) { first -= interval; } @@ -244,15 +243,13 @@ protected void computeAxisValues(float min, float max) { mAxis.mDecimals = 0; } - if (centeringEnabled) { + if (mAxis.isCenterAxisLabelsEnabled()) { if (mAxis.mCenteredEntries.length < n) { mAxis.mCenteredEntries = new float[n]; } - float offset = 0.f; - if (mAxis.mEntries.length > 1) - offset = (mAxis.mEntries[1] - mAxis.mEntries[0]) / 2f; + float offset = (float)interval / 2f; for (int i = 0; i < n; i++) { mAxis.mCenteredEntries[i] = mAxis.mEntries[i] + offset; From 06bed3332d21f4c4e1e2e7ed67d8f5f1a304aac2 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 29 Sep 2016 09:00:18 +0300 Subject: [PATCH 086/291] Allow label centering for 1 label --- .../java/com/github/mikephil/charting/components/AxisBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index c2e04fc0a6..fc92dc8fb7 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -194,7 +194,7 @@ public void setCenterAxisLabels(boolean enabled) { } public boolean isCenterAxisLabelsEnabled() { - return mCenterAxisLabels && mEntryCount > 1; + return mCenterAxisLabels && mEntryCount > 0; } /** From c8553da89cadb9e81c5c500de7f241ff4afd3e5e Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 29 Sep 2016 09:09:05 +0300 Subject: [PATCH 087/291] Set z-index of markers to be the highest --- .../com/github/mikephil/charting/charts/BarLineChartBase.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index 817c70b6c9..6fa3b4d312 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -252,10 +252,10 @@ protected void onDraw(Canvas canvas) { mLegendRenderer.renderLegend(canvas); - drawMarkers(canvas); - drawDescription(canvas); + drawMarkers(canvas); + if (mLogEnabled) { long drawtime = (System.currentTimeMillis() - starttime); totalTime += drawtime; From cf0f1fca3443141e58696b1861b0a32e9c1fd4ad Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 29 Sep 2016 19:38:48 +0800 Subject: [PATCH 088/291] [FIX] not find centeringEnabled --- .../com/github/mikephil/charting/renderer/AxisRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java index 4c8ce4c8ba..90528a1359 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java @@ -179,7 +179,7 @@ protected void computeAxisValues(float min, float max) { interval = Math.floor(10 * intervalMagnitude); } - int n = centeringEnabled ? 1 : 0; + int n = mAxis.isCenterAxisLabelsEnabled() ? 1 : 0; // force label count if (mAxis.isForceLabelsEnabled()) { From 323329d8a6e66f75cabd9b37f738e9753d26e0fc Mon Sep 17 00:00:00 2001 From: PhilJay Date: Sun, 2 Oct 2016 14:36:55 +0200 Subject: [PATCH 089/291] Extend test cases --- .../github/mikephil/charting/data/DataSet.java | 8 -------- .../charting/highlight/ChartHighlighter.java | 2 +- .../mikephil/charting/test/DataSetTest.java | 16 ++++++++++++++++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java index 7496a50396..45e5917dd9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -339,14 +339,6 @@ public int getEntryIndex(float xValue, Rounding rounding) { return high; } - /** - * Returns all Entry objects at the given xIndex. INFORMATION: This method - * does calculations at runtime. Do not over-use in performance critical - * situations. - * - * @param xValue - * @return - */ @Override public List getEntriesForXValue(float xValue) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java index c4a1716596..a7a3ca7eb5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java @@ -138,7 +138,7 @@ protected List getHighlightsAtXValue(float xVal, float x, float y) { IDataSet dataSet = data.getDataSetByIndex(i); - // dont include datasets that cannot be highlighted + // don't include DataSets that cannot be highlighted if (!dataSet.isHighlightEnabled()) continue; diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java index 2311dc7838..8d3843e5ce 100644 --- a/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java @@ -218,5 +218,21 @@ public void testGetEntryForXValueWithDuplicates() { closest = set.getEntryForXValue(7, DataSet.Rounding.CLOSEST); assertEquals(7, closest.getX(), 0.01f); assertEquals(100, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(4f, DataSet.Rounding.CLOSEST); + assertEquals(4, closest.getX(), 0.01f); + assertEquals(60, closest.getY(), 0.01f); + + List entries = set.getEntriesForXValue(4f); + assertEquals(2, entries.size()); + assertEquals(60, entries.get(0).getY(), 0.01f); + assertEquals(70, entries.get(1).getY(), 0.01f); + + entries = set.getEntriesForXValue(3.5f); + assertEquals(0, entries.size()); + + entries = set.getEntriesForXValue(2f); + assertEquals(1, entries.size()); + assertEquals(30, entries.get(0).getY(), 0.01f); } } From 3398cf4462bc7f49bb1156ef22040e8a0d63369f Mon Sep 17 00:00:00 2001 From: PhilJay Date: Sun, 2 Oct 2016 15:14:09 +0200 Subject: [PATCH 090/291] Refactoring, prepare example for testing highlight --- .../mpchartexample/LineChartActivity2.java | 43 +++++++++++++------ .../mikephil/charting/data/DataSet.java | 3 ++ .../charting/renderer/RadarChartRenderer.java | 4 +- .../charting/utils/ColorTemplate.java | 9 +++- 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 72977c26fa..79f40c4e07 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -2,7 +2,6 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; -import android.graphics.Paint; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -16,7 +15,6 @@ import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.Legend.LegendForm; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; @@ -243,6 +241,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } case R.id.animateX: { mChart.animateX(3000); + //mChart.highlightValue(9.7f, 1, false); break; } case R.id.animateY: { @@ -287,30 +286,39 @@ private void setData(int count, float range) { for (int i = 0; i < count; i++) { float mult = range / 2f; - float val = (float) (Math.random() * mult) + 50;// + (float) - // ((mult * - // 0.1) / 10); + float val = (float) (Math.random() * mult) + 50; yVals1.add(new Entry(i, val)); } ArrayList yVals2 = new ArrayList(); - for (int i = 0; i < count; i++) { + for (int i = 0; i < count-1; i++) { float mult = range; - float val = (float) (Math.random() * mult) + 450;// + (float) - // ((mult * - // 0.1) / 10); + float val = (float) (Math.random() * mult) + 450; yVals2.add(new Entry(i, val)); +// if(i == 10) { +// yVals2.add(new Entry(i, val + 50)); +// } + } + + ArrayList yVals3 = new ArrayList(); + + for (int i = 0; i < count; i++) { + float mult = range; + float val = (float) (Math.random() * mult) + 500; + yVals3.add(new Entry(i, val)); } - LineDataSet set1, set2; + LineDataSet set1, set2, set3; if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set1 = (LineDataSet) mChart.getData().getDataSetByIndex(0); set2 = (LineDataSet) mChart.getData().getDataSetByIndex(1); + set3 = (LineDataSet) mChart.getData().getDataSetByIndex(2); set1.setValues(yVals1); set2.setValues(yVals2); + set3.setValues(yVals3); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { @@ -344,12 +352,19 @@ private void setData(int count, float range) { set2.setHighLightColor(Color.rgb(244, 117, 117)); //set2.setFillFormatter(new MyFillFormatter(900f)); - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets - dataSets.add(set2); + set3 = new LineDataSet(yVals3, "DataSet 3"); + set3.setAxisDependency(AxisDependency.RIGHT); + set3.setColor(Color.YELLOW); + set3.setCircleColor(Color.WHITE); + set3.setLineWidth(2f); + set3.setCircleRadius(3f); + set3.setFillAlpha(65); + set3.setFillColor(ColorTemplate.colorWithAlpha(Color.YELLOW, 200)); + set3.setDrawCircleHole(false); + set3.setHighLightColor(Color.rgb(244, 117, 117)); // create a data object with the datasets - LineData data = new LineData(dataSets); + LineData data = new LineData(set1, set2, set3); data.setValueTextColor(Color.WHITE); data.setValueTextSize(9f); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java index 45e5917dd9..adc0083ec0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -351,11 +351,14 @@ public List getEntriesForXValue(float xValue) { int m = (high + low) / 2; T entry = mValues.get(m); + // if we have a match if (xValue == entry.getX()) { while (m > 0 && mValues.get(m - 1).getX() == xValue) m--; high = mValues.size(); + + // loop over all "equal" entries for (; m < high; m++) { entry = mValues.get(m); if (entry.getX() == xValue) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java index dd0918d559..6c05b08dca 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java @@ -18,8 +18,6 @@ import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; -import java.util.List; - public class RadarChartRenderer extends LineRadarRenderer { protected RadarChart mChart; @@ -306,7 +304,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } if (set.getHighlightCircleStrokeAlpha() < 255) { - strokeColor = ColorTemplate.getColorWithAlphaComponent(strokeColor, set.getHighlightCircleStrokeAlpha()); + strokeColor = ColorTemplate.colorWithAlpha(strokeColor, set.getHighlightCircleStrokeAlpha()); } drawHighlightCircle(c, diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ColorTemplate.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ColorTemplate.java index 106671ad13..4d9c1de790 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ColorTemplate.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ColorTemplate.java @@ -78,7 +78,14 @@ public static int getHoloBlue() { return Color.rgb(51, 181, 229); } - public static int getColorWithAlphaComponent(int color, int alpha) { + /** + * Sets the alpha component of the given color. + * + * @param color + * @param alpha 0 - 255 + * @return + */ + public static int colorWithAlpha(int color, int alpha) { return (color & 0xffffff) | ((alpha & 0xff) << 24); } From 463ef829d7084504e4891ae1ab59cc8a43b5617a Mon Sep 17 00:00:00 2001 From: nielsz Date: Sat, 8 Oct 2016 12:18:13 +0200 Subject: [PATCH 091/291] Fix typo --- .../main/java/com/github/mikephil/charting/charts/Chart.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index cd6bb46d39..443c70092b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -355,14 +355,14 @@ public boolean isEmpty() { public abstract void notifyDataSetChanged(); /** - * calculates the offsets of the chart to the border depending on the + * Calculates the offsets of the chart to the border depending on the * position of an eventual legend or depending on the length of the y-axis * and x-axis labels and their position */ protected abstract void calculateOffsets(); /** - * calcualtes the y-min and y-max value and the y-delta and x-delta value + * Calculates the y-min and y-max value and the y-delta and x-delta value */ protected abstract void calcMinMax(); From 815dee86a097bb843447eedb427269b8c8b69bf0 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sat, 8 Oct 2016 21:47:31 +0300 Subject: [PATCH 092/291] Corrected calcMinMaxY for autoScaleMinMax --- .../mikephil/charting/data/CandleDataSet.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java index 13bf6062d4..7574b78b27 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java @@ -112,6 +112,22 @@ protected void calcMinMax(CandleEntry e) { calcMinMaxX(e); } + @Override + protected void calcMinMaxY(CandleEntry e) { + + if (e.getHigh() < mYMin) + mYMin = e.getHigh(); + + if (e.getHigh() > mYMax) + mYMax = e.getHigh(); + + if (e.getLow() < mYMin) + mYMin = e.getLow(); + + if (e.getLow() > mYMax) + mYMax = e.getLow(); + } + /** * Sets the space that is left out on the left and right side of each * candle, default 0.1f (10%), max 0.45f, min 0f From b77063d8185f73631b1fda75cc17e1ddd0984678 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sat, 8 Oct 2016 22:24:08 +0300 Subject: [PATCH 093/291] Feature: spaceMin/spaceMax for axis --- .../charting/components/AxisBase.java | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index fc92dc8fb7..997612eb53 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -112,6 +112,16 @@ public abstract class AxisBase extends ComponentBase { */ protected boolean mDrawLimitLineBehindData = false; + /** + * Extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum` + */ + protected float mSpaceMin = 0.f; + + /** + * Extra spacing for `axisMaximum` to be added to automatically calculated `axisMaximum` + */ + protected float mSpaceMax = 0.f; + /** * flag indicating that the axis-min value has been customized */ @@ -705,8 +715,8 @@ public void setAxisMaxValue(float max) { public void calculate(float dataMin, float dataMax) { // if custom, use value as is, else use data value - float min = mCustomAxisMin ? mAxisMinimum : dataMin; - float max = mCustomAxisMax ? mAxisMaximum : dataMax; + float min = mCustomAxisMin ? mAxisMinimum : (dataMin - mSpaceMin); + float max = mCustomAxisMax ? mAxisMaximum : (dataMax + mSpaceMax); // temporary range (before calculations) float range = Math.abs(max - min); @@ -723,4 +733,36 @@ public void calculate(float dataMin, float dataMax) { // actual range this.mAxisRange = Math.abs(max - min); } + + /** + * Gets extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum` + */ + public float getSpaceMin() + { + return mSpaceMin; + } + + /** + * Sets extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum` + */ + public void setSpaceMin(float mSpaceMin) + { + this.mSpaceMin = mSpaceMin; + } + + /** + * Gets extra spacing for `axisMaximum` to be added to automatically calculated `axisMaximum` + */ + public float getSpaceMax() + { + return mSpaceMax; + } + + /** + * Sets extra spacing for `axisMaximum` to be added to automatically calculated `axisMaximum` + */ + public void setSpaceMax(float mSpaceMax) + { + this.mSpaceMax = mSpaceMax; + } } From 8d655962661f4079d3f49777f14534f298182bf6 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sat, 8 Oct 2016 22:25:44 +0300 Subject: [PATCH 094/291] Default spaceMin/spaceMax for bar charts This avoids having to set custom axisMinimum/axisMaximum, or offsetting x --- .../mpchartexample/BarChartActivity.java | 5 +---- .../mpchartexample/BarChartPositiveNegative.java | 12 +++++------- .../github/mikephil/charting/charts/BarChart.java | 3 +++ 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 5eb67e7c41..650d6183c9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -231,15 +231,12 @@ private void setData(int count, float range) { float start = 0f; - mChart.getXAxis().setAxisMinimum(start); - mChart.getXAxis().setAxisMaximum(start + count + 2); - ArrayList yVals1 = new ArrayList(); for (int i = (int) start; i < start + count + 1; i++) { float mult = (range + 1); float val = (float) (Math.random() * mult); - yVals1.add(new BarEntry(i + 1f, val)); + yVals1.add(new BarEntry(i, val)); } BarDataSet set1; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 6fe6bc8e8c..10a7bb9fd9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -61,8 +61,6 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setDrawAxisLine(false); xAxis.setTextColor(Color.LTGRAY); xAxis.setTextSize(13f); - xAxis.setAxisMinimum(0f); - xAxis.setAxisMaximum(5f); xAxis.setLabelCount(5); xAxis.setCenterAxisLabels(true); xAxis.setGranularity(1f); @@ -81,11 +79,11 @@ protected void onCreate(Bundle savedInstanceState) { // THIS IS THE ORIGINAL DATA YOU WANT TO PLOT final List data = new ArrayList<>(); - data.add(new Data(0.5f, -224.1f, "12-29")); - data.add(new Data(1.5f, 238.5f, "12-30")); - data.add(new Data(2.5f, 1280.1f, "12-31")); - data.add(new Data(3.5f, -442.3f, "01-01")); - data.add(new Data(4.5f, -2280.1f, "01-02")); + data.add(new Data(0f, -224.1f, "12-29")); + data.add(new Data(1f, 238.5f, "12-30")); + data.add(new Data(2f, 1280.1f, "12-31")); + data.add(new Data(3f, -442.3f, "01-01")); + data.add(new Data(4f, -2280.1f, "01-02")); xAxis.setValueFormatter(new IAxisValueFormatter() { @Override 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 index a805815952..b048a4fffa 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java @@ -57,6 +57,9 @@ protected void init() { mRenderer = new BarChartRenderer(this, mAnimator, mViewPortHandler); setHighlighter(new BarHighlighter(this)); + + getXAxis().setSpaceMin(0.5f); + getXAxis().setSpaceMax(0.5f); } @Override From e10628958f7ca17ab415e07f220dcaa44c7ad08e Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 10 Oct 2016 16:31:53 +0300 Subject: [PATCH 095/291] Improved highlight for scatter/bubble, and fixes highlightValueWithX --- .../DynamicalAddingActivity.java | 2 +- .../mikephil/charting/charts/Chart.java | 52 ++++++++---- .../mikephil/charting/charts/PieChart.java | 2 +- .../mikephil/charting/data/BaseDataSet.java | 2 +- .../mikephil/charting/data/ChartData.java | 6 +- .../mikephil/charting/data/DataSet.java | 64 +++++++++++---- .../charting/highlight/BarHighlighter.java | 2 +- .../charting/highlight/ChartHighlighter.java | 40 +++++++--- .../highlight/CombinedHighlighter.java | 13 ++- .../charting/highlight/Highlight.java | 5 +- .../highlight/HorizontalBarHighlighter.java | 34 +++++++- .../interfaces/datasets/IDataSet.java | 39 +++++---- .../charting/renderer/BarChartRenderer.java | 2 +- .../BarLineScatterCandleBubbleRenderer.java | 4 +- .../renderer/BubbleChartRenderer.java | 80 +++++++++---------- .../renderer/CandleStickChartRenderer.java | 2 +- .../charting/renderer/LineChartRenderer.java | 2 +- .../renderer/ScatterChartRenderer.java | 2 +- .../mikephil/charting/test/DataSetTest.java | 26 +++--- 19 files changed, 242 insertions(+), 137 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index 7d3e5f431f..2875b89d7e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -87,7 +87,7 @@ private void removeLastEntry() { if (set != null) { - Entry e = set.getEntryForXValue(set.getEntryCount() - 1); + Entry e = set.getEntryForXValue(set.getEntryCount() - 1, Float.NaN); data.removeEntry(e, 0); // or remove by index diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index cd6bb46d39..bcf722015e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -535,8 +535,8 @@ protected void setLastHighlighted(Highlight[] highs) { /** * Highlights the values at the given indices in the given DataSets. Provide * null or an empty array to undo all highlighting. This should be used to - * programmatically highlight values. This DOES NOT generate a callback to - * the OnChartValueSelectedListener. + * programmatically highlight values. + * This method *will not* call the listener. * * @param highs */ @@ -552,35 +552,59 @@ public void highlightValues(Highlight[] highs) { } /** - * Highlights the value at the given x-value in the given DataSet. Provide - * -1 as the dataSetIndex to undo all highlighting. This will trigger a callback to the OnChartValueSelectedListener. - * - * @param x - * @param dataSetIndex + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * This method will call the listener. + * @param x The x-value to highlight + * @param dataSetIndex The dataset index to search in */ public void highlightValue(float x, int dataSetIndex) { highlightValue(x, dataSetIndex, true); } /** - * Highlights the value at the given x-value in the given DataSet. Provide - * -1 as the dataSetIndex to undo all highlighting. - * - * @param x - * @param dataSetIndex + * Highlights the value at the given x-value and y-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * This method will call the listener. + * @param x The x-value to highlight + * @param y The y-value to highlight. Supply `NaN` for "any" + * @param dataSetIndex The dataset index to search in + */ + public void highlightValue(float x, float y, int dataSetIndex) { + highlightValue(x, y, dataSetIndex, true); + } + + /** + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * @param x The x-value to highlight + * @param dataSetIndex The dataset index to search in + * @param callListener Should the listener be called for this change */ public void highlightValue(float x, int dataSetIndex, boolean callListener) { + highlightValue(x, Float.NaN, dataSetIndex, callListener); + } + + /** + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * @param x The x-value to highlight + * @param y The y-value to highlight. Supply `NaN` for "any" + * @param dataSetIndex The dataset index to search in + * @param callListener Should the listener be called for this change + */ + public void highlightValue(float x, float y, int dataSetIndex, boolean callListener) { if (dataSetIndex < 0 || dataSetIndex >= mData.getDataSetCount()) { highlightValue(null, callListener); } else { - highlightValue(new Highlight(x, dataSetIndex), callListener); + highlightValue(new Highlight(x, y, dataSetIndex), callListener); } } /** * Highlights the values represented by the provided Highlight object - * This DOES NOT generate a callback to the OnChartValueSelectedListener. + * This method *will not* call the listener. * * @param highlight contains information about which entry should be highlighted */ 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 index db327fa8b9..660fd29bdc 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java @@ -328,7 +328,7 @@ public int getDataSetIndexForIndex(int xIndex) { List dataSets = mData.getDataSets(); for (int i = 0; i < dataSets.size(); i++) { - if (dataSets.get(i).getEntryForXValue(xIndex) != null) + if (dataSets.get(i).getEntryForXValue(xIndex, Float.NaN) != null) return i; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java index f3107ebed7..89ed9b4001 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -432,7 +432,7 @@ public boolean removeLast() { @Override public boolean removeEntryByXValue(float xValue) { - T e = getEntryForXValue(xValue); + T e = getEntryForXValue(xValue, Float.NaN); return removeEntry(e); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java index 1ffc1dbcc4..60d89f4753 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java @@ -338,7 +338,7 @@ public Entry getEntryForHighlight(Highlight highlight) { if (highlight.getDataSetIndex() >= mDataSets.size()) return null; else { - return mDataSets.get(highlight.getDataSetIndex()).getEntryForXValue(highlight.getX()); + return mDataSets.get(highlight.getDataSetIndex()).getEntryForXValue(highlight.getX(), highlight.getY()); } } @@ -550,7 +550,7 @@ public boolean removeEntry(float xValue, int dataSetIndex) { return false; IDataSet dataSet = mDataSets.get(dataSetIndex); - Entry e = dataSet.getEntryForXValue(xValue); + Entry e = dataSet.getEntryForXValue(xValue, Float.NaN); if (e == null) return false; @@ -575,7 +575,7 @@ public T getDataSetForEntry(Entry e) { T set = mDataSets.get(i); for (int j = 0; j < set.getEntryCount(); j++) { - if (e.equalTo(set.getEntryForXValue(e.getX()))) + if (e.equalTo(set.getEntryForXValue(e.getX(), e.getY()))) return set; } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java index adc0083ec0..a96cfdcf66 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -83,8 +83,8 @@ public void calcMinMaxY(float fromX, float toX) { mYMax = -Float.MAX_VALUE; mYMin = Float.MAX_VALUE; - int indexFrom = getEntryIndex(fromX, Rounding.DOWN); - int indexTo = getEntryIndex(toX, Rounding.UP); + int indexFrom = getEntryIndex(fromX, Float.NaN, Rounding.DOWN); + int indexTo = getEntryIndex(toX, Float.NaN, Rounding.UP); for (int i = indexFrom; i <= indexTo; i++) { @@ -213,7 +213,7 @@ public void addEntryOrdered(T e) { calcMinMax(e); if (mValues.size() > 0 && mValues.get(mValues.size() - 1).getX() > e.getX()) { - int closestIndex = getEntryIndex(e.getX(), Rounding.UP); + int closestIndex = getEntryIndex(e.getX(), e.getY(), Rounding.UP); mValues.add(closestIndex, e); } else { mValues.add(e); @@ -268,17 +268,17 @@ public int getEntryIndex(Entry e) { } @Override - public T getEntryForXValue(float xValue, Rounding rounding) { + public T getEntryForXValue(float xValue, float closestToY, Rounding rounding) { - int index = getEntryIndex(xValue, rounding); + int index = getEntryIndex(xValue, closestToY, rounding); if (index > -1) return mValues.get(index); return null; } @Override - public T getEntryForXValue(float xValue) { - return getEntryForXValue(xValue, Rounding.CLOSEST); + public T getEntryForXValue(float xValue, float closestToY) { + return getEntryForXValue(xValue, closestToY, Rounding.CLOSEST); } @Override @@ -287,13 +287,14 @@ public T getEntryForIndex(int index) { } @Override - public int getEntryIndex(float xValue, Rounding rounding) { + public int getEntryIndex(float xValue, float closestToY, Rounding rounding) { if (mValues == null || mValues.isEmpty()) return -1; int low = 0; int high = mValues.size() - 1; + int closest = high; while (low < high) { int m = (low + high) / 2; @@ -321,22 +322,53 @@ public int getEntryIndex(float xValue, Rounding rounding) { low = m + 1; } } + + closest = high; } - if (high != -1) { - float closestXValue = mValues.get(high).getX(); + if (closest != -1) { + float closestXValue = mValues.get(closest).getX(); if (rounding == Rounding.UP) { - if (closestXValue < xValue && high < mValues.size() - 1) { - ++high; + // If rounding up, and found x-value is lower than specified x, and we can go upper... + if (closestXValue < xValue && closest < mValues.size() - 1) { + ++closest; } } else if (rounding == Rounding.DOWN) { - if (closestXValue > xValue && high > 0) { - --high; + // If rounding down, and found x-value is upper than specified x, and we can go lower... + if (closestXValue > xValue && closest > 0) { + --closest; } } + + // Search by closest to y-value + if (!Float.isNaN(closestToY)) { + while (closest > 0 && mValues.get(closest - 1).getX() == closestXValue) + closest -= 1; + + float closestYValue = mValues.get(closest).getY(); + int closestYIndex = closest; + + while (true) { + closest += 1; + if (closest >= mValues.size()) + break; + + final Entry value = mValues.get(closest); + + if (value.getX() != closestXValue) + break; + + if (Math.abs(value.getY() - closestToY) < Math.abs(closestYValue - closestToY)) { + closestYValue = closestToY; + closestYIndex = closest; + } + } + + closest = closestYIndex; + } } - return high; + return closest; } @Override @@ -382,7 +414,7 @@ public List getEntriesForXValue(float xValue) { /** * Determines how to round DataSet index values for - * {@link DataSet#getEntryIndex(float, Rounding)} DataSet.getEntryIndex()} + * {@link DataSet#getEntryIndex(float, float, Rounding)} DataSet.getEntryIndex()} * when an exact x-index is not found. */ public enum Rounding { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java index b60eeabfe9..af83a4539f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java @@ -54,7 +54,7 @@ public Highlight getHighlight(float x, float y) { */ public Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal, float yVal) { - BarEntry entry = set.getEntryForXValue(xVal); + BarEntry entry = set.getEntryForXValue(xVal, yVal); if (entry == null) return null; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java index a7a3ca7eb5..f889bf19d4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java @@ -142,18 +142,14 @@ protected List getHighlightsAtXValue(float xVal, float x, float y) { if (!dataSet.isHighlightEnabled()) continue; - Highlight high = buildHighlight(dataSet, i, xVal, DataSet.Rounding.CLOSEST); - - if(high != null) - mHighlightBuffer.add(high); - //vals.add(buildHighlight(dataSet, i, xVal, DataSet.Rounding.DOWN)); + mHighlightBuffer.addAll(buildHighlights(dataSet, i, xVal, DataSet.Rounding.CLOSEST)); } return mHighlightBuffer; } /** - * Returns the Highlight object corresponding to the selected xValue and dataSetIndex. + * An array of `Highlight` objects corresponding to the selected xValue and dataSetIndex. * * @param set * @param dataSetIndex @@ -161,16 +157,36 @@ protected List getHighlightsAtXValue(float xVal, float x, float y) { * @param rounding * @return */ - protected Highlight buildHighlight(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + protected List buildHighlights(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + + ArrayList highlights = new ArrayList<>(); + + //noinspection unchecked + List entries = set.getEntriesForXValue(xVal); + if (entries.size() == 0) { + // Try to find closest x-value and take all entries for that x-value + final Entry closest = set.getEntryForXValue(xVal, Float.NaN, rounding); + if (closest != null) + { + //noinspection unchecked + entries = set.getEntriesForXValue(closest.getX()); + } + } - final Entry e = set.getEntryForXValue(xVal, rounding); + if (entries.size() == 0) + return highlights; - if (e == null) - return null; + for (Entry e : entries) { + MPPointD pixels = mChart.getTransformer( + set.getAxisDependency()).getPixelForValues(e.getX(), e.getY()); - MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), e.getY()); + highlights.add(new Highlight( + e.getX(), e.getY(), + (float) pixels.x, (float) pixels.y, + dataSetIndex, set.getAxisDependency())); + } - return new Highlight(e.getX(), e.getY(), (float) pixels.x, (float) pixels.y, dataSetIndex, set.getAxisDependency()); + return highlights; } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java index f75b0e925d..76788af6e0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java @@ -57,13 +57,12 @@ protected List getHighlightsAtXValue(float xVal, float x, float y) { if (!dataSet.isHighlightEnabled()) continue; - Highlight s1 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.CLOSEST); - s1.setDataIndex(i); - mHighlightBuffer.add(s1); - -// Highlight s2 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.DOWN); -// s2.setDataIndex(i); -// vals.add(s2); + List highs = buildHighlights(dataSet, j, xVal, DataSet.Rounding.CLOSEST); + for (Highlight high : highs) + { + high.setDataIndex(i); + mHighlightBuffer.add(high); + } } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java index 5599882ce7..f1d542d80b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java @@ -60,13 +60,14 @@ public class Highlight { */ private float mDrawY; - public Highlight(float x, int dataSetIndex) { + public Highlight(float x, float y, int dataSetIndex) { this.mX = x; + this.mY = y; this.mDataSetIndex = dataSetIndex; } public Highlight(float x, int dataSetIndex, int stackIndex) { - this(x, dataSetIndex); + this(x, Float.NaN, dataSetIndex); this.mStackIndex = stackIndex; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java index 60ec66ba9d..d96298e07d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java @@ -8,6 +8,9 @@ import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.MPPointD; +import java.util.ArrayList; +import java.util.List; + /** * Created by Philipp Jahoda on 22/07/15. */ @@ -43,13 +46,36 @@ public Highlight getHighlight(float x, float y) { } @Override - protected Highlight buildHighlight(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + protected List buildHighlights(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + + ArrayList highlights = new ArrayList<>(); + + //noinspection unchecked + List entries = set.getEntriesForXValue(xVal); + if (entries.size() == 0) { + // Try to find closest x-value and take all entries for that x-value + final Entry closest = set.getEntryForXValue(xVal, Float.NaN, rounding); + if (closest != null) + { + //noinspection unchecked + entries = set.getEntriesForXValue(closest.getX()); + } + } - final Entry e = set.getEntryForXValue(xVal, rounding); + if (entries.size() == 0) + return highlights; - MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getY(), e.getX()); + for (Entry e : entries) { + MPPointD pixels = mChart.getTransformer( + set.getAxisDependency()).getPixelForValues(e.getY(), e.getX()); + + highlights.add(new Highlight( + e.getX(), e.getY(), + (float) pixels.x, (float) pixels.y, + dataSetIndex, set.getAxisDependency())); + } - return new Highlight(e.getX(), e.getY(), (float) pixels.x, (float) pixels.y, dataSetIndex, set.getAxisDependency()); + return highlights; } @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index 337adc35d2..5f922ba569 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -70,28 +70,36 @@ public interface IDataSet { /** * Returns the first Entry object found at the given x-value with binary - * search. If the no Entry at the specified x-value is found, this method - * returns the Entry at the x-value according to the rounding. + * search. + * If the no Entry at the specified x-value is found, this method + * returns the Entry at the closest x-value according to the rounding. * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xValue - * @param rounding determine to round up/down/closest if there is no Entry matching the provided x-index + * @param xValue the x-value + * @param closestToY If there are multiple y-values for the specified x-value, + * @param rounding determine whether to round up/down/closest + * if there is no Entry matching the provided x-value * @return + * + * */ - T getEntryForXValue(float xValue, DataSet.Rounding rounding); + T getEntryForXValue(float xValue, float closestToY, DataSet.Rounding rounding); /** * Returns the first Entry object found at the given x-value with binary - * search. If the no Entry at the specified x-value is found, this method - * returns the index at the closest x-value. + * search. + * If the no Entry at the specified x-value is found, this method + * returns the Entry at the closest x-value. * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xValue + * + * @param xValue the x-value + * @param closestToY If there are multiple y-values for the specified x-value, * @return */ - T getEntryForXValue(float xValue); + T getEntryForXValue(float xValue, float closestToY); /** * Returns all Entry objects found at the given x-value with binary @@ -114,16 +122,19 @@ public interface IDataSet { /** * Returns the first Entry index found at the given x-value with binary - * search. If the no Entry at the specified x-value is found, this method - * returns the Entry at the closest x-value. + * search. + * If the no Entry at the specified x-value is found, this method + * returns the Entry at the closest x-value according to the rounding. * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xValue - * @param rounding determine to round up/down/closest if there is no Entry matching the provided x-index + * @param xValue the x-value + * @param closestToY If there are multiple y-values for the specified x-value, + * @param rounding determine whether to round up/down/closest + * if there is no Entry matching the provided x-value * @return */ - int getEntryIndex(float xValue, DataSet.Rounding rounding); + int getEntryIndex(float xValue, float closestToY, DataSet.Rounding rounding); /** * Returns the position of the provided entry in the DataSets Entry array. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index cf41858f46..ad12feae45 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -341,7 +341,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - BarEntry e = set.getEntryForXValue(high.getX()); + BarEntry e = set.getEntryForXValue(high.getX(), high.getY()); if (!isInBoundsX(e, set)) continue; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java index 5b58a01deb..e7a9c77f52 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java @@ -85,8 +85,8 @@ public void set(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCan float low = chart.getLowestVisibleX(); float high = chart.getHighestVisibleX(); - Entry entryFrom = dataSet.getEntryForXValue(low, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXValue(high, DataSet.Rounding.UP); + Entry entryFrom = dataSet.getEntryForXValue(low, Float.NaN, DataSet.Rounding.DOWN); + Entry entryTo = dataSet.getEntryForXValue(high, Float.NaN, DataSet.Rounding.UP); min = dataSet.getEntryIndex(entryFrom); max = dataSet.getEntryIndex(entryTo); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index f21f03d3e3..680e7810e8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -189,64 +189,60 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - // In bubble charts - it makes sense to have multiple bubbles on the same X value in the same dataset. - final List entries = set.getEntriesForXValue(high.getX()); + final BubbleEntry entry = set.getEntryForXValue(high.getX(), high.getY()); - for (BubbleEntry entry : entries) { - - if (entry.getY() != high.getY()) - continue; + if (entry.getY() != high.getY()) + continue; - if (!isInBoundsX(entry, set)) - continue; + if (!isInBoundsX(entry, set)) + continue; - Transformer trans = mChart.getTransformer(set.getAxisDependency()); + Transformer trans = mChart.getTransformer(set.getAxisDependency()); - sizeBuffer[0] = 0f; - sizeBuffer[2] = 1f; + sizeBuffer[0] = 0f; + sizeBuffer[2] = 1f; - trans.pointValuesToPixel(sizeBuffer); + trans.pointValuesToPixel(sizeBuffer); - boolean normalizeSize = set.isNormalizeSizeEnabled(); + boolean normalizeSize = set.isNormalizeSizeEnabled(); - // calcualte the full width of 1 step on the x-axis - final float maxBubbleWidth = Math.abs(sizeBuffer[2] - sizeBuffer[0]); - final float maxBubbleHeight = Math.abs( - mViewPortHandler.contentBottom() - mViewPortHandler.contentTop()); - final float referenceSize = Math.min(maxBubbleHeight, maxBubbleWidth); + // calcualte the full width of 1 step on the x-axis + final float maxBubbleWidth = Math.abs(sizeBuffer[2] - sizeBuffer[0]); + final float maxBubbleHeight = Math.abs( + mViewPortHandler.contentBottom() - mViewPortHandler.contentTop()); + final float referenceSize = Math.min(maxBubbleHeight, maxBubbleWidth); - pointBuffer[0] = entry.getX(); - pointBuffer[1] = (entry.getY()) * phaseY; - trans.pointValuesToPixel(pointBuffer); + pointBuffer[0] = entry.getX(); + pointBuffer[1] = (entry.getY()) * phaseY; + trans.pointValuesToPixel(pointBuffer); - high.setDraw(pointBuffer[0], pointBuffer[1]); + high.setDraw(pointBuffer[0], pointBuffer[1]); - float shapeHalf = getShapeSize(entry.getSize(), - set.getMaxSize(), - referenceSize, - normalizeSize) / 2f; + float shapeHalf = getShapeSize(entry.getSize(), + set.getMaxSize(), + referenceSize, + normalizeSize) / 2f; - if (!mViewPortHandler.isInBoundsTop(pointBuffer[1] + shapeHalf) - || !mViewPortHandler.isInBoundsBottom(pointBuffer[1] - shapeHalf)) - continue; + if (!mViewPortHandler.isInBoundsTop(pointBuffer[1] + shapeHalf) + || !mViewPortHandler.isInBoundsBottom(pointBuffer[1] - shapeHalf)) + continue; - if (!mViewPortHandler.isInBoundsLeft(pointBuffer[0] + shapeHalf)) - continue; + if (!mViewPortHandler.isInBoundsLeft(pointBuffer[0] + shapeHalf)) + continue; - if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) - break; + if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) + break; - final int originalColor = set.getColor((int) entry.getX()); + final int originalColor = set.getColor((int) entry.getX()); - Color.RGBToHSV(Color.red(originalColor), Color.green(originalColor), - Color.blue(originalColor), _hsvBuffer); - _hsvBuffer[2] *= 0.5f; - final int color = Color.HSVToColor(Color.alpha(originalColor), _hsvBuffer); + Color.RGBToHSV(Color.red(originalColor), Color.green(originalColor), + Color.blue(originalColor), _hsvBuffer); + _hsvBuffer[2] *= 0.5f; + final int color = Color.HSVToColor(Color.alpha(originalColor), _hsvBuffer); - mHighlightPaint.setColor(color); - mHighlightPaint.setStrokeWidth(set.getHighlightCircleWidth()); - c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mHighlightPaint); - } + mHighlightPaint.setColor(color); + mHighlightPaint.setStrokeWidth(set.getHighlightCircleWidth()); + c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mHighlightPaint); } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index 8809c0f44b..efa4c5d012 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -313,7 +313,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - CandleEntry e = set.getEntryForXValue(high.getX()); + CandleEntry e = set.getEntryForXValue(high.getX(), high.getY()); if (!isInBoundsX(e, set)) continue; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 9cab2208fb..1f46e5b7ef 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -670,7 +670,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - Entry e = set.getEntryForXValue(high.getX()); + Entry e = set.getEntryForXValue(high.getX(), high.getY()); if (!isInBoundsX(e, set)) continue; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index 91dc40fc38..9c4fdac24e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -148,7 +148,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - Entry e = set.getEntryForXValue(high.getX()); + final Entry e = set.getEntryForXValue(high.getX(), high.getY()); if (!isInBoundsX(e, set)) continue; diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java index 8d3843e5ce..3be28d2a57 100644 --- a/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java @@ -150,31 +150,31 @@ public void testGetEntryForXValue() { ScatterDataSet set = new ScatterDataSet(entries, ""); - Entry closest = set.getEntryForXValue(17, DataSet.Rounding.CLOSEST); + Entry closest = set.getEntryForXValue(17, Float.NaN, DataSet.Rounding.CLOSEST); assertEquals(15, closest.getX(), 0.01f); assertEquals(5, closest.getY(), 0.01f); - closest = set.getEntryForXValue(17, DataSet.Rounding.DOWN); + closest = set.getEntryForXValue(17, Float.NaN, DataSet.Rounding.DOWN); assertEquals(15, closest.getX(), 0.01f); assertEquals(5, closest.getY(), 0.01f); - closest = set.getEntryForXValue(15, DataSet.Rounding.DOWN); + closest = set.getEntryForXValue(15, Float.NaN, DataSet.Rounding.DOWN); assertEquals(15, closest.getX(), 0.01f); assertEquals(5, closest.getY(), 0.01f); - closest = set.getEntryForXValue(14, DataSet.Rounding.DOWN); + closest = set.getEntryForXValue(14, Float.NaN, DataSet.Rounding.DOWN); assertEquals(10, closest.getX(), 0.01f); assertEquals(10, closest.getY(), 0.01f); - closest = set.getEntryForXValue(17, DataSet.Rounding.UP); + closest = set.getEntryForXValue(17, Float.NaN, DataSet.Rounding.UP); assertEquals(21, closest.getX(), 0.01f); assertEquals(5, closest.getY(), 0.01f); - closest = set.getEntryForXValue(21, DataSet.Rounding.UP); + closest = set.getEntryForXValue(21, Float.NaN, DataSet.Rounding.UP); assertEquals(21, closest.getX(), 0.01f); assertEquals(5, closest.getY(), 0.01f); - closest = set.getEntryForXValue(21, DataSet.Rounding.CLOSEST); + closest = set.getEntryForXValue(21, Float.NaN, DataSet.Rounding.CLOSEST); assertEquals(21, closest.getX(), 0.01f); assertEquals(5, closest.getY(), 0.01f); } @@ -199,27 +199,27 @@ public void testGetEntryForXValueWithDuplicates() { ScatterDataSet set = new ScatterDataSet(values, ""); - Entry closest = set.getEntryForXValue(0, DataSet.Rounding.CLOSEST); + Entry closest = set.getEntryForXValue(0, Float.NaN, DataSet.Rounding.CLOSEST); assertEquals(0, closest.getX(), 0.01f); assertEquals(10, closest.getY(), 0.01f); - closest = set.getEntryForXValue(5, DataSet.Rounding.CLOSEST); + closest = set.getEntryForXValue(5, Float.NaN, DataSet.Rounding.CLOSEST); assertEquals(5, closest.getX(), 0.01f); assertEquals(80, closest.getY(), 0.01f); - closest = set.getEntryForXValue(5.4f, DataSet.Rounding.CLOSEST); + closest = set.getEntryForXValue(5.4f, Float.NaN, DataSet.Rounding.CLOSEST); assertEquals(5, closest.getX(), 0.01f); assertEquals(80, closest.getY(), 0.01f); - closest = set.getEntryForXValue(4.6f, DataSet.Rounding.CLOSEST); + closest = set.getEntryForXValue(4.6f, Float.NaN, DataSet.Rounding.CLOSEST); assertEquals(5, closest.getX(), 0.01f); assertEquals(80, closest.getY(), 0.01f); - closest = set.getEntryForXValue(7, DataSet.Rounding.CLOSEST); + closest = set.getEntryForXValue(7, Float.NaN, DataSet.Rounding.CLOSEST); assertEquals(7, closest.getX(), 0.01f); assertEquals(100, closest.getY(), 0.01f); - closest = set.getEntryForXValue(4f, DataSet.Rounding.CLOSEST); + closest = set.getEntryForXValue(4f, Float.NaN, DataSet.Rounding.CLOSEST); assertEquals(4, closest.getX(), 0.01f); assertEquals(60, closest.getY(), 0.01f); From 555176bcdbaf0e92dafe8218789b57e3a20c060c Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 10 Oct 2016 17:20:05 +0300 Subject: [PATCH 096/291] Avoid crash when dataset is empty --- .../charting/renderer/BarLineScatterCandleBubbleRenderer.java | 4 ++-- .../java/com/github/mikephil/charting/utils/Transformer.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java index e7a9c77f52..7b48244688 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java @@ -88,8 +88,8 @@ public void set(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCan Entry entryFrom = dataSet.getEntryForXValue(low, Float.NaN, DataSet.Rounding.DOWN); Entry entryTo = dataSet.getEntryForXValue(high, Float.NaN, DataSet.Rounding.UP); - min = dataSet.getEntryIndex(entryFrom); - max = dataSet.getEntryIndex(entryTo); + min = entryFrom == null ? 0 : dataSet.getEntryIndex(entryFrom); + max = entryTo == null ? 0 : dataSet.getEntryIndex(entryTo); range = (int) ((max - min) * phaseX); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java index 6848bc58d1..10377398c7 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java @@ -171,7 +171,7 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY public float[] generateTransformedValuesLine(ILineDataSet data, float phaseX, float phaseY, int from, int to) { - final int count = (int) ((to - from) * phaseX + 1) * 2; + final int count = (int) ((to - from) * phaseX) * 2; if (valuePointsForGenerateTransformedValuesLine.length != count) { valuePointsForGenerateTransformedValuesLine = new float[count]; From 8ed6dd24bf1f262789e191f4be7f21651bdbb44e Mon Sep 17 00:00:00 2001 From: PhilJay Date: Sun, 16 Oct 2016 19:09:42 +0200 Subject: [PATCH 097/291] Update gradle & android sdk --- MPChartExample/AndroidManifest.xml | 4 ++-- MPChartExample/build.gradle | 10 +++++----- MPChartLib/build.gradle | 4 ++-- build.gradle | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index ab730cd2bd..2a0d4d3cbd 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -1,12 +1,12 @@ + android:targetSdkVersion="24" /> Date: Tue, 18 Oct 2016 18:17:16 +0300 Subject: [PATCH 098/291] Default spaceMin/max for xAxis in candlestick charts --- .../com/github/mikephil/charting/charts/CandleStickChart.java | 3 +++ 1 file changed, 3 insertions(+) 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 index 61dff4bb61..fa36e3522f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java @@ -32,6 +32,9 @@ protected void init() { super.init(); mRenderer = new CandleStickChartRenderer(this, mAnimator, mViewPortHandler); + + getXAxis().setSpaceMin(0.5f); + getXAxis().setSpaceMax(0.5f); } @Override From 730e5d3d9b63eb75da3aef8bac1c71cfbdf96222 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 18 Oct 2016 18:27:21 +0300 Subject: [PATCH 099/291] Fixed last label of line chart not rendering --- .../com/github/mikephil/charting/utils/Transformer.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java index 10377398c7..0496bd0673 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java @@ -169,9 +169,10 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY * @return */ public float[] generateTransformedValuesLine(ILineDataSet data, - float phaseX, float phaseY, int from, int to) { + float phaseX, float phaseY, + int min, int max) { - final int count = (int) ((to - from) * phaseX) * 2; + final int count = ((int) ((max - min) * phaseX) + 1) * 2; if (valuePointsForGenerateTransformedValuesLine.length != count) { valuePointsForGenerateTransformedValuesLine = new float[count]; @@ -180,7 +181,7 @@ public float[] generateTransformedValuesLine(ILineDataSet data, for (int j = 0; j < count; j += 2) { - Entry e = data.getEntryForIndex(j / 2 + from); + Entry e = data.getEntryForIndex(j / 2 + min); if (e != null) { valuePoints[j] = e.getX(); From d65ffb88712e682e4ecdbee5cb0d706a327a91b6 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 18 Oct 2016 18:39:04 +0300 Subject: [PATCH 100/291] Fixed bar chart demo first value being empty --- .../mpchartexample/BarChartActivity.java | 2 +- .../mpchartexample/custom/DayAxisValueFormatter.java | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 650d6183c9..427c46756a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -229,7 +229,7 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - float start = 0f; + float start = 1f; ArrayList yVals1 = new ArrayList(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java index 6d58892398..59f9f8e3fd 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -107,19 +107,19 @@ private int determineMonth(int dayOfYear) { return Math.max(month, 0); } - private int determineDayOfMonth(int dayOfYear, int month) { + private int determineDayOfMonth(int days, int month) { int count = 0; - int days = 0; + int daysForMonths = 0; while (count < month) { - int year = determineYear(days); - days += getDaysForMonth(count % 12, year); + int year = determineYear(daysForMonths); + daysForMonths += getDaysForMonth(count % 12, year); count++; } - return dayOfYear - days; + return days - daysForMonths; } private int determineYear(int days) { From 0fec2ef06b25a675880358cbf0978995eea5dc20 Mon Sep 17 00:00:00 2001 From: PhilJay Date: Tue, 18 Oct 2016 22:40:45 +0200 Subject: [PATCH 101/291] Clear up grouped bar example --- .../res/layout/activity_barchart.xml | 8 +- .../BarChartActivityMultiDataset.java | 91 +++++++++---------- 2 files changed, 46 insertions(+), 53 deletions(-) diff --git a/MPChartExample/res/layout/activity_barchart.xml b/MPChartExample/res/layout/activity_barchart.xml index 83c812d93e..ccb26bb81c 100644 --- a/MPChartExample/res/layout/activity_barchart.xml +++ b/MPChartExample/res/layout/activity_barchart.xml @@ -35,7 +35,7 @@ + android:textAppearance="?android:attr/textAppearanceSmall" /> + android:textAppearance="?android:attr/textAppearanceSmall" /> diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index da2ba2c8bc..9377e0f1b2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -14,7 +14,6 @@ import com.github.mikephil.charting.charts.BarChart; import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; @@ -46,6 +45,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_barchart); tvX = (TextView) findViewById(R.id.tvXMax); + tvX.setTextSize(10); tvY = (TextView) findViewById(R.id.tvYMax); mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); @@ -59,7 +59,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.getDescription().setEnabled(false); // mChart.setDrawBorders(true); - + // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); @@ -83,14 +83,15 @@ protected void onCreate(Bundle savedInstanceState) { l.setDrawInside(true); l.setTypeface(mTfLight); l.setYOffset(0f); + l.setXOffset(10f); l.setYEntrySpace(0f); l.setTextSize(8f); - XAxis xl = mChart.getXAxis(); - xl.setTypeface(mTfLight); - xl.setGranularity(1f); - xl.setCenterAxisLabels(true); - xl.setValueFormatter(new IAxisValueFormatter() { + XAxis xAxis = mChart.getXAxis(); + xAxis.setTypeface(mTfLight); + xAxis.setGranularity(1f); + xAxis.setCenterAxisLabels(true); + xAxis.setValueFormatter(new IAxisValueFormatter() { @Override public String getFormattedValue(float value, AxisBase axis) { return String.valueOf((int) value); @@ -106,7 +107,7 @@ public int getDecimalDigits() { leftAxis.setTypeface(mTfLight); leftAxis.setValueFormatter(new LargeValueFormatter()); leftAxis.setDrawGridLines(false); - leftAxis.setSpaceTop(30f); + leftAxis.setSpaceTop(35f); leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) mChart.getAxisRight().setEnabled(false); @@ -145,13 +146,13 @@ public boolean onOptionsItemSelected(MenuItem item) { } case R.id.actionToggleBarBorders: { for (IBarDataSet set : mChart.getData().getDataSets()) - ((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + ((BarDataSet) set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); mChart.invalidate(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { + if (mChart.getData() != null) { mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); mChart.invalidate(); } @@ -171,7 +172,6 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.animateXY: { - mChart.animateXY(3000, 3000); break; } @@ -182,78 +182,73 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - float groupSpace = 0.04f; - float barSpace = 0.02f; // x3 dataset - float barWidth = 0.3f; // x3 dataset - // (0.3 + 0.02) * 3 + 0.04 = 1.00 -> interval per "group" + float groupSpace = 0.08f; + float barSpace = 0.03f; // x4 DataSet + float barWidth = 0.2f; // x4 DataSet + // (0.2 + 0.03) * 4 + 0.08 = 1.00 -> interval per "group" + int groupCount = mSeekBarX.getProgress() + 1; int startYear = 1980; - int endYear = startYear + mSeekBarX.getProgress(); + int endYear = startYear + groupCount; - tvX.setText(startYear + "\n-" + endYear); + tvX.setText(startYear + "-" + endYear); tvY.setText("" + (mSeekBarY.getProgress())); ArrayList yVals1 = new ArrayList(); ArrayList yVals2 = new ArrayList(); ArrayList yVals3 = new ArrayList(); + ArrayList yVals4 = new ArrayList(); - float mult = mSeekBarY.getProgress() * 100000f; + float randomMultiplier = mSeekBarY.getProgress() * 100000f; for (int i = startYear; i < endYear; i++) { - float val = (float) (Math.random() * mult) + 3; - yVals1.add(new BarEntry(i, val)); + yVals1.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + yVals2.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + yVals3.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + yVals4.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); } - for (int i = startYear; i < endYear; i++) { - float val = (float) (Math.random() * mult) + 3; - yVals2.add(new BarEntry(i, val)); - } - - for (int i = startYear; i < endYear; i++) { - float val = (float) (Math.random() * mult) + 3; - yVals3.add(new BarEntry(i, val)); - } + BarDataSet set1, set2, set3, set4; - BarDataSet set1, set2, set3; + if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { - if (mChart.getData() != null && - mChart.getData().getDataSetCount() > 0) { - set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); - set2 = (BarDataSet)mChart.getData().getDataSetByIndex(1); - set3 = (BarDataSet)mChart.getData().getDataSetByIndex(2); + set1 = (BarDataSet) mChart.getData().getDataSetByIndex(0); + set2 = (BarDataSet) mChart.getData().getDataSetByIndex(1); + set3 = (BarDataSet) mChart.getData().getDataSetByIndex(2); + set4 = (BarDataSet) mChart.getData().getDataSetByIndex(3); set1.setValues(yVals1); set2.setValues(yVals2); set3.setValues(yVals3); + set4.setValues(yVals4); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); + } else { - // create 3 datasets with different types + // create 4 DataSets set1 = new BarDataSet(yVals1, "Company A"); - // set1.setColors(ColorTemplate.createColors(getApplicationContext(), - // ColorTemplate.FRESH_COLORS)); set1.setColor(Color.rgb(104, 241, 175)); set2 = new BarDataSet(yVals2, "Company B"); set2.setColor(Color.rgb(164, 228, 251)); set3 = new BarDataSet(yVals3, "Company C"); set3.setColor(Color.rgb(242, 247, 158)); + set4 = new BarDataSet(yVals4, "Company D"); + set4.setColor(Color.rgb(255, 102, 0)); - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - dataSets.add(set2); - dataSets.add(set3); - - BarData data = new BarData(dataSets); + BarData data = new BarData(set1, set2, set3, set4); data.setValueFormatter(new LargeValueFormatter()); - - // add space between the dataset groups in percent of bar-width data.setValueTypeface(mTfLight); mChart.setData(data); } + // specify the width each bar should have mChart.getBarData().setBarWidth(barWidth); + + // restrict the x-axis range mChart.getXAxis().setAxisMinimum(startYear); - mChart.getXAxis().setAxisMaximum(mChart.getBarData().getGroupWidth(groupSpace, barSpace) * mSeekBarX.getProgress() + startYear); + + // barData.getGroupWith(...) is a helper that calculates the width each group needs based on the provided parameters + mChart.getXAxis().setAxisMaximum(startYear + mChart.getBarData().getGroupWidth(groupSpace, barSpace) * groupCount); mChart.groupBars(startYear, groupSpace, barSpace); mChart.invalidate(); } @@ -261,13 +256,11 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub - } @Override public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub - } @Override From 810c99c579aa99f2b99174b23380272291bd3931 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 19 Oct 2016 10:08:42 +0300 Subject: [PATCH 102/291] Make highlightFullBarEnabled feature work again --- .../mikephil/charting/charts/BarChart.java | 11 ++++++-- .../charting/charts/CombinedChart.java | 28 +++++++++++++++++++ .../charting/highlight/Highlight.java | 4 +-- 3 files changed, 39 insertions(+), 4 deletions(-) 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 index b048a4fffa..2ba15c9118 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java @@ -92,8 +92,15 @@ 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 - return getHighlighter().getHighlight(x, y); + } 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()); + } } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java index 65ce85ceb8..b1975b973e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java @@ -3,6 +3,7 @@ import android.content.Context; import android.util.AttributeSet; +import android.util.Log; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BubbleData; @@ -11,6 +12,7 @@ import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.highlight.CombinedHighlighter; +import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider; import com.github.mikephil.charting.renderer.CombinedChartRenderer; @@ -92,6 +94,32 @@ public void setData(CombinedData data) { mRenderer.initBuffers(); } + /** + * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch + * point + * inside the CombinedChart. + * + * @param x + * @param y + * @return + */ + @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()); + } + } + @Override public LineData getLineData() { if (mData == null) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java index f1d542d80b..032698d5e5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java @@ -78,7 +78,7 @@ public Highlight(float x, int dataSetIndex, int stackIndex) { * @param y the y-value of the highlighted value * @param dataSetIndex the index of the DataSet the highlighted value belongs to */ - protected Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, YAxis.AxisDependency axis) { + public Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, YAxis.AxisDependency axis) { this.mX = x; this.mY = y; this.mXPx = xPx; @@ -96,7 +96,7 @@ protected Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, YA * @param stackIndex references which value of a stacked-bar entry has been * selected */ - protected Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, int stackIndex, YAxis.AxisDependency axis) { + public Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, int stackIndex, YAxis.AxisDependency axis) { this(x, y, xPx, yPx, dataSetIndex, axis); this.mStackIndex = stackIndex; } From b618524a19512eef89af9d6d920857b15958710f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 19 Oct 2016 23:03:54 +0200 Subject: [PATCH 103/291] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 518240d305..989fca594d 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ If you are having questions or problems, you should: - **Review your code**. Make absolutely sure that everything is correct on your side. - Make sure you are using the **latest version** of the library. Check the [**release-section**](https://github.com/PhilJay/MPAndroidChart/releases). - - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.0-beta1/javadoc/) + - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.0/javadoc/) - Search or open questions on [**stackoverflow**](https://stackoverflow.com/search?q=mpandroidchart) with the `mpandroidchart` tag - Search [**known issues**](https://github.com/PhilJay/MPAndroidChart/issues) for your problem (open and closed) - Create new issues (please :fire: **search known issues before** :fire:, do not create duplicate issues) @@ -117,7 +117,7 @@ allprojects { ```gradle dependencies { - compile 'com.github.PhilJay:MPAndroidChart:v3.0.0-beta1' + compile 'com.github.PhilJay:MPAndroidChart:v3.0.0' } ``` @@ -132,7 +132,7 @@ dependencies { com.github.PhilJay MPAndroidChart - v3.0.0-beta1 + v3.0.0 ``` @@ -152,7 +152,7 @@ dependencies { Documentation ----- -For a **detailed documentation** :notebook_with_decorative_cover:, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.0-beta1/javadoc/). +For a **detailed documentation** :notebook_with_decorative_cover:, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.0/javadoc/). Furthermore, you can also rely on the [**MPChartExample**](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample) folder and check out the example code in that project. The corresponding application to the example project is also [**available in the Google PlayStore**](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample). From 3ce2016048601def85ca31dcc680c892852968d4 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 19 Oct 2016 23:09:03 +0200 Subject: [PATCH 104/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 989fca594d..ecbd2241a8 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ Features - Dragging / Panning (with touch-gesture) - Combined-Charts (line-, bar-, scatter-, candle-data) - Dual (separate) Axes - - Customizable Axes (both xPx- and yPx-axis) + - Customizable Axes (both x- and y-axis) - Highlighting values (with customizable popup-views) - Save chart to SD-Card (as image, or as .txt file) - Predefined color templates From 49b61e92a4b45b33f92a9ee77f94bcd36cbcfdf9 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 22 Oct 2016 15:54:26 +0200 Subject: [PATCH 105/291] Update README.md --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ecbd2241a8..d8fc9856be 100644 --- a/README.md +++ b/README.md @@ -122,17 +122,21 @@ dependencies { ``` **2. Maven** -- Add the following to your `pom.xml`: +- Add the following to the `` section of your `pom.xml`: + ```xml - jitpack.io - https://jitpack.io + jitpack.io + https://jitpack.io +``` +- Add the following to the `` section of your `pom.xml`: + ```xml - com.github.PhilJay - MPAndroidChart - v3.0.0 + com.github.PhilJay + MPAndroidChart + v3.0.0 ``` From 9b7cffa27420431fbcc481cab7bdffe1eb4acdbb Mon Sep 17 00:00:00 2001 From: PhilJay Date: Sat, 22 Oct 2016 23:54:31 +0200 Subject: [PATCH 106/291] Move to Realm v2.0.2, update example --- MPChartExample/build.gradle | 2 +- .../mpchartexample/realm/RealmDatabaseActivityLine.java | 5 +++-- .../mpchartexample/realm/RealmWikiExample.java | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index f02c69be7d..b4c1900648 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -56,7 +56,7 @@ repositories { dependencies { //compile fileTree(dir: 'libs', include: ['*.jar']) //compile project(':MPChartLib-Realm') // clone "https://github.com/PhilJay/MPAndroidChart-Realm" to get this or uncomment the gradle dependency below: - compile 'com.github.PhilJay:MPAndroidChart-Realm:v1.1.0@aar' + compile 'com.github.PhilJay:MPAndroidChart-Realm:v2.0.2@aar' compile project(':MPChartLib') compile 'com.android.support:appcompat-v7:24.2.1' diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java index 13513fd50e..55f7f6dd4c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java @@ -6,6 +6,7 @@ import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -55,13 +56,13 @@ private void setData() { RealmResults result = mRealm.where(RealmDemoData.class).findAll(); RealmLineDataSet set = new RealmLineDataSet(result, "xValue", "yValue"); - set.setDrawCubic(false); + set.setMode(LineDataSet.Mode.CUBIC_BEZIER); set.setLabel("Realm LineDataSet"); set.setDrawCircleHole(false); set.setColor(ColorTemplate.rgb("#FF5722")); set.setCircleColor(ColorTemplate.rgb("#FF5722")); set.setLineWidth(1.8f); - set.setCircleSize(3.6f); + set.setCircleRadius(3.6f); ArrayList dataSets = new ArrayList(); dataSets.add(set); // add the dataset diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java index a2b9e05880..01a6ab7092 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java @@ -9,6 +9,7 @@ import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; import com.github.mikephil.charting.formatter.IAxisValueFormatter; @@ -100,13 +101,13 @@ public int getDecimalDigits() { barChart.getXAxis().setValueFormatter(formatter); RealmLineDataSet lineDataSet = new RealmLineDataSet(results, "scoreNr", "totalScore"); - lineDataSet.setDrawCubic(false); + lineDataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER); lineDataSet.setLabel("Result Scores"); lineDataSet.setDrawCircleHole(false); lineDataSet.setColor(ColorTemplate.rgb("#FF5722")); lineDataSet.setCircleColor(ColorTemplate.rgb("#FF5722")); lineDataSet.setLineWidth(1.8f); - lineDataSet.setCircleSize(3.6f); + lineDataSet.setCircleRadius(3.6f); ArrayList dataSets = new ArrayList(); dataSets.add(lineDataSet); From 9a19bf6670570f125b2633f99a17586708d82c4f Mon Sep 17 00:00:00 2001 From: PhilJay Date: Sun, 23 Oct 2016 00:02:49 +0200 Subject: [PATCH 107/291] Migrate to Realm v2.0.2, fix example --- .../mpchartexample/realm/RealmBaseActivity.java | 6 +++--- .../mpchartexample/realm/RealmDatabaseActivityPie.java | 3 +-- .../mpchartexample/realm/RealmMainActivity.java | 4 +++- build.gradle | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java index f7d7233d88..9b98f00f92 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -83,7 +83,7 @@ protected void onResume() { super.onResume(); // Create a RealmConfiguration that saves the Realm file in the app's "files" directory. - RealmConfiguration realmConfig = new RealmConfiguration.Builder(getApplicationContext()).build(); + RealmConfiguration realmConfig = new RealmConfiguration.Builder().build(); Realm.setDefaultConfiguration(realmConfig); mRealm = Realm.getDefaultInstance(); @@ -189,8 +189,8 @@ protected void writeToDBPie() { float value4 = 15f + (float) (Math.random() * 8f); float value5 = 100f - value1 - value2 - value3 - value4; - float[] values = new float[] { value1, value2, value3, value4, value5 }; - String[] labels = new String[]{ "iOS", "Android", "WP 10", "BlackBerry", "Other"}; + float[] values = new float[]{value1, value2, value3, value4, value5}; + String[] labels = new String[]{"iOS", "Android", "WP 10", "BlackBerry", "Other"}; for (int i = 0; i < values.length; i++) { RealmDemoData d = new RealmDemoData(values[i], labels[i]); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java index 7b1eb675d9..2936379d55 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java @@ -53,8 +53,7 @@ private void setData() { RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmPieDataSet set = new RealmPieDataSet(result, "yValue", "label"); // stacked entries + RealmPieDataSet set = new RealmPieDataSet(result, "yValue", "label"); set.setColors(ColorTemplate.VORDIPLOM_COLORS); set.setLabel("Example market share"); set.setSliceSpace(2); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java index 3198320272..07c54ad88c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java @@ -56,8 +56,10 @@ protected void onCreate(Bundle savedInstanceState) { lv.setOnItemClickListener(this); + Realm.init(this); + // Create a RealmConfiguration that saves the Realm file in the app's "files" directory. - RealmConfiguration realmConfig = new RealmConfiguration.Builder(getApplicationContext()).build(); + RealmConfiguration realmConfig = new RealmConfiguration.Builder().build(); Realm.setDefaultConfiguration(realmConfig); Realm realm = Realm.getDefaultInstance(); diff --git a/build.gradle b/build.gradle index 616c6ebc00..76344c5b7e 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath "io.realm:realm-gradle-plugin:1.1.0" + classpath "io.realm:realm-gradle-plugin:2.0.2" classpath 'com.android.tools.build:gradle:2.2.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } From bc13bc2c172bac486d8d09d5b4c4d4f1d8a04fac Mon Sep 17 00:00:00 2001 From: Evgeniy Date: Thu, 27 Oct 2016 08:45:39 +0300 Subject: [PATCH 108/291] Update AxisBase.java Mistake fixed --- .../com/github/mikephil/charting/components/AxisBase.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index 997612eb53..1fd98413db 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -306,7 +306,7 @@ public boolean isDrawLabelsEnabled() { * Sets the number of label entries for the y-axis max = 25, min = 2, default: 6, be aware * that this number is not fixed. * - * @param count the number of y-axis labels that sould be displayed + * @param count the number of y-axis labels that should be displayed */ public void setLabelCount(int count) { @@ -324,7 +324,7 @@ public void setLabelCount(int count) { * that this number is not * fixed (if force == false) and can only be approximated. * - * @param count the number of y-axis labels that sould be displayed + * @param count the number of y-axis labels that should be displayed * @param force if enabled, the set label count will be forced, meaning that the exact * specified count of labels will * be drawn and evenly distributed alongside the axis - this might cause labels From fb8094e9f2fa687a9440f32be2272770ee45b5a6 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 31 Oct 2016 11:45:05 +0200 Subject: [PATCH 109/291] Make automatically disabling slice-spacing an opt-in feature --- .../mikephil/charting/data/PieDataSet.java | 22 +++++++++++++++++++ .../interfaces/datasets/IPieDataSet.java | 8 +++++++ .../charting/renderer/PieChartRenderer.java | 3 +++ 3 files changed, 33 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java index 3e864a25d8..229ed5339b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java @@ -11,6 +11,7 @@ public class PieDataSet extends DataSet implements IPieDataSet { /** the space in pixels between the chart-slices, default 0f */ private float mSliceSpace = 0f; + private boolean mAutomaticallyDisableSliceSpacing; /** indicates the selection distance of a pie slice */ private float mShift = 18f; @@ -75,6 +76,27 @@ public float getSliceSpace() { return mSliceSpace; } + /** + * When enabled, slice spacing will be 0.0 when the smallest value is going to be + * smaller than the slice spacing itself. + * + * @param autoDisable + */ + public void setAutomaticallyDisableSliceSpacing(boolean autoDisable) { + mAutomaticallyDisableSliceSpacing = autoDisable; + } + + /** + * When enabled, slice spacing will be 0.0 when the smallest value is going to be + * smaller than the slice spacing itself. + * + * @return + */ + @Override + public boolean isAutomaticallyDisableSliceSpacing() { + return mAutomaticallyDisableSliceSpacing; + } + /** * sets the distance the highlighted piechart-slice of this DataSet is * "shifted" away from the center of the chart, default 12f diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java index 3b10d003ba..aa1c57524a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java @@ -17,6 +17,14 @@ public interface IPieDataSet extends IDataSet { */ float getSliceSpace(); + /** + * When enabled, slice spacing will be 0.0 when the smallest value is going to be + * smaller than the slice spacing itself. + * + * @return + */ + boolean isAutomaticallyDisableSliceSpacing(); + /** * Returns the distance a highlighted piechart slice is "shifted" away from * the chart-center in dp. 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 1681d5baae..3c52f7ceca 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 @@ -199,6 +199,9 @@ protected float calculateMinimumRadiusForSpacedSlice( */ protected float getSliceSpace(IPieDataSet dataSet) { + if (!dataSet.isAutomaticallyDisableSliceSpacing()) + return dataSet.getSliceSpace(); + float spaceSizeRatio = dataSet.getSliceSpace() / mViewPortHandler.getSmallestContentExtension(); float minValueRatio = dataSet.getYMin() / mChart.getData().getYValueSum() * 2; From 49b4d1a96b55d255015e366557fe0eb733542e62 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 31 Oct 2016 12:09:49 +0200 Subject: [PATCH 110/291] Bugfix: Corrected clipRect on the proper rect variable --- .../com/github/mikephil/charting/renderer/YAxisRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java index 46f581732c..bd3994a09e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -221,7 +221,7 @@ protected void drawZeroLine(Canvas c) { int clipRestoreCount = c.save(); mZeroLineClippingRect.set(mViewPortHandler.getContentRect()); mZeroLineClippingRect.inset(0.f, -mYAxis.getZeroLineWidth() / 2.f); - c.clipRect(mLimitLineClippingRect); + c.clipRect(mZeroLineClippingRect); // draw zero line MPPointD pos = mTrans.getPixelForValues(0f, 0f); From 70691bea41a43d9df21db1a785ea3cec3f79c8fc Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 31 Oct 2016 13:10:11 +0200 Subject: [PATCH 111/291] Fixed glitch in clipping rects. It's the Android's renderer's bug. When specifying exact clipping rects, they are clipping more than they should! So drawing a thin 1px line on the edge of a clipping rect fail. Instead of insetting by half the line width, inset by full line width. --- .../mikephil/charting/renderer/XAxisRenderer.java | 4 ++-- .../renderer/XAxisRendererHorizontalBarChart.java | 4 ++-- .../mikephil/charting/renderer/YAxisRenderer.java | 10 +++++----- .../renderer/YAxisRendererHorizontalBarChart.java | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java index e2a9b8215f..2c305796df 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -270,7 +270,7 @@ public void renderGridLines(Canvas c) { public RectF getGridClippingRect() { mGridClippingRect.set(mViewPortHandler.getContentRect()); - mGridClippingRect.inset(-mAxis.getGridLineWidth() / 2.f, 0.f); + mGridClippingRect.inset(-mAxis.getGridLineWidth(), 0.f); return mGridClippingRect; } @@ -322,7 +322,7 @@ public void renderLimitLines(Canvas c) { int clipRestoreCount = c.save(); mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); - mLimitLineClippingRect.inset(-l.getLineWidth() / 2.f, 0.f); + mLimitLineClippingRect.inset(-l.getLineWidth(), 0.f); c.clipRect(mLimitLineClippingRect); position[0] = l.getLimit(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java index 48e1ece8b5..86047cf1b8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java @@ -165,7 +165,7 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { @Override public RectF getGridClippingRect() { mGridClippingRect.set(mViewPortHandler.getContentRect()); - mGridClippingRect.inset(0.f, -mAxis.getGridLineWidth() / 2.f); + mGridClippingRect.inset(0.f, -mAxis.getGridLineWidth()); return mGridClippingRect; } @@ -238,7 +238,7 @@ public void renderLimitLines(Canvas c) { int clipRestoreCount = c.save(); mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); - mLimitLineClippingRect.inset(0.f, -l.getLineWidth() / 2.f); + mLimitLineClippingRect.inset(0.f, -l.getLineWidth()); c.clipRect(mLimitLineClippingRect); mLimitLinePaint.setStyle(Paint.Style.STROKE); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java index bd3994a09e..8de852f645 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -167,7 +167,7 @@ public void renderGridLines(Canvas c) { public RectF getGridClippingRect() { mGridClippingRect.set(mViewPortHandler.getContentRect()); - mGridClippingRect.inset(0.f, -mAxis.getGridLineWidth() / 2.f); + mGridClippingRect.inset(0.f, -mAxis.getGridLineWidth()); return mGridClippingRect; } @@ -220,7 +220,7 @@ protected void drawZeroLine(Canvas c) { int clipRestoreCount = c.save(); mZeroLineClippingRect.set(mViewPortHandler.getContentRect()); - mZeroLineClippingRect.inset(0.f, -mYAxis.getZeroLineWidth() / 2.f); + mZeroLineClippingRect.inset(0.f, -mYAxis.getZeroLineWidth()); c.clipRect(mZeroLineClippingRect); // draw zero line @@ -232,8 +232,8 @@ protected void drawZeroLine(Canvas c) { Path zeroLinePath = mDrawZeroLinePath; zeroLinePath.reset(); - zeroLinePath.moveTo(mViewPortHandler.contentLeft(), (float) pos.y - 1); - zeroLinePath.lineTo(mViewPortHandler.contentRight(), (float) pos.y - 1); + zeroLinePath.moveTo(mViewPortHandler.contentLeft(), (float) pos.y); + zeroLinePath.lineTo(mViewPortHandler.contentRight(), (float) pos.y); // draw a path because lines don't support dashing on lower android versions c.drawPath(zeroLinePath, mZeroLinePaint); @@ -272,7 +272,7 @@ public void renderLimitLines(Canvas c) { int clipRestoreCount = c.save(); mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); - mLimitLineClippingRect.inset(0.f, -l.getLineWidth() / 2.f); + mLimitLineClippingRect.inset(0.f, -l.getLineWidth()); c.clipRect(mLimitLineClippingRect); mLimitLinePaint.setStyle(Paint.Style.STROKE); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java index 2f96fa71f6..2e394af99a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java @@ -168,7 +168,7 @@ protected float[] getTransformedPositions() { @Override public RectF getGridClippingRect() { mGridClippingRect.set(mViewPortHandler.getContentRect()); - mGridClippingRect.inset(-mAxis.getGridLineWidth() / 2.f, 0.f); + mGridClippingRect.inset(-mAxis.getGridLineWidth(), 0.f); return mGridClippingRect; } @@ -188,7 +188,7 @@ protected void drawZeroLine(Canvas c) { int clipRestoreCount = c.save(); mZeroLineClippingRect.set(mViewPortHandler.getContentRect()); - mZeroLineClippingRect.inset(-mYAxis.getZeroLineWidth() / 2.f, 0.f); + mZeroLineClippingRect.inset(-mYAxis.getZeroLineWidth(), 0.f); c.clipRect(mLimitLineClippingRect); // draw zero line @@ -242,7 +242,7 @@ public void renderLimitLines(Canvas c) { int clipRestoreCount = c.save(); mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); - mLimitLineClippingRect.inset(-l.getLineWidth() / 2.f, 0.f); + mLimitLineClippingRect.inset(-l.getLineWidth(), 0.f); c.clipRect(mLimitLineClippingRect); pts[0] = l.getLimit(); From 6a5580b101d7d22ec320a994a90c6c07c90d23bf Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 31 Oct 2016 13:26:25 +0200 Subject: [PATCH 112/291] Renamed new property getter --- .../main/java/com/github/mikephil/charting/data/PieDataSet.java | 2 +- .../mikephil/charting/interfaces/datasets/IPieDataSet.java | 2 +- .../com/github/mikephil/charting/renderer/PieChartRenderer.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java index 229ed5339b..010cfbddc5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java @@ -93,7 +93,7 @@ public void setAutomaticallyDisableSliceSpacing(boolean autoDisable) { * @return */ @Override - public boolean isAutomaticallyDisableSliceSpacing() { + public boolean isAutomaticallyDisableSliceSpacingEnabled() { return mAutomaticallyDisableSliceSpacing; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java index aa1c57524a..a53a9645af 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java @@ -23,7 +23,7 @@ public interface IPieDataSet extends IDataSet { * * @return */ - boolean isAutomaticallyDisableSliceSpacing(); + boolean isAutomaticallyDisableSliceSpacingEnabled(); /** * Returns the distance a highlighted piechart slice is "shifted" away from 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 3c52f7ceca..ff22018364 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 @@ -199,7 +199,7 @@ protected float calculateMinimumRadiusForSpacedSlice( */ protected float getSliceSpace(IPieDataSet dataSet) { - if (!dataSet.isAutomaticallyDisableSliceSpacing()) + if (!dataSet.isAutomaticallyDisableSliceSpacingEnabled()) return dataSet.getSliceSpace(); float spaceSizeRatio = dataSet.getSliceSpace() / mViewPortHandler.getSmallestContentExtension(); From 8045d64260ebcd9d49cb6b60c02771a7da18ba73 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 31 Oct 2016 13:32:47 +0200 Subject: [PATCH 113/291] Added `clipValuesToContent` property for clipping values --- .../charting/charts/BarLineChartBase.java | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index 6fa3b4d312..c4b32c16eb 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -97,6 +97,8 @@ public abstract class BarLineChartBase Date: Mon, 31 Oct 2016 13:34:22 +0200 Subject: [PATCH 114/291] Added missing isDrawBordersEnabled getter --- .../mikephil/charting/charts/BarLineChartBase.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index c4b32c16eb..b4a2fb7c19 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -1153,8 +1153,8 @@ public void setDrawGridBackground(boolean enabled) { } /** - * Sets drawing the borders rectangle to true. If this is enabled, there is - * no point drawing the axis-lines of x- and y-axis. + * When enabled, the borders rectangle will be rendered. + * If this is enabled, there is no point drawing the axis-lines of x- and y-axis. * * @param enabled */ @@ -1162,6 +1162,16 @@ public void setDrawBorders(boolean enabled) { mDrawBorders = enabled; } + /** + * When enabled, the borders rectangle will be rendered. + * If this is enabled, there is no point drawing the axis-lines of x- and y-axis. + * + * @return + */ + public boolean isDrawBordersEnabled() { + return mDrawBorders; + } + /** * When enabled, the values will be clipped to contentRect, * otherwise they can bleed outside the content rect. From 00f36054c79938e2cb6c0276df16ae10b42a6400 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 31 Oct 2016 15:35:16 +0200 Subject: [PATCH 115/291] Fixed weird glitch in mixed (pos/neg) stacked bars highlight https://github.com/danielgindi/Charts/pull/1744 https://github.com/danielgindi/Charts/issues/1726 --- .../java/com/github/mikephil/charting/data/BarEntry.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java index dbf1e945db..922f151879 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java @@ -42,8 +42,8 @@ public BarEntry(float x, float[] vals) { super(x, calcSum(vals)); this.mYVals = vals; - calcRanges(); calcPosNegSum(); + calcRanges(); } /** @@ -67,8 +67,8 @@ public BarEntry(float x, float[] vals, String label) { super(x, calcSum(vals), label); this.mYVals = vals; - calcRanges(); calcPosNegSum(); + calcRanges(); } /** @@ -242,8 +242,8 @@ protected void calcRanges() { float value = values[i]; if (value < 0) { - mRanges[i] = new Range(negRemain, negRemain + value); - negRemain += Math.abs(value); + mRanges[i] = new Range(negRemain, negRemain - value); + negRemain -= value; } else { mRanges[i] = new Range(posRemain, posRemain + value); posRemain += value; From 3928e285ae4e8970b74e3e5104703105477386fc Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 8 Nov 2016 17:25:19 +0200 Subject: [PATCH 116/291] Fixed double calculation of xAxis spacing --- .../github/mikephil/charting/charts/BarLineChartBase.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index b4a2fb7c19..8aeb6828d8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -397,18 +397,12 @@ protected void calculateLegendOffsets(RectF offsets) { offsets.top += Math.min(mLegend.mNeededHeight, mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) + mLegend.getYOffset(); - - if (getXAxis().isEnabled() && getXAxis().isDrawLabelsEnabled()) - offsets.top += getXAxis().mLabelRotatedHeight; break; case BOTTOM: offsets.bottom += Math.min(mLegend.mNeededHeight, mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) + mLegend.getYOffset(); - - if (getXAxis().isEnabled() && getXAxis().isDrawLabelsEnabled()) - offsets.bottom += getXAxis().mLabelRotatedHeight; break; default: From 262d877b9ffa4b529724de2b54db840c1395ea38 Mon Sep 17 00:00:00 2001 From: Mahesh Gaya Date: Sat, 12 Nov 2016 12:23:36 -0600 Subject: [PATCH 117/291] Fix: typo for October --- .../mpchartexample/custom/DayAxisValueFormatter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java index 59f9f8e3fd..b03e773781 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -11,7 +11,7 @@ public class DayAxisValueFormatter implements IAxisValueFormatter { protected String[] mMonths = new String[]{ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; private BarLineChartBase chart; From ee020bba67a60018e8e864af3bbc6aa32113fd27 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 16 Nov 2016 10:53:47 +0200 Subject: [PATCH 118/291] Gradle updates --- MPChartExample/build.gradle | 2 +- build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index b4c1900648..662018126f 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -39,7 +39,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.1' + classpath 'com.android.tools.build:gradle:2.2.2' //classpath 'io.realm:realm-gradle-plugin:0.88.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/build.gradle b/build.gradle index 76344c5b7e..2894c13be0 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { classpath "io.realm:realm-gradle-plugin:2.0.2" - classpath 'com.android.tools.build:gradle:2.2.1' + classpath 'com.android.tools.build:gradle:2.2.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } } From 0818d766dbd71a5106df836784c796cc69f7302f Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 16 Nov 2016 11:01:17 +0200 Subject: [PATCH 119/291] Fixed EPSILON. (Closes #2432) Closes #2424 Closes #2394 Closes #2393 Closes #2385 --- .../main/java/com/github/mikephil/charting/utils/Utils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java index b555b44917..b44f9d9ea7 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java @@ -40,10 +40,10 @@ public abstract class Utils { public final static float FDEG2RAD = ((float) Math.PI / 180.f); @SuppressWarnings("unused") - public final static double DOUBLE_EPSILON = Double.longBitsToDouble(Double.doubleToLongBits(1.0) + 1); + public final static double DOUBLE_EPSILON = Double.longBitsToDouble(1); @SuppressWarnings("unused") - public final static float FLOAT_EPSILON = Float.intBitsToFloat(Float.floatToIntBits(1f) + 1); + public final static float FLOAT_EPSILON = Float.intBitsToFloat(1); /** * initialize method, called inside the Chart.init() method. From 4cb83a7b915628204131485fee156a873a216cde Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 16 Nov 2016 11:13:29 +0200 Subject: [PATCH 120/291] Added IndexAxisValueFormatter, to allow for easy x-axis labels like MPAndroidChart 2.0 --- .../BarChartActivityMultiDataset.java | 5 -- .../BarChartPositiveNegative.java | 5 -- .../mpchartexample/CombinedChartActivity.java | 5 -- .../mpchartexample/LineChartTime.java | 5 -- .../mpchartexample/RadarChartActivitry.java | 5 -- .../StackedBarActivityNegative.java | 10 --- .../custom/DayAxisValueFormatter.java | 5 -- .../custom/MyAxisValueFormatter.java | 5 -- .../custom/MyCustomXAxisValueFormatter.java | 5 -- .../custom/YearXAxisFormatter.java | 5 -- .../realm/RealmWikiExample.java | 5 -- .../charting/components/AxisBase.java | 8 +-- .../formatter/DefaultAxisValueFormatter.java | 6 +- .../formatter/IAxisValueFormatter.java | 7 -- .../formatter/IndexAxisValueFormatter.java | 69 +++++++++++++++++++ .../formatter/LargeValueFormatter.java | 1 - .../charting/formatter/PercentFormatter.java | 1 - 17 files changed, 77 insertions(+), 75 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 9377e0f1b2..671ab5abec 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -96,11 +96,6 @@ protected void onCreate(Bundle savedInstanceState) { public String getFormattedValue(float value, AxisBase axis) { return String.valueOf((int) value); } - - @Override - public int getDecimalDigits() { - return 0; - } }); YAxis leftAxis = mChart.getAxisLeft(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 10a7bb9fd9..743b7e735d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -90,11 +90,6 @@ protected void onCreate(Bundle savedInstanceState) { public String getFormattedValue(float value, AxisBase axis) { return data.get(Math.min(Math.max((int) value, 0), data.size()-1)).xAxisValue; } - - @Override - public int getDecimalDigits() { - return 0; - } }); setData(data); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index f221dcf3b7..fadfbf175f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -84,11 +84,6 @@ protected void onCreate(Bundle savedInstanceState) { public String getFormattedValue(float value, AxisBase axis) { return mMonths[(int) value % mMonths.length]; } - - @Override - public int getDecimalDigits() { - return 0; - } }); CombinedData data = new CombinedData(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 04070d2c26..30e5e2a978 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -100,11 +100,6 @@ public String getFormattedValue(float value, AxisBase axis) { long millis = TimeUnit.HOURS.toMillis((long) value); return mFormat.format(new Date(millis)); } - - @Override - public int getDecimalDigits() { - return 0; - } }); YAxis leftAxis = mChart.getAxisLeft(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index b787f17e85..937973fe4e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -81,11 +81,6 @@ protected void onCreate(Bundle savedInstanceState) { public String getFormattedValue(float value, AxisBase axis) { return mActivities[(int) value % mActivities.length]; } - - @Override - public int getDecimalDigits() { - return 0; - } }); xAxis.setTextColor(Color.WHITE); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 8b9819092a..01d4f2f89b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -85,11 +85,6 @@ protected void onCreate(Bundle savedInstanceState) { public String getFormattedValue(float value, AxisBase axis) { return format.format(value) + "-" + format.format(value + 10); } - - @Override - public int getDecimalDigits() { - return 0; - } }); Legend l = mChart.getLegend(); @@ -243,10 +238,5 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View public String getFormattedValue(float value, AxisBase axis) { return mFormat.format(Math.abs(value)) + "m"; } - - @Override - public int getDecimalDigits() { - return 0; - } } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java index 59f9f8e3fd..6edffb9315 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -136,9 +136,4 @@ else if (days <= 1458) return 2020; } - - @Override - public int getDecimalDigits() { - return 0; - } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java index 137097b84e..e8456675a9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java @@ -18,9 +18,4 @@ public MyAxisValueFormatter() { public String getFormattedValue(float value, AxisBase axis) { return mFormat.format(value) + " $"; } - - @Override - public int getDecimalDigits() { - return 1; - } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index 23f5785c11..c66e5d4569 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -37,9 +37,4 @@ else if (xScale > 1) else return mFormat.format(value); } - - @Override - public int getDecimalDigits() { - return 1; - } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java index 0b1245cd77..34d76c25eb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java @@ -24,9 +24,4 @@ public String getFormattedValue(float value, AxisBase axis) { float percent = value / axis.mAxisRange; return mMonths[(int) (mMonths.length * percent)]; } - - @Override - public int getDecimalDigits() { - return 0; - } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java index 01a6ab7092..6c1d7cde03 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java @@ -90,11 +90,6 @@ private void setData() { public String getFormattedValue(float value, AxisBase axis) { return results.get((int) value).getPlayerName(); } - - @Override - public int getDecimalDigits() { - return 0; - } }; lineChart.getXAxis().setValueFormatter(formatter); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index 997612eb53..8113d63bca 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -496,12 +496,10 @@ public void setValueFormatter(IAxisValueFormatter f) { */ public IAxisValueFormatter getValueFormatter() { - if (mAxisValueFormatter == null) { + if (mAxisValueFormatter == null || + (mAxisValueFormatter instanceof DefaultAxisValueFormatter && + ((DefaultAxisValueFormatter)mAxisValueFormatter).getDecimalDigits() != mDecimals)) mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals); - } else if (mAxisValueFormatter.getDecimalDigits() != mDecimals && mAxisValueFormatter instanceof - DefaultAxisValueFormatter) { - mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals); - } return mAxisValueFormatter; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java index f29c554059..552c150e69 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java @@ -45,7 +45,11 @@ public String getFormattedValue(float value, AxisBase axis) { return mFormat.format(value); } - @Override + /** + * Returns the number of decimal digits this formatter uses or -1, if unspecified. + * + * @return + */ public int getDecimalDigits() { return digits; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java index 5be1d28752..51939b5432 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java @@ -20,11 +20,4 @@ public interface IAxisValueFormatter * @return */ String getFormattedValue(float value, AxisBase axis); - - /** - * Returns the number of decimal digits this formatter uses or -1, if unspecified. - * - * @return - */ - int getDecimalDigits(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java new file mode 100644 index 0000000000..07349a6a0e --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java @@ -0,0 +1,69 @@ + +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.Collection; + +/** + * This formatter is used for passing an array of x-axis labels, on whole x steps. + */ +public class IndexAxisValueFormatter implements IAxisValueFormatter +{ + private String[] mValues = new String[] {}; + private int mValueCount = 0; + + /** + * An empty constructor. + * Use `setValues` to set the axis labels. + */ + public IndexAxisValueFormatter() { + } + + /** + * Constructor that specifies axis labels. + * + * @param values The values string array + */ + public IndexAxisValueFormatter(String[] values) { + if (values != null) + setValues(values); + } + + /** + * Constructor that specifies axis labels. + * + * @param values The values string array + */ + public IndexAxisValueFormatter(Collection values) { + if (values != null) + setValues(values.toArray(new String[values.size()])); + } + + public String getFormattedValue(float value, AxisBase axis) { + int index = Math.round(value); + + if (index < 0 || index >= mValueCount || index != (int)value) + return ""; + + return mValues[index]; + } + + public String[] getValues() + { + return mValues; + } + + public void setValues(String[] values) + { + if (values == null) + values = new String[] {}; + + this.mValues = values; + this.mValueCount = values.length; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java index 0b4e8306c1..c950d640b3 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java @@ -93,7 +93,6 @@ private String makePretty(double number) { return r; } - @Override public int getDecimalDigits() { return 0; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java index d321b5f03e..de8a10255a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java @@ -43,7 +43,6 @@ public String getFormattedValue(float value, AxisBase axis) { return mFormat.format(value) + " %"; } - @Override public int getDecimalDigits() { return 1; } From 2e80fada31eb1fdec98f847a2b5f039ed8ea9337 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 16 Nov 2016 12:04:57 +0200 Subject: [PATCH 121/291] Fixed a bug where the mod-360 bypass draws a full-circle for 0 slices. --- .../mikephil/charting/renderer/PieChartRenderer.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java index ff22018364..84fda6e5f2 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 @@ -269,7 +269,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { float arcStartPointX = center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD); float arcStartPointY = center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD); - if (sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { // Android is doing "mod 360" mPathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW); } else { @@ -318,7 +318,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { } final float endAngleInner = startAngleInner + sweepAngleInner; - if (sweepAngleOuter % 360f == 0.f) { + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { // Android is doing "mod 360" mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW); } else { @@ -335,7 +335,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { } } else { - if (sweepAngleOuter % 360f != 0.f) { + if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) { if (accountForSliceSpacing) { float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f; @@ -818,7 +818,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { mPathBuffer.reset(); - if (sweepAngleOuter % 360f == 0.f) { + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { // Android is doing "mod 360" mPathBuffer.addCircle(center.x, center.y, highlightedRadius, Path.Direction.CW); } else { @@ -875,7 +875,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } final float endAngleInner = startAngleInner + sweepAngleInner; - if (sweepAngleOuter % 360f == 0.f) { + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { // Android is doing "mod 360" mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW); } else { @@ -892,7 +892,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } } else { - if (sweepAngleOuter % 360f != 0.f) { + if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) { if (accountForSliceSpacing) { final float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f; From a7fd778fc862e9165dfc896105593823eb93bd60 Mon Sep 17 00:00:00 2001 From: PhilJay Date: Thu, 17 Nov 2016 19:01:21 +0100 Subject: [PATCH 122/291] Upgrade version --- MPChartExample/build.gradle | 4 ++-- MPChartLib/build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 662018126f..8ba24587be 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -7,8 +7,8 @@ android { defaultConfig { minSdkVersion 16 targetSdkVersion 24 - versionCode 53 - versionName '3.0.0' + versionCode 54 + versionName '3.0.1' testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" sourceSets { diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index 9bbffe9862..6f1e5d3a00 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -11,7 +11,7 @@ android { minSdkVersion 9 targetSdkVersion 24 versionCode 3 - versionName '3.0.0' + versionName '3.0.1' } buildTypes { release { From 08f295eae1279ba69a18184ee4db86f770df0e18 Mon Sep 17 00:00:00 2001 From: PhilJay Date: Thu, 17 Nov 2016 19:01:53 +0100 Subject: [PATCH 123/291] Upgrade manifest --- MPChartExample/AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index 2a0d4d3cbd..b1ac584a2e 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="54" + android:versionName="3.0.1" > Date: Sat, 19 Nov 2016 10:01:13 +0100 Subject: [PATCH 124/291] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d8fc9856be..a86d8f123d 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ If you are having questions or problems, you should: - **Review your code**. Make absolutely sure that everything is correct on your side. - Make sure you are using the **latest version** of the library. Check the [**release-section**](https://github.com/PhilJay/MPAndroidChart/releases). - - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.0/javadoc/) + - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.1/javadoc/) - Search or open questions on [**stackoverflow**](https://stackoverflow.com/search?q=mpandroidchart) with the `mpandroidchart` tag - Search [**known issues**](https://github.com/PhilJay/MPAndroidChart/issues) for your problem (open and closed) - Create new issues (please :fire: **search known issues before** :fire:, do not create duplicate issues) @@ -117,7 +117,7 @@ allprojects { ```gradle dependencies { - compile 'com.github.PhilJay:MPAndroidChart:v3.0.0' + compile 'com.github.PhilJay:MPAndroidChart:v3.0.1' } ``` @@ -136,7 +136,7 @@ dependencies { com.github.PhilJay MPAndroidChart - v3.0.0 + v3.0.1 ``` @@ -156,7 +156,7 @@ dependencies { Documentation ----- -For a **detailed documentation** :notebook_with_decorative_cover:, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.0/javadoc/). +For a **detailed documentation** :notebook_with_decorative_cover:, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.1/javadoc/). Furthermore, you can also rely on the [**MPChartExample**](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample) folder and check out the example code in that project. The corresponding application to the example project is also [**available in the Google PlayStore**](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample). From 9aaa3328f011cc8f52d2ae3449ae3f16b57cf952 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 10 Dec 2016 10:35:28 +0100 Subject: [PATCH 125/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a86d8f123d..e2e8f853a6 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Donations - [**Donate 100 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KY7F59RYPCYCQ): You are the man! This project saved me hours (if not days) of struggle and hard work, simply awesome! - Of course, you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome! -If you just want to be nice, you can check out my [**Amazon-Wishlist**]( https://www.amazon.de/registry/wishlist/2DYHJ69VMF8HM/ref=cm_sw_em_r_mt_ws_WVCyxb4KKQ2G6). +If you just want to be nice, you can check out and rate the new [**Telegram Chat Bot**]( https://storebot.me/bot/myalfred_bot) we created for scheduling meetings and other stuff. ## Xamarin From 8abe7e84ad16cbfa320652573d60f945263ce7bb Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 10 Dec 2016 10:35:56 +0100 Subject: [PATCH 126/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e2e8f853a6..8b25078be2 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Donations - [**Donate 100 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KY7F59RYPCYCQ): You are the man! This project saved me hours (if not days) of struggle and hard work, simply awesome! - Of course, you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome! -If you just want to be nice, you can check out and rate the new [**Telegram Chat Bot**]( https://storebot.me/bot/myalfred_bot) we created for scheduling meetings and other stuff. +If you just want to be nice, you can check out and rate the new [**Telegram Chat Bot**]( https://storebot.me/bot/myalfred_bot) we created for scheduling meetings and other stuff. 😉 ## Xamarin From 34e7f4497962d235d94072d1544e22a7a362ae30 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 19 Dec 2016 21:56:03 +0200 Subject: [PATCH 127/291] drawBottomYLabelEntryEnabled --- .../github/mikephil/charting/components/YAxis.java | 14 ++++++++++++++ .../mikephil/charting/renderer/YAxisRenderer.java | 10 ++++++---- .../renderer/YAxisRendererHorizontalBarChart.java | 10 ++++++---- .../charting/renderer/YAxisRendererRadarChart.java | 10 +++++----- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java index 82d77c4c8f..e84caab76b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java @@ -18,6 +18,11 @@ */ public class YAxis extends AxisBase { + /** + * indicates if the bottom y-label entry is drawn or not + */ + private boolean mDrawBottomYLabelEntry = true; + /** * indicates if the top y-label entry is drawn or not */ @@ -168,6 +173,15 @@ public boolean isDrawTopYLabelEntryEnabled() { return mDrawTopYLabelEntry; } + /** + * returns true if drawing the bottom y-axis label entry is enabled + * + * @return + */ + public boolean isDrawBottomYLabelEntryEnabled() { + return mDrawBottomYLabelEntry; + } + /** * set this to true to enable drawing the top y-label entry. Disabling this can be helpful * when the top y-label and diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java index 8de852f645..a2bf679777 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -114,14 +114,16 @@ public void renderAxisLine(Canvas c) { */ protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) { + final int from = mYAxis.isDrawBottomYLabelEntryEnabled() ? 0 : 1; + final int to = mYAxis.isDrawTopYLabelEntryEnabled() + ? mYAxis.mEntryCount + : (mYAxis.mEntryCount - 1); + // draw - for (int i = 0; i < mYAxis.mEntryCount; i++) { + for (int i = from; i < to; i++) { String text = mYAxis.getFormattedLabel(i); - if (!mYAxis.isDrawTopYLabelEntryEnabled() && i >= mYAxis.mEntryCount - 1) - return; - c.drawText(text, fixedPosition, positions[i * 2 + 1] + offset, mAxisLabelPaint); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java index 2e394af99a..71275b03c3 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java @@ -137,12 +137,14 @@ protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, flo mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); mAxisLabelPaint.setColor(mYAxis.getTextColor()); - for (int i = 0; i < mYAxis.mEntryCount; i++) { + final int from = mYAxis.isDrawBottomYLabelEntryEnabled() ? 0 : 1; + final int to = mYAxis.isDrawTopYLabelEntryEnabled() + ? mYAxis.mEntryCount + : (mYAxis.mEntryCount - 1); - String text = mYAxis.getFormattedLabel(i); + for (int i = from; i < to; i++) { - if (!mYAxis.isDrawTopYLabelEntryEnabled() && i >= mYAxis.mEntryCount - 1) - return; + String text = mYAxis.getFormattedLabel(i); c.drawText(text, positions[i * 2], fixedPosition - offset, mAxisLabelPaint); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java index 64bbb1e085..ee7392e928 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -156,12 +156,12 @@ public void renderAxisLabels(Canvas c) { MPPointF pOut = MPPointF.getInstance(0,0); float factor = mChart.getFactor(); - int labelCount = mYAxis.mEntryCount; + final int from = mYAxis.isDrawBottomYLabelEntryEnabled() ? 0 : 1; + final int to = mYAxis.isDrawTopYLabelEntryEnabled() + ? mYAxis.mEntryCount + : (mYAxis.mEntryCount - 1); - for (int j = 0; j < labelCount; j++) { - - if (j == labelCount - 1 && mYAxis.isDrawTopYLabelEntryEnabled() == false) - break; + for (int j = from; j < to; j++) { float r = (mYAxis.mEntries[j] - mYAxis.mAxisMinimum) * factor; From a934501315226f4fd13bab18083d59c6ffc90e1b Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 19 Dec 2016 21:56:16 +0200 Subject: [PATCH 128/291] Gradle updates --- MPChartExample/build.gradle | 2 +- build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 8ba24587be..96c367bb0b 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -39,7 +39,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.2' + classpath 'com.android.tools.build:gradle:2.2.3' //classpath 'io.realm:realm-gradle-plugin:0.88.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/build.gradle b/build.gradle index 2894c13be0..5969598ccc 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { classpath "io.realm:realm-gradle-plugin:2.0.2" - classpath 'com.android.tools.build:gradle:2.2.2' + classpath 'com.android.tools.build:gradle:2.2.3' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } } From d5e5ec3a924248a3b1d4b977b3b7658a0f3d0a12 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 19 Dec 2016 22:06:41 +0200 Subject: [PATCH 129/291] resetZoom() --- .../charting/charts/BarLineChartBase.java | 37 ++++++++++++------- .../charting/utils/ViewPortHandler.java | 10 +++++ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index 8aeb6828d8..745d6019e0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -571,17 +571,17 @@ public void computeScroll() { * VIEWPORT */ - protected Matrix mZoomInMatrixBuffer = new Matrix(); + protected Matrix mZoomMatrixBuffer = new Matrix(); /** - * Zooms in by 1.4f, into the charts center. center. + * Zooms in by 1.4f, into the charts center. */ public void zoomIn() { MPPointF center = mViewPortHandler.getContentCenter(); - mViewPortHandler.zoomIn(center.x, -center.y, mZoomInMatrixBuffer); - mViewPortHandler.refresh(mZoomInMatrixBuffer, this, false); + mViewPortHandler.zoomIn(center.x, -center.y, mZoomMatrixBuffer); + mViewPortHandler.refresh(mZoomMatrixBuffer, this, false); MPPointF.recycleInstance(center); @@ -592,17 +592,15 @@ public void zoomIn() { postInvalidate(); } - protected Matrix mZoomOutMatrixBuffer = new Matrix(); - /** - * Zooms out by 0.7f, from the charts center. center. + * Zooms out by 0.7f, from the charts center. */ public void zoomOut() { MPPointF center = mViewPortHandler.getContentCenter(); - mViewPortHandler.zoomOut(center.x, -center.y, mZoomOutMatrixBuffer); - mViewPortHandler.refresh(mZoomOutMatrixBuffer, this, false); + mViewPortHandler.zoomOut(center.x, -center.y, mZoomMatrixBuffer); + mViewPortHandler.refresh(mZoomMatrixBuffer, this, false); MPPointF.recycleInstance(center); @@ -613,7 +611,20 @@ public void zoomOut() { postInvalidate(); } - protected Matrix mZoomMatrixBuffer = new Matrix(); + /** + * Zooms out to original size. + */ + public void resetZoom() { + + mViewPortHandler.resetZoom(mZoomMatrixBuffer); + mViewPortHandler.refresh(mZoomMatrixBuffer, this, false); + + // Range might have changed, which means that Y-axis labels + // could have changed in size, affecting Y-axis size. + // So we need to recalculate offsets. + calculateOffsets(); + postInvalidate(); + } /** * Zooms in or out by the given scale factor. x and y are the coordinates @@ -625,9 +636,9 @@ public void zoomOut() { * @param y */ public void zoom(float scaleX, float scaleY, float x, float y) { - Matrix save = mZoomMatrixBuffer; - mViewPortHandler.zoom(scaleX, scaleY, x, -y, save); - mViewPortHandler.refresh(save, this, false); + + mViewPortHandler.zoom(scaleX, scaleY, x, -y, mZoomMatrixBuffer); + mViewPortHandler.refresh(mZoomMatrixBuffer, this, false); // Range might have changed, which means that Y-axis labels // could have changed in size, affecting Y-axis size. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java index 541c726eeb..71b4b9bf79 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java @@ -223,6 +223,16 @@ public void zoomOut(float x, float y, Matrix outputMatrix) { outputMatrix.postScale(0.7f, 0.7f, x, y); } + /** + * Zooms out to original size. + * @param outputMatrix + */ + public void resetZoom(Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(1.0f, 1.0f, 0.0f, 0.0f); + } + /** * Post-scales by the specified scale factors. * From ccf6b7e3b1054a5a47d2822148cc19c14e9c3da6 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 19 Dec 2016 22:50:08 +0200 Subject: [PATCH 130/291] Correctly position 0 in stacked bar --- .../github/mikephil/charting/buffer/BarBuffer.java | 6 +++++- .../charting/renderer/BarChartRenderer.java | 11 +++++++++-- .../renderer/HorizontalBarChartRenderer.java | 13 ++++++++++--- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/BarBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/BarBuffer.java index 1a6b85bcb3..136a231445 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/BarBuffer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/BarBuffer.java @@ -90,7 +90,11 @@ public void feed(IBarDataSet data) { float value = vals[k]; - if (value >= 0f) { + if (value == 0.0f && (posY == 0.0f || negY == 0.0f)) { + // Take care of the situation of a 0.0 value, which overlaps a non-zero bar + y = value; + yStart = y; + } else if (value >= 0.0f) { y = posY; yStart = posY + value; posY = yStart; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index ad12feae45..d0e644d806 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -292,7 +292,10 @@ public void drawValues(Canvas c) { float value = vals[idx]; float y; - if (value >= 0f) { + if (value == 0.0f && (posY == 0.0f || negY == 0.0f)) { + // Take care of the situation of a 0.0 value, which overlaps a non-zero bar + y = value; + } else if (value >= 0.0f) { posY += value; y = posY; } else { @@ -307,8 +310,12 @@ public void drawValues(Canvas c) { for (int k = 0; k < transformed.length; k += 2) { + final float val = vals[k / 2]; + final boolean drawBelow = + (val == 0.0f && negY == 0.0f && posY > 0.0f) || + val < 0.0f; float y = transformed[k + 1] - + (vals[k / 2] >= 0 ? posOffset : negOffset); + + (drawBelow ? negOffset : posOffset); if (!mViewPortHandler.isInBoundsRight(x)) break; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index 00cce881f2..0a910f6f89 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -264,7 +264,10 @@ public void drawValues(Canvas c) { float value = vals[idx]; float y; - if (value >= 0f) { + if (value == 0.0f && (posY == 0.0f || negY == 0.0f)) { + // Take care of the situation of a 0.0 value, which overlaps a non-zero bar + y = value; + } else if (value >= 0.0f) { posY += value; y = posY; } else { @@ -279,7 +282,7 @@ public void drawValues(Canvas c) { for (int k = 0; k < transformed.length; k += 2) { - float val = vals[k / 2]; + final float val = vals[k / 2]; String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); // calculate the correct offset depending on the draw position of the value @@ -292,8 +295,12 @@ public void drawValues(Canvas c) { negOffset = -negOffset - valueTextWidth; } + final boolean drawBelow = + (val == 0.0f && negY == 0.0f && posY > 0.0f) || + val < 0.0f; + float x = transformed[k] - + (val >= 0 ? posOffset : negOffset); + + (drawBelow ? negOffset : posOffset); float y = (buffer.buffer[bufferIndex + 1] + buffer.buffer[bufferIndex + 3]) / 2f; if (!mViewPortHandler.isInBoundsTop(y)) From 6966b8117e6b4706e5e068eba96dce21cceafeed Mon Sep 17 00:00:00 2001 From: Patrick Ivarsson Date: Fri, 23 Dec 2016 12:38:42 +0100 Subject: [PATCH 131/291] Fix for default text size being set in PX instead of DP The default text size in ComponentBase was defined as 10 pixels instead of 10dp, which causes tiny text and does not reflect the javadoc and the general behavior of setTextSize(...) --- .../com/github/mikephil/charting/components/ComponentBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java index 7d31d0350a..d3a1d4d82a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java @@ -36,7 +36,7 @@ public abstract class ComponentBase { /** * the text size of the labels */ - protected float mTextSize = 10f; + protected float mTextSize = Utils.convertDpToPixel(10f); /** * the text color to use for the labels From 30a331781f72f4217153b5d88b21bd191c58db8f Mon Sep 17 00:00:00 2001 From: Patrick Ivarsson Date: Fri, 23 Dec 2016 14:15:53 +0100 Subject: [PATCH 132/291] Fix circles inherit alpha (Fixes #2620) ISSUE: When using multiple LineDataSets like the follows: int solidColor = 0xFFFF00FF; dataSet.setColor(solidColor); dataSet.setCircleColor(solidColor); int semiTransparentColor = 0x8A000000; fadedSet.setColor(semiTransparentColor); LineData data = new LineData(dataSet, fadedSet); the circles in 'dataSet' will rendered with the alpha from fadedSet (0x8A). The reason for this is that mRenderPaint is not reset properly before drawing the circles. The first time drawCircles is called the imageCache.fill(...) method is used where the color is set by mRenderPaint.setColor(set.getCircleColor(i)), restoring the alpha to 0xFF. The second time homever, imageCache.fill(...) is not called which means that mRenderPaint will use it's old color/alpha, which in this case is from fadedSet. TEST INFO: To trigger the issue, add the following to LineChartActivity1: final ArrayList fadedEntries = new ArrayList<>(); for (int i = 0; i < count; i++) { float val = (float) (Math.random() * range) + 3; fadedEntries.add(new Entry(i, val)); } final LineDataSet fadedDateSet = new LineDataSet(fadedEntries, "Faded"); fadedDateSet.setColor(0x42000000); dataSets.add(fadedDateSet); // add the datasets and launch the first item in the example app. SOLUTION: This commit replaces mRenderPaint with null when drawing the circle bitmap. If circleColor has been defined with a semi-transparent color, it will be drawn that way in the cached bitmap, hence the the bitmap itself does not need to be drawn with an alpha. --- .../github/mikephil/charting/renderer/LineChartRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 1f46e5b7ef..2c32943d4c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -652,7 +652,7 @@ protected void drawCircles(Canvas c) { Bitmap circleBitmap = imageCache.getBitmap(j); if (circleBitmap != null) { - c.drawBitmap(circleBitmap, mCirclesBuffer[0] - circleRadius, mCirclesBuffer[1] - circleRadius, mRenderPaint); + c.drawBitmap(circleBitmap, mCirclesBuffer[0] - circleRadius, mCirclesBuffer[1] - circleRadius, null); } } } From ed618084f9c8a568ec10a0a85e04a5f0af0158ff Mon Sep 17 00:00:00 2001 From: travisfw Date: Mon, 13 Feb 2017 14:02:55 -0800 Subject: [PATCH 133/291] fix tests for java executable location. bug #2805 https://github.com/PhilJay/MPAndroidChart/issues/2805 --- gradlew | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradlew b/gradlew index 86ee20772b..f6890acbdf 100755 --- a/gradlew +++ b/gradlew @@ -64,13 +64,13 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then - if [ -xPx "$JAVA_HOME/jre/sh/java" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi - if [ ! -xPx "$JAVACMD" ] ; then + if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the From 749827798b90c4f060caa4aceaa1fcfc7344bdc4 Mon Sep 17 00:00:00 2001 From: travisfw Date: Tue, 14 Feb 2017 11:07:10 -0800 Subject: [PATCH 134/291] fix https://github.com/PhilJay/MPAndroidChart/issues/2813 --- MPChartLib/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index 6f1e5d3a00..429fc1df45 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -55,6 +55,7 @@ task sourcesJar(type: Jar) { } task javadoc(type: Javadoc) { + options.charSet = 'UTF-8' failOnError false source = android.sourceSets.main.java.sourceFiles classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) From a02e108f467cda0e74b40bc055251486fe075f45 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 20 Feb 2017 13:33:39 +0200 Subject: [PATCH 135/291] Implemented icon support --- .../mikephil/charting/data/BarEntry.java | 87 ++++++++++++--- .../mikephil/charting/data/BaseDataSet.java | 35 ++++++ .../mikephil/charting/data/BaseEntry.java | 34 ++++++ .../mikephil/charting/data/BubbleEntry.java | 28 +++++ .../mikephil/charting/data/CandleEntry.java | 64 +++++++++-- .../github/mikephil/charting/data/Entry.java | 26 +++++ .../mikephil/charting/data/PieEntry.java | 21 +++- .../interfaces/datasets/IDataSet.java | 40 ++++++- .../charting/renderer/BarChartRenderer.java | 92 ++++++++++++++-- .../BarLineScatterCandleBubbleRenderer.java | 2 +- .../renderer/BubbleChartRenderer.java | 27 ++++- .../renderer/CandleStickChartRenderer.java | 34 +++++- .../charting/renderer/DataRenderer.java | 2 + .../renderer/HorizontalBarChartRenderer.java | 102 +++++++++++++++--- .../charting/renderer/LineChartRenderer.java | 27 ++++- .../charting/renderer/PieChartRenderer.java | 24 +++++ .../charting/renderer/RadarChartRenderer.java | 43 +++++++- .../renderer/ScatterChartRenderer.java | 33 +++++- .../mikephil/charting/utils/MPPointF.java | 11 ++ .../github/mikephil/charting/utils/Utils.java | 42 +++++--- 20 files changed, 698 insertions(+), 76 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java index 922f151879..365ef51a2d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.data; import android.annotation.SuppressLint; +import android.graphics.drawable.Drawable; import com.github.mikephil.charting.highlight.Range; @@ -33,10 +34,54 @@ public class BarEntry extends Entry { private float mPositiveSum; /** - * Constructor for stacked bar entries. + * Constructor for normal bars (not stacked). + * + * @param x + * @param y + */ + public BarEntry(float x, float y) { + super(x, y); + } + + /** + * Constructor for normal bars (not stacked). + * + * @param x + * @param y + * @param data - Spot for additional data this Entry represents. + */ + public BarEntry(float x, float y, Object data) { + super(x, y, data); + } + + /** + * Constructor for normal bars (not stacked). + * + * @param x + * @param y + * @param icon - icon image + */ + public BarEntry(float x, float y, Drawable icon) { + super(x, y, icon); + } + + /** + * Constructor for normal bars (not stacked). * * @param x - * @param vals - the stack values, use at lest 2 + * @param y + * @param icon - icon image + * @param data - Spot for additional data this Entry represents. + */ + public BarEntry(float x, float y, Drawable icon, Object data) { + super(x, y, icon, data); + } + + /** + * Constructor for stacked bar entries. One data object for whole stack + * + * @param x + * @param vals - the stack values, use at least 2 */ public BarEntry(float x, float[] vals) { super(x, calcSum(vals)); @@ -47,24 +92,29 @@ public BarEntry(float x, float[] vals) { } /** - * Constructor for normal bars (not stacked). + * Constructor for stacked bar entries. One data object for whole stack * * @param x - * @param y + * @param vals - the stack values, use at least 2 + * @param data - Spot for additional data this Entry represents. */ - public BarEntry(float x, float y) { - super(x, y); + public BarEntry(float x, float[] vals, Object data) { + super(x, calcSum(vals), data); + + this.mYVals = vals; + calcPosNegSum(); + calcRanges(); } /** - * Constructor for stacked bar entries. + * Constructor for stacked bar entries. One data object for whole stack * * @param x - * @param vals - the stack values, use at least 2 - * @param label Additional description label. + * @param vals - the stack values, use at least 2 + * @param icon - icon image */ - public BarEntry(float x, float[] vals, String label) { - super(x, calcSum(vals), label); + public BarEntry(float x, float[] vals, Drawable icon) { + super(x, calcSum(vals), icon); this.mYVals = vals; calcPosNegSum(); @@ -72,14 +122,19 @@ public BarEntry(float x, float[] vals, String label) { } /** - * Constructor for normal bars (not stacked). + * Constructor for stacked bar entries. One data object for whole stack * * @param x - * @param y - * @param data Spot for additional data this Entry represents. + * @param vals - the stack values, use at least 2 + * @param icon - icon image + * @param data - Spot for additional data this Entry represents. */ - public BarEntry(float x, float y, Object data) { - super(x, y, data); + public BarEntry(float x, float[] vals, Drawable icon, Object data) { + super(x, calcSum(vals), icon, data); + + this.mYVals = vals; + calcPosNegSum(); + calcRanges(); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java index 89ed9b4001..3869a00895 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -11,8 +11,11 @@ import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; import java.util.ArrayList; import java.util.List; @@ -68,6 +71,16 @@ public abstract class BaseDataSet implements IDataSet { */ protected boolean mDrawValues = true; + /** + * if true, y-icons are drawn on the chart + */ + protected boolean mDrawIcons = true; + + /** + * the offset for drawing icons (in dp) + */ + protected MPPointF mIconsOffset = new MPPointF(); + /** * the size of the value-text labels */ @@ -371,6 +384,28 @@ public boolean isDrawValuesEnabled() { return mDrawValues; } + @Override + public void setDrawIcons(boolean enabled) { + mDrawIcons = enabled; + } + + @Override + public boolean isDrawIconsEnabled() { + return mDrawIcons; + } + + @Override + public void setIconsOffset(MPPointF offsetDp) { + + mIconsOffset.x = offsetDp.x; + mIconsOffset.y = offsetDp.y; + } + + @Override + public MPPointF getIconsOffset() { + return mIconsOffset; + } + @Override public void setVisible(boolean visible) { mVisible = visible; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseEntry.java index 099fee86d8..eb1ada84d8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseEntry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseEntry.java @@ -1,5 +1,7 @@ package com.github.mikephil.charting.data; +import android.graphics.drawable.Drawable; + /** * Created by Philipp Jahoda on 02/06/16. */ @@ -11,6 +13,9 @@ public abstract class BaseEntry { /** optional spot for additional data this Entry represents */ private Object mData = null; + /** optional icon image */ + private Drawable mIcon = null; + public BaseEntry() { } @@ -24,6 +29,17 @@ public BaseEntry(float y, Object data) { this.mData = data; } + public BaseEntry(float y, Drawable icon) { + this(y); + this.mIcon = icon; + } + + public BaseEntry(float y, Drawable icon, Object data) { + this(y); + this.mIcon = icon; + this.mData = data; + } + /** * Returns the y value of this Entry. * @@ -33,6 +49,24 @@ public float getY() { return y; } + /** + * Sets the icon drawable + * + * @param icon + */ + public void setIcon(Drawable icon) { + this.mIcon = icon; + } + + /** + * Returns the icon of this Entry. + * + * @return + */ + public Drawable getIcon() { + return mIcon; + } + /** * Sets the y-value for the Entry. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleEntry.java index c6ff908b85..35d8330aaa 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleEntry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleEntry.java @@ -2,6 +2,7 @@ package com.github.mikephil.charting.data; import android.annotation.SuppressLint; +import android.graphics.drawable.Drawable; /** * Subclass of Entry that holds a value for one entry in a BubbleChart. Bubble @@ -41,6 +42,33 @@ public BubbleEntry(float x, float y, float size, Object data) { this.mSize = size; } + /** + * Constructor. + * + * @param x The value on the x-axis. + * @param y The value on the y-axis. + * @param size The size of the bubble. + * @param icon Icon image + */ + public BubbleEntry(float x, float y, float size, Drawable icon) { + super(x, y, icon); + this.mSize = size; + } + + /** + * Constructor. + * + * @param x The value on the x-axis. + * @param y The value on the y-axis. + * @param size The size of the bubble. + * @param icon Icon image + * @param data Spot for additional data this Entry represents. + */ + public BubbleEntry(float x, float y, float size, Drawable icon, Object data) { + super(x, y, icon, data); + this.mSize = size; + } + public BubbleEntry copy() { BubbleEntry c = new BubbleEntry(getX(), getY(), mSize, getData()); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java index efe87d077a..5a049957a2 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java @@ -2,6 +2,7 @@ package com.github.mikephil.charting.data; import android.annotation.SuppressLint; +import android.graphics.drawable.Drawable; /** * Subclass of Entry that holds all values for one entry in a CandleStickChart. @@ -26,11 +27,11 @@ public class CandleEntry extends Entry { /** * Constructor. * - * @param x The value on the x-axis. - * @param shadowH The (shadow) high value. - * @param shadowL The (shadow) low value. - * @param open The open value. - * @param close The close value. + * @param x The value on the x-axis + * @param shadowH The (shadow) high value + * @param shadowL The (shadow) low value + * @param open The open value + * @param close The close value */ public CandleEntry(float x, float shadowH, float shadowL, float open, float close) { super(x, (shadowH + shadowL) / 2f); @@ -43,16 +44,16 @@ public CandleEntry(float x, float shadowH, float shadowL, float open, float clos /** * Constructor. - * - * @param x The value on the x-axis. - * @param shadowH The (shadow) high value. - * @param shadowL The (shadow) low value. + * + * @param x The value on the x-axis + * @param shadowH The (shadow) high value + * @param shadowL The (shadow) low value * @param open * @param close - * @param data Spot for additional data this Entry represents. + * @param data Spot for additional data this Entry represents */ public CandleEntry(float x, float shadowH, float shadowL, float open, float close, - Object data) { + Object data) { super(x, (shadowH + shadowL) / 2f, data); this.mShadowHigh = shadowH; @@ -61,6 +62,47 @@ public CandleEntry(float x, float shadowH, float shadowL, float open, float clos this.mClose = close; } + /** + * Constructor. + * + * @param x The value on the x-axis + * @param shadowH The (shadow) high value + * @param shadowL The (shadow) low value + * @param open + * @param close + * @param icon Icon image + */ + public CandleEntry(float x, float shadowH, float shadowL, float open, float close, + Drawable icon) { + super(x, (shadowH + shadowL) / 2f, icon); + + this.mShadowHigh = shadowH; + this.mShadowLow = shadowL; + this.mOpen = open; + this.mClose = close; + } + + /** + * Constructor. + * + * @param x The value on the x-axis + * @param shadowH The (shadow) high value + * @param shadowL The (shadow) low value + * @param open + * @param close + * @param icon Icon image + * @param data Spot for additional data this Entry represents + */ + public CandleEntry(float x, float shadowH, float shadowL, float open, float close, + Drawable icon, Object data) { + super(x, (shadowH + shadowL) / 2f, icon, data); + + this.mShadowHigh = shadowH; + this.mShadowLow = shadowL; + this.mOpen = open; + this.mClose = close; + } + /** * Returns the overall range (difference) between shadow-high and * shadow-low. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java index f7a7ca32f0..b7a887990d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.data; +import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.ParcelFormatException; import android.os.Parcelable; @@ -45,6 +46,31 @@ public Entry(float x, float y, Object data) { this.x = x; } + /** + * A Entry represents one single entry in the chart. + * + * @param x the x value + * @param y the y value (the actual value of the entry) + * @param icon icon image + */ + public Entry(float x, float y, Drawable icon) { + super(y, icon); + this.x = x; + } + + /** + * A Entry represents one single entry in the chart. + * + * @param x the x value + * @param y the y value (the actual value of the entry) + * @param icon icon image + * @param data Spot for additional data this Entry represents. + */ + public Entry(float x, float y, Drawable icon, Object data) { + super(y, icon, data); + this.x = x; + } + /** * Returns the x-value of this Entry object. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java index f5c40804c1..65741ef1da 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java @@ -1,10 +1,11 @@ package com.github.mikephil.charting.data; import android.annotation.SuppressLint; +import android.graphics.drawable.Drawable; import android.util.Log; /** - * Created by Philipp Jahoda on 31/05/16. + * @author Philipp Jahoda */ @SuppressLint("ParcelCreator") public class PieEntry extends Entry { @@ -19,6 +20,14 @@ public PieEntry(float value, Object data) { super(0f, value, data); } + public PieEntry(float value, Drawable icon) { + super(0f, value, icon); + } + + public PieEntry(float value, Drawable icon, Object data) { + super(0f, value, icon, data); + } + public PieEntry(float value, String label) { super(0f, value); this.label = label; @@ -29,6 +38,16 @@ public PieEntry(float value, String label, Object data) { this.label = label; } + public PieEntry(float value, String label, Drawable icon) { + super(0f, value, icon); + this.label = label; + } + + public PieEntry(float value, String label, Drawable icon, Object data) { + super(0f, value, icon, data); + this.label = label; + } + /** * This is the same as getY(). Returns the value of the PieEntry. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index 5f922ba569..fd8af7064b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.interfaces.datasets; import android.graphics.DashPathEffect; +import android.graphics.PointF; import android.graphics.Typeface; import com.github.mikephil.charting.components.Legend; @@ -8,6 +9,7 @@ import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.MPPointF; import java.util.List; @@ -419,10 +421,10 @@ public interface IDataSet { DashPathEffect getFormLineDashEffect(); /** - * set this to true to draw y-values on the chart NOTE (for bar and - * linechart): if "maxvisiblecount" is reached, no values will be drawn even - * if this is enabled + * set this to true to draw y-values on the chart. * + * NOTE (for bar and line charts): if `maxVisibleCount` is reached, no values will be drawn even + * if this is enabled * @param enabled */ void setDrawValues(boolean enabled); @@ -434,6 +436,38 @@ public interface IDataSet { */ boolean isDrawValuesEnabled(); + /** + * Set this to true to draw y-icons on the chart. + * + * NOTE (for bar and line charts): if `maxVisibleCount` is reached, no icons will be drawn even + * if this is enabled + * + * @param enabled + */ + void setDrawIcons(boolean enabled); + + /** + * Returns true if y-icon drawing is enabled, false if not + * + * @return + */ + boolean isDrawIconsEnabled(); + + /** + * Offset of icons drawn on the chart. + * + * For all charts except Pie and Radar it will be ordinary (x offset,y offset). + * + * For Pie and Radar chart it will be (y offset, distance from center offset); so if you want icon to be rendered under value, you should increase X component of CGPoint, and if you want icon to be rendered closet to center, you should decrease height component of CGPoint. + * @param offset + */ + void setIconsOffset(MPPointF offset); + + /** + * Get the offset for drawing icons. + */ + MPPointF getIconsOffset(); + /** * Set the visibility of this DataSet. If not visible, the DataSet will not * be drawn to the chart upon refreshing it. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index d0e644d806..f17761234e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -5,6 +5,7 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; +import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.buffer.BarBuffer; @@ -14,6 +15,7 @@ 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.MPPointF; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -224,6 +226,10 @@ public void drawValues(Canvas c) { final float phaseY = mAnimator.getPhaseY(); + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + // if only single values are drawn (sum) if (!dataSet.isStacked()) { @@ -241,9 +247,34 @@ public void drawValues(Canvas c) { BarEntry entry = dataSet.getEntryForIndex(j / 4); float val = entry.getY(); - drawValue(c, dataSet.getValueFormatter(), val, entry, i, x, - val >= 0 ? (buffer.buffer[j + 1] + posOffset) : (buffer.buffer[j + 3] + negOffset), - dataSet.getValueTextColor(j / 4)); + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, dataSet.getValueFormatter(), val, entry, i, x, + val >= 0 ? + (buffer.buffer[j + 1] + posOffset) : + (buffer.buffer[j + 3] + negOffset), + dataSet.getValueTextColor(j / 4)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float px = x; + float py = val >= 0 ? + (buffer.buffer[j + 1] + posOffset) : + (buffer.buffer[j + 3] + negOffset); + + px += iconsOffset.x; + py += iconsOffset.y; + + Utils.drawImage( + c, + icon, + (int)px, + (int)py, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } } // if we have stacks @@ -275,9 +306,32 @@ public void drawValues(Canvas c) { || !mViewPortHandler.isInBoundsLeft(x)) continue; - drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, - buffer.buffer[bufferIndex + 1] + (entry.getY() >= 0 ? posOffset : negOffset), - color); + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, + buffer.buffer[bufferIndex + 1] + + (entry.getY() >= 0 ? posOffset : negOffset), + color); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float px = x; + float py = buffer.buffer[bufferIndex + 1] + + (entry.getY() >= 0 ? posOffset : negOffset); + + px += iconsOffset.x; + py += iconsOffset.y; + + Utils.drawImage( + c, + icon, + (int)px, + (int)py, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } // draw stack values } else { @@ -324,7 +378,29 @@ public void drawValues(Canvas c) { || !mViewPortHandler.isInBoundsLeft(x)) continue; - drawValue(c, dataSet.getValueFormatter(), vals[k / 2], entry, i, x, y, color); + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + dataSet.getValueFormatter(), + vals[k / 2], + entry, + i, + x, + y, + color); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } } } @@ -332,6 +408,8 @@ public void drawValues(Canvas c) { index++; } } + + MPPointF.recycleInstance(iconsOffset); } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java index 7b48244688..06599187d3 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java @@ -29,7 +29,7 @@ public BarLineScatterCandleBubbleRenderer(ChartAnimator animator, ViewPortHandle * @return */ protected boolean shouldDrawValues(IDataSet set) { - return set.isVisible() && set.isDrawValuesEnabled(); + return set.isVisible() && (set.isDrawValuesEnabled() || set.isDrawIconsEnabled()); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index 680e7810e8..17bba048b9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -4,6 +4,7 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint.Style; +import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.data.BubbleData; @@ -11,6 +12,7 @@ import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -145,6 +147,10 @@ public void drawValues(Canvas c) { final float alpha = phaseX == 1 ? phaseY : phaseX; + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + for (int j = 0; j < positions.length; j += 2) { int valueTextColor = dataSet.getValueTextColor(j / 2 + mXBounds.min); @@ -162,9 +168,26 @@ public void drawValues(Canvas c) { BubbleEntry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); - drawValue(c, dataSet.getValueFormatter(), entry.getSize(), entry, i, x, - y + (0.5f * lineHeight), valueTextColor); + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, dataSet.getValueFormatter(), entry.getSize(), entry, i, x, + y + (0.5f * lineHeight), valueTextColor); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } } + + MPPointF.recycleInstance(iconsOffset); } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index efa4c5d012..e4c06fe46c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -3,6 +3,7 @@ import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.data.CandleData; @@ -12,6 +13,7 @@ import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -277,6 +279,10 @@ public void drawValues(Canvas c) { float yOffset = Utils.convertDpToPixel(5f); + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + for (int j = 0; j < positions.length; j += 2) { float x = positions[j]; @@ -290,9 +296,33 @@ public void drawValues(Canvas c) { CandleEntry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); - drawValue(c, dataSet.getValueFormatter(), entry.getHigh(), entry, i, x, y - yOffset, dataSet - .getValueTextColor(j / 2)); + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + dataSet.getValueFormatter(), + entry.getHigh(), + entry, + i, + x, + y - yOffset, + dataSet + .getValueTextColor(j / 2)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } } + + MPPointF.recycleInstance(iconsOffset); } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java index 905c6449cb..e8e5446f4d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java @@ -6,6 +6,7 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Paint.Style; +import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.data.Entry; @@ -13,6 +14,7 @@ import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index 0a910f6f89..a1e1650865 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -4,6 +4,7 @@ import android.graphics.Canvas; import android.graphics.Paint.Align; import android.graphics.RectF; +import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.buffer.BarBuffer; @@ -15,6 +16,7 @@ import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -172,6 +174,10 @@ public void drawValues(Canvas c) { final float phaseY = mAnimator.getPhaseY(); + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + // if only single values are drawn (sum) if (!dataSet.isStacked()) { @@ -188,9 +194,9 @@ public void drawValues(Canvas c) { if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1])) continue; - BarEntry e = dataSet.getEntryForIndex(j / 4); - float val = e.getY(); - String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); + BarEntry entry = dataSet.getEntryForIndex(j / 4); + float val = entry.getY(); + String formattedValue = formatter.getFormattedValue(val, entry, i, mViewPortHandler); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); @@ -202,8 +208,32 @@ public void drawValues(Canvas c) { negOffset = -negOffset - valueTextWidth; } - drawValue(c, formattedValue, buffer.buffer[j + 2] + (val >= 0 ? posOffset : negOffset), - y + halfTextHeight, dataSet.getValueTextColor(j / 2)); + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + formattedValue, + buffer.buffer[j + 2] + (val >= 0 ? posOffset : negOffset), + y + halfTextHeight, + dataSet.getValueTextColor(j / 2)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float px = buffer.buffer[j + 2] + (val >= 0 ? posOffset : negOffset); + float py = y; + + px += iconsOffset.x; + py += iconsOffset.y; + + Utils.drawImage( + c, + icon, + (int)px, + (int)py, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } } // if each value of a potential stack should be drawn @@ -216,10 +246,10 @@ public void drawValues(Canvas c) { while (index < dataSet.getEntryCount() * mAnimator.getPhaseX()) { - BarEntry e = dataSet.getEntryForIndex(index); + BarEntry entry = dataSet.getEntryForIndex(index); int color = dataSet.getValueTextColor(index); - float[] vals = e.getYVals(); + float[] vals = entry.getYVals(); // we still draw stacked bars, but there is one // non-stacked @@ -235,8 +265,9 @@ public void drawValues(Canvas c) { if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[bufferIndex + 1])) continue; - float val = e.getY(); - String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); + float val = entry.getY(); + String formattedValue = formatter.getFormattedValue(val, + entry, i, mViewPortHandler); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); @@ -248,16 +279,39 @@ public void drawValues(Canvas c) { negOffset = -negOffset - valueTextWidth; } - drawValue(c, formattedValue, buffer.buffer[bufferIndex + 2] - + (e.getY() >= 0 ? posOffset : negOffset), - buffer.buffer[bufferIndex + 1] + halfTextHeight, color); + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, formattedValue, + buffer.buffer[bufferIndex + 2] + + (entry.getY() >= 0 ? posOffset : negOffset), + buffer.buffer[bufferIndex + 1] + halfTextHeight, color); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float px = buffer.buffer[bufferIndex + 2] + + (entry.getY() >= 0 ? posOffset : negOffset); + float py = buffer.buffer[bufferIndex + 1]; + + px += iconsOffset.x; + py += iconsOffset.y; + + Utils.drawImage( + c, + icon, + (int)px, + (int)py, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } } else { float[] transformed = new float[vals.length * 2]; float posY = 0f; - float negY = -e.getNegativeSum(); + float negY = -entry.getNegativeSum(); for (int k = 0, idx = 0; k < transformed.length; k += 2, idx++) { @@ -283,7 +337,8 @@ public void drawValues(Canvas c) { for (int k = 0; k < transformed.length; k += 2) { final float val = vals[k / 2]; - String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); + String formattedValue = formatter.getFormattedValue(val, + entry, i, mViewPortHandler); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); @@ -312,7 +367,22 @@ public void drawValues(Canvas c) { if (!mViewPortHandler.isInBoundsBottom(y)) continue; - drawValue(c, formattedValue, x, y + halfTextHeight, color); + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, formattedValue, x, y + halfTextHeight, color); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } } } @@ -320,6 +390,8 @@ public void drawValues(Canvas c) { index++; } } + + MPPointF.recycleInstance(iconsOffset); } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 1f46e5b7ef..67633ac19a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -18,7 +18,9 @@ import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; import java.lang.ref.WeakReference; @@ -545,6 +547,10 @@ public void drawValues(Canvas c) { float[] positions = trans.generateTransformedValuesLine(dataSet, mAnimator.getPhaseX(), mAnimator .getPhaseY(), mXBounds.min, mXBounds.max); + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + for (int j = 0; j < positions.length; j += 2) { float x = positions[j]; @@ -558,9 +564,26 @@ public void drawValues(Canvas c) { Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); - drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, - y - valOffset, dataSet.getValueTextColor(j / 2)); + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, + y - valOffset, dataSet.getValueTextColor(j / 2)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } } + + MPPointF.recycleInstance(iconsOffset); } } } 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 84fda6e5f2..be72a0834f 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 @@ -9,6 +9,7 @@ import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.RectF; +import android.graphics.drawable.Drawable; import android.os.Build; import android.text.Layout; import android.text.StaticLayout; @@ -444,6 +445,10 @@ public void drawValues(Canvas c) { final float sliceSpace = getSliceSpace(dataSet); + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + for (int j = 0; j < entryCount; j++) { PieEntry entry = dataSet.getEntryForIndex(j); @@ -588,8 +593,27 @@ public void drawValues(Canvas c) { } } + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float x = (labelRadius + iconsOffset.y) * sliceXBase + center.x; + float y = (labelRadius + iconsOffset.y) * sliceYBase + center.y; + y += iconsOffset.x; + + Utils.drawImage( + c, + icon, + (int)x, + (int)y, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + xIndex++; } + + MPPointF.recycleInstance(iconsOffset); } MPPointF.recycleInstance(center); c.restore(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java index 6c05b08dca..dbf0e8f807 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java @@ -160,6 +160,7 @@ public void drawValues(Canvas c) { MPPointF center = mChart.getCenterOffsets(); MPPointF pOut = MPPointF.getInstance(0,0); + MPPointF pIcon = MPPointF.getInstance(0,0); float yoffset = Utils.convertDpToPixel(5f); @@ -173,6 +174,10 @@ public void drawValues(Canvas c) { // apply the text-styling defined by the DataSet applyValueTextStyle(dataSet); + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + for (int j = 0; j < dataSet.getEntryCount(); j++) { RadarEntry entry = dataSet.getEntryForIndex(j); @@ -183,13 +188,47 @@ public void drawValues(Canvas c) { sliceangle * j * phaseX + mChart.getRotationAngle(), pOut); - drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, pOut.x, pOut.y - yoffset, dataSet.getValueTextColor - (j)); + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + dataSet.getValueFormatter(), + entry.getY(), + entry, + i, + pOut.x, + pOut.y - yoffset, + dataSet.getValueTextColor + (j)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.getPosition( + center, + (entry.getY()) * factor * phaseY + iconsOffset.y, + sliceangle * j * phaseX + mChart.getRotationAngle(), + pIcon); + + //noinspection SuspiciousNameCombination + pIcon.y += iconsOffset.x; + + Utils.drawImage( + c, + icon, + (int)pIcon.x, + (int)pIcon.y, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } } + + MPPointF.recycleInstance(iconsOffset); } MPPointF.recycleInstance(center); MPPointF.recycleInstance(pOut); + MPPointF.recycleInstance(pIcon); } @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index 9c4fdac24e..36a38a53c5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -2,6 +2,7 @@ package com.github.mikephil.charting.renderer; import android.graphics.Canvas; +import android.graphics.drawable.Drawable; import android.util.Log; import com.github.mikephil.charting.animation.ChartAnimator; @@ -12,6 +13,7 @@ import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -113,6 +115,10 @@ public void drawValues(Canvas c) { float shapeSize = Utils.convertDpToPixel(dataSet.getScatterShapeSize()); + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + for (int j = 0; j < positions.length; j += 2) { if (!mViewPortHandler.isInBoundsRight(positions[j])) @@ -125,9 +131,32 @@ public void drawValues(Canvas c) { Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); - drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, positions[j], - positions[j + 1] - shapeSize, dataSet.getValueTextColor(j / 2 + mXBounds.min)); + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + dataSet.getValueFormatter(), + entry.getY(), + entry, + i, + positions[j], + positions[j + 1] - shapeSize, + dataSet.getValueTextColor(j / 2 + mXBounds.min)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(positions[j] + iconsOffset.x), + (int)(positions[j + 1] + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } } + + MPPointF.recycleInstance(iconsOffset); } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java index 36dd6d33ea..fb5a00f508 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java @@ -35,6 +35,17 @@ public static MPPointF getInstance(float x, float y) { return result; } + public static MPPointF getInstance() { + return pool.get(); + } + + public static MPPointF getInstance(MPPointF copy) { + MPPointF result = pool.get(); + result.x = copy.x; + result.y = copy.y; + return result; + } + public static void recycleInstance(MPPointF instance){ pool.recycle(instance); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java index b44f9d9ea7..c302673919 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java @@ -7,12 +7,15 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.os.Build; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.util.DisplayMetrics; import android.util.Log; +import android.util.SizeF; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; @@ -107,14 +110,9 @@ public static float convertDpToPixel(float dp) { " calling Utils.convertDpToPixel(...). Otherwise conversion does not " + "take place."); return dp; - // throw new IllegalStateException( - // "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before - // calling Utils.convertDpToPixel(...)."); } - DisplayMetrics metrics = mMetrics; - float px = dp * (metrics.densityDpi / 160f); - return px; + return dp * mMetrics.density; } /** @@ -133,14 +131,9 @@ public static float convertPixelsToDp(float px) { " calling Utils.convertPixelsToDp(...). Otherwise conversion does not" + " take place."); return px; - // throw new IllegalStateException( - // "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before - // calling Utils.convertPixelsToDp(...)."); } - DisplayMetrics metrics = mMetrics; - float dp = px / (metrics.densityDpi / 160f); - return dp; + return px / mMetrics.density; } /** @@ -532,6 +525,31 @@ public static float getNormalizedAngle(float angle) { return angle % 360.f; } + private static Rect mDrawableBoundsCache = new Rect(); + + public static void drawImage(Canvas canvas, + Drawable drawable, + int x, int y, + int width, int height) { + + MPPointF drawOffset = MPPointF.getInstance(); + drawOffset.x = x - (width / 2); + drawOffset.y = y - (height / 2); + + drawable.copyBounds(mDrawableBoundsCache); + drawable.setBounds( + mDrawableBoundsCache.left, + mDrawableBoundsCache.top, + mDrawableBoundsCache.left + width, + mDrawableBoundsCache.top + width); + + int saveId = canvas.save(); + // translate to the correct position and draw + canvas.translate(drawOffset.x, drawOffset.y); + drawable.draw(canvas); + canvas.restoreToCount(saveId); + } + private static Rect mDrawTextRectBuffer = new Rect(); private static Paint.FontMetrics mFontMetricsBuffer = new Paint.FontMetrics(); From 144af7b295076d85f6deeb94e0e77796ec7fa5f1 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 20 Feb 2017 13:33:53 +0200 Subject: [PATCH 136/291] Added examples for icon entries --- MPChartExample/res/drawable-hdpi/star.png | Bin 0 -> 1221 bytes MPChartExample/res/menu/bar.xml | 4 +++ MPChartExample/res/menu/bubble.xml | 4 +++ MPChartExample/res/menu/line.xml | 4 +++ MPChartExample/res/menu/pie.xml | 4 +++ MPChartExample/res/menu/radar.xml | 4 +++ MPChartExample/res/menu/scatter.xml | 4 +++ .../mpchartexample/BarChartActivity.java | 17 +++++++++++- .../mpchartexample/BubbleChartActivity.java | 17 ++++++++++-- .../CandleStickChartActivity.java | 26 ++++++++++++++++-- .../HorizontalBarChartActivity.java | 18 +++++++++++- .../mpchartexample/LineChartActivity1.java | 17 +++++++++++- .../mpchartexample/PieChartActivity.java | 16 ++++++++++- .../mpchartexample/StackedBarActivity.java | 19 ++++++++++++- .../StackedBarActivityNegative.java | 15 ++++++++++ 15 files changed, 160 insertions(+), 9 deletions(-) create mode 100644 MPChartExample/res/drawable-hdpi/star.png diff --git a/MPChartExample/res/drawable-hdpi/star.png b/MPChartExample/res/drawable-hdpi/star.png new file mode 100644 index 0000000000000000000000000000000000000000..c7811ef2b89b0097f0c64f26718cc07e5a5b64b8 GIT binary patch literal 1221 zcmV;$1UmbPP)U=4^(ZyKr5oQs+34wu%k3i zNrRP02!%k}R7xn6hXLd3$C)m$1Abpf`mL-pbLRVIe9oD<1dF&C47!xayF?TakzQhM zS4ugv<`@CXL=;uPlm=Yy8ykyRiA&WSrh~z50)|-R0+<~Z_3i{Nx z4%V9S_?HZP0zk7!T!^%YdaaVv3{NeCoRRJ@> z;G0793j^NgM7|G^)r`oQ{_*j-vI4T1|7wYx2;LKo0D!eS>y||bk^gWdIQL^N6spUX z-BRX_-7d5STF@G3fxF9PO#qRPfFEYgovSGlunT-)`9CMXd+I=^ug?ci)eDtF4>=s4 z=Lwh&2D`JT-4YtQ+^F#$+{jv=1MW^|g&jrsbS4z)d@6u|Ay~a?M0wfgP-?9LY$P%S z05p64ygQ}qPpm#N?pK{?d;jH6YOp~>%_NeWDI~X&5G6&v%v_5^;;fB`0)-jC0S&P30N){L?eO+cHH@{;!IHze|*0P4|J3)aTZEsiHRs$DG&o_ z_7L_>shqtl-rBvKZ zCU0#s0Ad~+n4H|P0RS?YVQAV%0Boh4YYQrnPAV+je2k^v zo&Zef^f%*`lg-d{KD4K{EV)uDB{th-Alrh!zISi^ z%&!;vZ+jS?HYfg$uEG`v8Vq~U^~#7~GnIm>0bV@e!`g!woSVg6P?U7O}^7n + + diff --git a/MPChartExample/res/menu/bubble.xml b/MPChartExample/res/menu/bubble.xml index a25afd9861..b7950291e9 100644 --- a/MPChartExample/res/menu/bubble.xml +++ b/MPChartExample/res/menu/bubble.xml @@ -5,6 +5,10 @@ android:id="@+id/actionToggleValues" android:title="Toggle Values"> + + diff --git a/MPChartExample/res/menu/line.xml b/MPChartExample/res/menu/line.xml index aea6845249..f9f5be9615 100644 --- a/MPChartExample/res/menu/line.xml +++ b/MPChartExample/res/menu/line.xml @@ -5,6 +5,10 @@ android:id="@+id/actionToggleValues" android:title="Toggle Values"> + + diff --git a/MPChartExample/res/menu/pie.xml b/MPChartExample/res/menu/pie.xml index fd315839a5..0e5323a590 100644 --- a/MPChartExample/res/menu/pie.xml +++ b/MPChartExample/res/menu/pie.xml @@ -5,6 +5,10 @@ android:id="@+id/actionToggleValues" android:title="Toggle Y-Values"> + + diff --git a/MPChartExample/res/menu/radar.xml b/MPChartExample/res/menu/radar.xml index 3565065baa..14690f446c 100644 --- a/MPChartExample/res/menu/radar.xml +++ b/MPChartExample/res/menu/radar.xml @@ -5,6 +5,10 @@ android:id="@+id/actionToggleValues" android:title="Toggle Values"> + + diff --git a/MPChartExample/res/menu/scatter.xml b/MPChartExample/res/menu/scatter.xml index a25afd9861..b7950291e9 100644 --- a/MPChartExample/res/menu/scatter.xml +++ b/MPChartExample/res/menu/scatter.xml @@ -5,6 +5,10 @@ android:id="@+id/actionToggleValues" android:title="Toggle Values"> + + diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 427c46756a..5772359773 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -153,6 +153,13 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleIcons: { + for (IDataSet set : mChart.getData().getDataSets()) + set.setDrawIcons(!set.isDrawIconsEnabled()); + + mChart.invalidate(); + break; + } case R.id.actionToggleHighlight: { if (mChart.getData() != null) { mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); @@ -236,7 +243,12 @@ private void setData(int count, float range) { for (int i = (int) start; i < start + count + 1; i++) { float mult = (range + 1); float val = (float) (Math.random() * mult); - yVals1.add(new BarEntry(i, val)); + + if (Math.random() * 100 < 25) { + yVals1.add(new BarEntry(i, val, getResources().getDrawable(R.drawable.star))); + } else { + yVals1.add(new BarEntry(i, val)); + } } BarDataSet set1; @@ -249,6 +261,9 @@ private void setData(int count, float range) { mChart.notifyDataSetChanged(); } else { set1 = new BarDataSet(yVals1, "The year 2017"); + + set1.setDrawIcons(false); + set1.setColors(ColorTemplate.MATERIAL_COLORS); ArrayList dataSets = new ArrayList(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java index db72f29495..0ecc1e9c93 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -25,6 +25,7 @@ import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointF; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -108,6 +109,13 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleIcons: { + for (IDataSet set : mChart.getData().getDataSets()) + set.setDrawIcons(!set.isDrawIconsEnabled()); + + mChart.invalidate(); + break; + } case R.id.actionToggleHighlight: { if(mChart.getData() != null) { mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); @@ -167,14 +175,14 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { float val = (float) (Math.random() * range); float size = (float) (Math.random() * range); - yVals1.add(new BubbleEntry(i, val, size)); + yVals1.add(new BubbleEntry(i, val, size, getResources().getDrawable(R.drawable.star))); } for (int i = 0; i < count; i++) { float val = (float) (Math.random() * range); float size = (float) (Math.random() * range); - yVals2.add(new BubbleEntry(i, val, size)); + yVals2.add(new BubbleEntry(i, val, size, getResources().getDrawable(R.drawable.star))); } for (int i = 0; i < count; i++) { @@ -186,11 +194,16 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // create a dataset and give it a type BubbleDataSet set1 = new BubbleDataSet(yVals1, "DS 1"); + set1.setDrawIcons(false); set1.setColor(ColorTemplate.COLORFUL_COLORS[0], 130); set1.setDrawValues(true); + BubbleDataSet set2 = new BubbleDataSet(yVals2, "DS 2"); + set2.setDrawIcons(false); + set2.setIconsOffset(new MPPointF(0, 15)); set2.setColor(ColorTemplate.COLORFUL_COLORS[1], 130); set2.setDrawValues(true); + BubbleDataSet set3 = new BubbleDataSet(yVals3, "DS 3"); set3.setColor(ColorTemplate.COLORFUL_COLORS[2], 130); set3.setDrawValues(true); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java index 3921215be6..bd8dde108f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -21,6 +21,7 @@ import com.github.mikephil.charting.data.CandleDataSet; import com.github.mikephil.charting.data.CandleEntry; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -92,6 +93,20 @@ public boolean onCreateOptionsMenu(Menu menu) { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { + case R.id.actionToggleValues: { + for (IDataSet set : mChart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + mChart.invalidate(); + break; + } + case R.id.actionToggleIcons: { + for (IDataSet set : mChart.getData().getDataSets()) + set.setDrawIcons(!set.isDrawIconsEnabled()); + + mChart.invalidate(); + break; + } case R.id.actionToggleHighlight: { if(mChart.getData() != null) { mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); @@ -171,11 +186,18 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { boolean even = i % 2 == 0; - yVals1.add(new CandleEntry(i, val + high, val - low, even ? val + open : val - open, - even ? val - close : val + close)); + yVals1.add(new CandleEntry( + i, val + high, + val - low, + even ? val + open : val - open, + even ? val - close : val + close, + getResources().getDrawable(R.drawable.star) + )); } CandleDataSet set1 = new CandleDataSet(yVals1, "Data Set"); + + set1.setDrawIcons(false); set1.setAxisDependency(AxisDependency.LEFT); // set1.setColor(Color.rgb(80, 80, 80)); set1.setShadowColor(Color.DKGRAY); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index f5ea95098b..d68b75cc15 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -138,6 +138,19 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleIcons: { + List sets = mChart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + IBarDataSet set = (BarDataSet) iSet; + set.setDrawIcons(!set.isDrawIconsEnabled()); + } + + mChart.invalidate(); + break; + } case R.id.actionToggleHighlight: { if(mChart.getData() != null) { mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); @@ -223,7 +236,8 @@ private void setData(int count, float range) { for (int i = 0; i < count; i++) { float val = (float) (Math.random() * range); - yVals1.add(new BarEntry(i * spaceForBar, val)); + yVals1.add(new BarEntry(i * spaceForBar, val, + getResources().getDrawable(R.drawable.star))); } BarDataSet set1; @@ -237,6 +251,8 @@ private void setData(int count, float range) { } else { set1 = new BarDataSet(yVals1, "DataSet 1"); + set1.setDrawIcons(false); + ArrayList dataSets = new ArrayList(); dataSets.add(set1); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index f6747bbf6f..b18309a26a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -190,6 +190,19 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleIcons: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setDrawIcons(!set.isDrawIconsEnabled()); + } + + mChart.invalidate(); + break; + } case R.id.actionToggleHighlight: { if(mChart.getData() != null) { mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); @@ -342,7 +355,7 @@ private void setData(int count, float range) { for (int i = 0; i < count; i++) { float val = (float) (Math.random() * range) + 3; - values.add(new Entry(i, val)); + values.add(new Entry(i, val, getResources().getDrawable(R.drawable.star))); } LineDataSet set1; @@ -357,6 +370,8 @@ private void setData(int count, float range) { // create a dataset and give it a type set1 = new LineDataSet(values, "DataSet 1"); + set1.setDrawIcons(false); + // set the line to be drawn like this "- - - - - -" set1.enableDashedLine(10f, 5f, 0f); set1.enableDashedHighlightLine(10f, 5f, 0f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 4493da1f97..085580a923 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -29,6 +29,7 @@ import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointF; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -127,6 +128,13 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleIcons: { + for (IDataSet set : mChart.getData().getDataSets()) + set.setDrawIcons(!set.isDrawIconsEnabled()); + + mChart.invalidate(); + break; + } case R.id.actionToggleHole: { if (mChart.isDrawHoleEnabled()) mChart.setDrawHoleEnabled(false); @@ -197,11 +205,17 @@ private void setData(int count, float range) { // 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) ((Math.random() * mult) + mult / 5), mParties[i % mParties.length])); + entries.add(new PieEntry((float) ((Math.random() * mult) + mult / 5), + mParties[i % mParties.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 diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 31ad84e5ac..70afd7c4c6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -122,6 +122,19 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleIcons: { + List sets = mChart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + BarDataSet set = (BarDataSet) iSet; + set.setDrawIcons(!set.isDrawIconsEnabled()); + } + + mChart.invalidate(); + break; + } case R.id.actionToggleHighlight: { if (mChart.getData() != null) { mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); @@ -188,7 +201,10 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { float val2 = (float) (Math.random() * mult) + mult / 3; float val3 = (float) (Math.random() * mult) + mult / 3; - yVals1.add(new BarEntry(i, new float[]{val1, val2, val3})); + yVals1.add(new BarEntry( + i, + new float[]{val1, val2, val3}, + getResources().getDrawable(R.drawable.star))); } BarDataSet set1; @@ -201,6 +217,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mChart.notifyDataSetChanged(); } else { set1 = new BarDataSet(yVals1, "Statistics Vienna 2014"); + set1.setDrawIcons(false); set1.setColors(getColors()); set1.setStackLabels(new String[]{"Births", "Divorces", "Marriages"}); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 01d4f2f89b..d5e0f8c885 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -103,6 +103,7 @@ public String getFormattedValue(float value, AxisBase axis) { yValues.add(new BarEntry(25, new float[]{ -15, 15 })); yValues.add(new BarEntry(35, new float[]{ -17, 17 })); yValues.add(new BarEntry(45, new float[]{ -19, 20 })); + yValues.add(new BarEntry(45, new float[]{ -19, 20 }, getResources().getDrawable(R.drawable.star))); yValues.add(new BarEntry(55, new float[]{ -19, 19 })); yValues.add(new BarEntry(65, new float[]{ -16, 16 })); yValues.add(new BarEntry(75, new float[]{ -13, 14 })); @@ -111,6 +112,7 @@ public String getFormattedValue(float value, AxisBase axis) { yValues.add(new BarEntry(105, new float[]{ -1, 2 })); BarDataSet set = new BarDataSet(yValues, "Age Distribution"); + set.setDrawIcons(false); set.setValueFormatter(new CustomFormatter()); set.setValueTextSize(7f); set.setAxisDependency(YAxis.AxisDependency.RIGHT); @@ -150,6 +152,19 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleIcons: { + List sets = mChart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + BarDataSet set = (BarDataSet) iSet; + set.setDrawIcons(!set.isDrawIconsEnabled()); + } + + mChart.invalidate(); + break; + } case R.id.actionToggleHighlight: { if(mChart.getData() != null) { mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); From 94957fad3369a6c495ceea532035b6e1143e55a8 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 20 Feb 2017 13:55:16 +0200 Subject: [PATCH 137/291] Improved feb29 formula --- .../custom/DayAxisValueFormatter.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java index 6edffb9315..86a602eb39 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -73,14 +73,14 @@ private int getDaysForMonth(int month, int year) { // month is 0-based if (month == 1) { - int x400 = month % 400; - if (x400 < 0) - { - x400 = -x400; - } - boolean is29 = (month % 4) == 0 && x400 != 100 && x400 != 200 && x400 != 300; + boolean is29Feb = false; + + if (year < 1582) + is29Feb = (year < 1 ? year + 1 : year) % 4 == 0; + else if (year > 1582) + is29Feb = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); - return is29 ? 29 : 28; + return is29Feb ? 29 : 28; } if (month == 3 || month == 5 || month == 8 || month == 10) From a5e0a9b86c60d453c64f009e01bd2596ab4ff31c Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 21 Feb 2017 14:24:13 +0200 Subject: [PATCH 138/291] Moved auto scale before render of axis lines https://github.com/danielgindi/Charts/pull/2177 --- .../github/mikephil/charting/charts/BarLineChartBase.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index 745d6019e0..e9b993139f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -201,14 +201,14 @@ protected void onDraw(Canvas canvas) { if (mXAxis.isEnabled()) mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); - mXAxisRenderer.renderAxisLine(canvas); - mAxisRendererLeft.renderAxisLine(canvas); - mAxisRendererRight.renderAxisLine(canvas); - if (mAutoScaleMinMaxEnabled) { autoScale(); } + mXAxisRenderer.renderAxisLine(canvas); + mAxisRendererLeft.renderAxisLine(canvas); + mAxisRendererRight.renderAxisLine(canvas); + mXAxisRenderer.renderGridLines(canvas); mAxisRendererLeft.renderGridLines(canvas); mAxisRendererRight.renderGridLines(canvas); From 0c919ab02d5919ac3ac76c9f0fb0642a19153f20 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Feb 2017 17:29:19 +0200 Subject: [PATCH 139/291] Consider isEnabled in more axis rendering cases --- .../charting/charts/BarLineChartBase.java | 33 +++++++++++-------- .../mikephil/charting/charts/RadarChart.java | 6 +++- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index e9b993139f..0dccacc02d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -194,17 +194,19 @@ protected void onDraw(Canvas canvas) { // execute all drawing commands drawGridBackground(canvas); + if (mAutoScaleMinMaxEnabled) { + autoScale(); + } + if (mAxisLeft.isEnabled()) mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum, mAxisLeft.isInverted()); + if (mAxisRight.isEnabled()) mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum, mAxisRight.isInverted()); + if (mXAxis.isEnabled()) mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); - if (mAutoScaleMinMaxEnabled) { - autoScale(); - } - mXAxisRenderer.renderAxisLine(canvas); mAxisRendererLeft.renderAxisLine(canvas); mAxisRendererRight.renderAxisLine(canvas); @@ -213,13 +215,13 @@ protected void onDraw(Canvas canvas) { mAxisRendererLeft.renderGridLines(canvas); mAxisRendererRight.renderGridLines(canvas); - if (mXAxis.isDrawLimitLinesBehindDataEnabled()) + if (mXAxis.isEnabled() && mXAxis.isDrawLimitLinesBehindDataEnabled()) mXAxisRenderer.renderLimitLines(canvas); - if (mAxisLeft.isDrawLimitLinesBehindDataEnabled()) + if (mAxisLeft.isEnabled() && mAxisLeft.isDrawLimitLinesBehindDataEnabled()) mAxisRendererLeft.renderLimitLines(canvas); - if (mAxisRight.isDrawLimitLinesBehindDataEnabled()) + if (mAxisRight.isEnabled() && mAxisRight.isDrawLimitLinesBehindDataEnabled()) mAxisRendererRight.renderLimitLines(canvas); // make sure the data cannot be drawn outside the content-rect @@ -237,13 +239,13 @@ protected void onDraw(Canvas canvas) { mRenderer.drawExtras(canvas); - if (!mXAxis.isDrawLimitLinesBehindDataEnabled()) + if (mXAxis.isEnabled() && !mXAxis.isDrawLimitLinesBehindDataEnabled()) mXAxisRenderer.renderLimitLines(canvas); - if (!mAxisLeft.isDrawLimitLinesBehindDataEnabled()) + if (mAxisLeft.isEnabled() && !mAxisLeft.isDrawLimitLinesBehindDataEnabled()) mAxisRendererLeft.renderLimitLines(canvas); - if (!mAxisRight.isDrawLimitLinesBehindDataEnabled()) + if (mAxisRight.isEnabled() && !mAxisRight.isDrawLimitLinesBehindDataEnabled()) mAxisRendererRight.renderLimitLines(canvas); mXAxisRenderer.renderAxisLabels(canvas); @@ -347,9 +349,14 @@ protected void autoScale() { mXAxis.calculate(mData.getXMin(), mData.getXMax()); // calculate axis range (min / max) according to provided data - mAxisLeft.calculate(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); - mAxisRight.calculate(mData.getYMin(AxisDependency.RIGHT), mData.getYMax(AxisDependency - .RIGHT)); + + if (mAxisLeft.isEnabled()) + mAxisLeft.calculate(mData.getYMin(AxisDependency.LEFT), + mData.getYMax(AxisDependency.LEFT)); + + if (mAxisRight.isEnabled()) + mAxisRight.calculate(mData.getYMin(AxisDependency.RIGHT), + mData.getYMax(AxisDependency.RIGHT)); calculateOffsets(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java index 1cc1bd6d60..3c9aec0dde 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java @@ -137,13 +137,17 @@ protected void onDraw(Canvas canvas) { if (mDrawWeb) mRenderer.drawExtras(canvas); - mYAxisRenderer.renderLimitLines(canvas); + if (mYAxis.isEnabled() && mYAxis.isDrawLimitLinesBehindDataEnabled()) + mYAxisRenderer.renderLimitLines(canvas); mRenderer.drawData(canvas); if (valuesToHighlight()) mRenderer.drawHighlighted(canvas, mIndicesToHighlight); + if (mYAxis.isEnabled() && !mYAxis.isDrawLimitLinesBehindDataEnabled()) + mYAxisRenderer.renderLimitLines(canvas); + mYAxisRenderer.renderAxisLabels(canvas); mRenderer.drawValues(canvas); From 6c54f0b0a91ca87fe39dd63d9095e4f55d6c59ff Mon Sep 17 00:00:00 2001 From: Stephen McBride Date: Tue, 28 Feb 2017 15:58:42 +1300 Subject: [PATCH 140/291] Fix for missing setters in getInstance method The zoomAndCenterAnimated method in BarLineChartBase crashes with a NullPointer exception because the yAxis variable is null when onAnimationUpdate is called. The yAxis is null because of missing setters in the getInstance method of AnimatedZoomJob. --- .../java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java index 0157e8fa76..e5e4c417d3 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java @@ -33,6 +33,8 @@ public static AnimatedZoomJob getInstance(ViewPortHandler viewPortHandler, View result.view = v; result.xOrigin = xOrigin; result.yOrigin = yOrigin; + result.yAxis = axis; + result.xAxisRange = xAxisRange; result.resetAnimator(); result.animator.setDuration(duration); return result; From 993d273a5249f615004234d794f69971b05dc37f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 14 Mar 2017 10:39:05 +0100 Subject: [PATCH 141/291] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8b25078be2..c4bbb33027 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Remember: *It's all about the looks.* +## An app using this library is featured on Product Hunt, [check it out](https://www.producthunt.com/posts/myalfred)! ![alt tag](https://raw.github.com/PhilJay/MPChart/master/design/feature_graphic.png) From 2eb12901a9847fc4d559fc1c0d3b8b275a0cad41 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 14 Mar 2017 10:39:45 +0100 Subject: [PATCH 142/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c4bbb33027..3beea7c468 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Remember: *It's all about the looks.* -## An app using this library is featured on Product Hunt, [check it out](https://www.producthunt.com/posts/myalfred)! +## Our app which uses this library is featured on Product Hunt, [check it out](https://www.producthunt.com/posts/myalfred)! ![alt tag](https://raw.github.com/PhilJay/MPChart/master/design/feature_graphic.png) From 6df13dd66e49650c08231030a3c76d95587a436c Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 15 Mar 2017 15:55:23 +0100 Subject: [PATCH 143/291] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 3beea7c468..30759eb357 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,6 @@ Remember: *It's all about the looks.* -## Our app which uses this library is featured on Product Hunt, [check it out](https://www.producthunt.com/posts/myalfred)! - ![alt tag](https://raw.github.com/PhilJay/MPChart/master/design/feature_graphic.png) [**MPAndroidChart**](https://github.com/PhilJay/MPAndroidChart) :zap: is a powerful & easy to use chart library for Android. It runs on [API level 8](http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels) and upwards. From b564704b8272ab8091b3bbc04271a53993bcf07b Mon Sep 17 00:00:00 2001 From: PhilJay Date: Thu, 23 Mar 2017 16:13:21 +0100 Subject: [PATCH 144/291] Updating versions --- MPChartExample/AndroidManifest.xml | 6 +++--- MPChartExample/build.gradle | 12 ++++++------ MPChartLib/build.gradle | 8 ++++---- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index b1ac584a2e..87d8b72402 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -1,12 +1,12 @@ + android:versionCode="55" + android:versionName="3.0.2" > + android:targetSdkVersion="25" /> Date: Thu, 23 Mar 2017 16:15:16 +0100 Subject: [PATCH 145/291] Remove line width minimum constraint --- .../com/github/mikephil/charting/data/LineRadarDataSet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java index deced96ee9..6971144e14 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java @@ -101,8 +101,8 @@ public void setFillAlpha(int alpha) { */ public void setLineWidth(float width) { - if (width < 0.2f) - width = 0.2f; + if (width < 0.0f) + width = 0.0f; if (width > 10.0f) width = 10.0f; mLineWidth = Utils.convertDpToPixel(width); From 208bf181addad9b62427f0532dcb1f426ba38c39 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 23 Mar 2017 16:23:45 +0100 Subject: [PATCH 146/291] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 30759eb357..406e9a9f2e 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ If you are having questions or problems, you should: - **Review your code**. Make absolutely sure that everything is correct on your side. - Make sure you are using the **latest version** of the library. Check the [**release-section**](https://github.com/PhilJay/MPAndroidChart/releases). - - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.1/javadoc/) + - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.2/javadoc/) - Search or open questions on [**stackoverflow**](https://stackoverflow.com/search?q=mpandroidchart) with the `mpandroidchart` tag - Search [**known issues**](https://github.com/PhilJay/MPAndroidChart/issues) for your problem (open and closed) - Create new issues (please :fire: **search known issues before** :fire:, do not create duplicate issues) @@ -116,7 +116,7 @@ allprojects { ```gradle dependencies { - compile 'com.github.PhilJay:MPAndroidChart:v3.0.1' + compile 'com.github.PhilJay:MPAndroidChart:v3.0.2' } ``` @@ -135,7 +135,7 @@ dependencies { com.github.PhilJay MPAndroidChart - v3.0.1 + v3.0.2 ``` @@ -155,7 +155,7 @@ dependencies { Documentation ----- -For a **detailed documentation** :notebook_with_decorative_cover:, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.1/javadoc/). +For a **detailed documentation** :notebook_with_decorative_cover:, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.2/javadoc/). Furthermore, you can also rely on the [**MPChartExample**](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample) folder and check out the example code in that project. The corresponding application to the example project is also [**available in the Google PlayStore**](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample). From f6a398b6b230e09da081f34bd135dd1793bbef09 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 24 Mar 2017 11:36:12 +0100 Subject: [PATCH 147/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 406e9a9f2e..11d0d7cc32 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![Twitter](https://img.shields.io/badge/Twitter-@PhilippJahoda-blue.svg?style=flat)](http://twitter.com/philippjahoda) [![Twitter](https://img.shields.io/badge/Twitter-@mpandroidchart-blue.svg?style=flat)](http://twitter.com/mpandroidchart) [![Android Arsenal](http://img.shields.io/badge/Android%20Arsenal-MPAndroidChart-orange.svg?style=flat)](http://android-arsenal.com/details/1/741) -[![Release](https://img.shields.io/github/release/PhilJay/MPAndroidChart.svg?label=maven central)](https://jitpack.io/#PhilJay/MPAndroidChart) [![API](https://img.shields.io/badge/API-8%2B-green.svg?style=flat)](https://android-arsenal.com/api?level=8) +[![Release](https://img.shields.io/github/release/PhilJay/MPAndroidChart.svg?style=flat)](https://jitpack.io/#PhilJay/MPAndroidChart) [![API](https://img.shields.io/badge/API-8%2B-green.svg?style=flat)](https://android-arsenal.com/api?level=8) Remember: *It's all about the looks.* From 0af86819eac5afee7d3b0a05b8a14b7d37757f37 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 4 Apr 2017 10:29:38 +0200 Subject: [PATCH 148/291] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 11d0d7cc32..2c23d1da4e 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,12 @@ Donations **PayPal** - - [**Donate 5 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7G52RA87ED8NY): Thank's for creating this project, here's a coffee (or some beer) for you! - - [**Donate 10 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4C9TPE67F5PUQ): Wow, I am stunned. Let me take you to the movies! - - [**Donate 15 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YKMPTFMVK3JMC): I really appreciate your work, let's grab some lunch! - - [**Donate 25 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H9JA4QX7UHXCY): That's some awesome stuff you did right there, dinner is on me! - - [**Donate 50 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZPQVJ2XRBSBYY): I really really want to support this project, great job! - - [**Donate 100 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KY7F59RYPCYCQ): You are the man! This project saved me hours (if not days) of struggle and hard work, simply awesome! + - [**Donate 5 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7G52RA87ED8NY): Thank's for creating this project, here's a coffee (or some beer) for you! + - [**Donate 10 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4C9TPE67F5PUQ): Wow, I am stunned. Let me take you to the movies! + - [**Donate 15 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YKMPTFMVK3JMC): I really appreciate your work, let's grab some lunch! + - [**Donate 25 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H9JA4QX7UHXCY): That's some awesome stuff you did right there, dinner is on me! + - [**Donate 50 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZPQVJ2XRBSBYY): I really really want to support this project, great job! + - [**Donate 100 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KY7F59RYPCYCQ): You are the man! This project saved me hours (if not days) of struggle and hard work, simply awesome! - Of course, you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome! If you just want to be nice, you can check out and rate the new [**Telegram Chat Bot**]( https://storebot.me/bot/myalfred_bot) we created for scheduling meetings and other stuff. 😉 From acf985ff0cd3a6ba894bcad107732b49530d8098 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 26 Apr 2017 20:45:12 +0300 Subject: [PATCH 149/291] Clear lastHighlighted when `clear` is called --- .../src/main/java/com/github/mikephil/charting/charts/Chart.java | 1 + 1 file changed, 1 insertion(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 79f699320b..9e80d9234a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -315,6 +315,7 @@ public void clear() { mData = null; mOffsetsCalculated = false; mIndicesToHighlight = null; + mChartTouchListener.setLastHighlighted(null); invalidate(); } From fcb506c2199e10215ca997aa18085382f5fa0114 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 26 Jun 2017 09:16:23 +0200 Subject: [PATCH 150/291] Create LICENSE --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 43ae497ecf7427587687945496c6a70e65f01fcb Mon Sep 17 00:00:00 2001 From: Scott Kennedy Date: Mon, 26 Jun 2017 10:50:20 -0700 Subject: [PATCH 151/291] Fix some potential NPEs with WeakReference usage Even if the WeakReference field is not null, the contained value may be null. Additionally, you always need a strong reference to the value to ensure it isn't garbage collected while you're using it. --- .../charting/renderer/LineChartRenderer.java | 23 +++++++++++-------- .../charting/renderer/PieChartRenderer.java | 21 ++++++++++------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 9921cb1ba6..a0e1777569 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -76,19 +76,21 @@ public void drawData(Canvas c) { int width = (int) mViewPortHandler.getChartWidth(); int height = (int) mViewPortHandler.getChartHeight(); - if (mDrawBitmap == null - || (mDrawBitmap.get().getWidth() != width) - || (mDrawBitmap.get().getHeight() != height)) { + Bitmap drawBitmap = mDrawBitmap == null ? null : mDrawBitmap.get(); - if (width > 0 && height > 0) { + if (drawBitmap == null + || (drawBitmap.getWidth() != width) + || (drawBitmap.getHeight() != height)) { - mDrawBitmap = new WeakReference(Bitmap.createBitmap(width, height, mBitmapConfig)); - mBitmapCanvas = new Canvas(mDrawBitmap.get()); + if (width > 0 && height > 0) { + drawBitmap = Bitmap.createBitmap(width, height, mBitmapConfig); + mDrawBitmap = new WeakReference<>(drawBitmap); + mBitmapCanvas = new Canvas(drawBitmap); } else return; } - mDrawBitmap.get().eraseColor(Color.TRANSPARENT); + drawBitmap.eraseColor(Color.TRANSPARENT); LineData lineData = mChart.getLineData(); @@ -98,7 +100,7 @@ public void drawData(Canvas c) { drawDataSet(c, set); } - c.drawBitmap(mDrawBitmap.get(), 0, 0, mRenderPaint); + c.drawBitmap(drawBitmap, 0, 0, mRenderPaint); } protected void drawDataSet(Canvas c, ILineDataSet dataSet) { @@ -738,7 +740,10 @@ public void releaseBitmap() { mBitmapCanvas = null; } if (mDrawBitmap != null) { - mDrawBitmap.get().recycle(); + Bitmap drawBitmap = mDrawBitmap.get(); + if (drawBitmap != null) { + drawBitmap.recycle(); + } mDrawBitmap.clear(); mDrawBitmap = null; } 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 be72a0834f..ef401eff44 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 @@ -126,19 +126,21 @@ public void drawData(Canvas c) { int width = (int) mViewPortHandler.getChartWidth(); int height = (int) mViewPortHandler.getChartHeight(); - if (mDrawBitmap == null - || (mDrawBitmap.get().getWidth() != width) - || (mDrawBitmap.get().getHeight() != height)) { + Bitmap drawBitmap = mDrawBitmap == null ? null : mDrawBitmap.get(); - if (width > 0 && height > 0) { + if (drawBitmap == null + || (drawBitmap.getWidth() != width) + || (drawBitmap.getHeight() != height)) { - mDrawBitmap = new WeakReference(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444)); - mBitmapCanvas = new Canvas(mDrawBitmap.get()); + if (width > 0 && height > 0) { + drawBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444); + mDrawBitmap = new WeakReference<>(drawBitmap); + mBitmapCanvas = new Canvas(drawBitmap); } else return; } - mDrawBitmap.get().eraseColor(Color.TRANSPARENT); + drawBitmap.eraseColor(Color.TRANSPARENT); PieData pieData = mChart.getData(); @@ -1010,7 +1012,10 @@ public void releaseBitmap() { mBitmapCanvas = null; } if (mDrawBitmap != null) { - mDrawBitmap.get().recycle(); + Bitmap drawBitmap = mDrawBitmap.get(); + if (drawBitmap != null) { + drawBitmap.recycle(); + } mDrawBitmap.clear(); mDrawBitmap = null; } From ad3c82f634ed3e53aaa6706b2df7632333318921 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 4 Aug 2017 17:05:42 +0200 Subject: [PATCH 152/291] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 2c23d1da4e..9125f75e63 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,6 @@ Donations - [**Donate 100 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KY7F59RYPCYCQ): You are the man! This project saved me hours (if not days) of struggle and hard work, simply awesome! - Of course, you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome! -If you just want to be nice, you can check out and rate the new [**Telegram Chat Bot**]( https://storebot.me/bot/myalfred_bot) we created for scheduling meetings and other stuff. 😉 - ## Xamarin From 4f77a154bf876e43b96de5e82c53a6a77a44c89c Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 11 Aug 2017 16:31:29 +0200 Subject: [PATCH 153/291] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 9125f75e63..0fd4b1cd69 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ Donations - [**Donate 50 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZPQVJ2XRBSBYY): I really really want to support this project, great job! - [**Donate 100 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KY7F59RYPCYCQ): You are the man! This project saved me hours (if not days) of struggle and hard work, simply awesome! - Of course, you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome! + +## Got a question? +[Contact me via 21.co](https://21.co/philjay/) ## Xamarin From 98f97f0df1a98596ee0b76cc9c328e539fd6f9bd Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Fri, 25 Aug 2017 12:13:45 +0300 Subject: [PATCH 154/291] Run view port jobs after applying changes --- .../mikephil/charting/charts/Chart.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 9e80d9234a..ea26f2cfca 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -1676,20 +1676,23 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { Log.i(LOG_TAG, "OnSizeChanged()"); if (w > 0 && h > 0 && w < 10000 && h < 10000) { - - mViewPortHandler.setChartDimens(w, h); - if (mLogEnabled) Log.i(LOG_TAG, "Setting chart dimens, width: " + w + ", height: " + h); + mViewPortHandler.setChartDimens(w, h); + } else { + if (mLogEnabled) + Log.w(LOG_TAG, "*Avoiding* setting chart dimens! width: " + w + ", height: " + h); + } - for (Runnable r : mJobs) { - post(r); - } + // This may cause the chart view to mutate properties affecting the view port -- + // lets do this before we try to run any pending jobs on the view port itself + notifyDataSetChanged(); - mJobs.clear(); + for (Runnable r : mJobs) { + post(r); } - notifyDataSetChanged(); + mJobs.clear(); super.onSizeChanged(w, h, oldw, oldh); } From ed770762fb7ba043f4f1eaeae217fccbbaeec93e Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Fri, 25 Aug 2017 12:15:42 +0300 Subject: [PATCH 155/291] Add default x spacing (half width) for scatter chart as well --- .../java/com/github/mikephil/charting/charts/ScatterChart.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java index decf1e855a..37e8395b5e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java @@ -35,6 +35,9 @@ protected void init() { super.init(); mRenderer = new ScatterChartRenderer(this, mAnimator, mViewPortHandler); + + getXAxis().setSpaceMin(0.5f); + getXAxis().setSpaceMax(0.5f); } @Override From ea93823f6373caf3acfc02e2b42e4c2a73ae05f6 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Fri, 8 Sep 2017 10:13:12 +0300 Subject: [PATCH 156/291] Fix CombinedChartView not drawing markers --- .../charting/charts/CombinedChart.java | 43 ++++++++++++++++++ .../mikephil/charting/data/CombinedData.java | 44 +++++++++++++------ 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java index b1975b973e..cd01f0ef73 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java @@ -2,6 +2,7 @@ package com.github.mikephil.charting.charts; import android.content.Context; +import android.graphics.Canvas; import android.util.AttributeSet; import android.util.Log; @@ -9,11 +10,13 @@ import com.github.mikephil.charting.data.BubbleData; import com.github.mikephil.charting.data.CandleData; import com.github.mikephil.charting.data.CombinedData; +import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.highlight.CombinedHighlighter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.renderer.CombinedChartRenderer; /** @@ -226,4 +229,44 @@ public void setDrawOrder(DrawOrder[] order) { return; mDrawOrder = order; } + + /** + * draws all MarkerViews on the highlighted positions + */ + protected void drawMarkers(Canvas canvas) { + + // if there is no marker view or drawing marker is disabled + if (mMarker == null || !isDrawMarkersEnabled() || !valuesToHighlight()) + return; + + for (int i = 0; i < mIndicesToHighlight.length; i++) { + + Highlight highlight = mIndicesToHighlight[i]; + + IDataSet set = mData.getDataSetByHighlight(highlight); + + Entry e = mData.getEntryForHighlight(highlight); + if (e == null) + continue; + + int entryIndex = set.getEntryIndex(e); + + // make sure entry not null + if (entryIndex > set.getEntryCount() * mAnimator.getPhaseX()) + continue; + + float[] pos = getMarkerPosition(highlight); + + // check bounds + if (!mViewPortHandler.isInBounds(pos[0], pos[1])) + continue; + + // callbacks to update the content + mMarker.refreshContent(e, highlight); + + // draw the marker + mMarker.draw(canvas, pos[0], pos[1]); + } + } + } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java index c81097da90..39625b30f9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java @@ -177,28 +177,44 @@ public void notifyDataChanged() { @Override public Entry getEntryForHighlight(Highlight highlight) { - List dataObjects = getAllData(); - - if (highlight.getDataIndex() >= dataObjects.size()) + if (highlight.getDataIndex() >= getAllData().size()) return null; - ChartData data = dataObjects.get(highlight.getDataIndex()); + ChartData data = getDataByIndex(highlight.getDataIndex()); if (highlight.getDataSetIndex() >= data.getDataSetCount()) return null; - else { - // The value of the highlighted entry could be NaN - - // if we are not interested in highlighting a specific value. - List entries = data.getDataSetByIndex(highlight.getDataSetIndex()) - .getEntriesForXValue(highlight.getX()); - for (Entry entry : entries) - if (entry.getY() == highlight.getY() || - Float.isNaN(highlight.getY())) - return entry; + // The value of the highlighted entry could be NaN - + // if we are not interested in highlighting a specific value. + + List entries = data.getDataSetByIndex(highlight.getDataSetIndex()) + .getEntriesForXValue(highlight.getX()); + for (Entry entry : entries) + if (entry.getY() == highlight.getY() || + Float.isNaN(highlight.getY())) + return entry; + + return null; + } + /** + * Get dataset for highlight + * + * @param highlight current highlight + * @return dataset related to highlight + */ + public IBarLineScatterCandleBubbleDataSet getDataSetByHighlight(Highlight highlight) { + if (highlight.getDataIndex() >= getAllData().size()) return null; - } + + BarLineScatterCandleBubbleData data = getDataByIndex(highlight.getDataIndex()); + + if (highlight.getDataSetIndex() >= data.getDataSetCount()) + return null; + + return (IBarLineScatterCandleBubbleDataSet) + data.getDataSets().get(highlight.getDataSetIndex()); } public int getDataIndex(ChartData data) { From 72031d33cacf9d880261e491a5de6f0c1b2de78d Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Fri, 8 Sep 2017 11:00:45 +0300 Subject: [PATCH 157/291] Allow locking drag on either axes --- .../charting/charts/BarLineChartBase.java | 44 ++++++++++++- .../listener/BarLineChartTouchListener.java | 64 ++++++++++++------- 2 files changed, 82 insertions(+), 26 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index 0dccacc02d..bf4c42e241 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -78,7 +78,8 @@ public abstract class BarLineChartBase= 2) { @@ -161,12 +162,17 @@ public boolean onTouch(View v, MotionEvent event) { midPoint(mTouchPointCenter, event); } break; + case MotionEvent.ACTION_MOVE: if (mTouchMode == DRAG) { mChart.disableScroll(); - performDrag(event); + + float x = mChart.isDragXEnabled() ? event.getX() - mTouchStartPoint.x : 0.f; + float y = mChart.isDragYEnabled() ? event.getY() - mTouchStartPoint.y : 0.f; + + performDrag(event, x, y); } else if (mTouchMode == X_ZOOM || mTouchMode == Y_ZOOM || mTouchMode == PINCH_ZOOM) { @@ -179,22 +185,36 @@ public boolean onTouch(View v, MotionEvent event) { && Math.abs(distance(event.getX(), mTouchStartPoint.x, event.getY(), mTouchStartPoint.y)) > mDragTriggerDist) { - if (mChart.hasNoDragOffset()) { + if (mChart.isDragEnabled()) { + + boolean shouldPan = !mChart.isFullyZoomedOut() || + !mChart.hasNoDragOffset(); + + if (shouldPan) { + + float distanceX = Math.abs(event.getX() - mTouchStartPoint.x); + float distanceY = Math.abs(event.getY() - mTouchStartPoint.y); + + // Disable dragging in a direction that's disallowed + if ((mChart.isDragXEnabled() || distanceY >= distanceX) && + (mChart.isDragYEnabled() || distanceY <= distanceX)) { + + mLastGesture = ChartGesture.DRAG; + mTouchMode = DRAG; + } - if (!mChart.isFullyZoomedOut() && mChart.isDragEnabled()) { - mTouchMode = DRAG; } else { - mLastGesture = ChartGesture.DRAG; + if (mChart.isHighlightPerDragEnabled()) { + mLastGesture = ChartGesture.DRAG; - if (mChart.isHighlightPerDragEnabled()) - performHighlightDrag(event); + if (mChart.isHighlightPerDragEnabled()) + performHighlightDrag(event); + } } - } else if (mChart.isDragEnabled()) { - mLastGesture = ChartGesture.DRAG; - mTouchMode = DRAG; } + } break; @@ -292,7 +312,7 @@ private void saveTouchStart(MotionEvent event) { * * @param event */ - private void performDrag(MotionEvent event) { + private void performDrag(MotionEvent event, float distanceX, float distanceY) { mLastGesture = ChartGesture.DRAG; @@ -300,28 +320,21 @@ private void performDrag(MotionEvent event) { OnChartGestureListener l = mChart.getOnChartGestureListener(); - float dX, dY; - // check if axis is inverted if (inverted()) { // if there is an inverted horizontalbarchart if (mChart instanceof HorizontalBarChart) { - dX = -(event.getX() - mTouchStartPoint.x); - dY = event.getY() - mTouchStartPoint.y; + distanceX = -distanceX; } else { - dX = event.getX() - mTouchStartPoint.x; - dY = -(event.getY() - mTouchStartPoint.y); + distanceY = -distanceY; } - } else { - dX = event.getX() - mTouchStartPoint.x; - dY = event.getY() - mTouchStartPoint.y; } - mMatrix.postTranslate(dX, dY); + mMatrix.postTranslate(distanceX, distanceY); if (l != null) - l.onChartTranslate(event, dX, dY); + l.onChartTranslate(event, distanceX, distanceY); } /** @@ -652,7 +665,12 @@ public void computeScroll() { MotionEvent event = MotionEvent.obtain(currentTime, currentTime, MotionEvent.ACTION_MOVE, mDecelerationCurrentPoint.x, mDecelerationCurrentPoint.y, 0); - performDrag(event); + + float dragDistanceX = mChart.isDragXEnabled() ? mDecelerationCurrentPoint.x - mTouchStartPoint.x : 0.f; + float dragDistanceY = mChart.isDragYEnabled() ? mDecelerationCurrentPoint.y - mTouchStartPoint.y : 0.f; + + performDrag(event, dragDistanceX, dragDistanceY); + event.recycle(); mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, false); From c97b8d531d5584b767d0587805c1c95018cf92d6 Mon Sep 17 00:00:00 2001 From: davidgoli Date: Fri, 22 Sep 2017 18:57:07 -0700 Subject: [PATCH 158/291] add option to draw limit lines on top of data --- .../charting/charts/BarLineChartBase.java | 20 ++++++++++++++++--- .../charting/components/AxisBase.java | 17 ++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index bf4c42e241..790ca67b78 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -212,9 +212,14 @@ protected void onDraw(Canvas canvas) { mAxisRendererLeft.renderAxisLine(canvas); mAxisRendererRight.renderAxisLine(canvas); - mXAxisRenderer.renderGridLines(canvas); - mAxisRendererLeft.renderGridLines(canvas); - mAxisRendererRight.renderGridLines(canvas); + if (mXAxis.isDrawGridLinesBehindDataEnabled()) + mXAxisRenderer.renderGridLines(canvas); + + if (mAxisLeft.isDrawGridLinesBehindDataEnabled()) + mAxisRendererLeft.renderGridLines(canvas); + + if (mAxisRight.isDrawGridLinesBehindDataEnabled()) + mAxisRendererRight.renderGridLines(canvas); if (mXAxis.isEnabled() && mXAxis.isDrawLimitLinesBehindDataEnabled()) mXAxisRenderer.renderLimitLines(canvas); @@ -231,6 +236,15 @@ protected void onDraw(Canvas canvas) { mRenderer.drawData(canvas); + if (!mXAxis.isDrawGridLinesBehindDataEnabled()) + mXAxisRenderer.renderGridLines(canvas); + + if (!mAxisLeft.isDrawGridLinesBehindDataEnabled()) + mAxisRendererLeft.renderGridLines(canvas); + + if (!mAxisRight.isDrawGridLinesBehindDataEnabled()) + mAxisRendererRight.renderGridLines(canvas); + // if highlighting is enabled if (valuesToHighlight()) mRenderer.drawHighlighted(canvas, mIndicesToHighlight); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index 40835f7347..3c8028c24b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -112,6 +112,11 @@ public abstract class AxisBase extends ComponentBase { */ protected boolean mDrawLimitLineBehindData = false; + /** + * flag indicating the grid lines layer depth + */ + protected boolean mDrawGridLinesBehindData = true; + /** * Extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum` */ @@ -444,6 +449,18 @@ public boolean isDrawLimitLinesBehindDataEnabled() { return mDrawLimitLineBehindData; } + /** + * If this is set to false, the grid lines are draw on top of the actual data, + * otherwise behind. Default: true + * + * @param enabled + */ + public void setDrawGridLinesBehindData(boolean enabled) { mDrawGridLinesBehindData = enabled; } + + public boolean isDrawGridLinesBehindDataEnabled() { + return mDrawGridLinesBehindData; + } + /** * Returns the longest formatted label (in terms of characters), this axis * contains. From d3c339da100f874f7df8d350bc46dd516ad1d577 Mon Sep 17 00:00:00 2001 From: Maxim Pestryakov Date: Fri, 6 Oct 2017 17:12:46 +0300 Subject: [PATCH 159/291] Refactored LargeValueFormatter --- .../charting/formatter/LargeValueFormatter.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java index c950d640b3..01eae56f51 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java @@ -20,10 +20,10 @@ public class LargeValueFormatter implements IValueFormatter, IAxisValueFormatter { - private static String[] SUFFIX = new String[]{ + private String[] mSuffix = new String[]{ "", "k", "m", "b", "t" }; - private static final int MAX_LENGTH = 5; + private int mMaxLength = 5; private DecimalFormat mFormat; private String mText = ""; @@ -68,8 +68,12 @@ public void setAppendix(String appendix) { * * @param suff new suffix */ - public void setSuffix(String[] suff) { - SUFFIX = suff; + public void setSuffix(String[] suffix) { + this.mSuffix = suffix; + } + + public void setMaxLength(int maxLength) { + this.mMaxLength = maxLength; } /** @@ -84,9 +88,9 @@ private String makePretty(double number) { int numericValue2 = Character.getNumericValue(r.charAt(r.length() - 2)); int combined = Integer.valueOf(numericValue2 + "" + numericValue1); - r = r.replaceAll("E[0-9][0-9]", SUFFIX[combined / 3]); + r = r.replaceAll("E[0-9][0-9]", mSuffix[combined / 3]); - while (r.length() > MAX_LENGTH || r.matches("[0-9]+\\.[a-z]")) { + while (r.length() > mMaxLength || r.matches("[0-9]+\\.[a-z]")) { r = r.substring(0, r.length() - 2) + r.substring(r.length() - 1); } From a5a482fbdcc034c1c8d0340b61a5ac072dbcda7c Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 28 Nov 2017 10:50:37 +0100 Subject: [PATCH 160/291] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 0fd4b1cd69..e257daa0ab 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,7 @@ Donations - [**Donate 10 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4C9TPE67F5PUQ): Wow, I am stunned. Let me take you to the movies! - [**Donate 15 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YKMPTFMVK3JMC): I really appreciate your work, let's grab some lunch! - [**Donate 25 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H9JA4QX7UHXCY): That's some awesome stuff you did right there, dinner is on me! - - [**Donate 50 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZPQVJ2XRBSBYY): I really really want to support this project, great job! - - [**Donate 100 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KY7F59RYPCYCQ): You are the man! This project saved me hours (if not days) of struggle and hard work, simply awesome! - - Of course, you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome! + - Or you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome! ## Got a question? [Contact me via 21.co](https://21.co/philjay/) From f2dedb30de20122ceb0f43d3d8649a2dfc98bd20 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 29 Nov 2017 15:23:57 +0100 Subject: [PATCH 161/291] Update gradle and dependencies --- MPChartExample/build.gradle | 15 +++++++-------- MPChartLib/build.gradle | 8 ++++---- build.gradle | 12 ++++++++++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 75aec0ed32..b2e82531c3 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -2,11 +2,11 @@ apply plugin: 'com.android.application' apply plugin: 'realm-android' android { - compileSdkVersion 25 - buildToolsVersion '25.0.0' + compileSdkVersion 27 + buildToolsVersion '26.0.2' defaultConfig { minSdkVersion 16 - targetSdkVersion 25 + targetSdkVersion 27 versionCode 55 versionName '3.0.2' testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -22,7 +22,6 @@ android { } buildTypes { - release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' @@ -39,7 +38,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.0' + classpath 'com.android.tools.build:gradle:3.0.1' //classpath 'io.realm:realm-gradle-plugin:0.88.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -56,10 +55,10 @@ repositories { dependencies { //compile fileTree(dir: 'libs', include: ['*.jar']) //compile project(':MPChartLib-Realm') // clone "https://github.com/PhilJay/MPAndroidChart-Realm" to get this or uncomment the gradle dependency below: - compile 'com.github.PhilJay:MPAndroidChart-Realm:v2.0.2@aar' + implementation 'com.github.PhilJay:MPAndroidChart-Realm:v2.0.2@aar' - compile project(':MPChartLib') - compile 'com.android.support:appcompat-v7:24.2.1' + implementation project(':MPChartLib') + implementation 'com.android.support:appcompat-v7:27.0.2' //compile 'io.realm:realm-android:0.87.5' // dependency for realm-database API (http://realm.io) //compile 'com.github.PhilJay:MPAndroidChart:v2.2.5' } diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index 58d12e2236..125fe6fdc1 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -4,12 +4,12 @@ apply plugin: 'maven' //apply plugin: 'realm-android' android { - compileSdkVersion 25 - buildToolsVersion '25.0.0' + compileSdkVersion 27 + buildToolsVersion '26.0.2' // resourcePrefix 'mpcht' defaultConfig { minSdkVersion 9 - targetSdkVersion 25 + targetSdkVersion 27 versionCode 3 versionName '3.0.2' } @@ -38,7 +38,7 @@ dependencies { //compile 'com.android.support:support-v4:19.+' //provided 'io.realm:realm-android:0.87.5' // "optional" dependency to realm-database API testCompile 'junit:junit:4.12' - testCompile "org.mockito:mockito-core:1.9.5" + testCompile "org.mockito:mockito-core:1.10.19" } android.libraryVariants.all { variant -> diff --git a/build.gradle b/build.gradle index 2a65e34ca0..a338cc40ff 100644 --- a/build.gradle +++ b/build.gradle @@ -5,10 +5,14 @@ task wrapper(type: Wrapper) { buildscript { repositories { jcenter() + maven { + url 'https://maven.google.com/' + name 'Google' + } } dependencies { - classpath "io.realm:realm-gradle-plugin:2.0.2" - classpath 'com.android.tools.build:gradle:2.3.0' + classpath "io.realm:realm-gradle-plugin:4.2.0" + classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } } @@ -16,5 +20,9 @@ buildscript { allprojects { repositories { jcenter() + maven { + url 'https://maven.google.com/' + name 'Google' + } } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 10dbafb60f..845ff30cac 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Mar 23 15:58:00 CET 2017 +#Mon Nov 20 11:59:54 CET 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip From 796dfb21f8d454296e0590b2f8beafa8a1d0d7e9 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 29 Nov 2017 15:28:03 +0100 Subject: [PATCH 162/291] Out comment gradle wrapper --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index a338cc40ff..6ac1e616b1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ -task wrapper(type: Wrapper) { - gradleVersion = '2.9' -} +//task wrapper(type: Wrapper) { +// gradleVersion = '2.9' +//} buildscript { repositories { From ed9340ea51891a53e47556ba13bb62a1bb980fbf Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 29 Nov 2017 15:47:10 +0100 Subject: [PATCH 163/291] Update maven android plugin --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6ac1e616b1..1a6e95f12b 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ buildscript { dependencies { classpath "io.realm:realm-gradle-plugin:4.2.0" classpath 'com.android.tools.build:gradle:3.0.1' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' } } From dfaffa39cca041dc68ad3171bc1348805618e26a Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 29 Nov 2017 15:48:42 +0100 Subject: [PATCH 164/291] Add maven plugin to example --- MPChartExample/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index b2e82531c3..5122ce0873 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.application' apply plugin: 'realm-android' +apply plugin: 'maven' android { compileSdkVersion 27 From 23aef1a0ab247b7e9212d0c1d203c3773bfc583e Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 29 Nov 2017 15:55:18 +0100 Subject: [PATCH 165/291] Add new google repo --- MPChartExample/build.gradle | 1 - build.gradle | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 5122ce0873..b2e82531c3 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -1,6 +1,5 @@ apply plugin: 'com.android.application' apply plugin: 'realm-android' -apply plugin: 'maven' android { compileSdkVersion 27 diff --git a/build.gradle b/build.gradle index 1a6e95f12b..92f3d64ccd 100644 --- a/build.gradle +++ b/build.gradle @@ -5,10 +5,7 @@ buildscript { repositories { jcenter() - maven { - url 'https://maven.google.com/' - name 'Google' - } + google() } dependencies { classpath "io.realm:realm-gradle-plugin:4.2.0" From 0868d9bf89ebff78ba8e0108954cc343bc8b711d Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 29 Nov 2017 15:57:30 +0100 Subject: [PATCH 166/291] Add new google repo --- MPChartExample/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index b2e82531c3..be9289ae07 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -36,6 +36,7 @@ android { buildscript { repositories { jcenter() + google() } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' From 47485f8364e3520b73b4cf60de1e5c2683a84e51 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 29 Nov 2017 16:03:11 +0100 Subject: [PATCH 167/291] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e257daa0ab..50b1947b10 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ If you are having questions or problems, you should: - **Review your code**. Make absolutely sure that everything is correct on your side. - Make sure you are using the **latest version** of the library. Check the [**release-section**](https://github.com/PhilJay/MPAndroidChart/releases). - - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.2/javadoc/) + - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.3/javadoc/) - Search or open questions on [**stackoverflow**](https://stackoverflow.com/search?q=mpandroidchart) with the `mpandroidchart` tag - Search [**known issues**](https://github.com/PhilJay/MPAndroidChart/issues) for your problem (open and closed) - Create new issues (please :fire: **search known issues before** :fire:, do not create duplicate issues) @@ -115,7 +115,7 @@ allprojects { ```gradle dependencies { - compile 'com.github.PhilJay:MPAndroidChart:v3.0.2' + compile 'com.github.PhilJay:MPAndroidChart:v3.0.3' } ``` @@ -134,7 +134,7 @@ dependencies { com.github.PhilJay MPAndroidChart - v3.0.2 + v3.0.3 ``` @@ -154,7 +154,7 @@ dependencies { Documentation ----- -For a **detailed documentation** :notebook_with_decorative_cover:, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.2/javadoc/). +For a **detailed documentation** :notebook_with_decorative_cover:, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.3/javadoc/). Furthermore, you can also rely on the [**MPChartExample**](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample) folder and check out the example code in that project. The corresponding application to the example project is also [**available in the Google PlayStore**](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample). From 5e97f561f79a487b1a0813fa2a24f720644646f0 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 29 Nov 2017 16:06:58 +0100 Subject: [PATCH 168/291] Update version --- MPChartExample/build.gradle | 4 ++-- MPChartLib/build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index be9289ae07..164f11425a 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -7,8 +7,8 @@ android { defaultConfig { minSdkVersion 16 targetSdkVersion 27 - versionCode 55 - versionName '3.0.2' + versionCode 56 + versionName '3.0.3' testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" sourceSets { diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index 125fe6fdc1..8e19df7541 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -11,7 +11,7 @@ android { minSdkVersion 9 targetSdkVersion 27 versionCode 3 - versionName '3.0.2' + versionName '3.0.3' } buildTypes { release { From 1ac72ea811944ae7409e37dbfe40f6a6f2bb89e8 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 30 Nov 2017 09:27:59 +0100 Subject: [PATCH 169/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 50b1947b10..da7b77f309 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ allprojects { ```gradle dependencies { - compile 'com.github.PhilJay:MPAndroidChart:v3.0.3' + implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3' } ``` From d9000987c7386ccf94d653295f1c1dd8a1c23907 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 30 Nov 2017 13:40:49 +0100 Subject: [PATCH 170/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index da7b77f309..12c8e0623c 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ Features - Animations (build up animations, on both xPx- and yPx-axis) - Limit lines (providing additional information, maximums, ...) - Fully customizable (paints, typefaces, legends, colors, background, gestures, dashed lines, ...) - - Smooth zooming and scrolling for up to 30.000 data points in Line- and BarChart + - Smooth zooming and scrolling for up to 10.000 data points in Line- and BarChart - Gradle support - Plotting data directly from [**Realm.io**](https://realm.io) mobile database: [**MPAndroidChart-Realm**](https://github.com/PhilJay/MPAndroidChart-Realm) :zap: From e416736ad52e35d4e25ac67af08525a8d64d4aaa Mon Sep 17 00:00:00 2001 From: Pawel Grzybek Date: Sun, 10 Dec 2017 20:19:06 +0100 Subject: [PATCH 171/291] Added option to set restrictions for Y axis autoscaling. --- .../mikephil/charting/components/YAxis.java | 67 ++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java index e84caab76b..c572e3043c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java @@ -38,6 +38,27 @@ public class YAxis extends AxisBase { */ protected boolean mDrawZeroLine = false; + /** + * flag indicating that auto scale min restriction should be used + */ + + private boolean mUseAutoScaleRestrictionMin = false; + /** + * flag indicating that auto scale max restriction should be used + */ + + private boolean mUseAutoScaleRestrictionMax = false; + /** + * restriction value of autoscale min + */ + + private float mAutoScaleMinRestriction = 0f; + + /** + * restriction value of autoscale max + */ + private float mAutoScaleMaxRestriction = 0f; + /** * Color of the zero line */ @@ -357,12 +378,54 @@ public boolean needsOffset() { return false; } + /** + * Sets min value restriction for autoscale + */ + public void setAutoScaleMinRestriction(float restrictionValue) { + mUseAutoScaleRestrictionMin = true; + mAutoScaleMinRestriction = restrictionValue; + } + + /** + * Sets max value restriction for autoscale + */ + public void setAutoScaleMaxRestriction(float restrictionValue) { + mUseAutoScaleRestrictionMax = true; + mAutoScaleMaxRestriction = restrictionValue; + } + + /** + * Resets min value restriction for autoscale + */ + public void resetAutoScaleMinRestriction() { + mUseAutoScaleRestrictionMin = false; + } + + /** + * Resets max value restriction for autoscale + */ + public void resetAutoScaleMaxRestriction() { + mUseAutoScaleRestrictionMax = false; + } + @Override public void calculate(float dataMin, float dataMax) { + float min = dataMin; + float max = dataMax; + // if custom, use value as is, else use data value - float min = mCustomAxisMin ? mAxisMinimum : dataMin; - float max = mCustomAxisMax ? mAxisMaximum : dataMax; + if( mCustomAxisMin ) { + min = mAxisMinimum; + } else if( mUseAutoScaleRestrictionMin ) { + min = Math.min( min, mAutoScaleMinRestriction ); + } + + if( mCustomAxisMax ) { + max = mAxisMaximum; + } else if( mUseAutoScaleRestrictionMax ) { + max = Math.max( max, mAutoScaleMaxRestriction ); + } // temporary range (before calculations) float range = Math.abs(max - min); From 16a9be8ea09021ea3dfb9f2e61a9acb4bfdb3fcc Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 14 Dec 2017 19:37:09 +0100 Subject: [PATCH 172/291] Update gitignore, add assets --- MPChartExample/.gitignore | 1 + .../notimportant/MainActivity.java | 11 +++++------ design/other/bottom.png | Bin 0 -> 23219 bytes design/other/left.png | Bin 0 -> 241389 bytes design/other/right.png | Bin 0 -> 4161 bytes 5 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 design/other/bottom.png create mode 100644 design/other/left.png create mode 100644 design/other/right.png diff --git a/MPChartExample/.gitignore b/MPChartExample/.gitignore index 796b96d1c4..67e07b8fea 100644 --- a/MPChartExample/.gitignore +++ b/MPChartExample/.gitignore @@ -1 +1,2 @@ /build +/release diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index 617e43c021..7490c3c933 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -90,7 +90,8 @@ protected void onCreate(Bundle savedInstanceState) { "A bar chart with multiple DataSet objects. One multiple colors per DataSet.")); objects.add(new ContentItem( "Charts in ViewPager Fragments", - "Demonstration of charts inside ViewPager Fragments. In this example the focus was on the design and look and feel of the chart.")); + "Demonstration of charts inside ViewPager Fragments. In this example the focus was on the design and look and feel of the" + + " chart.")); objects.add(new ContentItem( "BarChart inside ListView", "Demonstrates the usage of a BarChart inside a ListView item.")); @@ -135,12 +136,10 @@ protected void onCreate(Bundle savedInstanceState) { "Realm.io Database", "This demonstrates how to use this library with Realm.io mobile database."); objects.add(realm); - - ContentItem time = new ContentItem( + objects.add(new ContentItem( "Time Chart", - "Simple demonstration of a time-chart. This chart draws one line entry per hour originating from the current time in milliseconds."); - time.isNew = true; - objects.add(time); + "Simple demonstration of a time-chart. This chart draws one line entry per hour originating from the current time in " + + "milliseconds.")); objects.add(new ContentItem( "Filled LineChart", "This demonstrates how to fill an area between two LineDataSets.")); diff --git a/design/other/bottom.png b/design/other/bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..368f30987b37ae23057c10ddf90b42cb34441a0d GIT binary patch literal 23219 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 %T`KiZG#R^r$WU0u>{}dMbIUs!BhSKu! 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.T4bj&=rv0Wc(JapY+0wrM9nS_Wwg zJaawVK1o`KBVAm6EOas(ArDeR>IdrR|rgOA78(mqWEF4>P{^CylG_;qJ(FJr6S&jhPuTFBmSpD%gHp z4L&p(-+a`)ZfNm?Tln@}X-vB65_@q=xqxXsxXyppsm;LJ_CG!n+w#v`V^zx@)>j{5 z?qANXS#WMnB|i28J9>3(!1Yv;hG{THBCa_2gFh5nmABW-y%sdXKhN*>HsX#mmO$@J|XLxrO=F?DVlU-{% z2kCK$xxkJ=LH1x!T0O8Zf;b9+S?FG*wF4(b zuH(!RQf4efq23>#&LkEM(>GCCvRheJqse2(Al7nZ860Xs0B@s7yKJO|F4F$9Pay*j z$4Bi1$5Z1F=4gZr2B4my-y=6%0AXG^93SpR! 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

  • qx`i9{_&>4qeYQ~kZ6sIdNX2+o*J-3t3UwrT$M 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%0DHQS4)3Q)nfGGjY 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 z ztsf;8i|yI7S zTHsTG75}Dqt?YjAh5H76Yw-i^ayO2yk;f{M}cj54y7J{5IkQ+9hwX;&!=WK+ zk(YFh7HUn8Vu_8`RM8M*X`p;`MH*?apSR&gF#b2!_+uAF$`QA=-gZYq2X##g!ht z%Eg?l#5FCMl{m#RY36dfvg~E2O+|A}!a79uA!>e*UEsnd&039yl&p5In(atKQOI?| ziU39)g3@R+e1D!FHmrYUvJXZUU?4#j>s{+$dJ~*QnROhtA%l*=RHfxmT!GLWv?swU zld0Mzo^S>+0Op}Ud-X_DzgQ#gnAh{o|sDghZOte7;)npo?At;T*Bp$;l*n$$X3syu@ z`n7%;dIw;6fLfe(M#>P%vv=J`ul5Twayyy{(FG(KiCb|ilfOQ)o=Xfub(?Ri6R3@s5S5Coh~b_?Z>g4 z$LlT?%VgBrJG^}F_J*8+msiOw*5haB_kc6f=`?c2TA}vOXg=fJ(izEms}KC0*G*Z0 z4=dgG^Q%3hXB(XKy;tt-8(6w#zXL@!-R_ijatTZ~tsOey&gn?(XDp$dmGeg3- zK6dMrE2+W>SWa};>h7qMTc&!-mM;VahPYm9X$VcjhyuhqAIQ8Ql5{-}9SV4Qp%jAE ztKel1-PAdrVJ*{S6GnH#mV00s(Z|nJgsnRD83!VuJ}T&sb7E6tI_$-KZ@wD#JLO($ z+2vN9?G!cWz>mOc(oK+?x{IYWgt8A zF2=HihDMU)UR#tj+o0(_Tg?ho$}SrLJor|xLx!V_kW}PNwPt#1wikETN%Ni0Ln;qc zjP%qXh-oqaRhPjkgR9jHulk@d1WAL43@`p-6{I%E^+BSIN{fzy1dL)u0CgL6 zDkPN3l$R7kBWHju%MD3v78>F6`q-pBR0k;wo+jAqAif9YIM6EKMa1ub6y7O@HsjE9 z!rp8GH4ckn%R`U{^(?GM#j^{Jbx_g zIMOg37kH(r`{wLngESJB!c?t;%j>|N*0%y-J~El3Mwf)QWjaC z*<@TzMPe<3XLJ*9m(rV`kTde7n&z-H{jM&=f zl+;{(@g&U&Z?PvY@@sZswr!dEYdOQ^a-kBv^UgcI@P#j|HDF%g3|W?uGb)wJ&nRcS zPx%ZobLo?fz^AQMe^0z2$`7BuvwP?A<(poyXQ}!*52e^mR!$TG+cy!k(9Ttww%w29eR<>(UW!3Eg_+_ zT7*Ccls9gh?!9xF;jGMK(Zqpn{3p~FWeAMH^k(X$LG#njXvq%vpxo+azyN^DgKrta zDk*qv21yq`UPyn23>vXzz#yEZN!uNAM!6kw_$VnLtH3va3qrXQvN1Z%J6hdwla(cr~7-S6KIU;MwV^<|!im@={*HdTl$*(wG#<4|Obr@>J z>dQ0FqU3EkI|D_Ko)~>>GGVIV9i( zv=|1TuDT09HB3;h`vbH-zjJ@%z9eJQ@2$mWa8 zyIUvPvNWee4=s9IgmEz(Ux$>!j}^M z)Uh_Y;Iy}|dPWgE>4_R{nro9nSiH}@PkCuz09>k98K zHIObGk#bC`S(mD&SM(^|@=CSr@eDL7WsjYq3=^Z^5&SOzrEPGWWeu05;jJozFW>Tq z*GZkIG7Y@~Sew_KTX*NAE2Nmua4S!@C7{Ou0b}!Fm>s9iQ!G?1pJB5|Bumi85{vlQ z4Ds*d+++fWWq{F1=meDn(|T$|nlxtH2H1ndhl7mc&Oq1#8YB8BjDY;5lOLI!b2EIzDjX5Jd9Z12b+iRf+VsBPB@C zRLHZ*u0YoWY#eF*t42t|K*8aKDn`pGh`C6I4H@*^ny|!oL!y^j!uO)!1Wbp+SJI%_ zk{vW1)=kwI+EQ(?N`{c*391s+h!T%1>Sbs5i?CxKdw9@UwrnQvM3PiT&{w$0_e?eA z<~%xOgJOs}MLrP2bh3PI(V8v%ad+SuyJ=iHXRN*U`2Jta8IM2y_!qzU#p$W3L^An) z0^iTK_j^MD+Sk|j;SYcKcYf!0UavFO0*;?^#;Y41Q3bKaaD7hWe>sxkw-;&!_jjMW zx9_s0Yc6{cW}W&`H(j!-tbeK4JLJrTHW!=gU8QoMaZKWJW^6R=-rKCsd3i~5c@%Sz zZLG@68N%CPdKY_8_p`2u8W=5E99uf% z*AtwL*vxSOqkymloxt2|Gu;-{D7y(1%7v7ZKG;sNRxnbgYo=M@xD6J&&48z%nV0Gf z*)&*MwB^30yrT}oV8!v;Ic%>@jXO@6(q82CX%0oLXshg)RnN)#EN`l8kqIy%yWhXC zwA#}%vRcc_OvX}8*Niigv7@~ZAD|Z**e_8LrGeYS1Kt0|X+B)7dML45h zF`i>{KM1cv=W44UbsVM;TTPlV9FBD?ycB0)U4Y~#qUT|-37iKI^rnzXD?Y!rpa3I>@2h>CD14xQUzf^9vpU9h|e zUHF`KLUIDeo8a3E$J|g&Lz_(eOY$6JxS3|o3|Tgdq9os1zrv65l3pBQwStU`f(HNS zqLdC&G3>nJ>_P1^EYAgE@ zxfZ%#ONDwDYHjiZNADXPT)eh_KeJ>jOQNMPMLM{uboMbz-ay6XX}u}r>!FI}^A6Se zw=S_qOgvjXz*`j>>eO_7G}Ub9TlG9NNolASnZ@QWf*;RPghq|5vG`SR`yscNKi<=m zko`@@HtJ3Lg}H3v0GAL6~Noe{Mf zUllx@+l-Jg+{ ziKNlb_Cx+KY>{bYAA4}t8A@60{SXg0g@)tChX=(3C*YYmoMKugTc1w`o0uHWAjz3Di$uF>cOt3uyB7UaIje@3?KY9pH~Odhf=hOewk#@Vgo|dt zM(KoSNE-pB%rJn|^glwuA8Lw;obitPGcMK{7&Atr(Vuh1&$riXZ^jvG-XX{t<#PFL z`!lGEhV;D;objf!UEhOR+bUzXo9@pvJ@-o~cYm^(bUnFrQ)l1muE-H9&6Kk~7GF{V zRk2=3czY^!S*~Z@m9lTe>U2bUvv}Hrh#S;vrrXGH7<3ci^SNR#ve+A zBm_4!Y0yw)P{6yvz%!1K1@Uv8{0eKDB`Z)QMf|G#;-x9^Rx*hqN=A@O)b5r?j+enN!M2LnsIwJ|732+9W52mX%aLM9g32B(ZeG)-@a?Bkiqa_1Y ziSFBdlsp&gr{rLsUz$i&RoCq~$qUim8k7wf7nlGB>Oq!1kX@DKT{o`Enm0Ua^q$Yy zglY6rwy~c-e^fY0jdC~0`a}YPTTmQ8f@4C5i6tg5rA@Gc>UNuTT9wx!rb1X@U?5X0 z$-;o{5uT~AXf;TVSULT823pi8Q`m6W@mJmfZ5plLC_Nn)My)Vc_lpwoi6gO-LkI6F?S*5twGJOS#ShTxQjm;B2>9MHJ^&VTRt?0XPTS@HPRPHJGAcc|-ge zYZO~@<|0W*aXr;@oKTf{AJk}*D`xFcEiCE8Dx*e?6zFa#v93@p!g9KHg-qd!W!BoP zgSt^?0$G_!Xp||60Ji}}pO>!ReeM-|pNhWCVHb@9!xa@l2~A&HGfN?dP8d(SApD!c z%vYjlJD%~;grhc3-%B%NV|dHa=`@`x+oAd>iqKI@6?!j6*wUk5BAKai?^xr1{FepJdzmg#5fVij*?c(c$H2t34G2LQ3qXg?`^poeK|ACb1h9x0UlH zN;@J}G~S_3Km!#{l7kd74>GBId&sm{B!%~;j;otW(jF#y)nI29X<+t4OLGheca)SLLk3;tf znGBI~%v0|5(ANpMh-_bpMm!hc4N^G=2Q1JF(3pmGAH+?V8nT9T)EW{EhC`N$!{ok8 zP`e4flRzU*j$*vUdla0sAtblUI4m8RIx&Sq5&!ELa^QVZ4wrcKW*2X9m%p(Ko2Xc5 zsmKXzUOXPDuXlsb2;>Y$_5#{X_&Vl73lTb1>xEx^r(>>vNI(Xkq%g=^llQy5v!lzg6#%P6`@C$ zIL-h7u9HbbK~#1)epZp3hFe%4Rx+rYIn<5mwzZ^QZtw2k{D^f)ik*cCI`bl!6=*HM zlD5q-GSH<|!(pn6nJt(aK(YcG0xJTSMV4{vq~x9|`W-VzciQaiDkXoL%}HwFo@Mgm6k7B*l8v3*S~I+sTMGFLO{9HN8tF1DM8TN1cTKZ{rB znpbYv2>*iTgQy#(J9a9HcuDXz&CVpcq{x{K^}|~E0pDks@lT3pT;mzn#*2;@#f)CB z*Jv~pMfvf@jF+pXFErlkCf}W^B@b;3hsGa2@$S9JOGlKmqncjux_Q#6;fXD7;(&Bs z)I2{W@g%ZpItp-nVyqlwc|@=Zt<9`u>si#PqvEA#4~2^D1kF|-k{4iK19Bp`F>tnP z`lLSS4rnFl9)wdV)Q>Bsfku|AH*j7BuOK$CgnU=h5{`P*^z5ceiG?!j`gQ6V6BHtfTdZ@YAdnI#PWo0?i;bLmr4-Cd`Jkz9ne4Wy zv;E%y1Q_``y>5sFEI_Q}WDDJ>$@2%I;f5|OM~rXzKVI6jT z3hkKb>8j7ezC*CWbf{36;X8et5=oHfn(NU_4f18JK}k&D2^b6XAu7Z@z?};trmgF6 z3gU=^wM|^MKuf`J1$HG_18|4(#Uj)-WK$l~B87c$A<3GMQx=t=v_|5C>7#4qWA*UM zJnu!6jrF6X&kI^)Q~G1c8gCF0hrgN5Jm|l)o=czj#3ydN@kaWw--&@=!;F78xb3bI zS9=y$bG&HY6vYh6Gbm>4hzR`nVTQkr8QS-U884}(Z#!>F`DIsncdEf3?2dZI$De%X z{=(v`jy>rGqH)HS8kB70duE1Ome>gkFCh1VI(R-QhJN-HJz;+cEruCRM-2ud??f$} zZAZC~+G8drF*d=t#Jc@t2}{{DvoNH7@t9SvQM*ZQgl7}nNL8GI(1H-QypkA2GqH6k z>xX^%TnIlA!U2Rb1kQl{jBF{o9c`2)Qbbq@&YYgCP5c%BLH3mT<072orWNv7rP3Qw0ht66?TJl0y zJjWf_h$=-^UeK99RmLg^Y{1k>I68pz6l_xdK^^lHbf#Wc&;D#*==;%EQ1mqCB4x{) zT!cLpdE*Fw#`nbKXf9widd)86s!$$6yvpJ?S`Nk&%!0Gd-aWQ22z|mFE71tJ?KCQx z#UA_~!wi`R6hIJXr&YENLjj_TFy(@Di*WL0<0(aS+IFUAr8A^sD>}%K4EsGNeGczF z1xIYgR48l*1|u|m%CZQc6!dn%%62J6JjOy$iAUj;^RPjmT~Zv*XlqOEJVoGnX=&4$ z9HL|@-&aI?+|@0|5Fsp!)d{)Yanck&Fo(QI>XY=CGh(8Z!|h>|^sur;$`No=uq6O) zK%Qb%mdO`-F6#`>>QoR3?z`vb=jmkNZ;foL)%yJBKTrMrm-W&w$}_s%?ha=Bxblo(TMPZ=z>E=( z%YpWOM|p3uE*xr8p79@_eA{k$^^LcDlgnec3|>p>c-j6&avC;`H;kQv(tM(5UXI13 z;pBZa?hZ_iFFXz`uq=REm%D@X*f%2PDO3N@B802r$r_Z$+y1i3O8j)fU_+y(@Md@} zC61K5lT$mWk2eNW^k$}vO=MtesLQ&i%NAq+9U z2+}}~0eT2v0FJE;gG3nPli8uZ$AY6QoC1pwT0)3<#DkCu`C`P*@c$aZJ~!}FLnRJ8 zsDW#w#zw_t;^H?z_i|dgKus4};pQl2P_(0%LCKIzJb{ZETww_>SXeh0!i1C@%)v_W z7dqMpea;WzPq6=Ket?q~qkTO{?^YN0h?lU(b^SCUJiY7Qb}#WUQ_L;bA)SL!0eS%o z%quZg$H%onAF?hNVG?Z2AcPVc#|LS<$}f~4(7@NX`*n9%NuVl2FwL(WGoCSIv*l)+ zqoPKvNM#{`Y|!bu$GFF1-esXyy=3n6waxfe^59l zVTTL32FW)`j%W8W;t?4l!^B3&ZiJEujbm-k$Am|Tn?~2a`Z@BSkv|aJK;gttkR#KL zxwf3(hN2L;2Kv=}<*VLH>(lqiPkxeO#!jm>!!WKoW|T@LY8l`9*0-KGapLjEAHV0G zd#Ew`pgd#s1qLg@5K7mLab@gvq>a(E%G*=w zR-aGw__UERGkT9T-7%1}rg)cRwd4xv@bc=sfWoO_RmD!!<#@_rCJA{KVFkINYK~)3 zl8^5h>qkwX81$Eb`y8)hY81mrfIJBFzU=Ke_hBsQ6q97|y|EM}xnyM&ZF2k#*j(1Q z^EmSOIx4kEP6a1Lmx{wNY>fq@%Vh){#lx`;QVUYRs@tli6ZP2p945f<0@8rQ3TxQ}6EIR3 z!%zvB3dn_&pknXQ2X4r-{=7tzM6oSG)m)E<~5#6JRSX``+6ER{<@l_;5PN-WE#pJPp#)u;V zipE$!jSmkYnPEYii24=+gt}mr@2Jh(y%zkT5Dn=YClDqoXZcukd?iL~Mer>}`L$yC zLI0&`hrECpKl|AoPydHJDC7D4-~auEg@r3$^gQp*JMVn-(MMm_OYdOD z+uruJowKPcdBzJd@SkwZ`2OP=9P3}l?dIVB;K%p!y&x@khV+%E?wN3_uXxRq;-Qe8 z{FunCEBz-EMr^FzY2zV3{92+PCRD{M+|v}_w|zGCX^2Lw;yOIVw^}(rkP`eTt`2d; zt@*R{s6_2T1B%q#l%PHW<6E$w8lxLwjdGU>sELA~k_Q3~TV5U!k`nu_rYLDo3OueD zaW?|JDEST%!w~iYcDd(H9~C8kKD`<dF_utYNE!jzyKabq{y6sPd# z0k83J1o#YwRf0T5BS;F7|AHtlfds29_lC0T$wD#G6Q;8vD|hNY#NDwYI0>AQPI+Y{c#~xkDtn$oC&eHAne=pi`tQ^Q}2$ zMUcaOkn$~7?6!h;vB%A-sY@-OtcQ2DwWqdw=OJp=`w(k^f?3NWF9%)(UWaW89D-s) zA`RX_SW>}mLH;nDc?#y&VDc{U$rH&Z1lg|-i&d*UXRKuMeXMYUezMm})`Q9C$l(rT zV&M5qkEDGJotZ>cpGY2I8PXIvs2$%HA)dShGsoaG#SBUZ<7l9R#S+VK~2y9zCFt2@(b4R!3Q6tzU>bmGpMPezVl;`J+^1ho-1Fc%krf!edz-q_`p>O z@VxPjZ={%U;J|?^U!;cn4R3hE6Hh$xvR?W{F=I#9c1Mcp`_V%GDZ&hDj2005mNfp^ zQV(XxU?cwTpLo~)iKU;r`yojUz0)%1MwX|y7IRa3x2Rj{EXz(e)(?}s9v0r-)_>VX zr!jmEMzy%*YsSjBZRWPZ37HRyLW6MNgevtwsdZa|S!#pQFz7(43o&Y3X1T^a+ye@6 zaE6b?EMXXShS~LK+DGDqSS#ywRdhm;M>i0-8KG4`k7HcsIV*Bj6s}CVkC6+oeWP zf$JuPp&hCNoTQX5&Gl@gwQ#P7Gn98u@HGY5&8W1F>C=KNG-Re;MRzeDh(hW${r#^$ zkN*foCQE>BT7_1a#T*wSW6G8a@y;L>|E*a5Yo-&tl%7jFn2|^%uHxAgH4XH-lvNx$ zbVwA%9ZPX_F=KLa@}Y+w+P81tm9Nt^q-*m2_rHH*W8;TBCHNkg(d+f@x#ylA4rY9> zv#A}8!XLkyzLyCz=#2V2nhAt|6NLZER3|v*+iS}I``2&ZeQ^2iTfY&t`0kTw$qw2= z=K0Q{*GxW|n`a3#-eqaCHtCCE`G4-`{TYRkn)T+Po783VXhlysKqIZwPbcw z4@>9U`vMSKko7>$LN5u!B`B}JuD!;_z2z^(OzLaWTPEY0zGY7InEf43!8$bya>iBT z9xKjxH^8GX{Eb9Amo4pU#99{R4S1Rh9v68#68?Dz*8}`2;7x8Yikmgv(8kHP579eV zp8-~>eL7-|O6g+OInqH~g^&XHpA(@(ZJ6p^kT?r#qN8e#AVq9leh$bbAZHNrNFP#* z)}B6L8s-LXYGCzodW%e1tOKjJUCs?2NYsLt?x|O#MkT@isx<2*1s<{4G4Elnc_R#AH>_&VQI~UaFmJVHN zEks11zc~bDH(WlUJe5xNM`ql!q!_VzE)sLT6KR(^3tKQzhC1Dhq7=?2E)UIkh=j6M zANPy~&bJ{$_uC}bsGzEVJw%_Jh@SM0(oJ3vx>>0s20=5(H=&q-0ezqIu#aApwT~5| ze6odk_^alv0q6>JO3>H?8ya{6F|OU=?7jOU{s=-%Qo|(+X)5m!j7EX3a&}hhnbZ1j z%Gs|CSRMHHj~So()TbzBP_!72$J8`X+kI7d2DJ{QQt6%Vd?&TY^ar@An6Wb-ieY!% zb=Q?I(q;MG-~HW>e)OZ%QvdL$bk#9qhi6==h5o|X)DMCg|8T&MJ7)BFv@75~6ohY@ zGRMadS%KgE*88u2!`V0A@<8~E=sqlK13OTJrkR(MenJ`R!kE&IjmA{Wu%_SL6n{yD z(Qy1bA}k@JV0x#8#-QY4B?eZ+362oM7ztr82K@{iq&#B|R)y_!pjDW|2E4KKmrBR+ zF=Z1ZU@2O;I?3xW+KX1P%86>A+~}d-Cln#zKS1~i!Mj10J#X`Y3E}!EcS~D7<-;bR zUm|b^!27^EFLME=yn)&pIKm0<5#U}K>;S&yffvFwK!KySi{Zi7g6La3Z(!EwsVXr} zAd6bYPADU@8H>HeK^F*=fZL1xGg9@zVmr#mz%0s56?<#p#0sg*e$ z(*Ii3KkRcNk3R+D|0?#EQ-rBVm2zr(VrC0=v*HwG-RoUQT^cM%gszFP0aHCD{mc3i z&cNydTI!JEh3&H@ixq66Dkzj;>;tO|ZJ#9$=om!A+IpK=Xzzv90HiZ8JB0W_ars#4 zq^8+pOCNQNxH5{}W^;EFy0P59l%G6(-I>l|OvDk3uB&_C@?HA36B7f95J695omP2E zoCqm>T&~THA)$gr`BRLu=DFo@qzAm15qljcQ-z5sek(7xEm0Xc3BxxtM$b2-i=92L zFBgqQIbC=9N_|xFI7JnlDCGM%MK4OJOi^lTS!ci}RyHe?blwgRQ?&4qlp$S;I`&=K z?7geQe-}^ohLl0`1+30Xty|8^<+fENXx|NX}^b}-|Gct$pxrI>NG zV&HTdd-m*k*Sp^Jp7*?mPT=8(AEs9OYQ?}`|N7T|>|-CBot=H*l~boqQ4G6u>C#n6 zy?x7D-tq^3@CVb=)8F;_i4!O2LcOe){$4zz)oShJ+Wy31h9|I24lfxqI5ZatAM*WQ zD7J#@vD20R^EcnRYhvTXEl+|A=n1UX?3R#Qv3E@=>-iho;%!0Dmj?GIM^zUL4(_eiOMk zR0Qfvs-_!6(m6D5;@FMcDT(tX$rolKpw3VkMvunB^Fn%WkC>?bc!58HQ@0N&$iPKl z$x*Ln^?L4_0_RAy%G;_Y-4iC>H->#Y{$_->FiJ9>0Zqh=p$d2%!q4-95PFd^FcXf) z3|uNc5Ov=MO*`On=Lorxa7Re+h*MVdm`m(E&-1SX-xHKb*NiIEge|^UB0S28=@_#?_I7 z)idrPia>L)Lgz!tq6Gd0*ry}&I&`vybJR+w`5xvGY1LDV#gT@c~I-);DF47h7UF?DCm9~LQ58?z)j;3cfzZB^d1?){&V z-0@p(e%-FPSjwSRmUJS_4I!mT*6&o34`BZ#^<4Tc%(&`#Q*@NeHh&{?8GyE5WT4%Y0UU1>e_N-0;})0m3PTid3V?0l)rxZ zHA!=DX!^8bcve+&2TB{VohW+<&SomTXrA+J$vLaKwk*BIPyJG#|2BcY0)HSz4lh_y z8)q+&10GV#Xv4wWc0Q%hgWR?XY%l?98rU@`)?qRa{Uh+af*eT;l4!u0c+9XQ$wO2? zZAEaUxX;B2t3vakFC<+-&3YYvPtw^ z6Bad^EN|++18E{|FR(-VhZ&T?2_SJq{sfVTMWX9osS%Xo+;AqdDrQ0qJhu-A=OvJd zl=l1U$qTFdT%_J2J_;dyT4WjK+EC0uCkg!yWF-h&kS#zfo*im;yT*+UdOCDID~&v3 z5%xX?`wwv!UW1<2MJG%OTr7-yp;OCjMUx%K)qs^}8{nv{WKX3(*V&;>lxNE1+$QKU zP<}F|R$c|uVe07Iwp@844gGCt+a9&-r(oXzk~fMck7u4yVs2}kZFh26(&gqOAr_@N z120gXHjh1G9UU?ODpdE*kF|fn!qp%j_ zi_km-XX;SA4ATi@W?)L!)dzvv(TZCGh)*2F^UcJ z24&=Ek{u#x)2*FC}m4)1Uq{wTVAcn6Wc<@7_%(f3=+Y7slM&T%}U+eSdv@y;`ka#hHkr zSS%J3iNwwW+A*(|Wi=WNI=z?m(%-A5k79-_%RgXE-%lWBjJT*o^nYd<{|xHtT-7x6 zPd)S2%4qY>>2HZfINFS_4W?R95cudi_4$M}qK>v}D-=R3K{Jkyj~ zQt#U|k?}Y=-b2Y-x$jhPZJEqmf`u`ps*vU&+zTrSuqf@QLn#aGBXFAfl8WlZwGm(# zQRCo|SY!oAP$W+%1z6)kUf_+0HS`&&QN(HcoQvPtE&0Jzd{b`w9+8B%hVltN_#FXn zaoFQrPjN*hw%P7shx_rRFw55oR&-IKaoK{YPWlsF~$aWW>eM#J*~L zp9zaBvC288@|Q3QaU;raSQVa&D&l4WMzO$J!_${wH_Itv8c=)=x+me#1XN3?k-Tw` zc*T%k#NI>V2*sZv$0!3h$er6SEOI2WvFd_Czq(OsNhI^-xuicId;k659hz0T68 zgcQqJB3)K_D5k(uz|L*=&oDucSjMfCfo_R`)>s2`8TK?Fw^uxKYv!?pZVksqx08-2 zUPxAAb{xxDK}~wTdEJB7u?}@ES(|bGC@da>XDCPvAxW7;8oCIujdY3`7s##+$iE7n zGg!qwZ?K$Th2x_#Y7E2pH{hlc*V#{&$0WWEQz__QC!W?+d)b;H5l87)8*=nJ2T>A+ zd1z5sqL8D*IKkG61p`A6Yfz$Y0Y#BGjBJQ)!emUeZn*QA$=BC{KNL7O3PQnmM3&Pf zDgovs7O4+$0)Det_^S60F{4i{%a0vqTpK?+<3%xp?kh?JDU$8fXZ*xshV{4g884yd z5=W+p{3|a1PPNYM84aYwA3pNFL-@kIyC0&4(q9!D-Qqf#Ard-Pd^WC+B9}A!vDPq8 zKeok#t=Qcb!mRn7dsa5jYN8vupgu?TtfS%{s2_&KfN<2^UWA2ZNG4!>43-0s&%gq8 zjb~x&D4d={D`G5AZgQ0a9$Dh-M^$ei@p`1i9C0ED5${s0Kje#I7@g(DrXk7x;@Lft z?#~`y({$S#%lH;fz1x9*%M|`#2)-*(%nRk@p24F+{(_fkiCv7-(<>^cnA>6mztbxN-JrLmwGYXs@o@{{*U8TxjGbkRWMwU%m9ur24lcEx zLJbOC)-}&bvWo7I>AizG0DTM!V&doQB+A)VD4_^pgWiF(#b!??pj&_*wZ+udpM!-u zWDkkwUYmO)sgK;T)N7?`Fj1UrmS0gvMG`KGtuuw^>Ib(-fr5kui3M1>2_C0boBor6 zUxxKua*zmZl0Ow5Y(V-boQrLriRGcughEfu6!@m(d8f#J0pSvKMdD#VY1EsD)rgKZ zx>JBP+y)OM5F=UlH!b&t)N(kJO}&~sw!xF8PA@E zBW*Bp(0?VIq4#pn&FUNhyZvO3`<=ld#lyU!;t3>F&RgbUhbn5C<5 zJ)ArUrwA)J&x;UD!+HV@IUS$I2Tof@=wm8DjfX&hfMVemOi>71kS+=1ph3zV$Y{vU zLz8YAD~|k z?EB_gaGdl{B_ICIZI#2D6UwIZjGFWZAfgR#%2Bkv6N_;)y3|Mdfx+x!&czL7WT9qO;dxan#FWKK2akYD5Vhj6S52Fg^kosRz9TvujYIcIOs& zdO~W%PK@)pg1uC*aZ9xtKj2`gc?BD?z;_NgMK$DT7il6 z1}TCHm5I%i-tR9ZjW>?PYRDoV1nj!jXcj1|p#J#muC-*P&tc4OVq8PrAj5S1cy1IJ zvt+FhZ_0aoE*Xv2^p(qVRhTfql)%emyTx4%9^t`C((Oob7`Jf20?h`k3R5E%18=3E z=WRz6Mv!zEqDWm(1K{#7E`qsTW`YHPDuWF<%UT#U($J&6ybS(xu)hV_SIQ@EEj=F7 z&0#;@ZzPk_AUj0NRFXBxNYK0A2d5Wcal4tTq=1uvkt#S++TRE& zi~T)p&d{Uog2=MXq;s&poT}Xtdwiol)r~S~uAApO4L<*ze?t)(C!~i7LM<=5i1(zq z-UOvEumaW2DY2v3I(B=6Wf%AZaP!+(phLFxuL~rwXkdodimzszE@vl z#i0%&9#au8<=AK(4@X=oIgm*(@Y4S_pZGeqn1msGznJj?&)BKYpr8M%m^XE8{AiBv zrEB{FX8gqFP5sj_W60wPNBx%|zvopA;SC-4oAP5{dR1w6vnUW9le{vw8(dGgBRh^uv%0b|FMQ1FV zeSOWJm53h$X9XrNLnRLG9BgS|Ey5m$76N*Kpe(S^|X`}D76sxAGoD1`W{{<|jL;K@uYdN>#&vhZ&g>((5L9gD( zPM>VVC>cvYP-3oJ_K*u38&hZDKnnVC80{p#*`Q3STLr1TGMX8YxWc=6v4gm%=1(NT z;ePEx5EGXMvtFR=RDe^$#6zfqV&rL8y&8*X8PRK^gefLh{eba!n= z75X45ui7~HnzQgph&I7f6(Z9mz(nNyl)&-Wugdhke=}MBiu3pMroJD{ICkt9H42tx zUBirP<3-2!ih)T$RAUCK>2rk-;^^%+H{c%E|F(Gg3x`wl{mPLI zu7Sw~p0>DAo*%H7Kvc6!oLi%%SG0Fdc$?R6Koi@-bazO~2tdGv)Z(!p74RLq0y8--sEO z$c4dJaHkdScC6Gq{!|3Fvy`XNpT$uHYp-;;lM!4-=p7omPk}$`=odTL)HT0m8@+Btg;9PTn6|oU2xvAwB`PMFG@`-YrAGvQGt*KLO(;;e6FK- z1NbcG{DJEIg%rzL!aooBX}0LmIY3VW<1}|bbeZ!X66I;1niF_>*|GY ze;<;wcdX6byuxo_`@HIUvQ^bu-2#2y1!!+TeisSCD46iO^=QAq}`C^mG_Y&wMoU&`IqK5)l5_)o0Ktt-f(#q(|`O$4wDB8+gI zcNOke`N~%YID9FX@tMzjhVl$u*I%gVyB5#5Hoj}TsCEd&47xE?Ri%688fN_6U@>qW zDxUO#kbmcq2KO^Vdr|w+7w@f*_3Ia&2NC#2B=jQF*M@$QSHr}@P*2!iUG8jTI0vV9 z_xNKtTI6^y;?;B+0dqwU&96aWcDv6%s$tt2e!xUoC2|oA!9s~#)81@6?n7}P8ZQpj6>Dn@F!ltye zva4%mrj*T@3boZN`xf2>jAUHLB-W#|y!9Je_$?vziUHh5(DmEE^A#X}1(D7L`{ihp z6VC?Ht9_K%4wDptTt@Eam2NBZ47q`N)`#vsn|pN!0>B}M$H@DHWR3MLZvsbLo5d$4 z4X~->AA>Ok+8p#Yq4ESA?t+?!k;YnRkU7|pA?Pz<$B3?;2kLHgaZX$X%09+?GBePU zt{;jZfoMiqT;(IF+A40DU7M(L}nzBpw-H)1am6b1+E$6tYMfm zZU#y~XJ=%tSV*VOvskr`t`B+X)NQ zDwmta)eIRf!`vb)v>;=^=pbA|Op0~-B<#te_AkQyIkAU|M8D2L1{t1-`xiyQi$am7 z_ChP$0;Y=y!7GKx6!>;1{#*#_sQF)VXo{qd_k}Z|`=pFsOXUB}XSEk=(0w9pZ^q)$ zh}za*Moit>hY>`3c=QT_(ipX6)?ieLps=luZ~^cl=RB^4=Xn;~zTOiKMoenzUxmv~jKu2bQKs#UN>wFmcL{ddJnI1%OuDM)j2f3JHro{fxio2d#5SlT? z9yx}Eb(!-d$;SqV6$CaidwLJ2Gti-UC3AA{36IE%2`1fvc`YV{^za|lW5y{<#afZ@IHz=YP2a=w~$i3_c2zF`(6 z@C#6*zOcMNEg|+tvNJ95N~C!Xe*}{us(+)vMI<#hKrW`FN4P0e-|4~=3bnW~OAVgo-(%`wFQ2Vw$?RhTmCkRi7qo&bk_ zzraH1VF64^-YTr5?i}4cXJH=&V8k${U*Ou);>MJ`E?ZtJ%DXvbV!BRIJisD-Sk`&1 z)++Y6kmBEsn; z*~6)`pa69h?U5=8LA*2)rXqKw*h^|)3)Cb>z?FqMrmI(uAONm|W4hCEGLM+ouan7L zP(J{dT#(w3;{Z|2kijj%Ru7U}Q0YJ+0mI|)R9UJkldddy)L4&gqmIe~4i@-DmG{s* zgh+6=6fA(ChhicW_l7v+df$nIiL@Dq(+TN=I2j7JM^IyFwS#Blc2$e1wq*Duh13}r zM+i$ie>g%12zv}`^pOI~Wj)2TRpA+EK9H{u^V!|K$?00;^L!zKJ7N4+M)2Q-m}&Wf zw2bEZL3&=hoXvNwPRwyt3UW%qg)ZURghTjwa#RC53!S7s)Y3MkWow<8T2LNAya@GK zSS>=!1!V)`wZ zG&W(P0SUTUIFNEMoZ`3Ul$C@rjs$r^|eYvKB)(QI<4D`LM%cAr#E^z$F3wx`ITRJVSDuZ{TE{3*UqM{Sg&oKG3m=6@Wpq}HPDH^J5Zkg(vfDXI1TH2#fwtH zH(h;TCXhsm6D_Iig9+iPP&Er3oRcMQM%r*WDR#tBzz>!LNesrO_U-=h%VcH`)GAON zfk82YI-4PgD%eG6@DQxRTpcDTX6#0n=cHxIA&88GFeEkvo+ywY#=DWo3A;l?z262W zCPP9|Nf(rR{m9}kd~r7np>l959v{Qzb}H09AZrj3VN^4`maG^q1q>vhl-?&iLdOZ( zPof?dSscF)^H~g!Z1)z7fx9ZNU##@~WNErFIalN27|jyam-cyp)2zpz6{$@>?}A!} zx;|lU45uT7%jz9z;M)U@wU*+&r=nLTNpD74i%VlKl6zsw!Z93iTQI32XO>?r(f2@d z%`1h7n>1>TUcM7$0K_GjJO?u>tGB2Yp{cMUJN**u?m(Ic-v@+Q$+tZN>jL;T)3)+L z2z>@c{Rw_EE{-*1#VL$ueHMM0WfAk>vS?q_?7lj}2}N=;d@l$@Q_vUjTs)?4hJxab(vAz(Cs%F`?G{X9gCnmWqx?xSMQ^=>gvM4nLAzX46fZ#l8vHsqK zZpn}`7^UD-$HK3QqoFRyScru+yWDq@dA^lY22HJ~B)r0OQzG8@f?@SFPgX|5xCT}s z-J<)aCFQzmCZlvG57*!*2|mGdlbBeXw;_;_ISQC?-ayj2?huS$uaq8)yzmMw^zR2V zjvqg+Y1(i&q?obO4}2#vhI)}JD=YM$JD>2YZM&|Gt24;Sf0 z9p#_?>gw6HI!D>QxElslIBYyJGkUWOaYYDjs=psZ8-}W*sGmn9b!#FMVRP2xh zaun4X()(t~MI4GQkv$>=1-_{$4n~~qYis@t1&lQGGh%l#v6asDJt8-nh0qbo$@*q@ zVjRX~h%UqI8CWpDo`$+auLkluBu7kUN>kBENmQta|R47 zh|!IigErl#mtnq-Qgky3TyR)FN*lRb+Px3bA6ilyVjWjH9$@GwQdnFLA@pey(LQYLMLKb)Xso10~ zMxnPM%7$OHCtHJ@OdJKd0$~rRu!T8a!MOwMd@vAc3gMEVXAMJ|p*RORbNKPNc@kD8ovfr7DzFVE5|?TEy{U z%w?E)4oVUDcfqr_!8s2Ey6ck4P?em%of?Nm2QnKlt#bBl+9NW3 zPfwBw>nkP5K|ySFt<*A^PjI72Vawn~zAO4UMSg`ZsKNRpm2oRhn4}bOkuoH4Lo^R- zXE-||5~A$Q$I@Vsusv-Aar%aP;bKAQL5MW4R!2Q!WxJN5!*?8GzZfWQ6om)i{O$nE{$zw?7n;|ICl zmuZGSB8zk7wcrZO*x?!3Z1x&v{N32eo0{^Ze;sn~JWzw5G2NE@+^26yJDm!@LB>eT zgglS!mg!!z5LnfX;LW}h% z%~^yx&c4FhqngzAQO9%48zzn^#GMj06QvO<0m(uHyxUU7^*H8GYX3l<_INJ}Yl7bw z!2{X!`N;_GVIon~vcL#ztTi?Z&QQu<*UjDDfei?r5u@|y|6}hxfGoSpbJ2f=olXvY zPUkc;Jy{c^8I7b-mW;qcFhm&yHehV*dwElbFgf;2=iH}H-e{#c>=>Z8Lo3xS6E%UGy_yWeed2o)x z35hw}Mv<~exe&1-5-}V7$%(p%PM5nhKgdhUi5d6iOb$vyRiNss$@u|8%2ujI z>O{b6hq*i~*pL{3J{wHs0Eb112W_yZx?Grc%&-EB++8PJ^lUA)-k~YY*Mf(7J-`eFgims6HH^h{Ss&>8Oa~dMa8fr|d$!Jv;Ns zeD<48cwIc>i(mZWRi1IxpK-P4($(@I@WU%yE0|uBt*>41+Wpu5Y1RG9{`%ULS6+7c zm-w&m4lAU5>w&Gj@3psH^@7)4cCGQR{Ohu_Ha`;D~y99?! z=b>#!Y%P-7U)A$IpU2)6V`(8%3^GQ$*S~p!*;w)qz3Pi|z*1zXm~|FS3%gk3gs~cn z&vDg-Ni}w+i`{)QVJ}tY@6iMoq&qWX9(G{};6@O6#7|*(T#iYgdja3ZVHCs}Bo)_i zj^Hw*TR9v8=41EE7Sey7^Bhcjo7U=JO-?Q`4V<*%hsb-w2`3IFwm4m>kdYK zhM|5CCjq?x%EKXP&C4pCqA;A9Ki-b&B4;e7cbAmdomcHTYG-2fVkpJte3-9$(GDr2 zaizW`iW(fbfDzXr&~PASs?YS()4Tct`F^UtZiJMufHVTj2V;y3^x(-YsgdZQ9d=L^ zYgG?cjRiO0T3Hf9q|z><$d*{*kVriq(RrZPy};HPQw0`1 zWN@9-Jk^LPrIk4+4QfU2uIC?GsQrE1)EvzYc@cpoBq^-b<8yPl&^6jJbDwO>9d)nI zHua@1eMw@5)D|UXq*AHZY*Q=cP74uy?O$8S{T5e)H3E~?#ef!fxmNtGC0>_ZeC3|C zkZG--S4F0kkFzRPwZ_X;CHT7Fv~mC|udpiAt(^IPk&6EsGk(G|{?+z_e&8HE8m}F> zu>gm`pD`}}&n;fX%*7YlSkcrS!nwO-IMj;YX4Ce7Uqp>lR<)j#8fA2|ow~myx-dEo z+T~EC$Z>6Yyj#ldHhn47C=amVdy6bLDh7JA>GsTAQp&Z>oV`ryuf`m1nMp5kOO3uR zfVVc&=)Mc^HPtH>?oo0SUQsPwB}lM-OsX6W3zi@_F6=WAyp>yr95Tg)6#ZKOJ<#-8 zPeOQx)J9`<7L9k*Q+-vB7^q#4o)G^mL4!y<7V<9$)Q#{x9G+0eS0>`s`2-4tX}FPn zjmUkjDnLE~wc{~dV>*O|Lfe%^)yr%p7(3z%uM|FruAh`)0-)>xx0(UXUPeQLG4Ov(kB9}Iqqn7DagG80btZpZHQ5ED6MqfxZ2@`kLu2tuR+LaiFUi*wL^aJ1ic4uJt+raj*}kH< zOVBhApC$J3XkEnoxYluG-s*Q1U<4{y)*q)Lj*UY;q?9R1JdIcp!%quh32_Wq2d0#V z1%h@8$O`r}B^Nn;a|rKOQyF|#V_ziRg6_pkYkwnts2*sVRt;bl;9Vx{!QyFOJQ<=C zLcK!egr5a>*sy0U$)-dUtUZo|G{`EORd|d@IHdveF6&pu1{WaL3G=;gn2FqOHwuyZ zvr%KwOY~Odu)UK8CPKf%cN%7JQo{vq^JvwJCveTsJ*qS9c3(dfh$8sMbY9130G#6{ zU%<*@ql`s*T-AJCvY>GmZ;xZ!<<{Z5!;j; z-L!R-rb5Yj8fD9k8qs|XU9O(XNVTH!p>QdKqL96%DA$(#igj*e=Qx)S((VD>3nvc3 z%N%0~dYB7IYbfGUSfi*299r};v+=GRTTIp}r683^RI~YF!;O~5lT1(_GbQ+2RKm&Q$ka*cgCvi;K{lcV1 zV}TZgWEV5u?!jlkuTkz``5JZLWE@#}A=!l6rrui!o%p3-tdY<%A923*kv%GdRp? z!8nlzNI_ z(cozkJ=j2P$}7%JU^YqU?F{$Ic|@v&lr7+!0BtDPsd?u$dMq#>@ZrB;IK`bWnlK?q zRzizK_J(>_T@n}n1r48J^ye|W0jW@&HU^JE9-9Y{=JK7zx35{&Dm%SpeYHz49bM2hSbsz#wE;c$kum%}75 z;tJKkt^)Bals2J}oLFK&BI@DM81z%j2K&cy4-^m;5JEUk-6d1s?$K;0stEhatT%JA zI}CCAt_70k)eG^NvCc)FABG{h)()a$JMcMGNtaPcn542F0U+c(n3!LVUftsHlVugMeI`4Bu-{?iS{w5 zG^=~*k~Gl9sDTQ+P7-5b|JL+nNj0#+p&J@06IH?Nb>V&xe*>OlxHCE~Gc)QTT~sR3 zN-Z67EuC9;M~rwu;WTDGiZd#=TdtN9r}u}%HUh7lB;CT2WP zJMyN26r;i4lCi%Wh{f#8O=XrvS~AdfhpNKTry*P*_#P2-(Z(r@pCX!+8y^noM>Ke> z!KXPN$6_2{JND0M-uHCiAhm2G<4*_OGfQ0t_)>9B>g65LOOhwJp+4+wt)k?C!IzSI zs$w@%7NLtEj*DO5^W{z%8? zS)|j^f67M(nx&|A$&2W44&Zq|c!d^+(_upEzHf%V3!`a)mV~4r!3ixGrlgG-dB5?B z)o6=2o=NKxGR$ewg^1XbJTo!Y6VKM$?q0MudFpX=kR*ne5dOA!<-(0qc8>*aGXvaB z;7X1x6?tX`+7qIxb2bg#^N??VMOA?*%>$zwrVqgJG?XN1tKoPqx7c%FN}CU0332%* zHBRD^D5!{L`G56fV&u}G&>^2$z`h8o+N^W6Qe?lp;9>gwUgKm#SYb#F-PesE3`il! zn$(Xa>V;%h3xk-b>#lDvL=olD{su?5ay}UVO?Fi!eELie%2bj_bE}7M*_?J!b3?C0A(4&WHM{X-X27-Ay_NV>9sMfY)DS2N->kaYUT+9Rie=y$0**i?G z>%q5aFRhaBAkp?;!#`-(+l->6|V_FS}S#$ za~N^Y(a!|CYG&&8*t5w>8NE)my=KPwdHfKTAyGR1A29%AS)Rh2JZ za^W!s2Ql1;#q)xHPN}ivElf2NGL(x#j3EC44W=}=5vkg7!*?xkqMI99PXPB2^cGVE z-M(b2&?y7#r{d)=CB(c)^;Mz|*>Dd+6`@|n(+Dc66?t%3tDcOpB+1>o6xx)y412s@ zj?ER@NuBk>1kUrxO3y+$k2t3qtLVw1NTyL~cjSV-6shufI$g@|T%-q?=tcYr=3haQ zCDfN9=Nh|;s$G<*fhGZCKRH(mVo~8PL3#`Z=OABJ-THVQDp7DHu1J#621^0>?CX6Jhrbh)bH;0fc^7xn}NJq1;Qnbi6z3onQrlcF~Yb`|2AjVpKR z&oWM^)ZiIulr7WnjJOxtq~3_t@^LK^#8KUJ+{U6QD6$T^IKk^r#CQX-l%}->u~;2( zAp+1yC>IhhV%P+}q1)3DG8;y_8rn~VVhGr2A~X~rO)O#Ubbv;f7wUrI2sk#5p{y`> zt2A;^ksOM{J|VfCI180hJoKnWd{>y&dR(e3!Y8S&g-6pJNA2t58DIVCSNHAPr)iq} z>@%5+q#9R)rj{MwLbk373@vEHx+bO;;&0WFtQ5p8oE)u((PBs&v?I0N)^*{mHQJR^ zy%9I~TAXIBH?k@>TKRsj;Te-no{_h0g&9}>^b^czVn!T2da!on7R56f3)VA#bZ2V3 zwD|T>BSd!~_(iJPE`7s?msA%G_ljOAq z<$-Z*pSHp|EuAi-epN|@G`MeTi`AT@4!v}en`A2Jf+C9fbxe4mOOk*Lg5(WwE)B)=urm&|obrX($#|vhh9#O~tWNkGS{Q8`Zfp%f>x9`9 zshDwRocE2_2Yjy2L$)7{-KRYhX>WKr!)=%lh*-kbN?~O#IqP*uIO;w)YlLpoqZ1OF zqb?D2N`4uki=mJ-E(x0Mi#S46(Ca?5p~kosI9iaWtc~g;zBT9SUB2j(>{7?G%o{X# zju;pHaE7>+LF=K1E#2~?T{iCLb`TL$5R2Q|ZGl?P-?pTpz_OV2Ax?=iYL+Kc4vF<{ zyZpOW+tDTNUr&v~qeqWQ%#dHWZQH5lcCBp+)}s;YhTg6v4c8J|tIAne0YV!LI$G88 zv-0k%ZfnVO*Z@yji^QNceB&)#>#IIKtcMCMf|8eQt5UHRFKd}?>S}(5Ts%MF89&&R zZOX+%N%ZJ_wIi=DKo-2y`l&zJn;I@|-Zp}_3)Rvn3+|Ftc{ZLMC>ldPBvN2#p{ND( zATA(7cs|j!T_MFGno{K+=EmA9;|WigbrkC-V-e99V*@TS+wh_qG=jXoSYz>Thj%$x ze|P+`wzeX4Dz!P7*I*1tjpG|RelJ&b#b1HodEFh+X)@He1n7Q_-zwk)_`jz?UeH@O zOd)n21$8X|BldxS;6R)RB-KzY$^o1?t8_U@IoBed}Z$i#Y zKD!Xz?}%>XPU!AS7Wgp5yQ^WM1I>4tGg>@w0&}j?ah~s}L2MVC-IF+rw+g?msl1pi z3?~?zM5T+Vl1Lktn>(rcMZ;@^A*f_(4dly$%jmn z61)LCS4ZtehY|NIhYoD_jRfK{}!WA3n{;@n%~;PzwA0K=*6;Ywm{ZZ z559^Sl}crD0F6L$zj9JCREZgl=JpfJP@Ccz3=a|X=(gICw-n$uWu_nb{muRb(wUh{ zOO3V{cspX_l)Zq>9*^9_VLO48$%`&K1?)vaLx!?|_i^MZ-!g9_^Jj9CXL}qGk&1Ay z>Y*y8p}PnfgSb&-he%(5c9S{tyseI-Kb3sCCpQyD3G*>8kf2KRSYYn;R3GoO>Quqc zBK94kN}CS?Jsi^4sbnws3pHKn5iAC1_#MPv5@eI0TjcxZ-ZPX96RG4y^N}Y}U%LXm=xZcNsP{{TXiraT?Gx_%*%yY8*~O zJX%A&KyOkdJc9FD`Bc&kjL4u9$=Qw5QObe{b{h>-!Y9>2Dv?JMdeRT3H7yb7cLr#y z2*$MH^J!eI2ZpZ1aN3bPDJqOnbpQ4)r8jn0b5 zEJ1n`zH(#iRn|cek*i2mo0p?1O{Qw)gj}Z-dsfmT?V%Le_DsahMj~76lv9RrB>yU0 zxtOugk%S78X3D)Uu=|3cmjb5YdKd|-)JnRM4oBFi;YZ}fBU>D;GZ01O4I9paS#=Zh zD8D6rIjTAG94=5%LP|>8+;!PAjnE=`Bcvh_QQ%9*$~YFJT^4miK1bsa5TA7)6N zv48*mtC(?>XGpYYk<+(wTi0B^s`zVh?z--iRVCxHo3_T1>jkHE0dLtc|CYP23e2z` zD_V7uHy&uP@}_G+4m1r=D%`SKot~bilwQS*WpPtK-k9Oxp%{Af&f1Y91=tM1v)afX z3|2?u?fJRxTW9f3E+PodAb5qN9H2Mk;YbSF8gRS_&%6Ae1!@SiQ?(}&;|NY8sV~}> zti_RBgGQraEx~Kb{?5}a}{gnxr>F*8jX3W2d;4H_l6Uv`43I3P#a0cr` z0o571j6;udm-wa>X3XgFwA%#glQK+*gu|c04cDTx3@#wHfJrh`Rw;SkS%5>JDyvIS zoG0}wQEyf^XkhlbbSNYl<`&KBxj2`1F@2g3%+M>BlcwYOjB-jeEl^A%B_iDptuo;u ze!FT(>%3%5KiehQMi-fxV%;fG=9m_Fx6k_o6C zLNBECvRn#_Ji7>O7W3jzH?S8cRSg9qz!Dosl4WmRGW$JV!NEnkG?JaIcd0aQqroFM zfvdGfeA?|=6iJDDJK)N`$O+mLA}Zk%^9CY>&-?iiub&0DH=NOlA5Axmh+8kjq%=?7 zP}M|OI+03{s+Dqh9EU`MnxJNghm>zWe4a8w0!jmi8fh=G%aJRyeoRiCDe=SF=HL!4 z8o)+KV+)&UUPrL7#u6{3}=R9p%g-5HnG?N5`B1_<^2r2vq z={Z`xzCL|l``XteX2>sJe)bYD5{ZOp7Q0?n|6H_8mR?JMt}4DaC~#N3__|s-tjAZ@ z0)y7Jpj9n(Eev$)3gs39-Z)6Kwuvp9y^TO@iwVE>vR5&qUa!y0%*dZJnT+eY^4A{? zGvv=#Z}cApGY}MPJd{I^zPWbf4FyO5KZ(nKlBy2Jqjy%buPfnrv*gA!@b4hx2=vGR z+}#1?3JlMNXKZmC2xIsFhZ`Um2X;coOgQIFDZ7a!8m6gbLJK+Gf?g_RN-RYP%i{$- z^yfs!6!c1wcLO~g0XKNNf$k9G1)n`bASv{p4wcK{^8oF@;v{?tOZA!T$I!09ja*y~ zYky{;4lVM=kT6Gu`OW}zAb$tJ#>q%y)S^kAy0t79=WCn`2ac#t7cTQCaxJkbq%D9cRRvJl7Mtbdzyq#_LLm zDkW@zzRQ|sUB)HtjA)b)gF7)>^Ekkd+kWp)jZ!jC*dR1y0{edrO9NL6%#vZC@0t~ zQ6XfeoJk>-iSRu20v+pI!=a>Bq7%4AB(*{k&J9Km3A_`8E%?je7Ywh*q$7b@aXn!; zhDCg))qL8{^h3T|H}#l@mUwW5vKmHdCJ`&}X}nmsWBSKQ|IuO-GdAqcc>M9luf|PX ziy2pKQ>#jl4F*KDP`cN`4A`(urnMaW(J*7xk^FDKj1Ai8*1BeEKT2!HwsM}AP54?) zm#df|abk9MR_>I`<|@x9mLDL%18m8>zSg?2ki!9EY5M zsa_GzF>R|)h5-6>=!>fCh*tyu^Q3Xk!clJB-p~#+Rmbo_Wx5piCm-XyRNY~ zmt%9J!j?Ti_94ac_wtD)YfUWC*%bQsvZMSlhB!t8b| z-3^P9IbrS~#wpW@17?Y*m`Ab$OZoejNG#;%kUL^dRQqa6G2!5JM|ty|R|1VzPsFR2 zV^dyV10wzA#O96>nhPClVj_&JhhtnaKQS-FFbc62d&E|eY>PPfyrzYr-={g$vXG0% z7-p2K33kZ};bAi8mFIBM9wriZFQY(;oXXZ1mdX>DS!ti z7xXzH$&F%IMGMurmGwl23ZD~8&3ulGT+@g62YrI-@oEy*2KgP6_w*KLqraTLr>m z^C+m%vYwQBsOfrA-LCtw3QPE0kBfQ=mF8J~j1MNP+TMYau^}5CPK!1QU#*~dUrG(= zhOj^j&$bykH{n!aiKG7$D6bnt@c0gSMlBj+n zYV2^eAcQ$38E39^g)?+hbhOw=sS>s#G6}tYGx5R%w}>)?GLyMn<(Y5~j9F-x;HpYp zK`8|l9r;y}upt)1^#rNv$w2S(8x6BQ8I^Q8c0)b7zag){OPKqJTcq$@c40Vg#hgv| zkI=iriDSJBU&$ryn#4OP-IE=a{Psn&+f}8lG5}kU^Rj^?UJ}u`Y{lMY>-Pl`nZiG& zk^!KH1t*xCRfS1JjG)Z|+Es{}7)2@&7Qnk=n8XWWnu`t9Raa<|Ri?L;kuKP&xvT!i@~(muJ`&a1_>L+HrN2UX?(d*rf_ zi)US)aV=W>N5>3?hqR^-{J{d;1^yZR+{d@4{8HZ=OL&tGh6|f1H#By-MvFe`h$?^S zs2!fBi|I9vnCgm($O7_oaUuEIvNZ6pPN=S^=!ua??UmfE8WIRJk z1ch=LILut+ZOkBjo2Qbys^|?OsG;C%nigT2U@#0UhXSFK?VJ=CAHnz`j_>6Vp>RV4 zYIXP}4lxWLLXhO}6~Ue)L4ggR8+{)PEkt7aLm5!*>al4=7aO5O@IKs48H^7ZJNeP#x&jP?b-=w;BFBNj=w) zPhoELw4Vx~&^)UM-YK0|h^7bF=RAYyK^C4O*x@SMISGD4FP}{EDV)ibN`;geIuaEo zRGr#b8cH#!tJ1xo60J1|uo9kT6{2z{QEIoh1J27m^5Ce#G!elKsn~1T(9)d3Vq2x2 zP&I~+6zHB1E&)6R!63!w#Kif&B2I73U5stD%f;m4aBkM`79jG`#g4YQcvnq3sJ!}O zN%;rDle9V&w|T^3ZsMn^`W}fH*gb0Mn|FX$(=WyS%YW-^?JX#(0z33`uc(Y#b5lz9e3Q()zu}x`A8&k6*I2F z#=23V%XY3Mn^rw{<2w7YOKrr)weG;KOG>Ug&K8ri78%;0A#1ItSd}wDOP~c<)0nmq zWV-fO(%dA^SX^9`dn8Gfi>FqrweHjR6PgxkB+aU&d5vlWa-sqWrxfY$orvTba0GZe;opyuq>rkOQa^W~@C?C2 z9EXCRLFhh#_X?;`xYdBPlMvR9-l^a+<{5XU?SUE^8^We`_ zokVk!Uztb-OEi9G&3w>P(*H8RTciqs{8PmHypB$Q(eG>T3{?wL!Fr+u*rWl|yB)EF3SW+qzB*RL$U_@1mDwKtSlZ`j-Q=Im9bRfa&>IBf7I{0DSbk!{Pk*V zZlwK6u=NV`Z$ZO%_dRRu^(73jX$&549gj?I4vM4)CXINYAMr_wdo#x3tGPtBnz*H^ zlGulv!E9SJ*eNF=6fcXgr14$Y&^$P%5-?RY$#G52n3fOpyL{Lzp%1AVEoB6AA{ZgX z@2B|;>D)J`{8a<0mKJQna07t_4xeK}&hUT2VvhSKG*LvPGejw2pR~MVTJnLa6W2d> zrtjz#9{ShO5ByV~`qaJm-a9xrC{@LHJl@{kzLsrjEuFf>0oUb>-Jk%4YpBCo$F~^p zy1tnf@^r0dS$R6NMStvOjYu>;oT6Kmai+3UNOZ@nhvHBFNsF+MJ30ZOHk zuIq(i@DrZV9IO1C1@vM4SNq{TuV0`KR=o-R_^<3wTgA;cj8jAK%^VZPrx9<%T2PvD9(W0({mOA5UP5RS$Abcr zUGTPj0(+I;8No0eeQ$yccEgv;;_t@gG@*M0>;bsatZdkgr1pxwE5cLMtr=#_Zxl^t z;pm$h`rQHeO_ljBP(NIO@E=Ipu9ba3}~4Yy?KclwcojyG-0J>AaA zq`P-!el(Zym*n(cgl!jLdjO;hW-?G(fUE_+2BBOv6^PF&LIjIP;kVJ9+GBoDYrHOEyg5nUBF4d*2l(?U-(hg`B% zz!p`41>_fXD;07nKf_cDsC$qY6~3c$3p5&{8t2m>7La5!4Ojb(xxS;b?7Dcyhd=z` z`|rPB@{H-}>2x}M*IjqL7Hj+3DQnrL;9pyQmdW&u$m?}Q;g5_uEo@e;1En?|Wz-rS zH)40X-bYw#R$Iei3$6n#(FIn6#@8@oVq)Tj7hW(;bLY+-VJJndXn_`MVFvzT+u;9D zm{A4Z$zmVM$9~~$)7JZD{mc4^5A97=OPdDAXq<~}0tG@pE85}{*@D!xi5Q5E3kI`xr&L9=n+lPAE)0oqiPB=| zT{gTWl6;VmUpow)anPoefBD5(_>2Q*n-+Er;1&q0G<=%Ufq-@~XivcVx|DV3_vesR z#d{3oBw@it#j>I%djTD8nu#WxkWdEx0`|`9p0CsXkk%L>jQljiuWyzHDT7j$%is)~ z{u7%1YG8JH=+rzX0;7A->3DCQKY;lr ztaS#+68spReY~Sli1yzxuH6!-s$Azlyaaw!M>h&B6|xYM%?vFx$(AG>y~^{X=z?6M ze=q8<#6%fLov{SbZV2>UzAHbtDG$qKYC=$<)e{MR0Y|ikyj{?#8QiBXlga_~BVI*N z;b@;wz}yY&5*9B2st6GQ$5o|mF-9Jb7;oK8Rq~Cdf+pXT!@SI9-Pd1Ee^ zL$uL!T?mjb(DOjp$l8Z4VwDUNk?TktHjOX;{0f8DrfJ;wXq00os7) zk#pIoU5R^XBYIoqcgmed$C>Y5hi&TCTW@{jkw@;k@4lItnT3Ugx4!kQ@-x2b(vZlp zstkg)6;ilP4Sg+n*W#KhwPRR8cQ&YruXWkhF83BndTUR{21g4mA-TnbtTj0+@4NCk zYmI#62w%gDXP$XRa?}3)eo0cV*0q%TTL8wg(5Y)NV;O9~zjw?i2(lIUrv@r-d{-IX z4c@4J`e%2iFO~=PkC9vW4>02#zzD$Wo1*-srbLcHFh;8{CmPhuy}xMwoU4wJ-}wzH zlic~TS*^sYBB4Q8gJ>NhU1TAiuElb7GwtG%)bY^8@}^=Xvv2>o=z~6N@m0Uo1;W1% z;$?xUfb9aA%6}l15dQ~+a{}Efl;!_M6DpzetAI0s_G>!Y8iOa@;IAA0_jIj0pbs&; zm!YZ(ft5qG&qb&HvP(kOVAt3}xxJCFy52pLeyFTWC6i4}IR}y%)c;4UJ{pbO#8M57H@p$8{A2=O5^Y^Y^kFH1^3aiiu?CDRs0c2tSrV~;iX*AWlw%Y; zNnZnvI*ujDjn(NTx~lvH5{H2v;oi7j{D%x%BHg=Z#4!cBAtN*O&NE`y1aw&7C!2N7 zV^D8EWC_{=pcDcrkL3hPuD3_KoaJR2hVuQ_QFI_ZHgNM8`5;qtYFg(-LfQj-k30~2 zFW{g=%~2QT#Qa2ieL9{C7EIyTBDx5<2^gG*bPr7KfXg_+Qb)?>O}_GPkE@m;m6HiB zaI&M?n6{87Ot~;IW1QdvobG4hJBkp{P0Yzf%9XfQ3stV)ErIe~eMi$krH{;)^ZI-c zuhm3U#Rx=hXkd>kRfA*u`;q#YNWiE@!?vy^a)SyDU~@PeqoRiBU`SsVN}N!kQ%^KK z;WvSZg7?o_^=y1eZ)zH)!SGh*(@XjMkXZBCrb9fV%&Mn9*W3el*Nz0jO(jEl5mj%((XYSDA$T z;qk{GcU^bu)~#3Lv*m`@+gf18wXgyIt}#Q7sEF_;kNif?efWcO@Db*Y>n|NSkU3S_ zwttwuGgP%S=0LSUg=W-{l#*X>nkIIEGhr=$tF1xI-VaYlKhaQt@lPP`f#4}E{0z07 z*u0mE^I96ps!xNdS(_V1mk(Lh7!{r2OXo8Vwz_YeHD2#4Gf|`&JaZ2CQ&^NhIlbdT zGMjLM24AGwts%W9gsQUcgOs*^N+iaR_cq(LK94G&jn~h_JAQhO-pnB*lnvcg=Be-) zt^IA(<7NgGUl6xc+Y>*o6)I5T9z>#m zwxSCi+7#ie9wz1?=ODZ*c415RB)bWbZOr9554A|I-Lx&FS+OZ zr>`ZBmkG*iW%Nee6|gqKt$X6KVXVuj)*AL&6Ta-BS{$}&v;wTaxmAa;76PFk2HMKi z@})0*$+E0%+qTIi1lLrwUFiu&*W`z3!ogIly2DDbxRpLK$Hmkn$SwFJdtU=zwspShdf`NF+>tqM}P7YZ36c@EQNQfWJdx z3|AHtUZ~q1Sy1a z9K99zEasoZ_)doJ7f?Y8!wdocZ<8Xu0YM*#SHOSF@Gfexn{0fLE9y{h`ZIj+p4J!t zCRgL}HoY*)2EB~7{XbvIy|UWuOOyU)RHsnK`9j1RgEQKnuj4S`_rOmJiBPqQh7 zC1f5tW+A7M^6sunJwr>{t#Uo#U@<1*RNE4u3x0QIGNrZq@qMo9z38fzSxZ;4OMcplxSf6Tm<7;{aJPVIP;F6e1hl_d zPh||CU$IFQkvykD72pmGWiI{}!6=|X0lgs3BXJS%exa0%(@lqmoIUyOhUKrKbS1UK zGX1d$dJ98w!A5B~uVG+XTM*oY$zg^x%=}^A@zD1-)47tk;CQ1c`-(g_mD`IgvP@D4DCY@ zjoMFJ!B`mIT_cx6@6(#YjmUp@^@n?a_AAY~It?!`hJWjZFKDVLw}lt$x#B`54z+|? zjd#|fdm6|R;v8C>$vQ(;=R0Th_W{HPU?dMk8`9HotfVQrJ_9(=%-@#NLX?T)^4ABS`Lo-0ql*Iv$Fnoa zlPo@j&vN^%W!@gbPhK}frRjVyZ(6-zbs7dO2QOkO*Q=39?F-hd_PijzDXJP#Ag)% zqdfwiYeo|-!Srm`aAQlFS31$GC$t*GYmgj+4j*VIlw;typf|vbfYS|A^0E?CG#=>L zApeW=@dF%94nkiN`eUHkfNa(AFXe){2s18xu?81HXz*Z3v;C+UbLdD=9ZgIG{fy8k zuf@B{Z4XwAJ4{90Dz1v$5P>yR(bjJ8bwyZrHr5zn?VKE4g12Qk>8s6cQY3sdO7oD2R(3{vN^0fZnXb zuiXss*8{5o8HUDL2(JJS;G6;9;UHDvkM0EeE)Y3DC8&Q77LEZ9p}h;1iclHj84lk@ z@PvG!2-3}7eqK{0m)re1+)v?30G|m|TiH9B3OiOduE`TU=k4uLZg+}VNh5@^mN_G+ ze+qgM!cIcP2Q82=g$kM##7JHKAR4&TmQ4I#wamLI#BK(%1L#YNSfTp>WmIr$;~Y4z zz{nCzc)%p`1!7bs$k(2X+uw@{fr-vF-?w_|#xD!sE$N}oMqcAEdfSJCcS{L6cx zeG3=`Kn-Q+;hwTk;GN3cAf8nzXvaeMd%7@37( z4R-H>P3=vMcL;a_Uj8g-4H&u?Vx7W%+WY*A{*$TbpSmO$gXcZCR8wfT9fi+w?@x7e z%!6AI8W+eA=$B(~Z;whetCyjDJ5-7=IR%dw;bj2_5xl<#x(+stDH&kLAvg)N3}ZGt z;Xu6yKK_=beX6JmQ8~w!$COj6|xY&-+D|o+ll?s~OzYA<)5>_pou) z>~m8GDuRJOWs#4xYwyW6W53$LolwNe*b80?{Dvz0tIzODll3nzp%dlUU<19ufGr94 z3G98zfeA(nJTxiX&#VtJ{jSZ;N-2Zt6`y~?`|O2qo<<&|=(f!&6=Sgo|5Q-Up&Jnl zG;<-V5g50?wBSG&B-$a!gBeo+bLFRDuBfo^rRH0bL+gye0|UxqWNd@z9>I$IrLUlG zj^T->MN8guR}%6`NOVFh2gHTNsu)8sI00HsxIUD8saU9giy%f6?XW)tAOFmjqu++m zaSdamwL`9MhYuhAgFpC#?c29kt5tbQE|)V5<0{E`&8xAlFkLC*R+Wq!aVo$XW}lV& zTL|SAH+}6=t6qF9a6%jQ7{i9~W7$Yr9I)2u>jG+Pdbh!cx;DT`Di-te^T}jVDj5<# zu2z~t*BLbR9~v{3*w9w`=x+}lIU)t(J>V{hXa6g7UqL(X1h!573e*KdYQ;A2IN&Yn z8Jx3Fyr8tplXGy2!MlD2c4vX7)M&)lp>zqpScGFt3LWlZZ&c@Juu*r(L#hC(IE8{m zT&|>`C=v&&`d@Y7t?z59nsW+`#?FDU0G$-PJ}~wvMcqCF<@1odTj|uwVfKiXJ`MCFZ9v7A3*U8%#J~^4xL+6Al8e|z!PW1b}UL^@J%MNRR5I@d|;p% zFV(FQG*}Yc6L9)5xOf@*`r*(+5V@o2n{HKt( zMA0~~ipJxZN}Wa!ZN_6Ec5uYumInsHBAKbC_mm_H!8t)TF;RwKQ77++lG`PBh(nx! z9Z`mVl{kVRYCtXuI^p)z%pW@F3+2uSJ^cH95Vyp4CcVEa&V8XHp0^`WJ8Y-sZ*pzh zAo!ioo&jP)^+oaGs922PI|&R*Qk78!j9)mVuwlzquxyBzfVC-;mv1Ww*9L$h(5ou? z7gu273e3AO8$xLY`Y3F>0cyR09!mavS0wZ;bnX_B@{W5WOUSD$D6R-YPA^2XfztE z&#-OQTbj*g*AY`u;i;co%-Em%=&$ei>9-!{W|aB(-~2L+UW41W!}1uMYQS&~M)GiR z8a8-qBe9o%X^Fo0O8d-W=b1*~N{jsJLvZU5@MT8kp!s8XX%7CP#q27*Oq$hf^ICx~ z;Wttvck`i8^c-o{vaJg_a;B3%n$3QGH`#YPuSV@MET4zdXLyF0F@XE-fJPZIhoLzG z*S-oHZ-ddpJW}K^29SgA{T^iVu=o8?*#Vb62j@Nu`Ei(hj90000S5BCA~PAv=*~0W zaVA`-H``xfm@$y~CwDk+*zKVL^}Zfn;RA%?r;bO7eYM;%x7a&g?^ZDW8spY~#y+MnNZPc7fc|&b{ z+f?ajolhyM^RJ0?E@a%clR44J-o=1w045!nbvS?x5Z)#m%dja6xni=U{$=jR6> zCh!|u$Z!Xqo9%qPIrpt%@s>vEO5MHI$$Y>iZAO5KaL*7pBXIQs%v=Sh1UoCReHUbh z;A$0~c?I^5!0z3goD~$-c43$5ypjSFX@v1+o=N z2Pn<39z(0o!3(XWn|D9;@x?!X`a-+OFvIByGnhQ^zyqKD^rts(-aI=y%N#~VHOl2O zgAidsg0K-BX4=yT(V$sOI4@Fxhf<58p`Xv&jsN8Kx`7LW{766R)NEPg0*haQa@ zfQv1-q8h9lT)K3Lkx}MVh^1{AW<-s$RkVYcE805IA6$Yc%=koC%*euADJfnhzW`CO<_{KGOewwGvPeJ>IJpHkgbDMCF z%VRZLc)XpS&X89q{J@1bmf?4I!gzt-J`{UC3<%je+xFTlkN zZ_xP;Y@2`+Z-bi-cmru)@y6S-*1rlDF7uGlrtL7k&kL-a^DtwCM_3wd{$Y^Zd7jUh z8{%leE?um_MhZK|c_@Es8D6cy#0=~_2(>zV^(A<25pK@GBR9jy&EQVMg%`Zk^j64h z;Mt*e3#RHYeh@O5_Nni<7f$6jwBT}sK10b5-Nr~E^ZicdG_BRj3+*8{OGXD~OJhrX z5suq{o_FX=4lGb-fWm_%xOWJ4Zh&GNzH^x~jJ=aEyn%lZc665GTw@dbw+ry6bFeuN zKRW^yDsnU8<4&8Y(c>C%9eYwpim}gwqHxmglV{B~fsi&TL{PD+`^H{IfE0xOV=x9El7qDS{ z>b2|Gr@mlDXi(^eU`Ftqpi>`pM|hoJMzE0qVFm*LwsU4?=G?h+g+gIsVxn5DGKQhD z-=z0R;4!dawFCL&n=>9}P=*=B4ndefnc0lP&{B)AqS#egjybTD;|(y-r1dOW%JCtu=avTl`to=qA!aPneUpWczQ6L0 zf6(k?8*pj~a&xey1OImfzBdYQclpA6q|HDA{=$KS4C}J+SIaO{C;J_`zvI4CB?CLj zmQ66mh{zQ__2FyF^y_sPW3D-Ef2~M<2-$6QddN!%Guu)n^gCI;=HN1sHIlQ041LZX4!k_w8x8R)M``J}6+4@NahfF#8H0&QsGozE&haqO8!FJ;$e0jE;tGM~W$093cn=f{@bXJ=@d`hA=T0E=e3%@= zb;!cdO+2k%U4nX*v!7f6vSk>Ugz78c%)-bpN35BP@XRGx$idyiu-t~NE#AoA$dDHu zdaFaXJMPOJa-vL1ZAwdgJ@s~zwo5Rdp=U1BMbg?l`jO9+{^wsWwj1?g!RZw<*qeOl z(4o(K<}=&3Z*O`s;t4B30fu5538P6dv7M;%qliUN;UuG;tn;HHTJe#tnDZ^08?@0; z2-h_>6&0tlY8tA~%FYQ@M%i};28>>uIdi7nZZi(b4l&1q0R~Mll`nqW6d+`>qG4M3 zCy$TuuQ_vG4fzgXe;H;l6Hggt0Ab9d!CtOBEw_os6Zp{3mWLVC`wVoY$iL8yg=Yz)?srIe&FzrCf!Q-jN)&&Oi#F*y3gjwi!N+$!rN=GzeyMZ7et-voo+<-!h z4^F+t7Z|70z`GFJ9%O)4fEhm7g%p8w7+yE&E(#ZEJu?i0q&U97mtq{vfs=#1E-Vsw zw#+{YzQ<(%%ANGflzbykkG9F-Irvc>evsoIm>SQ)*&4js;KYKp&oE;uIoMNzD`h_F zcMIWb1r`_i!HsE*-3MKlN%j8lOcCl_Z2$yqYiV?2@ z>>-SW(2a!L;_}(+->t(!8x9p=I0IvC*vNbw2cB<|=bL=CJ98069J-H^0p{Rj`2?U% zq%~cx|4pg<%hQ>SEf>7gma?>(fmw%6u|{aOZgIZ&SH-VCTWvM!g`DHk-Y|nnu~_`z z2S50}_q}g$aFAgJ+cWBkSbhqkgH%zrJJkL{v?B}w<`I)-2>Q-RlGO<@pzBpp$%7c` z6WR_C1{J}UNzPG%HHs-bwDHxp5;2MI{Q2|q^YhG&7#bR4goApk%LHd&IK5BKz_)`? z%s6=9PNRBCb@9{thZ1|gbxCk1MMcFwuvV*6t<^f6c2px`Veeipn;R;Y^87vYmU?x( zA+{Yp>5QAdoH)9jN1Y=#!E|`N zh*wAAHBp{fZaBYW5x=D&C=HWv6fB$tJ(Lp$v5gtoj!V5hVHXDMlNGfoE_ zj{4w*SzDCTwWfDFRyd=+%x<&Bsd%ln>~&#AdRU_|+%0%JnVyWFb-YKwny%<~E$^#` zj4o!|wQPR4>115*7C_l8TyNrJ%i-8LN@{Idt=926Q9WS>J9%Pa;!~gc)X}3y*(=Q) z$y_eSFoW3$=3QbVB?jOIjZQ)#-Nxz!&s0C{w?5ertghj#m^fQ#Y1ZAN20lTwxrb;< z5f%E2A7$GW5`tGnaUdcy-aH!Qve~D+1 zJmcre%Vjx~$Qx6sCqaKjX|62IMVQG=4AJKhc}^#Qs|4Lt2(AuS!c&)r1U#dENp zy4?>5@hd`%y7~W#hbYRSNI8N!0DhFgodg}A#+lrUBGvja>k#yCddCcL{_*3- zpM3Jed-v`w6pHLZMm~mzhZ%B+nc*M-L%~LjxrAO~@zx~?M~fQICwd-9%yv)Nj)Zjy z4YGArCbY7l+=9erhJuABX>hfzY&UpI%P`mB?Af!7VN^U}_|a;$1l24Qx;%zbh0-V*aUtFr&(}h46g*AA zVR&nrDnWGP#H$SvjbmL4VP(dg98@OQ_@;sGa_eA~N%RQ>ozopNB5o&+3&09ADU}#Q z!hG1CFata@@$PrO`|-!$yLId4N@aiOZ{!5j%h09aQ`eZw&$7_CYY zwZl-sggtBalkthEsj1o7*;=i}0E6vR>jL0FQ$pk*3BJ$=CTOLYkxD9SC{fP-$}oe; z@bK_sk3DwsQ4)Cj$v z->+*|BV9QV9z+W!APHi@_As>b2GqpOOL7sTYHw7(&$R_4)aQ`T054X9QY^Armh4_8A9&6@4km0et6rqZDK)@k&%&HF2{;xm{F-z*kATjL^DLF0iu(IBzCezRJ1+n zBnI~I_yr*hEo!wIgNlWP#b&d` ztUBAnx()LRve_JCmN=jk3K%hjgY>dwNQxP$q;fqZ#>%V`GnkBzk3aIrBe&gl+wkxZ z69yb(V`GDZgTfDGCom_JA&2-Y$cK9WafV|(c;BlL5-|@Wh;81UkWni%c79?MMkdq^ zy5%iIEKyJg!73V!W~E-e)D>~Uu@*-*4d5}rz!{l;ivA%|a! zK8~9*hpQN)Vf1hn;*Afbfx(p4V?$S=eJg*8sk~mr?+RXz5ksBjY!Eek7mFIPOW{V^ z#3+kAJ3GrTgIS7InxwsZ_Z~ZTY|oxO3>XT90<$KJb1>$?z(d5T%$J`q)5Oe>_)@g^ zD|ArcVVOC;LFFK8uOxPnC==sN=+LInjIO{l?B++Ux)-js+7>2HH6uR6c8cn& zu7lNf?L29?E$?#MJoy;+9`}7_mnU(I=t)?0!DaMAk}8 zTc8klNrKn&KVCaf*?s;_kdd({~>Y}vBq$dMzPHf<87uv97z3=A^-5V>)70%IG@fC?+2bw@bC zOp<3cjPf0{j0Bal&9UJS+(pbI6GsS|cP9R_a{z1xplmdrQz8zD^8vZx!o){$2yqSr zLZOd}#fjy!^6W- zom2sR9)jWbeicUa-xW0h4kW6|cTdg!qS6=p-J1LmagX3-RAeR18HWBVWXy zd?zR2sY?RG@ndZrIx`$ILMJ-JLXd%Y=mnKk^O{T4oC<`b#>IwG-8JtjR5pt_W5Sqd zpxYaWn((M5I3`e}kC+jnJfJ=#ACbRFMMtN4Iwop9vN8%{6cUFS(ItciL#SS7fr5>Q z)@&2ykWpEY;UPZ7kI|0Jn>UY+j^y(N#ypq_&1HFPDwpTsDQ}M_*$kf??`s0WK4A4g z@)E~;Q90R=3k_foT_KP%?iOaK^FbO)iA)+r0y%j{V2@>;g$YB<%x@BfXQE7totVsn zoz}rs5U3z@R{G=hyDxMEvLi39-F+-n3-LVk$3{EJi#P5`0Ms1S~|PtkiV*&)=-9 z47j zCMz9?jQbXmqNeBnj*X2C4GuESF}iW1M>R^tVyRFlh^&S`s>=HxAj|-1^3-5|)xO}d z5akw{VHW>iWpe>pJsEr9n+eSl^}iC=EMsJtPz}Oy);@?nxwRw^763fkZ}^3i?dPO4B^;0b*El;8bMb>QVC^hB)?EZgV;AG3BL7GcsX{Iwb-I%VmYrZZ-YD@f|PaXX5?x9^4RH&aZm_fpF z<|_+Kfx?V{N9eT-)pkd}V+5!o2&%PZ1jvuv^nXZ(^1XF10Ebl2vry)MXifE_?YCaG zf>sO4cpUZ?aT-OED7>Z-Xv~8?+cKo242K2O7AolI5035-N%M#fR>cI)*3KH+R7``1 zzOf?7IPilMMj=XBXR4mfnnOs4mn!REJR}qfHM_5h-~z&V-@0p(Fa0Px<{R*7{F1so zvc5Ns$S#bGj6C}2qs3y8IrygrPG2id1Cei35TC73rI+s>mP6>Hd?;;(Q;9+|7<~~1SkapDBKf8F4s=PB(*(z_MA9zqSBrY=_mMG#m2^=%=6{zlsTAXIZm)MYfUcGg6a5dc7)b z4f|wfTm15U+AUj2q&xr+p;hOt8JmbZNn)F-4hoT26{JAS2BXTbnC;mAK0tkYuj!Z{ z&PVstP^QLaXgDDN+Lw_O&ZnRlNOVAZ?dYiaWXvo@AVQ3qlghUcAQ(nRlt1h>G+C<; z6}~&8UT)(;6{WP*@4JgYyp@y&2M3QFIkJEM{^jN6sr>ZW%7sRzDdYIwlXr;X89s7= z{z;SlN z<{4d0(d9MtGcAroLeDUl4+E5g#3{l+f?;jc)u9moDI>0Cyp#l+T%0so8x@UbYPP2y zb8KFK@I)$Zxb>Q7fYHbVnPmuCufDajN@xIJ{|0%{GD|E&*Gf~W@L>%pani~p#in3< zGtepu^yT0kzpg&oCc14e=o)sKA;8q5`M}%E`q!9X*9XTVNSc zg8;4t!UC&kO*0f8j=6rmM-LyC7ZE@FqsA_)XpPK0--sD=X5T~^7Syan#-XIG1m9`a zz$~JgMEA5Xyu-34SRR(5Fi((t%KG9NfvDD(2hnH;8Lcwr#sMgf74WBmb;X`eUxn4K z*=!bz#lwdW-*wkr)oQh#Y5aKbZhA+~NtPv`k4Xv9sIJHuay;h+Y^(a(aM~5lpurWN3%^di5eL_=y>qj0p zhU`eMNWKv&V-dK8naBX`v_#CBSC8MuqP1n_l4R`Oz5D+A?=P3j3_s46&R;5BaUItW zIw`6_h~^eD`zPu12~frffkBFJ+k%Wd-wiqD1nHYyB9x$FA6GRFaw8- z5yb#w%j{Eqr}oiNcri}LjJzu_1D(o{8JLilqyM(dp7^fv$l7+PIF<6)WiD2G@48MQ zD)+a6RcivVhn-<}77dm|vmkVg64~7~Ok)_zMN|Scp2Rq!Nu6&yjW`*`4x8Xvu5{um zt+s24RXPH-R8crQi&W+{5C^1@#me*hEggA;aQZ&ql;et~seuvif*I2zq&p}uA zm#j^kBu-uByCY>lG9;?wC1AOSrjBc%F~#j-v?mT?Nb`$e0K+nxX-kpju!bXy!rK^h zuu|ikuP!xAqNdCC;JI!N`t_?0=r_)uUPw@FQDD=rx0;nnWF|+gW3m!Wu5U;RJiy51 zat99{Jap(#xm>O;SEmXyrv^{^weOAt;Cs7sTQjpe(H3s~YzP2@^hPrH{-MDkt5Q^jPgHyb8x6}x!(>B3&g#XQ zf3N3%18B#{h@q%pFP@jxC;!)ep~H@=`Bc)9stl%;g;r4@J({H-ItEn=gwF8`q!PeF z5npqv{C598oOKDTIMts~skF=6tb<}^P|bt_+0(#Jh$Om{B1h>u&aLQO#6nNmZ1&b$Z)GH-QmNFIYYVx>Q**r9JG+BG_eOw5z<#e)Sg}h8wB+Npr)1+spSMY)Zh_CjBvBRy+rQo}LB!Un0oT<#5Ug|u!cvu`21 zv0x?+!Pq_aID+97PT4RSlJb6Mq`9;Q+7fI@r5QC@MKDRQmO^^FMS%TaR@4P8^!Af? z{_}e`y(tDT;-gZspdLsDfHl@uc1Sg`mV$l-wNIyPv|lqBLdLQA6^l;66J-O;=$CY} zWB@Jj^7rvQF~i9=T|jN*2X6GTCPwCwB1iptFLY$y46l+W2hM8THDOG%3}UQ%a$`gp zR-Jd#>T8(5HPxdKa63@^U--S&?$O}xt#+t`XzfbS!A`Y~q+FXxbuDVV*xDVC?B#|& zFhjhhTiuNtH!`%?y?ZyK8vNUHnfhy`Gt>DwUPlE3jx?7+hT$ml1`s}I%1UOe8=gTT z_t+ZZsG39w5HBkVOo@%j@=d>UKu6AGvB{||LwQm&R~Oh-`=P? z)zihZbGiAB;}X0UNC-|y2vK_!bl8KY5W~2>JfnM?*paEgHiimW8JylX0K@S&e({!{ z|EcY7>j^L*HY#Ophg76gDgjDus_Nlzv94HH_p!qW{^p7^auzQ)_X(?pmws?1{A$eT z79#ISk+5nry&S4zJ5=Q{^n5+-6Qc6O4|grPuwe$#K(mm@p}+hjP8M5L_J2`gWQssK zwupOTtCGajS}94>iv~bT7;A(uELSkEjVm5w%;&gkMK}zs>k| z3|_cF5KQq9tl3Nmf)TEUG-m=B5zDHppTon$J9g~YwQJW+H{Db&mqmnXIy-Z|aB)7j z$ZzSWWkS5tZ20wS~(}Vc| z5-ZRx9@PV8)NOGBZ`4Tx%eZ?I1;qQySr_t*zqa*%BfT03B$zEj1LG()o zYPPsuLCfPOfZvPoqO|=*r#n8!(vyrs-Fl%0PPmdFbFwu@@sw`8JlN!5S-a4C`OYf( zoJXKtZRqe?)A4HJ0)g%?plmrE88X5~A;L})bkU(+n>ms@B(dE-@Hoo(4!?y|O5IMC884DfM)QiZACsDlV0eFe$w2ACcuM^{EZfa zYLLK&f(k&OsKFy7pCO9;`#azM5BL3iLbO;VVDJ;3c;c6gK!uD!X~>qngs2Cj3-$Nu z&$7rPj+)mm+#h8$2yPh5?KC^9oXAIY#Kiazjn#3fKUrN`bhIjlzfeCeoS@JoW5x)I8K0j&I~vbzvII zq$*~KvQ{cMn9}gx2_JDP@y(4wL&N&XjU$h z``x3T-c+7gJzxZXhScMG-+LB2E~qe~1BjlM-lrG!cb$|D+#hiKquvJ`JwmCTw(0^z zmPF*IBNYmPgsHH0#-`P9u1znLT2>LANhz|fG-zZfe(|N@#pcE^!srBKz2zz1SM2x^ zSKnCO09$+NzM$7d*XmA4vRljf|Gw3@TuMgzniPz%m2(6MTSM{Ac{i!)!kel zb|G-S{Ha?=;f!oK$-DfI(jWk&U|^XmxfE4kVZxj`nOYCauc5bJeTLI`Z0J))RO{#G zHPADb&B}Y9>opybO+|bRLB4?5I8{)kbs;4?q3epinhl1R71seAhs#)$}=!LM4*M*K7P}& zj~x6LAdPZYzzE&2wj(R@mB0B4R#}n;1PuZXh?;~+@XeHV>LN85Sf1e!)ib<4AYB9h zKXmtiQ=pwaOKp3Zp1_ti>H7Q_%mCwEM#eG(GmyblzqR$CW_gB@vQ&|V4qqTYo2C#0 zp@Y&{!G|^%hQX)E_E@G~KpF*%JzgFrh#6K}n7=hwQk02E{6dXswJ%c9uNhKTq7Q~y z1KL7pl4>iZWdQW_U9>)^Wb2!B_KgvXGy0UNjoI~7DGD=Ce-PFmDeu96fukB)5K1UL zUH@UZ{!lmSH>8W6r-{MFTYGKWDFi1p5;S?JbYT|(XUy#KZRnk{Wyg{a08UkurTU{w>C=F(0R|Zub zsiYNDirJWfng;o;G|;lR1bi(uPt#a$+IAXw$b6C!dIrn4+C0p8&`lY3COSmzN`!O^ zD;#K4UONxiMR8I8Qcn)Yn=CMs1QB-|e1ZXiSepRMsbBUx6!@M;U^smOaS3gac7rr) zPPOJNFJ+e7PRkFDiLxY4*d#*^ni6|_YeVD+7mOS#CXR{%FfjwFYXv2aHu)Ago8mXj`Kvo1B zV5Ea0_N$$Cgw4Lw+E)PRLZ z@Ce*AQiWB#%aP+pgRU__W=`P*m{bFyf&vLlO-zD-X$>*FV1H{jfHG!7n-T#rxv|Q+UXX)jd<#NF|lzvNG6!DC7VWGy>oeO)vW3@R4qN z12GfZ%43G%9rjZf3O{f@Lx!RJ9J)nZtPxY|gk&Ubx){P?Rw0^j7!B=x#g9>qO{EDi zA)zmT!LK^<2lwm1lp2m{!v+M9LrDr9sicxjlEVfhM-B+Oagk}q9$r>8%r=<>11v$b z05t3eBS#Q06yX597+*BWFst8ZG2q z*BxebZ9hZhB(x*?kk~jda_4W~^T|Ha3ths|KYGAl&cH&80CE7NG0U-E^;cqdv3+H=igN`%RX6OkdFaUr#&D)&Br=wC3KqGw9FuiV+bX_Gj@|S&t#GMm%^0vy>FC6=9 z#>q%c`qLZ?Gv4Ckf?CfLFSaDn!d`wUg^E;CS-q?nIjn2#JV=57*D57qsHl;TwTwFo zMj6{-iSCHW-gw)*QIKT7y1UI=DkOT0*j1)9dNu?i>a4o19?vQM7RNvp(E*8yBHuwW&bvz2B>q|lbBQA}Bi(k(q zBfj~%w<^TJLp)jt*^nRpl~npJiC-%kA9$jP zclJd>hx!Ea(&pmch+YIDR8fEt5|zTYcs-Jgpu$m_$N!8fM4tI`yWS(F`1MWaWB>6% z{ha}X9DtU$!Dx{(j8sxtE9vFkq6xDWYx%y0*l2Ui4~j%V7+M%RBSTV>rWjGFz)fFw zfDxx>x3Cq&K!tg7-pT*b@!uCy{Q9HvvH$d-ffitF8*5CZGr&_x*>zrhC*EV7Ns@o+GZGP}y9elEH${zdACqj}LXb8Ca zldd$CN-Eb?=n4;CpO-W|v5ID9SnCzqXc3G@g#(8E39m1}Sb-XGSU^^Gp}6+5NB`Zv z;XVDS)|>y!3Hy7SRzAbR4@h}NDydvYN%XoUeJA?^CnR!La1n1SnE^qxfL;JY7iDO& z9xXA~Dv;=-*8Uy@Fd={euLqLRqx7~A?~3a9Z|?fok%@cypz53d>qGqC1W_rlaUunc zR8qNd($DuVVOim}Dv-d4i%{8U0p>VbSRG)%`ofHUF3G;?lD1d2;VFJASLFDYAJQbl z`mRgY?01S9sid-gWR2cOlKf~O6OQ9c5RDdq;K9O=*DYYIO4g<#AG-aYKDPbQ)v9*< z%MaT)5s|%!4o0QWkxDA-UV3{?doWr_zf?+${ars(`jZS8H%Qfy{DvtrP z{bByEH5CIO@U@JDAN@)tl~jImg>*Y$&Eo?bDgZ*z!rmF29tbvI!0Q?_Zk#;6`)B^q zzF$~P3FQ7iJ(=W7d^C+prIJc25N?`R_@&Yq2*G{?tNg)xxZNd?P0inSt2&EU00!H8DD8>pstQ-ut=V`+ClEodk0;6E03MP6h@BF4zr2Bz>Gbeb|}k z@3+O%2nGgTc9@}_6=v*rwr{c(RkS-eDi@mlo1vL z@k&u{>Xnb2O|48xOv;1k{3q&OWk&-lO=OPpcXbYe7Ut&|zbgnS+2XB*iyDf|1(ZElapv#V*!Sh*v1E-b->ZelcRLq^U7bcExts(=$h7n&QmC0r4AxGM>r4*eJTq3Zv>;YfZq4Ch0y zPMD^N4)L(Nz|Mn3$(UN#f<-o#s=|1}BM}x;iwU?sfXP^45hPRRx7_*k=n{?PP@(%2 zBq|QUiAs{bhH>0M<}CzSjqU7z&f#JLls55(8|=y{F=(Y7{2-c%YkwWji;F@334`TR zC#D`;56WZtj^eWkOih7&CE;3McftAi)1j*d047GuB0}=Fvj+U*c7U6iC$D0}^08pA zQtCu}l^lDrF4?Ux2lzvkokM~}-yDTEPkRD6htu6*SpX$GYVpM_M)SvOw_#!NdQ!Yh zdw4^@SObSEVj3ujx{N%kNBAu3mT^Sne>&w(%^RR+e$N5eKAIPMkmPaVZ*eT#@}iD! zw+i?A)ybuUspVoI=QZ;sPy%=4QKZ`|)4v8_3TAoJ?rhWyc4%ztX#0~vF_0_bDH^Qk zHcneo$-e42bYt?(=oC<|pO@4IH(qKDk;Lf_qSYOvO}4%Oc`NyOE)HZ(jMv`j`0OB@ z7>ZA+sCEvY*j8h5LQ$-oh9Up~*s!RLbj!c|+BRxxSh=bbkD6QT*T%@Qsx)zwU!j5L zWaw_qUgt7AD%^DYXMVGQ-$2;3pb%~(X?D8uH}m9SF0ZM>z`L0*Q`46h1RW1F8N6F=x9 zm|yJRg9KANPPVTit{&eY4cJ2G``VTFTi_ z=%$nYfeBK_#|lp9&YPL=Ngb;yb~L`SQ*@O3Vq{Dynpl4nqO$2<_eS^p`3?NDsQUy6 z+00<0Bx)*n$GP!x;8I?Vl9ijOA~j1KN*IZNqG1~BDTG30)-XlUAh%{UKMY$NzY-WG zkP=H<%rXdty$suXpH&qU^$X9oCw!>Lp7WM&euMAtawz;_Ey{j}15hLF`mHN-N{9e$ zeg)Y_D13#lw~C@2aB};2{G)6tyq~<4p?iL28(-TgBdt0&Q?Wc-CKjt zIdC$P?nbtwz)jrH2wYV2{2qS0OG=(=OBmu9>3Zx=xD^#d(>|@ZSdnD|YxJ1rjcw!8Rj3vZ|oq>Q0^&B*(I z-aCq{+3T?>$t9xj@p>K03hs*s8jJ25JBFuO*W?h=-jWOKrI$d9lPk*27qsV?!OSv^ zsLK?#zjwLbd7E241a(7iaE)+*OvQ#&92_$`>W! zu*O7_YUMBX3A1Y%Vam?~oc}p$-Pfw`y$?MfdKc#z#89`&rPmCf2$Vfs{gbHfNxE(fJ#Kc3H8MAH|2XJ zlHTeUN2CZGO$rnw-|ihN!;4|CMwiqVyng##J3BTJPmY5(kE1SHo3yb8JizSeQ#b{t0A9 zC9yAD`=MjqqSNO#q}4ASc?->6gK})o4Is8%%(1MTdLihp3F#p@dAG#-7b&(`_DLT! zF-umh$+Z&m-A*=umO1S%i5`bjAaSdci7L^u+)Gk%gw*OQkTtp=dUN+}E$^SMm>pP) z==D~|;|PUi)a`plgqkIn$L9(!5ME$l^jc*dC{5aeXR`^=oI}pg(S5p4r7rbbZ2919SLu(I?{Wzed@7lR41Y;!Qejx2!j?TB1BNFWgk z?=#0<;Im--;t#||{h*&*$4hBss+Bm-4d_&|@!z0wLZNp$H0?s>wYA?Z_kmc|A1j~) z>%csx`CgfcIMmeNVoW$}Y?=6;r}@G(DB;J~jN9*dvPBDSoF47sH zHH!J@_CECBTU5=g1hZA%QHbQvbIo2s7(38nfs7`rqKc#->C+2j>EaWUf7wvhJV0;m z+J7u++y}H5G>u&KUq5Tn-mIyGN9yd41SO!>r!jW?XN~l&n-0i zBv4Cw4o)>24&MX&jg2=igkoNERmgcm1iPg&qTJ*qXKE&9ip+w#q!PDJ_fsQNSB7g* zYVDp49q{83Kh4Y)2NH?89huQ|klnIQVaK}g9%%Gw2|y!fiWCC^7nC95$M#?CRx1R9 zx>UMj2&}LV8vvlUoc8U*i|#pdW~8wwZg79QLw}1pRkdW+ZdE`@kpmUWAc;G)LHO854@ z_0;9TajR}A`LL_Et9_nFEO??IALEADaw%Vl?S1B?zzO(R&*u_$Fx9A*4Hb8 z&Cp1Y8DunBaBfuHbw1*K_{| z12Az@^+>O>#Ui&sixx67Di)~4pN_v-WEd{$yyE(0OQf)6R+5u0hB2EqZN<-WjAR3L zzXM}O8MFI-l-X4N-~8;+h<4PwxyP!(&R=Wq7^vT$s(=1i+kD2LwXdYj0ikX2T9MUa z4k>hKd#1Hdq1~0&(h|N=f>gvcP&iS>0$Ra{=*dt98~e7th4wCD&#I7?YP^!TWv}yg z!=MPtSd2@xWN`E8W@W8XfSHfpwEMT7@?oFbVmNXiw6}qh7Y1XH#WqvJ<8=s z8LhMec`?5R%fqGl_cj)I%AtcSBnTFa7&=NIB96PrVO-pa|h4*<$LSg&95LU<#OcY^9>2TH1lpL zjOeYE=$1}b^ScfRjHrXY)9@q0{jR9YIwHJdgw)aZqJz5P+aI;7GsJUQ0>ja6tND-+ zV+dH{uq@*MKag?^MqoH(cN1{d!xZxpI+FIbQwqc3xL)N!Sp0F0%z~mjsQI%SJy;I~ zxMkH2H)l^%Wk)@f7D>`u=M#vlCuWB(PsK2T^otNHCn5Se8z56apGH<#P( zF^I6p%CY80J>2|SPj}$M#6bbvG^j+NLM}xQsPu;$!x3>OH{x+Uamj_3%yL3M8Qe?l z>8v8smIoc4%LZXMO509`%dF?YxLXVBd{!Jl?`o0l9yYxblNnHnq&Cg1+#}|clJ2yG zMv|Uh9dVtH#VD0Ooj$}L51cF8s$lr`h#+N)DH|O^`o?jC^`=MG#NSCeA#Vd75_Hcv z_Nxft1`hv3VmJia__4#2+THXmN;{Cjm=cefo2?J=r)jT`kywaD~TH2u7w+7nbyecc9 f+_wPbM>_2+KkZRqt6BQh00Yd(%& Date: Tue, 19 Dec 2017 14:14:54 +0100 Subject: [PATCH 173/291] Update README.md --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 12c8e0623c..46c57a6d53 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,22 @@ Remember: *It's all about the looks.* As an additional feature, this library allows cross-platform development between Android and iOS as an iOS version of this library is also available: [**Charts**](https://github.com/danielgindi/Charts) :zap: -Are you using this library? Let me know about it and I will add your project to the [**references**](https://github.com/PhilJay/MPAndroidChart/wiki/References). +Enterprise Solution Discount +----- + + + + +MPAndroidChart is free software, as a result **dynamic & realtime data is not officially supported**. If you are looking for an enterprise-grade chart solution with extreme realtime performance and tech support, we recommend [SciChart](http://store.scichart.com?productTab=Android&CouponCode=MPANDROIDCHART). + + + +All MPAndroidChart users are entitled to a special **discount of 5%** off the [SciChart store](http://store.scichart.com?productTab=Android&CouponCode=MPANDROIDCHART), using the following discount code: **MPANDROIDCHART** + + + +
    + Donations ----- From 21ad19661b33a05cd605e8ef4e81757516584a72 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 19 Dec 2017 14:19:30 +0100 Subject: [PATCH 174/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 46c57a6d53..d15dcc7ae9 100644 --- a/README.md +++ b/README.md @@ -229,7 +229,7 @@ Chart types License ======= -Copyright 2016 Philipp Jahoda +Copyright 2018 Philipp Jahoda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 4b7eb1fd04a31d8d8400b6c9c0f990326c45bf79 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 19 Dec 2017 14:38:34 +0100 Subject: [PATCH 175/291] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d15dcc7ae9..5ab1b95640 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,12 @@ Enterprise Solution Discount -MPAndroidChart is free software, as a result **dynamic & realtime data is not officially supported**. If you are looking for an enterprise-grade chart solution with extreme realtime performance and tech support, we recommend [SciChart](http://store.scichart.com?productTab=Android&CouponCode=MPANDROIDCHART). +MPAndroidChart is free software, as a result **dynamic & realtime data is not officially supported**. If you are looking for an enterprise-grade chart solution with extreme realtime performance and tech support, we recommend +
    SciChart Android. -All MPAndroidChart users are entitled to a special **discount of 5%** off the [SciChart store](http://store.scichart.com?productTab=Android&CouponCode=MPANDROIDCHART), using the following discount code: **MPANDROIDCHART** +All MPAndroidChart users are entitled to a special **discount of 5%** off the SciChart store, using the following discount code: **MPANDROIDCHART** From 44125a87dd149298c8877b36ddc6068218d964af Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 20 Dec 2017 10:59:50 +0100 Subject: [PATCH 176/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ab1b95640..8a9e264b9b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Remember: *It's all about the looks.* As an additional feature, this library allows cross-platform development between Android and iOS as an iOS version of this library is also available: [**Charts**](https://github.com/danielgindi/Charts) :zap: -Enterprise Solution Discount +Enterprise Solution Discount | SciChart ----- From 89544095750cf35c24e3a1c000201e3acbf60419 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 20 Dec 2017 14:03:05 +0100 Subject: [PATCH 177/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a9e264b9b..2e7c452679 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Remember: *It's all about the looks.* As an additional feature, this library allows cross-platform development between Android and iOS as an iOS version of this library is also available: [**Charts**](https://github.com/danielgindi/Charts) :zap: -Enterprise Solution Discount | SciChart +[Enterprise Solution Discount | SciChart](http://store.scichart.com?productTab=Android&CouponCode=MPANDROIDCHART) ----- From 779e0f566580ff66ebbd1e935a95fbe22eaef12e Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 25 Dec 2017 19:58:23 +0100 Subject: [PATCH 178/291] Update README.md --- README.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/README.md b/README.md index 2e7c452679..6c4266d660 100644 --- a/README.md +++ b/README.md @@ -41,18 +41,7 @@ Donations - [**Donate 15 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YKMPTFMVK3JMC): I really appreciate your work, let's grab some lunch! - [**Donate 25 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H9JA4QX7UHXCY): That's some awesome stuff you did right there, dinner is on me! - Or you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome! - -## Got a question? -[Contact me via 21.co](https://21.co/philjay/) - - -## Xamarin - -Xamarin port (by [Flash3001](https://github.com/Flash3001)): *Android* - [GitHub](https://github.com/Flash3001/MPAndroidChart.Xamarin)/[NuGet](https://www.nuget.org/packages/MPAndroidChart/). *iOS* - [GitHub](https://github.com/Flash3001/iOSCharts.Xamarin)/[NuGet](https://www.nuget.org/packages/iOSCharts/). - -## Realm.io -[MPAndroidChart-Realm](https://github.com/PhilJay/MPAndroidChart-Realm) allows to directly plot / draw data from [Realm.io](https://realm.io) mobile database. Spread the word ----- From 2c08b560d66d51b99ab3d0ee6d666c3e56174956 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 25 Dec 2017 20:05:18 +0100 Subject: [PATCH 179/291] Update README.md --- README.md | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 6c4266d660..159fb7f3b3 100644 --- a/README.md +++ b/README.md @@ -83,22 +83,7 @@ Features ----- **Core features:** - - 8 different chart types - - Scaling on both axes (with touch-gesture, axes separately or pinch-zoom) - - Dragging / Panning (with touch-gesture) - - Combined-Charts (line-, bar-, scatter-, candle-data) - - Dual (separate) Axes - - Customizable Axes (both x- and y-axis) - - Highlighting values (with customizable popup-views) - - Save chart to SD-Card (as image, or as .txt file) - - Predefined color templates - - Legends (generated automatically, customizable) - - Animations (build up animations, on both xPx- and yPx-axis) - - Limit lines (providing additional information, maximums, ...) - - Fully customizable (paints, typefaces, legends, colors, background, gestures, dashed lines, ...) - - Smooth zooming and scrolling for up to 10.000 data points in Line- and BarChart - - Gradle support - - Plotting data directly from [**Realm.io**](https://realm.io) mobile database: [**MPAndroidChart-Realm**](https://github.com/PhilJay/MPAndroidChart-Realm) :zap: +You can have a look at the core features of this libary [**here**](https://github.com/PhilJay/MPAndroidChart/wiki/Core-Features). Usage ----- @@ -143,19 +128,9 @@ dependencies { ``` -**3. jar file only** - - Download the [**latest .jar file**](https://github.com/PhilJay/MPAndroidChart/releases) from the releases section - - Copy the **mpandroidchartlibrary-version.jar** file into the `libs` folder of your Android application project - - Start using the library +**3. jar file only** (not recommended) -**4. clone whole repository** - - Open your **commandline-input** and navigate to the desired destination folder on your machine (where you want to place the library) - - Use the command `git clone https://github.com/PhilJay/MPAndroidChart.git` to download the full MPAndroidChart repository to your computer (this includes the folder of the library as well as the folder of the example project) - - Import the library folder (`MPChartLib`) into Android Studio (recommended) or your Eclipse workspace - - Add it as a reference to your project: - - [referencing library projects in Eclipse](http://developer.android.com/tools/projects/projects-eclipse.html#ReferencingLibraryProject) - - [managing projects from Android Studio](https://developer.android.com/sdk/installing/create-project.html) - +**4. clone whole repository** (not recommended) Documentation ----- From c867d7cb3749f43a25d9961c74319718f14b60a6 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 25 Dec 2017 20:06:18 +0100 Subject: [PATCH 180/291] Update README.md --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 159fb7f3b3..f5e4f11c6f 100644 --- a/README.md +++ b/README.md @@ -81,8 +81,6 @@ Please do not expect answers to your questions if you have not considered all ab Features ----- - -**Core features:** You can have a look at the core features of this libary [**here**](https://github.com/PhilJay/MPAndroidChart/wiki/Core-Features). Usage @@ -127,10 +125,8 @@ dependencies { v3.0.3 ``` - -**3. jar file only** (not recommended) -**4. clone whole repository** (not recommended) +**3. clone whole repository** (not recommended) Documentation ----- From c50f03cf3970bcb76d677014d32f13c9628b2dc7 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 25 Dec 2017 20:09:46 +0100 Subject: [PATCH 181/291] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index f5e4f11c6f..8bbc199314 100644 --- a/README.md +++ b/README.md @@ -133,9 +133,7 @@ Documentation For a **detailed documentation** :notebook_with_decorative_cover:, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.3/javadoc/). Furthermore, you can also rely on the [**MPChartExample**](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample) folder and check out the example code in that project. The corresponding application to the example project is also [**available in the Google PlayStore**](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample). - -You have a problem that cannot be solved by having a look at the example project and documentation? -No problem, let's talk: [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/PhilJay/MPAndroidChart?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) +You can also join others in a discussion on [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/PhilJay/MPAndroidChart?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) Chart types ----- From 6ccaf940d8cde1bf100ba7531d794d1fded8bceb Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 27 Dec 2017 16:24:45 +0100 Subject: [PATCH 182/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8bbc199314..3a45288016 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Remember: *It's all about the looks.* As an additional feature, this library allows cross-platform development between Android and iOS as an iOS version of this library is also available: [**Charts**](https://github.com/danielgindi/Charts) :zap: -[Enterprise Solution Discount | SciChart](http://store.scichart.com?productTab=Android&CouponCode=MPANDROIDCHART) +[Enterprise Solution 5% Discount Coupon | SciChart](http://store.scichart.com?productTab=Android&CouponCode=MPANDROIDCHART) ----- From 8fd1a86b0a33e886026e9fe79233f78d40028578 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 27 Dec 2017 16:25:56 +0100 Subject: [PATCH 183/291] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 3a45288016..006fc677d1 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,6 @@ If you like this library, please tell others about it :two_hearts: :two_hearts: - []()Follow me on **Twitter**: [**@PhilippJahoda**](https://twitter.com/PhilippJahoda) - - Contact me on **LinkedIn**: [**PhilippJahoda**](https://www.linkedin.com/in/philippjahoda/en) - Look me up on **StackOverflow**: [**Philipp Jahoda**](http://stackoverflow.com/users/1590502/philipp-jahoda) From b3e23583343c81a77463c37a51a0c6c24b78c105 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 12 Jan 2018 14:06:19 +0100 Subject: [PATCH 184/291] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 006fc677d1..4774844aa2 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,10 @@ Donations **This project needs you!** If you would like to support this project's further development, the creator of this project or the continuous maintenance of this project, **feel free to donate**. Your donation is highly appreciated (and I love food, coffee and beer). Thank you! +**Bitcoin Wallet** + +1G8G6tqQ3oh38BvDH3xq8o6gGVMvBTkcUg + **PayPal** - [**Donate 5 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7G52RA87ED8NY): Thank's for creating this project, here's a coffee (or some beer) for you! From 297ce9f2cc1f48da67937c0a87d6b69bc39b9395 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 12 Jan 2018 14:07:02 +0100 Subject: [PATCH 185/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4774844aa2..11be4d1109 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Donations **This project needs you!** If you would like to support this project's further development, the creator of this project or the continuous maintenance of this project, **feel free to donate**. Your donation is highly appreciated (and I love food, coffee and beer). Thank you! -**Bitcoin Wallet** +**My Bitcoin Wallet** (Bitcoin only) 1G8G6tqQ3oh38BvDH3xq8o6gGVMvBTkcUg From a5250888b96af4d900bba98ff6f8f385d53c6fa3 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 12 Jan 2018 15:27:40 +0100 Subject: [PATCH 186/291] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 11be4d1109..063680ed0d 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,10 @@ Donations 1G8G6tqQ3oh38BvDH3xq8o6gGVMvBTkcUg +**My Ethereum Wallet** (Ethereum only) + +0x04ef098bf9f91871391363e3caf791afa3adc39b + **PayPal** - [**Donate 5 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7G52RA87ED8NY): Thank's for creating this project, here's a coffee (or some beer) for you! From 82668ebc34b7636e74ee2734c8055a6b8510dbcb Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 24 Feb 2018 12:50:04 +0100 Subject: [PATCH 187/291] Add files via upload --- googlee1205ea43aa2c32a.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 googlee1205ea43aa2c32a.html diff --git a/googlee1205ea43aa2c32a.html b/googlee1205ea43aa2c32a.html new file mode 100644 index 0000000000..ae23863ce1 --- /dev/null +++ b/googlee1205ea43aa2c32a.html @@ -0,0 +1 @@ +google-site-verification: googlee1205ea43aa2c32a.html \ No newline at end of file From 3e1eb1445a753d77b4dd02db205b2e5bbc41ce4b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 24 Feb 2018 12:51:49 +0100 Subject: [PATCH 188/291] Delete googlee1205ea43aa2c32a.html --- googlee1205ea43aa2c32a.html | 1 - 1 file changed, 1 deletion(-) delete mode 100644 googlee1205ea43aa2c32a.html diff --git a/googlee1205ea43aa2c32a.html b/googlee1205ea43aa2c32a.html deleted file mode 100644 index ae23863ce1..0000000000 --- a/googlee1205ea43aa2c32a.html +++ /dev/null @@ -1 +0,0 @@ -google-site-verification: googlee1205ea43aa2c32a.html \ No newline at end of file From aee6058dbb74f4eb1aae8e88c7562d28a91e1e1c Mon Sep 17 00:00:00 2001 From: zhanglong Date: Tue, 27 Feb 2018 15:04:47 +0800 Subject: [PATCH 189/291] Avoid that the last label entry in the x-labels clip off the edge of the screen #3819 --- .../com/github/mikephil/charting/renderer/XAxisRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java index 2c305796df..8adb56c73a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -207,7 +207,7 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { if (mXAxis.isAvoidFirstLastClippingEnabled()) { // avoid clipping of the last - if (i == mXAxis.mEntryCount - 1 && mXAxis.mEntryCount > 1) { + if (i / 2 == mXAxis.mEntryCount - 1 && mXAxis.mEntryCount > 1) { float width = Utils.calcTextWidth(mAxisLabelPaint, label); if (width > mViewPortHandler.offsetRight() * 2 From 1e6e58d2c026be332d6756b04d85b33cd3d36c2b Mon Sep 17 00:00:00 2001 From: sembozdemir Date: Wed, 28 Mar 2018 20:23:51 +0300 Subject: [PATCH 190/291] Add option for using slice color as value line color Fixes: #3897 --- .../mpchartexample/PiePolylineChartActivity.java | 3 ++- .../mikephil/charting/data/PieDataSet.java | 16 ++++++++++++++-- .../interfaces/datasets/IPieDataSet.java | 6 +++++- .../charting/renderer/PieChartRenderer.java | 5 +++++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index 49fc4959e9..e0d3063ede 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -19,7 +19,6 @@ 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.components.Legend.LegendPosition; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; @@ -226,6 +225,8 @@ private void setData(int count, float range) { dataSet.setValueLinePart1OffsetPercentage(80.f); dataSet.setValueLinePart1Length(0.2f); dataSet.setValueLinePart2Length(0.4f); + //dataSet.setUsingSliceColorAsValueLineColor(true); + //dataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); dataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java index 010cfbddc5..98b434d3d7 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java @@ -18,6 +18,7 @@ public class PieDataSet extends DataSet implements IPieDataSet { private ValuePosition mXValuePosition = ValuePosition.INSIDE_SLICE; private ValuePosition mYValuePosition = ValuePosition.INSIDE_SLICE; + private boolean mUsingSliceColorAsValueLineColor = false; private int mValueLineColor = 0xff000000; private float mValueLineWidth = 1.0f; private float mValueLinePart1OffsetPercentage = 75.f; @@ -134,6 +135,18 @@ public void setYValuePosition(ValuePosition yValuePosition) this.mYValuePosition = yValuePosition; } + /** + * When valuePosition is OutsideSlice, use slice colors as line color if true + * */ + @Override + public boolean isUsingSliceColorAsValueLineColor() { + return mUsingSliceColorAsValueLineColor; + } + + public void setUsingSliceColorAsValueLineColor(boolean usingSliceColorAsValueLineColor) { + this.mUsingSliceColorAsValueLineColor = usingSliceColorAsValueLineColor; + } + /** When valuePosition is OutsideSlice, indicates line color */ @Override public int getValueLineColor() @@ -141,8 +154,7 @@ public int getValueLineColor() return mValueLineColor; } - public void setValueLineColor(int valueLineColor) - { + public void setValueLineColor(int valueLineColor) { this.mValueLineColor = valueLineColor; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java index a53a9645af..1698ef786b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java @@ -1,6 +1,5 @@ package com.github.mikephil.charting.interfaces.datasets; -import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.data.PieEntry; @@ -36,6 +35,11 @@ public interface IPieDataSet extends IDataSet { PieDataSet.ValuePosition getXValuePosition(); PieDataSet.ValuePosition getYValuePosition(); + /** + * When valuePosition is OutsideSlice, use slice colors as line color if true + * */ + boolean isUsingSliceColorAsValueLineColor(); + /** * When valuePosition is OutsideSlice, indicates line color * */ 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 be72a0834f..495ae72f3b 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 @@ -536,6 +536,11 @@ public void drawValues(Canvas c) { } if (dataSet.getValueLineColor() != ColorTemplate.COLOR_NONE) { + + if (dataSet.isUsingSliceColorAsValueLineColor()) { + mValueLinePaint.setColor(dataSet.getColor(j)); + } + c.drawLine(pt0x, pt0y, pt1x, pt1y, mValueLinePaint); c.drawLine(pt1x, pt1y, pt2x, pt2y, mValueLinePaint); } From 508cc565a9818d65ee6d295e8a98a4cda14eb9f3 Mon Sep 17 00:00:00 2001 From: Wilder Pereira Date: Sun, 15 Apr 2018 21:51:15 -0300 Subject: [PATCH 191/291] Rename RadarChartActivitry to RadarChartActivity --- MPChartExample/AndroidManifest.xml | 2 +- .../{RadarChartActivitry.java => RadarChartActivity.java} | 2 +- .../mpchartexample/notimportant/MainActivity.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename MPChartExample/src/com/xxmassdeveloper/mpchartexample/{RadarChartActivitry.java => RadarChartActivity.java} (99%) diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index 87d8b72402..3fa15cd69c 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -44,7 +44,7 @@ - + diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java similarity index 99% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java rename to MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java index 937973fe4e..d060449d9c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java @@ -28,7 +28,7 @@ import java.util.ArrayList; -public class RadarChartActivitry extends DemoBase { +public class RadarChartActivity extends DemoBase { private RadarChart mChart; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index 7490c3c933..45ca879ee3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -39,7 +39,7 @@ import com.xxmassdeveloper.mpchartexample.PieChartActivity; import com.xxmassdeveloper.mpchartexample.PiePolylineChartActivity; import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.RadarChartActivitry; +import com.xxmassdeveloper.mpchartexample.RadarChartActivity; import com.xxmassdeveloper.mpchartexample.RealtimeLineChartActivity; import com.xxmassdeveloper.mpchartexample.ScatterChartActivity; import com.xxmassdeveloper.mpchartexample.ScrollViewActivity; @@ -242,7 +242,7 @@ public void onItemClick(AdapterView av, View v, int pos, long arg3) { startActivity(i); break; case 20: - i = new Intent(this, RadarChartActivitry.class); + i = new Intent(this, RadarChartActivity.class); startActivity(i); break; case 21: From 993a8554c02c82d455c38abbdf6fc12bb5c01b21 Mon Sep 17 00:00:00 2001 From: Wilder Pereira Date: Sun, 15 Apr 2018 21:51:35 -0300 Subject: [PATCH 192/291] Remove unused imports --- .../com/xxmassdeveloper/mpchartexample/RadarChartActivity.java | 1 - 1 file changed, 1 deletion(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java index d060449d9c..bcd9fac285 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java @@ -13,7 +13,6 @@ import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.MarkerView; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; From e1411f169e5c4798e85f2ba07bc5b0dc655b5828 Mon Sep 17 00:00:00 2001 From: almic Date: Fri, 27 Apr 2018 00:08:27 -0600 Subject: [PATCH 193/291] Remove Custom Check calculate() no longer checks if min and max is custom, it just adds the padding. --- .../mikephil/charting/components/YAxis.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java index e84caab76b..571e0f393e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java @@ -373,19 +373,11 @@ public void calculate(float dataMin, float dataMax) { min = min - 1f; } - // bottom-space only effects non-custom min - if (!mCustomAxisMin) { - - float bottomSpace = range / 100f * getSpaceBottom(); - this.mAxisMinimum = (min - bottomSpace); - } - - // top-space only effects non-custom max - if (!mCustomAxisMax) { - - float topSpace = range / 100f * getSpaceTop(); - this.mAxisMaximum = (max + topSpace); - } + float bottomSpace = range / 100f * getSpaceBottom(); + this.mAxisMinimum = (min - bottomSpace); + + float topSpace = range / 100f * getSpaceTop(); + this.mAxisMaximum = (max + topSpace); // calc actual range this.mAxisRange = Math.abs(this.mAxisMaximum - this.mAxisMinimum); From d8ea67aa216e924268643b078ed5b7a383a3f64b Mon Sep 17 00:00:00 2001 From: Hannes Achleitner Date: Wed, 25 Apr 2018 08:14:09 +0200 Subject: [PATCH 194/291] update to Android Studio 3.1.2 --- MPChartExample/build.gradle | 4 ++-- MPChartLib/build.gradle | 8 +++----- build.gradle | 11 ++--------- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 164f11425a..c7bacd5e4d 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'realm-android' android { compileSdkVersion 27 - buildToolsVersion '26.0.2' + buildToolsVersion '27.0.3' defaultConfig { minSdkVersion 16 targetSdkVersion 27 @@ -39,7 +39,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.1.2' //classpath 'io.realm:realm-gradle-plugin:0.88.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index 8e19df7541..8575e37e2f 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -5,7 +5,7 @@ apply plugin: 'maven' android { compileSdkVersion 27 - buildToolsVersion '26.0.2' + buildToolsVersion '27.0.3' // resourcePrefix 'mpcht' defaultConfig { minSdkVersion 9 @@ -34,11 +34,9 @@ repositories { } dependencies { - //compile fileTree(dir: 'libs', include: ['*.jar']) - //compile 'com.android.support:support-v4:19.+' //provided 'io.realm:realm-android:0.87.5' // "optional" dependency to realm-database API - testCompile 'junit:junit:4.12' - testCompile "org.mockito:mockito-core:1.10.19" + testImplementation 'junit:junit:4.12' + testImplementation "org.mockito:mockito-core:1.10.19" } android.libraryVariants.all { variant -> diff --git a/build.gradle b/build.gradle index 92f3d64ccd..65ca5186cf 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,3 @@ -//task wrapper(type: Wrapper) { -// gradleVersion = '2.9' -//} - buildscript { repositories { jcenter() @@ -9,7 +5,7 @@ buildscript { } dependencies { classpath "io.realm:realm-gradle-plugin:4.2.0" - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.1.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' } } @@ -17,9 +13,6 @@ buildscript { allprojects { repositories { jcenter() - maven { - url 'https://maven.google.com/' - name 'Google' - } + google() } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 845ff30cac..02b0428be0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Nov 20 11:59:54 CET 2017 +#Wed Apr 25 08:04:33 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip From 7286f8703f50b8c128a89c543a58f268d9902123 Mon Sep 17 00:00:00 2001 From: Mick A Date: Sun, 29 Apr 2018 09:48:01 -0600 Subject: [PATCH 195/291] Create ISSUE_TEMPLATE.md --- ISSUE_TEMPLATE.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 ISSUE_TEMPLATE.md diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..b87cd42091 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,13 @@ +- [ ] I have read the [CONTRIBUTING](CONTRIBUTING.md) file before posting this issue. + +### The problem + +What is the problem, what is going wrong? Is this a bug, question, or a feature request? + +### What should happen + +What do you think _should_ happen? + +### Addition information + +You can add addition info here, like code snippets or references. You can also attach files like images or stacktraces. Images that are no taller than 500px can be put inside the issue text, but please post larger images and stacktraces as links to a [Gist](https://help.github.com/articles/creating-gists/) or attach the file by clicking "Attach files" below, so that we don't have to scroll all the way down a page to respond to you. From 6fbd49276168b6270a85cc8ddb2d655b0afaa1f2 Mon Sep 17 00:00:00 2001 From: Mick A Date: Sun, 29 Apr 2018 09:58:33 -0600 Subject: [PATCH 196/291] Create PULL_REQUEST_TEMPLATE.md --- PULL_REQUEST_TEMPLATE.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 PULL_REQUEST_TEMPLATE.md diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..fb64ddf8be --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,14 @@ +- [ ] I have read the [CONTRIBUTING](CONTRIBUTING.md) file before posting submitting this pull request. + +Before describing this pull request, please prefix the title with a topic from the list: +`Feature`, `Fix`, `Style` + +### [Feature / Fix / Style] (choose ONE) + +What will this PR do? Remember that you don't need to explain everything, let the changes speak for itself. + +If a bug fix, reference any issues that this fixes, like so: "fixes #99" + +### Why should this be merged? + +If needed, further explain this PR and why it should be merged. If the changes are too broad, your pull request may be denied for having too much in it. It may also be denied if the commits aren't properly formatted. From 35c9fc937ecda0dd2eb1a7b879aa869a48bfad3d Mon Sep 17 00:00:00 2001 From: almic Date: Mon, 30 Apr 2018 12:24:18 -0600 Subject: [PATCH 197/291] refactor(EasingFunction): Simplified EasingFunction EasingFunction has been simplified greatly, and I've added a MUCH needed annotation to relevant methods. Easing.EasingOptions has been deprecated, as well as any methods using them. Converting is as simple as deleting the "EasingOptions" part. A new signature is available for animateXY()! You are now able to pass one EasingFunction to animateXY() if you want both axes to be animated the same way. Quietly included are some gradle build updates, incrementing the appcompat version to 27.1.1, and using the new `javacompiler` to avoid deprecation of `javacompile` --- MPChartExample/build.gradle | 2 +- .../mpchartexample/HalfPieChartActivity.java | 2 +- .../mpchartexample/LineChartActivity1.java | 2 +- .../mpchartexample/PieChartActivity.java | 5 +- .../PiePolylineChartActivity.java | 4 +- .../mpchartexample/RadarChartActivity.java | 8 +- .../realm/RealmDatabaseActivityBar.java | 2 +- .../realm/RealmDatabaseActivityBubble.java | 2 +- .../realm/RealmDatabaseActivityCandle.java | 2 +- .../RealmDatabaseActivityHorizontalBar.java | 2 +- .../realm/RealmDatabaseActivityLine.java | 2 +- .../realm/RealmDatabaseActivityScatter.java | 2 +- .../realm/RealmWikiExample.java | 4 +- MPChartLib/build.gradle | 8 +- .../charting/animation/ChartAnimator.java | 292 +++--- .../mikephil/charting/animation/Easing.java | 942 ++++++------------ .../charting/animation/EasingFunction.java | 15 - .../mikephil/charting/charts/Chart.java | 44 +- .../charting/charts/PieRadarChartBase.java | 5 +- 19 files changed, 524 insertions(+), 821 deletions(-) delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/animation/EasingFunction.java diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index c7bacd5e4d..8e6fe137b8 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -59,7 +59,7 @@ dependencies { implementation 'com.github.PhilJay:MPAndroidChart-Realm:v2.0.2@aar' implementation project(':MPChartLib') - implementation 'com.android.support:appcompat-v7:27.0.2' + implementation 'com.android.support:appcompat-v7:27.1.1' //compile 'io.realm:realm-android:0.87.5' // dependency for realm-database API (http://realm.io) //compile 'com.github.PhilJay:MPAndroidChart:v2.2.5' } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java index 15da39a8f3..a524f36a43 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java @@ -68,7 +68,7 @@ protected void onCreate(Bundle savedInstanceState) { setData(4, 100); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); + mChart.animateY(1400, Easing.EaseInOutQuad); Legend l = mChart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index b18309a26a..6cf7150c97 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -302,7 +302,7 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.animateY: { - mChart.animateY(3000, Easing.EasingOption.EaseInCubic); + mChart.animateY(3000, Easing.EaseInCubic); break; } case R.id.animateXY: { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 085580a923..eb60524cb0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -90,7 +90,7 @@ protected void onCreate(Bundle savedInstanceState) { setData(4, 100); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); + mChart.animateY(1400, Easing.EaseInOutQuad); // mChart.spin(2000, 0, 360); mSeekBarX.setOnSeekBarChangeListener(this); @@ -179,8 +179,7 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.actionToggleSpin: { - mChart.spin(1000, mChart.getRotationAngle(), mChart.getRotationAngle() + 360, Easing.EasingOption - .EaseInCubic); + mChart.spin(1000, mChart.getRotationAngle(), mChart.getRotationAngle() + 360, Easing.EaseInCubic); break; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index e0d3063ede..44fbf06c89 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -38,7 +38,7 @@ public class PiePolylineChartActivity extends DemoBase implements OnSeekBarChang private PieChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - + private Typeface tf; @Override @@ -97,7 +97,7 @@ protected void onCreate(Bundle savedInstanceState) { setData(4, 100); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); + mChart.animateY(1400, Easing.EaseInOutQuad); // mChart.spin(2000, 0, 360); Legend l = mChart.getLegend(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java index bcd9fac285..f1fd4cc891 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java @@ -62,10 +62,7 @@ protected void onCreate(Bundle savedInstanceState) { setData(); - mChart.animateXY( - 1400, 1400, - Easing.EasingOption.EaseInOutQuad, - Easing.EasingOption.EaseInOutQuad); + mChart.animateXY(1400, 1400, Easing.EaseInOutQuad); XAxis xAxis = mChart.getXAxis(); xAxis.setTypeface(mTfLight); @@ -193,8 +190,7 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.actionToggleSpin: { - mChart.spin(2000, mChart.getRotationAngle(), mChart.getRotationAngle() + 360, Easing.EasingOption - .EaseInCubic); + mChart.spin(2000, mChart.getRotationAngle(), mChart.getRotationAngle() + 360, Easing.EaseInCubic); break; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java index c87290050d..4f1d42f1db 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java @@ -64,6 +64,6 @@ private void setData() { // set data mChart.setData(data); mChart.setFitBars(true); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + mChart.animateY(1400, Easing.EaseInOutQuart); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java index d0aa25b864..fb28c3f08a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java @@ -66,6 +66,6 @@ private void setData() { // set data mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + mChart.animateY(1400, Easing.EaseInOutQuart); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java index a388df3741..17c0d8d2f0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java @@ -72,6 +72,6 @@ private void setData() { // set data mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + mChart.animateY(1400, Easing.EaseInOutQuart); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java index 5fcfa76bff..35138d656d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java @@ -69,6 +69,6 @@ private void setData() { // set data mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + mChart.animateY(1400, Easing.EaseInOutQuart); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java index 55f7f6dd4c..24982cf7fa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java @@ -73,6 +73,6 @@ private void setData() { // set data mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + mChart.animateY(1400, Easing.EaseInOutQuart); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java index 14175ac73a..d2e2fd70f5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java @@ -68,6 +68,6 @@ private void setData() { // set data mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + mChart.animateY(1400, Easing.EaseInOutQuart); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java index 6c1d7cde03..25011b0e2b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java @@ -112,7 +112,7 @@ public String getFormattedValue(float value, AxisBase axis) { // set data lineChart.setData(lineData); - lineChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + lineChart.animateY(1400, Easing.EaseInOutQuart); // BAR-CHART @@ -128,6 +128,6 @@ public String getFormattedValue(float value, AxisBase axis) { barChart.setData(barData); barChart.setFitBars(true); - barChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); + barChart.animateY(1400, Easing.EaseInOutQuart); } } diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index 8575e37e2f..19c06befb3 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -8,6 +8,7 @@ android { buildToolsVersion '27.0.3' // resourcePrefix 'mpcht' defaultConfig { + //noinspection MinSdkTooLow minSdkVersion 9 targetSdkVersion 27 versionCode 3 @@ -35,6 +36,7 @@ repositories { dependencies { //provided 'io.realm:realm-android:0.87.5' // "optional" dependency to realm-database API + implementation 'com.android.support:support-annotations:27.1.1' testImplementation 'junit:junit:4.12' testImplementation "org.mockito:mockito-core:1.10.19" } @@ -42,9 +44,9 @@ dependencies { android.libraryVariants.all { variant -> def name = variant.buildType.name def task = project.tasks.create "jar${name.capitalize()}", Jar - task.dependsOn variant.javaCompile - task.from variant.javaCompile.destinationDir - artifacts.add('archives', task); + task.dependsOn variant.javaCompiler + task.from variant.javaCompiler.destinationDir + artifacts.add('archives', task) } task sourcesJar(type: Jar) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java index 639442a4c9..026a1b30d3 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java @@ -3,224 +3,190 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.annotation.SuppressLint; +import android.support.annotation.RequiresApi; + +import com.github.mikephil.charting.animation.Easing.EasingFunction; /** - * Object responsible for all animations in the Chart. ANIMATIONS ONLY WORK FOR - * API LEVEL 11 (Android 3.0.x) AND HIGHER. - * + * Object responsible for all animations in the Chart. Animations require API level 11. + * * @author Philipp Jahoda + * @author Mick Ashton */ public class ChartAnimator { /** object that is updated upon animation update */ private AnimatorUpdateListener mListener; - public ChartAnimator() { + /** The phase of drawn values on the y-axis. 0 - 1 */ + @SuppressWarnings("WeakerAccess") + protected float mPhaseY = 1f; - } + /** The phase of drawn values on the x-axis. 0 - 1 */ + @SuppressWarnings("WeakerAccess") + protected float mPhaseX = 1f; + + public ChartAnimator() { } + @RequiresApi(11) public ChartAnimator(AnimatorUpdateListener listener) { mListener = listener; } - /** - * ################ ################ ################ ################ - */ - /** CODE BELOW THIS RELATED TO ANIMATION */ - - /** the phase that is animated and influences the drawn values on the y-axis */ - protected float mPhaseY = 1f; - - /** the phase that is animated and influences the drawn values on the x-axis */ - protected float mPhaseX = 1f; + @RequiresApi(11) + private ObjectAnimator xAnimator(int duration, EasingFunction easing) { - /** - * ################ ################ ################ ################ - */ - /** METHODS FOR CUSTOM EASING */ + ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); + animatorX.setInterpolator(easing); + animatorX.setDuration(duration); - /** - * Animates the drawing / rendering of the chart on both x- and y-axis with - * the specified animation time. If animate(...) is called, no further - * calling of invalidate() is necessary to refresh the chart. - * - * @param durationMillisX - * @param durationMillisY - * @param easingX - * @param easingY - */ - public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easingX, - EasingFunction easingY) { + return animatorX; + } - if (android.os.Build.VERSION.SDK_INT < 11) - return; + @RequiresApi(11) + private ObjectAnimator yAnimator(int duration, EasingFunction easing) { ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - animatorY.setInterpolator(easingY); - animatorY.setDuration( - durationMillisY); - ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - animatorX.setInterpolator(easingX); - animatorX.setDuration( - durationMillisX); + animatorY.setInterpolator(easing); + animatorY.setDuration(duration); - // make sure only one animator produces update-callbacks (which then - // call invalidate()) - if (durationMillisX > durationMillisY) { - animatorX.addUpdateListener(mListener); - } else { - animatorY.addUpdateListener(mListener); - } + return animatorY; + } - animatorX.start(); - animatorY.start(); + /** + * Animates values along the X axis, in a linear fashion. + * + * @param durationMillis animation duration + */ + @RequiresApi(11) + public void animateX(int durationMillis) { + animateX(durationMillis, Easing.Linear); } /** - * Animates the rendering of the chart on the x-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. + * Animates values along the X axis. * - * @param durationMillis - * @param easing + * @param durationMillis animation duration + * @param easing EasingFunction */ + @RequiresApi(11) public void animateX(int durationMillis, EasingFunction easing) { - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - animatorX.setInterpolator(easing); - animatorX.setDuration(durationMillis); + ObjectAnimator animatorX = xAnimator(durationMillis, easing); animatorX.addUpdateListener(mListener); animatorX.start(); } /** - * Animates the rendering of the chart on the y-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. + * Animates values along both the X and Y axes, in a linear fashion. * - * @param durationMillis - * @param easing + * @param durationMillisX animation duration along the X axis + * @param durationMillisY animation duration along the Y axis */ - public void animateY(int durationMillis, EasingFunction easing) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - animatorY.setInterpolator(easing); - animatorY.setDuration(durationMillis); - animatorY.addUpdateListener(mListener); - animatorY.start(); + @RequiresApi(11) + public void animateXY(int durationMillisX, int durationMillisY) { + animateXY(durationMillisX, durationMillisY, Easing.Linear, Easing.Linear); } /** - * ################ ################ ################ ################ - */ - /** METHODS FOR PREDEFINED EASING */ - - /** - * Animates the drawing / rendering of the chart on both x- and y-axis with - * the specified animation time. If animate(...) is called, no further - * calling of invalidate() is necessary to refresh the chart. + * Animates values along both the X and Y axes. * - * @param durationMillisX - * @param durationMillisY - * @param easingX - * @param easingY + * @param durationMillisX animation duration along the X axis + * @param durationMillisY animation duration along the Y axis + * @param easing EasingFunction for both axes */ - public void animateXY(int durationMillisX, int durationMillisY, Easing.EasingOption easingX, - Easing.EasingOption easingY) { + @RequiresApi(11) + public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easing) { - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - animatorY.setInterpolator(Easing.getEasingFunctionFromOption(easingY)); - animatorY.setDuration( - durationMillisY); - ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - animatorX.setInterpolator(Easing.getEasingFunctionFromOption(easingX)); - animatorX.setDuration( - durationMillisX); + ObjectAnimator xAnimator = xAnimator(durationMillisX, easing); + ObjectAnimator yAnimator = yAnimator(durationMillisY, easing); - // make sure only one animator produces update-callbacks (which then - // call invalidate()) if (durationMillisX > durationMillisY) { - animatorX.addUpdateListener(mListener); + xAnimator.addUpdateListener(mListener); } else { - animatorY.addUpdateListener(mListener); + yAnimator.addUpdateListener(mListener); } - animatorX.start(); - animatorY.start(); + xAnimator.start(); + yAnimator.start(); } /** - * Animates the rendering of the chart on the x-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. + * Animates values along both the X and Y axes. * - * @param durationMillis - * @param easing + * @param durationMillisX animation duration along the X axis + * @param durationMillisY animation duration along the Y axis + * @param easingX EasingFunction for the X axis + * @param easingY EasingFunction for the Y axis */ - public void animateX(int durationMillis, Easing.EasingOption easing) { + @RequiresApi(11) + public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easingX, + EasingFunction easingY) { - if (android.os.Build.VERSION.SDK_INT < 11) - return; + ObjectAnimator xAnimator = xAnimator(durationMillisX, easingX); + ObjectAnimator yAnimator = yAnimator(durationMillisY, easingY); - ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - animatorX.setInterpolator(Easing.getEasingFunctionFromOption(easing)); - animatorX.setDuration(durationMillis); - animatorX.addUpdateListener(mListener); - animatorX.start(); + if (durationMillisX > durationMillisY) { + xAnimator.addUpdateListener(mListener); + } else { + yAnimator.addUpdateListener(mListener); + } + + xAnimator.start(); + yAnimator.start(); } /** - * Animates the rendering of the chart on the y-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. + * Animates values along the Y axis, in a linear fashion. * - * @param durationMillis - * @param easing + * @param durationMillis animation duration */ - public void animateY(int durationMillis, Easing.EasingOption easing) { + @RequiresApi(11) + public void animateY(int durationMillis) { + animateY(durationMillis, Easing.Linear); + } - if (android.os.Build.VERSION.SDK_INT < 11) - return; + /** + * Animates values along the Y axis. + * + * @param durationMillis animation duration + * @param easing EasingFunction + */ + @RequiresApi(11) + public void animateY(int durationMillis, EasingFunction easing) { - ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - animatorY.setInterpolator(Easing.getEasingFunctionFromOption(easing)); - animatorY.setDuration(durationMillis); + ObjectAnimator animatorY = yAnimator(durationMillis, easing); animatorY.addUpdateListener(mListener); animatorY.start(); } - /** - * ################ ################ ################ ################ - */ - /** METHODS FOR ANIMATION WITHOUT EASING */ - /** * Animates the drawing / rendering of the chart on both x- and y-axis with * the specified animation time. If animate(...) is called, no further * calling of invalidate() is necessary to refresh the chart. * - * @param durationMillisX - * @param durationMillisY + * @param durationMillisX animation duration along the X axis + * @param durationMillisY animation duration along the Y axis + * @param easingX EasingFunction for the X axis + * @param easingY EasingFunction for the Y axis + * + * @deprecated Use {@link #animateXY(int, int, EasingFunction, EasingFunction)} + * @see #animateXY(int, int, EasingFunction, EasingFunction) */ - public void animateXY(int durationMillisX, int durationMillisY) { + @SuppressWarnings("deprecation") + @Deprecated + public void animateXY(int durationMillisX, int durationMillisY, Easing.EasingOption easingX, + Easing.EasingOption easingY) { if (android.os.Build.VERSION.SDK_INT < 11) return; ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); + animatorY.setInterpolator(Easing.getEasingFunctionFromOption(easingY)); animatorY.setDuration( durationMillisY); ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); + animatorX.setInterpolator(Easing.getEasingFunctionFromOption(easingX)); animatorX.setDuration( durationMillisX); @@ -241,14 +207,21 @@ public void animateXY(int durationMillisX, int durationMillisY) { * animation time. If animate(...) is called, no further calling of * invalidate() is necessary to refresh the chart. * - * @param durationMillis + * @param durationMillis animation duration + * @param easing EasingFunction + * + * @deprecated Use {@link #animateX(int, EasingFunction)} + * @see #animateX(int, EasingFunction) */ - public void animateX(int durationMillis) { + @SuppressWarnings("deprecation") + @Deprecated + public void animateX(int durationMillis, Easing.EasingOption easing) { if (android.os.Build.VERSION.SDK_INT < 11) return; ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); + animatorX.setInterpolator(Easing.getEasingFunctionFromOption(easing)); animatorX.setDuration(durationMillis); animatorX.addUpdateListener(mListener); animatorX.start(); @@ -259,52 +232,69 @@ public void animateX(int durationMillis) { * animation time. If animate(...) is called, no further calling of * invalidate() is necessary to refresh the chart. * - * @param durationMillis + * @param durationMillis animation duration + * @param easing EasingFunction + * + * @deprecated Use {@link #animateY(int, EasingFunction)} + * @see #animateY(int, EasingFunction) */ - public void animateY(int durationMillis) { + @SuppressWarnings("deprecation") + @Deprecated + public void animateY(int durationMillis, Easing.EasingOption easing) { if (android.os.Build.VERSION.SDK_INT < 11) return; ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); + animatorY.setInterpolator(Easing.getEasingFunctionFromOption(easing)); animatorY.setDuration(durationMillis); animatorY.addUpdateListener(mListener); animatorY.start(); } /** - * This gets the y-phase that is used to animate the values. + * Gets the Y axis phase of the animation. * - * @return + * @return float value of {@link #mPhaseY} */ public float getPhaseY() { return mPhaseY; } /** - * This modifys the y-phase that is used to animate the values. + * Sets the Y axis phase of the animation. * - * @param phase + * @param phase float value between 0 - 1 */ public void setPhaseY(float phase) { + if (phase > 1f) { + phase = 1f; + } else if (phase < 0f) { + phase = 0f; + } mPhaseY = phase; } /** - * This gets the x-phase that is used to animate the values. + * Gets the X axis phase of the animation. * - * @return + * @return float value of {@link #mPhaseX} */ public float getPhaseX() { return mPhaseX; } /** - * This modifys the x-phase that is used to animate the values. + * Sets the X axis phase of the animation. * - * @param phase + * @param phase float value between 0 - 1 */ public void setPhaseX(float phase) { + if (phase > 1f) { + phase = 1f; + } else if (phase < 0f) { + phase = 0f; + } mPhaseX = phase; } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java index 1741f6f511..631e313b10 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java @@ -1,17 +1,30 @@ package com.github.mikephil.charting.animation; +import android.animation.TimeInterpolator; +import android.support.annotation.RequiresApi; + /** * Easing options. - * + * * @author Daniel Cohen Gindi + * @author Mick Ashton */ +@SuppressWarnings("WeakerAccess") +@RequiresApi(11) public class Easing { + public interface EasingFunction extends TimeInterpolator { + @Override + float getInterpolation(float input); + } + /** - * Use EasingOption instead of EasingFunction to avoid crashes below Android - * 3.0 + * Enum holding EasingOption constants + * + * @deprecated Use Easing.Linear instead of Easing.EasingOption.Linear */ + @Deprecated public enum EasingOption { Linear, EaseInQuad, @@ -43,679 +56,362 @@ public enum EasingOption { EaseInOutBounce, } + /** + * Returns the EasingFunction of the given EasingOption + * + * @param easing EasingOption to get + * @return EasingFunction + */ + @SuppressWarnings("deprecation") + @Deprecated public static EasingFunction getEasingFunctionFromOption(EasingOption easing) { switch (easing) { default: case Linear: - return Easing.EasingFunctions.Linear; + return Easing.Linear; case EaseInQuad: - return Easing.EasingFunctions.EaseInQuad; + return Easing.EaseInQuad; case EaseOutQuad: - return Easing.EasingFunctions.EaseOutQuad; + return Easing.EaseOutQuad; case EaseInOutQuad: - return Easing.EasingFunctions.EaseInOutQuad; + return Easing.EaseInOutQuad; case EaseInCubic: - return Easing.EasingFunctions.EaseInCubic; + return Easing.EaseInCubic; case EaseOutCubic: - return Easing.EasingFunctions.EaseOutCubic; + return Easing.EaseOutCubic; case EaseInOutCubic: - return Easing.EasingFunctions.EaseInOutCubic; + return Easing.EaseInOutCubic; case EaseInQuart: - return Easing.EasingFunctions.EaseInQuart; + return Easing.EaseInQuart; case EaseOutQuart: - return Easing.EasingFunctions.EaseOutQuart; + return Easing.EaseOutQuart; case EaseInOutQuart: - return Easing.EasingFunctions.EaseInOutQuart; + return Easing.EaseInOutQuart; case EaseInSine: - return Easing.EasingFunctions.EaseInSine; + return Easing.EaseInSine; case EaseOutSine: - return Easing.EasingFunctions.EaseOutSine; + return Easing.EaseOutSine; case EaseInOutSine: - return Easing.EasingFunctions.EaseInOutSine; + return Easing.EaseInOutSine; case EaseInExpo: - return Easing.EasingFunctions.EaseInExpo; + return Easing.EaseInExpo; case EaseOutExpo: - return Easing.EasingFunctions.EaseOutExpo; + return Easing.EaseOutExpo; case EaseInOutExpo: - return Easing.EasingFunctions.EaseInOutExpo; + return Easing.EaseInOutExpo; case EaseInCirc: - return Easing.EasingFunctions.EaseInCirc; + return Easing.EaseInCirc; case EaseOutCirc: - return Easing.EasingFunctions.EaseOutCirc; + return Easing.EaseOutCirc; case EaseInOutCirc: - return Easing.EasingFunctions.EaseInOutCirc; + return Easing.EaseInOutCirc; case EaseInElastic: - return Easing.EasingFunctions.EaseInElastic; + return Easing.EaseInElastic; case EaseOutElastic: - return Easing.EasingFunctions.EaseOutElastic; + return Easing.EaseOutElastic; case EaseInOutElastic: - return Easing.EasingFunctions.EaseInOutElastic; + return Easing.EaseInOutElastic; case EaseInBack: - return Easing.EasingFunctions.EaseInBack; + return Easing.EaseInBack; case EaseOutBack: - return Easing.EasingFunctions.EaseOutBack; + return Easing.EaseOutBack; case EaseInOutBack: - return Easing.EasingFunctions.EaseInOutBack; + return Easing.EaseInOutBack; case EaseInBounce: - return Easing.EasingFunctions.EaseInBounce; + return Easing.EaseInBounce; case EaseOutBounce: - return Easing.EasingFunctions.EaseOutBounce; + return Easing.EaseOutBounce; case EaseInOutBounce: - return Easing.EasingFunctions.EaseInOutBounce; + return Easing.EaseInOutBounce; } } - - private static class EasingFunctions { - - /** - * ########## ########## ########## ########## ########## ########## - * PREDEFINED EASING FUNCTIONS BELOW THIS - */ - - public static final EasingFunction Linear = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // return elapsed / (float) duration; - // } - - @Override - public float getInterpolation(float input) { - return input; + + private static final float DOUBLE_PI = 2f * (float) Math.PI; + + @SuppressWarnings("unused") + public static final EasingFunction Linear = new EasingFunction() { + public float getInterpolation(float input) { + return input; + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInQuad = new EasingFunction() { + public float getInterpolation(float input) { + return input * input; + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseOutQuad = new EasingFunction() { + public float getInterpolation(float input) { + return -input * (input - 2f); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInOutQuad = new EasingFunction() { + public float getInterpolation(float input) { + input *= 2f; + + if (input < 1f) { + return 0.5f * input * input; } - }; - - public static final EasingFunction EaseInQuad = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return position * position; - // } - - @Override - public float getInterpolation(float input) { - return input * input; + + return -0.5f * ((--input) * (input - 2f) - 1f); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInCubic = new EasingFunction() { + public float getInterpolation(float input) { + return (float) Math.pow(input, 3); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseOutCubic = new EasingFunction() { + public float getInterpolation(float input) { + input--; + return (float) Math.pow(input, 3) + 1f; + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInOutCubic = new EasingFunction() { + public float getInterpolation(float input) { + input *= 2f; + if (input < 1f) { + return 0.5f * (float) Math.pow(input, 3); } - }; - - public static final EasingFunction EaseOutQuad = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return -position * (position - 2.f); - // } - - @Override - public float getInterpolation(float input) { - return -input * (input - 2f); + input -= 2f; + return 0.5f * ((float) Math.pow(input, 3) + 2f); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInQuart = new EasingFunction() { + + public float getInterpolation(float input) { + return (float) Math.pow(input, 4); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseOutQuart = new EasingFunction() { + public float getInterpolation(float input) { + input--; + return -((float) Math.pow(input, 4) - 1f); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInOutQuart = new EasingFunction() { + public float getInterpolation(float input) { + input *= 2f; + if (input < 1f) { + return 0.5f * (float) Math.pow(input, 4); } - }; + input -= 2f; + return -0.5f * ((float) Math.pow(input, 4) - 2f); + } + }; - public static final EasingFunction EaseInOutQuad = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (duration / 2.f); - // if (position < 1.f) - // { - // return 0.5f * position * position; - // } - // return -0.5f * ((--position) * (position - 2.f) - 1.f); - // } + @SuppressWarnings("unused") + public static final EasingFunction EaseInSine = new EasingFunction() { + public float getInterpolation(float input) { + return -(float) Math.cos(input * (Math.PI / 2f)) + 1f; + } + }; - @Override - public float getInterpolation(float input) { + @SuppressWarnings("unused") + public static final EasingFunction EaseOutSine = new EasingFunction() { + public float getInterpolation(float input) { + return (float) Math.sin(input * (Math.PI / 2f)); + } + }; - float position = input / 0.5f; + @SuppressWarnings("unused") + public static final EasingFunction EaseInOutSine = new EasingFunction() { + public float getInterpolation(float input) { + return -0.5f * ((float) Math.cos(Math.PI * input) - 1f); + } + }; - if (position < 1.f) { - return 0.5f * position * position; - } + @SuppressWarnings("unused") + public static final EasingFunction EaseInExpo = new EasingFunction() { + public float getInterpolation(float input) { + return (input == 0) ? 0f : (float) Math.pow(2f, 10f * (input - 1f)); + } + }; - return -0.5f * ((--position) * (position - 2.f) - 1.f); - } - }; - - public static final EasingFunction EaseInCubic = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return position * position * position; - // } - - @Override - public float getInterpolation(float input) { - return input * input * input; + @SuppressWarnings("unused") + public static final EasingFunction EaseOutExpo = new EasingFunction() { + public float getInterpolation(float input) { + return (input == 1f) ? 1f : (-(float) Math.pow(2f, -10f * (input + 1f))); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInOutExpo = new EasingFunction() { + public float getInterpolation(float input) { + if (input == 0) { + return 0f; + } else if (input == 1f) { + return 1f; } - }; - - public static final EasingFunction EaseOutCubic = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // position--; - // return (position * position * position + 1.f); - // } - - @Override - public float getInterpolation(float input) { - input--; - return (input * input * input + 1.f); - } - }; - - public static final EasingFunction EaseInOutCubic = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (duration / 2.f); - // if (position < 1.f) - // { - // return 0.5f * position * position * position; - // } - // position -= 2.f; - // return 0.5f * (position * position * position + 2.f); - // } - - @Override - public float getInterpolation(float input) { - - float position = input / 0.5f; - if (position < 1.f) { - return 0.5f * position * position * position; - } - position -= 2.f; - return 0.5f * (position * position * position + 2.f); - } - }; - - public static final EasingFunction EaseInQuart = new EasingFunction() { - - public float getInterpolation(float input) { - return input * input * input * input; + + input *= 2f; + if (input < 1f) { + return 0.5f * (float) Math.pow(2f, 10f * (input - 1f)); } - }; + return 0.5f * (-(float) Math.pow(2f, -10f * --input) + 2f); + } + }; - public static final EasingFunction EaseOutQuart = new EasingFunction() { + @SuppressWarnings("unused") + public static final EasingFunction EaseInCirc = new EasingFunction() { + public float getInterpolation(float input) { + return -((float) Math.sqrt(1f - input * input) - 1f); + } + }; - public float getInterpolation(float input) { - input--; - return -(input * input * input * input - 1f); - } - }; - - public static final EasingFunction EaseInOutQuart = new - EasingFunction() { - @Override - public float getInterpolation(float input) { - float position = input / 0.5f; - if (position < 1.f) { - return 0.5f * position * position * position * position; - } - position -= 2.f; - return -0.5f * (position * position * position * position - 2.f); - } - }; - - public static final EasingFunction EaseInSine = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return -(float) Math.cos(position * (Math.PI / 2.f)) + 1.f; - // } - @Override - public float getInterpolation(float input) { - return -(float) Math.cos(input * (Math.PI / 2.f)) + 1.f; + @SuppressWarnings("unused") + public static final EasingFunction EaseOutCirc = new EasingFunction() { + public float getInterpolation(float input) { + input--; + return (float) Math.sqrt(1f - input * input); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInOutCirc = new EasingFunction() { + public float getInterpolation(float input) { + input *= 2f; + if (input < 1f) { + return -0.5f * ((float) Math.sqrt(1f - input * input) - 1f); } - }; - - public static final EasingFunction EaseOutSine = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return (float) Math.sin(position * (Math.PI / 2.f)); - // } - @Override - public float getInterpolation(float input) { - return (float) Math.sin(input * (Math.PI / 2.f)); + return 0.5f * ((float) Math.sqrt(1f - (input -= 2f) * input) + 1f); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInElastic = new EasingFunction() { + public float getInterpolation(float input) { + if (input == 0) { + return 0f; + } else if (input == 1) { + return 1f; } - }; - - public static final EasingFunction EaseInOutSine = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return -0.5f * ((float) Math.cos(Math.PI * position) - 1.f); - // } - - @Override - public float getInterpolation(float input) { - return -0.5f * ((float) Math.cos(Math.PI * input) - 1.f); + + float p = 0.3f; + float s = p / DOUBLE_PI * (float) Math.asin(1f); + return -((float) Math.pow(2f, 10f * (input -= 1f)) + *(float) Math.sin((input - s) * DOUBLE_PI / p)); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseOutElastic = new EasingFunction() { + public float getInterpolation(float input) { + if (input == 0) { + return 0f; + } else if (input == 1) { + return 1f; } - }; - - public static final EasingFunction EaseInExpo = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // return (elapsed == 0) ? 0.f : (float) Math.pow(2.f, 10.f * (elapsed - // / (float) duration - 1.f)); - // } - @Override - public float getInterpolation(float input) { - return (input == 0) ? 0.f : (float) Math.pow(2.f, 10.f * (input - 1.f)); + + float p = 0.3f; + float s = p / DOUBLE_PI * (float) Math.asin(1f); + return 1f + + (float) Math.pow(2f, -10f * input) + * (float) Math.sin((input - s) * DOUBLE_PI / p); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInOutElastic = new EasingFunction() { + public float getInterpolation(float input) { + if (input == 0) { + return 0f; } - }; - - public static final EasingFunction EaseOutExpo = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // return (elapsed == duration) ? 1.f : (-(float) Math.pow(2.f, -10.f * - // elapsed - // / (float) duration) + 1.f); - // } - - @Override - public float getInterpolation(float input) { - return (input == 1f) ? 1.f : (-(float) Math.pow(2.f, -10.f * (input + 1.f))); + + input *= 2f; + if (input == 2) { + return 1f; } - }; - - public static final EasingFunction EaseInOutExpo = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // if (elapsed == 0) - // { - // return 0.f; - // } - // if (elapsed == duration) - // { - // return 1.f; - // } - // - // float position = elapsed / (duration / 2.f); - // if (position < 1.f) - // { - // return 0.5f * (float) Math.pow(2.f, 10.f * (position - 1.f)); - // } - // return 0.5f * (-(float) Math.pow(2.f, -10.f * --position) + - // 2.f); - // } - - @Override - public float getInterpolation(float input) { - if (input == 0) - { - return 0.f; - } - if (input == 1f) - { - return 1.f; - } - - float position = input / 0.5f; - if (position < 1.f) - { - return 0.5f * (float) Math.pow(2.f, 10.f * (position - 1.f)); - } - return 0.5f * (-(float) Math.pow(2.f, -10.f * --position) + 2.f); - } - }; - - public static final EasingFunction EaseInCirc = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return -((float) Math.sqrt(1.f - position * position) - 1.f); - // } - - @Override - public float getInterpolation(float input) { - return -((float) Math.sqrt(1.f - input * input) - 1.f); + + float p = 1f / 0.45f; + float s = 0.45f / DOUBLE_PI * (float) Math.asin(1f); + if (input < 1f) { + return -0.5f + * ((float) Math.pow(2f, 10f * (input -= 1f)) + * (float) Math.sin((input * 1f - s) * DOUBLE_PI * p)); } - }; - - public static final EasingFunction EaseOutCirc = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // position--; - // return (float) Math.sqrt(1.f - position * position); - // } - @Override - public float getInterpolation(float input) { - input--; - return (float) Math.sqrt(1.f - input * input); + return 1f + 0.5f + * (float) Math.pow(2f, -10f * (input -= 1f)) + * (float) Math.sin((input * 1f - s) * DOUBLE_PI * p); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInBack = new EasingFunction() { + public float getInterpolation(float input) { + final float s = 1.70158f; + return input * input * ((s + 1f) * input - s); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseOutBack = new EasingFunction() { + public float getInterpolation(float input) { + final float s = 1.70158f; + input--; + return (input * input * ((s + 1f) * input + s) + 1f); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInOutBack = new EasingFunction() { + public float getInterpolation(float input) { + float s = 1.70158f; + input *= 2f; + if (input < 1f) { + return 0.5f * (input * input * (((s *= (1.525f)) + 1f) * input - s)); } - }; - - public static final EasingFunction EaseInOutCirc = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (duration / 2.f); - // if (position < 1.f) - // { - // return -0.5f * ((float) Math.sqrt(1.f - position * position) - // - 1.f); - // } - // return 0.5f * ((float) Math.sqrt(1.f - (position -= 2.f) * - // position) - // + 1.f); - // } - - @Override - public float getInterpolation(float input) { - float position = input / 0.5f; - if (position < 1.f) - { - return -0.5f * ((float) Math.sqrt(1.f - position * position) - 1.f); - } - return 0.5f * ((float) Math.sqrt(1.f - (position -= 2.f) * position) - + 1.f); - } - }; - - public static final EasingFunction EaseInElastic = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // if (elapsed == 0) - // { - // return 0.f; - // } - // - // float position = elapsed / (float) duration; - // if (position == 1) - // { - // return 1.f; - // } - // - // float p = duration * .3f; - // float s = p / (2.f * (float) Math.PI) * (float) - // Math.asin(1.f); - // return -((float) Math.pow(2.f, 10.f * (position -= 1.f)) * - // (float) - // Math - // .sin((position * duration - s) * (2.f * Math.PI) / p)); - // } - - @Override - public float getInterpolation(float input) { - if (input == 0) - { - return 0.f; - } - - float position = input; - if (position == 1) - { - return 1.f; - } - - float p = .3f; - float s = p / (2.f * (float) Math.PI) * (float) Math.asin(1.f); - return -((float) Math.pow(2.f, 10.f * (position -= 1.f)) * (float) - Math - .sin((position - s) * (2.f * Math.PI) / p)); - } - }; - - public static final EasingFunction EaseOutElastic = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // if (elapsed == 0) - // { - // return 0.f; - // } - // - // float position = elapsed / (float) duration; - // if (position == 1) - // { - // return 1.f; - // } - // - // float p = duration * .3f; - // float s = p / (2 * (float) Math.PI) * (float) Math.asin(1.f); - // return (float) Math.pow(2, -10 * position) - // * (float) Math.sin((position * duration - s) * (2.f * - // Math.PI) / p) + - // 1.f; - // } - - @Override - public float getInterpolation(float input) { - if (input == 0) - { - return 0.f; - } - - float position = input; - if (position == 1) - { - return 1.f; - } - - float p = .3f; - float s = p / (2 * (float) Math.PI) * (float) Math.asin(1.f); - return (float) Math.pow(2, -10 * position) - * (float) Math.sin((position - s) * (2.f * Math.PI) / p) + - 1.f; - } - }; - - public static final EasingFunction EaseInOutElastic = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // if (elapsed == 0) - // { - // return 0.f; - // } - // - // float position = elapsed / (duration / 2.f); - // if (position == 2) - // { - // return 1.f; - // } - // - // float p = duration * (.3f * 1.5f); - // float s = p / (2.f * (float) Math.PI) * (float) - // Math.asin(1.f); - // if (position < 1.f) - // { - // return -.5f - // * ((float) Math.pow(2.f, 10.f * (position -= 1.f)) * (float) - // Math - // .sin((position * duration - s) * (2.f * Math.PI) / p)); - // } - // return (float) Math.pow(2.f, -10.f * (position -= 1.f)) - // * (float) Math.sin((position * duration - s) * (2.f * - // Math.PI) / p) * - // .5f - // + 1.f; - // } - - @Override - public float getInterpolation(float input) { - if (input == 0) - { - return 0.f; - } - - float position = input / 0.5f; - if (position == 2) - { - return 1.f; - } - - float p = (.3f * 1.5f); - float s = p / (2.f * (float) Math.PI) * (float) Math.asin(1.f); - if (position < 1.f) - { - return -.5f - * ((float) Math.pow(2.f, 10.f * (position -= 1.f)) * (float) Math - .sin((position * 1f - s) * (2.f * Math.PI) / p)); - } - return (float) Math.pow(2.f, -10.f * (position -= 1.f)) - * (float) Math.sin((position * 1f - s) * (2.f * Math.PI) / p) * - .5f - + 1.f; - } - }; - - public static final EasingFunction EaseInBack = new EasingFunction() - { - // @Override - // public float ease(long elapsed, long duration) { - // final float s = 1.70158f; - // float position = elapsed / (float) duration; - // return position * position * ((s + 1.f) * position - s); - // } - - @Override - public float getInterpolation(float input) { - final float s = 1.70158f; - float position = input; - return position * position * ((s + 1.f) * position - s); + return 0.5f * ((input -= 2f) * input * (((s *= (1.525f)) + 1f) * input + s) + 2f); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInBounce = new EasingFunction() { + public float getInterpolation(float input) { + return 1f - EaseOutBounce.getInterpolation(1f - input); + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseOutBounce = new EasingFunction() { + public float getInterpolation(float input) { + float s = 7.5625f; + if (input < (1f / 2.75f)) { + return s * input * input; + } else if (input < (2f / 2.75f)) { + return s * (input -= (1.5f / 2.75f)) * input + 0.75f; + } else if (input < (2.5f / 2.75f)) { + return s * (input -= (2.25f / 2.75f)) * input + 0.9375f; } - }; - - public static final EasingFunction EaseOutBack = new EasingFunction() - { - // @Override - // public float ease(long elapsed, long duration) { - // final float s = 1.70158f; - // float position = elapsed / (float) duration; - // position--; - // return (position * position * ((s + 1.f) * position + s) + 1.f); - // } - - @Override - public float getInterpolation(float input) { - final float s = 1.70158f; - float position = input; - position--; - return (position * position * ((s + 1.f) * position + s) + 1.f); + return s * (input -= (2.625f / 2.75f)) * input + 0.984375f; + } + }; + + @SuppressWarnings("unused") + public static final EasingFunction EaseInOutBounce = new EasingFunction() { + public float getInterpolation(float input) { + if (input < 0.5f) { + return EaseInBounce.getInterpolation(input * 2f) * 0.5f; } - }; - - public static final EasingFunction EaseInOutBack = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float s = 1.70158f; - // float position = elapsed / (duration / 2.f); - // if (position < 1.f) - // { - // return 0.5f * (position * position * (((s *= (1.525f)) + 1.f) - // * - // position - s)); - // } - // return 0.5f * ((position -= 2.f) * position - // * (((s *= (1.525f)) + 1.f) * position + s) + 2.f); - // } - - @Override - public float getInterpolation(float input) { - float s = 1.70158f; - float position = input / 0.5f; - if (position < 1.f) - { - return 0.5f * (position * position * (((s *= (1.525f)) + 1.f) * - position - s)); - } - return 0.5f * ((position -= 2.f) * position - * (((s *= (1.525f)) + 1.f) * position + s) + 2.f); - } - }; - - public static final EasingFunction EaseInBounce = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // return 1.f - EaseOutBounce.ease(duration - elapsed, - // duration); - // } - - @Override - public float getInterpolation(float input) { - return 1.f - EaseOutBounce.getInterpolation(1f - input); - } - }; - - public static final EasingFunction EaseOutBounce = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // if (position < (1.f / 2.75f)) - // { - // return (7.5625f * position * position); - // } - // else if (position < (2.f / 2.75f)) - // { - // return (7.5625f * (position -= (1.5f / 2.75f)) * position + - // .75f); - // } - // else if (position < (2.5f / 2.75f)) - // { - // return (7.5625f * (position -= (2.25f / 2.75f)) * position + - // .9375f); - // } - // else - // { - // return (7.5625f * (position -= (2.625f / 2.75f)) * position + - // .984375f); - // } - // } - - @Override - public float getInterpolation(float input) { - float position = input; - if (position < (1.f / 2.75f)) - { - return (7.5625f * position * position); - } - else if (position < (2.f / 2.75f)) - { - return (7.5625f * (position -= (1.5f / 2.75f)) * position + .75f); - } - else if (position < (2.5f / 2.75f)) - { - return (7.5625f * (position -= (2.25f / 2.75f)) * position + .9375f); - } - else - { - return (7.5625f * (position -= (2.625f / 2.75f)) * position + - .984375f); - } - } - }; - - public static final EasingFunction EaseInOutBounce = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // if (elapsed < duration / 2.f) - // { - // return EaseInBounce.ease(elapsed * 2, duration) * .5f; - // } - // return EaseOutBounce.ease(elapsed * 2 - duration, duration) * - // .5f + - // .5f; - // } - - @Override - public float getInterpolation(float input) { - if (input < 0.5f) - { - return EaseInBounce.getInterpolation(input * 2) * .5f; - } - return EaseOutBounce.getInterpolation(input * 2 - 1f) * .5f + - .5f; - } - }; + return EaseOutBounce.getInterpolation(input * 2f - 1f) * 0.5f + 0.5f; + } + }; - } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/EasingFunction.java b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/EasingFunction.java deleted file mode 100644 index 98d934da23..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/EasingFunction.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.github.mikephil.charting.animation; - -import android.animation.TimeInterpolator; -import android.annotation.SuppressLint; - -/** - * Interface for creating custom made easing functions. Uses the - * TimeInterpolator interface provided by Android. - */ -@SuppressLint("NewApi") -public interface EasingFunction extends TimeInterpolator { - - @Override - float getInterpolation(float input); -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index ea26f2cfca..35ec2ec1e0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -3,7 +3,6 @@ import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.annotation.SuppressLint; import android.content.ContentValues; import android.content.Context; import android.graphics.Bitmap; @@ -15,8 +14,10 @@ import android.graphics.RectF; import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Environment; import android.provider.MediaStore.Images; +import android.support.annotation.RequiresApi; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; @@ -26,7 +27,7 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.animation.EasingFunction; +import com.github.mikephil.charting.animation.Easing.EasingFunction; import com.github.mikephil.charting.components.Description; import com.github.mikephil.charting.components.IMarker; import com.github.mikephil.charting.components.Legend; @@ -60,7 +61,6 @@ * * @author Philipp Jahoda */ -@SuppressLint("NewApi") public abstract class Chart>> extends ViewGroup implements ChartInterface { @@ -209,9 +209,9 @@ protected void init() { setWillNotDraw(false); // setLayerType(View.LAYER_TYPE_HARDWARE, null); - if (android.os.Build.VERSION.SDK_INT < 11) + if (Build.VERSION.SDK_INT < 11) { mAnimator = new ChartAnimator(); - else + } else { mAnimator = new ChartAnimator(new AnimatorUpdateListener() { @Override @@ -220,6 +220,7 @@ public void onAnimationUpdate(ValueAnimator animation) { postInvalidate(); } }); + } // initialize the utils Utils.init(getContext()); @@ -836,11 +837,27 @@ public void setDragDecelerationFrictionCoef(float newValue) { * @param easingX a custom easing function to be used on the animation phase * @param easingY a custom easing function to be used on the animation phase */ + @RequiresApi(11) public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easingX, EasingFunction easingY) { mAnimator.animateXY(durationMillisX, durationMillisY, easingX, easingY); } + /** + * Animates the drawing / rendering of the chart on both x- and y-axis with + * the specified animation time. If animate(...) is called, no further + * calling of invalidate() is necessary to refresh the chart. ANIMATIONS + * ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. + * + * @param durationMillisX + * @param durationMillisY + * @param easing a custom easing function to be used on the animation phase + */ + @RequiresApi(11) + public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easing) { + mAnimator.animateXY(durationMillisX, durationMillisY, easing); + } + /** * Animates the rendering of the chart on the x-axis with the specified * animation time. If animate(...) is called, no further calling of @@ -850,6 +867,7 @@ public void animateXY(int durationMillisX, int durationMillisY, EasingFunction e * @param durationMillis * @param easing a custom easing function to be used on the animation phase */ + @RequiresApi(11) public void animateX(int durationMillis, EasingFunction easing) { mAnimator.animateX(durationMillis, easing); } @@ -863,6 +881,7 @@ public void animateX(int durationMillis, EasingFunction easing) { * @param durationMillis * @param easing a custom easing function to be used on the animation phase */ + @RequiresApi(11) public void animateY(int durationMillis, EasingFunction easing) { mAnimator.animateY(durationMillis, easing); } @@ -883,7 +902,11 @@ public void animateY(int durationMillis, EasingFunction easing) { * @param durationMillisY * @param easingX a predefined easing option * @param easingY a predefined easing option + * + * @deprecated Use {@link #animateXY(int, int, EasingFunction, EasingFunction)} + * @see #animateXY(int, int, EasingFunction, EasingFunction) */ + @Deprecated public void animateXY(int durationMillisX, int durationMillisY, Easing.EasingOption easingX, Easing.EasingOption easingY) { mAnimator.animateXY(durationMillisX, durationMillisY, easingX, easingY); @@ -897,7 +920,11 @@ public void animateXY(int durationMillisX, int durationMillisY, Easing.EasingOpt * * @param durationMillis * @param easing a predefined easing option + * + * @deprecated Use {@link #animateX(int, EasingFunction)} + * @see #animateX(int, EasingFunction) */ + @Deprecated public void animateX(int durationMillis, Easing.EasingOption easing) { mAnimator.animateX(durationMillis, easing); } @@ -910,7 +937,11 @@ public void animateX(int durationMillis, Easing.EasingOption easing) { * * @param durationMillis * @param easing a predefined easing option + * + * @deprecated Use {@link #animateY(int, EasingFunction)} + * @see #animateY(int, EasingFunction) */ + @Deprecated public void animateY(int durationMillis, Easing.EasingOption easing) { mAnimator.animateY(durationMillis, easing); } @@ -929,6 +960,7 @@ public void animateY(int durationMillis, Easing.EasingOption easing) { * * @param durationMillis */ + @RequiresApi(11) public void animateX(int durationMillis) { mAnimator.animateX(durationMillis); } @@ -941,6 +973,7 @@ public void animateX(int durationMillis) { * * @param durationMillis */ + @RequiresApi(11) public void animateY(int durationMillis) { mAnimator.animateY(durationMillis); } @@ -954,6 +987,7 @@ public void animateY(int durationMillis) { * @param durationMillisX * @param durationMillisY */ + @RequiresApi(11) public void animateXY(int durationMillisX, int durationMillisY) { mAnimator.animateXY(durationMillisX, durationMillisY); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java index d403a752cc..618de18a34 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java @@ -12,6 +12,7 @@ import android.view.MotionEvent; import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.animation.Easing.EasingFunction; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.data.ChartData; @@ -477,7 +478,7 @@ public float getYChartMin() { * @param toangle */ @SuppressLint("NewApi") - public void spin(int durationmillis, float fromangle, float toangle, Easing.EasingOption easing) { + public void spin(int durationmillis, float fromangle, float toangle, EasingFunction easing) { if (android.os.Build.VERSION.SDK_INT < 11) return; @@ -487,7 +488,7 @@ public void spin(int durationmillis, float fromangle, float toangle, Easing.Easi ObjectAnimator spinAnimator = ObjectAnimator.ofFloat(this, "rotationAngle", fromangle, toangle); spinAnimator.setDuration(durationmillis); - spinAnimator.setInterpolator(Easing.getEasingFunctionFromOption(easing)); + spinAnimator.setInterpolator(easing); spinAnimator.addUpdateListener(new AnimatorUpdateListener() { From 5519c5170df03b3dbae893e7d93979809d0d29b0 Mon Sep 17 00:00:00 2001 From: Mick A Date: Mon, 30 Apr 2018 17:42:35 -0600 Subject: [PATCH 198/291] fix(docs): Broken Contributing link --- ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index b87cd42091..e8699b3aa4 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,4 +1,4 @@ -- [ ] I have read the [CONTRIBUTING](CONTRIBUTING.md) file before posting this issue. +- [ ] I have read the [CONTRIBUTING](https://github.com/PhilJay/MPAndroidChart/blob/master/CONTRIBUTING.md) file before posting this issue. ### The problem From 11bf7aa8128b705a77bcbb821d804217d557d5de Mon Sep 17 00:00:00 2001 From: Mick A Date: Tue, 1 May 2018 22:57:40 -0600 Subject: [PATCH 199/291] Update issue templates --- .github/ISSUE_TEMPLATE/Bug_report.md | 31 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/Feature_request.md | 19 ++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/Bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/Feature_request.md diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md new file mode 100644 index 0000000000..72ab35658a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +- [ ] I have read the [CONTRIBUTING](https://github.com/PhilJay/MPAndroidChart/blob/master/CONTRIBUTING.md) file before posting this issue. + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Device (please complete the following information):** + - Device: [e.g. Google Pixel] + - Android Version [e.g. 7.0] + - Libaray Version (e.g. 3.0.3) + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md new file mode 100644 index 0000000000..85bcdbea3c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +- [ ] I have read the [CONTRIBUTING](https://github.com/PhilJay/MPAndroidChart/blob/master/CONTRIBUTING.md) file before posting this issue. + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From a7eb395b8197e6c211530974a0563bbc7fe91725 Mon Sep 17 00:00:00 2001 From: Mick A Date: Tue, 1 May 2018 22:59:06 -0600 Subject: [PATCH 200/291] Downgrade ISSUE_TEMPLATE.md to generic issue --- ISSUE_TEMPLATE.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index e8699b3aa4..65e0e5d643 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,13 +1 @@ - [ ] I have read the [CONTRIBUTING](https://github.com/PhilJay/MPAndroidChart/blob/master/CONTRIBUTING.md) file before posting this issue. - -### The problem - -What is the problem, what is going wrong? Is this a bug, question, or a feature request? - -### What should happen - -What do you think _should_ happen? - -### Addition information - -You can add addition info here, like code snippets or references. You can also attach files like images or stacktraces. Images that are no taller than 500px can be put inside the issue text, but please post larger images and stacktraces as links to a [Gist](https://help.github.com/articles/creating-gists/) or attach the file by clicking "Attach files" below, so that we don't have to scroll all the way down a page to respond to you. From 00e284c043617ae3c8bf81ace0544cfe52ce348b Mon Sep 17 00:00:00 2001 From: Mick A Date: Tue, 1 May 2018 23:02:01 -0600 Subject: [PATCH 201/291] Update Bug_report.md quick fix --- .github/ISSUE_TEMPLATE/Bug_report.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 72ab35658a..a816dd5bed 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -7,20 +7,16 @@ about: Create a report to help us improve - [ ] I have read the [CONTRIBUTING](https://github.com/PhilJay/MPAndroidChart/blob/master/CONTRIBUTING.md) file before posting this issue. **Describe the bug** + A clear and concise description of what the bug is. -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error +**Screenshots** + +If applicable, add screenshots to help explain your problem. **Expected behavior** -A clear and concise description of what you expected to happen. -**Screenshots** -If applicable, add screenshots to help explain your problem. +A clear and concise description of what you expected to happen. **Device (please complete the following information):** - Device: [e.g. Google Pixel] @@ -28,4 +24,5 @@ If applicable, add screenshots to help explain your problem. - Libaray Version (e.g. 3.0.3) **Additional context** -Add any other context about the problem here. + +Add any other context about the problem here. If you have source code demonstrating this bug, create a [Gist](https://help.github.com/articles/creating-gists/) and link to it. From 1ff676ecaf6dfe96e862f6cd6726d9e323e6c11b Mon Sep 17 00:00:00 2001 From: Mick A Date: Wed, 2 May 2018 00:44:47 -0600 Subject: [PATCH 202/291] Update CONTRIBUTING.md Condensed CONTRIBUTING and added helpful reference links. And cake! --- CONTRIBUTING.md | 87 +++++++++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2e4d5b866c..98cf0ab89b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,49 +1,66 @@ +> ### Notice +> *Before you continue, this is the* **ANDROID** *library. If you have an* **iOS** *device, please go here instead*: +> +> – https://github.com/danielgindi/Charts +> +> They might tell you to come back here, if they do, listen to them and ignore this notice. + # How to contribute -Bug-fixes and features often come from users of the MPAndroidChart library and improve it greatly. We want to keep it as easy as possible to contribute changes that improve the experience for users all around the world. There are a few guidelines that we -need contributors to follow so that we can have a chance of keeping on -top of things. +Bug-fixes and features often come from users of the MPAndroidChart library and improve it greatly. We want to keep it as easy as possible to contribute changes that improve the experience for users all around the world. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. + +## Creating Issues + +There are two main issue templates, one for bugs and another for feature requests. Please use them! You're issue will be much easier to understand, and bugs easier to fix, if you follow the templates. If your issue doesn't fit into those, just use the generic template. -## Simple issues and bug reports +Search existing [issues] to see if your bug has already been reported or if a feature request already exists. Don't forget to remove `is:open` so you see all the issues! If you find that one already exists, use reactions to show how much you care! -If you are reporting a bug which can be observed visually, please add to your issue either: +## Making Pull Requests -* Screenshots, if the bug is easily explainable -* A working sample project that we can compile, run, and immediately observe the issue +Careful! If you fail to follow these guidlines, you're pull request may be closed, *even if it's really awesome*. -## Getting Started with Contributions + 0. **Search** open [pull requests] AND existing [issues] to make sure what you want to do isn't already being worked on or already has an open pull request. + 1. **Fork** the repository + 1. **Create** a new branch based on `master`, and name it according to your changes + 1. **Add** your commits, they MUST follow the [Commit Style](#commit-style) below + 1. **Test** your changes by actually running the example app, or create a new example + 1. **Create** a pull request, following the auto-generated template + 1. ??? + 1. Profit :money_with_wings: + +You are encouraged to use [GitHub Desktop] to inspect your code changes before committing them. It can reveal small changes that might have gone unnoticed, and would be requested for removal before merging. -* Make sure you have a [GitHub account](https://github.com/signup/free) -* Submit a ticket for your issue, assuming one does not already exist. - * Clearly describe the issue including steps to reproduce when it is a bug. - * Make sure you fill in the earliest version (or commit number) that you know has the issue. -* Fork the repository on GitHub +Check out [#3975](https://github.com/PhilJay/MPAndroidChart/pull/3975) for an example of a good-made-better pull request. -## Making Changes +## Commit Style -* Create a topic branch from where you want to base your work. This is usually the master branch. -* Make commits of logical units. -* Make sure your code conforms to the code style around it. It's easy, just look around! -* If you have made changes back and forth, or have made merges, your commit history might look messy and hard to understand. A single issue or change should still be in one commit. So please squash those commits together and rebase them however you need to - to make our lives easier when reading it later. -* Check for unnecessary whitespace with `git diff --check` before committing. -* Make sure your commit messages are in the proper format. + * **Make commits of logical units** + Don't load your commits with tons of changes, this makes it hard to follow what is happening. However, if you have done a lot of work, and there are commits and merges all over the place, squash them down into fewer commits. + + * **Conform to the code style** + It's easy, just look around! + + * **Write good commit messages** + You may prefer [Tim Pope's style], you might like the [commitizen-friendly] way. Regardless of the color you pick, you MUST stay within the lines! + ``` +The commit title CANNOT exceed 50 characters -```` - First line must be up to 50 chars (Fixes #1234) +The body of the message comes after an empty new line, and describes the +changes more thoroughly. If the change is obvious and self-explanatory +from the title, you can omit the body. You should describe all changes +if many were made, or maybe some trickery that only code wizards can +understand. - The first line should be a short statement as to what have changed, and should also include an issue number, prefixed with a dash. - The body of the message comes after an empty new line, and describes the changes - more thoroughly, especially if there was a special case handled there, - or maybe some trickery that only code wizards can understand. -```` +Be polite and wrap your lines to 72 characters, but if you prefer going +to 100 characters then I guess we can't stop you. +``` -* Make sure you have tested your changes well. -* If your changes could theoretically affect some other component or case, which you do not necessarily use, you still have to test it. -* Create a Pull Request from your topic branch to the relevant branch in the main repo. If you go to the main repo of the framework, you'll see a big green button which pretty much prepares the PR for you. You just have to hit it. +## Final Notes -## Making Trivial Changes +Thanks for reading the contributing file! Have some cake! :cake: -For changes of a trivial nature to comments and documentation, it is not -always necessary to create a new ticket. In this case, it is -appropriate to start the first line of a commit with '(doc)' instead of -a ticket number. Even the default commit message the GitHub generates is fine with us. +[issues]: https://github.com/PhilJay/MPAndroidChart/issues +[pull requests]: https://github.com/PhilJay/MPAndroidChart/pulls +[GitHub Desktop]: https://desktop.github.com/ +[Tim Pope's style]: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[commitizen-friendly]: https://github.com/commitizen/cz-cli From 9b9d2a68cb1024973e77fb4710aa8747d05b618b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 3 May 2018 16:22:32 +0200 Subject: [PATCH 203/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 063680ed0d..8ffafdc4c4 100644 --- a/README.md +++ b/README.md @@ -209,4 +209,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -**Special thanks** to [danielgindi](https://github.com/danielgindi), [mikegr](https://github.com/mikegr), [tony](https://github.com/tonypatino-monoclesociety) and [jitpack.io](https://github.com/jitpack-io) for their contributions to this project. +**Special thanks** to [danielgindi](https://github.com/danielgindi), [mikegr](https://github.com/mikegr), [tony](https://github.com/tonypatino-monoclesociety), [almic](https://github.com/almic) and [jitpack.io](https://github.com/jitpack-io) for their contributions to this project. From 689d484615a5e587988c08dc7ef5579d29ace5a0 Mon Sep 17 00:00:00 2001 From: almic Date: Thu, 3 May 2018 08:37:00 -0600 Subject: [PATCH 204/291] Delete lingering MyEasingFunction.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I made the decision to remove this file instead of updating it, as I'm sure most will instead prefer to look at the actual Easing class. If you miss this example class... ¯\_(ツ)_/¯ --- .../custom/MyEasingFunction.java | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyEasingFunction.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyEasingFunction.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyEasingFunction.java deleted file mode 100644 index e874a57ab6..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyEasingFunction.java +++ /dev/null @@ -1,18 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample.custom; - -import com.github.mikephil.charting.animation.EasingFunction; - -/** - * Example of a custom made animation EasingFunction. - * - * @author Philipp Jahoda - */ -public class MyEasingFunction implements EasingFunction { - - @Override - public float getInterpolation(float input) { - // do awesome stuff here, this is just linear easing - return input; - } -} From e4ba3cdc20555b5f7cd793e614ee13406a7605ec Mon Sep 17 00:00:00 2001 From: Anirut Teerabut Date: Fri, 4 May 2018 04:21:26 +0700 Subject: [PATCH 205/291] - multiple gradient color --- .../mpchartexample/BarChartActivity.java | 30 +++++++++++++- .../mikephil/charting/data/BaseDataSet.java | 39 +++++++++++++++++++ .../interfaces/datasets/IDataSet.java | 23 +++++++++++ .../charting/model/GradientColor.java | 28 +++++++++++++ .../charting/renderer/BarChartRenderer.java | 28 +++++++++++++ 5 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/model/GradientColor.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 5772359773..2707d0bd51 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -4,6 +4,7 @@ import android.annotation.SuppressLint; import android.graphics.RectF; import android.os.Bundle; +import android.support.v4.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -16,7 +17,6 @@ import com.github.mikephil.charting.charts.BarChart; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.Legend.LegendForm; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.components.YAxis; @@ -31,6 +31,7 @@ import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.model.GradientColor; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.MPPointF; import com.xxmassdeveloper.mpchartexample.custom.DayAxisValueFormatter; @@ -39,6 +40,7 @@ import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.List; public class BarChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { @@ -264,7 +266,31 @@ private void setData(int count, float range) { set1.setDrawIcons(false); - set1.setColors(ColorTemplate.MATERIAL_COLORS); +// set1.setColors(ColorTemplate.MATERIAL_COLORS); + + /*int startColor = ContextCompat.getColor(this, android.R.color.holo_blue_dark); + int endColor = ContextCompat.getColor(this, android.R.color.holo_blue_bright); + set1.setGradientColor(startColor, endColor);*/ + + int startColor1 = ContextCompat.getColor(this, android.R.color.holo_orange_light); + int startColor2 = ContextCompat.getColor(this, android.R.color.holo_blue_light); + int startColor3 = ContextCompat.getColor(this, android.R.color.holo_orange_light); + int startColor4 = ContextCompat.getColor(this, android.R.color.holo_green_light); + int startColor5 = ContextCompat.getColor(this, android.R.color.holo_red_light); + int endColor1 = ContextCompat.getColor(this, android.R.color.holo_blue_dark); + int endColor2 = ContextCompat.getColor(this, android.R.color.holo_purple); + int endColor3 = ContextCompat.getColor(this, android.R.color.holo_green_dark); + int endColor4 = ContextCompat.getColor(this, android.R.color.holo_red_dark); + int endColor5 = ContextCompat.getColor(this, android.R.color.holo_orange_dark); + + List gradientColors = new ArrayList<>(); + gradientColors.add(new GradientColor(startColor1, endColor1)); + gradientColors.add(new GradientColor(startColor2, endColor2)); + gradientColors.add(new GradientColor(startColor3, endColor3)); + gradientColors.add(new GradientColor(startColor4, endColor4)); + gradientColors.add(new GradientColor(startColor5, endColor5)); + + set1.setGradientColors(gradientColors); ArrayList dataSets = new ArrayList(); dataSets.add(set1); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java index 3869a00895..1b9d97850a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -13,6 +13,7 @@ import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.model.GradientColor; import java.lang.annotation.Documented; import java.lang.annotation.Inherited; @@ -31,6 +32,10 @@ public abstract class BaseDataSet implements IDataSet { */ protected List mColors = null; + protected GradientColor gradientColor = null; + + protected List gradientColors = null; + /** * List representing all colors that are used for drawing the actual values for this DataSet */ @@ -144,6 +149,21 @@ public int getColor(int index) { return mColors.get(index % mColors.size()); } + @Override + public GradientColor getGradientColor() { + return gradientColor; + } + + @Override + public List getGradientColors() { + return gradientColors; + } + + @Override + public GradientColor getGradientColor(int index) { + return gradientColors.get(index % gradientColors.size()); + } + /** * ###### ###### COLOR SETTING RELATED METHODS ##### ###### */ @@ -219,6 +239,25 @@ public void setColor(int color) { mColors.add(color); } + /** + * Sets the start and end color for gradient color, ONLY color that should be used for this DataSet. + * + * @param startColor + * @param endColor + */ + public void setGradientColor(int startColor, int endColor) { + gradientColor = new GradientColor(startColor, endColor); + } + + /** + * Sets the start and end color for gradient colors, ONLY color that should be used for this DataSet. + * + * @param gradientColors + */ + public void setGradientColors(List gradientColors) { + this.gradientColors = gradientColors; + } + /** * Sets a color with a specific alpha value. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index fd8af7064b..f64db706e0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -10,6 +10,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.model.GradientColor; import java.util.List; @@ -285,6 +286,28 @@ public interface IDataSet { */ int getColor(); + /** + * Returns the Gradient color model + * + * @return + */ + GradientColor getGradientColor(); + + /** + * Returns the Gradient colors + * + * @return + */ + List getGradientColors(); + + /** + * Returns the Gradient colors + * + * @param index + * @return + */ + GradientColor getGradientColor(int index); + /** * Returns the color at the given index of the DataSet's color array. * Performs a IndexOutOfBounds check by modulus. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/model/GradientColor.java b/MPChartLib/src/main/java/com/github/mikephil/charting/model/GradientColor.java new file mode 100644 index 0000000000..1162c01198 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/model/GradientColor.java @@ -0,0 +1,28 @@ +package com.github.mikephil.charting.model; + +public class GradientColor { + + private int startColor; + private int endColor; + + public GradientColor(int startColor, int endColor) { + this.startColor = startColor; + this.endColor = endColor; + } + + public int getStartColor() { + return startColor; + } + + public void setStartColor(int startColor) { + this.startColor = startColor; + } + + public int getEndColor() { + return endColor; + } + + public void setEndColor(int endColor) { + this.endColor = endColor; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index f17761234e..d3f71af02c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -19,6 +19,8 @@ import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; +import android.graphics.LinearGradient; +import com.github.mikephil.charting.model.GradientColor; import java.util.List; @@ -163,6 +165,32 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { mRenderPaint.setColor(dataSet.getColor(j / 4)); } + if (dataSet.getGradientColor() != null) { + GradientColor gradientColor = dataSet.getGradientColor(); + mRenderPaint.setShader( + new LinearGradient( + buffer.buffer[j], + buffer.buffer[j + 3], + buffer.buffer[j], + buffer.buffer[j + 1], + gradientColor.getStartColor(), + gradientColor.getEndColor(), + android.graphics.Shader.TileMode.MIRROR)); + } + + if (dataSet.getGradientColors() != null) { + mRenderPaint.setShader( + new LinearGradient( + buffer.buffer[j], + buffer.buffer[j + 3], + buffer.buffer[j], + buffer.buffer[j + 1], + dataSet.getGradientColor(j / 4).getStartColor(), + dataSet.getGradientColor(j / 4).getEndColor(), + android.graphics.Shader.TileMode.MIRROR)); + } + + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], buffer.buffer[j + 3], mRenderPaint); From 03c538fc34a945a27a1a636410b0819e56a7c355 Mon Sep 17 00:00:00 2001 From: almic Date: Sun, 6 May 2018 00:02:06 -0600 Subject: [PATCH 206/291] docs(templates): Update Issue & PR templates I've updated the issue and pull request templates, again. This time I looked to the node.js request library for inspiration. It's no secret that this project has been attracting a LOT of very low-quality issues. Almost all are asking questions that can be easily answered if the person looked at the example project or the wiki. Specifically, the new Support_help.md file. This file will hopefully bait these low-quality support questions and reduce the number of opened issues regarding debugging or support significantly. I've updated the default ISSUE_TEMPLATE.md to be a copy of Bug_report.md to force people to read that notice text if they decide to not choose any of the templates. --- .github/ISSUE_TEMPLATE/Bug_report.md | 40 +++++++++++++++-------- .github/ISSUE_TEMPLATE/Feature_request.md | 26 +++++++++++---- .github/ISSUE_TEMPLATE/Support_help.md | 24 ++++++++++++++ ISSUE_TEMPLATE.md | 38 ++++++++++++++++++++- PULL_REQUEST_TEMPLATE.md | 21 ++++++------ 5 files changed, 119 insertions(+), 30 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/Support_help.md diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index a816dd5bed..496d0a0d8d 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -1,28 +1,42 @@ --- -name: Bug report -about: Create a report to help us improve +name: Bugs +about: Create a bug report to help us improve --- -- [ ] I have read the [CONTRIBUTING](https://github.com/PhilJay/MPAndroidChart/blob/master/CONTRIBUTING.md) file before posting this issue. + -**Expected behavior** +**Summary** + -A clear and concise description of what you expected to happen. +**Expected Behavior** + + +**Possible Solution** + + **Device (please complete the following information):** - Device: [e.g. Google Pixel] - Android Version [e.g. 7.0] - - Libaray Version (e.g. 3.0.3) - -**Additional context** + - Library Version (e.g. 3.0.3) -Add any other context about the problem here. If you have source code demonstrating this bug, create a [Gist](https://help.github.com/articles/creating-gists/) and link to it. +**Additional Context** + diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md index 85bcdbea3c..823b940961 100644 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -1,19 +1,33 @@ --- -name: Feature request +name: Feature Request about: Suggest an idea for this project --- -- [ ] I have read the [CONTRIBUTING](https://github.com/PhilJay/MPAndroidChart/blob/master/CONTRIBUTING.md) file before posting this issue. + **Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + **Describe the solution you'd like** -A clear and concise description of what you want to happen. + **Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. + **Additional context** -Add any other context or screenshots about the feature request here. + diff --git a/.github/ISSUE_TEMPLATE/Support_help.md b/.github/ISSUE_TEMPLATE/Support_help.md new file mode 100644 index 0000000000..64c87763df --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Support_help.md @@ -0,0 +1,24 @@ +--- +name: Support +about: I need help! + +--- + +# *STOP RIGHT THERE!* + +Issues are ***NOT*** for getting help, only for reporting bugs and feature requests. + +Search open and closed issues to see if your question already has an answer. However, **do not create a new issue.** + +Instead, do the following: + +1. Download the [Example App](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample) and check out the [source code](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample). 90% of the time there is an example that does exactly what you are trying to do. + +1. Look at the [Wiki](https://github.com/PhilJay/MPAndroidChart/wiki) for the official documentation for MPAndroidChart. You can also browse the [javadoc](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.3/javadoc/) for a more detailed tutorial of the API. + +1. Go to [StackOverflow](https://stackoverflow.com/questions/tagged/mpandroidchart) and ask your questions there. The community will be much more helpful and willing to offer guidance. + + +### You have been warned! + +From now on, any issues asking for help will get closed with a link to this file. diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 65e0e5d643..49ed0dfc1d 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1 +1,37 @@ -- [ ] I have read the [CONTRIBUTING](https://github.com/PhilJay/MPAndroidChart/blob/master/CONTRIBUTING.md) file before posting this issue. + + +**Summary** + + +**Expected Behavior** + + +**Possible Solution** + + + +**Device (please complete the following information):** + - Device: [e.g. Google Pixel] + - Android Version [e.g. 7.0] + - Library Version (e.g. 3.0.3) + +**Additional Context** + diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index fb64ddf8be..f65a30b178 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,14 +1,15 @@ -- [ ] I have read the [CONTRIBUTING](CONTRIBUTING.md) file before posting submitting this pull request. +## PR Checklist: +- [ ] I have tested this extensively and it does not break any existing behavior. +- [ ] I have added/updated examples and tests for any new behavior. +- [ ] If this is a significant change, an issue has already been created where the problem / solution was discussed: [N/A, or add link to issue here] + -Before describing this pull request, please prefix the title with a topic from the list: -`Feature`, `Fix`, `Style` -### [Feature / Fix / Style] (choose ONE) +## PR Description + -What will this PR do? Remember that you don't need to explain everything, let the changes speak for itself. + -If a bug fix, reference any issues that this fixes, like so: "fixes #99" - -### Why should this be merged? - -If needed, further explain this PR and why it should be merged. If the changes are too broad, your pull request may be denied for having too much in it. It may also be denied if the commits aren't properly formatted. + From 9583a18b84e88289774c9713d65764b1bb7c40d7 Mon Sep 17 00:00:00 2001 From: almic Date: Sun, 6 May 2018 00:29:31 -0600 Subject: [PATCH 207/291] chore(template): Move templates to .github folder --- ISSUE_TEMPLATE.md => .github/ISSUE_TEMPLATE.md | 0 PULL_REQUEST_TEMPLATE.md => .github/PULL_REQUEST_TEMPLATE.md | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename ISSUE_TEMPLATE.md => .github/ISSUE_TEMPLATE.md (100%) rename PULL_REQUEST_TEMPLATE.md => .github/PULL_REQUEST_TEMPLATE.md (100%) diff --git a/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md similarity index 100% rename from ISSUE_TEMPLATE.md rename to .github/ISSUE_TEMPLATE.md diff --git a/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST_TEMPLATE.md From 7abc9cd6696f78c77068ffc278793594b33497fe Mon Sep 17 00:00:00 2001 From: RobertZagorski Date: Tue, 8 May 2018 16:19:09 +0200 Subject: [PATCH 208/291] Update and reorganise copy data sets methods (Fix for #1604). Copying class properties is always done in protected copy method. --- .../LineChartActivityColored.java | 2 +- .../mikephil/charting/data/BarDataSet.java | 27 +++-- .../BarLineScatterCandleBubbleDataSet.java | 17 ++- .../mikephil/charting/data/BaseDataSet.java | 43 +++++--- .../mikephil/charting/data/BubbleDataSet.java | 18 ++-- .../mikephil/charting/data/CandleDataSet.java | 35 +++--- .../mikephil/charting/data/DataSet.java | 8 ++ .../mikephil/charting/data/LineDataSet.java | 42 ++++---- .../charting/data/LineRadarDataSet.java | 9 ++ .../data/LineScatterCandleRadarDataSet.java | 8 ++ .../mikephil/charting/data/PieDataSet.java | 102 +++++++++--------- .../mikephil/charting/data/RadarDataSet.java | 67 +++++------- .../charting/data/ScatterDataSet.java | 50 ++++----- 13 files changed, 238 insertions(+), 190 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index 68bba4b458..22984b4b6e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -53,7 +53,7 @@ protected void onCreate(Bundle savedInstanceState) { private void setupChart(LineChart chart, LineData data, int color) { - ((LineDataSet) data.getDataSetByIndex(0)).setCircleColorHole(color); + ((LineDataSet) data.getDataSetByIndex(0)).setCircleHoleColor(color); // no description text chart.getDescription().setEnabled(false); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java index ae11c97b3c..496f4046f8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java @@ -53,25 +53,24 @@ public BarDataSet(List yVals, String label) { @Override public DataSet copy() { - - List yVals = new ArrayList(); - yVals.clear(); - + List entries = new ArrayList(); for (int i = 0; i < mValues.size(); i++) { - yVals.add(mValues.get(i).copy()); + entries.add(mValues.get(i).copy()); } - - BarDataSet copied = new BarDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mStackSize = mStackSize; - copied.mBarShadowColor = mBarShadowColor; - copied.mStackLabels = mStackLabels; - copied.mHighLightColor = mHighLightColor; - copied.mHighLightAlpha = mHighLightAlpha; - + BarDataSet copied = new BarDataSet(entries, getLabel()); + copy(copied); return copied; } + protected void copy(BarDataSet barDataSet) { + super.copy(barDataSet); + barDataSet.mStackSize = mStackSize; + barDataSet.mBarShadowColor = mBarShadowColor; + barDataSet.mBarBorderWidth = mBarBorderWidth; + barDataSet.mStackLabels = mStackLabels; + barDataSet.mHighLightAlpha = mHighLightAlpha; + } + /** * Calculates the total number of entries this DataSet represents, including * stacks. All values belonging to a stack are calculated separately. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java index fba8216c3d..eab6dccc55 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java @@ -9,12 +9,16 @@ /** * Baseclass of all DataSets for Bar-, Line-, Scatter- and CandleStickChart. - * + * * @author Philipp Jahoda */ -public abstract class BarLineScatterCandleBubbleDataSet extends DataSet implements IBarLineScatterCandleBubbleDataSet { +public abstract class BarLineScatterCandleBubbleDataSet + extends DataSet + implements IBarLineScatterCandleBubbleDataSet { - /** default highlight color */ + /** + * default highlight color + */ protected int mHighLightColor = Color.rgb(255, 187, 115); public BarLineScatterCandleBubbleDataSet(List yVals, String label) { @@ -25,7 +29,7 @@ public BarLineScatterCandleBubbleDataSet(List yVals, String label) { * Sets the color that is used for drawing the highlight indicators. Dont * forget to resolve the color using getResources().getColor(...) or * Color.rgb(...). - * + * * @param color */ public void setHighLightColor(int color) { @@ -36,4 +40,9 @@ public void setHighLightColor(int color) { public int getHighLightColor() { return mHighLightColor; } + + protected void copy(BarLineScatterCandleBubbleDataSet barLineScatterCandleBubbleDataSet) { + super.copy(barLineScatterCandleBubbleDataSet); + barLineScatterCandleBubbleDataSet.mHighLightColor = mHighLightColor; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java index 1b9d97850a..7800986dcd 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -7,16 +7,13 @@ import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.formatter.DefaultValueFormatter; import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.model.GradientColor; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.model.GradientColor; -import java.lang.annotation.Documented; -import java.lang.annotation.Inherited; import java.util.ArrayList; import java.util.List; @@ -32,9 +29,9 @@ public abstract class BaseDataSet implements IDataSet { */ protected List mColors = null; - protected GradientColor gradientColor = null; + protected GradientColor mGradientColor = null; - protected List gradientColors = null; + protected List mGradientColors = null; /** * List representing all colors that are used for drawing the actual values for this DataSet @@ -151,17 +148,17 @@ public int getColor(int index) { @Override public GradientColor getGradientColor() { - return gradientColor; + return mGradientColor; } @Override public List getGradientColors() { - return gradientColors; + return mGradientColors; } @Override public GradientColor getGradientColor(int index) { - return gradientColors.get(index % gradientColors.size()); + return mGradientColors.get(index % mGradientColors.size()); } /** @@ -206,7 +203,7 @@ public void setColors(int... colors) { */ public void setColors(int[] colors, Context c) { - if(mColors == null){ + if (mColors == null) { mColors = new ArrayList<>(); } @@ -246,7 +243,7 @@ public void setColor(int color) { * @param endColor */ public void setGradientColor(int startColor, int endColor) { - gradientColor = new GradientColor(startColor, endColor); + mGradientColor = new GradientColor(startColor, endColor); } /** @@ -255,7 +252,7 @@ public void setGradientColor(int startColor, int endColor) { * @param gradientColors */ public void setGradientColors(List gradientColors) { - this.gradientColors = gradientColors; + this.mGradientColors = gradientColors; } /** @@ -285,7 +282,7 @@ public void setColors(int[] colors, int alpha) { * Resets all colors of this DataSet and recreates the colors array. */ public void resetColors() { - if(mColors == null) { + if (mColors == null) { mColors = new ArrayList(); } mColors.clear(); @@ -527,4 +524,24 @@ public boolean contains(T e) { return false; } + + protected void copy(BaseDataSet baseDataSet) { + baseDataSet.mAxisDependency = mAxisDependency; + baseDataSet.mColors = mColors; + baseDataSet.mDrawIcons = mDrawIcons; + baseDataSet.mDrawValues = mDrawValues; + baseDataSet.mForm = mForm; + baseDataSet.mFormLineDashEffect = mFormLineDashEffect; + baseDataSet.mFormLineWidth = mFormLineWidth; + baseDataSet.mFormSize = mFormSize; + baseDataSet.mGradientColor = mGradientColor; + baseDataSet.mGradientColors = mGradientColors; + baseDataSet.mHighlightEnabled = mHighlightEnabled; + baseDataSet.mIconsOffset = mIconsOffset; + baseDataSet.mValueColors = mValueColors; + baseDataSet.mValueFormatter = mValueFormatter; + baseDataSet.mValueColors = mValueColors; + baseDataSet.mValueTextSize = mValueTextSize; + baseDataSet.mVisible = mVisible; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java index d8c0c13013..1f88272dd9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java @@ -41,20 +41,20 @@ protected void calcMinMax(BubbleEntry e) { @Override public DataSet copy() { - - List yVals = new ArrayList(); - + List entries = new ArrayList(); for (int i = 0; i < mValues.size(); i++) { - yVals.add(mValues.get(i).copy()); + entries.add(mValues.get(i).copy()); } - - BubbleDataSet copied = new BubbleDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mHighLightColor = mHighLightColor; - + BubbleDataSet copied = new BubbleDataSet(entries, getLabel()); + copy(copied); return copied; } + protected void copy(BubbleDataSet bubbleDataSet) { + bubbleDataSet.mHighlightCircleWidth = mHighlightCircleWidth; + bubbleDataSet.mNormalizeSize = mNormalizeSize; + } + @Override public float getMaxSize() { return mMaxSize; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java index 7574b78b27..c7f8362803 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java @@ -79,27 +79,30 @@ public CandleDataSet(List yVals, String label) { @Override public DataSet copy() { - - List yVals = new ArrayList(); - yVals.clear(); - + List entries = new ArrayList(); for (int i = 0; i < mValues.size(); i++) { - yVals.add(mValues.get(i).copy()); + entries.add(mValues.get(i).copy()); } - - CandleDataSet copied = new CandleDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mShadowWidth = mShadowWidth; - copied.mShowCandleBar = mShowCandleBar; - copied.mBarSpace = mBarSpace; - copied.mHighLightColor = mHighLightColor; - copied.mIncreasingPaintStyle = mIncreasingPaintStyle; - copied.mDecreasingPaintStyle = mDecreasingPaintStyle; - copied.mShadowColor = mShadowColor; - + CandleDataSet copied = new CandleDataSet(entries, getLabel()); + copy(copied); return copied; } + protected void copy(CandleDataSet candleDataSet) { + super.copy(candleDataSet); + candleDataSet.mShadowWidth = mShadowWidth; + candleDataSet.mShowCandleBar = mShowCandleBar; + candleDataSet.mBarSpace = mBarSpace; + candleDataSet.mShadowColorSameAsCandle = mShadowColorSameAsCandle; + candleDataSet.mHighLightColor = mHighLightColor; + candleDataSet.mIncreasingPaintStyle = mIncreasingPaintStyle; + candleDataSet.mDecreasingPaintStyle = mDecreasingPaintStyle; + candleDataSet.mNeutralColor = mNeutralColor; + candleDataSet.mIncreasingColor = mIncreasingColor; + candleDataSet.mDecreasingColor = mDecreasingColor; + candleDataSet.mShadowColor = mShadowColor; + } + @Override protected void calcMinMax(CandleEntry e) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java index a96cfdcf66..3c69d9c58f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -157,6 +157,14 @@ public void setValues(List values) { */ public abstract DataSet copy(); + /** + * + * @param dataSet + */ + protected void copy(DataSet dataSet) { + super.copy(dataSet); + } + @Override public String toString() { StringBuffer buffer = new StringBuffer(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java index 5eced95e43..c1018d1fb4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java @@ -30,7 +30,7 @@ public class LineDataSet extends LineRadarDataSet implements ILineDataSet /** * the color of the inner circles */ - private int mCircleColorHole = Color.WHITE; + private int mCircleHoleColor = Color.WHITE; /** * the radius of the circle-shaped value indicators @@ -84,27 +84,29 @@ public LineDataSet(List yVals, String label) { @Override public DataSet copy() { - - List yVals = new ArrayList(); - + List entries = new ArrayList(); for (int i = 0; i < mValues.size(); i++) { - yVals.add(mValues.get(i).copy()); + entries.add(mValues.get(i).copy()); } - - LineDataSet copied = new LineDataSet(yVals, getLabel()); - copied.mMode = mMode; - copied.mColors = mColors; - copied.mCircleRadius = mCircleRadius; - copied.mCircleHoleRadius = mCircleHoleRadius; - copied.mCircleColors = mCircleColors; - copied.mDashPathEffect = mDashPathEffect; - copied.mDrawCircles = mDrawCircles; - copied.mDrawCircleHole = mDrawCircleHole; - copied.mHighLightColor = mHighLightColor; - + LineDataSet copied = new LineDataSet(entries, getLabel()); + copy(copied); return copied; } + protected void copy(LineDataSet lineDataSet) { + super.copy(lineDataSet); + lineDataSet.mCircleColors = mCircleColors; + lineDataSet.mCircleHoleColor = mCircleHoleColor; + lineDataSet.mCircleHoleRadius = mCircleHoleRadius; + lineDataSet.mCircleRadius = mCircleRadius; + lineDataSet.mCubicIntensity = mCubicIntensity; + lineDataSet.mDashPathEffect = mDashPathEffect; + lineDataSet.mDrawCircleHole = mDrawCircleHole; + lineDataSet.mDrawCircles = mDrawCircleHole; + lineDataSet.mFillFormatter = mFillFormatter; + lineDataSet.mMode = mMode; + } + /** * Returns the drawing mode for this line dataset * @@ -364,13 +366,13 @@ public void resetCircleColors() { * * @param color */ - public void setCircleColorHole(int color) { - mCircleColorHole = color; + public void setCircleHoleColor(int color) { + mCircleHoleColor = color; } @Override public int getCircleHoleColor() { - return mCircleColorHole; + return mCircleHoleColor; } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java index 6971144e14..688585cbdd 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java @@ -122,4 +122,13 @@ public void setDrawFilled(boolean filled) { public boolean isDrawFilledEnabled() { return mDrawFilled; } + + protected void copy(LineRadarDataSet lineRadarDataSet) { + super.copy(lineRadarDataSet); + lineRadarDataSet.mDrawFilled = mDrawFilled; + lineRadarDataSet.mFillAlpha = mFillAlpha; + lineRadarDataSet.mFillColor = mFillColor; + lineRadarDataSet.mFillDrawable = mFillDrawable; + lineRadarDataSet.mLineWidth = mLineWidth; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java index 90a0a43fb3..d4618d809e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java @@ -109,4 +109,12 @@ public boolean isDashedHighlightLineEnabled() { public DashPathEffect getDashPathEffectHighlight() { return mHighlightDashPathEffect; } + + protected void copy(LineScatterCandleRadarDataSet lineScatterCandleRadarDataSet) { + super.copy(lineScatterCandleRadarDataSet); + lineScatterCandleRadarDataSet.mDrawHorizontalHighlightIndicator = mDrawHorizontalHighlightIndicator; + lineScatterCandleRadarDataSet.mDrawVerticalHighlightIndicator = mDrawVerticalHighlightIndicator; + lineScatterCandleRadarDataSet.mHighlightLineWidth = mHighlightLineWidth; + lineScatterCandleRadarDataSet.mHighlightDashPathEffect = mHighlightDashPathEffect; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java index 98b434d3d7..e592399513 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java @@ -9,11 +9,15 @@ public class PieDataSet extends DataSet implements IPieDataSet { - /** the space in pixels between the chart-slices, default 0f */ + /** + * the space in pixels between the chart-slices, default 0f + */ private float mSliceSpace = 0f; private boolean mAutomaticallyDisableSliceSpacing; - /** indicates the selection distance of a pie slice */ + /** + * indicates the selection distance of a pie slice + */ private float mShift = 18f; private ValuePosition mXValuePosition = ValuePosition.INSIDE_SLICE; @@ -33,20 +37,19 @@ public PieDataSet(List yVals, String label) { @Override public DataSet copy() { - - List yVals = new ArrayList<>(); - + List entries = new ArrayList<>(); for (int i = 0; i < mValues.size(); i++) { - yVals.add(mValues.get(i).copy()); + entries.add(mValues.get(i).copy()); } - - PieDataSet copied = new PieDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mSliceSpace = mSliceSpace; - copied.mShift = mShift; + PieDataSet copied = new PieDataSet(entries, getLabel()); + copy(copied); return copied; } + protected void copy(PieDataSet pieDataSet) { + super.copy(pieDataSet); + } + @Override protected void calcMinMax(PieEntry e) { @@ -79,7 +82,7 @@ public float getSliceSpace() { /** * When enabled, slice spacing will be 0.0 when the smallest value is going to be - * smaller than the slice spacing itself. + * smaller than the slice spacing itself. * * @param autoDisable */ @@ -89,7 +92,7 @@ public void setAutomaticallyDisableSliceSpacing(boolean autoDisable) { /** * When enabled, slice spacing will be 0.0 when the smallest value is going to be - * smaller than the slice spacing itself. + * smaller than the slice spacing itself. * * @return */ @@ -101,7 +104,7 @@ public boolean isAutomaticallyDisableSliceSpacingEnabled() { /** * sets the distance the highlighted piechart-slice of this DataSet is * "shifted" away from the center of the chart, default 12f - * + * * @param shift */ public void setSelectionShift(float shift) { @@ -114,30 +117,26 @@ public float getSelectionShift() { } @Override - public ValuePosition getXValuePosition() - { + public ValuePosition getXValuePosition() { return mXValuePosition; } - public void setXValuePosition(ValuePosition xValuePosition) - { + public void setXValuePosition(ValuePosition xValuePosition) { this.mXValuePosition = xValuePosition; } @Override - public ValuePosition getYValuePosition() - { + public ValuePosition getYValuePosition() { return mYValuePosition; } - public void setYValuePosition(ValuePosition yValuePosition) - { + public void setYValuePosition(ValuePosition yValuePosition) { this.mYValuePosition = yValuePosition; } /** * When valuePosition is OutsideSlice, use slice colors as line color if true - * */ + */ @Override public boolean isUsingSliceColorAsValueLineColor() { return mUsingSliceColorAsValueLineColor; @@ -147,10 +146,11 @@ public void setUsingSliceColorAsValueLineColor(boolean usingSliceColorAsValueLin this.mUsingSliceColorAsValueLineColor = usingSliceColorAsValueLineColor; } - /** When valuePosition is OutsideSlice, indicates line color */ + /** + * When valuePosition is OutsideSlice, indicates line color + */ @Override - public int getValueLineColor() - { + public int getValueLineColor() { return mValueLineColor; } @@ -158,63 +158,63 @@ public void setValueLineColor(int valueLineColor) { this.mValueLineColor = valueLineColor; } - /** When valuePosition is OutsideSlice, indicates line width */ + /** + * When valuePosition is OutsideSlice, indicates line width + */ @Override - public float getValueLineWidth() - { + public float getValueLineWidth() { return mValueLineWidth; } - public void setValueLineWidth(float valueLineWidth) - { + public void setValueLineWidth(float valueLineWidth) { this.mValueLineWidth = valueLineWidth; } - /** When valuePosition is OutsideSlice, indicates offset as percentage out of the slice size */ + /** + * When valuePosition is OutsideSlice, indicates offset as percentage out of the slice size + */ @Override - public float getValueLinePart1OffsetPercentage() - { + public float getValueLinePart1OffsetPercentage() { return mValueLinePart1OffsetPercentage; } - public void setValueLinePart1OffsetPercentage(float valueLinePart1OffsetPercentage) - { + public void setValueLinePart1OffsetPercentage(float valueLinePart1OffsetPercentage) { this.mValueLinePart1OffsetPercentage = valueLinePart1OffsetPercentage; } - /** When valuePosition is OutsideSlice, indicates length of first half of the line */ + /** + * When valuePosition is OutsideSlice, indicates length of first half of the line + */ @Override - public float getValueLinePart1Length() - { + public float getValueLinePart1Length() { return mValueLinePart1Length; } - public void setValueLinePart1Length(float valueLinePart1Length) - { + public void setValueLinePart1Length(float valueLinePart1Length) { this.mValueLinePart1Length = valueLinePart1Length; } - /** When valuePosition is OutsideSlice, indicates length of second half of the line */ + /** + * When valuePosition is OutsideSlice, indicates length of second half of the line + */ @Override - public float getValueLinePart2Length() - { + public float getValueLinePart2Length() { return mValueLinePart2Length; } - public void setValueLinePart2Length(float valueLinePart2Length) - { + public void setValueLinePart2Length(float valueLinePart2Length) { this.mValueLinePart2Length = valueLinePart2Length; } - /** When valuePosition is OutsideSlice, this allows variable line length */ + /** + * When valuePosition is OutsideSlice, this allows variable line length + */ @Override - public boolean isValueLineVariableLength() - { + public boolean isValueLineVariableLength() { return mValueLineVariableLength; } - public void setValueLineVariableLength(boolean valueLineVariableLength) - { + public void setValueLineVariableLength(boolean valueLineVariableLength) { this.mValueLineVariableLength = valueLineVariableLength; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java index f18aa8c23a..09c94b417d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java @@ -20,7 +20,7 @@ public class RadarDataSet extends LineRadarDataSet implements IRadar /// If Utils.COLOR_NONE, the color of the dataset is taken. protected int mHighlightCircleStrokeColor = ColorTemplate.COLOR_NONE; - protected int mHighlightCircleStrokeAlpha = (int)(0.3 * 255); + protected int mHighlightCircleStrokeAlpha = (int) (0.3 * 255); protected float mHighlightCircleInnerRadius = 3.0f; protected float mHighlightCircleOuterRadius = 4.0f; protected float mHighlightCircleStrokeWidth = 2.0f; @@ -31,101 +31,92 @@ public RadarDataSet(List yVals, String label) { /// Returns true if highlight circle should be drawn, false if not @Override - public boolean isDrawHighlightCircleEnabled() - { + public boolean isDrawHighlightCircleEnabled() { return mDrawHighlightCircleEnabled; } /// Sets whether highlight circle should be drawn or not @Override - public void setDrawHighlightCircleEnabled(boolean enabled) - { + public void setDrawHighlightCircleEnabled(boolean enabled) { mDrawHighlightCircleEnabled = enabled; } @Override - public int getHighlightCircleFillColor() - { + public int getHighlightCircleFillColor() { return mHighlightCircleFillColor; } - public void setHighlightCircleFillColor(int color) - { + public void setHighlightCircleFillColor(int color) { mHighlightCircleFillColor = color; } /// Returns the stroke color for highlight circle. /// If Utils.COLOR_NONE, the color of the dataset is taken. @Override - public int getHighlightCircleStrokeColor() - { + public int getHighlightCircleStrokeColor() { return mHighlightCircleStrokeColor; } /// Sets the stroke color for highlight circle. /// Set to Utils.COLOR_NONE in order to use the color of the dataset; - public void setHighlightCircleStrokeColor(int color) - { + public void setHighlightCircleStrokeColor(int color) { mHighlightCircleStrokeColor = color; } @Override - public int getHighlightCircleStrokeAlpha() - { + public int getHighlightCircleStrokeAlpha() { return mHighlightCircleStrokeAlpha; } - public void setHighlightCircleStrokeAlpha(int alpha) - { + public void setHighlightCircleStrokeAlpha(int alpha) { mHighlightCircleStrokeAlpha = alpha; } @Override - public float getHighlightCircleInnerRadius() - { + public float getHighlightCircleInnerRadius() { return mHighlightCircleInnerRadius; } - public void setHighlightCircleInnerRadius(float radius) - { + public void setHighlightCircleInnerRadius(float radius) { mHighlightCircleInnerRadius = radius; } @Override - public float getHighlightCircleOuterRadius() - { + public float getHighlightCircleOuterRadius() { return mHighlightCircleOuterRadius; } - public void setHighlightCircleOuterRadius(float radius) - { + public void setHighlightCircleOuterRadius(float radius) { mHighlightCircleOuterRadius = radius; } @Override - public float getHighlightCircleStrokeWidth() - { + public float getHighlightCircleStrokeWidth() { return mHighlightCircleStrokeWidth; } - public void setHighlightCircleStrokeWidth(float strokeWidth) - { + public void setHighlightCircleStrokeWidth(float strokeWidth) { mHighlightCircleStrokeWidth = strokeWidth; } @Override public DataSet copy() { - - List yVals = new ArrayList(); - + List entries = new ArrayList(); for (int i = 0; i < mValues.size(); i++) { - yVals.add(mValues.get(i).copy()); + entries.add(mValues.get(i).copy()); } - - RadarDataSet copied = new RadarDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mHighLightColor = mHighLightColor; - + RadarDataSet copied = new RadarDataSet(entries, getLabel()); + copy(copied); return copied; } + + protected void copy(RadarDataSet radarDataSet) { + super.copy(radarDataSet); + radarDataSet.mDrawHighlightCircleEnabled = mDrawHighlightCircleEnabled; + radarDataSet.mHighlightCircleFillColor = mHighlightCircleFillColor; + radarDataSet.mHighlightCircleInnerRadius = mHighlightCircleInnerRadius; + radarDataSet.mHighlightCircleStrokeAlpha = mHighlightCircleStrokeAlpha; + radarDataSet.mHighlightCircleStrokeColor = mHighlightCircleStrokeColor; + radarDataSet.mHighlightCircleStrokeWidth = mHighlightCircleStrokeWidth; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java index a9d73885b5..d234c751a0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java @@ -47,28 +47,23 @@ public ScatterDataSet(List yVals, String label) { @Override public DataSet copy() { - - List yVals = new ArrayList(); - + List entries = new ArrayList(); for (int i = 0; i < mValues.size(); i++) { - yVals.add(mValues.get(i).copy()); + entries.add(mValues.get(i).copy()); } - - ScatterDataSet copied = new ScatterDataSet(yVals, getLabel()); - copied.mDrawValues = mDrawValues; - copied.mValueColors = mValueColors; - copied.mColors = mColors; - copied.mShapeSize = mShapeSize; - copied.mShapeRenderer = mShapeRenderer; - copied.mScatterShapeHoleRadius = mScatterShapeHoleRadius; - copied.mScatterShapeHoleColor = mScatterShapeHoleColor; - copied.mHighlightLineWidth = mHighlightLineWidth; - copied.mHighLightColor = mHighLightColor; - copied.mHighlightDashPathEffect = mHighlightDashPathEffect; - + ScatterDataSet copied = new ScatterDataSet(entries, getLabel()); + copy(copied); return copied; } + protected void copy(ScatterDataSet scatterDataSet) { + super.copy(scatterDataSet); + scatterDataSet.mShapeSize = mShapeSize; + scatterDataSet.mShapeRenderer = mShapeRenderer; + scatterDataSet.mScatterShapeHoleRadius = mScatterShapeHoleRadius; + scatterDataSet.mScatterShapeHoleColor = mScatterShapeHoleColor; + } + /** * Sets the size in density pixels the drawn scattershape will have. This * only applies for non custom shapes. @@ -141,13 +136,20 @@ public int getScatterShapeHoleColor() { public static IShapeRenderer getRendererForShape(ScatterChart.ScatterShape shape) { switch (shape) { - case SQUARE: return new SquareShapeRenderer(); - case CIRCLE: return new CircleShapeRenderer(); - case TRIANGLE: return new TriangleShapeRenderer(); - case CROSS: return new CrossShapeRenderer(); - case X: return new XShapeRenderer(); - case CHEVRON_UP: return new ChevronUpShapeRenderer(); - case CHEVRON_DOWN: return new ChevronDownShapeRenderer(); + case SQUARE: + return new SquareShapeRenderer(); + case CIRCLE: + return new CircleShapeRenderer(); + case TRIANGLE: + return new TriangleShapeRenderer(); + case CROSS: + return new CrossShapeRenderer(); + case X: + return new XShapeRenderer(); + case CHEVRON_UP: + return new ChevronUpShapeRenderer(); + case CHEVRON_DOWN: + return new ChevronDownShapeRenderer(); } return null; From 92c14db5b4de4efd43c9fd74361886b92111ca9f Mon Sep 17 00:00:00 2001 From: Pawel Grzybek Date: Wed, 9 May 2018 08:09:03 +0200 Subject: [PATCH 209/291] Fixed code review comments. --- .../mikephil/charting/components/YAxis.java | 56 ++++++++----------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java index c572e3043c..2d2782cf85 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java @@ -41,23 +41,12 @@ public class YAxis extends AxisBase { /** * flag indicating that auto scale min restriction should be used */ - private boolean mUseAutoScaleRestrictionMin = false; + /** * flag indicating that auto scale max restriction should be used */ - private boolean mUseAutoScaleRestrictionMax = false; - /** - * restriction value of autoscale min - */ - - private float mAutoScaleMinRestriction = 0f; - - /** - * restriction value of autoscale max - */ - private float mAutoScaleMaxRestriction = 0f; /** * Color of the zero line @@ -379,35 +368,34 @@ public boolean needsOffset() { } /** - * Sets min value restriction for autoscale + * Returns true if autoscale restriction for axis min value is enabled */ - public void setAutoScaleMinRestriction(float restrictionValue) { - mUseAutoScaleRestrictionMin = true; - mAutoScaleMinRestriction = restrictionValue; + public boolean isUseAutoScaleMinRestriction( ) { + return mUseAutoScaleRestrictionMin; } /** - * Sets max value restriction for autoscale + * Sets autoscale restriction for axis min value as enabled/disabled */ - public void setAutoScaleMaxRestriction(float restrictionValue) { - mUseAutoScaleRestrictionMax = true; - mAutoScaleMaxRestriction = restrictionValue; + public void setUseAutoScaleMinRestriction( boolean isEnabled ) { + mUseAutoScaleRestrictionMin = isEnabled; } /** - * Resets min value restriction for autoscale + * Returns true if autoscale restriction for axis max value is enabled */ - public void resetAutoScaleMinRestriction() { - mUseAutoScaleRestrictionMin = false; + public boolean isUseAutoScaleMaxRestriction() { + return mUseAutoScaleRestrictionMax; } /** - * Resets max value restriction for autoscale + * Sets autoscale restriction for axis max value as enabled/disabled */ - public void resetAutoScaleMaxRestriction() { - mUseAutoScaleRestrictionMax = false; + public void setUseAutoScaleMaxRestriction( boolean isEnabled ) { + mUseAutoScaleRestrictionMax = isEnabled; } + @Override public void calculate(float dataMin, float dataMax) { @@ -416,15 +404,19 @@ public void calculate(float dataMin, float dataMax) { // if custom, use value as is, else use data value if( mCustomAxisMin ) { - min = mAxisMinimum; - } else if( mUseAutoScaleRestrictionMin ) { - min = Math.min( min, mAutoScaleMinRestriction ); + if( mUseAutoScaleRestrictionMin ) { + min = Math.min( dataMin, mAxisMinimum ); + } else { + min = mAxisMinimum; + } } if( mCustomAxisMax ) { - max = mAxisMaximum; - } else if( mUseAutoScaleRestrictionMax ) { - max = Math.max( max, mAutoScaleMaxRestriction ); + if( mUseAutoScaleRestrictionMax ) { + max = Math.max( max, mAxisMaximum ); + } else { + max = mAxisMaximum; + } } // temporary range (before calculations) From 89436221db3d83991f9ecd830f871d4ce7da47a4 Mon Sep 17 00:00:00 2001 From: RobertZagorski Date: Wed, 9 May 2018 08:44:22 +0200 Subject: [PATCH 210/291] Remove mLabelRotatedHeight counted twice, when calculating legend offsets. (Fix for #2369). Removed statements where completely not needed as calculating offsets is alredy done in BarLineCharBase#calculateOffsets(...) --- .../charting/charts/BarLineChartBase.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index bf4c42e241..1cca83ddd0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -427,18 +427,12 @@ protected void calculateLegendOffsets(RectF offsets) { offsets.top += Math.min(mLegend.mNeededHeight, mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) + mLegend.getYOffset(); - - if (getXAxis().isEnabled() && getXAxis().isDrawLabelsEnabled()) - offsets.top += getXAxis().mLabelRotatedHeight; break; case BOTTOM: offsets.bottom += Math.min(mLegend.mNeededHeight, mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) + mLegend.getYOffset(); - - if (getXAxis().isEnabled() && getXAxis().isDrawLabelsEnabled()) - offsets.bottom += getXAxis().mLabelRotatedHeight; break; default: @@ -478,21 +472,21 @@ public void calculateOffsets() { if (mXAxis.isEnabled() && mXAxis.isDrawLabelsEnabled()) { - float xlabelheight = mXAxis.mLabelRotatedHeight + mXAxis.getYOffset(); + float xLabelHeight = mXAxis.mLabelRotatedHeight + mXAxis.getYOffset(); // offsets for x-labels if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - offsetBottom += xlabelheight; + offsetBottom += xLabelHeight; } else if (mXAxis.getPosition() == XAxisPosition.TOP) { - offsetTop += xlabelheight; + offsetTop += xLabelHeight; } else if (mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - offsetBottom += xlabelheight; - offsetTop += xlabelheight; + offsetBottom += xLabelHeight; + offsetTop += xLabelHeight; } } @@ -1224,7 +1218,7 @@ public boolean isDrawBordersEnabled() { /** * When enabled, the values will be clipped to contentRect, - * otherwise they can bleed outside the content rect. + * otherwise they can bleed outside the content rect. * * @param enabled */ @@ -1234,7 +1228,7 @@ public void setClipValuesToContent(boolean enabled) { /** * When enabled, the values will be clipped to contentRect, - * otherwise they can bleed outside the content rect. + * otherwise they can bleed outside the content rect. * * @return */ From 5869c9de23bf46eb36631a1dcc2785fe6f0e19f0 Mon Sep 17 00:00:00 2001 From: Maxim Pestryakov Date: Wed, 9 May 2018 16:03:48 +0300 Subject: [PATCH 211/291] Fixed Javadoc --- .../github/mikephil/charting/formatter/LargeValueFormatter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java index 01eae56f51..211401ad8a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java @@ -66,7 +66,7 @@ public void setAppendix(String appendix) { * Set custom suffix to be appended after the values. * Default suffix: ["", "k", "m", "b", "t"] * - * @param suff new suffix + * @param suffix new suffix */ public void setSuffix(String[] suffix) { this.mSuffix = suffix; From 75dd04cc42b519b6221b68061a74ee6b7f222d88 Mon Sep 17 00:00:00 2001 From: RobertZagorski Date: Thu, 10 May 2018 08:01:03 +0200 Subject: [PATCH 212/291] Remove redundant findViewById casts, that became obsolete after migration to API 26. --- .../mpchartexample/AnotherBarActivity.java | 10 +++++----- .../mpchartexample/BarChartActivity.java | 10 +++++----- .../mpchartexample/BarChartActivityMultiDataset.java | 10 +++++----- .../mpchartexample/BarChartActivitySinus.java | 6 +++--- .../mpchartexample/BarChartPositiveNegative.java | 2 +- .../mpchartexample/BubbleChartActivity.java | 10 +++++----- .../mpchartexample/CandleStickChartActivity.java | 10 +++++----- .../mpchartexample/CombinedChartActivity.java | 2 +- .../mpchartexample/CubicLineChartActivity.java | 10 +++++----- .../mpchartexample/DrawChartActivity.java | 2 +- .../mpchartexample/DynamicalAddingActivity.java | 2 +- .../mpchartexample/FilledLineActivity.java | 2 +- .../mpchartexample/HalfPieChartActivity.java | 2 +- .../mpchartexample/HorizontalBarChartActivity.java | 10 +++++----- .../mpchartexample/InvertedLineChartActivity.java | 10 +++++----- .../mpchartexample/LineChartActivity1.java | 10 +++++----- .../mpchartexample/LineChartActivity2.java | 10 +++++----- .../mpchartexample/LineChartActivityColored.java | 8 ++++---- .../xxmassdeveloper/mpchartexample/LineChartTime.java | 6 +++--- .../mpchartexample/ListViewBarChartActivity.java | 4 ++-- .../mpchartexample/ListViewMultiChartActivity.java | 2 +- .../mpchartexample/MultiLineChartActivity.java | 10 +++++----- .../mpchartexample/PerformanceLineChart.java | 6 +++--- .../mpchartexample/PieChartActivity.java | 10 +++++----- .../mpchartexample/PiePolylineChartActivity.java | 10 +++++----- .../mpchartexample/RadarChartActivity.java | 4 ++-- .../mpchartexample/RealtimeLineChartActivity.java | 2 +- .../mpchartexample/ScatterChartActivity.java | 10 +++++----- .../mpchartexample/ScrollViewActivity.java | 2 +- .../mpchartexample/StackedBarActivity.java | 10 +++++----- .../mpchartexample/StackedBarActivityNegative.java | 2 +- .../mpchartexample/custom/MyMarkerView.java | 2 +- .../mpchartexample/custom/RadarMarkerView.java | 2 +- .../mpchartexample/custom/StackedBarsMarkerView.java | 2 +- .../mpchartexample/custom/XYMarkerView.java | 2 +- .../mpchartexample/fragments/BarChartFrag.java | 2 +- .../mpchartexample/fragments/ComplexityFragment.java | 2 +- .../mpchartexample/fragments/PieChartFrag.java | 2 +- .../mpchartexample/fragments/ScatterChartFrag.java | 2 +- .../mpchartexample/fragments/SimpleChartDemo.java | 2 +- .../mpchartexample/fragments/SineCosineFragment.java | 2 +- .../mpchartexample/listviewitems/BarChartItem.java | 2 +- .../mpchartexample/listviewitems/LineChartItem.java | 2 +- .../mpchartexample/listviewitems/PieChartItem.java | 2 +- .../mpchartexample/notimportant/MainActivity.java | 2 +- .../mpchartexample/notimportant/MyAdapter.java | 6 +++--- .../mpchartexample/realm/RealmDatabaseActivityBar.java | 2 +- .../realm/RealmDatabaseActivityBubble.java | 2 +- .../realm/RealmDatabaseActivityCandle.java | 2 +- .../realm/RealmDatabaseActivityHorizontalBar.java | 2 +- .../realm/RealmDatabaseActivityLine.java | 2 +- .../mpchartexample/realm/RealmDatabaseActivityPie.java | 2 +- .../realm/RealmDatabaseActivityRadar.java | 2 +- .../realm/RealmDatabaseActivityScatter.java | 2 +- .../mpchartexample/realm/RealmMainActivity.java | 2 +- .../mpchartexample/realm/RealmWikiExample.java | 4 ++-- 56 files changed, 130 insertions(+), 130 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index ba01acd794..f6cffddcbe 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -36,16 +36,16 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_barchart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + mSeekBarX = findViewById(R.id.seekBar1); mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarY = findViewById(R.id.seekBar2); mSeekBarY.setOnSeekBarChangeListener(this); - mChart = (BarChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.getDescription().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 2707d0bd51..c0e3405625 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -56,13 +56,13 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_barchart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarX = findViewById(R.id.seekBar1); + mSeekBarY = findViewById(R.id.seekBar2); - mChart = (BarChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); mChart.setDrawBarShadow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 671ab5abec..204dc1fe64 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -44,17 +44,17 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_barchart); - tvX = (TextView) findViewById(R.id.tvXMax); + tvX = findViewById(R.id.tvXMax); tvX.setTextSize(10); - tvY = (TextView) findViewById(R.id.tvYMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + mSeekBarX = findViewById(R.id.seekBar1); mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarY = findViewById(R.id.seekBar2); mSeekBarY.setOnSeekBarChangeListener(this); - mChart = (BarChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); mChart.getDescription().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index 56add4458e..82b039909f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -44,11 +44,11 @@ protected void onCreate(Bundle savedInstanceState) { mSinusData = FileUtils.loadBarEntriesFromAssets(getAssets(), "othersine.txt"); - tvX = (TextView) findViewById(R.id.tvValueCount); + tvX = findViewById(R.id.tvValueCount); - mSeekBarX = (SeekBar) findViewById(R.id.seekbarValues); + mSeekBarX = findViewById(R.id.seekbarValues); - mChart = (BarChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setDrawBarShadow(false); mChart.setDrawValueAboveBar(true); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 743b7e735d..7d6bd44896 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -37,7 +37,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_barchart_noseekbar); mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - mChart = (BarChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setBackgroundColor(Color.WHITE); mChart.setExtraTopOffset(-30f); mChart.setExtraBottomOffset(10f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java index 0ecc1e9c93..bc1750381e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -44,16 +44,16 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_bubblechart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + mSeekBarX = findViewById(R.id.seekBar1); mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarY = findViewById(R.id.seekBar2); mSeekBarY.setOnSeekBarChangeListener(this); - mChart = (BubbleChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.getDescription().setEnabled(false); mChart.setOnChartValueSelectedListener(this); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java index bd8dde108f..54eb768a7b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -39,16 +39,16 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_candlechart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + mSeekBarX = findViewById(R.id.seekBar1); mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarY = findViewById(R.id.seekBar2); mSeekBarY.setOnSeekBarChangeListener(this); - mChart = (CandleStickChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setBackgroundColor(Color.WHITE); mChart.getDescription().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index fadfbf175f..e4d8fb2e3b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -48,7 +48,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_combined); - mChart = (CombinedChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.getDescription().setEnabled(false); mChart.setBackgroundColor(Color.WHITE); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index cb979e80e9..4a278c398e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -39,11 +39,11 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarX = findViewById(R.id.seekBar1); + mSeekBarY = findViewById(R.id.seekBar2); mSeekBarX.setProgress(45); mSeekBarY.setProgress(100); @@ -51,7 +51,7 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setOnSeekBarChangeListener(this); mSeekBarX.setOnSeekBarChangeListener(this); - mChart = (LineChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setViewPortOffsets(0, 0, 0, 0); mChart.setBackgroundColor(Color.rgb(104, 241, 175)); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java index 970ba12909..d3551068c1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java @@ -43,7 +43,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_draw_chart); - mChart = (LineChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); // listener for selecting and drawing mChart.setOnChartValueSelectedListener(this); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index 2875b89d7e..f8f64c374f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -32,7 +32,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart_noseekbar); - mChart = (LineChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); mChart.getDescription().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java index d824167d6b..9109d5d29c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java @@ -31,7 +31,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart_noseekbar); - mChart = (LineChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setBackgroundColor(Color.WHITE); mChart.setGridBackgroundColor(mFillColor); mChart.setDrawGridBackground(true); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java index a524f36a43..38a228b322 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java @@ -37,7 +37,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_piechart_half); - mChart = (PieChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setBackgroundColor(Color.WHITE); moveOffScreen(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index d68b75cc15..95e138aade 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -46,13 +46,13 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_horizontalbarchart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarX = findViewById(R.id.seekBar1); + mSeekBarY = findViewById(R.id.seekBar2); - mChart = (HorizontalBarChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); // mChart.setHighlightEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index f87a9a8098..4ad4e691ef 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -44,11 +44,11 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarX = findViewById(R.id.seekBar1); + mSeekBarY = findViewById(R.id.seekBar2); mSeekBarX.setProgress(45); mSeekBarY.setProgress(100); @@ -56,7 +56,7 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setOnSeekBarChangeListener(this); mSeekBarX.setOnSeekBarChangeListener(this); - mChart = (LineChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 6cf7150c97..85d213e351 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -54,11 +54,11 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarX = findViewById(R.id.seekBar1); + mSeekBarY = findViewById(R.id.seekBar2); mSeekBarX.setProgress(45); mSeekBarY.setProgress(100); @@ -66,7 +66,7 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setOnSeekBarChangeListener(this); mSeekBarX.setOnSeekBarChangeListener(this); - mChart = (LineChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setOnChartGestureListener(this); mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 79f40c4e07..e2a381ff91 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -44,10 +44,10 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + mSeekBarX = findViewById(R.id.seekBar1); + mSeekBarY = findViewById(R.id.seekBar2); mSeekBarX.setProgress(45); mSeekBarY.setProgress(100); @@ -55,7 +55,7 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setOnSeekBarChangeListener(this); mSeekBarX.setOnSeekBarChangeListener(this); - mChart = (LineChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); // no description text diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index 22984b4b6e..39730d55b1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -27,10 +27,10 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_colored_lines); - mCharts[0] = (LineChart) findViewById(R.id.chart1); - mCharts[1] = (LineChart) findViewById(R.id.chart2); - mCharts[2] = (LineChart) findViewById(R.id.chart3); - mCharts[3] = (LineChart) findViewById(R.id.chart4); + mCharts[0] = findViewById(R.id.chart1); + mCharts[1] = findViewById(R.id.chart2); + mCharts[2] = findViewById(R.id.chart3); + mCharts[3] = findViewById(R.id.chart4); mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Bold.ttf"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 30e5e2a978..6bf96f02a7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -45,14 +45,14 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart_time); - tvX = (TextView) findViewById(R.id.tvXMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + tvX = findViewById(R.id.tvXMax); + mSeekBarX = findViewById(R.id.seekBar1); mSeekBarX.setProgress(100); tvX.setText("100"); mSeekBarX.setOnSeekBarChangeListener(this); - mChart = (LineChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); // no description text mChart.getDescription().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index 7ee212ff60..54218a53da 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java @@ -41,7 +41,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_listview_chart); - ListView lv = (ListView) findViewById(R.id.listView1); + ListView lv = findViewById(R.id.listView1); ArrayList list = new ArrayList(); @@ -73,7 +73,7 @@ public View getView(int position, View convertView, ViewGroup parent) { convertView = LayoutInflater.from(getContext()).inflate( R.layout.list_item_barchart, null); - holder.chart = (BarChart) convertView.findViewById(R.id.chart); + holder.chart = convertView.findViewById(R.id.chart); convertView.setTag(holder); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java index 0e273596fe..0c9f132f03 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java @@ -45,7 +45,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_listview_chart); - ListView lv = (ListView) findViewById(R.id.listView1); + ListView lv = findViewById(R.id.listView1); ArrayList list = new ArrayList(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index 19f7bae938..e6acf01670 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -39,16 +39,16 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + mSeekBarX = findViewById(R.id.seekBar1); mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarY = findViewById(R.id.seekBar2); mSeekBarY.setOnSeekBarChangeListener(this); - mChart = (LineChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java index 0763f7f88a..a2d4becadc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -30,15 +30,15 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_performance_linechart); - mTvCount = (TextView) findViewById(R.id.tvValueCount); - mSeekBarValues = (SeekBar) findViewById(R.id.seekbarValues); + mTvCount = findViewById(R.id.tvValueCount); + mSeekBarValues = findViewById(R.id.seekbarValues); mTvCount.setText("500"); mSeekBarValues.setProgress(500); mSeekBarValues.setOnSeekBarChangeListener(this); - mChart = (LineChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setDrawGridBackground(false); // no description text diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index eb60524cb0..0252ff8ff0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -48,15 +48,15 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_piechart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarX = findViewById(R.id.seekBar1); + mSeekBarY = findViewById(R.id.seekBar2); mSeekBarX.setProgress(4); mSeekBarY.setProgress(10); - mChart = (PieChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setUsePercentValues(true); mChart.getDescription().setEnabled(false); mChart.setExtraOffsets(5, 10, 5, 5); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index 44fbf06c89..c0199723d9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -48,18 +48,18 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_piechart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarX = findViewById(R.id.seekBar1); + mSeekBarY = findViewById(R.id.seekBar2); mSeekBarY.setProgress(10); mSeekBarX.setOnSeekBarChangeListener(this); mSeekBarY.setOnSeekBarChangeListener(this); - mChart = (PieChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setUsePercentValues(true); mChart.getDescription().setEnabled(false); mChart.setExtraOffsets(5, 10, 5, 5); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java index f1fd4cc891..d354886f6e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java @@ -38,12 +38,12 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_radarchart_noseekbar); - TextView tv = (TextView) findViewById(R.id.textView); + TextView tv = findViewById(R.id.textView); tv.setTypeface(mTfLight); tv.setTextColor(Color.WHITE); tv.setBackgroundColor(Color.rgb(60, 65, 82)); - mChart = (RadarChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setBackgroundColor(Color.rgb(60, 65, 82)); mChart.getDescription().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 13346bf631..f3661628d0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -36,7 +36,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_realtime_linechart); - mChart = (LineChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); // enable description text diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index 29904a95c9..f0f889e194 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -42,16 +42,16 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_scatterchart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + mSeekBarX = findViewById(R.id.seekBar1); mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarY = findViewById(R.id.seekBar2); mSeekBarY.setOnSeekBarChangeListener(this); - mChart = (ScatterChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.getDescription().setEnabled(false); mChart.setOnChartValueSelectedListener(this); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java index ff098c32e0..1aeb7f0f0c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java @@ -26,7 +26,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_scrollview); - mChart = (BarChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.getDescription().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 70afd7c4c6..9951060177 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -44,16 +44,16 @@ protected void onCreate(Bundle savedInstanceState) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_barchart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + mSeekBarX = findViewById(R.id.seekBar1); mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarY = findViewById(R.id.seekBar2); mSeekBarY.setOnSeekBarChangeListener(this); - mChart = (BarChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); mChart.getDescription().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index d5e0f8c885..c1d64a106b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -46,7 +46,7 @@ protected void onCreate(Bundle savedInstanceState) { setTitle("Age Distribution Austria"); - mChart = (HorizontalBarChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); mChart.getDescription().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java index 8d97346195..ef20bda3b7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java @@ -24,7 +24,7 @@ public class MyMarkerView extends MarkerView { public MyMarkerView(Context context, int layoutResource) { super(context, layoutResource); - tvContent = (TextView) findViewById(R.id.tvContent); + tvContent = findViewById(R.id.tvContent); } // callbacks everytime the MarkerView is redrawn, can be used to update the diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java index 1a350f45ac..12b473f7d0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java @@ -28,7 +28,7 @@ public class RadarMarkerView extends MarkerView { public RadarMarkerView(Context context, int layoutResource) { super(context, layoutResource); - tvContent = (TextView) findViewById(R.id.tvContent); + tvContent = findViewById(R.id.tvContent); tvContent.setTypeface(Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf")); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java index d0781b2ee2..487705bb7d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java @@ -24,7 +24,7 @@ public class StackedBarsMarkerView extends MarkerView { public StackedBarsMarkerView(Context context, int layoutResource) { super(context, layoutResource); - tvContent = (TextView) findViewById(R.id.tvContent); + tvContent = findViewById(R.id.tvContent); } // callbacks everytime the MarkerView is redrawn, can be used to update the diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java index 177527b3bf..0475bdd038 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java @@ -29,7 +29,7 @@ public XYMarkerView(Context context, IAxisValueFormatter xAxisValueFormatter) { super(context, R.layout.custom_marker_view); this.xAxisValueFormatter = xAxisValueFormatter; - tvContent = (TextView) findViewById(R.id.tvContent); + tvContent = findViewById(R.id.tvContent); format = new DecimalFormat("###.0"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java index 1cdba2735f..655fc6bb25 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java @@ -60,7 +60,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa xAxis.setEnabled(false); // programatically add the chart - FrameLayout parent = (FrameLayout) v.findViewById(R.id.parentLayout); + FrameLayout parent = v.findViewById(R.id.parentLayout); parent.addView(mChart); return v; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java index caf9e3e295..b960e9ae0c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java @@ -25,7 +25,7 @@ public static Fragment newInstance() { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag_simple_line, container, false); - mChart = (LineChart) v.findViewById(R.id.lineChart1); + mChart = v.findViewById(R.id.lineChart1); mChart.getDescription().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java index 7888aa632f..946532ac40 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java @@ -28,7 +28,7 @@ public static Fragment newInstance() { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag_simple_pie, container, false); - mChart = (PieChart) v.findViewById(R.id.pieChart1); + mChart = v.findViewById(R.id.pieChart1); mChart.getDescription().setEnabled(false); Typeface tf = Typeface.createFromAsset(getActivity().getAssets(), "OpenSans-Light.ttf"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java index 90aba051d3..b8a3f0f324 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java @@ -28,7 +28,7 @@ public static Fragment newInstance() { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag_simple_scatter, container, false); - mChart = (ScatterChart) v.findViewById(R.id.scatterChart1); + mChart = v.findViewById(R.id.scatterChart1); mChart.getDescription().setEnabled(false); Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java index 7af599e927..ee64ffdfce 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java @@ -29,7 +29,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_awesomedesign); - ViewPager pager = (ViewPager) findViewById(R.id.pager); + ViewPager pager = findViewById(R.id.pager); pager.setOffscreenPageLimit(3); PageAdapter a = new PageAdapter(getSupportFragmentManager()); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java index 60173c368f..7e425172fb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java @@ -25,7 +25,7 @@ public static Fragment newInstance() { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag_simple_line, container, false); - mChart = (LineChart) v.findViewById(R.id.lineChart1); + mChart = v.findViewById(R.id.lineChart1); mChart.getDescription().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java index cf9b4b553d..c09297a391 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java @@ -39,7 +39,7 @@ public View getView(int position, View convertView, Context c) { convertView = LayoutInflater.from(c).inflate( R.layout.list_item_barchart, null); - holder.chart = (BarChart) convertView.findViewById(R.id.chart); + holder.chart = convertView.findViewById(R.id.chart); convertView.setTag(holder); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java index f988844fce..107930af2a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java @@ -40,7 +40,7 @@ public View getView(int position, View convertView, Context c) { convertView = LayoutInflater.from(c).inflate( R.layout.list_item_linechart, null); - holder.chart = (LineChart) convertView.findViewById(R.id.chart); + holder.chart = convertView.findViewById(R.id.chart); convertView.setTag(holder); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java index db6ba32416..5503018792 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java @@ -47,7 +47,7 @@ public View getView(int position, View convertView, Context c) { convertView = LayoutInflater.from(c).inflate( R.layout.list_item_piechart, null); - holder.chart = (PieChart) convertView.findViewById(R.id.chart); + holder.chart = convertView.findViewById(R.id.chart); convertView.setTag(holder); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index 45ca879ee3..d994f87c96 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -149,7 +149,7 @@ protected void onCreate(Bundle savedInstanceState) { MyAdapter adapter = new MyAdapter(this, objects); - ListView lv = (ListView) findViewById(R.id.listView1); + ListView lv = findViewById(R.id.listView1); lv.setAdapter(adapter); lv.setOnItemClickListener(this); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java index 8395ce2720..5b424e88a5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java @@ -39,9 +39,9 @@ public View getView(int position, View convertView, ViewGroup parent) { holder = new ViewHolder(); convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item, null); - holder.tvName = (TextView) convertView.findViewById(R.id.tvName); - holder.tvDesc = (TextView) convertView.findViewById(R.id.tvDesc); - holder.tvNew = (TextView) convertView.findViewById(R.id.tvNew); + holder.tvName = convertView.findViewById(R.id.tvName); + holder.tvDesc = convertView.findViewById(R.id.tvDesc); + holder.tvNew = convertView.findViewById(R.id.tvNew); convertView.setTag(holder); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java index 4f1d42f1db..1e5d5cb8d5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java @@ -30,7 +30,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_barchart_noseekbar); - mChart = (BarChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); setup(mChart); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java index fb28c3f08a..ad65a3de18 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java @@ -30,7 +30,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_bubblechart_noseekbar); - mChart = (BubbleChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); setup(mChart); mChart.getXAxis().setDrawGridLines(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java index 17c0d8d2f0..96fbade855 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java @@ -31,7 +31,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_candlechart_noseekbar); - mChart = (CandleStickChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); setup(mChart); mChart.getAxisLeft().setDrawGridLines(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java index 35138d656d..b5e3345134 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java @@ -31,7 +31,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_horizontalbarchart_noseekbar); - mChart = (HorizontalBarChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); setup(mChart); mChart.getAxisLeft().setAxisMinimum(0f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java index 24982cf7fa..7d2e49fdff 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java @@ -31,7 +31,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart_noseekbar); - mChart = (LineChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); setup(mChart); mChart.getAxisLeft().setAxisMaximum(150f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java index 2936379d55..9fad49c617 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java @@ -32,7 +32,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_piechart_noseekbar); - mChart = (PieChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); setup(mChart); mChart.setCenterText(generateCenterSpannableText()); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java index 411f4b6ac9..02f3ac0492 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java @@ -30,7 +30,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_radarchart_noseekbar); - mChart = (RadarChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); setup(mChart); mChart.getYAxis().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java index d2e2fd70f5..9da64cbf1d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java @@ -30,7 +30,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_scatterchart_noseekbar); - mChart = (ScatterChart) findViewById(R.id.chart1); + mChart = findViewById(R.id.chart1); setup(mChart); mChart.getAxisLeft().setDrawGridLines(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java index 07c54ad88c..1776a8bd7e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java @@ -51,7 +51,7 @@ protected void onCreate(Bundle savedInstanceState) { MyAdapter adapter = new MyAdapter(this, objects); - ListView lv = (ListView) findViewById(R.id.listView1); + ListView lv = findViewById(R.id.listView1); lv.setAdapter(adapter); lv.setOnItemClickListener(this); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java index 25011b0e2b..f223be6093 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java @@ -37,8 +37,8 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_realm_wiki); - lineChart = (LineChart) findViewById(R.id.lineChart); - barChart = (BarChart) findViewById(R.id.barChart); + lineChart = findViewById(R.id.lineChart); + barChart = findViewById(R.id.barChart); setup(lineChart); setup(barChart); From 536a35a6fd045e1279edf72f8afad736de3d93a1 Mon Sep 17 00:00:00 2001 From: almic Date: Thu, 10 May 2018 11:03:03 -0600 Subject: [PATCH 213/291] docs(README): Update & simplify README - Re-ordered some of the sections - Simplified some sections - Reformatted here and there - Added a few emojis, how cute! The goal of this change was to hopefully further reduce the amount of issue support/ question spam. By moving the sections around to show the more important parts higher up, like usage and documentation links, I hope this will make it easier for people to find the documentation. --- README.md | 243 +++++++++++++++-------------- design/feature_graphic_smaller.png | Bin 0 -> 22385 bytes 2 files changed, 127 insertions(+), 116 deletions(-) create mode 100644 design/feature_graphic_smaller.png diff --git a/README.md b/README.md index 8ffafdc4c4..b651ea3157 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ -[![Twitter](https://img.shields.io/badge/Twitter-@PhilippJahoda-blue.svg?style=flat)](http://twitter.com/philippjahoda) -[![Twitter](https://img.shields.io/badge/Twitter-@mpandroidchart-blue.svg?style=flat)](http://twitter.com/mpandroidchart) -[![Android Arsenal](http://img.shields.io/badge/Android%20Arsenal-MPAndroidChart-orange.svg?style=flat)](http://android-arsenal.com/details/1/741) -[![Release](https://img.shields.io/github/release/PhilJay/MPAndroidChart.svg?style=flat)](https://jitpack.io/#PhilJay/MPAndroidChart) [![API](https://img.shields.io/badge/API-8%2B-green.svg?style=flat)](https://android-arsenal.com/api?level=8) +**Remember: _It's all about the looks._** -Remember: *It's all about the looks.* +![banner](https://raw.github.com/PhilJay/MPChart/master/design/feature_graphic_smaller.png) -![alt tag](https://raw.github.com/PhilJay/MPChart/master/design/feature_graphic.png) +[![Release](https://img.shields.io/github/release/PhilJay/MPAndroidChart.svg?style=flat)](https://jitpack.io/#PhilJay/MPAndroidChart) +[![API](https://img.shields.io/badge/API-8%2B-green.svg?style=flat)](https://android-arsenal.com/api?level=8) +[![Android Arsenal](http://img.shields.io/badge/Android%20Arsenal-MPAndroidChart-orange.svg?style=flat)](http://android-arsenal.com/details/1/741) +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/PhilJay/MPAndroidChart?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) +[![Twitter](https://img.shields.io/badge/Twitter-@mpandroidchart-blue.svg?style=flat)](http://twitter.com/mpandroidchart) -[**MPAndroidChart**](https://github.com/PhilJay/MPAndroidChart) :zap: is a powerful & easy to use chart library for Android. It runs on [API level 8](http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels) and upwards. +:zap: A powerful & easy to use chart library for Android :zap: -As an additional feature, this library allows cross-platform development between Android and iOS as an iOS version of this library is also available: [**Charts**](https://github.com/danielgindi/Charts) :zap: +[**Charts**](https://github.com/danielgindi/Charts) is the iOS version of this library -[Enterprise Solution 5% Discount Coupon | SciChart](http://store.scichart.com?productTab=Android&CouponCode=MPANDROIDCHART) ------ +## [Enterprise Solution 5% Discount Coupon | SciChart](http://store.scichart.com?productTab=Android&CouponCode=MPANDROIDCHART) @@ -24,177 +24,178 @@ MPAndroidChart is free software, as a result **dynamic & realtime data is not of All MPAndroidChart users are entitled to a special **discount of 5%** off the SciChart store, using the following discount code: **MPANDROIDCHART** +
    +## Usage :chart_with_upwards_trend: -
    +**Gradle** +- **Project level `build.gradle`** +```gradle +allprojects { + repositories { + maven { url 'https://jitpack.io/' } + } +} +``` +- **App level `build.gradle`** +```gradle +dependencies { + implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3' +} +``` -Donations ------ +**Maven** -**This project needs you!** If you would like to support this project's further development, the creator of this project or the continuous maintenance of this project, **feel free to donate**. Your donation is highly appreciated (and I love food, coffee and beer). Thank you! +```xml + + + jitpack.io + https://jitpack.io + -**My Bitcoin Wallet** (Bitcoin only) -1G8G6tqQ3oh38BvDH3xq8o6gGVMvBTkcUg + + + com.github.PhilJay + MPAndroidChart + v3.0.3 + +``` -**My Ethereum Wallet** (Ethereum only) +
    -0x04ef098bf9f91871391363e3caf791afa3adc39b +## Documentation :notebook_with_decorative_cover: -**PayPal** +See the [**documentation**](https://github.com/PhilJay/MPAndroidChart/wiki) for examples and general use of MPAndroidChart. - - [**Donate 5 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7G52RA87ED8NY): Thank's for creating this project, here's a coffee (or some beer) for you! - - [**Donate 10 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4C9TPE67F5PUQ): Wow, I am stunned. Let me take you to the movies! - - [**Donate 15 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YKMPTFMVK3JMC): I really appreciate your work, let's grab some lunch! - - [**Donate 25 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H9JA4QX7UHXCY): That's some awesome stuff you did right there, dinner is on me! - - Or you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome! +See the [**javadocs**](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.3/javadoc/) for more advanced documentation. +
    -Spread the word ------ +## Examples :eyes: +Download the [MPAndroidChart Example App](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample) or look at the [source code](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample). -If you like this library, please tell others about it :two_hearts: :two_hearts: +[![ScreenShot](https://github.com/PhilJay/MPAndroidChart/blob/master/design/video_thumbnail.png)](https://www.youtube.com/watch?v=ufaK_Hd6BpI) - - - +
    - - []()Follow me on **Twitter**: [**@PhilippJahoda**](https://twitter.com/PhilippJahoda) - - Look me up on **StackOverflow**: [**Philipp Jahoda**](http://stackoverflow.com/users/1590502/philipp-jahoda) +## Questions & Issues :thinking: +This repository's issue tracker is only for bugs and feature requests. The maintainers ask that you refrain from asking questions about how to use MPAndroidChart through the issue tracker. -Demo ------ +Please read the [**documentation**](https://github.com/PhilJay/MPAndroidChart/wiki) first, then ask all your questions on [stackoverflow.com](https://stackoverflow.com/questions/tagged/mpandroidchart) for the fastest answer. -For a brief overview of the most important features, please download the **PlayStore Demo** [**MPAndroidChart Example.apk**](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample) and try it out. The corresponding code for the demo-application is also included in this repository inside the **MPChartExample folder**. +
    -[![ScreenShot](https://github.com/PhilJay/MPAndroidChart/blob/master/design/video_thumbnail.png)](https://www.youtube.com/watch?v=ufaK_Hd6BpI) +## Donations :heart: -Questions & Issues ------ +**This project needs you!** If you would like to support this project's further development, the creator of this project or the continuous maintenance of this project, **feel free to donate**. Your donation is highly appreciated (and I love food, coffee and beer). Thank you! -If you are having questions or problems, you should: +**My Bitcoin Wallet** (Bitcoin only) - - **Review your code**. Make absolutely sure that everything is correct on your side. - - Make sure you are using the **latest version** of the library. Check the [**release-section**](https://github.com/PhilJay/MPAndroidChart/releases). - - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.3/javadoc/) - - Search or open questions on [**stackoverflow**](https://stackoverflow.com/search?q=mpandroidchart) with the `mpandroidchart` tag - - Search [**known issues**](https://github.com/PhilJay/MPAndroidChart/issues) for your problem (open and closed) - - Create new issues (please :fire: **search known issues before** :fire:, do not create duplicate issues) - - Check this: ["how not to contribute"](https://github.com/PhilJay/MPAndroidChart/wiki/How-not-to-contribute) - -Please do not expect answers to your questions if you have not considered all above mentioned approaches in advance. +1G8G6tqQ3oh38BvDH3xq8o6gGVMvBTkcUg -Features ------ -You can have a look at the core features of this libary [**here**](https://github.com/PhilJay/MPAndroidChart/wiki/Core-Features). - -Usage ------ +**My Ethereum Wallet** (Ethereum only) -In order to use the library, there are 4 different options: +0x04ef098bf9f91871391363e3caf791afa3adc39b -**1. Gradle dependency** (recommended) +**PayPal** - - Add the following to your project level `build.gradle`: - -```gradle -allprojects { - repositories { - maven { url "https://jitpack.io/" } - } -} -``` - - Add this to your app `build.gradle`: - -```gradle -dependencies { - implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3' -} -``` +- [**Donate 5 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7G52RA87ED8NY): Thank's for creating this project, here's a coffee (or some beer) for you! +- [**Donate 10 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4C9TPE67F5PUQ): Wow, I am stunned. Let me take you to the movies! +- [**Donate 15 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YKMPTFMVK3JMC): I really appreciate your work, let's grab some lunch! +- [**Donate 25 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H9JA4QX7UHXCY): That's some awesome stuff you did right there, dinner is on me! +- Or you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome! -**2. Maven** -- Add the following to the `` section of your `pom.xml`: +
    - ```xml - - jitpack.io - https://jitpack.io - -``` -- Add the following to the `` section of your `pom.xml`: +## Social Media :fire: - ```xml - - com.github.PhilJay - MPAndroidChart - v3.0.3 - -``` - -**3. clone whole repository** (not recommended) +If you like this library, please tell others about it :two_hearts: :two_hearts: + +[![Share on Twitter](https://github.com/PhilJay/MPAndroidChart/blob/master/design/twitter_icon.png)](https://twitter.com/intent/tweet?text=Check%20out%20the%20awesome%20MPAndroidChart%20library%20on%20Github:%20https://github.com/PhilJay/MPAndroidChart) +[![Share on Google+](https://github.com/PhilJay/MPAndroidChart/blob/master/design/googleplus_icon.png)](https://plus.google.com/share?url=https://github.com/PhilJay/MPAndroidChart) +[![Share on Facebook](https://github.com/PhilJay/MPAndroidChart/blob/master/design/facebook_icon.png)](https://www.facebook.com/sharer/sharer.php?u=https://github.com/PhilJay/MPAndroidChart) -Documentation ------ -For a **detailed documentation** :notebook_with_decorative_cover:, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.3/javadoc/). +You can follow the creator on Twitter [**@PhilippJahoda**](https://twitter.com/PhilippJahoda) -Furthermore, you can also rely on the [**MPChartExample**](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample) folder and check out the example code in that project. The corresponding application to the example project is also [**available in the Google PlayStore**](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample). -You can also join others in a discussion on [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/PhilJay/MPAndroidChart?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) - -Chart types ------ +Philipp is also on [StackOverflow](http://stackoverflow.com/users/1590502/philipp-jahoda) + +
    + +## More Examples :+1: + +
    + +**LineChart (with legend, simple design)** - - **LineChart (with legend, simple design)** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart4.png) - - **LineChart (with legend, simple design)** +

    + +**LineChart (with legend, simple design)** + ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart3.png) +

    + +**LineChart (cubic lines)** - - **LineChart (cubic lines)** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/cubiclinechart.png) +

    - - **LineChart (gradient fill)** -![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/line_chart_gradient.png) +**LineChart (gradient fill)** - - **Combined-Chart (bar- and linechart in this case)** -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/combined_chart.png) +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/line_chart_gradient.png) +

    - - **BarChart (with legend, simple design)** +**BarChart (with legend, simple design)** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_barchart3.png) +

    - - **BarChart (grouped DataSets)** +**BarChart (grouped DataSets)** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/groupedbarchart.png) +

    - - **Horizontal-BarChart** +**Horizontal-BarChart** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/horizontal_barchart.png) +

    + +**Combined-Chart (bar- and linechart in this case)** +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/combined_chart.png) +

    - - **PieChart (with selection, ...)** +**PieChart (with selection, ...)** ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/simpledesign_piechart1.png) +

    - - **ScatterChart** (with squares, triangles, circles, ... and more) +**ScatterChart** (with squares, triangles, circles, ... and more) ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/scatterchart.png) +

    - - **CandleStickChart** (for financial data) +**CandleStickChart** (for financial data) ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/candlestickchart.png) +

    - - **BubbleChart** (area covered by bubbles indicates the yValue) +**BubbleChart** (area covered by bubbles indicates the yValue) ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/bubblechart.png) +

    - - **RadarChart** (spider web chart) +**RadarChart** (spider web chart) ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/radarchart.png) +
    + +# License :page_facing_up: -License -======= Copyright 2018 Philipp Jahoda Licensed under the Apache License, Version 2.0 (the "License"); @@ -209,4 +210,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -**Special thanks** to [danielgindi](https://github.com/danielgindi), [mikegr](https://github.com/mikegr), [tony](https://github.com/tonypatino-monoclesociety), [almic](https://github.com/almic) and [jitpack.io](https://github.com/jitpack-io) for their contributions to this project. +
    + +## Special Thanks :heart: + +These people rock! + +- [danielgindi](https://github.com/danielgindi) - Daniel Gindi +- [mikegr](https://github.com/mikegr) - Michael Greifeneder +- [tony](https://github.com/tonypatino-monoclesociety) - Tony +- [almic](https://github.com/almic) - Mick A. +- [jitpack.io](https://github.com/jitpack-io) - JitPack.io diff --git a/design/feature_graphic_smaller.png b/design/feature_graphic_smaller.png new file mode 100644 index 0000000000000000000000000000000000000000..c1e5db57999c7bf354399d7d8e5d52185f0ab108 GIT binary patch literal 22385 zcmcG#RZv_()GeIgZb5^4a0^bb1lItAyAx!BI|O%Ua0%|N!EJDNcXzj8;PUDJcI&?U zFXwc1_wHW1`gHfHz1Ip;R+K_V`Hb@6!v}O3=^rW|KEP7Emj{s$-k(@8L7(4WL{1W# zPO3n2Cs!i}vkzjXKw~os8CxR@GZixFV*_zzk)#o^W7lQAgj4-Be#2$T4o3<9`<2oO7e0}Sf}%YrRvZRdM*1Rs z%_h5+>n77;e>8b0)6><$rRZG9*LMKj)!dH%VbyWtAmhP1h6NV|nKBSd9Emc}6h^S@ z&wrM%#6SK=#d|sD!+$qoq5O9X68wL)PzEahPvf2I3sXaw7>S%tWAr2I${12QA6vu#69Fs}Bvf79_ z@kM<|!-v%TVQCgolF~;&N$v+q8uVdCa#YH|{8aM1h_8uJ<@i1K%RnM?m5?tMHNTL~ zZfEiP-Y??3m}{;u`^B^*CXqH3I;lig!#j~Y79SVCkI?+izB2unJ;rS z^P2XULW&(i?3CO%#x?d1PI-`qgtI8byF+SA(;zU^oN*#92s4Z!LP*`|8 zsEWY@7=Wa*T4m5&X&ZwRFEvf);0_^H8=w9sN8d5af8QqT1~$nm5eqnj1_YV>cJ2y9 z1DTcgFrPJ@-CacXi7OGQ?Et6TD8W_f^+)jKNo{cbXY_OrpZaaMSFD$plt3qdC((zz z5}$>yGBi5`2(uWcz3#@!c11(4O8eA5EQ`1hXu=I#1=j21~Qd;3lEb1bQT0t6`i$y6=7~Kj-ojXk#z3u3M=?^DIWX3%cWumKN9!AdMJ;1Y$#v!V4VSGo2~(U{m5w_T z(vSiQB@z?`KsFk1^MiS$&nEkajC)*U3Lm?nDkuHoT-|();Mw&0DRRU4d18_tX`}L& zUFl2ZI$9h!k50ygFn5Gxn`_#LGbtI4igQq=RzOb+vslkg^cQYMj7@w+zjU9&@jH2H4 z<=Lr!CaMwMH)+A!=u9qZwweufjsyK`>v-fg;)X^_GiS-NeF`UhVSSOiFe$^?tG(?l zNo$Cd?%!R{3(?E764`%%Le%&L{4Oddw>RAZNzycbgyPhi3UIW_OcH+}oK0$w+g<6M z-e9cb$HQ+vzkLsD{vfX03;oSoR&WW2)Ma_U;Q06wTzVa(%NG~U*mSDOUp-9ia~0HU zgX4BGJ3D{M+fR4T?=ttd=MzN^{t=RQi<9H#js$VKfsd*El^~nZ?)U;nD{2j;`9+utJ0|cgoaWJg zi8wS7(DEV$>@~zjsNN*=R33GG`*IZHNYfX|QTjr$-}%NMZ-^u1s}nlOSorbv1&`lr z{f4FnH_&R}0$n=YsZ@VG{df06`dnw)Y^O`9FSoR#yRtZJp#s@Smg`Z?oNN202%a3^DU(svsWTD^#46OFTQX^xsy7DR`hN6p;1Xv4x5UnTM_dH zX6W2BT*m{R3whu!o=DPQ@gtAkxyoKyd6KYS`JWo=GD5L(lK3dy+~Hib49sm5rC90Z z4ZXH+G;*V+c(_>~7~-XeJDcQ%TN$M$hq-+>Q%98|we zPB&J4L;XTot1lDhU3y}D?tOPs=|^2!ZZ2*&JZ+WM80WhFsJ*};6~&t_-kzw3MCmRS zz<$|QwYhfd#GC!54T{LmM$5&Hhtp*C@KyhAgo4$FWq3~!i-@rE8=%OsId8D0*F7nqV}Owg9%cFmrZG2e;fIo$?H$^LiG7*-fDDt=GT9mDr-O9-~LKWUBjwaAC#g1h|6Eh%V z{OreLx;2WUmkfG_D)0VGNCpq|=MsfPI+9es8FF-w<*bOx=+|U7dXG+*06|7+P&-Fvt%+3s;VxG7W}uGX@ml zHeu~%ESU(<%affYP1<2=*iJni$ot7<*yHb_aTw^l-<;3HBA`JkYe{Wd&d~4DIb50l zIS&dN7*W2Foa!HTq))>TPd=89(pzhNC$BJEL2^W^&2R>X`B8A(l#O_>B0R%QBvUrx zzMc^EU%T&dZd+3e`)!A9CB{ulku?%2_O1CFhdu3Y_sJ)efoBaDhRUb15ficFZh}Cg z?prCIb#l4US`$vDbY!SE?JWfG)FIQ`B3qlDYU+x~_gwEYSmB*~0pv5?;@ z#Vz7^0WoK_pyqO7G~lu$RMy%5c2hotf!rX8AByH-nvF~yp~&y-t5?k5OydK$6 z$ge2@X#*YiQO9~cg7Uv@+8|!QQrg}P&wA%RTo&YtZsB-7+xghK&qsLX;gTC5thmFC z29myS_BI|z6q$=(%oa_ZjIi@A~)c zha;oc8+!`(&(79Mo7oeb*|Uo8BB<$i?NBw)0&T6bIV*=K6>P&N=M|F54QyDGtW0%p z7M;7gC~kxG!M|rJr6)pvSIi<(S{;cc`fG5AerFUzDlA1JKR#vRXo(bxJph`OCTM^A zGT!Bb~_);S-f zGhpTWI?5M?W8g;Ec350_6ABa^S!Wgto|}q#oPf1w-vAMrHn6<}VYHj$Ddi)^0Uc!J zNiUZ8kxS{`E!abtvPT ze3zv(-iG?#2?rG^+ta`p0>z^KPS<82plu}vMc2(sYz4Qjz%{^Jvw^haVao^VlLVRB zQR9~(-fy>O37&clWOO|gi*;(0y0y(S?py2ZTLzPP)5X*476kcnW)xq`1T(L(&dG1fE0584T&0d*Kf5%$RgBLw0o9lO}>M4v@@%RUFc0rAdW1dB!E*q`R>Leb;DGYm9&BLwm#64kh8pYqCtsw1Bh&v>$l z4^2F;CL30H%pIPU=NkjJ*P1J6Z&4mC08np&myf4THdOu3BI@6S$0o)T`9@#bh&)BA!REjZPY2gK7R2%arRiMpln&B-~a46-Oc8uLK3O{i4X zK^+D8K1>n^p0$jq4VSC*L3iwmARU_RByRe#vw_jf9Db#hlK{Fi?$~~7{sm(QDg1R> z_!;V^_FV@4AH;%3zJtSq)7`K7a$!COMrves4I9a`RjolLT_dJ`jEIH3HnET3A1m@5 z4_s?a59(g!Np1@}95UfC9h*lJm;g#7EW(Hzg~o|l+L;suY~xuEnH&_X^fB`}jF~g~ zXoiE5{wPN_@yma$@-GM)^P;;p2l`Iz!tCL6@>k}9sH4Ih6WjuNk<)60S8heW{IZd; zVWwVdQ|N*0>jj4H_BFG~8pE66vL#GR$tWaE1N`l8H_ znt$_W<5Wq$aRM~@tQ%cTxb!9QCyiq{y`^i>)1W=6mzLJg0SN;6Ff^e>`R9Gnbm(U9 z1B-y+TX)AEInXh+zDUHphAyw>d=qM$1 zQyNVAN}y3u?EO%uSce;41}*(GZx-%ll7+@#-+v3T&VeKO{FcWUd+vyFU5td~bPD zEb@;nUPyOKYB($gO#%VN2KKDSv-dk?)qM(q31b-BB&lz!gJ! zWq!+U?oCmH&zsZ)Jov6KBl5+&X7js^pJi$3sry}9W z9#OxI>x=2%3T{T7c1EK?{g{M@Pv^)*2&^eSmc{wzf4*x0188sq@rs0_em3&7>YX+& z4>ig-?8gnj=T~swP7lZl_`DrQbX$l#757aNY-?i5^^JjnMqUL0mVdSr@V8-&Kb*@( zd|F&!Q~51?Q_&YPwtww=o@Tv_nd$n^aW)Zpg{6i-Es+Q%>40YkoyDmb37U$7DGxFT zPY-e5hTmSBZYn%qadmHZH{@8>#eO6EMJ!icSBo$m6}At@)-;eNdml7+4H1mRb{B9* z|Aj-OB&JY_-(hbmp?~(gO<(Y8@8S z>YvhAezH+7t+m$c!%mDd_Ul0hdj7rBq%rieaTqe)_7|8R^;>4R6qo;m4eps=lNBd5 z*}g8l@($_Zr)0peX?Hp;&9iQp@0nK%M1}C8mY%jaoXb0B!xU^|rcC?^Y;w1e{ln+u zu@fN{Q#7)eGdh<0fnSAyy6O<)38a7S8seix**c_$L!`&csy?`m+2 zzFU0+-`MUs-xW+R+a&iR+<4;C>xg48Ffxj`%wy-6V5XhBpJpxy8F<{T=c;E{4NeN1 zA18|z!XZTt$kD8G*Gn{&(Q_>(VSV*3Swh^25n6XVAM3n}N#iMz|3+K6ulGD>eNwUv zK4(q}x1#rD{ra>!;V$wBaU_i~@U3XI>it|d%HBEKKeppOU$JH}r$5nQ?sC9uC!`mw zF$?06O*bj~8Vu@B9|6ftxZA{xUD;;-3Z&(!8(LUfA} zJ1J&6eqBHD-f~we%y>JTi1F1WPAZ)^%9{Q5?b`mw-usDK1&2}-2{TC#1u=5Jl5$?)z?;M z6l`1q@5sn|{gB8vSbA=CS?)Vv;=c%-88&Et4IZb&14z2!2vyM%wP_IkWsiQQ#yBoU zi-0uhDB~rYcXdb^OmC9Fh zzzcy|q%?+4HTa_{pT)8W@IvpX z#iL`$$%|v*Uj?GhNYU*Xhy3swb18w^_Dq(2)kVT~rMW{UT~a%qC9;=|PfEbIe)pT< zf`TCi+lnw$Os+fu>_KCL3 z;e9sf9#4_S7{SGIT=OM69#v_Q>laE+)6>a#VCSp4<)RpuHVay(*ARx~l73b572Jet zr-%BJqLmA`jAu_zuY9N*z_m`@YMQ!8zO?O5M8lm#(X+*b{l204QxZY-8eF$u>VA_- zo53Qzd8No6J}iefJj|O#d=~gT*2ba;+}ayg2!Yu$imdTg=WH6s%=vJXgxK=)^a#!` zhhE$113a=&CTJj8U^(rZxmMyd|28o%Wb5~$-~_*Ctp}M9D{`PmU_6g8P%0e-zmI;>E;niec%On zK>xnc_ym9DBccM^Td&mt)g_xoVdqv-4Mp$3OV)YSy>T{Ci2K18G1%zB zvtehRn~_pgn-QQPa@AJnFp0&r=Nq5T+wGTM*G^{t{P9fWx%&y6ZkDWqBHZVapW85O zQ483Y=c|5M7RfkHzAdlA0{`mnD^O{saB)&dyGLJ;iUD)nAk%RF8xyCIADZO1!A+0| zMt+YyYSQm(p^qo+Y6L3=yW@zcFH)pBmklyp{bZ#*##`R?e!&D94o?$C1f~W#6}bMm zeA2R4AHVtpHKB`UOsc%^lXPzNQqkqFK0M{3OOHySuDiyjMciK%4RV>1F{z{o8;*8kW@!2&Tja*g7;oKIpr(jS%@P%`rwcerbUR(X>-uq#^ z?o9uRpn-PF0fdC4sR1q8$*uCKbbCoOI*O#M*Isi6_$|w83ho?zJtt>P#!R=#rrbvs zbx1`73n?0_2S>;F^(($-K==cfkTK|A5 zq6QN^_8iJM$=)Z;K)z$-cxv-NqSgOmAj_T=L0 z##8&jFw630%(UBQdu!iFlAminJzqA3i4}EY54r4dJK^=fw^u1?NTYIitCEGaHK(~79djk(Hh z?AsnG_B-yX79Q5Y7)ka< zet#909Py)9QHo7xg)-wZK0aB2tQ*=bZ$m@F%b^rzgYV95Ynd0Rp1Jj;A$ERt!Ijgt zq}v&f&O)19VcE>BKtf}6&+sr%@TOjdr)kBE$2cp>@K%GSAwu1G06)Q28diHb#GA~7 zPJSYj6IQ*H8j6n3kOotk@Q@w|AsXy~1{8aKLmhw`uhwn-hQoZ`hVO3t%V(P!YvT9Y zgJdA`hK(zoQ`AeA6(R4tSKrtpspqTOoaR}ya!b0kDGTj_5&cDS^nG}%9Z$pxL(qXv zOp7;fz`%-mt*t28eEL$0GNH7z#poIJ*E0rD2clgz-+ogFfBf=Kp5`q+`Dkd>t9A{m z^FO>{1kx`6N&w89%K2J-NzPLfugH|kxL9ZX8YF_C!dw_XVrY1m0giMX-hTdkYDbV> ze!WT4zg>Rs+`0~(oOjrLm1Aej--6?pAY*USW*!dhZnnX|~hQ&?*#dV%>))6iJ7gm<|ZWShd zGYyYVdUQ+8L!DAHX*%zm3S*os53Ca!JM*NA-JHb#0(Vh(x^ zeP}<$@K32~(T(@(r3%K4tDBjjGc{arL6sp=6GBsYHQBIz4GYMsA^#*bFfgbdg+94~ z+1?`*i;{{Yhgz*(tvb>In!h*}<5|;K0u6jAD2~$gf~_HpJR0wt0MY0AvhHi8qA;%n z4BYFRIsfci{0OMl2ki21$IrqQk#_e1v89Ey+mVm7dz>e`)kw}!LPxdeE3ZqSi>)oP z-J_F5v5_3AB)@EdMJFBHjzPl5pZ9>B;N>)x4J`|j;_<4v^&;?TzN#Xhk?+7HB`AwU zM<8#&KF%|aGp8Id=6ab_xr4ZY>GIs z+$54C)(CkxY0KM>0c)k*!2uJtM8$mNY@CZ)C!~&>SiYa(LdSA5gY&B&nR^EuwQnz; zBnwTBgmJ__#?ym@-oYf08$ADZ$6-s+gw2sp{vkl# ztV>z2HAB?Q43|ByM<()c%AoH$_m}hNct^URN>z97ZrpJqZM$_Z#{L|lDMM(F&SthL zu!Y#c9b>?%`C)>vi!6oT!6^?8CV-1dxMv^mA;CCW3nCw?&g!uX3^ zXzPQog);Id9djMr@^b7MbB}sI@|DgmTN9C?appLpK|4!%RpJz<$6`G_9759(WiY&2 z6i7O*&Q7|31vBf)%j{FxUkxYIN4FEry{CZot}1fWJtx{PWbsCADTwmxSyDZ-AVV1! zZNi@ObnV@5nK>t6s0d9x##cid0sFI32=NoQ2!PhNf-7Pf`1aB=Q?qHzgST(kp zOVLa@kO=hs%sEl(jn_*zlPhrx+5c}~Mnk}`9>a!h-`KQt!|m7@Cpkl3eQ$~3@8FoG z1VEMu?lan;xh)(qSC?VI=}G>BD%8A&oG{g=Q1paoW+C*ZYda!Up9HCf-abO!=XDnf z!(~p6j}+40Vx4~h>#dmTa!A=dk$szcvE=J(>{Y3#YF3=I~1ytBh3SpHzxq_A2ITSIwNyjj;8T{TtpioF@nK zzIxjExyfCp4K=-is4j&XK(<+UP6pBIL_q~OB;DB9xT7rnQ_Rd8p@cJ6IhSbYE8NkF*Z@7*+%!Zt1SfuhiTStySMdjFqAqQ-kztwFu`WvaoZK`GyI7q z_tMpjee0<4q1`fHmsLwGTdmagX+4#4I5G1H8r=BlTq>a|m!S;e;d|z_)|`HHP(VmC z39G%+6Q?idN7~+10N(5HbN5GD(baEZv&=Y6l9F0xHEVju4v3tqbG_(gzeKH)?W#&{ zZK`F(lr6a^T2XferVy!@U^)4yf}P{F-eP-kx1Yb_Qn;hdXLTJHLWmj-*5p23!zUo( zvjso7tI=5N-T@>hipwX5xi23&O=cDRisqstt)xmvA3Ce02w`6;z_?&yWa*8J{rP8hPx&U& zI&p7~_T2-T8!if2_GE%yh2pQ^Gv{>hJ$60l86B(KLe7-p9ycs|XpkO-<=a>CQXxbj zN_nJDgb@T6fC1Rc#3gJ)5;jPea`wjsL%R>0(-`Xcrj^cwZEe0C6AB?`u}3XsH_4e>zXbFK3bY#BER2Ce`C>nNdK#_=|E+BmB{T^QBX1=^`BlqkM|C*?1m_l0BX%@?E5Yf;_G zyLP5#WHcF}qh8V?$#$}nKggEyegBD9#)YEROsS{PJdpy`2X%A7QdYbhSLmf|4>6>? zlg@oPc{=@3q*x?8^`*Zi|Dr&;T52i6Tg#C;;*ZNP))0(aQFLlN;4!f$qN4@ReHMz~ zq&dnmmH%ko(0}^qzq#i~yPZ4tX%NZ2W9Z&ft3m7IgKg~4J)+vbijeUprN#uFd|#$3 zCSHwW9tT9Ak}-mw20LM_;UMe%=!#R0^l^nad-e7zO`EMPQozt~L+{n9r9 zp!h}9W0M<@RO0a02m(Sz!Gg<<;aFC}(XtLS&0^EBc`GqB2dt#4x%Zetz_=7ieNX8Z zzkrUe9)g*)U?MWnI>G#$3YHlTiX|%6nV($;=!_7U${$1zTa6{u&&&Q2>pU}&4*wD{ zxLHM8>*jRM@1D;Jz~_HtD-8y}$5zaND3I zAhW3|E@AswAp@GP{Vl5g_gh9HwY$~G5XW?{brPVO;33r}ot3W4lHT%Cm|9nio48Bl z2`k}@%}m@VD!aicG!DeF_ z?=pgoIN%8*Ikc#tqS$$Wr%r2FSGJC*>C)h)vN1PWz3SV{78k}HBhczpTEo+O$QXir zT6>LTD9xzvL;s4_AAVl3_JZ2ciXJo~-4z6cYBMw&(JizsIob*Y=e z`?1w~Az9)OIdtEuRiL%Sgug;QxXIszegeeL_69W#CN?jo^pD>gmM!d{V@6oiThxbW zduNHepYn(qEw92VzcC=bKLw0F|2;HM~YJ)x? zH+(I$U3%7FY0Rei*T9Nevu|M>ZwDEjp=q+fa+8R^6AJ=QFZJvya~SMkGlIhHd0=)L_h$E zeO=IcWu>yJRI3zc=BIhP2O!Yu9$&h>io?!wt!Ay!3f z<1>k;!D-M20aC+PXE#zJp9aS6nYq=;q)IdDIBffpA(#5-x<%VPAx+&&l%0N#Jj8mD z=R@7QNcfR_N7wKl1M3yF1z>9+@Il273ndor-@tpGd|?}M8I);V3v~(W`x86rPheaz zdv&KQ}8^``L+3M9z5H&a9i!>H}V%|~P`$798CUcbp_{;!i~$HXhc_`|jDF@JY~)|xxYVlP%ZRUO5dB@= zsIIlKsQHCevy8^49dV^mNqD)U+}ePyeM7vl`uNiOe%=~PcR?Q-Q>Y898d=veIKh?b zPfS;ZR>^3aL7w}a3W-}Lj3H=%Xp05V%qWK)p-^#;>-dtUwR@9-{}IkeWo1;lt84A^ zdyvav)vneycPW5*zxzN_)l{&IV@EzoI<_h9f!1?L`;~Q{i%kld;D*y5dv#Cp`(s{3 zdMgdqA(l>XOWC%umF+U%daLkm4_@UW718lY8Nx*QtcFC?`+x`(t384H?= zRT?gU64b(mJTz4?KhKvZN=Z+1TkJc9!Y!XpbT;SuH&LM7g#P?PgH|~AW>iaRPXX)6 z?#AH{`CP`nLs1M7PdIA@xHFF&xO9SD%8|D>D^Aw`uFS2g-`%&&iJ4^<`bsc0yYuT^ zzl58O!+2UzJoW2yuyq?wUTtlV*C#K7v*ThBJo%h;4ZHJvIUu-PR1*h=?d{g9lhcbo z-+zalyEz#YJt2PS8a440sQTKVFrQt88pU#lvc4)jafh9D!UdjSjFRh8MAmO|QNem< za;8h$V_A4iF1X8(F-4tp7#h5kF=IpN6Co%tIR&wB!eY)0zHfUr)h6W8a_stFJMC4a zgkj(h{AwD{t~6MCpWYi0w-p9I!)N$V*s@J`!EF7GK6OJVHKzb{p?d88wf8}wk@vCP zjQa>=qN(;zh18@sUZDf51f8>)lOgO7bgW)iwy;0S%Wenyt+91iIw9s!&-YPxj5lRN z`#?GO&!iD&wDC|sGGj$uYBYqIM|QzUG&%2w_0BGQv>+7Qnv)9r73(E52GRJB_OKD} zq=7l#7*(_8tvW@N*m5Hf4TXk!Zs&|KgtpDubLvYRywJloxty?5735U4N)1rNMo0>& zD92#Y`p5kE8r)C`Qx0j3B-VDdVRAp(-+&t-#X&2WN^yfr_l74cHbXsbn@_~qo=rF|#gK8*lSvvQq|_mL z$l~%2XxLzu5pX*)fdsd^!tW5R&c#75Yf2aBWMYHxUF-mzO^6g%BaTf-E-q<(GMd2h zvOhViS8hh@&t0^$!*)D(o7c#MoI2~$q`oRaYKe5nd07RH*5!rk3o2Rb#AvLfde7T# z!f#ag@0G1SmtMuv8TuA!ZOk?Ep*WO!nCp+>rM=2@dV1`rwcXoXeKPB#tjrc&e=S$V z^CFULDp4$A6${{zGS7ZAY3)M$n_h`*tZ!OSt2K# za0h@eBZ&>%ERCkPfUJik46xrzTp9;ngNOcXjsV%ty{@(}x!T8JB9~`co?eJcg($Fj z&Embc5%AV^s&mTBEKG_hzRQp0&(>PpX*StI=F%VI znNvEx>ijtp4U6IJ`c+d^H_VAqYolv?74P+RVEm2^f92qsr~m4Ef^B6TS#$4Ny_rcm z4rPfW*dI^G@UIj1`ElCAq8FiKPzK>9HbD5R-D;at4rE>Kz$H@;I{oqm9MBmEAD^n`@KND@hSaAW!C;|mwCUhnG# z_H9m3%d>k3KBZ)I;-LThd+3`1M`3GtwsmO>4N$7k*qdo86`je1|4-MX*1J5VibHjf z;*gK^WU6sKX?w@VH}C7k>N;5#a#*?cI)s3}n4G!$-ep6k>2%!S!5)VrSBV;LxX6p;gB6VWlh20W@B$a!ecD7noy*Vubn zPniw83Rid7TMS4OeQCTb-D|h5n%anp^{@gdZfJB~T0q|U97{G;mspQh@4Q%xFJVf z={B=kblCZW8U~d=G^pm>!m^@wuOI*jZ~y(y5tN#6?Ev3bU8(nf+~Km@I|)kXRVT5Z zLBF2Qg|AT2IRFhG>!&)Z==-H&ZcGE1v=3gMgD2&z)j#V=>I2NQKF$m3@^l#Acep*g z3Nre-&LZbD$O&6jRDAMX>Y_QrGpklB*_fRV^<6r-aCx~eUW$&%_AM<>hja?W9G@|U zd`ntpfg)FE3#DjL$wwa>h`&WW5j^>^}&UUoo;6dARI9$|I5(m&N)up zc8jQ-iO#^?!rI=qo30Rb9h7TTtn9k#SZ@0!*OO2)XCui_y>+ZX7g| z0Vj3&Ypv|IYWO}1ruOZ%G__D<)7Nvbd&d$Yx$TBF4uwHMM_AhuulSiOtPgwlQ@R@e%4q=lh->q~SVzHah=Ffc ztH)f@zL?6I7Ta96KnWUvQHRLdy}1utUk%H5?f9*RG<(PM7_vT=?$WU8)@$(1g3Qb0 z>Y8`qK*h6N48-iAc3=Gy6A>v)~` z21*BdE}b4nb1Pm|x9f`VnKSSe&`jB%GkCSsqLH|d=i8cKlIZGvtClZC1VIv!kSP@VDqk0fZuy&RfDTRX5wA+9spm}k+hg zfd+T(2zVRZ*2WY_Z6lKHj=WwA0H7-9gDPZP?@XOb;joT;;cY#hswR~U&WYT79dnY>J;^5+89e=kY~<^*)(>MwY9KD?CB-Nn zEiK}9MXEf!yhCMWBIvBkZQW~lXkCA5(x7^UD}dR%gF2WhNR@T(rG`2A^RmvG+}mAZ zvH!u8x`>?t(N}ww$t@>@P2k>%C8izJt>mk%u0aVRyhobzTOni9u6t%qri-PC5UXVG z#uDKAIw3*=gfpjm1{ouq5rgjb$e{l{Vv;aLWJo)UuIg} z8y}6kM_KrkU-H(#NI-Y#T_2VjpM@soLn*mA#4*bJsQ_{#B4 zaiYaP$igIY40jqZae1x#v^wBS*QrPIP7)}eww-n(E6P7|t5b6y$X!?Y$v$Is`%`4# zG85j)t0Q}Ki^sG4N3BUg*?G;yM8VT%AE3i3{j^(Ks+C<8`}Hx`tbPLg2{BYKYGGZK zzb^UUn>5w=Y(F$JW9wPY;_VXhb?!+J-}u@y_`){%++qmlqvSJBf+HVG$nz+rhiF;V z-E)Fh!;B~66A7$Ju9R|QV!@FcDy>gpo+>fR(chk)O}V{qb!mm;cc~o7=d+`OAq|@B zvbV8diw)^qC~?X7rSl>SoPEnGIr~Z|!;v*uXwC@u^n=)LP{nw3t7CqbNPgi)FRs@X zzfxl6&asIh2%<-it-*FGxMr_oei7_csd4Ee@2QuteeaQDS0^Zc%td;}trgpSPGj5L z8$=V^ZPG~<_w!->-ijMPKr%I-D#{kGE{&l0+A92hp&SzQ-8ns(Y}r%+<&>}tk|<}* zXS7dvB?BzXVYx-U*7{?UD7~qd%ya1VysQx#Ou;bo<^ZH{{ zW9gLTvdMPtZhy{~gLE-#=b=MA<`3MRyXRde_Ma#DtojXz^EZe|<;MrN-~OE&MwaA1 zdudX-t-G#xEqACIq4|O*$hyc_v*B9o<)5bJBfOmsH+o_PrSBh^((kxd?izwr9;aVt zcVG8B8VTzw3{F})Rj8=5+K*x;5884b{Jhr^b6i@-cD-vDVF#djb|#ipRrlAKMmAOI zc7@grTc4!uJ!i}5 zzZ-sRXL!tXXh@5I@lk@VZ~APvob9kaaPycufpg|utXnY3hJBuaHFkugRs#5yX1S-S z!M#`P*Nd^+JCSjNgB1GxingN_V|Oc6!_r}qkC59&D(LoFoYa_G#t(B;7>+#N6&&2D z=JhL%Gg!zIGb-EgmNwRN-J;JV;^?$35!-ipZxbgQ!^o>f)y@oj>%5x^ID_WW+FH{?^|#AAEP54fLy4M%kA<4V_x?;lIOlAUAB>7SFkPT_x-6-g;IK3DTqv~2%BJi$G_w$%WLXL16HVGe)_CGNBFpkf zak;MedxOuJqwnbWOkQSqR$l4UU$#`!2gUXk#|T#o*VJM&-2kUd#JdgS7sK z8k#A0=IW#EkJ!=8#BU7z?d8$=E7r;=yrIj@;XEr!_ZJeBO#>N+Obnv`qx2mjgaZEj zrWF5gN%jBer&IHbi>1v+Qz#*)b(l=lQIqfy{~@uaiE5Pfb}*T#^1iwXvEiR*?%Q{y}c7k9UVJ7ENf+Ji`TlwYS*Mgf+6)t^tc&-*|YK=cpvNk z1A&!cParatpGwvuNE=dG5LJCy;d{@86+FH(3E{o{I z#Kh2zi#OkN@R5`3_wNDaI!%f^HjA;>JJEz3;?Y9agO?o!zRamz9RH!u_iof*k`KCB$KRq^E zqH1=!ShLmG{ClhC()ku131xJb$Rd!!fkHCRR`{2Ols*dDKUK=9VojE9-@!W@I!ZAVgQUiD=0)H2qwMghli8!5jfY_;It^o(KspKR1`k z<_D{OTk`}Ycv6&95?yPSVYOYc`E79L5=%PIuxw?swwf<@UL)!Fs>8#rkmRSn=PiBxfMukFQ~yU8%o>8BmU4-Qh4uBUq$PpXCM6N5n#*Y@^D#y@oPEea*nh7u zB}W&n+5PF#R-6H$MLTz>;cp{TQUuM}-~I9^l5bnN@H^#A5G7m^R7pCoC+uv0QKP={ zzRw50TYGE*Q@D4QoQy2EAX|+A!INd$5AspD}c@4fO&0#=Qt9h^XLL%`Vy>^n17{)gf}+ zSld%Xxb=vu-|OA$|4%LF84gz$?QxxCh#E#lGNJ|1Mj3+WqC_GhN`zo02GOI1(Ivw$ zNO_fnAb6RGZj=yZbfQOZqeK@&^uf6D%6spJ`#kr#_rv{mKAd&-K6{_D*7~pgtY5Rk zxQ;RlqIu)yfXuq!pRIpmRu61!gJOEHec==ym|yQ>(&wtBW;bqN<{y?KURJ=HlrIwd z90C~XH$7687~3PjK|oI7Ko3c2YPx;$AyHBj#dr=3sNZ4OWrWeWUs1Tm@M_8Lq1E*j zb{UpPfSwnEqE#n_UWs!e3LZg6qe@oFKXZlYTP%+W8g4EUhFR4MlU50g<%=X9a3 zmYq%dwzSaY&Di`)`JNNkDknZZe)CQ+i{i}Jlu%J2Y_a&t@X&Ch1&3ZS#sHwaJ34HJ zp7&k`>MF+hi0&3o&|7TuJ?jvUXn4BK02iI>+TpkZGRZ~ekOy^Rg>o5T`=q66cv^`? zpwB6MGr9-)p1aBh458eF$Xg|SQ znQ5bhsBt&I0dRX>JzoW#(prYN#+XwFUB=5_j)R)O|L&f#4F|YDK?=t!kb&=#f@Kgj zfZvSfloU(Of288Q3k(0oep%e`rFzU(gI}Z6%a63wT*DNNQx0m4l})}je>GPbsXw1N z>=Uz+^2((17mW!{xfV;AC;+U)ZiQ0QqgExMBUcvBRlH9P>)b*w?aWQt&Lm z-p`OQ?elL4hO4#!Ie-(DX)L@ftws=g%z^F?o?^EFRecT|WCD;P(+ysHy}C!c7~zli zOGXP?vZpdD1D^X_H5ERKG_P>fz9N{VhE_10p4UKij<5BA!fj}g@}vgM;zjR+Vrh2> zDQ*iD8XeZ>fpO4bl4kMV(vM(bNnQrDMrS{1_G=Dm6*X}RrWhXkUZ!ndk+GLqO^U1% znvoYaqLhBVFk|&L_8!D?)p7BDmm^&@&1JBc4V*Cq> zG`ae9E92I~fw(ItcZFI)f2bIll{|=y)0jC}Od}=^&;Q65JdIx^w+99pLI?X^&cV$3 zBxo`U?2PcNA9m9BmsIaP|KqW~*=QpwE!3aeG=Xb>9m_4ra+}ltfsY7i*Eb7S#Zs3j z+O|;hH-2Kw6MAuGl~o^lq7;_9H)@~eE+iNNjKFTB@ALjK%QEDRzStkm_xWOfJRgVy z#sdqS5m9FN!~Z~27Z{s+iNF6_gq3BEvMbU@}ZxP_k4rH!# zJN4n(R&GFEN3P1cQ;}#y?yonZ`LqMj3q!+X=Y*cjTk)9>PRo))DkcAbMRZ}3w{whR z6=oyd%X6Q=~ z`TQ@yjWN`H5sse$0%t4nl@;u{(Ah4y3DM^xAIk_x``6ry_kD114>Zl-r5&tw+G4m6zeY*dd$p(``EK%5XuOWWQtuz_M)`6>g{LwUH8NBYK0CXh` zKe?=j_C+e*kNn`uwgU%t9j-#Fz3f+EhjJ7!1)$_gaT&<%{t23 zn!Qa|4^Q%38xgboW9#l|hVhX|vu%si^%?WUrPX?m1jpBv84-k(zgMa19;SEHKzh&* zuSq`u!xLP=k4*eVW~X+qV;N6@L9c);TORu}5fN~eXIcRPI(+yJhTKyDi*E|#6?)$^5`+BO6MKV;cW}|&O}``Nlbs5zCZ{` zA^a-&)zk^2i`Uerj>0k)^t>cnJA+pNw7xc{<7u(axCYDaeUsY44j}qTgugy$roAb7 z1Q&Wl>!RB*0?uJNq~Dgi%(fNCkQ#iu$*KcWOj3^3#ouf>&W}8B!;mW+8@*nts7|Dv z%bmbBk9`t4d{=mzZ?k4hyClvH9Zo}UakvqraXwD~z%SfMusn%o8R`uk4-HO7uW&6iy z(KPI{@MbsOv4kc24VvSWiS(CEWx2R-SB6W3xf~!z?wuaBImDmbXU-usZ9Vlc*VflYR1_)X>sRUOLLkuH#3=v z!4rz>GkeOR05y8gp zX-0;R;AAB%aaq|VE4Gi%v!pf$yhJLLUCS|MV=ig!jB71&i7udzl<8z)>hqShtl(w< zz%3rdsVoeVEIoo-R(%w2fo2R>TmmUIwtUc?2uyz%o0?Xwxdhdjy;9_H}OcXOnq>v+Qgb_ZB|Wa`~b>X{yT!OdUUZOLWa z8Vw_lw<)_k7n*(iW9=3&;1K{l&NM#bTBZqTDT9prI@qEPy`yI2DfN_vgm^bIe;G}e z``TmF&MiT=0$qf`uGnEpivS)GWc9+#SNoOVlk~Wr#!JUMUqPNR4g~A(v*YGmeFpFx zM?Ck%RTe7Wr7hX0+qj)TT$|tZg;9K|Z;Teq#}yh$t!$>d$(Wd*2oDP1eaRg&+^qa# z#>6VEc#qTN=vm;p&h}z$!at5Tw9L$y-#F)P*WV#qr8@-TwM~_}%w%GKNpu9gfl7F2 znK{S_%@P9v!be=$SIr~7j46~>rP2$QkTth0vK=&uh-hNt$d>*u2rsj_bHsQT)MY{k zzs7v4k6Aik&s;npD1I6Ok&(3o{chREHeeojp%%d?f31U@W-@fSZq9gOA!GfS>9QMf zAlOhIcM~{rva&E{-4_PA%$#6dK?r0TcPgtdu~?+}S<_v_)N-OPuGc z$64nmtCx&?&AF*b@(ljQ>vbw{wIHq&l^de-p=!Klr_G8FFlvQ&r>pze$++a6RwqjL zPivPGS^H7SmRP#?M^ME}$VxH~6&!}~cqhCu%Iv`3<0#dph@)KYzcmw^1Q%ODq ztMJ`QjwcZ*i>)HG5Ulz8mGqR-niVnls{EM2#VN64;6FN#*3jF#j1dGiP*hZWCNe28 zd;N`-Y+)O$+6p8;vN*^F23{FJ&0I(QyrwVUN{!;S{Cm2hRu2^1M-bF6CX>zXFfL)0 zdI2S^W-3oJy%L8iGP%kPeB~=gZG;<|>5D}efVKD27S&v&$YN-CgH5Zh@~r4n+|qc~ zOH*G3|4Nii)Od<`sN!Dqm*m9EWPcST90Yfl($-VU1yQ6NPYDf|TjUAkh>Y<;?>zjc2nBJEAj% zDK+~g+N*c=s}J%9o&@Vsg@oAclT<~JCEmv(Xn4X?{N}@`b^9uY=E&xC#=Q)IRXN4d z{=snw6}riKU}+b?RZkc0&NI^L6j%PU%C(kVsyMCMztRu^(RY`80l10~W0fz}+U4(V zS6E63=9ZJd40mnr_@>RwhaXWso>rNH2ehknzFVV=qwLnK$SHTdC`64$uT0537sJ`4 z(8rPO&MZudV8o{17YWcE%&+wo@9f&vYJb1SiA#$VhX;vG_9~5@{lm0UtgXeG0JrNI z9^9+2a@z%%fqWge)2}=&8ONVF^t7{$j!5^=BT=rid)DL6T0LmXG2l#x= zl0Z;7g&Xw!tjB;?>P})uck?*gP5FLo(S4*_!A(OvORMgm>=>P*cNnf7b7W06lfX9^ zFyb`o{L=@|-u4tjb);2x+FsFCd@>UZs$mL|{qh!027S7HEh&Ht@Y#wT9?%l@;M0bS zyypzQP0rH3ND((Gyk#D)@N^KRa)0~r(|uPf4C_}V{3>%ZaCjJ)9I7RK-*(`DS*Y%1 zVNf!s$0Iu;#^e}{eT`YOE@e!+$_||9E-L5*LB>B$K|Di%F^?GQLRUw>k(5+_Hu`Cs zDb|e@T?Xi=*utjw5|VMR{H%7HZZw|{2W)$CDBbp{CU<$VrF}X7u1;dWMbamGT~mA@ zPCft}?HNSqeSDU&l2JX20QbhUHnz; zqx=%G5rN0!^rEeRESUP|WK<6PzW!&Hh1 zl>NuBAa6DzbSOQIwNl+>0CucC2_^RT5X=n5TE0{r*I!S@cFXk|y|-69EF3hM&eVQLbOy<<3N z)o(&*XxUyH^jC)V?UI0pd6T1^^=mz4C9O>2eLaQp2kXgIE_o2#^~jn#LNMBf3(H}U zEtR_jL1&=;^KYZNVLyvH{+ut)H9Y`Sb&x%2hW%{}Kr9mlV$Jl?Fo?ydi>x2!X#XNiJm#l`*O3vQjLh|sHIn2X1m~n@92u$lnQxv)2w6Razr*aR7i7wGRyxUK9_CAOx6|XHg0hb1+Y0_TUtE4*K)7mo22EItKIL z42pt-JK{8N&P8gN%H?ft`bfs1&EFn4yZ(FMh5Lnk;jI065)!YRrm_p=-qBK5Z@ZNj zx4+VA9~Uz}{fK_c=%a7|E8FiY7L9*v{Qrdx{a>L||3!cPuk8OPbm@N||DRC#d!lD< z{B)N8ll}h*efpn+ofYJNzyJTE{lDiVFS4Re*lj3epgD74&`Y_e3|$I6?Ymk98a847 E0R<|U(f|Me literal 0 HcmV?d00001 From b65ba083243d74f90a36d154029622c9fc241619 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 15 May 2018 15:57:09 +0200 Subject: [PATCH 214/291] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b651ea3157..55eb482f91 100644 --- a/README.md +++ b/README.md @@ -12,20 +12,21 @@ [**Charts**](https://github.com/danielgindi/Charts) is the iOS version of this library -## [Enterprise Solution 5% Discount Coupon | SciChart](http://store.scichart.com?productTab=Android&CouponCode=MPANDROIDCHART) +## [Realtime Graphing Solution | SciChart](https://scichart.com/android-chart-features?source=MPAndroidChart) -MPAndroidChart is free software, as a result **dynamic & realtime data is not officially supported**. If you are looking for an enterprise-grade chart solution with extreme realtime performance and tech support, we recommend -
    SciChart Android. +MPAndroidChart is free software, as a result **dynamic & realtime data is not officially supported**. If you are looking for an enterprise-grade chart solution with extreme realtime performance and tech support, we recommend +SciChart Android. -All MPAndroidChart users are entitled to a special **discount of 5%** off the SciChart store, using the following discount code: **MPANDROIDCHART** +All MPAndroidChart users are entitled to a special **discount of 5%** off the SciChart store, using the following discount code: **MPANDROIDCHART**
    + ## Usage :chart_with_upwards_trend: **Gradle** From c1f6fcebf0c3516e067b34312b283189f909bde5 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 6 Jun 2018 09:14:41 +0200 Subject: [PATCH 215/291] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 55eb482f91..986e4f8102 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -**Remember: _It's all about the looks._** - ![banner](https://raw.github.com/PhilJay/MPChart/master/design/feature_graphic_smaller.png) [![Release](https://img.shields.io/github/release/PhilJay/MPAndroidChart.svg?style=flat)](https://jitpack.io/#PhilJay/MPAndroidChart) From 073ad3c87713421025ec1dd102283ae46d10fdf6 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 26 Aug 2018 20:14:32 +0200 Subject: [PATCH 216/291] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 986e4f8102..ed3220eb3a 100644 --- a/README.md +++ b/README.md @@ -117,9 +117,7 @@ If you like this library, please tell others about it :two_hearts: :two_hearts: [![Share on Google+](https://github.com/PhilJay/MPAndroidChart/blob/master/design/googleplus_icon.png)](https://plus.google.com/share?url=https://github.com/PhilJay/MPAndroidChart) [![Share on Facebook](https://github.com/PhilJay/MPAndroidChart/blob/master/design/facebook_icon.png)](https://www.facebook.com/sharer/sharer.php?u=https://github.com/PhilJay/MPAndroidChart) -You can follow the creator on Twitter [**@PhilippJahoda**](https://twitter.com/PhilippJahoda) - -Philipp is also on [StackOverflow](http://stackoverflow.com/users/1590502/philipp-jahoda) +You can follow me on Twitter [**@PhilippJahoda**](https://twitter.com/PhilippJahoda) or sign up for my [**daily coding newsletter**](https://philjay.substack.com).
    From 6f0b7432ac97adbf5a19853670cc65910d25a717 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 26 Aug 2018 20:21:19 +0200 Subject: [PATCH 217/291] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index ed3220eb3a..ceb3385962 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,9 @@ All MPAndroidChart users are entitled to a special **discount of 5%** off the +## [Daily Coding Newsletter](philjay.substack.com) + +Sign up for my [daily coding newsletter](philjay.substack.com) to get quick updates on Kotlin and Android developent related topics. ## Usage :chart_with_upwards_trend: From 2316f8500f0478fc6ab48cac65e2df65d39657bb Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 26 Aug 2018 20:21:49 +0200 Subject: [PATCH 218/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ceb3385962..270b034cf3 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ All MPAndroidChart users are entitled to a special **discount of 5%** off the Date: Sun, 26 Aug 2018 20:31:00 +0200 Subject: [PATCH 219/291] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 270b034cf3..7ad6824cfe 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ All MPAndroidChart users are entitled to a special **discount of 5%** off the From 20bca4a8ca6a6d9e0c508ec6363d387df1aaa999 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 26 Aug 2018 20:31:44 +0200 Subject: [PATCH 220/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ad6824cfe..96d9ac9039 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ All MPAndroidChart users are entitled to a special **discount of 5%** off the -## [Daily Coding Newsletter](philjay.substack.com) +## [Daily Coding Newsletter](https://philjay.substack.com/subscribe) Sign up for my [daily coding newsletter](https://philjay.substack.com/subscribe) to get quick updates on Kotlin and Android development related topics. From d67ea481af1e8528c617dfd404d0c7827b0134b2 Mon Sep 17 00:00:00 2001 From: almic Date: Fri, 26 Oct 2018 10:30:22 -0600 Subject: [PATCH 221/291] Huge Project Refresh Before anyone freaks out, all these changes are under-the-hood, meaning that you probably won't even notice them at all. The biggest difference is raising the minSdkVersion from 9 to 14. Recently android bumped this number interally as there are basically no devices running lower than 14 anymore. Sorry but you are just wasting your time if you are trying to support anything lower than 14 now. The next biggest change is updating the project to the new AndroidX libraries, which changes some imports and nothing else really. The third biggest change is fixing a few bugs in the code that cause values to be drawn even if there are none, which results in your app crashing. Surprisingly, these checks already existed in a few of the newer chart types, but most lacked this simple check. Other than those three changes, nothing else is functionally different. Well, saving to gallery works on all charts in the example app now, and you can quickly see the code for each example in the menus. The only real potential "breaking" change is that charts are now saved as PNGs by default instead of JPGs if you go the route of not specifying as a JPG. You may want to double check your file sizes as PNGs can be larger than low quality JPGs. I still have more plans for simplifying the API and fixing other issues with the project, for the small few that closely pay attention to individual commits: there's going to be more soon. --- MPChartExample/AndroidManifest.xml | 99 ++-- MPChartExample/build.gradle | 38 +- MPChartExample/res/drawable-nodpi/marker.png | Bin 12480 -> 0 bytes .../res/layout/activity_age_distribution.xml | 2 +- .../res/layout/activity_awesomedesign.xml | 6 +- .../res/layout/activity_barchart.xml | 8 +- .../layout/activity_barchart_noseekbar.xml | 4 +- .../res/layout/activity_barchart_sinus.xml | 2 +- .../res/layout/activity_bubblechart.xml | 8 +- .../layout/activity_bubblechart_noseekbar.xml | 4 +- .../res/layout/activity_candlechart.xml | 8 +- .../layout/activity_candlechart_noseekbar.xml | 4 +- .../res/layout/activity_colored_lines.xml | 2 +- .../res/layout/activity_combined.xml | 2 +- .../res/layout/activity_draw_chart.xml | 2 +- .../layout/activity_horizontalbarchart.xml | 8 +- .../activity_horizontalbarchart_noseekbar.xml | 4 +- .../res/layout/activity_linechart.xml | 11 +- .../layout/activity_linechart_noseekbar.xml | 4 +- .../res/layout/activity_linechart_time.xml | 2 +- .../res/layout/activity_listview_chart.xml | 2 +- MPChartExample/res/layout/activity_main.xml | 5 +- .../layout/activity_performance_linechart.xml | 5 +- .../res/layout/activity_piechart.xml | 8 +- .../res/layout/activity_piechart_half.xml | 4 +- .../layout/activity_piechart_noseekbar.xml | 4 +- .../res/layout/activity_radarchart.xml | 2 +- .../layout/activity_radarchart_noseekbar.xml | 22 - .../res/layout/activity_realm_wiki.xml | 3 +- .../layout/activity_realtime_linechart.xml | 2 +- .../res/layout/activity_scatterchart.xml | 8 +- .../activity_scatterchart_noseekbar.xml | 4 +- .../res/layout/activity_scrollview.xml | 14 +- .../res/layout/custom_marker_view.xml | 6 +- MPChartExample/res/layout/frag_simple_bar.xml | 6 +- MPChartExample/res/layout/list_item.xml | 8 +- .../res/layout/list_item_section.xml | 42 ++ .../res/layout/radar_markerview.xml | 9 +- MPChartExample/res/menu/bar.xml | 46 +- MPChartExample/res/menu/bubble.xml | 36 +- MPChartExample/res/menu/candle.xml | 46 +- MPChartExample/res/menu/combined.xml | 13 +- MPChartExample/res/menu/draw.xml | 26 +- MPChartExample/res/menu/dynamical.xml | 30 +- MPChartExample/res/menu/line.xml | 51 +- MPChartExample/res/menu/main.xml | 12 +- MPChartExample/res/menu/only_github.xml | 7 + MPChartExample/res/menu/pie.xml | 44 +- MPChartExample/res/menu/radar.xml | 49 +- MPChartExample/res/menu/realm.xml | 9 +- MPChartExample/res/menu/realtime.xml | 17 +- MPChartExample/res/menu/scatter.xml | 34 +- MPChartExample/res/values-sw600dp/dimens.xml | 8 - .../res/values-sw720dp-land/dimens.xml | 9 - MPChartExample/res/values-v11/styles.xml | 11 - MPChartExample/res/values-v14/styles.xml | 12 - MPChartExample/res/values/dimens.xml | 7 - MPChartExample/res/values/strings.xml | 51 +- MPChartExample/res/values/styles.xml | 14 +- .../mpchartexample/AnotherBarActivity.java | 186 +++---- .../mpchartexample/BarChartActivity.java | 292 +++++------ .../BarChartActivityMultiDataset.java | 256 +++++----- .../mpchartexample/BarChartActivitySinus.java | 182 +++---- .../BarChartPositiveNegative.java | 97 ++-- .../mpchartexample/BubbleChartActivity.java | 238 +++++---- .../CandleStickChartActivity.java | 219 +++++---- .../mpchartexample/CombinedChartActivity.java | 96 ++-- .../CubicLineChartActivity.java | 267 +++++----- .../mpchartexample/DrawChartActivity.java | 84 ++-- .../DynamicalAddingActivity.java | 153 +++--- .../mpchartexample/FilledLineActivity.java | 119 +++-- .../mpchartexample/HalfPieChartActivity.java | 103 ++-- .../HorizontalBarChartActivity.java | 230 ++++----- .../InvertedLineChartActivity.java | 205 ++++---- .../mpchartexample/LineChartActivity1.java | 462 +++++++++--------- .../mpchartexample/LineChartActivity2.java | 344 ++++++------- .../LineChartActivityColored.java | 72 ++- .../mpchartexample/LineChartTime.java | 220 +++++---- .../ListViewBarChartActivity.java | 79 ++- .../ListViewMultiChartActivity.java | 116 +++-- .../MultiLineChartActivity.java | 285 +++++++---- .../mpchartexample/PerformanceLineChart.java | 138 +++--- .../mpchartexample/PieChartActivity.java | 271 +++++----- .../PiePolylineChartActivity.java | 259 +++++----- .../mpchartexample/RadarChartActivity.java | 222 +++++---- .../RealtimeLineChartActivity.java | 129 +++-- .../mpchartexample/ScatterChartActivity.java | 233 ++++----- .../mpchartexample/ScrollViewActivity.java | 69 ++- .../mpchartexample/StackedBarActivity.java | 226 ++++----- .../StackedBarActivityNegative.java | 156 +++--- .../custom/DayAxisValueFormatter.java | 2 +- .../custom/MyCustomXAxisValueFormatter.java | 3 + .../custom/MyFillFormatter.java | 9 +- .../mpchartexample/custom/MyMarkerView.java | 10 +- .../custom/RadarMarkerView.java | 10 +- .../mpchartexample/custom/RealmDemoData.java | 47 +- .../mpchartexample/custom/RealmFloat.java | 1 + .../custom/StackedBarsMarkerView.java | 13 +- .../mpchartexample/custom/XYMarkerView.java | 6 +- .../custom/YearXAxisFormatter.java | 6 +- .../fragments/BarChartFrag.java | 68 +-- .../fragments/ComplexityFragment.java | 51 +- .../fragments/PieChartFrag.java | 48 +- .../fragments/ScatterChartFrag.java | 55 ++- .../fragments/SimpleChartDemo.java | 61 ++- .../fragments/SimpleFragment.java | 144 +++--- .../fragments/SineCosineFragment.java | 51 +- .../listviewitems/BarChartItem.java | 18 +- .../listviewitems/ChartItem.java | 25 +- .../listviewitems/LineChartItem.java | 6 +- .../listviewitems/PieChartItem.java | 5 +- .../notimportant/ContentItem.java | 7 + .../mpchartexample/notimportant/DemoBase.java | 77 ++- .../notimportant/MainActivity.java | 271 ++++------ .../notimportant/MyAdapter.java | 39 +- .../realm/RealmBaseActivity.java | 7 +- .../realm/RealmDatabaseActivityBar.java | 8 +- .../realm/RealmDatabaseActivityBubble.java | 4 +- .../realm/RealmDatabaseActivityCandle.java | 4 +- .../RealmDatabaseActivityHorizontalBar.java | 8 +- .../realm/RealmDatabaseActivityLine.java | 4 +- .../realm/RealmDatabaseActivityPie.java | 2 +- .../realm/RealmDatabaseActivityRadar.java | 28 +- .../realm/RealmDatabaseActivityScatter.java | 4 +- .../realm/RealmMainActivity.java | 6 +- .../realm/RealmWikiExample.java | 13 +- .../mpchartexample/realm/Score.java | 37 +- MPChartLib/build.gradle | 11 +- .../charting/animation/ChartAnimator.java | 2 +- .../mikephil/charting/animation/Easing.java | 3 +- .../mikephil/charting/charts/Chart.java | 17 +- .../mikephil/charting/components/YAxis.java | 36 +- .../listener/PieRadarChartTouchListener.java | 1 + .../renderer/BubbleChartRenderer.java | 5 +- .../renderer/CandleStickChartRenderer.java | 2 +- .../charting/renderer/LineChartRenderer.java | 6 +- .../renderer/ScatterChartRenderer.java | 5 +- build.gradle | 2 +- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 140 files changed, 4397 insertions(+), 3771 deletions(-) delete mode 100644 MPChartExample/res/drawable-nodpi/marker.png delete mode 100644 MPChartExample/res/layout/activity_radarchart_noseekbar.xml create mode 100644 MPChartExample/res/layout/list_item_section.xml create mode 100644 MPChartExample/res/menu/only_github.xml delete mode 100644 MPChartExample/res/values-sw600dp/dimens.xml delete mode 100644 MPChartExample/res/values-sw720dp-land/dimens.xml delete mode 100644 MPChartExample/res/values-v11/styles.xml delete mode 100644 MPChartExample/res/values-v14/styles.xml delete mode 100644 MPChartExample/res/values/dimens.xml diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index 3fa15cd69c..292e0933a2 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -1,71 +1,66 @@ + package="com.xxmassdeveloper.mpchartexample"> - + android:theme="@style/AppTheme"> - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 8e6fe137b8..2856bc0109 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -2,14 +2,14 @@ apply plugin: 'com.android.application' apply plugin: 'realm-android' android { - compileSdkVersion 27 - buildToolsVersion '27.0.3' + compileSdkVersion 28 defaultConfig { + applicationId "com.xxmassdeveloper.mpchartexample" minSdkVersion 16 - targetSdkVersion 27 + targetSdkVersion 28 versionCode 56 versionName '3.0.3' - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" sourceSets { main { @@ -33,17 +33,14 @@ android { } } -buildscript { - repositories { - jcenter() - google() - } - dependencies { - classpath 'com.android.tools.build:gradle:3.1.2' - //classpath 'io.realm:realm-gradle-plugin:0.88.2' - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } +dependencies { + implementation "androidx.appcompat:appcompat:1.0.0" + implementation 'com.google.android.material:material:1.0.0' + //implementation "androidx.legacy:legacy-support-v4:1.0.0" + //implementation "androidx.legacy:legacy-support-v13:1.0.0" + //compile project(':MPChartLib-Realm') // clone "https://github.com/PhilJay/MPAndroidChart-Realm" to get this or uncomment the gradle dependency below: + implementation 'com.github.PhilJay:MPAndroidChart-Realm:v2.0.2@aar' + implementation project(':MPChartLib') } repositories { @@ -52,14 +49,3 @@ repositories { url 'http://oss.jfrog.org/artifactory/oss-snapshot-local' } } - -dependencies { - //compile fileTree(dir: 'libs', include: ['*.jar']) - //compile project(':MPChartLib-Realm') // clone "https://github.com/PhilJay/MPAndroidChart-Realm" to get this or uncomment the gradle dependency below: - implementation 'com.github.PhilJay:MPAndroidChart-Realm:v2.0.2@aar' - - implementation project(':MPChartLib') - implementation 'com.android.support:appcompat-v7:27.1.1' - //compile 'io.realm:realm-android:0.87.5' // dependency for realm-database API (http://realm.io) - //compile 'com.github.PhilJay:MPAndroidChart:v2.2.5' -} diff --git a/MPChartExample/res/drawable-nodpi/marker.png b/MPChartExample/res/drawable-nodpi/marker.png deleted file mode 100644 index 616cdd7b31c3b3f511793ee72b9c9f595af4c2b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12480 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 KMNUMnLSTa9_GI<| diff --git a/MPChartExample/res/layout/activity_age_distribution.xml b/MPChartExample/res/layout/activity_age_distribution.xml index b023d3ab2d..574510fa0b 100644 --- a/MPChartExample/res/layout/activity_age_distribution.xml +++ b/MPChartExample/res/layout/activity_age_distribution.xml @@ -1,7 +1,7 @@ + android:layout_height="match_parent"> - - + + + android:layout_height="match_parent"> - + + android:layout_height="match_parent"> - \ No newline at end of file + diff --git a/MPChartExample/res/layout/activity_barchart_sinus.xml b/MPChartExample/res/layout/activity_barchart_sinus.xml index 78b849081f..f39f2c7739 100644 --- a/MPChartExample/res/layout/activity_barchart_sinus.xml +++ b/MPChartExample/res/layout/activity_barchart_sinus.xml @@ -28,7 +28,7 @@ android:layout_height="wrap_content" android:layout_alignBottom="@+id/seekbarValues" android:layout_alignParentRight="true" - android:text="0" + android:text="@string/dash" android:layout_marginBottom="15dp" android:layout_marginRight="10dp" android:gravity="right" diff --git a/MPChartExample/res/layout/activity_bubblechart.xml b/MPChartExample/res/layout/activity_bubblechart.xml index 1cc55dfb42..d3df042fd0 100644 --- a/MPChartExample/res/layout/activity_bubblechart.xml +++ b/MPChartExample/res/layout/activity_bubblechart.xml @@ -1,7 +1,7 @@ + android:layout_height="match_parent"> - + + android:layout_height="match_parent"> - \ No newline at end of file + diff --git a/MPChartExample/res/layout/activity_candlechart.xml b/MPChartExample/res/layout/activity_candlechart.xml index f9384c9158..39df4c8104 100644 --- a/MPChartExample/res/layout/activity_candlechart.xml +++ b/MPChartExample/res/layout/activity_candlechart.xml @@ -1,14 +1,14 @@ + android:layout_height="match_parent"> - + + android:layout_height="match_parent"> - \ No newline at end of file + diff --git a/MPChartExample/res/layout/activity_colored_lines.xml b/MPChartExample/res/layout/activity_colored_lines.xml index cac3442e54..f6b61cfc54 100644 --- a/MPChartExample/res/layout/activity_colored_lines.xml +++ b/MPChartExample/res/layout/activity_colored_lines.xml @@ -2,7 +2,7 @@ + android:orientation="vertical"> - + diff --git a/MPChartExample/res/layout/activity_draw_chart.xml b/MPChartExample/res/layout/activity_draw_chart.xml index 5b3792395b..5e2f1a21fc 100644 --- a/MPChartExample/res/layout/activity_draw_chart.xml +++ b/MPChartExample/res/layout/activity_draw_chart.xml @@ -1,7 +1,7 @@ + android:layout_height="match_parent"> + android:layout_height="match_parent"> - + + android:layout_height="match_parent"> - \ No newline at end of file + diff --git a/MPChartExample/res/layout/activity_linechart.xml b/MPChartExample/res/layout/activity_linechart.xml index 7cadd8dd65..d7cd3a1e39 100644 --- a/MPChartExample/res/layout/activity_linechart.xml +++ b/MPChartExample/res/layout/activity_linechart.xml @@ -1,5 +1,6 @@ - @@ -8,7 +9,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/seekBar1" /> - + + android:layout_height="match_parent"> - \ No newline at end of file + diff --git a/MPChartExample/res/layout/activity_linechart_time.xml b/MPChartExample/res/layout/activity_linechart_time.xml index 27f70f8ba3..c63d3e6f41 100644 --- a/MPChartExample/res/layout/activity_linechart_time.xml +++ b/MPChartExample/res/layout/activity_linechart_time.xml @@ -25,7 +25,7 @@ android:layout_width="50dp" android:layout_height="wrap_content" android:layout_alignParentRight="true" - android:text="500" + android:text="@string/dash" android:layout_marginBottom="15dp" android:layout_marginRight="10dp" android:gravity="right" diff --git a/MPChartExample/res/layout/activity_listview_chart.xml b/MPChartExample/res/layout/activity_listview_chart.xml index b11c3d1ef8..12aa2f8500 100644 --- a/MPChartExample/res/layout/activity_listview_chart.xml +++ b/MPChartExample/res/layout/activity_listview_chart.xml @@ -2,7 +2,7 @@ + android:orientation="vertical"> + android:layout_height="fill_parent" + android:scrollbarFadeDuration="0"> - \ No newline at end of file + diff --git a/MPChartExample/res/layout/activity_performance_linechart.xml b/MPChartExample/res/layout/activity_performance_linechart.xml index d7cd5747fe..515321e1de 100644 --- a/MPChartExample/res/layout/activity_performance_linechart.xml +++ b/MPChartExample/res/layout/activity_performance_linechart.xml @@ -1,8 +1,7 @@ + android:layout_height="match_parent"> + android:layout_height="match_parent"> - + + android:layout_height="match_parent"> - \ No newline at end of file + diff --git a/MPChartExample/res/layout/activity_piechart_noseekbar.xml b/MPChartExample/res/layout/activity_piechart_noseekbar.xml index 52c62806b0..a92eec3c48 100644 --- a/MPChartExample/res/layout/activity_piechart_noseekbar.xml +++ b/MPChartExample/res/layout/activity_piechart_noseekbar.xml @@ -1,11 +1,11 @@ + android:layout_height="match_parent"> - \ No newline at end of file + diff --git a/MPChartExample/res/layout/activity_radarchart.xml b/MPChartExample/res/layout/activity_radarchart.xml index a197875bb8..aff98010c8 100644 --- a/MPChartExample/res/layout/activity_radarchart.xml +++ b/MPChartExample/res/layout/activity_radarchart.xml @@ -1,7 +1,7 @@ + android:layout_height="match_parent"> - - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_realm_wiki.xml b/MPChartExample/res/layout/activity_realm_wiki.xml index d4e27933cf..a9ba53fa6f 100644 --- a/MPChartExample/res/layout/activity_realm_wiki.xml +++ b/MPChartExample/res/layout/activity_realm_wiki.xml @@ -2,7 +2,6 @@ - \ No newline at end of file + diff --git a/MPChartExample/res/layout/activity_realtime_linechart.xml b/MPChartExample/res/layout/activity_realtime_linechart.xml index 0f09b88325..9ee392a8f2 100644 --- a/MPChartExample/res/layout/activity_realtime_linechart.xml +++ b/MPChartExample/res/layout/activity_realtime_linechart.xml @@ -7,5 +7,5 @@ android:id="@+id/chart1" android:layout_width="match_parent" android:layout_height="match_parent"/> - + diff --git a/MPChartExample/res/layout/activity_scatterchart.xml b/MPChartExample/res/layout/activity_scatterchart.xml index 947f8ce56d..41df167e5a 100644 --- a/MPChartExample/res/layout/activity_scatterchart.xml +++ b/MPChartExample/res/layout/activity_scatterchart.xml @@ -1,14 +1,14 @@ + android:layout_height="match_parent"> - + + android:layout_height="match_parent"> - \ No newline at end of file + diff --git a/MPChartExample/res/layout/activity_scrollview.xml b/MPChartExample/res/layout/activity_scrollview.xml index 95c78fedeb..f4865426fd 100644 --- a/MPChartExample/res/layout/activity_scrollview.xml +++ b/MPChartExample/res/layout/activity_scrollview.xml @@ -1,18 +1,18 @@ + android:layout_height="wrap_content"> - + + android:text="@string/scrollViewStart" /> @@ -30,13 +30,13 @@ - + - + android:text="@string/scrollViewEnd" /> + - \ No newline at end of file + diff --git a/MPChartExample/res/layout/custom_marker_view.xml b/MPChartExample/res/layout/custom_marker_view.xml index 12cb53c2e2..f8444bf8c4 100644 --- a/MPChartExample/res/layout/custom_marker_view.xml +++ b/MPChartExample/res/layout/custom_marker_view.xml @@ -1,8 +1,10 @@ + android:background="@drawable/marker2" + tools:ignore="Overdraw"> - - + diff --git a/MPChartExample/res/layout/list_item.xml b/MPChartExample/res/layout/list_item.xml index c9c11e93ba..2b6069b4f4 100644 --- a/MPChartExample/res/layout/list_item.xml +++ b/MPChartExample/res/layout/list_item.xml @@ -12,7 +12,7 @@ android:layout_alignParentTop="true" android:layout_marginLeft="4dp" android:text="Medium Text" - android:textSize="16dp"/> + android:textSize="16sp"/> @@ -32,11 +32,11 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceSmall" - android:text="NEW" + android:text="@string/textNew" android:background="@drawable/new_background" android:textColor="@android:color/white" android:id="@+id/tvNew" - android:textSize="11dp" + android:textSize="12sp" android:layout_marginRight="5dp" android:layout_centerVertical="true" android:layout_alignParentRight="true" diff --git a/MPChartExample/res/layout/list_item_section.xml b/MPChartExample/res/layout/list_item_section.xml new file mode 100644 index 0000000000..f71901d1fb --- /dev/null +++ b/MPChartExample/res/layout/list_item_section.xml @@ -0,0 +1,42 @@ + + + + + + + + + + diff --git a/MPChartExample/res/layout/radar_markerview.xml b/MPChartExample/res/layout/radar_markerview.xml index 9ab1b84121..d94768dd67 100644 --- a/MPChartExample/res/layout/radar_markerview.xml +++ b/MPChartExample/res/layout/radar_markerview.xml @@ -1,8 +1,10 @@ + android:background="@drawable/radar_marker" + tools:ignore="Overdraw"> + android:textAppearance="?android:attr/textAppearanceSmall" + tools:ignore="SmallSp" /> diff --git a/MPChartExample/res/menu/bar.xml b/MPChartExample/res/menu/bar.xml index 4bbfedd4db..e05fc59797 100644 --- a/MPChartExample/res/menu/bar.xml +++ b/MPChartExample/res/menu/bar.xml @@ -2,48 +2,48 @@ + android:id="@+id/viewGithub" + android:title="@string/viewGithub"> + android:id="@+id/actionToggleBarBorders" + android:title="@string/actionToggleBarBorders"> + android:id="@+id/actionToggleValues" + android:title="@string/actionToggleValues"> + android:id="@+id/actionToggleIcons" + android:title="@string/actionToggleIcons"> + android:id="@+id/actionToggleHighlight" + android:title="@string/actionToggleHighlight"> + android:id="@+id/actionTogglePinch" + android:title="@string/actionTogglePinch"> + android:id="@+id/actionToggleAutoScaleMinMax" + android:title="@string/actionToggleAutoScale"> + android:id="@+id/animateX" + android:title="@string/animateX"> + android:id="@+id/animateY" + android:title="@string/animateY"> + android:id="@+id/animateXY" + android:title="@string/animateXY"> + android:id="@+id/actionSave" + android:title="@string/actionSave"> - \ No newline at end of file + diff --git a/MPChartExample/res/menu/bubble.xml b/MPChartExample/res/menu/bubble.xml index b7950291e9..7b9ab5cd11 100644 --- a/MPChartExample/res/menu/bubble.xml +++ b/MPChartExample/res/menu/bubble.xml @@ -1,45 +1,45 @@ + + + android:title="@string/actionToggleValues"> + android:title="@string/actionToggleIcons"> + android:title="@string/actionToggleHighlight"> + + + android:id="@+id/actionToggleAutoScaleMinMax" + android:title="@string/actionToggleAutoScale"> + android:title="@string/animateX"> + android:title="@string/animateY"> + android:title="@string/animateXY"> - - - - + android:title="@string/actionSave"> - \ No newline at end of file + diff --git a/MPChartExample/res/menu/candle.xml b/MPChartExample/res/menu/candle.xml index cdf1c4e4dd..42a1a7e050 100644 --- a/MPChartExample/res/menu/candle.xml +++ b/MPChartExample/res/menu/candle.xml @@ -2,40 +2,48 @@ + android:id="@+id/viewGithub" + android:title="@string/viewGithub"> - - + android:id="@+id/actionToggleValues" + android:title="@string/actionToggleValues"> + android:id="@+id/actionToggleIcons" + android:title="@string/actionToggleIcons"> + android:id="@+id/actionToggleHighlight" + android:title="@string/actionToggleHighlight"> + android:id="@+id/actionToggleMakeShadowSameColorAsCandle" + android:title="@string/actionToggleCandleShadow"> + android:title="@string/actionTogglePinch"> + android:title="@string/actionToggleAutoScale"> + android:id="@+id/animateX" + android:title="@string/animateX"> + + + + + + - \ No newline at end of file + diff --git a/MPChartExample/res/menu/combined.xml b/MPChartExample/res/menu/combined.xml index 7e37ba0d83..c7def2509c 100644 --- a/MPChartExample/res/menu/combined.xml +++ b/MPChartExample/res/menu/combined.xml @@ -1,16 +1,21 @@ + + + android:title="@string/actionToggleLineValues"> + android:title="@string/actionToggleBarValues"> + android:title="@string/actionRemoveDataSet"> - \ No newline at end of file + + diff --git a/MPChartExample/res/menu/draw.xml b/MPChartExample/res/menu/draw.xml index 50f35239e7..36383db54f 100644 --- a/MPChartExample/res/menu/draw.xml +++ b/MPChartExample/res/menu/draw.xml @@ -3,34 +3,30 @@ + android:title="@string/actionToggleValues"> + android:title="@string/actionToggleFilled"> + android:title="@string/actionToggleCircles"> - - - - + android:title="@string/actionToggleHighlight"> + android:title="@string/actionTogglePinch"> + android:title="@string/actionToggleAutoScale"> + + - \ No newline at end of file + diff --git a/MPChartExample/res/menu/dynamical.xml b/MPChartExample/res/menu/dynamical.xml index c43a3a0ae6..68d4fab0c9 100644 --- a/MPChartExample/res/menu/dynamical.xml +++ b/MPChartExample/res/menu/dynamical.xml @@ -1,33 +1,33 @@ + + + android:title="@string/actionAddEntry"> - + android:title="@string/actionRemoveEntry"> - - + android:title="@string/actionAddDataSet"> - + android:title="@string/actionRemoveDataSet"> - + android:id="@+id/actionClear" + android:title="@string/actionClearChart"> - + android:id="@+id/actionSave" + android:title="@string/actionSave"> - \ No newline at end of file + + diff --git a/MPChartExample/res/menu/line.xml b/MPChartExample/res/menu/line.xml index f9f5be9615..a812b91b5a 100644 --- a/MPChartExample/res/menu/line.xml +++ b/MPChartExample/res/menu/line.xml @@ -1,64 +1,65 @@ + + + android:title="@string/actionToggleValues"> + android:title="@string/actionToggleIcons"> + android:title="@string/actionToggleFilled"> + android:title="@string/actionToggleCircles"> + android:title="@string/actionToggleCubic"> + android:title="@string/actionToggleStepped"> + android:title="@string/actionToggleHorizontalCubic"> + + + + + android:title="@string/actionToggleHighlight"> + android:title="@string/animateX"> + android:title="@string/animateY"> - - + android:title="@string/animateXY"> - - + android:title="@string/actionSave"> - - - \ No newline at end of file + + diff --git a/MPChartExample/res/menu/main.xml b/MPChartExample/res/menu/main.xml index b45d3bbb9f..9ac13dbbae 100644 --- a/MPChartExample/res/menu/main.xml +++ b/MPChartExample/res/menu/main.xml @@ -3,19 +3,15 @@ + android:title="@string/viewGithub"> - - + android:title="@string/reportProblem"> + android:title="@string/viewWebsite"> - \ No newline at end of file + diff --git a/MPChartExample/res/menu/only_github.xml b/MPChartExample/res/menu/only_github.xml new file mode 100644 index 0000000000..c0a9b66934 --- /dev/null +++ b/MPChartExample/res/menu/only_github.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/MPChartExample/res/menu/pie.xml b/MPChartExample/res/menu/pie.xml index 0e5323a590..b2de043409 100644 --- a/MPChartExample/res/menu/pie.xml +++ b/MPChartExample/res/menu/pie.xml @@ -1,49 +1,53 @@ + + + android:title="@string/actionToggleYValues"> + android:id="@+id/actionToggleXValues" + android:title="@string/actionToggleXValues"> + android:id="@+id/actionToggleIcons" + android:title="@string/actionToggleIcons"> + android:title="@string/actionTogglePercent"> + android:title="@string/actionToggleHole"> + + + + + android:title="@string/animateX"> + android:title="@string/animateY"> - - + android:title="@string/animateXY"> - - + android:title="@string/actionSave"> - \ No newline at end of file + diff --git a/MPChartExample/res/menu/radar.xml b/MPChartExample/res/menu/radar.xml index 14690f446c..2a5c19cf81 100644 --- a/MPChartExample/res/menu/radar.xml +++ b/MPChartExample/res/menu/radar.xml @@ -1,57 +1,62 @@ + + + android:title="@string/actionToggleValues"> + android:title="@string/actionToggleIcons"> + android:title="@string/actionToggleFilled"> + android:title="@string/actionToggleHighlight"> + android:title="@string/actionToggleHighlightCircle"> + android:id="@+id/actionToggleRotate" + android:title="@string/actionToggleRotation"> + android:id="@+id/actionToggleYLabels" + android:title="@string/actionToggleYValues"> + android:id="@+id/actionToggleXLabels" + android:title="@string/actionToggleXValues"> + android:id="@+id/actionToggleSpin" + android:title="@string/actionToggleSpin"> + android:id="@+id/animateX" + android:title="@string/animateX"> + android:id="@+id/animateY" + android:title="@string/animateY"> + android:id="@+id/animateXY" + android:title="@string/animateXY"> + android:id="@+id/actionSave" + android:title="@string/actionSave"> - \ No newline at end of file + + diff --git a/MPChartExample/res/menu/realm.xml b/MPChartExample/res/menu/realm.xml index f954443b30..ce149bef91 100644 --- a/MPChartExample/res/menu/realm.xml +++ b/MPChartExample/res/menu/realm.xml @@ -1,8 +1,13 @@ + + + android:title="@string/realmIOWebsite"> - \ No newline at end of file + + diff --git a/MPChartExample/res/menu/realtime.xml b/MPChartExample/res/menu/realtime.xml index a4b2d22a78..48cc7ccd0a 100644 --- a/MPChartExample/res/menu/realtime.xml +++ b/MPChartExample/res/menu/realtime.xml @@ -1,16 +1,25 @@ + + + android:title="@string/actionAddEntry"> + android:title="@string/actionClearChart"> + android:title="@string/actionFeedMultiple"> - \ No newline at end of file + + + + diff --git a/MPChartExample/res/menu/scatter.xml b/MPChartExample/res/menu/scatter.xml index b7950291e9..eb8e0efa67 100644 --- a/MPChartExample/res/menu/scatter.xml +++ b/MPChartExample/res/menu/scatter.xml @@ -1,45 +1,45 @@ + + + android:title="@string/actionToggleValues"> + android:title="@string/actionToggleIcons"> - - + android:title="@string/actionToggleHighlight"> + android:title="@string/animateX"> + android:title="@string/animateY"> - - + android:title="@string/animateXY"> + android:title="@string/actionTogglePinch"> + android:title="@string/actionToggleAutoScale"> + + - \ No newline at end of file + diff --git a/MPChartExample/res/values-sw600dp/dimens.xml b/MPChartExample/res/values-sw600dp/dimens.xml deleted file mode 100644 index 44f01db75f..0000000000 --- a/MPChartExample/res/values-sw600dp/dimens.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/MPChartExample/res/values-sw720dp-land/dimens.xml b/MPChartExample/res/values-sw720dp-land/dimens.xml deleted file mode 100644 index 61e3fa8fbc..0000000000 --- a/MPChartExample/res/values-sw720dp-land/dimens.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - 128dp - - diff --git a/MPChartExample/res/values-v11/styles.xml b/MPChartExample/res/values-v11/styles.xml deleted file mode 100644 index 3c02242ad0..0000000000 --- a/MPChartExample/res/values-v11/styles.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/MPChartExample/res/values-v14/styles.xml b/MPChartExample/res/values-v14/styles.xml deleted file mode 100644 index a91fd0372b..0000000000 --- a/MPChartExample/res/values-v14/styles.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/MPChartExample/res/values/dimens.xml b/MPChartExample/res/values/dimens.xml deleted file mode 100644 index 55c1e5908c..0000000000 --- a/MPChartExample/res/values/dimens.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - 16dp - 16dp - - diff --git a/MPChartExample/res/values/strings.xml b/MPChartExample/res/values/strings.xml index 7f59af64bb..d81d9b1a0c 100644 --- a/MPChartExample/res/values/strings.xml +++ b/MPChartExample/res/values/strings.xml @@ -2,7 +2,54 @@ MPAndroidChart Example - Settings - Hello world! + + View on GitHub + Problem Report + Developer Website + Save to Gallery + realm.io website + + Animate X + Animate Y + Animate XY + + Toggle Values + Toggle Y-Values + Toggle X-Values + + Toggle Icons + Toggle Highlight + Toggle PinchZoom + Toggle Auto Scale + + Toggle Line Values + Toggle Bar Values + Toggle Bar Borders + Toggle Filled + Toggle Circles + Toggle Shadow Color + + Toggle Cubic + Toggle Stepped + Toggle Horizontal Cubic + + Add Entry + Add Multiple + Remove Entry + Add Data Set + Remove Data Set + Clear chart + + Toggle Percent + Toggle Hole + Draw Center Text + Toggle Highlight Circle + Toggle Rotation + Spin Animation + + - + START OF SCROLLVIEW + END OF SCROLLVIEW + NEW diff --git a/MPChartExample/res/values/styles.xml b/MPChartExample/res/values/styles.xml index 6ce89c7ba4..9d5b53bd6c 100644 --- a/MPChartExample/res/values/styles.xml +++ b/MPChartExample/res/values/styles.xml @@ -1,19 +1,7 @@ - - - - diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index f6cffddcbe..5916619645 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -1,14 +1,18 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; 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 android.widget.Toast; import com.github.mikephil.charting.charts.BarChart; import com.github.mikephil.charting.components.XAxis; @@ -25,8 +29,8 @@ public class AnotherBarActivity extends DemoBase implements OnSeekBarChangeListener { - private BarChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private BarChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -36,48 +40,89 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_barchart); + setTitle("AnotherBarActivity"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); - mSeekBarY = findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); + chart = findViewById(R.id.chart1); - mChart.getDescription().setEnabled(false); + chart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn - mChart.setMaxVisibleValueCount(60); + chart.setMaxVisibleValueCount(60); // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); + chart.setPinchZoom(false); - mChart.setDrawBarShadow(false); - mChart.setDrawGridBackground(false); + chart.setDrawBarShadow(false); + chart.setDrawGridBackground(false); - XAxis xAxis = mChart.getXAxis(); + XAxis xAxis = chart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); xAxis.setDrawGridLines(false); - - mChart.getAxisLeft().setDrawGridLines(false); + + chart.getAxisLeft().setDrawGridLines(false); // setting data - mSeekBarX.setProgress(10); - mSeekBarY.setProgress(100); + seekBarX.setProgress(10); + seekBarY.setProgress(100); // add a nice and smooth animation - mChart.animateY(2500); - - mChart.getLegend().setEnabled(false); + chart.animateY(1500); + + chart.getLegend().setEnabled(false); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < seekBarX.getProgress(); i++) { + float multi = (seekBarY.getProgress() + 1); + float val = (float) (Math.random() * multi) + multi / 3; + values.add(new BarEntry(i, val)); + } + + BarDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(values, "Data Set"); + set1.setColors(ColorTemplate.VORDIPLOM_COLORS); + set1.setDrawValues(false); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); + + BarData data = new BarData(dataSets); + chart.setData(data); + chart.setFitBars(true); + } + + chart.invalidate(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.bar, menu); + menu.removeItem(R.id.actionToggleIcons); return true; } @@ -85,63 +130,71 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - for (IDataSet set : mChart.getData().getDataSets()) + for (IDataSet set : chart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); - mChart.invalidate(); + chart.invalidate(); break; } + /* + case R.id.actionToggleIcons: { break; } + */ case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionToggleBarBorders: { - for (IBarDataSet set : mChart.getData().getDataSets()) + for (IBarDataSet set : chart.getData().getDataSets()) ((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -149,52 +202,13 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); - - ArrayList yVals1 = new ArrayList(); - - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - float mult = (mSeekBarY.getProgress() + 1); - float val = (float) (Math.random() * mult) + mult / 3; - yVals1.add(new BarEntry(i, val)); - } - - BarDataSet set1; - - if (mChart.getData() != null && - mChart.getData().getDataSetCount() > 0) { - set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); - set1.setValues(yVals1); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); - } else { - set1 = new BarDataSet(yVals1, "Data Set"); - set1.setColors(ColorTemplate.VORDIPLOM_COLORS); - set1.setDrawValues(false); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - - BarData data = new BarData(dataSets); - mChart.setData(data); - mChart.setFitBars(true); - } - - mChart.invalidate(); + public void saveToGallery() { + saveToGallery(chart, "AnotherBarActivity"); } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStartTrackingTouch(SeekBar seekBar) {} @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStopTrackingTouch(SeekBar seekBar) {} } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index c0e3405625..fcdaae364d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -1,10 +1,13 @@ package com.xxmassdeveloper.mpchartexample; -import android.annotation.SuppressLint; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.RectF; +import android.net.Uri; import android.os.Bundle; -import android.support.v4.content.ContextCompat; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -12,7 +15,6 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; -import android.widget.Toast; import com.github.mikephil.charting.charts.BarChart; import com.github.mikephil.charting.components.Legend; @@ -32,7 +34,6 @@ import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.model.GradientColor; -import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.MPPointF; import com.xxmassdeveloper.mpchartexample.custom.DayAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; @@ -45,8 +46,8 @@ public class BarChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - protected BarChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private BarChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -56,35 +57,40 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_barchart); + setTitle("BarChartActivity"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarY = findViewById(R.id.seekBar2); + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); + + seekBarY.setOnSeekBarChangeListener(this); + seekBarX.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); - mChart.setDrawBarShadow(false); - mChart.setDrawValueAboveBar(true); + chart.setDrawBarShadow(false); + chart.setDrawValueAboveBar(true); - mChart.getDescription().setEnabled(false); + chart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn - mChart.setMaxVisibleValueCount(60); + chart.setMaxVisibleValueCount(60); // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); + chart.setPinchZoom(false); - mChart.setDrawGridBackground(false); - // mChart.setDrawYLabels(false); + chart.setDrawGridBackground(false); + // chart.setDrawYLabels(false); - IAxisValueFormatter xAxisFormatter = new DayAxisValueFormatter(mChart); + IAxisValueFormatter xAxisFormatter = new DayAxisValueFormatter(chart); - XAxis xAxis = mChart.getXAxis(); + XAxis xAxis = chart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setTypeface(mTfLight); + xAxis.setTypeface(tfLight); xAxis.setDrawGridLines(false); xAxis.setGranularity(1f); // only intervals of 1 day xAxis.setLabelCount(7); @@ -92,23 +98,23 @@ protected void onCreate(Bundle savedInstanceState) { IAxisValueFormatter custom = new MyAxisValueFormatter(); - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(mTfLight); + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setTypeface(tfLight); leftAxis.setLabelCount(8, false); leftAxis.setValueFormatter(custom); leftAxis.setPosition(YAxisLabelPosition.OUTSIDE_CHART); leftAxis.setSpaceTop(15f); leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) - YAxis rightAxis = mChart.getAxisRight(); + YAxis rightAxis = chart.getAxisRight(); rightAxis.setDrawGridLines(false); - rightAxis.setTypeface(mTfLight); + rightAxis.setTypeface(tfLight); rightAxis.setLabelCount(8, false); rightAxis.setValueFormatter(custom); rightAxis.setSpaceTop(15f); rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); l.setOrientation(Legend.LegendOrientation.HORIZONTAL); @@ -117,25 +123,85 @@ protected void onCreate(Bundle savedInstanceState) { l.setFormSize(9f); l.setTextSize(11f); l.setXEntrySpace(4f); - // l.setExtra(ColorTemplate.VORDIPLOM_COLORS, new String[] { "abc", - // "def", "ghj", "ikl", "mno" }); - // l.setCustom(ColorTemplate.VORDIPLOM_COLORS, new String[] { "abc", - // "def", "ghj", "ikl", "mno" }); XYMarkerView mv = new XYMarkerView(this, xAxisFormatter); - mv.setChartView(mChart); // For bounds control - mChart.setMarker(mv); // Set the marker to the chart + mv.setChartView(chart); // For bounds control + chart.setMarker(mv); // Set the marker to the chart + // setting data + seekBarY.setProgress(50); + seekBarX.setProgress(12); setData(12, 50); - // setting data - mSeekBarY.setProgress(50); - mSeekBarX.setProgress(12); + // chart.setDrawLegend(false); + } + + private void setData(int count, float range) { + + float start = 1f; + + ArrayList values = new ArrayList<>(); + + for (int i = (int) start; i < start + count; i++) { + float val = (float) (Math.random() * (range + 1)); + + if (Math.random() * 100 < 25) { + values.add(new BarEntry(i, val, getResources().getDrawable(R.drawable.star))); + } else { + values.add(new BarEntry(i, val)); + } + } + + BarDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + + } else { + set1 = new BarDataSet(values, "The year 2017"); + + set1.setDrawIcons(false); + +// set1.setColors(ColorTemplate.MATERIAL_COLORS); + + /*int startColor = ContextCompat.getColor(this, android.R.color.holo_blue_dark); + int endColor = ContextCompat.getColor(this, android.R.color.holo_blue_bright); + set1.setGradientColor(startColor, endColor);*/ + + int startColor1 = ContextCompat.getColor(this, android.R.color.holo_orange_light); + int startColor2 = ContextCompat.getColor(this, android.R.color.holo_blue_light); + int startColor3 = ContextCompat.getColor(this, android.R.color.holo_orange_light); + int startColor4 = ContextCompat.getColor(this, android.R.color.holo_green_light); + int startColor5 = ContextCompat.getColor(this, android.R.color.holo_red_light); + int endColor1 = ContextCompat.getColor(this, android.R.color.holo_blue_dark); + int endColor2 = ContextCompat.getColor(this, android.R.color.holo_purple); + int endColor3 = ContextCompat.getColor(this, android.R.color.holo_green_dark); + int endColor4 = ContextCompat.getColor(this, android.R.color.holo_red_dark); + int endColor5 = ContextCompat.getColor(this, android.R.color.holo_orange_dark); + + List gradientColors = new ArrayList<>(); + gradientColors.add(new GradientColor(startColor1, endColor1)); + gradientColors.add(new GradientColor(startColor2, endColor2)); + gradientColors.add(new GradientColor(startColor3, endColor3)); + gradientColors.add(new GradientColor(startColor4, endColor4)); + gradientColors.add(new GradientColor(startColor5, endColor5)); + + set1.setGradientColors(gradientColors); - mSeekBarY.setOnSeekBarChangeListener(this); - mSeekBarX.setOnSeekBarChangeListener(this); + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); - // mChart.setDrawLegend(false); + BarData data = new BarData(dataSets); + data.setValueTextSize(10f); + data.setValueTypeface(tfLight); + data.setBarWidth(0.9f); + + chart.setData(data); + } } @Override @@ -148,68 +214,72 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - for (IDataSet set : mChart.getData().getDataSets()) + for (IDataSet set : chart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleIcons: { - for (IDataSet set : mChart.getData().getDataSets()) + for (IDataSet set : chart.getData().getDataSets()) set.setDrawIcons(!set.isDrawIconsEnabled()); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionToggleBarBorders: { - for (IBarDataSet set : mChart.getData().getDataSets()) + for (IBarDataSet set : chart.getData().getDataSets()) ((BarDataSet) set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -219,110 +289,42 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 2)); - tvY.setText("" + (mSeekBarY.getProgress())); + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); - setData(mSeekBarX.getProgress() + 1 , mSeekBarY.getProgress()); - mChart.invalidate(); + setData(seekBarX.getProgress(), seekBarY.getProgress()); + chart.invalidate(); } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub + public void saveToGallery() { + saveToGallery(chart, "BarChartActivity"); } @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - } - - private void setData(int count, float range) { - - float start = 1f; - - ArrayList yVals1 = new ArrayList(); - - for (int i = (int) start; i < start + count + 1; i++) { - float mult = (range + 1); - float val = (float) (Math.random() * mult); - - if (Math.random() * 100 < 25) { - yVals1.add(new BarEntry(i, val, getResources().getDrawable(R.drawable.star))); - } else { - yVals1.add(new BarEntry(i, val)); - } - } - - BarDataSet set1; - - if (mChart.getData() != null && - mChart.getData().getDataSetCount() > 0) { - set1 = (BarDataSet) mChart.getData().getDataSetByIndex(0); - set1.setValues(yVals1); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); - } else { - set1 = new BarDataSet(yVals1, "The year 2017"); - - set1.setDrawIcons(false); - -// set1.setColors(ColorTemplate.MATERIAL_COLORS); - - /*int startColor = ContextCompat.getColor(this, android.R.color.holo_blue_dark); - int endColor = ContextCompat.getColor(this, android.R.color.holo_blue_bright); - set1.setGradientColor(startColor, endColor);*/ - - int startColor1 = ContextCompat.getColor(this, android.R.color.holo_orange_light); - int startColor2 = ContextCompat.getColor(this, android.R.color.holo_blue_light); - int startColor3 = ContextCompat.getColor(this, android.R.color.holo_orange_light); - int startColor4 = ContextCompat.getColor(this, android.R.color.holo_green_light); - int startColor5 = ContextCompat.getColor(this, android.R.color.holo_red_light); - int endColor1 = ContextCompat.getColor(this, android.R.color.holo_blue_dark); - int endColor2 = ContextCompat.getColor(this, android.R.color.holo_purple); - int endColor3 = ContextCompat.getColor(this, android.R.color.holo_green_dark); - int endColor4 = ContextCompat.getColor(this, android.R.color.holo_red_dark); - int endColor5 = ContextCompat.getColor(this, android.R.color.holo_orange_dark); - - List gradientColors = new ArrayList<>(); - gradientColors.add(new GradientColor(startColor1, endColor1)); - gradientColors.add(new GradientColor(startColor2, endColor2)); - gradientColors.add(new GradientColor(startColor3, endColor3)); - gradientColors.add(new GradientColor(startColor4, endColor4)); - gradientColors.add(new GradientColor(startColor5, endColor5)); - - set1.setGradientColors(gradientColors); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); + public void onStartTrackingTouch(SeekBar seekBar) {} - BarData data = new BarData(dataSets); - data.setValueTextSize(10f); - data.setValueTypeface(mTfLight); - data.setBarWidth(0.9f); - - mChart.setData(data); - } - } + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} - protected RectF mOnValueSelectedRectF = new RectF(); + private RectF onValueSelectedRectF = new RectF(); - @SuppressLint("NewApi") @Override public void onValueSelected(Entry e, Highlight h) { if (e == null) return; - RectF bounds = mOnValueSelectedRectF; - mChart.getBarBounds((BarEntry) e, bounds); - MPPointF position = mChart.getPosition(e, AxisDependency.LEFT); + RectF bounds = onValueSelectedRectF; + chart.getBarBounds((BarEntry) e, bounds); + MPPointF position = chart.getPosition(e, AxisDependency.LEFT); Log.i("bounds", bounds.toString()); Log.i("position", position.toString()); Log.i("x-index", - "low: " + mChart.getLowestVisibleX() + ", high: " - + mChart.getHighestVisibleX()); + "low: " + chart.getLowestVisibleX() + ", high: " + + chart.getHighestVisibleX()); MPPointF.recycleInstance(position); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 204dc1fe64..d0c0bc453d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -1,8 +1,13 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -29,12 +34,13 @@ import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.Locale; public class BarChartActivityMultiDataset extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - private BarChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private BarChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -44,51 +50,53 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_barchart); + setTitle("BarChartActivityMultiDataset"); + tvX = findViewById(R.id.tvXMax); tvX.setTextSize(10); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); - mSeekBarY = findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - mChart.getDescription().setEnabled(false); + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + chart.getDescription().setEnabled(false); -// mChart.setDrawBorders(true); +// chart.setDrawBorders(true); // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); + chart.setPinchZoom(false); - mChart.setDrawBarShadow(false); + chart.setDrawBarShadow(false); - mChart.setDrawGridBackground(false); + chart.setDrawGridBackground(false); // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - mv.setChartView(mChart); // For bounds control - mChart.setMarker(mv); // Set the marker to the chart + mv.setChartView(chart); // For bounds control + chart.setMarker(mv); // Set the marker to the chart - mSeekBarX.setProgress(10); - mSeekBarY.setProgress(100); + seekBarX.setProgress(10); + seekBarY.setProgress(100); - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); l.setOrientation(Legend.LegendOrientation.VERTICAL); l.setDrawInside(true); - l.setTypeface(mTfLight); + l.setTypeface(tfLight); l.setYOffset(0f); l.setXOffset(10f); l.setYEntrySpace(0f); l.setTextSize(8f); - XAxis xAxis = mChart.getXAxis(); - xAxis.setTypeface(mTfLight); + XAxis xAxis = chart.getXAxis(); + xAxis.setTypeface(tfLight); xAxis.setGranularity(1f); xAxis.setCenterAxisLabels(true); xAxis.setValueFormatter(new IAxisValueFormatter() { @@ -98,14 +106,88 @@ public String getFormattedValue(float value, AxisBase axis) { } }); - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(mTfLight); + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setTypeface(tfLight); leftAxis.setValueFormatter(new LargeValueFormatter()); leftAxis.setDrawGridLines(false); leftAxis.setSpaceTop(35f); leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) - mChart.getAxisRight().setEnabled(false); + chart.getAxisRight().setEnabled(false); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + float groupSpace = 0.08f; + float barSpace = 0.03f; // x4 DataSet + float barWidth = 0.2f; // x4 DataSet + // (0.2 + 0.03) * 4 + 0.08 = 1.00 -> interval per "group" + + int groupCount = seekBarX.getProgress() + 1; + int startYear = 1980; + int endYear = startYear + groupCount; + + tvX.setText(String.format(Locale.ENGLISH, "%d-%d", startYear, endYear)); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + ArrayList values1 = new ArrayList<>(); + ArrayList values2 = new ArrayList<>(); + ArrayList values3 = new ArrayList<>(); + ArrayList values4 = new ArrayList<>(); + + float randomMultiplier = seekBarY.getProgress() * 100000f; + + for (int i = startYear; i < endYear; i++) { + values1.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + values2.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + values3.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + values4.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + } + + BarDataSet set1, set2, set3, set4; + + if (chart.getData() != null && chart.getData().getDataSetCount() > 0) { + + set1 = (BarDataSet) chart.getData().getDataSetByIndex(0); + set2 = (BarDataSet) chart.getData().getDataSetByIndex(1); + set3 = (BarDataSet) chart.getData().getDataSetByIndex(2); + set4 = (BarDataSet) chart.getData().getDataSetByIndex(3); + set1.setValues(values1); + set2.setValues(values2); + set3.setValues(values3); + set4.setValues(values4); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + + } else { + // create 4 DataSets + set1 = new BarDataSet(values1, "Company A"); + set1.setColor(Color.rgb(104, 241, 175)); + set2 = new BarDataSet(values2, "Company B"); + set2.setColor(Color.rgb(164, 228, 251)); + set3 = new BarDataSet(values3, "Company C"); + set3.setColor(Color.rgb(242, 247, 158)); + set4 = new BarDataSet(values4, "Company D"); + set4.setColor(Color.rgb(255, 102, 0)); + + BarData data = new BarData(set1, set2, set3, set4); + data.setValueFormatter(new LargeValueFormatter()); + data.setValueTypeface(tfLight); + + chart.setData(data); + } + + // specify the width each bar should have + chart.getBarData().setBarWidth(barWidth); + + // restrict the x-axis range + chart.getXAxis().setAxisMinimum(startYear); + + // barData.getGroupWith(...) is a helper that calculates the width each group needs based on the provided parameters + chart.getXAxis().setAxisMaximum(startYear + chart.getBarData().getGroupWidth(groupSpace, barSpace) * groupCount); + chart.groupBars(startYear, groupSpace, barSpace); + chart.invalidate(); } @Override @@ -118,56 +200,65 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - for (IBarDataSet set : mChart.getData().getDataSets()) + for (IBarDataSet set : chart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionToggleBarBorders: { - for (IBarDataSet set : mChart.getData().getDataSets()) + for (IBarDataSet set : chart.getData().getDataSets()) ((BarDataSet) set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionSave: { - // mChart.saveToGallery("title"+System.currentTimeMillis()); - mChart.saveToPath("title" + System.currentTimeMillis(), ""); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } } @@ -175,88 +266,15 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - float groupSpace = 0.08f; - float barSpace = 0.03f; // x4 DataSet - float barWidth = 0.2f; // x4 DataSet - // (0.2 + 0.03) * 4 + 0.08 = 1.00 -> interval per "group" - - int groupCount = mSeekBarX.getProgress() + 1; - int startYear = 1980; - int endYear = startYear + groupCount; - - tvX.setText(startYear + "-" + endYear); - tvY.setText("" + (mSeekBarY.getProgress())); - - ArrayList yVals1 = new ArrayList(); - ArrayList yVals2 = new ArrayList(); - ArrayList yVals3 = new ArrayList(); - ArrayList yVals4 = new ArrayList(); - - float randomMultiplier = mSeekBarY.getProgress() * 100000f; - - for (int i = startYear; i < endYear; i++) { - yVals1.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); - yVals2.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); - yVals3.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); - yVals4.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); - } - - BarDataSet set1, set2, set3, set4; - - if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { - - set1 = (BarDataSet) mChart.getData().getDataSetByIndex(0); - set2 = (BarDataSet) mChart.getData().getDataSetByIndex(1); - set3 = (BarDataSet) mChart.getData().getDataSetByIndex(2); - set4 = (BarDataSet) mChart.getData().getDataSetByIndex(3); - set1.setValues(yVals1); - set2.setValues(yVals2); - set3.setValues(yVals3); - set4.setValues(yVals4); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); - - } else { - // create 4 DataSets - set1 = new BarDataSet(yVals1, "Company A"); - set1.setColor(Color.rgb(104, 241, 175)); - set2 = new BarDataSet(yVals2, "Company B"); - set2.setColor(Color.rgb(164, 228, 251)); - set3 = new BarDataSet(yVals3, "Company C"); - set3.setColor(Color.rgb(242, 247, 158)); - set4 = new BarDataSet(yVals4, "Company D"); - set4.setColor(Color.rgb(255, 102, 0)); - - BarData data = new BarData(set1, set2, set3, set4); - data.setValueFormatter(new LargeValueFormatter()); - data.setValueTypeface(mTfLight); - - mChart.setData(data); - } - - // specify the width each bar should have - mChart.getBarData().setBarWidth(barWidth); - - // restrict the x-axis range - mChart.getXAxis().setAxisMinimum(startYear); - - // barData.getGroupWith(...) is a helper that calculates the width each group needs based on the provided parameters - mChart.getXAxis().setAxisMaximum(startYear + mChart.getBarData().getGroupWidth(groupSpace, barSpace) * groupCount); - mChart.groupBars(startYear, groupSpace, barSpace); - mChart.invalidate(); + public void saveToGallery() { + saveToGallery(chart, "BarChartActivityMultiDataset"); } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - } + public void onStartTrackingTouch(SeekBar seekBar) {} @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - } + public void onStopTrackingTouch(SeekBar seekBar) {} @Override public void onValueSelected(Entry e, Highlight h) { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index 82b039909f..d7c16df440 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -1,20 +1,23 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; 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 android.widget.Toast; import com.github.mikephil.charting.charts.BarChart; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.Legend.LegendForm; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; @@ -29,11 +32,11 @@ public class BarChartActivitySinus extends DemoBase implements OnSeekBarChangeListener { - protected BarChart mChart; - private SeekBar mSeekBarX; + private BarChart chart; + private SeekBar seekBarX; private TextView tvX; - private List mSinusData; + private List data; @Override protected void onCreate(Bundle savedInstanceState) { @@ -42,57 +45,59 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_barchart_sinus); - mSinusData = FileUtils.loadBarEntriesFromAssets(getAssets(), "othersine.txt"); + setTitle("BarChartActivitySinus"); + + data = FileUtils.loadBarEntriesFromAssets(getAssets(), "othersine.txt"); tvX = findViewById(R.id.tvValueCount); - mSeekBarX = findViewById(R.id.seekbarValues); + seekBarX = findViewById(R.id.seekbarValues); - mChart = findViewById(R.id.chart1); + chart = findViewById(R.id.chart1); - mChart.setDrawBarShadow(false); - mChart.setDrawValueAboveBar(true); + chart.setDrawBarShadow(false); + chart.setDrawValueAboveBar(true); - mChart.getDescription().setEnabled(false); + chart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn - mChart.setMaxVisibleValueCount(60); + chart.setMaxVisibleValueCount(60); // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); + chart.setPinchZoom(false); // draw shadows for each bar that show the maximum value - // mChart.setDrawBarShadow(true); + // chart.setDrawBarShadow(true); - // mChart.setDrawXLabels(false); + // chart.setDrawXLabels(false); - mChart.setDrawGridBackground(false); - // mChart.setDrawYLabels(false); + chart.setDrawGridBackground(false); + // chart.setDrawYLabels(false); - XAxis xAxis = mChart.getXAxis(); + XAxis xAxis = chart.getXAxis(); xAxis.setEnabled(false); - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(mTfLight); + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setTypeface(tfLight); leftAxis.setLabelCount(6, false); leftAxis.setAxisMinimum(-2.5f); leftAxis.setAxisMaximum(2.5f); leftAxis.setGranularityEnabled(true); leftAxis.setGranularity(0.1f); - YAxis rightAxis = mChart.getAxisRight(); + YAxis rightAxis = chart.getAxisRight(); rightAxis.setDrawGridLines(false); - rightAxis.setTypeface(mTfLight); + rightAxis.setTypeface(tfLight); rightAxis.setLabelCount(6, false); rightAxis.setAxisMinimum(-2.5f); rightAxis.setAxisMaximum(2.5f); rightAxis.setGranularity(0.1f); - mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarX.setProgress(150); // set data + seekBarX.setOnSeekBarChangeListener(this); + seekBarX.setProgress(150); // set data - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); l.setOrientation(Legend.LegendOrientation.HORIZONTAL); @@ -102,7 +107,37 @@ protected void onCreate(Bundle savedInstanceState) { l.setTextSize(11f); l.setXEntrySpace(4f); - mChart.animateXY(2000, 2000); + chart.animateXY(1500, 1500); + } + + private void setData(int count) { + + ArrayList entries = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + entries.add(data.get(i)); + } + + BarDataSet set; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set = (BarDataSet) chart.getData().getDataSetByIndex(0); + set.setValues(entries); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + set = new BarDataSet(entries, "Sinus Function"); + set.setColor(Color.rgb(240, 120, 124)); + } + + BarData data = new BarData(set); + data.setValueTextSize(10f); + data.setValueTypeface(tfLight); + data.setDrawValues(false); + data.setBarWidth(0.8f); + + chart.setData(data); } @Override @@ -115,61 +150,66 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - for (IBarDataSet set : mChart.getData().getDataSets()) + for (IBarDataSet set : chart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionToggleBarBorders: { - for (IBarDataSet set : mChart.getData().getDataSets()) + for (IBarDataSet set : chart.getData().getDataSets()) ((BarDataSet) set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.animateX: { - mChart.animateX(1500); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(1500); + chart.animateY(2000); break; } case R.id.animateXY: { - mChart.animateXY(2000, 2000); + chart.animateXY(2000, 2000); break; } case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -179,51 +219,21 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress())); + tvX.setText(String.valueOf(seekBarX.getProgress())); - setData(mSeekBarX.getProgress()); - mChart.invalidate(); + setData(seekBarX.getProgress()); + chart.invalidate(); } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - + public void saveToGallery() { + saveToGallery(chart, "BarChartActivitySinus"); } @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - private void setData(int count) { - - ArrayList entries = new ArrayList(); - - for (int i = 0; i < count; i++) { - entries.add(mSinusData.get(i)); - } - - BarDataSet set; + public void onStartTrackingTouch(SeekBar seekBar) {} - if (mChart.getData() != null && - mChart.getData().getDataSetCount() > 0) { - set = (BarDataSet) mChart.getData().getDataSetByIndex(0); - set.setValues(entries); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); - } else { - set = new BarDataSet(entries, "Sinus Function"); - set.setColor(Color.rgb(240, 120, 124)); - } - - BarData data = new BarData(set); - data.setValueTextSize(10f); - data.setValueTypeface(mTfLight); - data.setDrawValues(false); - data.setBarWidth(0.8f); + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} - mChart.setData(data); - } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 7d6bd44896..ce73317860 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -1,9 +1,12 @@ package com.xxmassdeveloper.mpchartexample; +import android.content.Intent; import android.graphics.Color; -import android.graphics.Typeface; +import android.net.Uri; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; import android.view.WindowManager; import com.github.mikephil.charting.charts.BarChart; @@ -26,8 +29,7 @@ public class BarChartPositiveNegative extends DemoBase { - protected BarChart mChart; - private Typeface mTf; + private BarChart chart; @Override protected void onCreate(Bundle savedInstanceState) { @@ -36,27 +38,28 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_barchart_noseekbar); - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - mChart = findViewById(R.id.chart1); - mChart.setBackgroundColor(Color.WHITE); - mChart.setExtraTopOffset(-30f); - mChart.setExtraBottomOffset(10f); - mChart.setExtraLeftOffset(70f); - mChart.setExtraRightOffset(70f); + setTitle("BarChartPositiveNegative"); - mChart.setDrawBarShadow(false); - mChart.setDrawValueAboveBar(true); + chart = findViewById(R.id.chart1); + chart.setBackgroundColor(Color.WHITE); + chart.setExtraTopOffset(-30f); + chart.setExtraBottomOffset(10f); + chart.setExtraLeftOffset(70f); + chart.setExtraRightOffset(70f); - mChart.getDescription().setEnabled(false); + chart.setDrawBarShadow(false); + chart.setDrawValueAboveBar(true); + + chart.getDescription().setEnabled(false); // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); + chart.setPinchZoom(false); - mChart.setDrawGridBackground(false); + chart.setDrawGridBackground(false); - XAxis xAxis = mChart.getXAxis(); + XAxis xAxis = chart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setTypeface(mTf); + xAxis.setTypeface(tfRegular); xAxis.setDrawGridLines(false); xAxis.setDrawAxisLine(false); xAxis.setTextColor(Color.LTGRAY); @@ -65,7 +68,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setCenterAxisLabels(true); xAxis.setGranularity(1f); - YAxis left = mChart.getAxisLeft(); + YAxis left = chart.getAxisLeft(); left.setDrawLabels(false); left.setSpaceTop(25f); left.setSpaceBottom(25f); @@ -74,8 +77,8 @@ protected void onCreate(Bundle savedInstanceState) { left.setDrawZeroLine(true); // draw a zero line left.setZeroLineColor(Color.GRAY); left.setZeroLineWidth(0.7f); - mChart.getAxisRight().setEnabled(false); - mChart.getLegend().setEnabled(false); + chart.getAxisRight().setEnabled(false); + chart.getLegend().setEnabled(false); // THIS IS THE ORIGINAL DATA YOU WANT TO PLOT final List data = new ArrayList<>(); @@ -97,8 +100,8 @@ public String getFormattedValue(float value, AxisBase axis) { private void setData(List dataList) { - ArrayList values = new ArrayList(); - List colors = new ArrayList(); + ArrayList values = new ArrayList<>(); + List colors = new ArrayList<>(); int green = Color.rgb(110, 190, 102); int red = Color.rgb(211, 74, 88); @@ -118,12 +121,12 @@ private void setData(List dataList) { BarDataSet set; - if (mChart.getData() != null && - mChart.getData().getDataSetCount() > 0) { - set = (BarDataSet)mChart.getData().getDataSetByIndex(0); + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set = (BarDataSet) chart.getData().getDataSetByIndex(0); set.setValues(values); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); } else { set = new BarDataSet(values, "Values"); set.setColors(colors); @@ -131,12 +134,12 @@ private void setData(List dataList) { BarData data = new BarData(set); data.setValueTextSize(13f); - data.setValueTypeface(mTf); + data.setValueTypeface(tfRegular); data.setValueFormatter(new ValueFormatter()); data.setBarWidth(0.8f); - mChart.setData(data); - mChart.invalidate(); + chart.setData(data); + chart.invalidate(); } } @@ -145,11 +148,11 @@ private void setData(List dataList) { */ private class Data { - public String xAxisValue; - public float yValue; - public float xValue; + String xAxisValue; + float yValue; + float xValue; - public Data(float xValue, float yValue, String xAxisValue) { + Data(float xValue, float yValue, String xAxisValue) { this.xAxisValue = xAxisValue; this.yValue = yValue; this.xValue = xValue; @@ -161,7 +164,7 @@ private class ValueFormatter implements IValueFormatter private DecimalFormat mFormat; - public ValueFormatter() { + ValueFormatter() { mFormat = new DecimalFormat("######.0"); } @@ -170,4 +173,28 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View return mFormat.format(value); } } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java")); + startActivity(i); + break; + } + } + + return true; + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java index bc1750381e..098c8f242b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -1,8 +1,13 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -13,7 +18,6 @@ import com.github.mikephil.charting.charts.BubbleChart; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BubbleData; @@ -33,10 +37,10 @@ public class BubbleChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - private BubbleChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private BubbleChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; - + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -44,52 +48,106 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_bubblechart); + setTitle("BubbleChartActivity"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); - mSeekBarY = findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); - mChart.getDescription().setEnabled(false); + chart = findViewById(R.id.chart1); + chart.getDescription().setEnabled(false); - mChart.setOnChartValueSelectedListener(this); + chart.setOnChartValueSelectedListener(this); - mChart.setDrawGridBackground(false); + chart.setDrawGridBackground(false); - mChart.setTouchEnabled(true); + chart.setTouchEnabled(true); // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); + chart.setDragEnabled(true); + chart.setScaleEnabled(true); - mChart.setMaxVisibleValueCount(200); - mChart.setPinchZoom(true); + chart.setMaxVisibleValueCount(200); + chart.setPinchZoom(true); - mSeekBarX.setProgress(10); - mSeekBarY.setProgress(50); + seekBarX.setProgress(10); + seekBarY.setProgress(50); - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); l.setOrientation(Legend.LegendOrientation.VERTICAL); l.setDrawInside(false); - l.setTypeface(mTfLight); + l.setTypeface(tfLight); - YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(mTfLight); + YAxis yl = chart.getAxisLeft(); + yl.setTypeface(tfLight); yl.setSpaceTop(30f); yl.setSpaceBottom(30f); yl.setDrawZeroLine(false); - - mChart.getAxisRight().setEnabled(false); - XAxis xl = mChart.getXAxis(); + chart.getAxisRight().setEnabled(false); + + XAxis xl = chart.getXAxis(); xl.setPosition(XAxis.XAxisPosition.BOTTOM); - xl.setTypeface(mTfLight); + xl.setTypeface(tfLight); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + int count = seekBarX.getProgress(); + int range = seekBarY.getProgress(); + + tvX.setText(String.valueOf(count)); + tvY.setText(String.valueOf(range)); + + ArrayList values1 = new ArrayList<>(); + ArrayList values2 = new ArrayList<>(); + ArrayList values3 = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + values1.add(new BubbleEntry(i, (float) (Math.random() * range), (float) (Math.random() * range), getResources().getDrawable(R.drawable.star))); + values2.add(new BubbleEntry(i, (float) (Math.random() * range), (float) (Math.random() * range), getResources().getDrawable(R.drawable.star))); + values3.add(new BubbleEntry(i, (float) (Math.random() * range), (float) (Math.random() * range))); + } + + // create a dataset and give it a type + BubbleDataSet set1 = new BubbleDataSet(values1, "DS 1"); + set1.setDrawIcons(false); + set1.setColor(ColorTemplate.COLORFUL_COLORS[0], 130); + set1.setDrawValues(true); + + BubbleDataSet set2 = new BubbleDataSet(values2, "DS 2"); + set2.setDrawIcons(false); + set2.setIconsOffset(new MPPointF(0, 15)); + set2.setColor(ColorTemplate.COLORFUL_COLORS[1], 130); + set2.setDrawValues(true); + + BubbleDataSet set3 = new BubbleDataSet(values3, "DS 3"); + set3.setColor(ColorTemplate.COLORFUL_COLORS[2], 130); + set3.setDrawValues(true); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); // add the data sets + dataSets.add(set2); + dataSets.add(set3); + + // create a data object with the data sets + BubbleData data = new BubbleData(dataSets); + data.setDrawValues(false); + data.setValueTypeface(tfLight); + data.setValueTextSize(8f); + data.setValueTextColor(Color.WHITE); + data.setHighlightCircleWidth(1.5f); + + chart.setData(data); + chart.invalidate(); } @Override @@ -102,56 +160,65 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - for (IDataSet set : mChart.getData().getDataSets()) + for (IDataSet set : chart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleIcons: { - for (IDataSet set : mChart.getData().getDataSets()) + for (IDataSet set : chart.getData().getDataSets()) set.setDrawIcons(!set.isDrawIconsEnabled()); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionSave: { - mChart.saveToPath("title" + System.currentTimeMillis(), ""); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } } @@ -159,70 +226,8 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - int count = mSeekBarX.getProgress(); - int range = mSeekBarY.getProgress(); - - tvX.setText("" + count); - tvY.setText("" + range); - - ArrayList yVals1 = new ArrayList(); - ArrayList yVals2 = new ArrayList(); - ArrayList yVals3 = new ArrayList(); - - for (int i = 0; i < count; i++) { - float val = (float) (Math.random() * range); - float size = (float) (Math.random() * range); - - yVals1.add(new BubbleEntry(i, val, size, getResources().getDrawable(R.drawable.star))); - } - - for (int i = 0; i < count; i++) { - float val = (float) (Math.random() * range); - float size = (float) (Math.random() * range); - - yVals2.add(new BubbleEntry(i, val, size, getResources().getDrawable(R.drawable.star))); - } - - for (int i = 0; i < count; i++) { - float val = (float) (Math.random() * range); - float size = (float) (Math.random() * range); - - yVals3.add(new BubbleEntry(i, val, size)); - } - - // create a dataset and give it a type - BubbleDataSet set1 = new BubbleDataSet(yVals1, "DS 1"); - set1.setDrawIcons(false); - set1.setColor(ColorTemplate.COLORFUL_COLORS[0], 130); - set1.setDrawValues(true); - - BubbleDataSet set2 = new BubbleDataSet(yVals2, "DS 2"); - set2.setDrawIcons(false); - set2.setIconsOffset(new MPPointF(0, 15)); - set2.setColor(ColorTemplate.COLORFUL_COLORS[1], 130); - set2.setDrawValues(true); - - BubbleDataSet set3 = new BubbleDataSet(yVals3, "DS 3"); - set3.setColor(ColorTemplate.COLORFUL_COLORS[2], 130); - set3.setDrawValues(true); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets - dataSets.add(set2); - dataSets.add(set3); - - // create a data object with the datasets - BubbleData data = new BubbleData(dataSets); - data.setDrawValues(false); - data.setValueTypeface(mTfLight); - data.setValueTextSize(8f); - data.setValueTextColor(Color.WHITE); - data.setHighlightCircleWidth(1.5f); - - mChart.setData(data); - mChart.invalidate(); + public void saveToGallery() { + saveToGallery(chart, "BubbleChartActivity"); } @Override @@ -233,20 +238,11 @@ public void onValueSelected(Entry e, Highlight h) { } @Override - public void onNothingSelected() { - // TODO Auto-generated method stub - - } + public void onNothingSelected() {} @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStartTrackingTouch(SeekBar seekBar) {} @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStopTrackingTouch(SeekBar seekBar) {} } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java index 54eb768a7b..b0b29dfbe4 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -1,16 +1,20 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; import android.graphics.Paint; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; 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 android.widget.Toast; import com.github.mikephil.charting.charts.CandleStickChart; import com.github.mikephil.charting.components.XAxis; @@ -28,8 +32,8 @@ public class CandleStickChartActivity extends DemoBase implements OnSeekBarChangeListener { - private CandleStickChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private CandleStickChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -39,48 +43,103 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_candlechart); + setTitle("CandleStickChartActivity"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); - mSeekBarY = findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); - mChart.setBackgroundColor(Color.WHITE); + chart = findViewById(R.id.chart1); + chart.setBackgroundColor(Color.WHITE); - mChart.getDescription().setEnabled(false); + chart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn - mChart.setMaxVisibleValueCount(60); + chart.setMaxVisibleValueCount(60); // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); + chart.setPinchZoom(false); - mChart.setDrawGridBackground(false); + chart.setDrawGridBackground(false); - XAxis xAxis = mChart.getXAxis(); + XAxis xAxis = chart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); xAxis.setDrawGridLines(false); - YAxis leftAxis = mChart.getAxisLeft(); + YAxis leftAxis = chart.getAxisLeft(); // leftAxis.setEnabled(false); leftAxis.setLabelCount(7, false); leftAxis.setDrawGridLines(false); leftAxis.setDrawAxisLine(false); - - YAxis rightAxis = mChart.getAxisRight(); + + YAxis rightAxis = chart.getAxisRight(); rightAxis.setEnabled(false); // rightAxis.setStartAtZero(false); // setting data - mSeekBarX.setProgress(40); - mSeekBarY.setProgress(100); - - mChart.getLegend().setEnabled(false); + seekBarX.setProgress(40); + seekBarY.setProgress(100); + + chart.getLegend().setEnabled(false); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + progress = (seekBarX.getProgress()); + + tvX.setText(String.valueOf(progress)); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + chart.resetTracking(); + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < progress; i++) { + float multi = (seekBarY.getProgress() + 1); + float val = (float) (Math.random() * 40) + multi; + + float high = (float) (Math.random() * 9) + 8f; + float low = (float) (Math.random() * 9) + 8f; + + float open = (float) (Math.random() * 6) + 1f; + float close = (float) (Math.random() * 6) + 1f; + + boolean even = i % 2 == 0; + + values.add(new CandleEntry( + i, val + high, + val - low, + even ? val + open : val - open, + even ? val - close : val + close, + getResources().getDrawable(R.drawable.star) + )); + } + + CandleDataSet set1 = new CandleDataSet(values, "Data Set"); + + set1.setDrawIcons(false); + set1.setAxisDependency(AxisDependency.LEFT); +// set1.setColor(Color.rgb(80, 80, 80)); + set1.setShadowColor(Color.DKGRAY); + set1.setShadowWidth(0.7f); + set1.setDecreasingColor(Color.RED); + set1.setDecreasingPaintStyle(Paint.Style.FILL); + set1.setIncreasingColor(Color.rgb(122, 242, 84)); + set1.setIncreasingPaintStyle(Paint.Style.STROKE); + set1.setNeutralColor(Color.BLUE); + //set1.setHighlightLineWidth(1f); + + CandleData data = new CandleData(set1); + + chart.setData(data); + chart.invalidate(); } @Override @@ -93,69 +152,73 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - for (IDataSet set : mChart.getData().getDataSets()) + for (IDataSet set : chart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleIcons: { - for (IDataSet set : mChart.getData().getDataSets()) + for (IDataSet set : chart.getData().getDataSets()) set.setDrawIcons(!set.isDrawIconsEnabled()); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionToggleMakeShadowSameColorAsCandle: { - for (ICandleDataSet set : mChart.getData().getDataSets()) { - //TODO: set.setShadowColorSameAsCandle(!set.getShadowColorSameAsCandle()); + for (ICandleDataSet set : chart.getData().getDataSets()) { + ((CandleDataSet) set).setShadowColorSameAsCandle(!set.getShadowColorSameAsCandle()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -163,67 +226,13 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - int prog = (mSeekBarX.getProgress() + 1); - - tvX.setText("" + prog); - tvY.setText("" + (mSeekBarY.getProgress())); - - mChart.resetTracking(); - - ArrayList yVals1 = new ArrayList(); - - for (int i = 0; i < prog; i++) { - float mult = (mSeekBarY.getProgress() + 1); - float val = (float) (Math.random() * 40) + mult; - - float high = (float) (Math.random() * 9) + 8f; - float low = (float) (Math.random() * 9) + 8f; - - float open = (float) (Math.random() * 6) + 1f; - float close = (float) (Math.random() * 6) + 1f; - - boolean even = i % 2 == 0; - - yVals1.add(new CandleEntry( - i, val + high, - val - low, - even ? val + open : val - open, - even ? val - close : val + close, - getResources().getDrawable(R.drawable.star) - )); - } - - CandleDataSet set1 = new CandleDataSet(yVals1, "Data Set"); - - set1.setDrawIcons(false); - set1.setAxisDependency(AxisDependency.LEFT); -// set1.setColor(Color.rgb(80, 80, 80)); - set1.setShadowColor(Color.DKGRAY); - set1.setShadowWidth(0.7f); - set1.setDecreasingColor(Color.RED); - set1.setDecreasingPaintStyle(Paint.Style.FILL); - set1.setIncreasingColor(Color.rgb(122, 242, 84)); - set1.setIncreasingPaintStyle(Paint.Style.STROKE); - set1.setNeutralColor(Color.BLUE); - //set1.setHighlightLineWidth(1f); - - CandleData data = new CandleData(set1); - - mChart.setData(data); - mChart.invalidate(); + public void saveToGallery() { + saveToGallery(chart, "CandleStickChartActivity"); } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStartTrackingTouch(SeekBar seekBar) {} @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStopTrackingTouch(SeekBar seekBar) {} } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index e4d8fb2e3b..7526dc1d59 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -1,7 +1,9 @@ package com.xxmassdeveloper.mpchartexample; +import android.content.Intent; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; @@ -38,8 +40,8 @@ public class CombinedChartActivity extends DemoBase { - private CombinedChart mChart; - private final int itemcount = 12; + private CombinedChart chart; + private final int count = 12; @Override protected void onCreate(Bundle savedInstanceState) { @@ -48,41 +50,43 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_combined); - mChart = findViewById(R.id.chart1); - mChart.getDescription().setEnabled(false); - mChart.setBackgroundColor(Color.WHITE); - mChart.setDrawGridBackground(false); - mChart.setDrawBarShadow(false); - mChart.setHighlightFullBarEnabled(false); + setTitle("CombinedChartActivity"); + + chart = findViewById(R.id.chart1); + chart.getDescription().setEnabled(false); + chart.setBackgroundColor(Color.WHITE); + chart.setDrawGridBackground(false); + chart.setDrawBarShadow(false); + chart.setHighlightFullBarEnabled(false); // draw bars behind lines - mChart.setDrawOrder(new DrawOrder[]{ + chart.setDrawOrder(new DrawOrder[]{ DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.CANDLE, DrawOrder.LINE, DrawOrder.SCATTER }); - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setWordWrapEnabled(true); l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); l.setOrientation(Legend.LegendOrientation.HORIZONTAL); l.setDrawInside(false); - YAxis rightAxis = mChart.getAxisRight(); + YAxis rightAxis = chart.getAxisRight(); rightAxis.setDrawGridLines(false); rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) - YAxis leftAxis = mChart.getAxisLeft(); + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setDrawGridLines(false); leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) - XAxis xAxis = mChart.getXAxis(); + XAxis xAxis = chart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTH_SIDED); xAxis.setAxisMinimum(0f); xAxis.setGranularity(1f); xAxis.setValueFormatter(new IAxisValueFormatter() { @Override public String getFormattedValue(float value, AxisBase axis) { - return mMonths[(int) value % mMonths.length]; + return months[(int) value % months.length]; } }); @@ -93,21 +97,21 @@ public String getFormattedValue(float value, AxisBase axis) { data.setData(generateBubbleData()); data.setData(generateScatterData()); data.setData(generateCandleData()); - data.setValueTypeface(mTfLight); + data.setValueTypeface(tfLight); xAxis.setAxisMaximum(data.getXMax() + 0.25f); - mChart.setData(data); - mChart.invalidate(); + chart.setData(data); + chart.invalidate(); } private LineData generateLineData() { LineData d = new LineData(); - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); - for (int index = 0; index < itemcount; index++) + for (int index = 0; index < count; index++) entries.add(new Entry(index + 0.5f, getRandom(15, 5))); LineDataSet set = new LineDataSet(entries, "Line DataSet"); @@ -129,10 +133,10 @@ private LineData generateLineData() { private BarData generateBarData() { - ArrayList entries1 = new ArrayList(); - ArrayList entries2 = new ArrayList(); + ArrayList entries1 = new ArrayList<>(); + ArrayList entries2 = new ArrayList<>(); - for (int index = 0; index < itemcount; index++) { + for (int index = 0; index < count; index++) { entries1.add(new BarEntry(0, getRandom(25, 25))); // stacked @@ -147,7 +151,7 @@ private BarData generateBarData() { BarDataSet set2 = new BarDataSet(entries2, ""); set2.setStackLabels(new String[]{"Stack 1", "Stack 2"}); - set2.setColors(new int[]{Color.rgb(61, 165, 255), Color.rgb(23, 197, 255)}); + set2.setColors(Color.rgb(61, 165, 255), Color.rgb(23, 197, 255)); set2.setValueTextColor(Color.rgb(61, 165, 255)); set2.setValueTextSize(10f); set2.setAxisDependency(YAxis.AxisDependency.LEFT); @@ -166,13 +170,13 @@ private BarData generateBarData() { return d; } - protected ScatterData generateScatterData() { + ScatterData generateScatterData() { ScatterData d = new ScatterData(); - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); - for (float index = 0; index < itemcount; index += 0.5f) + for (float index = 0; index < count; index += 0.5f) entries.add(new Entry(index + 0.25f, getRandom(10, 55))); ScatterDataSet set = new ScatterDataSet(entries, "Scatter DataSet"); @@ -185,13 +189,13 @@ protected ScatterData generateScatterData() { return d; } - protected CandleData generateCandleData() { + CandleData generateCandleData() { CandleData d = new CandleData(); - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); - for (int index = 0; index < itemcount; index += 2) + for (int index = 0; index < count; index += 2) entries.add(new CandleEntry(index + 1f, 90, 70, 85, 75f)); CandleDataSet set = new CandleDataSet(entries, "Candle DataSet"); @@ -205,13 +209,13 @@ protected CandleData generateCandleData() { return d; } - protected BubbleData generateBubbleData() { + BubbleData generateBubbleData() { BubbleData bd = new BubbleData(); - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); - for (int index = 0; index < itemcount; index++) { + for (int index = 0; index < count; index++) { float y = getRandom(10, 105); float size = getRandom(100, 105); entries.add(new BubbleEntry(index + 0.5f, y, size)); @@ -237,34 +241,42 @@ public boolean onCreateOptionsMenu(Menu menu) { @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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleLineValues: { - for (IDataSet set : mChart.getData().getDataSets()) { + for (IDataSet set : chart.getData().getDataSets()) { if (set instanceof LineDataSet) set.setDrawValues(!set.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleBarValues: { - for (IDataSet set : mChart.getData().getDataSets()) { + for (IDataSet set : chart.getData().getDataSets()) { if (set instanceof BarDataSet) set.setDrawValues(!set.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionRemoveDataSet: { - - int rnd = (int) getRandom(mChart.getData().getDataSetCount(), 0); - mChart.getData().removeDataSet(mChart.getData().getDataSetByIndex(rnd)); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); - mChart.invalidate(); + int rnd = (int) getRandom(chart.getData().getDataSetCount(), 0); + chart.getData().removeDataSet(chart.getData().getDataSetByIndex(rnd)); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + chart.invalidate(); break; } } return true; } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 4a278c398e..25134f71fd 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -1,15 +1,19 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; 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 android.widget.Toast; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.XAxis; @@ -28,8 +32,8 @@ public class CubicLineChartActivity extends DemoBase implements OnSeekBarChangeListener { - private LineChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private LineChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -39,60 +43,115 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart); + setTitle("CubicLineChartActivity"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarY = findViewById(R.id.seekBar2); - - mSeekBarX.setProgress(45); - mSeekBarY.setProgress(100); - - mSeekBarY.setOnSeekBarChangeListener(this); - mSeekBarX.setOnSeekBarChangeListener(this); + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); - mChart = findViewById(R.id.chart1); - mChart.setViewPortOffsets(0, 0, 0, 0); - mChart.setBackgroundColor(Color.rgb(104, 241, 175)); + chart = findViewById(R.id.chart1); + chart.setViewPortOffsets(0, 0, 0, 0); + chart.setBackgroundColor(Color.rgb(104, 241, 175)); // no description text - mChart.getDescription().setEnabled(false); + chart.getDescription().setEnabled(false); // enable touch gestures - mChart.setTouchEnabled(true); + chart.setTouchEnabled(true); // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); + chart.setDragEnabled(true); + chart.setScaleEnabled(true); // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(false); + chart.setPinchZoom(false); - mChart.setDrawGridBackground(false); - mChart.setMaxHighlightDistance(300); - - XAxis x = mChart.getXAxis(); + chart.setDrawGridBackground(false); + chart.setMaxHighlightDistance(300); + + XAxis x = chart.getXAxis(); x.setEnabled(false); - - YAxis y = mChart.getAxisLeft(); - y.setTypeface(mTfLight); + + YAxis y = chart.getAxisLeft(); + y.setTypeface(tfLight); y.setLabelCount(6, false); y.setTextColor(Color.WHITE); y.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); y.setDrawGridLines(false); y.setAxisLineColor(Color.WHITE); - - mChart.getAxisRight().setEnabled(false); + + chart.getAxisRight().setEnabled(false); // add data + seekBarY.setOnSeekBarChangeListener(this); + seekBarX.setOnSeekBarChangeListener(this); + + // lower max, as cubic runs significantly slower than linear + seekBarX.setMax(700); + + seekBarX.setProgress(45); + seekBarY.setProgress(100); setData(45, 100); - - mChart.getLegend().setEnabled(false); - - mChart.animateXY(2000, 2000); - // dont forget to refresh the drawing - mChart.invalidate(); + chart.getLegend().setEnabled(false); + + chart.animateXY(2000, 2000); + + // don't forget to refresh the drawing + chart.invalidate(); + } + + private void setData(int count, float range) { + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * (range + 1)) + 20; + values.add(new Entry(i, val)); + } + + LineDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + // create a dataset and give it a type + set1 = new LineDataSet(values, "DataSet 1"); + + set1.setMode(LineDataSet.Mode.CUBIC_BEZIER); + set1.setCubicIntensity(0.2f); + set1.setDrawFilled(true); + set1.setDrawCircles(false); + set1.setLineWidth(1.8f); + set1.setCircleRadius(4f); + set1.setCircleColor(Color.WHITE); + set1.setHighLightColor(Color.rgb(244, 117, 117)); + set1.setColor(Color.WHITE); + set1.setFillColor(Color.WHITE); + set1.setFillAlpha(100); + set1.setDrawHorizontalHighlightIndicator(false); + set1.setFillFormatter(new IFillFormatter() { + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + return chart.getAxisLeft().getAxisMinimum(); + } + }); + + // create a data object with the data sets + LineData data = new LineData(set1); + data.setValueTypeface(tfLight); + data.setValueTextSize(9f); + data.setDrawValues(false); + + // set data + chart.setData(data); + } } @Override @@ -105,23 +164,29 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - for (IDataSet set : mChart.getData().getDataSets()) + for (IDataSet set : chart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionToggleFilled: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -133,11 +198,11 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawFilled(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleCircles: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -148,11 +213,11 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawCircles(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleCubic: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -162,11 +227,11 @@ public boolean onOptionsItemSelected(MenuItem item) { ? LineDataSet.Mode.LINEAR : LineDataSet.Mode.CUBIC_BEZIER); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleStepped: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -176,11 +241,11 @@ public boolean onOptionsItemSelected(MenuItem item) { ? LineDataSet.Mode.LINEAR : LineDataSet.Mode.STEPPED); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHorizontalCubic: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -190,44 +255,41 @@ public boolean onOptionsItemSelected(MenuItem item) { ? LineDataSet.Mode.LINEAR : LineDataSet.Mode.HORIZONTAL_BEZIER); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } case R.id.actionSave: { - if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - - // mChart.saveToGallery("title"+System.currentTimeMillis()) + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -237,78 +299,23 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); + setData(seekBarX.getProgress(), seekBarY.getProgress()); // redraw - mChart.invalidate(); + chart.invalidate(); } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - + public void saveToGallery() { + saveToGallery(chart, "CubicLineChartActivity"); } @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - private void setData(int count, float range) { - - ArrayList yVals = new ArrayList(); - - for (int i = 0; i < count; i++) { - float mult = (range + 1); - float val = (float) (Math.random() * mult) + 20;// + (float) - // ((mult * - // 0.1) / 10); - yVals.add(new Entry(i, val)); - } - - LineDataSet set1; - - if (mChart.getData() != null && - mChart.getData().getDataSetCount() > 0) { - set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); - set1.setValues(yVals); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); - } else { - // create a dataset and give it a type - set1 = new LineDataSet(yVals, "DataSet 1"); - - set1.setMode(LineDataSet.Mode.CUBIC_BEZIER); - set1.setCubicIntensity(0.2f); - //set1.setDrawFilled(true); - set1.setDrawCircles(false); - set1.setLineWidth(1.8f); - set1.setCircleRadius(4f); - set1.setCircleColor(Color.WHITE); - set1.setHighLightColor(Color.rgb(244, 117, 117)); - set1.setColor(Color.WHITE); - set1.setFillColor(Color.WHITE); - set1.setFillAlpha(100); - set1.setDrawHorizontalHighlightIndicator(false); - set1.setFillFormatter(new IFillFormatter() { - @Override - public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { - return -10; - } - }); + public void onStartTrackingTouch(SeekBar seekBar) {} - // create a data object with the datasets - LineData data = new LineData(set1); - data.setValueTypeface(mTfLight); - data.setValueTextSize(9f); - data.setDrawValues(false); - - // set data - mChart.setData(data); - } - } + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java index d3551068c1..348b9ad0fc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java @@ -1,8 +1,10 @@ - +// TODO: Finish and add to main activity list package com.xxmassdeveloper.mpchartexample; -import android.graphics.Typeface; +import android.Manifest; +import android.content.pm.PackageManager; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -28,13 +30,13 @@ /** * This Activity demonstrates drawing into the Chart with the finger. Both line, * bar and scatter charts can be used for drawing. - * + * * @author Philipp Jahoda */ public class DrawChartActivity extends DemoBase implements OnChartValueSelectedListener, OnDrawListener { - private LineChart mChart; + private LineChart chart; @Override protected void onCreate(Bundle savedInstanceState) { @@ -43,49 +45,49 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_draw_chart); - mChart = findViewById(R.id.chart1); + setTitle("DrawChartActivity"); + + chart = findViewById(R.id.chart1); // listener for selecting and drawing - mChart.setOnChartValueSelectedListener(this); - mChart.setOnDrawListener(this); + chart.setOnChartValueSelectedListener(this); + chart.setOnDrawListener(this); - // if disabled, drawn datasets with the finger will not be automatically + // if disabled, drawn data sets with the finger will not be automatically // finished - // mChart.setAutoFinish(true); - mChart.setDrawGridBackground(false); + // chart.setAutoFinish(true); + chart.setDrawGridBackground(false); // add dummy-data to the chart initWithDummyData(); - Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - XAxis xl = mChart.getXAxis(); - xl.setTypeface(tf); + XAxis xl = chart.getXAxis(); + xl.setTypeface(tfRegular); xl.setAvoidFirstLastClipping(true); - YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(tf); + YAxis yl = chart.getAxisLeft(); + yl.setTypeface(tfRegular); - mChart.getLegend().setEnabled(false); + chart.getLegend().setEnabled(false); - // mChart.setYRange(-40f, 40f, true); + // chart.setYRange(-40f, 40f, true); // call this to reset the changed y-range - // mChart.resetYRange(true); + // chart.resetYRange(true); } private void initWithDummyData() { - ArrayList yVals = new ArrayList(); + ArrayList values = new ArrayList<>(); // create a dataset and give it a type (0) - LineDataSet set1 = new LineDataSet(yVals, "DataSet"); + LineDataSet set1 = new LineDataSet(values, "DataSet"); set1.setLineWidth(3f); set1.setCircleRadius(5f); - // create a data object with the datasets + // create a data object with the data sets LineData data = new LineData(set1); - mChart.setData(data); + chart.setData(data); } @Override @@ -99,7 +101,7 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -108,39 +110,47 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawValues(!set.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionSave: { - // mChart.saveToGallery("title"+System.currentTimeMillis()); - mChart.saveToPath("title" + System.currentTimeMillis(), ""); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } return true; } + @Override + public void saveToGallery() { + saveToGallery(chart, "DrawChartActivity"); + } + @Override public void onValueSelected(Entry e, Highlight h) { Log.i("VAL SELECTED", @@ -164,7 +174,7 @@ public void onDrawFinished(DataSet dataSet) { Log.i(Chart.LOG_TAG, "DataSet drawn. " + dataSet.toSimpleString()); // prepare the legend again - mChart.getLegendRenderer().computeLegend(mChart.getData()); + chart.getLegendRenderer().computeLegend(chart.getData()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index f8f64c374f..501090af62 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -1,8 +1,13 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.view.Menu; import android.view.MenuItem; import android.view.WindowManager; @@ -23,7 +28,7 @@ public class DynamicalAddingActivity extends DemoBase implements OnChartValueSelectedListener { - private LineChart mChart; + private LineChart chart; @Override protected void onCreate(Bundle savedInstanceState) { @@ -32,24 +37,30 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart_noseekbar); - mChart = findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - mChart.setDrawGridBackground(false); - mChart.getDescription().setEnabled(false); + setTitle("DynamicalAddingActivity"); - // add an empty data object - mChart.setData(new LineData()); -// mChart.getXAxis().setDrawLabels(false); -// mChart.getXAxis().setDrawGridLines(false); + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + chart.setDrawGridBackground(false); + chart.getDescription().setEnabled(false); + chart.setNoDataText("No chart data available. Use the menu to add entries and data sets!"); - mChart.invalidate(); +// chart.getXAxis().setDrawLabels(false); +// chart.getXAxis().setDrawGridLines(false); + + chart.invalidate(); } - int[] mColors = ColorTemplate.VORDIPLOM_COLORS; + private final int[] colors = ColorTemplate.VORDIPLOM_COLORS; private void addEntry() { - LineData data = mChart.getData(); + LineData data = chart.getData(); + + if (data == null) { + data = new LineData(); + chart.setData(data); + } ILineDataSet set = data.getDataSetByIndex(0); // set.addEntry(...); // can be called as well @@ -61,25 +72,26 @@ private void addEntry() { // choose a random dataSet int randomDataSetIndex = (int) (Math.random() * data.getDataSetCount()); - float yValue = (float) (Math.random() * 10) + 50f; + ILineDataSet randomSet = data.getDataSetByIndex(randomDataSetIndex); + float value = (float) (Math.random() * 50) + 50f * (randomDataSetIndex + 1); - data.addEntry(new Entry(data.getDataSetByIndex(randomDataSetIndex).getEntryCount(), yValue), randomDataSetIndex); + data.addEntry(new Entry(randomSet.getEntryCount(), value), randomDataSetIndex); data.notifyDataChanged(); // let the chart know it's data has changed - mChart.notifyDataSetChanged(); + chart.notifyDataSetChanged(); - mChart.setVisibleXRangeMaximum(6); - //mChart.setVisibleYRangeMaximum(15, AxisDependency.LEFT); -// + chart.setVisibleXRangeMaximum(6); + //chart.setVisibleYRangeMaximum(15, AxisDependency.LEFT); +// // // this automatically refreshes the chart (calls invalidate()) - mChart.moveViewTo(data.getEntryCount() - 7, 50f, AxisDependency.LEFT); + chart.moveViewTo(data.getEntryCount() - 7, 50f, AxisDependency.LEFT); } private void removeLastEntry() { - LineData data = mChart.getData(); + LineData data = chart.getData(); if (data != null) { @@ -93,31 +105,33 @@ private void removeLastEntry() { // or remove by index // mData.removeEntryByXValue(xIndex, dataSetIndex); data.notifyDataChanged(); - mChart.notifyDataSetChanged(); - mChart.invalidate(); + chart.notifyDataSetChanged(); + chart.invalidate(); } } } private void addDataSet() { - LineData data = mChart.getData(); - - if (data != null) { + LineData data = chart.getData(); + if (data == null) { + chart.setData(new LineData()); + } else { int count = (data.getDataSetCount() + 1); + int amount = data.getDataSetByIndex(0).getEntryCount(); - ArrayList yVals = new ArrayList(); + ArrayList values = new ArrayList<>(); - for (int i = 0; i < data.getEntryCount(); i++) { - yVals.add(new Entry(i, (float) (Math.random() * 50f) + 50f * count)); + for (int i = 0; i < amount; i++) { + values.add(new Entry(i, (float) (Math.random() * 50f) + 50f * count)); } - LineDataSet set = new LineDataSet(yVals, "DataSet " + count); + LineDataSet set = new LineDataSet(values, "DataSet " + count); set.setLineWidth(2.5f); set.setCircleRadius(4.5f); - int color = mColors[count % mColors.length]; + int color = colors[count % colors.length]; set.setColor(color); set.setCircleColor(color); @@ -127,33 +141,45 @@ private void addDataSet() { data.addDataSet(set); data.notifyDataChanged(); - mChart.notifyDataSetChanged(); - mChart.invalidate(); + chart.notifyDataSetChanged(); + chart.invalidate(); } } private void removeDataSet() { - LineData data = mChart.getData(); + LineData data = chart.getData(); if (data != null) { data.removeDataSet(data.getDataSetByIndex(data.getDataSetCount() - 1)); - mChart.notifyDataSetChanged(); - mChart.invalidate(); + chart.notifyDataSetChanged(); + chart.invalidate(); } } + private LineDataSet createSet() { + + LineDataSet set = new LineDataSet(null, "DataSet 1"); + set.setLineWidth(2.5f); + set.setCircleRadius(4.5f); + set.setColor(Color.rgb(240, 99, 99)); + set.setCircleColor(Color.rgb(240, 99, 99)); + set.setHighLightColor(Color.rgb(190, 190, 190)); + set.setAxisDependency(AxisDependency.LEFT); + set.setValueTextSize(10f); + + return set; + } + @Override public void onValueSelected(Entry e, Highlight h) { Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show(); } @Override - public void onNothingSelected() { - - } + public void onNothingSelected() {} @Override public boolean onCreateOptionsMenu(Menu menu) { @@ -165,47 +191,52 @@ public boolean onCreateOptionsMenu(Menu menu) { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.actionAddEntry: + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java")); + startActivity(i); + break; + } + case R.id.actionAddEntry: { addEntry(); Toast.makeText(this, "Entry added!", Toast.LENGTH_SHORT).show(); break; - case R.id.actionRemoveEntry: + } + case R.id.actionRemoveEntry: { removeLastEntry(); Toast.makeText(this, "Entry removed!", Toast.LENGTH_SHORT).show(); break; - case R.id.actionAddDataSet: + } + case R.id.actionAddDataSet: { addDataSet(); Toast.makeText(this, "DataSet added!", Toast.LENGTH_SHORT).show(); break; - case R.id.actionRemoveDataSet: + } + case R.id.actionRemoveDataSet: { removeDataSet(); Toast.makeText(this, "DataSet removed!", Toast.LENGTH_SHORT).show(); break; - case R.id.actionAddEmptyLineData: - mChart.setData(new LineData()); - mChart.invalidate(); - Toast.makeText(this, "Empty data added!", Toast.LENGTH_SHORT).show(); - break; - case R.id.actionClear: - mChart.clear(); + } + case R.id.actionClear: { + chart.clear(); Toast.makeText(this, "Chart cleared!", Toast.LENGTH_SHORT).show(); break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } } return true; } - private LineDataSet createSet() { - - LineDataSet set = new LineDataSet(null, "DataSet 1"); - set.setLineWidth(2.5f); - set.setCircleRadius(4.5f); - set.setColor(Color.rgb(240, 99, 99)); - set.setCircleColor(Color.rgb(240, 99, 99)); - set.setHighLightColor(Color.rgb(190, 190, 190)); - set.setAxisDependency(AxisDependency.LEFT); - set.setValueTextSize(10f); - - return set; + @Override + public void saveToGallery() { + saveToGallery(chart, "DynamicalAddingActivity"); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java index 9109d5d29c..e821a04969 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java @@ -1,8 +1,12 @@ package com.xxmassdeveloper.mpchartexample; +import android.content.Intent; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; import android.view.WindowManager; import com.github.mikephil.charting.charts.LineChart; @@ -19,10 +23,19 @@ import java.util.ArrayList; +/** + * This works by inverting the background and desired "fill" color. First, we draw the fill color + * that we want between the lines as the actual background of the chart. Then, we fill the area + * above the highest line and the area under the lowest line with the desired background color. + * + * This method makes it look like we filled the area between the lines, but really we are filling + * the area OUTSIDE the lines! + */ +@SuppressWarnings("SameParameterValue") public class FilledLineActivity extends DemoBase { - private LineChart mChart; - private int mFillColor = Color.argb(150, 51, 181, 229); + private LineChart chart; + private final int fillColor = Color.argb(150, 51, 181, 229); @Override protected void onCreate(Bundle savedInstanceState) { @@ -31,73 +44,71 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart_noseekbar); - mChart = findViewById(R.id.chart1); - mChart.setBackgroundColor(Color.WHITE); - mChart.setGridBackgroundColor(mFillColor); - mChart.setDrawGridBackground(true); + setTitle("FilledLineActivity"); - mChart.setDrawBorders(true); + chart = findViewById(R.id.chart1); + chart.setBackgroundColor(Color.WHITE); + chart.setGridBackgroundColor(fillColor); + chart.setDrawGridBackground(true); + + chart.setDrawBorders(true); // no description text - mChart.getDescription().setEnabled(false); + chart.getDescription().setEnabled(false); // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(false); + chart.setPinchZoom(false); - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setEnabled(false); - XAxis xAxis = mChart.getXAxis(); + XAxis xAxis = chart.getXAxis(); xAxis.setEnabled(false); - YAxis leftAxis = mChart.getAxisLeft(); + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setAxisMaximum(900f); leftAxis.setAxisMinimum(-250f); leftAxis.setDrawAxisLine(false); leftAxis.setDrawZeroLine(false); leftAxis.setDrawGridLines(false); - mChart.getAxisRight().setEnabled(false); + chart.getAxisRight().setEnabled(false); // add data setData(100, 60); - mChart.invalidate(); + chart.invalidate(); } private void setData(int count, float range) { - ArrayList yVals1 = new ArrayList(); + ArrayList values1 = new ArrayList<>(); for (int i = 0; i < count; i++) { - float val = (float) (Math.random() * range) + 50;// + (float) - // ((mult * - // 0.1) / 10); - yVals1.add(new Entry(i, val)); + float val = (float) (Math.random() * range) + 50; + values1.add(new Entry(i, val)); } - ArrayList yVals2 = new ArrayList(); + ArrayList values2 = new ArrayList<>(); for (int i = 0; i < count; i++) { - float val = (float) (Math.random() * range) + 450;// + (float) - // ((mult * - // 0.1) / 10); - yVals2.add(new Entry(i, val)); + float val = (float) (Math.random() * range) + 450; + values2.add(new Entry(i, val)); } LineDataSet set1, set2; - if (mChart.getData() != null && - mChart.getData().getDataSetCount() > 0) { - set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); - set2 = (LineDataSet)mChart.getData().getDataSetByIndex(1); - set1.setValues(yVals1); - set2.setValues(yVals2); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet) chart.getData().getDataSetByIndex(0); + set2 = (LineDataSet) chart.getData().getDataSetByIndex(1); + set1.setValues(values1); + set2.setValues(values2); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); } else { // create a dataset and give it a type - set1 = new LineDataSet(yVals1, "DataSet 1"); + set1 = new LineDataSet(values1, "DataSet 1"); set1.setAxisDependency(YAxis.AxisDependency.LEFT); set1.setColor(Color.rgb(255, 241, 46)); @@ -112,12 +123,14 @@ private void setData(int count, float range) { set1.setFillFormatter(new IFillFormatter() { @Override public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { - return mChart.getAxisLeft().getAxisMinimum(); + // change the return value here to better understand the effect + // return 0; + return chart.getAxisLeft().getAxisMinimum(); } }); // create a dataset and give it a type - set2 = new LineDataSet(yVals2, "DataSet 2"); + set2 = new LineDataSet(values2, "DataSet 2"); set2.setAxisDependency(YAxis.AxisDependency.LEFT); set2.setColor(Color.rgb(255, 241, 46)); set2.setDrawCircles(false); @@ -131,20 +144,46 @@ public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProv set2.setFillFormatter(new IFillFormatter() { @Override public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { - return mChart.getAxisLeft().getAxisMaximum(); + // change the return value here to better understand the effect + // return 600; + return chart.getAxisLeft().getAxisMaximum(); } }); - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); // add the data sets dataSets.add(set2); - // create a data object with the datasets + // create a data object with the data sets LineData data = new LineData(dataSets); data.setDrawValues(false); // set data - mChart.setData(data); + chart.setData(data); } } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java")); + startActivity(i); + break; + } + } + + return true; + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java index 38a228b322..ed2adcc960 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java @@ -1,22 +1,24 @@ package com.xxmassdeveloper.mpchartexample; +import android.content.Intent; import android.graphics.Color; -import android.graphics.Point; 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.view.Display; +import android.util.DisplayMetrics; +import android.view.Menu; +import android.view.MenuItem; import android.view.WindowManager; import android.widget.RelativeLayout; 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.components.Legend.LegendPosition; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.data.PieEntry; @@ -26,9 +28,10 @@ import java.util.ArrayList; +@SuppressWarnings("SameParameterValue") public class HalfPieChartActivity extends DemoBase { - private PieChart mChart; + private PieChart chart; @Override protected void onCreate(Bundle savedInstanceState) { @@ -37,40 +40,42 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_piechart_half); - mChart = findViewById(R.id.chart1); - mChart.setBackgroundColor(Color.WHITE); + setTitle("HalfPieChartActivity"); + + chart = findViewById(R.id.chart1); + chart.setBackgroundColor(Color.WHITE); moveOffScreen(); - mChart.setUsePercentValues(true); - mChart.getDescription().setEnabled(false); + chart.setUsePercentValues(true); + chart.getDescription().setEnabled(false); - mChart.setCenterTextTypeface(mTfLight); - mChart.setCenterText(generateCenterSpannableText()); + chart.setCenterTextTypeface(tfLight); + chart.setCenterText(generateCenterSpannableText()); - mChart.setDrawHoleEnabled(true); - mChart.setHoleColor(Color.WHITE); + chart.setDrawHoleEnabled(true); + chart.setHoleColor(Color.WHITE); - mChart.setTransparentCircleColor(Color.WHITE); - mChart.setTransparentCircleAlpha(110); + chart.setTransparentCircleColor(Color.WHITE); + chart.setTransparentCircleAlpha(110); - mChart.setHoleRadius(58f); - mChart.setTransparentCircleRadius(61f); + chart.setHoleRadius(58f); + chart.setTransparentCircleRadius(61f); - mChart.setDrawCenterText(true); + chart.setDrawCenterText(true); - mChart.setRotationEnabled(false); - mChart.setHighlightPerTapEnabled(true); + chart.setRotationEnabled(false); + chart.setHighlightPerTapEnabled(true); - mChart.setMaxAngle(180f); // HALF CHART - mChart.setRotationAngle(180f); - mChart.setCenterTextOffset(0, -20); + chart.setMaxAngle(180f); // HALF CHART + chart.setRotationAngle(180f); + chart.setCenterTextOffset(0, -20); setData(4, 100); - mChart.animateY(1400, Easing.EaseInOutQuad); + chart.animateY(1400, Easing.EaseInOutQuad); - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); l.setOrientation(Legend.LegendOrientation.HORIZONTAL); @@ -80,17 +85,17 @@ protected void onCreate(Bundle savedInstanceState) { l.setYOffset(0f); // entry label styling - mChart.setEntryLabelColor(Color.WHITE); - mChart.setEntryLabelTypeface(mTfRegular); - mChart.setEntryLabelTextSize(12f); + chart.setEntryLabelColor(Color.WHITE); + chart.setEntryLabelTypeface(tfRegular); + chart.setEntryLabelTextSize(12f); } private void setData(int count, float range) { - ArrayList values = new ArrayList(); + ArrayList values = new ArrayList<>(); for (int i = 0; i < count; i++) { - values.add(new PieEntry((float) ((Math.random() * range) + range / 5), mParties[i % mParties.length])); + values.add(new PieEntry((float) ((Math.random() * range) + range / 5), parties[i % parties.length])); } PieDataSet dataSet = new PieDataSet(values, "Election Results"); @@ -104,10 +109,10 @@ private void setData(int count, float range) { data.setValueFormatter(new PercentFormatter()); data.setValueTextSize(11f); data.setValueTextColor(Color.WHITE); - data.setValueTypeface(mTfLight); - mChart.setData(data); + data.setValueTypeface(tfLight); + chart.setData(data); - mChart.invalidate(); + chart.invalidate(); } private SpannableString generateCenterSpannableText() { @@ -124,14 +129,40 @@ private SpannableString generateCenterSpannableText() { private void moveOffScreen() { - Display display = getWindowManager().getDefaultDisplay(); - int height = display.getHeight(); // deprecated + DisplayMetrics displayMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + + int height = displayMetrics.heightPixels; int offset = (int)(height * 0.65); /* percent to move */ RelativeLayout.LayoutParams rlParams = - (RelativeLayout.LayoutParams)mChart.getLayoutParams(); + (RelativeLayout.LayoutParams) chart.getLayoutParams(); rlParams.setMargins(0, 0, 0, -offset); - mChart.setLayoutParams(rlParams); + chart.setLayoutParams(rlParams); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java")); + startActivity(i); + break; + } + } + + return true; } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 95e138aade..6e4e9275bf 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -1,9 +1,13 @@ package com.xxmassdeveloper.mpchartexample; -import android.annotation.SuppressLint; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.RectF; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -11,11 +15,9 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; -import android.widget.Toast; import com.github.mikephil.charting.charts.HorizontalBarChart; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.components.YAxis; @@ -35,8 +37,8 @@ public class HorizontalBarChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - protected HorizontalBarChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private HorizontalBarChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -46,67 +48,69 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_horizontalbarchart); + setTitle("HorizontalBarChartActivity"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarY = findViewById(R.id.seekBar2); + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); + + seekBarY.setOnSeekBarChangeListener(this); + seekBarX.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - // mChart.setHighlightEnabled(false); + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + // chart.setHighlightEnabled(false); - mChart.setDrawBarShadow(false); + chart.setDrawBarShadow(false); - mChart.setDrawValueAboveBar(true); + chart.setDrawValueAboveBar(true); - mChart.getDescription().setEnabled(false); + chart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn - mChart.setMaxVisibleValueCount(60); + chart.setMaxVisibleValueCount(60); // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); + chart.setPinchZoom(false); // draw shadows for each bar that show the maximum value - // mChart.setDrawBarShadow(true); + // chart.setDrawBarShadow(true); - mChart.setDrawGridBackground(false); + chart.setDrawGridBackground(false); - XAxis xl = mChart.getXAxis(); + XAxis xl = chart.getXAxis(); xl.setPosition(XAxisPosition.BOTTOM); - xl.setTypeface(mTfLight); + xl.setTypeface(tfLight); xl.setDrawAxisLine(true); xl.setDrawGridLines(false); xl.setGranularity(10f); - YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(mTfLight); + YAxis yl = chart.getAxisLeft(); + yl.setTypeface(tfLight); yl.setDrawAxisLine(true); yl.setDrawGridLines(true); yl.setAxisMinimum(0f); // this replaces setStartAtZero(true) // yl.setInverted(true); - YAxis yr = mChart.getAxisRight(); - yr.setTypeface(mTfLight); + YAxis yr = chart.getAxisRight(); + yr.setTypeface(tfLight); yr.setDrawAxisLine(true); yr.setDrawGridLines(false); yr.setAxisMinimum(0f); // this replaces setStartAtZero(true) // yr.setInverted(true); setData(12, 50); - mChart.setFitBars(true); - mChart.animateY(2500); + chart.setFitBars(true); + chart.animateY(2500); // setting data - mSeekBarY.setProgress(50); - mSeekBarX.setProgress(12); + seekBarY.setProgress(50); + seekBarX.setProgress(12); - mSeekBarY.setOnSeekBarChangeListener(this); - mSeekBarX.setOnSeekBarChangeListener(this); - - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); l.setOrientation(Legend.LegendOrientation.HORIZONTAL); @@ -115,6 +119,42 @@ protected void onCreate(Bundle savedInstanceState) { l.setXEntrySpace(4f); } + private void setData(int count, float range) { + + float barWidth = 9f; + float spaceForBar = 10f; + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range); + values.add(new BarEntry(i * spaceForBar, val, + getResources().getDrawable(R.drawable.star))); + } + + BarDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(values, "DataSet 1"); + + set1.setDrawIcons(false); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); + + BarData data = new BarData(dataSets); + data.setValueTextSize(10f); + data.setValueTypeface(tfLight); + data.setBarWidth(barWidth); + chart.setData(data); + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.bar, menu); @@ -125,80 +165,80 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (IBarDataSet iSet : sets) { - - IBarDataSet set = (BarDataSet) iSet; - set.setDrawValues(!set.isDrawValuesEnabled()); + iSet.setDrawValues(!iSet.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleIcons: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (IBarDataSet iSet : sets) { - - IBarDataSet set = (BarDataSet) iSet; - set.setDrawIcons(!set.isDrawIconsEnabled()); + iSet.setDrawIcons(!iSet.isDrawIconsEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionToggleBarBorders: { - for (IBarDataSet set : mChart.getData().getDataSets()) + for (IBarDataSet set : chart.getData().getDataSets()) ((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -208,64 +248,27 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); - mChart.setFitBars(true); - mChart.invalidate(); + setData(seekBarX.getProgress(), seekBarY.getProgress()); + chart.setFitBars(true); + chart.invalidate(); } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - + public void saveToGallery() { + saveToGallery(chart, "HorizontalBarChartActivity"); } @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - private void setData(int count, float range) { + public void onStartTrackingTouch(SeekBar seekBar) {} - float barWidth = 9f; - float spaceForBar = 10f; - ArrayList yVals1 = new ArrayList(); - - for (int i = 0; i < count; i++) { - float val = (float) (Math.random() * range); - yVals1.add(new BarEntry(i * spaceForBar, val, - getResources().getDrawable(R.drawable.star))); - } - - BarDataSet set1; - - if (mChart.getData() != null && - mChart.getData().getDataSetCount() > 0) { - set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); - set1.setValues(yVals1); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); - } else { - set1 = new BarDataSet(yVals1, "DataSet 1"); - - set1.setDrawIcons(false); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} - BarData data = new BarData(dataSets); - data.setValueTextSize(10f); - data.setValueTypeface(mTfLight); - data.setBarWidth(barWidth); - mChart.setData(data); - } - } + private RectF mOnValueSelectedRectF = new RectF(); - protected RectF mOnValueSelectedRectF = new RectF(); - @SuppressLint("NewApi") @Override public void onValueSelected(Entry e, Highlight h) { @@ -273,9 +276,9 @@ public void onValueSelected(Entry e, Highlight h) { return; RectF bounds = mOnValueSelectedRectF; - mChart.getBarBounds((BarEntry) e, bounds); + chart.getBarBounds((BarEntry) e, bounds); - MPPointF position = mChart.getPosition(e, mChart.getData().getDataSetByIndex(h.getDataSetIndex()) + MPPointF position = chart.getPosition(e, chart.getData().getDataSetByIndex(h.getDataSetIndex()) .getAxisDependency()); Log.i("bounds", bounds.toString()); @@ -285,6 +288,5 @@ public void onValueSelected(Entry e, Highlight h) { } @Override - public void onNothingSelected() { - }; + public void onNothingSelected() {} } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index 4ad4e691ef..cdc3188854 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -1,7 +1,12 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -9,7 +14,6 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; -import android.widget.Toast; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.Legend; @@ -33,8 +37,8 @@ public class InvertedLineChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - private LineChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private LineChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -44,72 +48,100 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart); + setTitle("InvertedLineChartActivity"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarY = findViewById(R.id.seekBar2); + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); + + seekBarX.setProgress(45); + seekBarY.setProgress(100); - mSeekBarX.setProgress(45); - mSeekBarY.setProgress(100); + seekBarY.setOnSeekBarChangeListener(this); + seekBarX.setOnSeekBarChangeListener(this); - mSeekBarY.setOnSeekBarChangeListener(this); - mSeekBarX.setOnSeekBarChangeListener(this); + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + chart.setDrawGridBackground(false); - mChart = findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - mChart.setDrawGridBackground(false); - // no description text - mChart.getDescription().setEnabled(false); + chart.getDescription().setEnabled(false); // enable touch gestures - mChart.setTouchEnabled(true); + chart.setTouchEnabled(true); // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); + chart.setDragEnabled(true); + chart.setScaleEnabled(true); // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(true); + chart.setPinchZoom(true); // set an alternative background color - // mChart.setBackgroundColor(Color.GRAY); + // chart.setBackgroundColor(Color.GRAY); // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - mv.setChartView(mChart); // For bounds control - mChart.setMarker(mv); // Set the marker to the chart - - XAxis xl = mChart.getXAxis(); + mv.setChartView(chart); // For bounds control + chart.setMarker(mv); // Set the marker to the chart + + XAxis xl = chart.getXAxis(); xl.setAvoidFirstLastClipping(true); xl.setAxisMinimum(0f); - - YAxis leftAxis = mChart.getAxisLeft(); + + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setInverted(true); leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) - - YAxis rightAxis = mChart.getAxisRight(); + + YAxis rightAxis = chart.getAxisRight(); rightAxis.setEnabled(false); // add data setData(25, 50); // // restrain the maximum scale-out factor - // mChart.setScaleMinima(3f, 3f); + // chart.setScaleMinima(3f, 3f); // // // center the view to a specific position inside the chart - // mChart.centerViewPort(10, 50); + // chart.centerViewPort(10, 50); // get the legend (only possible after setting data) - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); // modify the legend ... l.setForm(LegendForm.LINE); - // dont forget to refresh the drawing - mChart.invalidate(); + // don't forget to refresh the drawing + chart.invalidate(); + } + + private void setData(int count, float range) { + + ArrayList entries = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float xVal = (float) (Math.random() * range); + float yVal = (float) (Math.random() * range); + entries.add(new Entry(xVal, yVal)); + } + + // sort by x-value + Collections.sort(entries, new EntryXComparator()); + + // create a dataset and give it a type + LineDataSet set1 = new LineDataSet(entries, "DataSet 1"); + + set1.setLineWidth(1.5f); + set1.setCircleRadius(4f); + + // create a data object with the data sets + LineData data = new LineData(set1); + + // set data + chart.setData(data); } @Override @@ -122,8 +154,14 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -132,19 +170,19 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawValues(!set.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionToggleFilled: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -155,11 +193,11 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawFilled(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleCircles: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -170,45 +208,42 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawCircles(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionSave: { - if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - - // mChart.saveToGallery("title"+System.currentTimeMillis()) + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -218,13 +253,18 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); + setData(seekBarX.getProgress(), seekBarY.getProgress()); // redraw - mChart.invalidate(); + chart.invalidate(); + } + + @Override + public void saveToGallery() { + saveToGallery(chart, "InvertedLineChartActivity"); } @Override @@ -235,46 +275,11 @@ public void onValueSelected(Entry e, Highlight h) { } @Override - public void onNothingSelected() { - // TODO Auto-generated method stub - - } + public void onNothingSelected() {} @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStartTrackingTouch(SeekBar seekBar) {} @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - private void setData(int count, float range) { - - ArrayList entries = new ArrayList(); - - for (int i = 0; i < count; i++) { - float xVal = (float) (Math.random() * range); - float yVal = (float) (Math.random() * range); - entries.add(new Entry(xVal, yVal)); - } - - // sort by x-value - Collections.sort(entries, new EntryXComparator()); - - // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(entries, "DataSet 1"); - - set1.setLineWidth(1.5f); - set1.setCircleRadius(4f); - - // create a data object with the datasets - LineData data = new LineData(set1); - - // set data - mChart.setData(data); - } + public void onStopTrackingTouch(SeekBar seekBar) {} } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 85d213e351..4a970df995 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -1,21 +1,22 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; import android.graphics.DashPathEffect; -import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Bundle; -import android.support.v4.content.ContextCompat; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; -import android.view.MotionEvent; import android.view.WindowManager; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; -import android.widget.Toast; import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.LineChart; @@ -28,10 +29,10 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.IFillFormatter; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.listener.ChartTouchListener; -import com.github.mikephil.charting.listener.OnChartGestureListener; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.Utils; import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; @@ -40,11 +41,17 @@ import java.util.ArrayList; import java.util.List; +/** + * Example of a heavily customized {@link LineChart} with limit lines, custom line shapes, etc. + * + * @since 1.7.4 + * @version 3.0.3 + */ public class LineChartActivity1 extends DemoBase implements OnSeekBarChangeListener, - OnChartGestureListener, OnChartValueSelectedListener { + OnChartValueSelectedListener { - private LineChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private LineChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -54,117 +61,200 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart); + setTitle("LineChartActivity1"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarY = findViewById(R.id.seekBar2); + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); - mSeekBarX.setProgress(45); - mSeekBarY.setProgress(100); + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setMax(180); + seekBarY.setOnSeekBarChangeListener(this); - mSeekBarY.setOnSeekBarChangeListener(this); - mSeekBarX.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); - mChart.setOnChartGestureListener(this); - mChart.setOnChartValueSelectedListener(this); - mChart.setDrawGridBackground(false); + { // // Chart Style // // + chart = findViewById(R.id.chart1); - // no description text - mChart.getDescription().setEnabled(false); + // background color + chart.setBackgroundColor(Color.WHITE); - // enable touch gestures - mChart.setTouchEnabled(true); + // disable description text + chart.getDescription().setEnabled(false); - // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - // mChart.setScaleXEnabled(true); - // mChart.setScaleYEnabled(true); + // enable touch gestures + chart.setTouchEnabled(true); - // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(true); + // set listeners + chart.setOnChartValueSelectedListener(this); + chart.setDrawGridBackground(false); - // set an alternative background color - // mChart.setBackgroundColor(Color.GRAY); + // create marker to display box when values are selected + MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - // create a custom MarkerView (extend MarkerView) and specify the layout - // to use for it - MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - mv.setChartView(mChart); // For bounds control - mChart.setMarker(mv); // Set the marker to the chart + // Set the marker to the chart + mv.setChartView(chart); + chart.setMarker(mv); - // x-axis limit line - LimitLine llXAxis = new LimitLine(10f, "Index 10"); - llXAxis.setLineWidth(4f); - llXAxis.enableDashedLine(10f, 10f, 0f); - llXAxis.setLabelPosition(LimitLabelPosition.RIGHT_BOTTOM); - llXAxis.setTextSize(10f); + // enable scaling and dragging + chart.setDragEnabled(true); + chart.setScaleEnabled(true); + // chart.setScaleXEnabled(true); + // chart.setScaleYEnabled(true); - XAxis xAxis = mChart.getXAxis(); - xAxis.enableGridDashedLine(10f, 10f, 0f); - //xAxis.setValueFormatter(new MyCustomXAxisValueFormatter()); - //xAxis.addLimitLine(llXAxis); // add x-axis limit line + // force pinch zoom along both axis + chart.setPinchZoom(true); + } + XAxis xAxis; + { // // X-Axis Style // // + xAxis = chart.getXAxis(); - Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + // vertical grid lines + xAxis.enableGridDashedLine(10f, 10f, 0f); + } - LimitLine ll1 = new LimitLine(150f, "Upper Limit"); - ll1.setLineWidth(4f); - ll1.enableDashedLine(10f, 10f, 0f); - ll1.setLabelPosition(LimitLabelPosition.RIGHT_TOP); - ll1.setTextSize(10f); - ll1.setTypeface(tf); + YAxis yAxis; + { // // Y-Axis Style // // + yAxis = chart.getAxisLeft(); - LimitLine ll2 = new LimitLine(-30f, "Lower Limit"); - ll2.setLineWidth(4f); - ll2.enableDashedLine(10f, 10f, 0f); - ll2.setLabelPosition(LimitLabelPosition.RIGHT_BOTTOM); - ll2.setTextSize(10f); - ll2.setTypeface(tf); + // disable dual axis (only use LEFT axis) + chart.getAxisRight().setEnabled(false); - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.removeAllLimitLines(); // reset all limit lines to avoid overlapping lines - leftAxis.addLimitLine(ll1); - leftAxis.addLimitLine(ll2); - leftAxis.setAxisMaximum(200f); - leftAxis.setAxisMinimum(-50f); - //leftAxis.setYOffset(20f); - leftAxis.enableGridDashedLine(10f, 10f, 0f); - leftAxis.setDrawZeroLine(false); + // horizontal grid lines + yAxis.enableGridDashedLine(10f, 10f, 0f); - // limit lines are drawn behind data (and not on top) - leftAxis.setDrawLimitLinesBehindData(true); + // axis range + yAxis.setAxisMaximum(200f); + yAxis.setAxisMinimum(-50f); + } - mChart.getAxisRight().setEnabled(false); - //mChart.getViewPortHandler().setMaximumScaleY(2f); - //mChart.getViewPortHandler().setMaximumScaleX(2f); + { // // Create Limit Lines // // + LimitLine llXAxis = new LimitLine(9f, "Index 10"); + llXAxis.setLineWidth(4f); + llXAxis.enableDashedLine(10f, 10f, 0f); + llXAxis.setLabelPosition(LimitLabelPosition.RIGHT_BOTTOM); + llXAxis.setTextSize(10f); + llXAxis.setTypeface(tfRegular); + + LimitLine ll1 = new LimitLine(150f, "Upper Limit"); + ll1.setLineWidth(4f); + ll1.enableDashedLine(10f, 10f, 0f); + ll1.setLabelPosition(LimitLabelPosition.RIGHT_TOP); + ll1.setTextSize(10f); + ll1.setTypeface(tfRegular); + + LimitLine ll2 = new LimitLine(-30f, "Lower Limit"); + ll2.setLineWidth(4f); + ll2.enableDashedLine(10f, 10f, 0f); + ll2.setLabelPosition(LimitLabelPosition.RIGHT_BOTTOM); + ll2.setTextSize(10f); + ll2.setTypeface(tfRegular); + + // draw limit lines behind data instead of on top + yAxis.setDrawLimitLinesBehindData(true); + xAxis.setDrawLimitLinesBehindData(true); + + // add limit lines + yAxis.addLimitLine(ll1); + yAxis.addLimitLine(ll2); + //xAxis.addLimitLine(llXAxis); + } // add data - setData(45, 100); + seekBarX.setProgress(45); + seekBarY.setProgress(180); + setData(45, 180); -// mChart.setVisibleXRange(20); -// mChart.setVisibleYRange(20f, AxisDependency.LEFT); -// mChart.centerViewTo(20, 50, AxisDependency.LEFT); - - mChart.animateX(2500); - //mChart.invalidate(); + // draw points over time + chart.animateX(1500); // get the legend (only possible after setting data) - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); - // modify the legend ... + // draw legend entries as lines l.setForm(LegendForm.LINE); - - // // dont forget to refresh the drawing - // mChart.invalidate(); } - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); + private void setData(int count, float range) { + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + + float val = (float) (Math.random() * range) - 30; + values.add(new Entry(i, val, getResources().getDrawable(R.drawable.star))); + } + + LineDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + set1.notifyDataSetChanged(); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + // create a dataset and give it a type + set1 = new LineDataSet(values, "DataSet 1"); + + set1.setDrawIcons(false); + + // draw dashed line + set1.enableDashedLine(10f, 5f, 0f); + + // black lines and points + set1.setColor(Color.BLACK); + set1.setCircleColor(Color.BLACK); + + // line thickness and point size + set1.setLineWidth(1f); + set1.setCircleRadius(3f); + + // draw points as solid circles + set1.setDrawCircleHole(false); + + // customize legend entry + set1.setFormLineWidth(1f); + set1.setFormLineDashEffect(new DashPathEffect(new float[]{10f, 5f}, 0f)); + set1.setFormSize(15.f); + + // text size of values + set1.setValueTextSize(9f); + + // draw selection line as dashed + set1.enableDashedHighlightLine(10f, 5f, 0f); + + // set the filled area + set1.setDrawFilled(true); + set1.setFillFormatter(new IFillFormatter() { + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + return chart.getAxisLeft().getAxisMinimum(); + } + }); + + // set color of filled area + if (Utils.getSDKInt() >= 18) { + // drawables only supported on api level 18 and above + Drawable drawable = ContextCompat.getDrawable(this, R.drawable.fade_red); + set1.setFillDrawable(drawable); + } else { + set1.setFillColor(Color.BLACK); + } + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); // add the data sets + + // create a data object with the data sets + LineData data = new LineData(dataSets); + + // set data + chart.setData(data); + } } @Override @@ -177,8 +267,14 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -187,11 +283,11 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawValues(!set.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleIcons: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -200,19 +296,19 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawIcons(!set.isDrawIconsEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionToggleFilled: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -223,11 +319,11 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawFilled(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleCircles: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -238,11 +334,11 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawCircles(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleCubic: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -252,11 +348,11 @@ public boolean onOptionsItemSelected(MenuItem item) { ? LineDataSet.Mode.LINEAR : LineDataSet.Mode.CUBIC_BEZIER); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleStepped: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -266,11 +362,11 @@ public boolean onOptionsItemSelected(MenuItem item) { ? LineDataSet.Mode.LINEAR : LineDataSet.Mode.STEPPED); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHorizontalCubic: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -280,44 +376,41 @@ public boolean onOptionsItemSelected(MenuItem item) { ? LineDataSet.Mode.LINEAR : LineDataSet.Mode.HORIZONTAL_BEZIER); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000, Easing.EaseInCubic); + chart.animateY(2000, Easing.EaseInCubic); break; } case R.id.animateXY: { - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } case R.id.actionSave: { - if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - - // mChart.saveToGallery("title"+System.currentTimeMillis()) + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -327,134 +420,31 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); + setData(seekBarX.getProgress(), seekBarY.getProgress()); // redraw - mChart.invalidate(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - private void setData(int count, float range) { - - ArrayList values = new ArrayList(); - - for (int i = 0; i < count; i++) { - - float val = (float) (Math.random() * range) + 3; - values.add(new Entry(i, val, getResources().getDrawable(R.drawable.star))); - } - - LineDataSet set1; - - if (mChart.getData() != null && - mChart.getData().getDataSetCount() > 0) { - set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); - set1.setValues(values); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); - } else { - // create a dataset and give it a type - set1 = new LineDataSet(values, "DataSet 1"); - - set1.setDrawIcons(false); - - // set the line to be drawn like this "- - - - - -" - set1.enableDashedLine(10f, 5f, 0f); - set1.enableDashedHighlightLine(10f, 5f, 0f); - set1.setColor(Color.BLACK); - set1.setCircleColor(Color.BLACK); - set1.setLineWidth(1f); - set1.setCircleRadius(3f); - set1.setDrawCircleHole(false); - set1.setValueTextSize(9f); - set1.setDrawFilled(true); - set1.setFormLineWidth(1f); - set1.setFormLineDashEffect(new DashPathEffect(new float[]{10f, 5f}, 0f)); - set1.setFormSize(15.f); - - if (Utils.getSDKInt() >= 18) { - // fill drawable only supported on api level 18 and above - Drawable drawable = ContextCompat.getDrawable(this, R.drawable.fade_red); - set1.setFillDrawable(drawable); - } - else { - set1.setFillColor(Color.BLACK); - } - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets - - // create a data object with the datasets - LineData data = new LineData(dataSets); - - // set data - mChart.setData(data); - } - } - - @Override - public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { - Log.i("Gesture", "START, x: " + me.getX() + ", y: " + me.getY()); - } - - @Override - public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { - Log.i("Gesture", "END, lastGesture: " + lastPerformedGesture); - - // un-highlight values after the gesture is finished and no single-tap - if(lastPerformedGesture != ChartTouchListener.ChartGesture.SINGLE_TAP) - mChart.highlightValues(null); // or highlightTouch(null) for callback to onNothingSelected(...) - } - - @Override - public void onChartLongPressed(MotionEvent me) { - Log.i("LongPress", "Chart longpressed."); + chart.invalidate(); } @Override - public void onChartDoubleTapped(MotionEvent me) { - Log.i("DoubleTap", "Chart double-tapped."); + public void saveToGallery() { + saveToGallery(chart, "LineChartActivity1"); } @Override - public void onChartSingleTapped(MotionEvent me) { - Log.i("SingleTap", "Chart single-tapped."); - } - - @Override - public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { - Log.i("Fling", "Chart flinged. VeloX: " + velocityX + ", VeloY: " + velocityY); - } + public void onStartTrackingTouch(SeekBar seekBar) {} @Override - public void onChartScale(MotionEvent me, float scaleX, float scaleY) { - Log.i("Scale / Zoom", "ScaleX: " + scaleX + ", ScaleY: " + scaleY); - } - - @Override - public void onChartTranslate(MotionEvent me, float dX, float dY) { - Log.i("Translate / Move", "dX: " + dX + ", dY: " + dY); - } + public void onStopTrackingTouch(SeekBar seekBar) {} @Override public void onValueSelected(Entry e, Highlight h) { Log.i("Entry selected", e.toString()); - Log.i("LOWHIGH", "low: " + mChart.getLowestVisibleX() + ", high: " + mChart.getHighestVisibleX()); - Log.i("MIN MAX", "xmin: " + mChart.getXChartMin() + ", xmax: " + mChart.getXChartMax() + ", ymin: " + mChart.getYChartMin() + ", ymax: " + mChart.getYChartMax()); + Log.i("LOW HIGH", "low: " + chart.getLowestVisibleX() + ", high: " + chart.getHighestVisibleX()); + Log.i("MIN MAX", "xMin: " + chart.getXChartMin() + ", xMax: " + chart.getXChartMax() + ", yMin: " + chart.getYChartMin() + ", yMax: " + chart.getYChartMax()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index e2a381ff91..c0f72d87fa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -1,8 +1,13 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -10,7 +15,6 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; -import android.widget.Toast; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.Legend; @@ -30,11 +34,17 @@ import java.util.ArrayList; import java.util.List; +/** + * Example of a dual axis {@link LineChart} with multiple data sets. + * + * @since 1.7.4 + * @version 3.0.3 + */ public class LineChartActivity2 extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - private LineChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private LineChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -44,51 +54,53 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart); + setTitle("LineChartActivity2"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarY = findViewById(R.id.seekBar2); - mSeekBarX.setProgress(45); - mSeekBarY.setProgress(100); + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); - mSeekBarY.setOnSeekBarChangeListener(this); - mSeekBarX.setOnSeekBarChangeListener(this); + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); // no description text - mChart.getDescription().setEnabled(false); + chart.getDescription().setEnabled(false); // enable touch gestures - mChart.setTouchEnabled(true); + chart.setTouchEnabled(true); - mChart.setDragDecelerationFrictionCoef(0.9f); + chart.setDragDecelerationFrictionCoef(0.9f); // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - mChart.setDrawGridBackground(false); - mChart.setHighlightPerDragEnabled(true); + chart.setDragEnabled(true); + chart.setScaleEnabled(true); + chart.setDrawGridBackground(false); + chart.setHighlightPerDragEnabled(true); // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(true); + chart.setPinchZoom(true); // set an alternative background color - mChart.setBackgroundColor(Color.LTGRAY); + chart.setBackgroundColor(Color.LTGRAY); // add data + seekBarX.setProgress(20); + seekBarY.setProgress(30); setData(20, 30); - mChart.animateX(2500); + chart.animateX(1500); // get the legend (only possible after setting data) - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); // modify the legend ... l.setForm(LegendForm.LINE); - l.setTypeface(mTfLight); + l.setTypeface(tfLight); l.setTextSize(11f); l.setTextColor(Color.WHITE); l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); @@ -97,23 +109,23 @@ protected void onCreate(Bundle savedInstanceState) { l.setDrawInside(false); // l.setYOffset(11f); - XAxis xAxis = mChart.getXAxis(); - xAxis.setTypeface(mTfLight); + XAxis xAxis = chart.getXAxis(); + xAxis.setTypeface(tfLight); xAxis.setTextSize(11f); xAxis.setTextColor(Color.WHITE); xAxis.setDrawGridLines(false); xAxis.setDrawAxisLine(false); - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(mTfLight); + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setTypeface(tfLight); leftAxis.setTextColor(ColorTemplate.getHoloBlue()); leftAxis.setAxisMaximum(200f); leftAxis.setAxisMinimum(0f); leftAxis.setDrawGridLines(true); leftAxis.setGranularityEnabled(true); - YAxis rightAxis = mChart.getAxisRight(); - rightAxis.setTypeface(mTfLight); + YAxis rightAxis = chart.getAxisRight(); + rightAxis.setTypeface(tfLight); rightAxis.setTextColor(Color.RED); rightAxis.setAxisMaximum(900); rightAxis.setAxisMinimum(-200); @@ -122,6 +134,93 @@ protected void onCreate(Bundle savedInstanceState) { rightAxis.setGranularityEnabled(false); } + private void setData(int count, float range) { + + ArrayList values1 = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * (range / 2f)) + 50; + values1.add(new Entry(i, val)); + } + + ArrayList values2 = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range) + 450; + values2.add(new Entry(i, val)); + } + + ArrayList values3 = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range) + 500; + values3.add(new Entry(i, val)); + } + + LineDataSet set1, set2, set3; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet) chart.getData().getDataSetByIndex(0); + set2 = (LineDataSet) chart.getData().getDataSetByIndex(1); + set3 = (LineDataSet) chart.getData().getDataSetByIndex(2); + set1.setValues(values1); + set2.setValues(values2); + set3.setValues(values3); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + // create a dataset and give it a type + set1 = new LineDataSet(values1, "DataSet 1"); + + set1.setAxisDependency(AxisDependency.LEFT); + set1.setColor(ColorTemplate.getHoloBlue()); + set1.setCircleColor(Color.WHITE); + set1.setLineWidth(2f); + set1.setCircleRadius(3f); + set1.setFillAlpha(65); + set1.setFillColor(ColorTemplate.getHoloBlue()); + set1.setHighLightColor(Color.rgb(244, 117, 117)); + set1.setDrawCircleHole(false); + //set1.setFillFormatter(new MyFillFormatter(0f)); + //set1.setDrawHorizontalHighlightIndicator(false); + //set1.setVisible(false); + //set1.setCircleHoleColor(Color.WHITE); + + // create a dataset and give it a type + set2 = new LineDataSet(values2, "DataSet 2"); + set2.setAxisDependency(AxisDependency.RIGHT); + set2.setColor(Color.RED); + set2.setCircleColor(Color.WHITE); + set2.setLineWidth(2f); + set2.setCircleRadius(3f); + set2.setFillAlpha(65); + set2.setFillColor(Color.RED); + set2.setDrawCircleHole(false); + set2.setHighLightColor(Color.rgb(244, 117, 117)); + //set2.setFillFormatter(new MyFillFormatter(900f)); + + set3 = new LineDataSet(values3, "DataSet 3"); + set3.setAxisDependency(AxisDependency.RIGHT); + set3.setColor(Color.YELLOW); + set3.setCircleColor(Color.WHITE); + set3.setLineWidth(2f); + set3.setCircleRadius(3f); + set3.setFillAlpha(65); + set3.setFillColor(ColorTemplate.colorWithAlpha(Color.YELLOW, 200)); + set3.setDrawCircleHole(false); + set3.setHighLightColor(Color.rgb(244, 117, 117)); + + // create a data object with the data sets + LineData data = new LineData(set1, set2, set3); + data.setValueTextColor(Color.WHITE); + data.setValueTextSize(9f); + + // set data + chart.setData(data); + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.line, menu); @@ -132,8 +231,14 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -142,19 +247,19 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawValues(!set.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionToggleFilled: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -165,11 +270,11 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawFilled(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleCircles: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -180,11 +285,11 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawCircles(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleCubic: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -194,11 +299,11 @@ public boolean onOptionsItemSelected(MenuItem item) { ? LineDataSet.Mode.LINEAR : LineDataSet.Mode.CUBIC_BEZIER); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleStepped: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -208,11 +313,11 @@ public boolean onOptionsItemSelected(MenuItem item) { ? LineDataSet.Mode.LINEAR : LineDataSet.Mode.STEPPED); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHorizontalCubic: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -222,46 +327,41 @@ public boolean onOptionsItemSelected(MenuItem item) { ? LineDataSet.Mode.LINEAR : LineDataSet.Mode.HORIZONTAL_BEZIER); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.animateX: { - mChart.animateX(3000); - //mChart.highlightValue(9.7f, 1, false); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } - case R.id.actionSave: { - if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - - // mChart.saveToGallery("title"+System.currentTimeMillis()) + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -271,117 +371,29 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); + setData(seekBarX.getProgress(), seekBarY.getProgress()); // redraw - mChart.invalidate(); + chart.invalidate(); } - private void setData(int count, float range) { - - ArrayList yVals1 = new ArrayList(); - - for (int i = 0; i < count; i++) { - float mult = range / 2f; - float val = (float) (Math.random() * mult) + 50; - yVals1.add(new Entry(i, val)); - } - - ArrayList yVals2 = new ArrayList(); - - for (int i = 0; i < count-1; i++) { - float mult = range; - float val = (float) (Math.random() * mult) + 450; - yVals2.add(new Entry(i, val)); -// if(i == 10) { -// yVals2.add(new Entry(i, val + 50)); -// } - } - - ArrayList yVals3 = new ArrayList(); - - for (int i = 0; i < count; i++) { - float mult = range; - float val = (float) (Math.random() * mult) + 500; - yVals3.add(new Entry(i, val)); - } - - LineDataSet set1, set2, set3; - - if (mChart.getData() != null && - mChart.getData().getDataSetCount() > 0) { - set1 = (LineDataSet) mChart.getData().getDataSetByIndex(0); - set2 = (LineDataSet) mChart.getData().getDataSetByIndex(1); - set3 = (LineDataSet) mChart.getData().getDataSetByIndex(2); - set1.setValues(yVals1); - set2.setValues(yVals2); - set3.setValues(yVals3); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); - } else { - // create a dataset and give it a type - set1 = new LineDataSet(yVals1, "DataSet 1"); - - set1.setAxisDependency(AxisDependency.LEFT); - set1.setColor(ColorTemplate.getHoloBlue()); - set1.setCircleColor(Color.WHITE); - set1.setLineWidth(2f); - set1.setCircleRadius(3f); - set1.setFillAlpha(65); - set1.setFillColor(ColorTemplate.getHoloBlue()); - set1.setHighLightColor(Color.rgb(244, 117, 117)); - set1.setDrawCircleHole(false); - //set1.setFillFormatter(new MyFillFormatter(0f)); - //set1.setDrawHorizontalHighlightIndicator(false); - //set1.setVisible(false); - //set1.setCircleHoleColor(Color.WHITE); - - // create a dataset and give it a type - set2 = new LineDataSet(yVals2, "DataSet 2"); - set2.setAxisDependency(AxisDependency.RIGHT); - set2.setColor(Color.RED); - set2.setCircleColor(Color.WHITE); - set2.setLineWidth(2f); - set2.setCircleRadius(3f); - set2.setFillAlpha(65); - set2.setFillColor(Color.RED); - set2.setDrawCircleHole(false); - set2.setHighLightColor(Color.rgb(244, 117, 117)); - //set2.setFillFormatter(new MyFillFormatter(900f)); - - set3 = new LineDataSet(yVals3, "DataSet 3"); - set3.setAxisDependency(AxisDependency.RIGHT); - set3.setColor(Color.YELLOW); - set3.setCircleColor(Color.WHITE); - set3.setLineWidth(2f); - set3.setCircleRadius(3f); - set3.setFillAlpha(65); - set3.setFillColor(ColorTemplate.colorWithAlpha(Color.YELLOW, 200)); - set3.setDrawCircleHole(false); - set3.setHighLightColor(Color.rgb(244, 117, 117)); - - // create a data object with the datasets - LineData data = new LineData(set1, set2, set3); - data.setValueTextColor(Color.WHITE); - data.setValueTextSize(9f); - - // set data - mChart.setData(data); - } + @Override + public void saveToGallery() { + saveToGallery(chart, "LineChartActivity2"); } @Override public void onValueSelected(Entry e, Highlight h) { Log.i("Entry selected", e.toString()); - mChart.centerViewToAnimated(e.getX(), e.getY(), mChart.getData().getDataSetByIndex(h.getDataSetIndex()) + chart.centerViewToAnimated(e.getX(), e.getY(), chart.getData().getDataSetByIndex(h.getDataSetIndex()) .getAxisDependency(), 500); - //mChart.zoomAndCenterAnimated(2.5f, 2.5f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex) + //chart.zoomAndCenterAnimated(2.5f, 2.5f, e.getX(), e.getY(), chart.getData().getDataSetByIndex(dataSetIndex) // .getAxisDependency(), 1000); - //mChart.zoomAndCenterAnimated(1.8f, 1.8f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex) + //chart.zoomAndCenterAnimated(1.8f, 1.8f, e.getX(), e.getY(), chart.getData().getDataSetByIndex(dataSetIndex) // .getAxisDependency(), 1000); } @@ -391,14 +403,8 @@ public void onNothingSelected() { } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStartTrackingTouch(SeekBar seekBar) {} @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStopTrackingTouch(SeekBar seekBar) {} } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index 39730d55b1..7bb95b83ed 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -1,9 +1,13 @@ package com.xxmassdeveloper.mpchartexample; +import android.content.Intent; import android.graphics.Color; import android.graphics.Typeface; +import android.net.Uri; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; import android.view.WindowManager; import com.github.mikephil.charting.charts.LineChart; @@ -15,10 +19,10 @@ import java.util.ArrayList; +@SuppressWarnings("SameParameterValue") public class LineChartActivityColored extends DemoBase { - private LineChart[] mCharts = new LineChart[4]; - private Typeface mTf; + private LineChart[] charts = new LineChart[4]; @Override protected void onCreate(Bundle savedInstanceState) { @@ -27,26 +31,28 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_colored_lines); - mCharts[0] = findViewById(R.id.chart1); - mCharts[1] = findViewById(R.id.chart2); - mCharts[2] = findViewById(R.id.chart3); - mCharts[3] = findViewById(R.id.chart4); + setTitle("LineChartActivityColored"); - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Bold.ttf"); + charts[0] = findViewById(R.id.chart1); + charts[1] = findViewById(R.id.chart2); + charts[2] = findViewById(R.id.chart3); + charts[3] = findViewById(R.id.chart4); - for (int i = 0; i < mCharts.length; i++) { + Typeface mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Bold.ttf"); + + for (int i = 0; i < charts.length; i++) { LineData data = getData(36, 100); data.setValueTypeface(mTf); // add some transparency to the color with "& 0x90FFFFFF" - setupChart(mCharts[i], data, mColors[i % mColors.length]); + setupChart(charts[i], data, colors[i % colors.length]); } } - private int[] mColors = new int[] { - Color.rgb(137, 230, 81), - Color.rgb(240, 240, 30), + private final int[] colors = new int[] { + Color.rgb(137, 230, 81), + Color.rgb(240, 240, 30), Color.rgb(89, 199, 250), Color.rgb(250, 104, 104) }; @@ -57,8 +63,8 @@ private void setupChart(LineChart chart, LineData data, int color) { // no description text chart.getDescription().setEnabled(false); - - // mChart.setDrawHorizontalGrid(false); + + // chart.setDrawHorizontalGrid(false); // // enable / disable grid background chart.setDrawGridBackground(false); @@ -75,7 +81,7 @@ private void setupChart(LineChart chart, LineData data, int color) { chart.setPinchZoom(false); chart.setBackgroundColor(color); - + // set custom chart offsets (automatic offset calculation is hereby disabled) chart.setViewPortOffsets(10, 0, 10, 0); @@ -96,18 +102,18 @@ private void setupChart(LineChart chart, LineData data, int color) { // animate calls invalidate()... chart.animateX(2500); } - + private LineData getData(int count, float range) { - ArrayList yVals = new ArrayList(); + ArrayList values = new ArrayList<>(); for (int i = 0; i < count; i++) { float val = (float) (Math.random() * range) + 3; - yVals.add(new Entry(i, val)); + values.add(new Entry(i, val)); } // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(yVals, "DataSet 1"); + LineDataSet set1 = new LineDataSet(values, "DataSet 1"); // set1.setFillAlpha(110); // set1.setFillColor(Color.RED); @@ -119,9 +125,31 @@ private LineData getData(int count, float range) { set1.setHighLightColor(Color.WHITE); set1.setDrawValues(false); - // create a data object with the datasets - LineData data = new LineData(set1); + // create a data object with the data sets + return new LineData(set1); + } - return data; + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java")); + startActivity(i); + break; + } + } + + return true; + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 6bf96f02a7..a88842c5d0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -1,15 +1,19 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; 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 android.widget.Toast; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.AxisBase; @@ -25,17 +29,17 @@ import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; -import java.sql.Time; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.concurrent.TimeUnit; public class LineChartTime extends DemoBase implements OnSeekBarChangeListener { - private LineChart mChart; - private SeekBar mSeekBarX; + private LineChart chart; + private SeekBar seekBarX; private TextView tvX; @Override @@ -45,44 +49,44 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart_time); - tvX = findViewById(R.id.tvXMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarX.setProgress(100); - tvX.setText("100"); + setTitle("LineChartTime"); - mSeekBarX.setOnSeekBarChangeListener(this); + tvX = findViewById(R.id.tvXMax); + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); + chart = findViewById(R.id.chart1); // no description text - mChart.getDescription().setEnabled(false); + chart.getDescription().setEnabled(false); // enable touch gestures - mChart.setTouchEnabled(true); + chart.setTouchEnabled(true); - mChart.setDragDecelerationFrictionCoef(0.9f); + chart.setDragDecelerationFrictionCoef(0.9f); // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - mChart.setDrawGridBackground(false); - mChart.setHighlightPerDragEnabled(true); + chart.setDragEnabled(true); + chart.setScaleEnabled(true); + chart.setDrawGridBackground(false); + chart.setHighlightPerDragEnabled(true); // set an alternative background color - mChart.setBackgroundColor(Color.WHITE); - mChart.setViewPortOffsets(0f, 0f, 0f, 0f); + chart.setBackgroundColor(Color.WHITE); + chart.setViewPortOffsets(0f, 0f, 0f, 0f); // add data + seekBarX.setProgress(100); setData(100, 30); - mChart.invalidate(); + chart.invalidate(); // get the legend (only possible after setting data) - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setEnabled(false); - XAxis xAxis = mChart.getXAxis(); + XAxis xAxis = chart.getXAxis(); xAxis.setPosition(XAxis.XAxisPosition.TOP_INSIDE); - xAxis.setTypeface(mTfLight); + xAxis.setTypeface(tfLight); xAxis.setTextSize(10f); xAxis.setTextColor(Color.WHITE); xAxis.setDrawAxisLine(false); @@ -92,7 +96,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setGranularity(1f); // one hour xAxis.setValueFormatter(new IAxisValueFormatter() { - private SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm"); + private SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm", Locale.ENGLISH); @Override public String getFormattedValue(float value, AxisBase axis) { @@ -102,9 +106,9 @@ public String getFormattedValue(float value, AxisBase axis) { } }); - YAxis leftAxis = mChart.getAxisLeft(); + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); - leftAxis.setTypeface(mTfLight); + leftAxis.setTypeface(tfLight); leftAxis.setTextColor(ColorTemplate.getHoloBlue()); leftAxis.setDrawGridLines(true); leftAxis.setGranularityEnabled(true); @@ -113,10 +117,49 @@ public String getFormattedValue(float value, AxisBase axis) { leftAxis.setYOffset(-9f); leftAxis.setTextColor(Color.rgb(255, 192, 56)); - YAxis rightAxis = mChart.getAxisRight(); + YAxis rightAxis = chart.getAxisRight(); rightAxis.setEnabled(false); } + private void setData(int count, float range) { + + // now in hours + long now = TimeUnit.MILLISECONDS.toHours(System.currentTimeMillis()); + + ArrayList values = new ArrayList<>(); + + // count = hours + float to = now + count; + + // increment by 1 hour + for (float x = now; x < to; x++) { + + float y = getRandom(range, 50); + values.add(new Entry(x, y)); // add one entry per hour + } + + // create a dataset and give it a type + LineDataSet set1 = new LineDataSet(values, "DataSet 1"); + set1.setAxisDependency(AxisDependency.LEFT); + set1.setColor(ColorTemplate.getHoloBlue()); + set1.setValueTextColor(ColorTemplate.getHoloBlue()); + set1.setLineWidth(1.5f); + set1.setDrawCircles(false); + set1.setDrawValues(false); + set1.setFillAlpha(65); + set1.setFillColor(ColorTemplate.getHoloBlue()); + set1.setHighLightColor(Color.rgb(244, 117, 117)); + set1.setDrawCircleHole(false); + + // create a data object with the data sets + LineData data = new LineData(set1); + data.setValueTextColor(Color.WHITE); + data.setValueTextSize(9f); + + // set data + chart.setData(data); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.line, menu); @@ -127,8 +170,14 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -137,19 +186,19 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawValues(!set.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionToggleFilled: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -160,11 +209,11 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawFilled(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleCircles: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -175,11 +224,11 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawCircles(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleCubic: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -190,11 +239,11 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setMode(LineDataSet.Mode.CUBIC_BEZIER); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleStepped: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -205,45 +254,42 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setMode(LineDataSet.Mode.STEPPED); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } case R.id.actionSave: { - if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - - // mChart.saveToGallery("title"+System.currentTimeMillis()) + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -253,64 +299,22 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress())); + tvX.setText(String.valueOf(seekBarX.getProgress())); - setData(mSeekBarX.getProgress(), 50); + setData(seekBarX.getProgress(), 50); // redraw - mChart.invalidate(); - } - - private void setData(int count, float range) { - - // now in hours - long now = TimeUnit.MILLISECONDS.toHours(System.currentTimeMillis()); - - ArrayList values = new ArrayList(); - - float from = now; - - // count = hours - float to = now + count; - - // increment by 1 hour - for (float x = from; x < to; x++) { - - float y = getRandom(range, 50); - values.add(new Entry(x, y)); // add one entry per hour - } - - // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(values, "DataSet 1"); - set1.setAxisDependency(AxisDependency.LEFT); - set1.setColor(ColorTemplate.getHoloBlue()); - set1.setValueTextColor(ColorTemplate.getHoloBlue()); - set1.setLineWidth(1.5f); - set1.setDrawCircles(false); - set1.setDrawValues(false); - set1.setFillAlpha(65); - set1.setFillColor(ColorTemplate.getHoloBlue()); - set1.setHighLightColor(Color.rgb(244, 117, 117)); - set1.setDrawCircleHole(false); - - // create a data object with the datasets - LineData data = new LineData(set1); - data.setValueTextColor(Color.WHITE); - data.setValueTextSize(9f); - - // set data - mChart.setData(data); + chart.invalidate(); } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - + public void saveToGallery() { + saveToGallery(chart, "LineChartTime"); } @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub + public void onStartTrackingTouch(SeekBar seekBar) {} - } -} \ No newline at end of file + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index 54218a53da..1466e5f9de 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java @@ -1,11 +1,16 @@ package com.xxmassdeveloper.mpchartexample; +import android.annotation.SuppressLint; import android.content.Context; +import android.content.Intent; import android.graphics.Color; -import android.graphics.Typeface; +import android.net.Uri; import android.os.Bundle; +import androidx.annotation.NonNull; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -28,8 +33,8 @@ /** * Demonstrates the use of charts inside a ListView. IMPORTANT: provide a - * specific height attribute for the chart inside your listview-item - * + * specific height attribute for the chart inside your ListView item + * * @author Philipp Jahoda */ public class ListViewBarChartActivity extends DemoBase { @@ -40,10 +45,12 @@ protected void onCreate(Bundle savedInstanceState) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_listview_chart); - + + setTitle("ListViewBarChartActivity"); + ListView lv = findViewById(R.id.listView1); - ArrayList list = new ArrayList(); + ArrayList list = new ArrayList<>(); // 20 items for (int i = 0; i < 20; i++) { @@ -56,16 +63,18 @@ protected void onCreate(Bundle savedInstanceState) { private class ChartDataAdapter extends ArrayAdapter { - public ChartDataAdapter(Context context, List objects) { + ChartDataAdapter(Context context, List objects) { super(context, 0, objects); } + @SuppressLint("InflateParams") + @NonNull @Override - public View getView(int position, View convertView, ViewGroup parent) { + public View getView(int position, View convertView, @NonNull ViewGroup parent) { BarData data = getItem(position); - ViewHolder holder = null; + ViewHolder holder; if (convertView == null) { @@ -82,30 +91,32 @@ public View getView(int position, View convertView, ViewGroup parent) { } // apply styling - data.setValueTypeface(mTfLight); - data.setValueTextColor(Color.BLACK); + if (data != null) { + data.setValueTypeface(tfLight); + data.setValueTextColor(Color.BLACK); + } holder.chart.getDescription().setEnabled(false); holder.chart.setDrawGridBackground(false); XAxis xAxis = holder.chart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setTypeface(mTfLight); + xAxis.setTypeface(tfLight); xAxis.setDrawGridLines(false); - + YAxis leftAxis = holder.chart.getAxisLeft(); - leftAxis.setTypeface(mTfLight); + leftAxis.setTypeface(tfLight); leftAxis.setLabelCount(5, false); leftAxis.setSpaceTop(15f); - + YAxis rightAxis = holder.chart.getAxisRight(); - rightAxis.setTypeface(mTfLight); + rightAxis.setTypeface(tfLight); rightAxis.setLabelCount(5, false); rightAxis.setSpaceTop(15f); // set data holder.chart.setData(data); holder.chart.setFitBars(true); - + // do not forget to refresh the chart // holder.chart.invalidate(); holder.chart.animateY(700); @@ -121,12 +132,12 @@ private class ViewHolder { /** * generates a random ChartData object with just one DataSet - * - * @return + * + * @return Bar data */ private BarData generateData(int cnt) { - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); for (int i = 0; i < 12; i++) { entries.add(new BarEntry(i, (float) (Math.random() * 70) + 30)); @@ -135,12 +146,36 @@ private BarData generateData(int cnt) { BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); d.setColors(ColorTemplate.VORDIPLOM_COLORS); d.setBarShadowColor(Color.rgb(203, 203, 203)); - - ArrayList sets = new ArrayList(); + + ArrayList sets = new ArrayList<>(); sets.add(d); - + BarData cd = new BarData(sets); cd.setBarWidth(0.9f); return cd; } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java")); + startActivity(i); + break; + } + } + + return true; + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java index 0c9f132f03..1455691620 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java @@ -2,8 +2,13 @@ package com.xxmassdeveloper.mpchartexample; import android.content.Context; +import android.content.Intent; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import androidx.annotation.NonNull; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -32,8 +37,8 @@ /** * Demonstrates the use of charts inside a ListView. IMPORTANT: provide a - * specific height attribute for the chart inside your listview-item - * + * specific height attribute for the chart inside your ListView item + * * @author Philipp Jahoda */ public class ListViewMultiChartActivity extends DemoBase { @@ -44,20 +49,22 @@ protected void onCreate(Bundle savedInstanceState) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_listview_chart); - + + setTitle("ListViewMultiChartActivity"); + ListView lv = findViewById(R.id.listView1); - ArrayList list = new ArrayList(); + ArrayList list = new ArrayList<>(); // 30 items for (int i = 0; i < 30; i++) { - + if(i % 3 == 0) { list.add(new LineChartItem(generateDataLine(i + 1), getApplicationContext())); } else if(i % 3 == 1) { list.add(new BarChartItem(generateDataBar(i + 1), getApplicationContext())); } else if(i % 3 == 2) { - list.add(new PieChartItem(generateDataPie(i + 1), getApplicationContext())); + list.add(new PieChartItem(generateDataPie(), getApplicationContext())); } } @@ -67,77 +74,79 @@ protected void onCreate(Bundle savedInstanceState) { /** adapter that supports 3 different item types */ private class ChartDataAdapter extends ArrayAdapter { - - public ChartDataAdapter(Context context, List objects) { + + ChartDataAdapter(Context context, List objects) { super(context, 0, objects); } + @NonNull @Override - public View getView(int position, View convertView, ViewGroup parent) { + public View getView(int position, View convertView, @NonNull ViewGroup parent) { + //noinspection ConstantConditions return getItem(position).getView(position, convertView, getContext()); } - + @Override - public int getItemViewType(int position) { + public int getItemViewType(int position) { // return the views type - return getItem(position).getItemType(); + ChartItem ci = getItem(position); + return ci != null ? ci.getItemType() : 0; } - + @Override public int getViewTypeCount() { return 3; // we have 3 different item-types } } - + /** * generates a random ChartData object with just one DataSet - * - * @return + * + * @return Line data */ private LineData generateDataLine(int cnt) { - ArrayList e1 = new ArrayList(); + ArrayList values1 = new ArrayList<>(); for (int i = 0; i < 12; i++) { - e1.add(new Entry(i, (int) (Math.random() * 65) + 40)); + values1.add(new Entry(i, (int) (Math.random() * 65) + 40)); } - LineDataSet d1 = new LineDataSet(e1, "New DataSet " + cnt + ", (1)"); + LineDataSet d1 = new LineDataSet(values1, "New DataSet " + cnt + ", (1)"); d1.setLineWidth(2.5f); d1.setCircleRadius(4.5f); d1.setHighLightColor(Color.rgb(244, 117, 117)); d1.setDrawValues(false); - - ArrayList e2 = new ArrayList(); + + ArrayList values2 = new ArrayList<>(); for (int i = 0; i < 12; i++) { - e2.add(new Entry(i, e1.get(i).getY() - 30)); + values2.add(new Entry(i, values1.get(i).getY() - 30)); } - LineDataSet d2 = new LineDataSet(e2, "New DataSet " + cnt + ", (2)"); + LineDataSet d2 = new LineDataSet(values2, "New DataSet " + cnt + ", (2)"); d2.setLineWidth(2.5f); d2.setCircleRadius(4.5f); d2.setHighLightColor(Color.rgb(244, 117, 117)); d2.setColor(ColorTemplate.VORDIPLOM_COLORS[0]); d2.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[0]); d2.setDrawValues(false); - - ArrayList sets = new ArrayList(); + + ArrayList sets = new ArrayList<>(); sets.add(d1); sets.add(d2); - - LineData cd = new LineData(sets); - return cd; + + return new LineData(sets); } - + /** * generates a random ChartData object with just one DataSet - * - * @return + * + * @return Bar data */ private BarData generateDataBar(int cnt) { - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); for (int i = 0; i < 12; i++) { entries.add(new BarEntry(i, (int) (Math.random() * 70) + 30)); @@ -146,32 +155,55 @@ private BarData generateDataBar(int cnt) { BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); d.setColors(ColorTemplate.VORDIPLOM_COLORS); d.setHighLightAlpha(255); - + BarData cd = new BarData(d); cd.setBarWidth(0.9f); return cd; } - + /** * generates a random ChartData object with just one DataSet - * - * @return + * + * @return Pie data */ - private PieData generateDataPie(int cnt) { + private PieData generateDataPie() { - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); for (int i = 0; i < 4; i++) { entries.add(new PieEntry((float) ((Math.random() * 70) + 30), "Quarter " + (i+1))); } PieDataSet d = new PieDataSet(entries, ""); - + // space between slices d.setSliceSpace(2f); d.setColors(ColorTemplate.VORDIPLOM_COLORS); - - PieData cd = new PieData(d); - return cd; + + return new PieData(d); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java")); + startActivity(i); + break; + } + } + + return true; } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index e6acf01670..c5cbd570c6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -1,10 +1,16 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.WindowManager; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; @@ -12,12 +18,13 @@ import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.listener.ChartTouchListener; +import com.github.mikephil.charting.listener.OnChartGestureListener; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -26,10 +33,10 @@ import java.util.List; public class MultiLineChartActivity extends DemoBase implements OnSeekBarChangeListener, - OnChartValueSelectedListener { + OnChartGestureListener, OnChartValueSelectedListener { - private LineChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private LineChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -39,51 +46,101 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart); + setTitle("MultiLineChartActivity"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); + + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); - mSeekBarY = findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); - mChart = findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - - mChart.setDrawGridBackground(false); - mChart.getDescription().setEnabled(false); - mChart.setDrawBorders(false); + chart.setDrawGridBackground(false); + chart.getDescription().setEnabled(false); + chart.setDrawBorders(false); - mChart.getAxisLeft().setEnabled(false); - mChart.getAxisRight().setDrawAxisLine(false); - mChart.getAxisRight().setDrawGridLines(false); - mChart.getXAxis().setDrawAxisLine(false); - mChart.getXAxis().setDrawGridLines(false); + chart.getAxisLeft().setEnabled(false); + chart.getAxisRight().setDrawAxisLine(false); + chart.getAxisRight().setDrawGridLines(false); + chart.getXAxis().setDrawAxisLine(false); + chart.getXAxis().setDrawGridLines(false); // enable touch gestures - mChart.setTouchEnabled(true); + chart.setTouchEnabled(true); // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); + chart.setDragEnabled(true); + chart.setScaleEnabled(true); // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(false); + chart.setPinchZoom(false); - mSeekBarX.setProgress(20); - mSeekBarY.setProgress(100); + seekBarX.setProgress(20); + seekBarY.setProgress(100); - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); l.setOrientation(Legend.LegendOrientation.VERTICAL); l.setDrawInside(false); } + private final int[] colors = new int[] { + ColorTemplate.VORDIPLOM_COLORS[0], + ColorTemplate.VORDIPLOM_COLORS[1], + ColorTemplate.VORDIPLOM_COLORS[2] + }; + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + chart.resetTracking(); + + progress = seekBarX.getProgress(); + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + ArrayList dataSets = new ArrayList<>(); + + for (int z = 0; z < 3; z++) { + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < progress; i++) { + double val = (Math.random() * seekBarY.getProgress()) + 3; + values.add(new Entry(i, (float) val)); + } + + LineDataSet d = new LineDataSet(values, "DataSet " + (z + 1)); + d.setLineWidth(2.5f); + d.setCircleRadius(4f); + + int color = colors[z % colors.length]; + d.setColor(color); + d.setCircleColor(color); + dataSets.add(d); + } + + // make the first DataSet dashed + ((LineDataSet) dataSets.get(0)).enableDashedLine(10, 10, 0); + ((LineDataSet) dataSets.get(0)).setColors(ColorTemplate.VORDIPLOM_COLORS); + ((LineDataSet) dataSets.get(0)).setCircleColors(ColorTemplate.VORDIPLOM_COLORS); + + LineData data = new LineData(dataSets); + chart.setData(data); + chart.invalidate(); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.line, menu); + menu.removeItem(R.id.actionToggleIcons); return true; } @@ -91,8 +148,14 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -101,32 +164,35 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawValues(!set.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } + /* + case R.id.actionToggleIcons: { break; } + */ case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionToggleFilled: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -137,11 +203,11 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawFilled(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleCircles: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -152,74 +218,122 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawCircles(true); } - mChart.invalidate(); + chart.invalidate(); + break; + } + case R.id.actionToggleCubic: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.CUBIC_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.CUBIC_BEZIER); + } + chart.invalidate(); + break; + } + case R.id.actionToggleStepped: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.STEPPED + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.STEPPED); + } + chart.invalidate(); + break; + } + case R.id.actionToggleHorizontalCubic: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.HORIZONTAL_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.HORIZONTAL_BEZIER); + } + chart.invalidate(); break; } case R.id.actionSave: { - // mChart.saveToGallery("title"+System.currentTimeMillis()); - mChart.saveToPath("title" + System.currentTimeMillis(), ""); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } } return true; } - private int[] mColors = new int[] { - ColorTemplate.VORDIPLOM_COLORS[0], - ColorTemplate.VORDIPLOM_COLORS[1], - ColorTemplate.VORDIPLOM_COLORS[2] - }; - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - mChart.resetTracking(); + public void saveToGallery() { + saveToGallery(chart, "MultiLineChartActivity"); + } - tvX.setText("" + (mSeekBarX.getProgress())); - tvY.setText("" + (mSeekBarY.getProgress())); + @Override + public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { + Log.i("Gesture", "START, x: " + me.getX() + ", y: " + me.getY()); + } - ArrayList dataSets = new ArrayList(); + @Override + public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { + Log.i("Gesture", "END, lastGesture: " + lastPerformedGesture); - for (int z = 0; z < 3; z++) { + // un-highlight values after the gesture is finished and no single-tap + if(lastPerformedGesture != ChartTouchListener.ChartGesture.SINGLE_TAP) + chart.highlightValues(null); // or highlightTouch(null) for callback to onNothingSelected(...) + } - ArrayList values = new ArrayList(); + @Override + public void onChartLongPressed(MotionEvent me) { + Log.i("LongPress", "Chart long pressed."); + } - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - double val = (Math.random() * mSeekBarY.getProgress()) + 3; - values.add(new Entry(i, (float) val)); - } + @Override + public void onChartDoubleTapped(MotionEvent me) { + Log.i("DoubleTap", "Chart double-tapped."); + } - LineDataSet d = new LineDataSet(values, "DataSet " + (z + 1)); - d.setLineWidth(2.5f); - d.setCircleRadius(4f); + @Override + public void onChartSingleTapped(MotionEvent me) { + Log.i("SingleTap", "Chart single-tapped."); + } - int color = mColors[z % mColors.length]; - d.setColor(color); - d.setCircleColor(color); - dataSets.add(d); - } + @Override + public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { + Log.i("Fling", "Chart fling. VelocityX: " + velocityX + ", VelocityY: " + velocityY); + } - // make the first DataSet dashed - ((LineDataSet) dataSets.get(0)).enableDashedLine(10, 10, 0); - ((LineDataSet) dataSets.get(0)).setColors(ColorTemplate.VORDIPLOM_COLORS); - ((LineDataSet) dataSets.get(0)).setCircleColors(ColorTemplate.VORDIPLOM_COLORS); + @Override + public void onChartScale(MotionEvent me, float scaleX, float scaleY) { + Log.i("Scale / Zoom", "ScaleX: " + scaleX + ", ScaleY: " + scaleY); + } - LineData data = new LineData(dataSets); - mChart.setData(data); - mChart.invalidate(); + @Override + public void onChartTranslate(MotionEvent me, float dX, float dY) { + Log.i("Translate / Move", "dX: " + dX + ", dY: " + dY); } @Override @@ -230,16 +344,11 @@ public void onValueSelected(Entry e, Highlight h) { } @Override - public void onNothingSelected() { - // TODO Auto-generated method stub - - } + public void onNothingSelected() {} @Override - public void onStartTrackingTouch(SeekBar seekBar) { - } + public void onStartTrackingTouch(SeekBar seekBar) {} @Override - public void onStopTrackingTouch(SeekBar seekBar) { - } + public void onStopTrackingTouch(SeekBar seekBar) {} } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java index a2d4becadc..c557323451 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -1,8 +1,12 @@ package com.xxmassdeveloper.mpchartexample; +import android.content.Intent; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; import android.view.WindowManager; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; @@ -17,11 +21,12 @@ import java.util.ArrayList; +@SuppressWarnings("SameParameterValue") public class PerformanceLineChart extends DemoBase implements OnSeekBarChangeListener { - private LineChart mChart; - private SeekBar mSeekBarValues; - private TextView mTvCount; + private LineChart chart; + private SeekBar seekBarValues; + private TextView tvCount; @Override protected void onCreate(Bundle savedInstanceState) { @@ -30,80 +35,51 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_performance_linechart); - mTvCount = findViewById(R.id.tvValueCount); - mSeekBarValues = findViewById(R.id.seekbarValues); - mTvCount.setText("500"); + setTitle("PerformanceLineChart"); - mSeekBarValues.setProgress(500); - - mSeekBarValues.setOnSeekBarChangeListener(this); + tvCount = findViewById(R.id.tvValueCount); + seekBarValues = findViewById(R.id.seekbarValues); + seekBarValues.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); - mChart.setDrawGridBackground(false); + chart = findViewById(R.id.chart1); + chart.setDrawGridBackground(false); // no description text - mChart.getDescription().setEnabled(false); + chart.getDescription().setEnabled(false); // enable touch gestures - mChart.setTouchEnabled(true); + chart.setTouchEnabled(true); // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); + chart.setDragEnabled(true); + chart.setScaleEnabled(true); // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.getAxisLeft().setDrawGridLines(false); - mChart.getAxisRight().setEnabled(false); - mChart.getXAxis().setDrawGridLines(true); - mChart.getXAxis().setDrawAxisLine(false); - - // dont forget to refresh the drawing - mChart.invalidate(); - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - int count = mSeekBarValues.getProgress() + 1000; - mTvCount.setText("" + count); - - mChart.resetTracking(); - - setData(count, 500f); - - // redraw - mChart.invalidate(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub + chart.setPinchZoom(false); - } + chart.getAxisLeft().setDrawGridLines(false); + chart.getAxisRight().setEnabled(false); + chart.getXAxis().setDrawGridLines(true); + chart.getXAxis().setDrawAxisLine(false); - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub + seekBarValues.setProgress(9000); + // don't forget to refresh the drawing + chart.invalidate(); } private void setData(int count, float range) { - ArrayList yVals = new ArrayList(); + ArrayList values = new ArrayList<>(); for (int i = 0; i < count; i++) { - float mult = (range + 1); - float val = (float) (Math.random() * mult) + 3;// + (float) - // ((mult * - // 0.1) / 10); - yVals.add(new Entry(i * 0.001f, val)); + float val = (float) (Math.random() * (range + 1)) + 3; + values.add(new Entry(i * 0.001f, val)); } // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(yVals, "DataSet 1"); - + LineDataSet set1 = new LineDataSet(values, "DataSet 1"); + set1.setColor(Color.BLACK); set1.setLineWidth(0.5f); set1.setDrawValues(false); @@ -111,14 +87,58 @@ private void setData(int count, float range) { set1.setMode(LineDataSet.Mode.LINEAR); set1.setDrawFilled(false); - // create a data object with the datasets + // create a data object with the data sets LineData data = new LineData(set1); // set data - mChart.setData(data); - + chart.setData(data); + // get the legend (only possible after setting data) - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setEnabled(false); } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java")); + startActivity(i); + break; + } + } + + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + int count = seekBarValues.getProgress() + 1000; + tvCount.setText(String.valueOf(count)); + + chart.resetTracking(); + + setData(count, 500f); + + // redraw + chart.invalidate(); + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 0252ff8ff0..9d77dcc8b7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -1,9 +1,14 @@ 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 androidx.core.content.ContextCompat; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.text.style.RelativeSizeSpan; @@ -19,7 +24,6 @@ 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.components.Legend.LegendPosition; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; @@ -37,8 +41,8 @@ public class PieChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - private PieChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private PieChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -48,55 +52,57 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_piechart); + setTitle("PieChartActivity"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarY = findViewById(R.id.seekBar2); - mSeekBarX.setProgress(4); - mSeekBarY.setProgress(10); + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); + + seekBarX.setOnSeekBarChangeListener(this); + seekBarY.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); - mChart.setUsePercentValues(true); - mChart.getDescription().setEnabled(false); - mChart.setExtraOffsets(5, 10, 5, 5); + chart = findViewById(R.id.chart1); + chart.setUsePercentValues(true); + chart.getDescription().setEnabled(false); + chart.setExtraOffsets(5, 10, 5, 5); - mChart.setDragDecelerationFrictionCoef(0.95f); + chart.setDragDecelerationFrictionCoef(0.95f); - mChart.setCenterTextTypeface(mTfLight); - mChart.setCenterText(generateCenterSpannableText()); + chart.setCenterTextTypeface(tfLight); + chart.setCenterText(generateCenterSpannableText()); - mChart.setDrawHoleEnabled(true); - mChart.setHoleColor(Color.WHITE); + chart.setDrawHoleEnabled(true); + chart.setHoleColor(Color.WHITE); - mChart.setTransparentCircleColor(Color.WHITE); - mChart.setTransparentCircleAlpha(110); + chart.setTransparentCircleColor(Color.WHITE); + chart.setTransparentCircleAlpha(110); - mChart.setHoleRadius(58f); - mChart.setTransparentCircleRadius(61f); + chart.setHoleRadius(58f); + chart.setTransparentCircleRadius(61f); - mChart.setDrawCenterText(true); + chart.setDrawCenterText(true); - mChart.setRotationAngle(0); + chart.setRotationAngle(0); // enable rotation of the chart by touch - mChart.setRotationEnabled(true); - mChart.setHighlightPerTapEnabled(true); + chart.setRotationEnabled(true); + chart.setHighlightPerTapEnabled(true); - // mChart.setUnit(" €"); - // mChart.setDrawUnitsInChart(true); + // chart.setUnit(" €"); + // chart.setDrawUnitsInChart(true); // add a selection listener - mChart.setOnChartValueSelectedListener(this); + chart.setOnChartValueSelectedListener(this); + seekBarX.setProgress(4); + seekBarY.setProgress(10); setData(4, 100); - mChart.animateY(1400, Easing.EaseInOutQuad); - // mChart.spin(2000, 0, 360); + chart.animateY(1400, Easing.EaseInOutQuad); + // chart.spin(2000, 0, 360); - mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarY.setOnSeekBarChangeListener(this); - - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); l.setOrientation(Legend.LegendOrientation.VERTICAL); @@ -106,9 +112,65 @@ protected void onCreate(Bundle savedInstanceState) { l.setYOffset(0f); // entry label styling - mChart.setEntryLabelColor(Color.WHITE); - mChart.setEntryLabelTypeface(mTfRegular); - mChart.setEntryLabelTextSize(12f); + chart.setEntryLabelColor(Color.WHITE); + chart.setEntryLabelTypeface(tfRegular); + chart.setEntryLabelTextSize(12f); + } + + private void setData(int count, float range) { + ArrayList entries = new ArrayList<>(); + + // 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) ((Math.random() * 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); + + chart.invalidate(); } @Override @@ -121,65 +183,74 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - for (IDataSet set : mChart.getData().getDataSets()) + for (IDataSet set : chart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleIcons: { - for (IDataSet set : mChart.getData().getDataSets()) + for (IDataSet set : chart.getData().getDataSets()) set.setDrawIcons(!set.isDrawIconsEnabled()); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHole: { - if (mChart.isDrawHoleEnabled()) - mChart.setDrawHoleEnabled(false); + if (chart.isDrawHoleEnabled()) + chart.setDrawHoleEnabled(false); else - mChart.setDrawHoleEnabled(true); - mChart.invalidate(); + chart.setDrawHoleEnabled(true); + chart.invalidate(); break; } case R.id.actionDrawCenter: { - if (mChart.isDrawCenterTextEnabled()) - mChart.setDrawCenterText(false); + if (chart.isDrawCenterTextEnabled()) + chart.setDrawCenterText(false); else - mChart.setDrawCenterText(true); - mChart.invalidate(); + chart.setDrawCenterText(true); + chart.invalidate(); break; } - case R.id.actionToggleXVals: { + case R.id.actionToggleXValues: { - mChart.setDrawEntryLabels(!mChart.isDrawEntryLabelsEnabled()); - mChart.invalidate(); - break; - } - case R.id.actionSave: { - // mChart.saveToGallery("title"+System.currentTimeMillis()); - mChart.saveToPath("title" + System.currentTimeMillis(), ""); + chart.setDrawEntryLabels(!chart.isDrawEntryLabelsEnabled()); + chart.invalidate(); break; } case R.id.actionTogglePercent: - mChart.setUsePercentValues(!mChart.isUsePercentValuesEnabled()); - mChart.invalidate(); + chart.setUsePercentValues(!chart.isUsePercentValuesEnabled()); + chart.invalidate(); break; case R.id.animateX: { - mChart.animateX(1400); + chart.animateX(1400); break; } case R.id.animateY: { - mChart.animateY(1400); + chart.animateY(1400); break; } case R.id.animateXY: { - mChart.animateXY(1400, 1400); + chart.animateXY(1400, 1400); break; } case R.id.actionToggleSpin: { - mChart.spin(1000, mChart.getRotationAngle(), mChart.getRotationAngle() + 360, Easing.EaseInCubic); + 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; } } @@ -189,69 +260,15 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress())); - tvY.setText("" + (mSeekBarY.getProgress())); + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); - setData(mSeekBarX.getProgress(), mSeekBarY.getProgress()); + setData(seekBarX.getProgress(), seekBarY.getProgress()); } - private void setData(int count, float range) { - - float mult = range; - - ArrayList entries = new ArrayList(); - - // 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) ((Math.random() * mult) + mult / 5), - mParties[i % mParties.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(mTfLight); - mChart.setData(data); - - // undo all highlights - mChart.highlightValues(null); - - mChart.invalidate(); + @Override + public void saveToGallery() { + saveToGallery(chart, "PieChartActivity"); } private SpannableString generateCenterSpannableText() { @@ -282,14 +299,8 @@ public void onNothingSelected() { } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStartTrackingTouch(SeekBar seekBar) {} @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStopTrackingTouch(SeekBar seekBar) {} } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index c0199723d9..80ca82cde9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -1,9 +1,14 @@ 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 androidx.core.content.ContextCompat; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.text.style.RelativeSizeSpan; @@ -35,8 +40,8 @@ public class PiePolylineChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - private PieChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private PieChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; private Typeface tf; @@ -48,59 +53,61 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_piechart); + setTitle("PiePolylineChartActivity"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarY = findViewById(R.id.seekBar2); - - mSeekBarY.setProgress(10); + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); - mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarY.setOnSeekBarChangeListener(this); + seekBarX.setOnSeekBarChangeListener(this); + seekBarY.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); - mChart.setUsePercentValues(true); - mChart.getDescription().setEnabled(false); - mChart.setExtraOffsets(5, 10, 5, 5); + chart = findViewById(R.id.chart1); + chart.setUsePercentValues(true); + chart.getDescription().setEnabled(false); + chart.setExtraOffsets(5, 10, 5, 5); - mChart.setDragDecelerationFrictionCoef(0.95f); + chart.setDragDecelerationFrictionCoef(0.95f); tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - mChart.setCenterTextTypeface(Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf")); - mChart.setCenterText(generateCenterSpannableText()); + chart.setCenterTextTypeface(Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf")); + chart.setCenterText(generateCenterSpannableText()); - mChart.setExtraOffsets(20.f, 0.f, 20.f, 0.f); + chart.setExtraOffsets(20.f, 0.f, 20.f, 0.f); - mChart.setDrawHoleEnabled(true); - mChart.setHoleColor(Color.WHITE); + chart.setDrawHoleEnabled(true); + chart.setHoleColor(Color.WHITE); - mChart.setTransparentCircleColor(Color.WHITE); - mChart.setTransparentCircleAlpha(110); + chart.setTransparentCircleColor(Color.WHITE); + chart.setTransparentCircleAlpha(110); - mChart.setHoleRadius(58f); - mChart.setTransparentCircleRadius(61f); + chart.setHoleRadius(58f); + chart.setTransparentCircleRadius(61f); - mChart.setDrawCenterText(true); + chart.setDrawCenterText(true); - mChart.setRotationAngle(0); + chart.setRotationAngle(0); // enable rotation of the chart by touch - mChart.setRotationEnabled(true); - mChart.setHighlightPerTapEnabled(true); + chart.setRotationEnabled(true); + chart.setHighlightPerTapEnabled(true); - // mChart.setUnit(" €"); - // mChart.setDrawUnitsInChart(true); + // chart.setUnit(" €"); + // chart.setDrawUnitsInChart(true); // add a selection listener - mChart.setOnChartValueSelectedListener(this); + chart.setOnChartValueSelectedListener(this); + seekBarX.setProgress(4); + seekBarY.setProgress(100); setData(4, 100); - mChart.animateY(1400, Easing.EaseInOutQuad); - // mChart.spin(2000, 0, 360); + chart.animateY(1400, Easing.EaseInOutQuad); + // chart.spin(2000, 0, 360); - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); l.setOrientation(Legend.LegendOrientation.VERTICAL); @@ -108,89 +115,14 @@ protected void onCreate(Bundle savedInstanceState) { l.setEnabled(false); } - @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.actionToggleValues: { - for (IDataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHole: { - if (mChart.isDrawHoleEnabled()) - mChart.setDrawHoleEnabled(false); - else - mChart.setDrawHoleEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionDrawCenter: { - if (mChart.isDrawCenterTextEnabled()) - mChart.setDrawCenterText(false); - else - mChart.setDrawCenterText(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleXVals: { - - mChart.setDrawEntryLabels(!mChart.isDrawEntryLabelsEnabled()); - mChart.invalidate(); - break; - } - case R.id.actionSave: { - // mChart.saveToGallery("title"+System.currentTimeMillis()); - mChart.saveToPath("title" + System.currentTimeMillis(), ""); - break; - } - case R.id.actionTogglePercent: - mChart.setUsePercentValues(!mChart.isUsePercentValuesEnabled()); - mChart.invalidate(); - break; - case R.id.animateX: { - mChart.animateX(1400); - break; - } - case R.id.animateY: { - mChart.animateY(1400); - break; - } - case R.id.animateXY: { - mChart.animateXY(1400, 1400); - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress())); - tvY.setText("" + (mSeekBarY.getProgress())); - - setData(mSeekBarX.getProgress(), mSeekBarY.getProgress()); - } - private void setData(int count, float range) { - float mult = range; - - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); // 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) (Math.random() * mult) + mult / 5, mParties[i % mParties.length])); + entries.add(new PieEntry((float) (Math.random() * range) + range / 5, parties[i % parties.length])); } PieDataSet dataSet = new PieDataSet(entries, "Election Results"); @@ -199,7 +131,7 @@ private void setData(int count, float range) { // add a lot of colors - ArrayList colors = new ArrayList(); + ArrayList colors = new ArrayList<>(); for (int c : ColorTemplate.VORDIPLOM_COLORS) colors.add(c); @@ -235,12 +167,103 @@ private void setData(int count, float range) { data.setValueTextSize(11f); data.setValueTextColor(Color.BLACK); data.setValueTypeface(tf); - mChart.setData(data); + chart.setData(data); // undo all highlights - mChart.highlightValues(null); + chart.highlightValues(null); + + chart.invalidate(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.pie, menu); + return true; + } - mChart.invalidate(); + @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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + for (IDataSet set : chart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionToggleHole: { + if (chart.isDrawHoleEnabled()) + chart.setDrawHoleEnabled(false); + else + chart.setDrawHoleEnabled(true); + 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 + public void saveToGallery() { + saveToGallery(chart, "PiePolylineChartActivity"); } private SpannableString generateCenterSpannableText() { @@ -271,14 +294,8 @@ public void onNothingSelected() { } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStartTrackingTouch(SeekBar seekBar) {} @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStopTrackingTouch(SeekBar seekBar) {} } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java index d354886f6e..c2a5eb3d0a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java @@ -1,13 +1,16 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.view.Menu; import android.view.MenuItem; import android.view.WindowManager; -import android.widget.TextView; -import android.widget.Toast; import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.RadarChart; @@ -29,49 +32,46 @@ public class RadarChartActivity extends DemoBase { - private RadarChart mChart; + private RadarChart chart; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_radarchart_noseekbar); + setContentView(R.layout.activity_radarchart); - TextView tv = findViewById(R.id.textView); - tv.setTypeface(mTfLight); - tv.setTextColor(Color.WHITE); - tv.setBackgroundColor(Color.rgb(60, 65, 82)); + setTitle("RadarChartActivity"); - mChart = findViewById(R.id.chart1); - mChart.setBackgroundColor(Color.rgb(60, 65, 82)); + chart = findViewById(R.id.chart1); + chart.setBackgroundColor(Color.rgb(60, 65, 82)); - mChart.getDescription().setEnabled(false); + chart.getDescription().setEnabled(false); - mChart.setWebLineWidth(1f); - mChart.setWebColor(Color.LTGRAY); - mChart.setWebLineWidthInner(1f); - mChart.setWebColorInner(Color.LTGRAY); - mChart.setWebAlpha(100); + chart.setWebLineWidth(1f); + chart.setWebColor(Color.LTGRAY); + chart.setWebLineWidthInner(1f); + chart.setWebColorInner(Color.LTGRAY); + chart.setWebAlpha(100); // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it MarkerView mv = new RadarMarkerView(this, R.layout.radar_markerview); - mv.setChartView(mChart); // For bounds control - mChart.setMarker(mv); // Set the marker to the chart + mv.setChartView(chart); // For bounds control + chart.setMarker(mv); // Set the marker to the chart setData(); - mChart.animateXY(1400, 1400, Easing.EaseInOutQuad); + chart.animateXY(1400, 1400, Easing.EaseInOutQuad); - XAxis xAxis = mChart.getXAxis(); - xAxis.setTypeface(mTfLight); + XAxis xAxis = chart.getXAxis(); + xAxis.setTypeface(tfLight); xAxis.setTextSize(9f); xAxis.setYOffset(0f); xAxis.setXOffset(0f); xAxis.setValueFormatter(new IAxisValueFormatter() { - private String[] mActivities = new String[]{"Burger", "Steak", "Salad", "Pasta", "Pizza"}; + private final String[] mActivities = new String[]{"Burger", "Steak", "Salad", "Pasta", "Pizza"}; @Override public String getFormattedValue(float value, AxisBase axis) { @@ -80,25 +80,76 @@ public String getFormattedValue(float value, AxisBase axis) { }); xAxis.setTextColor(Color.WHITE); - YAxis yAxis = mChart.getYAxis(); - yAxis.setTypeface(mTfLight); + YAxis yAxis = chart.getYAxis(); + yAxis.setTypeface(tfLight); yAxis.setLabelCount(5, false); yAxis.setTextSize(9f); yAxis.setAxisMinimum(0f); yAxis.setAxisMaximum(80f); yAxis.setDrawLabels(false); - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); l.setOrientation(Legend.LegendOrientation.HORIZONTAL); l.setDrawInside(false); - l.setTypeface(mTfLight); + l.setTypeface(tfLight); l.setXEntrySpace(7f); l.setYEntrySpace(5f); l.setTextColor(Color.WHITE); } + private void setData() { + + float mul = 80; + float min = 20; + int cnt = 5; + + ArrayList entries1 = new ArrayList<>(); + ArrayList entries2 = new ArrayList<>(); + + // 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 < cnt; i++) { + float val1 = (float) (Math.random() * mul) + min; + entries1.add(new RadarEntry(val1)); + + float val2 = (float) (Math.random() * mul) + min; + entries2.add(new RadarEntry(val2)); + } + + RadarDataSet set1 = new RadarDataSet(entries1, "Last Week"); + set1.setColor(Color.rgb(103, 110, 129)); + set1.setFillColor(Color.rgb(103, 110, 129)); + set1.setDrawFilled(true); + set1.setFillAlpha(180); + set1.setLineWidth(2f); + set1.setDrawHighlightCircleEnabled(true); + set1.setDrawHighlightIndicators(false); + + RadarDataSet set2 = new RadarDataSet(entries2, "This Week"); + set2.setColor(Color.rgb(121, 162, 175)); + set2.setFillColor(Color.rgb(121, 162, 175)); + set2.setDrawFilled(true); + set2.setFillAlpha(180); + set2.setLineWidth(2f); + set2.setDrawHighlightCircleEnabled(true); + set2.setDrawHighlightIndicators(false); + + ArrayList sets = new ArrayList<>(); + sets.add(set1); + sets.add(set2); + + RadarData data = new RadarData(sets); + data.setValueTypeface(tfLight); + data.setValueTextSize(8f); + data.setDrawValues(false); + data.setValueTextColor(Color.WHITE); + + chart.setData(data); + chart.invalidate(); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.radar, menu); @@ -109,31 +160,37 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - for (IDataSet set : mChart.getData().getDataSets()) + for (IDataSet set : chart.getData().getDataSets()) set.setDrawValues(!set.isDrawValuesEnabled()); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionToggleRotate: { - if (mChart.isRotationEnabled()) - mChart.setRotationEnabled(false); + if (chart.isRotationEnabled()) + chart.setRotationEnabled(false); else - mChart.setRotationEnabled(true); - mChart.invalidate(); + chart.setRotationEnabled(true); + chart.invalidate(); break; } case R.id.actionToggleFilled: { - ArrayList sets = (ArrayList) mChart.getData() + ArrayList sets = (ArrayList) chart.getData() .getDataSets(); for (IRadarDataSet set : sets) { @@ -142,109 +199,62 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawFilled(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlightCircle: { - ArrayList sets = (ArrayList) mChart.getData() + ArrayList sets = (ArrayList) chart.getData() .getDataSets(); for (IRadarDataSet set : sets) { set.setDrawHighlightCircleEnabled(!set.isDrawHighlightCircleEnabled()); } - mChart.invalidate(); - break; - } - case R.id.actionSave: { - if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); + chart.invalidate(); break; } case R.id.actionToggleXLabels: { - mChart.getXAxis().setEnabled(!mChart.getXAxis().isEnabled()); - mChart.notifyDataSetChanged(); - mChart.invalidate(); + chart.getXAxis().setEnabled(!chart.getXAxis().isEnabled()); + chart.notifyDataSetChanged(); + chart.invalidate(); break; } case R.id.actionToggleYLabels: { - mChart.getYAxis().setEnabled(!mChart.getYAxis().isEnabled()); - mChart.invalidate(); + chart.getYAxis().setEnabled(!chart.getYAxis().isEnabled()); + chart.invalidate(); break; } case R.id.animateX: { - mChart.animateX(1400); + chart.animateX(1400); break; } case R.id.animateY: { - mChart.animateY(1400); + chart.animateY(1400); break; } case R.id.animateXY: { - mChart.animateXY(1400, 1400); + chart.animateXY(1400, 1400); break; } case R.id.actionToggleSpin: { - mChart.spin(2000, mChart.getRotationAngle(), mChart.getRotationAngle() + 360, Easing.EaseInCubic); + chart.spin(2000, 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; } - public void setData() { - - float mult = 80; - float min = 20; - int cnt = 5; - - ArrayList entries1 = new ArrayList(); - ArrayList entries2 = new ArrayList(); - - // 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 < cnt; i++) { - float val1 = (float) (Math.random() * mult) + min; - entries1.add(new RadarEntry(val1)); - - float val2 = (float) (Math.random() * mult) + min; - entries2.add(new RadarEntry(val2)); - } - - RadarDataSet set1 = new RadarDataSet(entries1, "Last Week"); - set1.setColor(Color.rgb(103, 110, 129)); - set1.setFillColor(Color.rgb(103, 110, 129)); - set1.setDrawFilled(true); - set1.setFillAlpha(180); - set1.setLineWidth(2f); - set1.setDrawHighlightCircleEnabled(true); - set1.setDrawHighlightIndicators(false); - - RadarDataSet set2 = new RadarDataSet(entries2, "This Week"); - set2.setColor(Color.rgb(121, 162, 175)); - set2.setFillColor(Color.rgb(121, 162, 175)); - set2.setDrawFilled(true); - set2.setFillAlpha(180); - set2.setLineWidth(2f); - set2.setDrawHighlightCircleEnabled(true); - set2.setDrawHighlightIndicators(false); - - ArrayList sets = new ArrayList(); - sets.add(set1); - sets.add(set2); - - RadarData data = new RadarData(sets); - data.setValueTypeface(mTfLight); - data.setValueTextSize(8f); - data.setDrawValues(false); - data.setValueTextColor(Color.WHITE); - - mChart.setData(data); - mChart.invalidate(); + @Override + public void saveToGallery() { + saveToGallery(chart, "RadarChartActivity"); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index f3661628d0..606d750efe 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -1,8 +1,13 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -27,7 +32,7 @@ public class RealtimeLineChartActivity extends DemoBase implements OnChartValueSelectedListener { - private LineChart mChart; + private LineChart chart; @Override protected void onCreate(Bundle savedInstanceState) { @@ -36,89 +41,64 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_realtime_linechart); - mChart = findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); + setTitle("RealtimeLineChartActivity"); + + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); // enable description text - mChart.getDescription().setEnabled(true); + chart.getDescription().setEnabled(true); // enable touch gestures - mChart.setTouchEnabled(true); + chart.setTouchEnabled(true); // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - mChart.setDrawGridBackground(false); + chart.setDragEnabled(true); + chart.setScaleEnabled(true); + chart.setDrawGridBackground(false); // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(true); + chart.setPinchZoom(true); // set an alternative background color - mChart.setBackgroundColor(Color.LTGRAY); + chart.setBackgroundColor(Color.LTGRAY); LineData data = new LineData(); data.setValueTextColor(Color.WHITE); // add empty data - mChart.setData(data); + chart.setData(data); // get the legend (only possible after setting data) - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); // modify the legend ... l.setForm(LegendForm.LINE); - l.setTypeface(mTfLight); + l.setTypeface(tfLight); l.setTextColor(Color.WHITE); - XAxis xl = mChart.getXAxis(); - xl.setTypeface(mTfLight); + XAxis xl = chart.getXAxis(); + xl.setTypeface(tfLight); xl.setTextColor(Color.WHITE); xl.setDrawGridLines(false); xl.setAvoidFirstLastClipping(true); xl.setEnabled(true); - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(mTfLight); + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setTypeface(tfLight); leftAxis.setTextColor(Color.WHITE); leftAxis.setAxisMaximum(100f); leftAxis.setAxisMinimum(0f); leftAxis.setDrawGridLines(true); - YAxis rightAxis = mChart.getAxisRight(); + YAxis rightAxis = chart.getAxisRight(); rightAxis.setEnabled(false); } - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.realtime, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionAdd: { - addEntry(); - break; - } - case R.id.actionClear: { - mChart.clearValues(); - Toast.makeText(this, "Chart cleared!", Toast.LENGTH_SHORT).show(); - break; - } - case R.id.actionFeedMultiple: { - feedMultiple(); - break; - } - } - return true; - } - private void addEntry() { - LineData data = mChart.getData(); + LineData data = chart.getData(); if (data != null) { @@ -134,17 +114,17 @@ private void addEntry() { data.notifyDataChanged(); // let the chart know it's data has changed - mChart.notifyDataSetChanged(); + chart.notifyDataSetChanged(); // limit the number of visible entries - mChart.setVisibleXRangeMaximum(120); - // mChart.setVisibleYRange(30, AxisDependency.LEFT); + chart.setVisibleXRangeMaximum(120); + // chart.setVisibleYRange(30, AxisDependency.LEFT); // move to the latest entry - mChart.moveViewToX(data.getEntryCount()); + chart.moveViewToX(data.getEntryCount()); // this automatically refreshes the chart (calls invalidate()) - // mChart.moveViewTo(data.getXValCount()-7, 55f, + // chart.moveViewTo(data.getXValCount()-7, 55f, // AxisDependency.LEFT); } } @@ -193,7 +173,6 @@ public void run() { try { Thread.sleep(25); } catch (InterruptedException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } @@ -203,6 +182,52 @@ public void run() { thread.start(); } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.realtime, 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionAdd: { + addEntry(); + break; + } + case R.id.actionClear: { + chart.clearValues(); + Toast.makeText(this, "Chart cleared!", Toast.LENGTH_SHORT).show(); + break; + } + case R.id.actionFeedMultiple: { + feedMultiple(); + 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 saveToGallery() { + saveToGallery(chart, "RealtimeLineChartActivity"); + } + @Override public void onValueSelected(Entry e, Highlight h) { Log.i("Entry selected", e.toString()); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index f0f889e194..9b441793f0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -1,7 +1,12 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -12,7 +17,6 @@ import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.Entry; @@ -31,10 +35,10 @@ public class ScatterChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - private ScatterChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private ScatterChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; - + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -42,52 +46,109 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_scatterchart); + setTitle("ScatterChartActivity"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); - mSeekBarY = findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); - mChart.getDescription().setEnabled(false); - mChart.setOnChartValueSelectedListener(this); + chart = findViewById(R.id.chart1); + chart.getDescription().setEnabled(false); + chart.setOnChartValueSelectedListener(this); - mChart.setDrawGridBackground(false); - mChart.setTouchEnabled(true); - mChart.setMaxHighlightDistance(50f); + chart.setDrawGridBackground(false); + chart.setTouchEnabled(true); + chart.setMaxHighlightDistance(50f); // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); + chart.setDragEnabled(true); + chart.setScaleEnabled(true); - mChart.setMaxVisibleValueCount(200); - mChart.setPinchZoom(true); + chart.setMaxVisibleValueCount(200); + chart.setPinchZoom(true); - mSeekBarX.setProgress(45); - mSeekBarY.setProgress(100); + seekBarX.setProgress(45); + seekBarY.setProgress(100); - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); l.setOrientation(Legend.LegendOrientation.VERTICAL); l.setDrawInside(false); - l.setTypeface(mTfLight); + l.setTypeface(tfLight); l.setXOffset(5f); - YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(mTfLight); + YAxis yl = chart.getAxisLeft(); + yl.setTypeface(tfLight); yl.setAxisMinimum(0f); // this replaces setStartAtZero(true) - - mChart.getAxisRight().setEnabled(false); - XAxis xl = mChart.getXAxis(); - xl.setTypeface(mTfLight); + chart.getAxisRight().setEnabled(false); + + XAxis xl = chart.getXAxis(); + xl.setTypeface(tfLight); xl.setDrawGridLines(false); } + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + ArrayList values1 = new ArrayList<>(); + ArrayList values2 = new ArrayList<>(); + ArrayList values3 = new ArrayList<>(); + + for (int i = 0; i < seekBarX.getProgress(); i++) { + float val = (float) (Math.random() * seekBarY.getProgress()) + 3; + values1.add(new Entry(i, val)); + } + + for (int i = 0; i < seekBarX.getProgress(); i++) { + float val = (float) (Math.random() * seekBarY.getProgress()) + 3; + values2.add(new Entry(i+0.33f, val)); + } + + for (int i = 0; i < seekBarX.getProgress(); i++) { + float val = (float) (Math.random() * seekBarY.getProgress()) + 3; + values3.add(new Entry(i+0.66f, val)); + } + + // create a dataset and give it a type + ScatterDataSet set1 = new ScatterDataSet(values1, "DS 1"); + set1.setScatterShape(ScatterChart.ScatterShape.SQUARE); + set1.setColor(ColorTemplate.COLORFUL_COLORS[0]); + ScatterDataSet set2 = new ScatterDataSet(values2, "DS 2"); + set2.setScatterShape(ScatterChart.ScatterShape.CIRCLE); + set2.setScatterShapeHoleColor(ColorTemplate.COLORFUL_COLORS[3]); + set2.setScatterShapeHoleRadius(3f); + set2.setColor(ColorTemplate.COLORFUL_COLORS[1]); + ScatterDataSet set3 = new ScatterDataSet(values3, "DS 3"); + set3.setShapeRenderer(new CustomScatterShapeRenderer()); + set3.setColor(ColorTemplate.COLORFUL_COLORS[2]); + + set1.setScatterShapeSize(8f); + set2.setScatterShapeSize(8f); + set3.setScatterShapeSize(8f); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); // add the data sets + dataSets.add(set2); + dataSets.add(set3); + + // create a data object with the data sets + ScatterData data = new ScatterData(dataSets); + data.setValueTypeface(tfLight); + + chart.setData(data); + chart.invalidate(); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.scatter, menu); @@ -98,8 +159,14 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (IScatterDataSet iSet : sets) { @@ -108,46 +175,49 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawValues(!set.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.actionSave: { - // mChart.saveToGallery("title"+System.currentTimeMillis()); - mChart.saveToPath("title" + System.currentTimeMillis(), ""); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(3000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(3000); break; } case R.id.animateXY: { - mChart.animateXY(3000, 3000); + chart.animateXY(3000, 3000); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -155,58 +225,8 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); - - ArrayList yVals1 = new ArrayList(); - ArrayList yVals2 = new ArrayList(); - ArrayList yVals3 = new ArrayList(); - - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - float val = (float) (Math.random() * mSeekBarY.getProgress()) + 3; - yVals1.add(new Entry(i, val)); - } - - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - float val = (float) (Math.random() * mSeekBarY.getProgress()) + 3; - yVals2.add(new Entry(i+0.33f, val)); - } - - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - float val = (float) (Math.random() * mSeekBarY.getProgress()) + 3; - yVals3.add(new Entry(i+0.66f, val)); - } - - // create a dataset and give it a type - ScatterDataSet set1 = new ScatterDataSet(yVals1, "DS 1"); - set1.setScatterShape(ScatterChart.ScatterShape.SQUARE); - set1.setColor(ColorTemplate.COLORFUL_COLORS[0]); - ScatterDataSet set2 = new ScatterDataSet(yVals2, "DS 2"); - set2.setScatterShape(ScatterChart.ScatterShape.CIRCLE); - set2.setScatterShapeHoleColor(ColorTemplate.COLORFUL_COLORS[3]); - set2.setScatterShapeHoleRadius(3f); - set2.setColor(ColorTemplate.COLORFUL_COLORS[1]); - ScatterDataSet set3 = new ScatterDataSet(yVals3, "DS 3"); - set3.setShapeRenderer(new CustomScatterShapeRenderer()); - set3.setColor(ColorTemplate.COLORFUL_COLORS[2]); - - set1.setScatterShapeSize(8f); - set2.setScatterShapeSize(8f); - set3.setScatterShapeSize(8f); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets - dataSets.add(set2); - dataSets.add(set3); - - // create a data object with the datasets - ScatterData data = new ScatterData(dataSets); - data.setValueTypeface(mTfLight); - - mChart.setData(data); - mChart.invalidate(); + public void saveToGallery() { + saveToGallery(chart, "ScatterChartActivity"); } @Override @@ -217,20 +237,11 @@ public void onValueSelected(Entry e, Highlight h) { } @Override - public void onNothingSelected() { - // TODO Auto-generated method stub - - } + public void onNothingSelected() {} @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStartTrackingTouch(SeekBar seekBar) {} @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStopTrackingTouch(SeekBar seekBar) {} } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java index 1aeb7f0f0c..37de64d728 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java @@ -1,7 +1,11 @@ package com.xxmassdeveloper.mpchartexample; +import android.content.Intent; +import android.net.Uri; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; import android.view.WindowManager; import com.github.mikephil.charting.charts.BarChart; @@ -15,9 +19,10 @@ import java.util.ArrayList; +@SuppressWarnings("SameParameterValue") public class ScrollViewActivity extends DemoBase { - private BarChart mChart; + private BarChart chart; @Override protected void onCreate(Bundle savedInstanceState) { @@ -26,45 +31,71 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_scrollview); - mChart = findViewById(R.id.chart1); + setTitle("ScrollViewActivity"); - mChart.getDescription().setEnabled(false); + chart = findViewById(R.id.chart1); + + chart.getDescription().setEnabled(false); // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); + chart.setPinchZoom(false); - mChart.setDrawBarShadow(false); - mChart.setDrawGridBackground(false); + chart.setDrawBarShadow(false); + chart.setDrawGridBackground(false); - XAxis xAxis = mChart.getXAxis(); + XAxis xAxis = chart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); xAxis.setDrawGridLines(false); - mChart.getAxisLeft().setDrawGridLines(false); - - mChart.getLegend().setEnabled(false); + chart.getAxisLeft().setDrawGridLines(false); + + chart.getLegend().setEnabled(false); setData(10); - mChart.setFitBars(true); + chart.setFitBars(true); } - + private void setData(int count) { - - ArrayList yVals = new ArrayList(); + + ArrayList values = new ArrayList<>(); for (int i = 0; i < count; i++) { float val = (float) (Math.random() * count) + 15; - yVals.add(new BarEntry(i, (int) val)); + values.add(new BarEntry(i, (int) val)); } - BarDataSet set = new BarDataSet(yVals, "Data Set"); + BarDataSet set = new BarDataSet(values, "Data Set"); set.setColors(ColorTemplate.VORDIPLOM_COLORS); set.setDrawValues(false); BarData data = new BarData(set); - mChart.setData(data); - mChart.invalidate(); - mChart.animateY(800); + chart.setData(data); + chart.invalidate(); + chart.animateY(800); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java")); + startActivity(i); + break; + } + } + + return true; } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 9951060177..c98abb68aa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -1,7 +1,12 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -9,11 +14,9 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; -import android.widget.Toast; import com.github.mikephil.charting.charts.BarChart; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.components.YAxis; @@ -34,8 +37,8 @@ public class StackedBarActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - private BarChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private BarChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -44,50 +47,52 @@ protected void onCreate(Bundle savedInstanceState) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_barchart); + setTitle("StackedBarActivity"); + tvX = findViewById(R.id.tvXMax); tvY = findViewById(R.id.tvYMax); - mSeekBarX = findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); - mSeekBarY = findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); - mChart = findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); - mChart.getDescription().setEnabled(false); + chart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn - mChart.setMaxVisibleValueCount(40); + chart.setMaxVisibleValueCount(40); // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); + chart.setPinchZoom(false); - mChart.setDrawGridBackground(false); - mChart.setDrawBarShadow(false); + chart.setDrawGridBackground(false); + chart.setDrawBarShadow(false); - mChart.setDrawValueAboveBar(false); - mChart.setHighlightFullBarEnabled(false); + chart.setDrawValueAboveBar(false); + chart.setHighlightFullBarEnabled(false); // change the position of the y-labels - YAxis leftAxis = mChart.getAxisLeft(); + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setValueFormatter(new MyAxisValueFormatter()); leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) - mChart.getAxisRight().setEnabled(false); + chart.getAxisRight().setEnabled(false); - XAxis xLabels = mChart.getXAxis(); + XAxis xLabels = chart.getXAxis(); xLabels.setPosition(XAxisPosition.TOP); - // mChart.setDrawXLabels(false); - // mChart.setDrawYLabels(false); + // chart.setDrawXLabels(false); + // chart.setDrawYLabels(false); // setting data - mSeekBarX.setProgress(12); - mSeekBarY.setProgress(100); + seekBarX.setProgress(12); + seekBarY.setProgress(100); - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); l.setOrientation(Legend.LegendOrientation.HORIZONTAL); @@ -96,7 +101,55 @@ protected void onCreate(Bundle savedInstanceState) { l.setFormToTextSpace(4f); l.setXEntrySpace(6f); - // mChart.setDrawLegend(false); + // chart.setDrawLegend(false); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < seekBarX.getProgress(); i++) { + float mul = (seekBarY.getProgress() + 1); + float val1 = (float) (Math.random() * mul) + mul / 3; + float val2 = (float) (Math.random() * mul) + mul / 3; + float val3 = (float) (Math.random() * mul) + mul / 3; + + values.add(new BarEntry( + i, + new float[]{val1, val2, val3}, + getResources().getDrawable(R.drawable.star))); + } + + BarDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(values, "Statistics Vienna 2014"); + set1.setDrawIcons(false); + set1.setColors(getColors()); + set1.setStackLabels(new String[]{"Births", "Divorces", "Marriages"}); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); + + BarData data = new BarData(dataSets); + data.setValueFormatter(new MyValueFormatter()); + data.setValueTextColor(Color.WHITE); + + chart.setData(data); + } + + chart.setFitBars(true); + chart.invalidate(); } @Override @@ -109,8 +162,14 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (IBarDataSet iSet : sets) { @@ -119,11 +178,11 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawValues(!set.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleIcons: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (IBarDataSet iSet : sets) { @@ -132,55 +191,56 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawIcons(!set.isDrawIconsEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionToggleBarBorders: { - for (IBarDataSet set : mChart.getData().getDataSets()) + for (IBarDataSet set : chart.getData().getDataSets()) ((BarDataSet) set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT).show(); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -188,64 +248,15 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); - - ArrayList yVals1 = new ArrayList(); - - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - float mult = (mSeekBarY.getProgress() + 1); - float val1 = (float) (Math.random() * mult) + mult / 3; - float val2 = (float) (Math.random() * mult) + mult / 3; - float val3 = (float) (Math.random() * mult) + mult / 3; - - yVals1.add(new BarEntry( - i, - new float[]{val1, val2, val3}, - getResources().getDrawable(R.drawable.star))); - } - - BarDataSet set1; - - if (mChart.getData() != null && - mChart.getData().getDataSetCount() > 0) { - set1 = (BarDataSet) mChart.getData().getDataSetByIndex(0); - set1.setValues(yVals1); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); - } else { - set1 = new BarDataSet(yVals1, "Statistics Vienna 2014"); - set1.setDrawIcons(false); - set1.setColors(getColors()); - set1.setStackLabels(new String[]{"Births", "Divorces", "Marriages"}); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - - BarData data = new BarData(dataSets); - data.setValueFormatter(new MyValueFormatter()); - data.setValueTextColor(Color.WHITE); - - mChart.setData(data); - } - - mChart.setFitBars(true); - mChart.invalidate(); + public void saveToGallery() { + saveToGallery(chart, "StackedBarActivity"); } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStartTrackingTouch(SeekBar seekBar) {} @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onStopTrackingTouch(SeekBar seekBar) {} @Override public void onValueSelected(Entry e, Highlight h) { @@ -259,21 +270,14 @@ public void onValueSelected(Entry e, Highlight h) { } @Override - public void onNothingSelected() { - // TODO Auto-generated method stub - - } + public void onNothingSelected() {} private int[] getColors() { - int stacksize = 3; - // have as many colors as stack-values per entry - int[] colors = new int[stacksize]; + int[] colors = new int[3]; - for (int i = 0; i < colors.length; i++) { - colors[i] = ColorTemplate.MATERIAL_COLORS[i]; - } + System.arraycopy(ColorTemplate.MATERIAL_COLORS, 0, colors, 0, 3); return colors; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index c1d64a106b..b9df66981a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -1,18 +1,21 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.WindowManager; -import android.widget.Toast; import com.github.mikephil.charting.charts.HorizontalBarChart; import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.components.YAxis; @@ -35,7 +38,7 @@ public class StackedBarActivityNegative extends DemoBase implements OnChartValueSelectedListener { - private HorizontalBarChart mChart; + private HorizontalBarChart chart; @Override protected void onCreate(Bundle savedInstanceState) { @@ -44,30 +47,30 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_age_distribution); - setTitle("Age Distribution Austria"); + setTitle("StackedBarActivityNegative"); - mChart = findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - mChart.setDrawGridBackground(false); - mChart.getDescription().setEnabled(false); + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + chart.setDrawGridBackground(false); + chart.getDescription().setEnabled(false); // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.setDrawBarShadow(false); - mChart.setDrawValueAboveBar(true); - mChart.setHighlightFullBarEnabled(false); - - mChart.getAxisLeft().setEnabled(false); - mChart.getAxisRight().setAxisMaximum(25f); - mChart.getAxisRight().setAxisMinimum(-25f); - mChart.getAxisRight().setDrawGridLines(false); - mChart.getAxisRight().setDrawZeroLine(true); - mChart.getAxisRight().setLabelCount(7, false); - mChart.getAxisRight().setValueFormatter(new CustomFormatter()); - mChart.getAxisRight().setTextSize(9f); - - XAxis xAxis = mChart.getXAxis(); + chart.setPinchZoom(false); + + chart.setDrawBarShadow(false); + chart.setDrawValueAboveBar(true); + chart.setHighlightFullBarEnabled(false); + + chart.getAxisLeft().setEnabled(false); + chart.getAxisRight().setAxisMaximum(25f); + chart.getAxisRight().setAxisMinimum(-25f); + chart.getAxisRight().setDrawGridLines(false); + chart.getAxisRight().setDrawZeroLine(true); + chart.getAxisRight().setLabelCount(7, false); + chart.getAxisRight().setValueFormatter(new CustomFormatter()); + chart.getAxisRight().setTextSize(9f); + + XAxis xAxis = chart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTH_SIDED); xAxis.setDrawGridLines(false); xAxis.setDrawAxisLine(false); @@ -87,7 +90,7 @@ public String getFormattedValue(float value, AxisBase axis) { } }); - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); l.setOrientation(Legend.LegendOrientation.HORIZONTAL); @@ -97,36 +100,34 @@ public String getFormattedValue(float value, AxisBase axis) { l.setXEntrySpace(6f); // IMPORTANT: When using negative values in stacked bars, always make sure the negative values are in the array first - ArrayList yValues = new ArrayList(); - yValues.add(new BarEntry(5, new float[]{ -10, 10 })); - yValues.add(new BarEntry(15, new float[]{ -12, 13 })); - yValues.add(new BarEntry(25, new float[]{ -15, 15 })); - yValues.add(new BarEntry(35, new float[]{ -17, 17 })); - yValues.add(new BarEntry(45, new float[]{ -19, 20 })); - yValues.add(new BarEntry(45, new float[]{ -19, 20 }, getResources().getDrawable(R.drawable.star))); - yValues.add(new BarEntry(55, new float[]{ -19, 19 })); - yValues.add(new BarEntry(65, new float[]{ -16, 16 })); - yValues.add(new BarEntry(75, new float[]{ -13, 14 })); - yValues.add(new BarEntry(85, new float[]{ -10, 11 })); - yValues.add(new BarEntry(95, new float[]{ -5, 6 })); - yValues.add(new BarEntry(105, new float[]{ -1, 2 })); - - BarDataSet set = new BarDataSet(yValues, "Age Distribution"); + ArrayList values = new ArrayList<>(); + values.add(new BarEntry(5, new float[]{ -10, 10 })); + values.add(new BarEntry(15, new float[]{ -12, 13 })); + values.add(new BarEntry(25, new float[]{ -15, 15 })); + values.add(new BarEntry(35, new float[]{ -17, 17 })); + values.add(new BarEntry(45, new float[]{ -19, 20 })); + values.add(new BarEntry(45, new float[]{ -19, 20 }, getResources().getDrawable(R.drawable.star))); + values.add(new BarEntry(55, new float[]{ -19, 19 })); + values.add(new BarEntry(65, new float[]{ -16, 16 })); + values.add(new BarEntry(75, new float[]{ -13, 14 })); + values.add(new BarEntry(85, new float[]{ -10, 11 })); + values.add(new BarEntry(95, new float[]{ -5, 6 })); + values.add(new BarEntry(105, new float[]{ -1, 2 })); + + BarDataSet set = new BarDataSet(values, "Age Distribution"); set.setDrawIcons(false); set.setValueFormatter(new CustomFormatter()); set.setValueTextSize(7f); set.setAxisDependency(YAxis.AxisDependency.RIGHT); - set.setColors(new int[] {Color.rgb(67,67,72), Color.rgb(124,181,236)}); + set.setColors(Color.rgb(67,67,72), Color.rgb(124,181,236)); set.setStackLabels(new String[]{ "Men", "Women" }); - String []xLabels = new String[]{"0-10", "10-20", "20-30", "30-40", "40-50", "50-60", "60-70", "70-80", "80-90", "90-100", "100+"}; - BarData data = new BarData(set); data.setBarWidth(8.5f); - mChart.setData(data); - mChart.invalidate(); + chart.setData(data); + chart.invalidate(); } @Override @@ -139,8 +140,14 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (IBarDataSet iSet : sets) { @@ -149,11 +156,11 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawValues(!set.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleIcons: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (IBarDataSet iSet : sets) { @@ -162,57 +169,56 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawIcons(!set.isDrawIconsEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionToggleBarBorders: { - for (IBarDataSet set : mChart.getData().getDataSets()) + for (IBarDataSet set : chart.getData().getDataSets()) ((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(3000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(3000); break; } case R.id.animateXY: { - mChart.animateXY(3000, 3000); + chart.animateXY(3000, 3000); break; } case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -220,8 +226,12 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void onValueSelected(Entry e, Highlight h) { + public void saveToGallery() { + saveToGallery(chart, "StackedBarActivityNegative"); + } + @Override + public void onValueSelected(Entry e, Highlight h) { BarEntry entry = (BarEntry) e; Log.i("VAL SELECTED", "Value: " + Math.abs(entry.getYVals()[h.getStackIndex()])); @@ -229,16 +239,14 @@ public void onValueSelected(Entry e, Highlight h) { @Override public void onNothingSelected() { - // TODO Auto-generated method stub Log.i("NOTING SELECTED", ""); } - private class CustomFormatter implements IValueFormatter, IAxisValueFormatter - { + private class CustomFormatter implements IValueFormatter, IAxisValueFormatter { private DecimalFormat mFormat; - public CustomFormatter() { + CustomFormatter() { mFormat = new DecimalFormat("###"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java index b8bc1a41c2..a27cff89c9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -10,7 +10,7 @@ public class DayAxisValueFormatter implements IAxisValueFormatter { - protected String[] mMonths = new String[]{ + private final String[] mMonths = new String[]{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index c66e5d4569..e9662aca78 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -8,7 +8,10 @@ /** * Created by Philipp Jahoda on 14/09/15. + * + * @deprecated The {@link MyAxisValueFormatter} does exactly the same thing and is more functional. */ +@Deprecated public class MyCustomXAxisValueFormatter implements IAxisValueFormatter { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java index e4b94c331f..2ca87b2f0f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java @@ -7,18 +7,19 @@ /** * Created by Philipp Jahoda on 12/09/15. */ +@SuppressWarnings("unused") public class MyFillFormatter implements IFillFormatter { - private float mFillPos = 0f; + private float fillPos; - public MyFillFormatter(float fillpos) { - this.mFillPos = fillpos; + public MyFillFormatter(float fillPos) { + this.fillPos = fillPos; } @Override public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { // your logic could be here - return mFillPos; + return fillPos; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java index ef20bda3b7..25d3195cb5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java @@ -1,6 +1,7 @@ package com.xxmassdeveloper.mpchartexample.custom; +import android.annotation.SuppressLint; import android.content.Context; import android.widget.TextView; @@ -14,9 +15,10 @@ /** * Custom implementation of the MarkerView. - * + * * @author Philipp Jahoda */ +@SuppressLint("ViewConstructor") public class MyMarkerView extends MarkerView { private TextView tvContent; @@ -27,7 +29,7 @@ public MyMarkerView(Context context, int layoutResource) { tvContent = findViewById(R.id.tvContent); } - // callbacks everytime the MarkerView is redrawn, can be used to update the + // runs every time the MarkerView is redrawn, can be used to update the // content (user-interface) @Override public void refreshContent(Entry e, Highlight highlight) { @@ -36,10 +38,10 @@ public void refreshContent(Entry e, Highlight highlight) { CandleEntry ce = (CandleEntry) e; - tvContent.setText("" + Utils.formatNumber(ce.getHigh(), 0, true)); + tvContent.setText(Utils.formatNumber(ce.getHigh(), 0, true)); } else { - tvContent.setText("" + Utils.formatNumber(e.getY(), 0, true)); + tvContent.setText(Utils.formatNumber(e.getY(), 0, true)); } super.refreshContent(e, highlight); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java index 12b473f7d0..6fb2161577 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java @@ -1,25 +1,25 @@ package com.xxmassdeveloper.mpchartexample.custom; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Typeface; import android.widget.TextView; import com.github.mikephil.charting.components.MarkerView; -import com.github.mikephil.charting.data.CandleEntry; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.utils.MPPointF; -import com.github.mikephil.charting.utils.Utils; import com.xxmassdeveloper.mpchartexample.R; import java.text.DecimalFormat; /** * Custom implementation of the MarkerView. - * + * * @author Philipp Jahoda */ +@SuppressLint("ViewConstructor") public class RadarMarkerView extends MarkerView { private TextView tvContent; @@ -32,11 +32,11 @@ public RadarMarkerView(Context context, int layoutResource) { tvContent.setTypeface(Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf")); } - // callbacks everytime the MarkerView is redrawn, can be used to update the + // runs every time the MarkerView is redrawn, can be used to update the // content (user-interface) @Override public void refreshContent(Entry e, Highlight highlight) { - tvContent.setText(format.format(e.getY()) + " %"); + tvContent.setText(String.format("%s %%", format.format(e.getY()))); super.refreshContent(e, highlight); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java index 7becc88c90..7a8584f8ef 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java @@ -8,6 +8,7 @@ * Demo class that encapsulates data stored in realm.io database. * This class represents data suitable for all chart-types. */ +@SuppressWarnings("unused") public class RealmDemoData extends RealmObject { private float yValue; @@ -26,15 +27,7 @@ public class RealmDemoData extends RealmObject { */ private String label; - // ofc there could me more fields here... - - public RealmDemoData() { - - } - - public RealmDemoData(float yValue) { - this.yValue = yValue; - } + public RealmDemoData() {} public RealmDemoData(float xValue, float yValue) { this.xValue = xValue; @@ -44,12 +37,12 @@ public RealmDemoData(float xValue, float yValue) { /** * Constructor for stacked bars. * - * @param xValue - * @param stackValues + * @param xValue x position for bars + * @param stackValues values of bars in the stack */ public RealmDemoData(float xValue, float[] stackValues) { this.xValue = xValue; - this.stackValues = new RealmList(); + this.stackValues = new RealmList<>(); for (float val : stackValues) { this.stackValues.add(new RealmFloat(val)); @@ -59,11 +52,11 @@ public RealmDemoData(float xValue, float[] stackValues) { /** * Constructor for candles. * - * @param xValue - * @param high - * @param low - * @param open - * @param close + * @param xValue x position of candle + * @param high high value for candle + * @param low low value for candle + * @param open open value for candle + * @param close close value for candle */ public RealmDemoData(float xValue, float high, float low, float open, float close) { this.yValue = (high + low) / 2f; @@ -77,9 +70,9 @@ public RealmDemoData(float xValue, float high, float low, float open, float clos /** * Constructor for bubbles. * - * @param xValue - * @param yValue - * @param bubbleSize + * @param xValue x position of bubble + * @param yValue y position of bubble + * @param bubbleSize size of bubble */ public RealmDemoData(float xValue, float yValue, float bubbleSize) { this.xValue = xValue; @@ -90,27 +83,27 @@ public RealmDemoData(float xValue, float yValue, float bubbleSize) { /** * Constructor for pie chart. * - * @param yValue - * @param label + * @param yValue size of pie slice + * @param label label for pie slice */ public RealmDemoData(float yValue, String label) { this.yValue = yValue; this.label = label; } - public float getyValue() { + public float getYValue() { return yValue; } - public void setyValue(float yValue) { + public void setYValue(float yValue) { this.yValue = yValue; } - public float getxValue() { + public float getXValue() { return xValue; } - public void setxValue(float xValue) { + public void setXValue(float xValue) { this.xValue = xValue; } @@ -177,4 +170,4 @@ public String getLabel() { public void setLabel(String label) { this.label = label; } -} \ No newline at end of file +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java index 15b027b3ff..a8310dc9b5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java @@ -5,6 +5,7 @@ /** * Created by Philipp Jahoda on 09/11/15. */ +@SuppressWarnings("unused") public class RealmFloat extends RealmObject { private float floatValue; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java index 487705bb7d..554ef2fe27 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java @@ -1,6 +1,7 @@ package com.xxmassdeveloper.mpchartexample.custom; +import android.annotation.SuppressLint; import android.content.Context; import android.widget.TextView; @@ -14,9 +15,11 @@ /** * Custom implementation of the MarkerView. - * + * * @author Philipp Jahoda */ +@SuppressWarnings("unused") +@SuppressLint("ViewConstructor") public class StackedBarsMarkerView extends MarkerView { private TextView tvContent; @@ -27,7 +30,7 @@ public StackedBarsMarkerView(Context context, int layoutResource) { tvContent = findViewById(R.id.tvContent); } - // callbacks everytime the MarkerView is redrawn, can be used to update the + // runs every time the MarkerView is redrawn, can be used to update the // content (user-interface) @Override public void refreshContent(Entry e, Highlight highlight) { @@ -39,13 +42,13 @@ public void refreshContent(Entry e, Highlight highlight) { if(be.getYVals() != null) { // draw the stack value - tvContent.setText("" + Utils.formatNumber(be.getYVals()[highlight.getStackIndex()], 0, true)); + tvContent.setText(Utils.formatNumber(be.getYVals()[highlight.getStackIndex()], 0, true)); } else { - tvContent.setText("" + Utils.formatNumber(be.getY(), 0, true)); + tvContent.setText(Utils.formatNumber(be.getY(), 0, true)); } } else { - tvContent.setText("" + Utils.formatNumber(e.getY(), 0, true)); + tvContent.setText(Utils.formatNumber(e.getY(), 0, true)); } super.refreshContent(e, highlight); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java index 0475bdd038..f85f538988 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java @@ -1,6 +1,7 @@ package com.xxmassdeveloper.mpchartexample.custom; +import android.annotation.SuppressLint; import android.content.Context; import android.widget.TextView; @@ -18,6 +19,7 @@ * * @author Philipp Jahoda */ +@SuppressLint("ViewConstructor") public class XYMarkerView extends MarkerView { private TextView tvContent; @@ -33,12 +35,12 @@ public XYMarkerView(Context context, IAxisValueFormatter xAxisValueFormatter) { format = new DecimalFormat("###.0"); } - // callbacks everytime the MarkerView is redrawn, can be used to update the + // runs every time the MarkerView is redrawn, can be used to update the // content (user-interface) @Override public void refreshContent(Entry e, Highlight highlight) { - tvContent.setText("x: " + xAxisValueFormatter.getFormattedValue(e.getX(), null) + ", y: " + format.format(e.getY())); + tvContent.setText(String.format("x: %s, y: %s", xAxisValueFormatter.getFormattedValue(e.getX(), null), format.format(e.getY()))); super.refreshContent(e, highlight); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java index 34d76c25eb..7122e0d80c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java @@ -6,16 +6,16 @@ /** * Created by Philipp Jahoda on 14/09/15. */ +@SuppressWarnings("unused") public class YearXAxisFormatter implements IAxisValueFormatter { - protected String[] mMonths = new String[]{ + private final String[] mMonths = new String[]{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" }; public YearXAxisFormatter() { - // maybe do something here or provide parameters in constructor - + // take parameters to change behavior of formatter } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java index 655fc6bb25..4bcc543722 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java @@ -1,7 +1,8 @@ package com.xxmassdeveloper.mpchartexample.fragments; import android.graphics.Typeface; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -21,48 +22,49 @@ public class BarChartFrag extends SimpleFragment implements OnChartGestureListener { + @NonNull public static Fragment newInstance() { return new BarChartFrag(); } - private BarChart mChart; - + private BarChart chart; + @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag_simple_bar, container, false); - + // create a new chart object - mChart = new BarChart(getActivity()); - mChart.getDescription().setEnabled(false); - mChart.setOnChartGestureListener(this); - + chart = new BarChart(getActivity()); + chart.getDescription().setEnabled(false); + chart.setOnChartGestureListener(this); + MyMarkerView mv = new MyMarkerView(getActivity(), R.layout.custom_marker_view); - mv.setChartView(mChart); // For bounds control - mChart.setMarker(mv); - - mChart.setDrawGridBackground(false); - mChart.setDrawBarShadow(false); - - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); - - mChart.setData(generateBarData(1, 20000, 12)); - - Legend l = mChart.getLegend(); + mv.setChartView(chart); // For bounds control + chart.setMarker(mv); + + chart.setDrawGridBackground(false); + chart.setDrawBarShadow(false); + + Typeface tf = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); + + chart.setData(generateBarData(1, 20000, 12)); + + Legend l = chart.getLegend(); l.setTypeface(tf); - - YAxis leftAxis = mChart.getAxisLeft(); + + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setTypeface(tf); leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) - mChart.getAxisRight().setEnabled(false); - - XAxis xAxis = mChart.getXAxis(); + chart.getAxisRight().setEnabled(false); + + XAxis xAxis = chart.getXAxis(); xAxis.setEnabled(false); - - // programatically add the chart + + // programmatically add the chart FrameLayout parent = v.findViewById(R.id.parentLayout); - parent.addView(mChart); - + parent.addView(chart); + return v; } @@ -74,12 +76,12 @@ public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture @Override public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { Log.i("Gesture", "END"); - mChart.highlightValues(null); + chart.highlightValues(null); } @Override public void onChartLongPressed(MotionEvent me) { - Log.i("LongPress", "Chart longpressed."); + Log.i("LongPress", "Chart long pressed."); } @Override @@ -94,9 +96,9 @@ public void onChartSingleTapped(MotionEvent me) { @Override public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { - Log.i("Fling", "Chart flinged. VeloX: " + velocityX + ", VeloY: " + velocityY); + Log.i("Fling", "Chart fling. VelocityX: " + velocityX + ", VelocityY: " + velocityY); } - + @Override public void onChartScale(MotionEvent me, float scaleX, float scaleY) { Log.i("Scale / Zoom", "ScaleX: " + scaleX + ", ScaleY: " + scaleY); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java index b960e9ae0c..9edee8bdfb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java @@ -1,7 +1,8 @@ package com.xxmassdeveloper.mpchartexample.fragments; import android.graphics.Typeface; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -15,38 +16,40 @@ public class ComplexityFragment extends SimpleFragment { + @NonNull public static Fragment newInstance() { return new ComplexityFragment(); } - private LineChart mChart; - + @SuppressWarnings("FieldCanBeLocal") + private LineChart chart; + @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag_simple_line, container, false); - - mChart = v.findViewById(R.id.lineChart1); - - mChart.getDescription().setEnabled(false); - - mChart.setDrawGridBackground(false); - - mChart.setData(getComplexity()); - mChart.animateX(3000); - - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); - - Legend l = mChart.getLegend(); + + chart = v.findViewById(R.id.lineChart1); + + chart.getDescription().setEnabled(false); + + chart.setDrawGridBackground(false); + + chart.setData(getComplexity()); + chart.animateX(3000); + + Typeface tf = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); + + Legend l = chart.getLegend(); l.setTypeface(tf); - - YAxis leftAxis = mChart.getAxisLeft(); + + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setTypeface(tf); - - mChart.getAxisRight().setEnabled(false); - - XAxis xAxis = mChart.getXAxis(); + + chart.getAxisRight().setEnabled(false); + + XAxis xAxis = chart.getXAxis(); xAxis.setEnabled(false); - + return v; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java index 946532ac40..5de9a46ea3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java @@ -2,7 +2,8 @@ import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.text.style.RelativeSizeSpan; @@ -12,44 +13,45 @@ import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.xxmassdeveloper.mpchartexample.R; public class PieChartFrag extends SimpleFragment { + @NonNull public static Fragment newInstance() { return new PieChartFrag(); } - private PieChart mChart; - + @SuppressWarnings("FieldCanBeLocal") + private PieChart chart; + @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag_simple_pie, container, false); - - mChart = v.findViewById(R.id.pieChart1); - mChart.getDescription().setEnabled(false); - - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(), "OpenSans-Light.ttf"); - - mChart.setCenterTextTypeface(tf); - mChart.setCenterText(generateCenterText()); - mChart.setCenterTextSize(10f); - mChart.setCenterTextTypeface(tf); - + + chart = v.findViewById(R.id.pieChart1); + chart.getDescription().setEnabled(false); + + Typeface tf = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); + + chart.setCenterTextTypeface(tf); + chart.setCenterText(generateCenterText()); + chart.setCenterTextSize(10f); + chart.setCenterTextTypeface(tf); + // radius of the center hole in percent of maximum radius - mChart.setHoleRadius(45f); - mChart.setTransparentCircleRadius(50f); - - Legend l = mChart.getLegend(); + chart.setHoleRadius(45f); + chart.setTransparentCircleRadius(50f); + + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); l.setOrientation(Legend.LegendOrientation.VERTICAL); l.setDrawInside(false); - - mChart.setData(generatePieData()); - + + chart.setData(generatePieData()); + return v; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java index b8a3f0f324..d5d292bf0b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java @@ -2,7 +2,8 @@ import android.graphics.Typeface; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -18,49 +19,51 @@ public class ScatterChartFrag extends SimpleFragment { + @NonNull public static Fragment newInstance() { return new ScatterChartFrag(); } - private ScatterChart mChart; - + @SuppressWarnings("FieldCanBeLocal") + private ScatterChart chart; + @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag_simple_scatter, container, false); - - mChart = v.findViewById(R.id.scatterChart1); - mChart.getDescription().setEnabled(false); - - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); - + + chart = v.findViewById(R.id.scatterChart1); + chart.getDescription().setEnabled(false); + + Typeface tf = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); + MyMarkerView mv = new MyMarkerView(getActivity(), R.layout.custom_marker_view); - mv.setChartView(mChart); // For bounds control - mChart.setMarker(mv); + mv.setChartView(chart); // For bounds control + chart.setMarker(mv); + + chart.setDrawGridBackground(false); + chart.setData(generateScatterData(6, 10000, 200)); - mChart.setDrawGridBackground(false); - mChart.setData(generateScatterData(6, 10000, 200)); - - XAxis xAxis = mChart.getXAxis(); + XAxis xAxis = chart.getXAxis(); xAxis.setEnabled(true); xAxis.setPosition(XAxisPosition.BOTTOM); - - YAxis leftAxis = mChart.getAxisLeft(); + + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setTypeface(tf); - - YAxis rightAxis = mChart.getAxisRight(); + + YAxis rightAxis = chart.getAxisRight(); rightAxis.setTypeface(tf); rightAxis.setDrawGridLines(false); - - Legend l = mChart.getLegend(); + + Legend l = chart.getLegend(); l.setWordWrapEnabled(true); l.setTypeface(tf); l.setFormSize(14f); l.setTextSize(9f); - + // increase the space between legend & bottom and legend & content - l.setYOffset(13f); - mChart.setExtraBottomOffset(16f); - + l.setYOffset(13f); + chart.setExtraBottomOffset(16f); + return v; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java index ee64ffdfce..32b78142b5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java @@ -4,11 +4,15 @@ import android.app.AlertDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; +import android.content.Intent; +import android.net.Uri; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; +import android.view.Menu; +import android.view.MenuItem; import android.view.WindowManager; import com.xxmassdeveloper.mpchartexample.R; @@ -16,7 +20,7 @@ /** * Demonstrates how to keep your charts straight forward, simple and beautiful with the MPAndroidChart library. - * + * * @author Philipp Jahoda */ public class SimpleChartDemo extends DemoBase { @@ -26,21 +30,22 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_awesomedesign); + setTitle("SimpleChartDemo"); + ViewPager pager = findViewById(R.id.pager); pager.setOffscreenPageLimit(3); - + PageAdapter a = new PageAdapter(getSupportFragmentManager()); pager.setAdapter(a); - - + + AlertDialog.Builder b = new AlertDialog.Builder(this); b.setTitle("This is a ViewPager."); b.setMessage("Swipe left and right for more awesome design examples!"); b.setPositiveButton("OK", new OnClickListener() { - + @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); @@ -48,17 +53,17 @@ public void onClick(DialogInterface dialog, int which) { }); b.show(); } - + private class PageAdapter extends FragmentPagerAdapter { - public PageAdapter(FragmentManager fm) { - super(fm); + PageAdapter(FragmentManager fm) { + super(fm); } @Override - public Fragment getItem(int pos) { + public Fragment getItem(int pos) { Fragment f = null; - + switch(pos) { case 0: f = SineCosineFragment.newInstance(); @@ -83,6 +88,30 @@ public Fragment getItem(int pos) { @Override public int getCount() { return 5; - } + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java")); + startActivity(i); + break; + } + } + + return true; + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java index 22b35e4963..5caa290178 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java @@ -1,9 +1,11 @@ package com.xxmassdeveloper.mpchartexample.fragments; +import android.content.Context; import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -28,58 +30,64 @@ import java.util.ArrayList; +@SuppressWarnings("SameParameterValue") public abstract class SimpleFragment extends Fragment { - + private Typeface tf; - + protected Context context; + + @Override + public void onAttach(Context context) { + super.onAttach(context); + this.context = context; + } + public SimpleFragment() { - + } - + @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - tf = Typeface.createFromAsset(getActivity().getAssets(), "OpenSans-Regular.ttf"); + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + tf = Typeface.createFromAsset(context.getAssets(), "OpenSans-Regular.ttf"); return super.onCreateView(inflater, container, savedInstanceState); } protected BarData generateBarData(int dataSets, float range, int count) { - - ArrayList sets = new ArrayList(); - + + ArrayList sets = new ArrayList<>(); + for(int i = 0; i < dataSets; i++) { - - ArrayList entries = new ArrayList(); - -// entries = FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "stacked_bars.txt"); - - for(int j = 0; j < count; j++) { + + ArrayList entries = new ArrayList<>(); + + for(int j = 0; j < count; j++) { entries.add(new BarEntry(j, (float) (Math.random() * range) + range / 4)); } - + BarDataSet ds = new BarDataSet(entries, getLabel(i)); ds.setColors(ColorTemplate.VORDIPLOM_COLORS); sets.add(ds); } - + BarData d = new BarData(sets); d.setValueTypeface(tf); return d; } - + protected ScatterData generateScatterData(int dataSets, float range, int count) { - - ArrayList sets = new ArrayList(); - + + ArrayList sets = new ArrayList<>(); + ScatterChart.ScatterShape[] shapes = ScatterChart.ScatterShape.getAllDefaultShapes(); - + for(int i = 0; i < dataSets; i++) { - - ArrayList entries = new ArrayList(); - - for(int j = 0; j < count; j++) { + + ArrayList entries = new ArrayList<>(); + + for(int j = 0; j < count; j++) { entries.add(new Entry(j, (float) (Math.random() * range) + range / 4)); } - + ScatterDataSet ds = new ScatterDataSet(entries, getLabel(i)); ds.setScatterShapeSize(12f); ds.setScatterShape(shapes[i % shapes.length]); @@ -87,82 +95,81 @@ protected ScatterData generateScatterData(int dataSets, float range, int count) ds.setScatterShapeSize(9f); sets.add(ds); } - + ScatterData d = new ScatterData(sets); d.setValueTypeface(tf); return d; } - + /** * generates less data (1 DataSet, 4 values) - * @return + * @return PieData */ protected PieData generatePieData() { - + int count = 4; - - ArrayList entries1 = new ArrayList(); - + + ArrayList entries1 = new ArrayList<>(); + for(int i = 0; i < count; i++) { entries1.add(new PieEntry((float) ((Math.random() * 60) + 40), "Quarter " + (i+1))); } - + PieDataSet ds1 = new PieDataSet(entries1, "Quarterly Revenues 2015"); ds1.setColors(ColorTemplate.VORDIPLOM_COLORS); ds1.setSliceSpace(2f); ds1.setValueTextColor(Color.WHITE); ds1.setValueTextSize(12f); - + PieData d = new PieData(ds1); d.setValueTypeface(tf); return d; } - + protected LineData generateLineData() { - - ArrayList sets = new ArrayList(); - - LineDataSet ds1 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "sine.txt"), "Sine function"); - LineDataSet ds2 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "cosine.txt"), "Cosine function"); - + + ArrayList sets = new ArrayList<>(); + LineDataSet ds1 = new LineDataSet(FileUtils.loadEntriesFromAssets(context.getAssets(), "sine.txt"), "Sine function"); + LineDataSet ds2 = new LineDataSet(FileUtils.loadEntriesFromAssets(context.getAssets(), "cosine.txt"), "Cosine function"); + ds1.setLineWidth(2f); ds2.setLineWidth(2f); - + ds1.setDrawCircles(false); ds2.setDrawCircles(false); - + ds1.setColor(ColorTemplate.VORDIPLOM_COLORS[0]); ds2.setColor(ColorTemplate.VORDIPLOM_COLORS[1]); - - // load DataSets from textfiles in assets folders + + // load DataSets from files in assets folder sets.add(ds1); sets.add(ds2); - + LineData d = new LineData(sets); d.setValueTypeface(tf); return d; } - + protected LineData getComplexity() { - - ArrayList sets = new ArrayList(); - - LineDataSet ds1 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "n.txt"), "O(n)"); - LineDataSet ds2 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "nlogn.txt"), "O(nlogn)"); - LineDataSet ds3 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "square.txt"), "O(n\u00B2)"); - LineDataSet ds4 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "three.txt"), "O(n\u00B3)"); - + + ArrayList sets = new ArrayList<>(); + + LineDataSet ds1 = new LineDataSet(FileUtils.loadEntriesFromAssets(context.getAssets(), "n.txt"), "O(n)"); + LineDataSet ds2 = new LineDataSet(FileUtils.loadEntriesFromAssets(context.getAssets(), "nlogn.txt"), "O(nlogn)"); + LineDataSet ds3 = new LineDataSet(FileUtils.loadEntriesFromAssets(context.getAssets(), "square.txt"), "O(n\u00B2)"); + LineDataSet ds4 = new LineDataSet(FileUtils.loadEntriesFromAssets(context.getAssets(), "three.txt"), "O(n\u00B3)"); + ds1.setColor(ColorTemplate.VORDIPLOM_COLORS[0]); ds2.setColor(ColorTemplate.VORDIPLOM_COLORS[1]); ds3.setColor(ColorTemplate.VORDIPLOM_COLORS[2]); ds4.setColor(ColorTemplate.VORDIPLOM_COLORS[3]); - + ds1.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[0]); ds2.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[1]); ds3.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[2]); ds4.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[3]); - + ds1.setLineWidth(2.5f); ds1.setCircleRadius(3f); ds2.setLineWidth(2.5f); @@ -171,22 +178,21 @@ protected LineData getComplexity() { ds3.setCircleRadius(3f); ds4.setLineWidth(2.5f); ds4.setCircleRadius(3f); - - - // load DataSets from textfiles in assets folders - sets.add(ds1); + + + // load DataSets from files in assets folder + sets.add(ds1); sets.add(ds2); sets.add(ds3); sets.add(ds4); - + LineData d = new LineData(sets); d.setValueTypeface(tf); return d; } - - private String[] mLabels = new String[] { "Company A", "Company B", "Company C", "Company D", "Company E", "Company F" }; -// private String[] mXVals = new String[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" }; - + + private final String[] mLabels = new String[] { "Company A", "Company B", "Company C", "Company D", "Company E", "Company F" }; + private String getLabel(int i) { return mLabels[i]; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java index 7e425172fb..0581529308 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java @@ -1,7 +1,8 @@ package com.xxmassdeveloper.mpchartexample.fragments; import android.graphics.Typeface; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -15,40 +16,42 @@ public class SineCosineFragment extends SimpleFragment { + @NonNull public static Fragment newInstance() { return new SineCosineFragment(); } - private LineChart mChart; - + @SuppressWarnings("FieldCanBeLocal") + private LineChart chart; + @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag_simple_line, container, false); - - mChart = v.findViewById(R.id.lineChart1); - - mChart.getDescription().setEnabled(false); - - mChart.setDrawGridBackground(false); - - mChart.setData(generateLineData()); - mChart.animateX(3000); - - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); - - Legend l = mChart.getLegend(); + + chart = v.findViewById(R.id.lineChart1); + + chart.getDescription().setEnabled(false); + + chart.setDrawGridBackground(false); + + chart.setData(generateLineData()); + chart.animateX(3000); + + Typeface tf = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); + + Legend l = chart.getLegend(); l.setTypeface(tf); - - YAxis leftAxis = mChart.getAxisLeft(); + + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setTypeface(tf); leftAxis.setAxisMaximum(1.2f); leftAxis.setAxisMinimum(-1.2f); - - mChart.getAxisRight().setEnabled(false); - - XAxis xAxis = mChart.getXAxis(); + + chart.getAxisRight().setEnabled(false); + + XAxis xAxis = chart.getXAxis(); xAxis.setEnabled(false); - + return v; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java index c09297a391..b49daa3be4 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java @@ -1,5 +1,6 @@ package com.xxmassdeveloper.mpchartexample.listviewitems; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Typeface; import android.view.LayoutInflater; @@ -14,9 +15,9 @@ import com.xxmassdeveloper.mpchartexample.R; public class BarChartItem extends ChartItem { - + private Typeface mTf; - + public BarChartItem(ChartData cd, Context c) { super(cd); @@ -28,10 +29,11 @@ public int getItemType() { return TYPE_BARCHART; } + @SuppressLint("InflateParams") @Override public View getView(int position, View convertView, Context c) { - ViewHolder holder = null; + ViewHolder holder; if (convertView == null) { @@ -57,13 +59,13 @@ public View getView(int position, View convertView, Context c) { xAxis.setTypeface(mTf); xAxis.setDrawGridLines(false); xAxis.setDrawAxisLine(true); - + YAxis leftAxis = holder.chart.getAxisLeft(); leftAxis.setTypeface(mTf); leftAxis.setLabelCount(5, false); leftAxis.setSpaceTop(20f); leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) - + YAxis rightAxis = holder.chart.getAxisRight(); rightAxis.setTypeface(mTf); rightAxis.setLabelCount(5, false); @@ -71,18 +73,18 @@ public View getView(int position, View convertView, Context c) { rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) mChartData.setValueTypeface(mTf); - + // set data holder.chart.setData((BarData) mChartData); holder.chart.setFitBars(true); - + // do not forget to refresh the chart // holder.chart.invalidate(); holder.chart.animateY(700); return convertView; } - + private static class ViewHolder { BarChart chart; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java index 0e6182165c..2a8ed0561d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java @@ -6,23 +6,24 @@ import com.github.mikephil.charting.data.ChartData; /** - * baseclass of the chart-listview items + * Base class of the Chart ListView items * @author philipp * */ +@SuppressWarnings("unused") public abstract class ChartItem { - - protected static final int TYPE_BARCHART = 0; - protected static final int TYPE_LINECHART = 1; - protected static final int TYPE_PIECHART = 2; - - protected ChartData mChartData; - - public ChartItem(ChartData cd) { - this.mChartData = cd; + + static final int TYPE_BARCHART = 0; + static final int TYPE_LINECHART = 1; + static final int TYPE_PIECHART = 2; + + ChartData mChartData; + + ChartItem(ChartData cd) { + this.mChartData = cd; } - + public abstract int getItemType(); - + public abstract View getView(int position, View convertView, Context c); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java index 107930af2a..e4c11869ac 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java @@ -1,6 +1,7 @@ package com.xxmassdeveloper.mpchartexample.listviewitems; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Typeface; import android.view.LayoutInflater; @@ -29,10 +30,11 @@ public int getItemType() { return TYPE_LINECHART; } + @SuppressLint("InflateParams") @Override public View getView(int position, View convertView, Context c) { - ViewHolder holder = null; + ViewHolder holder; if (convertView == null) { @@ -63,7 +65,7 @@ public View getView(int position, View convertView, Context c) { leftAxis.setTypeface(mTf); leftAxis.setLabelCount(5, false); leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) - + YAxis rightAxis = holder.chart.getAxisRight(); rightAxis.setTypeface(mTf); rightAxis.setLabelCount(5, false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java index 5503018792..0c5e56bd3b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java @@ -1,6 +1,7 @@ package com.xxmassdeveloper.mpchartexample.listviewitems; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Color; import android.graphics.Typeface; @@ -12,7 +13,6 @@ import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.formatter.PercentFormatter; @@ -36,10 +36,11 @@ public int getItemType() { return TYPE_PIECHART; } + @SuppressLint("InflateParams") @Override public View getView(int position, View convertView, Context c) { - ViewHolder holder = null; + ViewHolder holder; if (convertView == null) { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java index 97bb230ec9..41f05afc10 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java @@ -8,6 +8,13 @@ public class ContentItem { String name; String desc; boolean isNew = false; + boolean isSection = false; + + public ContentItem(String n) { + name = n; + desc = ""; + isSection = true; + } public ContentItem(String n, String d) { name = n; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java index 59b73b1ad7..ea0d1aa008 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java @@ -1,45 +1,55 @@ package com.xxmassdeveloper.mpchartexample.notimportant; +import android.Manifest; +import android.content.pm.PackageManager; import android.graphics.Typeface; import android.os.Bundle; -import android.renderscript.Type; -import android.support.annotation.Nullable; -import android.support.v4.app.FragmentActivity; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.material.snackbar.Snackbar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import android.view.View; +import android.widget.Toast; + +import com.github.mikephil.charting.charts.Chart; import com.xxmassdeveloper.mpchartexample.R; /** - * Baseclass of all Activities of the Demo Application. - * + * Base class of all Activities of the Demo Application. + * * @author Philipp Jahoda */ -public abstract class DemoBase extends FragmentActivity { +public abstract class DemoBase extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback { - protected String[] mMonths = new String[] { + protected final String[] months = new String[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" }; - protected String[] mParties = new String[] { + protected final String[] parties = new String[] { "Party A", "Party B", "Party C", "Party D", "Party E", "Party F", "Party G", "Party H", "Party I", "Party J", "Party K", "Party L", "Party M", "Party N", "Party O", "Party P", "Party Q", "Party R", "Party S", "Party T", "Party U", "Party V", "Party W", "Party X", "Party Y", "Party Z" }; - protected Typeface mTfRegular; - protected Typeface mTfLight; + private static final int PERMISSION_STORAGE = 0; + + protected Typeface tfRegular; + protected Typeface tfLight; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mTfRegular = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - mTfLight = Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf"); + tfRegular = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + tfLight = Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf"); } - protected float getRandom(float range, float startsfrom) { - return (float) (Math.random() * range) + startsfrom; + protected float getRandom(float range, float start) { + return (float) (Math.random() * range) + start; } @Override @@ -47,4 +57,43 @@ public void onBackPressed() { super.onBackPressed(); overridePendingTransition(R.anim.move_left_in_activity, R.anim.move_right_out_activity); } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode == PERMISSION_STORAGE) { + if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) + .show(); + } + } + } + + protected void requestStoragePermission(View view) { + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + Snackbar.make(view, "Write permission is required to save image to gallery", Snackbar.LENGTH_INDEFINITE) + .setAction(android.R.string.ok, new View.OnClickListener() { + @Override + public void onClick(View v) { + ActivityCompat.requestPermissions(DemoBase.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_STORAGE); + } + }).show(); + } else { + Toast.makeText(getApplicationContext(), "Permission Required!", Toast.LENGTH_SHORT) + .show(); + ActivityCompat.requestPermissions(DemoBase.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_STORAGE); + } + } + + protected void saveToGallery(Chart chart, String name) { + if (chart.saveToGallery(name + "_" + System.currentTimeMillis(), 70)) + Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", + Toast.LENGTH_SHORT).show(); + else + Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) + .show(); + } + + abstract public void saveToGallery(); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index d994f87c96..67749e742f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -1,7 +1,6 @@ package com.xxmassdeveloper.mpchartexample.notimportant; -import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -46,11 +45,12 @@ import com.xxmassdeveloper.mpchartexample.StackedBarActivity; import com.xxmassdeveloper.mpchartexample.StackedBarActivityNegative; import com.xxmassdeveloper.mpchartexample.fragments.SimpleChartDemo; -import com.xxmassdeveloper.mpchartexample.realm.RealmMainActivity; import java.util.ArrayList; -public class MainActivity extends Activity implements OnItemClickListener { +import androidx.appcompat.app.AppCompatActivity; + +public class MainActivity extends AppCompatActivity implements OnItemClickListener { @Override protected void onCreate(Bundle savedInstanceState) { @@ -64,88 +64,63 @@ protected void onCreate(Bundle savedInstanceState) { // initialize the utilities Utils.init(this); - ArrayList objects = new ArrayList(); - - objects.add(new ContentItem("Line Chart", "A simple demonstration of the linechart.")); - objects.add(new ContentItem("Line Chart (Dual YAxis)", - "Demonstration of the linechart with dual y-axis.")); - objects.add(new ContentItem("Bar Chart", "A simple demonstration of the bar chart.")); - objects.add(new ContentItem("Horizontal Bar Chart", - "A simple demonstration of the horizontal bar chart.")); - objects.add(new ContentItem("Combined Chart", - "Demonstrates how to create a combined chart (bar and line in this case).")); - objects.add(new ContentItem("Pie Chart", "A simple demonstration of the pie chart.")); - objects.add(new ContentItem("Pie Chart with value lines", "A simple demonstration of the pie chart with polyline notes.")); - objects.add(new ContentItem("Scatter Chart", "A simple demonstration of the scatter chart.")); - objects.add(new ContentItem("Bubble Chart", "A simple demonstration of the bubble chart.")); - objects.add(new ContentItem("Stacked Bar Chart", - "A simple demonstration of a bar chart with stacked bars.")); - objects.add(new ContentItem("Stacked Bar Chart Negative", - "A simple demonstration of stacked bars with negative and positive values.")); - objects.add(new ContentItem("Another Bar Chart", - "Implementation of a BarChart that only shows values at the bottom.")); - objects.add(new ContentItem("Multiple Lines Chart", - "A line chart with multiple DataSet objects. One color per DataSet.")); - objects.add(new ContentItem("Multiple Bars Chart", - "A bar chart with multiple DataSet objects. One multiple colors per DataSet.")); - objects.add(new ContentItem( - "Charts in ViewPager Fragments", - "Demonstration of charts inside ViewPager Fragments. In this example the focus was on the design and look and feel of the" + - " chart.")); - objects.add(new ContentItem( - "BarChart inside ListView", - "Demonstrates the usage of a BarChart inside a ListView item.")); - objects.add(new ContentItem( - "Multiple charts inside ListView", - "Demonstrates the usage of different chart types inside a ListView.")); - objects.add(new ContentItem( - "Inverted Line Chart", - "Demonstrates the feature of inverting the y-axis.")); - objects.add(new ContentItem( - "Candle Stick Chart", - "Demonstrates usage of the CandleStickChart.")); - objects.add(new ContentItem( - "Cubic Line Chart", - "Demonstrates cubic lines in a LineChart.")); - objects.add(new ContentItem( - "Radar Chart", - "Demonstrates the use of a spider-web like (net) chart.")); - objects.add(new ContentItem( - "Colored Line Chart", - "Shows a LineChart with different background and line color.")); - objects.add(new ContentItem( - "Realtime Chart", - "This chart is fed with new data in realtime. It also restrains the view on the x-axis.")); - objects.add(new ContentItem( - "Dynamical data adding", - "This Activity demonstrates dynamical adding of Entries and DataSets (real time graph).")); - objects.add(new ContentItem( - "Performance Line Chart", - "Renders up to 30.000 objects smoothly.")); - objects.add(new ContentItem( - "Sinus Bar Chart", - "A Bar Chart plotting the sinus function with 8.000 values.")); - objects.add(new ContentItem( - "Chart in ScrollView", - "This demonstrates how to use a chart inside a ScrollView.")); - objects.add(new ContentItem( - "BarChart positive / negative", - "This demonstrates how to create a BarChart with positive and negative values in different colors.")); - - ContentItem realm = new ContentItem( - "Realm.io Database", - "This demonstrates how to use this library with Realm.io mobile database."); - objects.add(realm); - objects.add(new ContentItem( - "Time Chart", - "Simple demonstration of a time-chart. This chart draws one line entry per hour originating from the current time in " + - "milliseconds.")); - objects.add(new ContentItem( - "Filled LineChart", - "This demonstrates how to fill an area between two LineDataSets.")); - objects.add(new ContentItem( - "Half PieChart", - "This demonstrates how to create a 180 degree PieChart.")); + ArrayList objects = new ArrayList<>(); + + //// + objects.add(0, new ContentItem("Line Charts")); + + objects.add(1, new ContentItem("Basic", "Simple line chart.")); + objects.add(2, new ContentItem("Multiple", "Show multiple data sets.")); + objects.add(3, new ContentItem("Dual Axis", "Line chart with dual y-axes.")); + objects.add(4, new ContentItem("Inverted Axis", "Inverted y-axis.")); + objects.add(5, new ContentItem("Cubic", "Line chart with a cubic line shape.")); + objects.add(6, new ContentItem("Colorful", "Colorful line chart.")); + objects.add(7, new ContentItem("Performance", "Render 30.000 data points smoothly.")); + objects.add(8, new ContentItem("Filled", "Colored area between two lines.")); + + //// + objects.add(9, new ContentItem("Bar Charts")); + + objects.add(10, new ContentItem("Basic", "Simple bar chart.")); + objects.add(11, new ContentItem("Basic 2", "Variation of the simple bar chart.")); + objects.add(12, new ContentItem("Multiple", "Show multiple data sets.")); + objects.add(13, new ContentItem("Horizontal", "Render bar chart horizontally.")); + objects.add(14, new ContentItem("Stacked", "Stacked bar chart.")); + objects.add(15, new ContentItem("Negative", "Positive and negative values with unique colors.")); + objects.add(16, new ContentItem("Stacked 2", "Stacked bar chart with negative values.")); + objects.add(17, new ContentItem("Sine", "Sine function in bar chart format.")); + + //// + objects.add(18, new ContentItem("Pie Charts")); + + objects.add(19, new ContentItem("Basic", "Simple pie chart.")); + objects.add(20, new ContentItem("Value Lines", "Stylish lines drawn outward from slices.")); + objects.add(21, new ContentItem("Half Pie", "180° (half) pie chart.")); + + //// + objects.add(22, new ContentItem("Other Charts")); + + objects.add(23, new ContentItem("Combined Chart", "Bar and line chart together.")); + objects.add(24, new ContentItem("Scatter Plot", "Simple scatter plot.")); + objects.add(25, new ContentItem("Bubble Chart", "Simple bubble chart.")); + objects.add(26, new ContentItem("Candlestick", "Simple financial chart.")); + objects.add(27, new ContentItem("Radar Chart", "Simple web chart.")); + + //// + objects.add(28, new ContentItem("Scrolling Charts")); + + objects.add(29, new ContentItem("Multiple", "Various types of charts as fragments.")); + objects.add(30, new ContentItem("View Pager", "Swipe through different charts.")); + objects.add(31, new ContentItem("Tall Bar Chart", "Bars bigger than your screen!")); + objects.add(32, new ContentItem("Many Bar Charts", "More bars than your screen can handle!")); + + //// + objects.add(33, new ContentItem("Even More Line Charts")); + + objects.add(34, new ContentItem("Dynamic", "Build a line chart by adding points and sets.")); + objects.add(35, new ContentItem("Realtime", "Add data points in realtime.")); + objects.add(36, new ContentItem("Hourly", "Uses the current time to add a data point for each hour.")); + //objects.add(37, new ContentItem("Realm.io Examples", "See more examples that use Realm.io mobile database.")); MyAdapter adapter = new MyAdapter(this, objects); @@ -158,140 +133,109 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onItemClick(AdapterView av, View v, int pos, long arg3) { - Intent i; + Intent i = null; switch (pos) { - case 0: - i = new Intent(this, LineChartActivity1.class); - startActivity(i); - break; case 1: - i = new Intent(this, LineChartActivity2.class); - startActivity(i); + i = new Intent(this, LineChartActivity1.class); break; case 2: - i = new Intent(this, BarChartActivity.class); - startActivity(i); + i = new Intent(this, MultiLineChartActivity.class); break; case 3: - i = new Intent(this, HorizontalBarChartActivity.class); - startActivity(i); + i = new Intent(this, LineChartActivity2.class); break; case 4: - i = new Intent(this, CombinedChartActivity.class); - startActivity(i); + i = new Intent(this, InvertedLineChartActivity.class); break; case 5: - i = new Intent(this, PieChartActivity.class); - startActivity(i); + i = new Intent(this, CubicLineChartActivity.class); break; case 6: - i = new Intent(this, PiePolylineChartActivity.class); - startActivity(i); + i = new Intent(this, LineChartActivityColored.class); break; case 7: - i = new Intent(this, ScatterChartActivity.class); - startActivity(i); + i = new Intent(this, PerformanceLineChart.class); break; case 8: - i = new Intent(this, BubbleChartActivity.class); - startActivity(i); - break; - case 9: - i = new Intent(this, StackedBarActivity.class); - startActivity(i); + i = new Intent(this, FilledLineActivity.class); break; case 10: - i = new Intent(this, StackedBarActivityNegative.class); - startActivity(i); + i = new Intent(this, BarChartActivity.class); break; case 11: i = new Intent(this, AnotherBarActivity.class); - startActivity(i); break; case 12: - i = new Intent(this, MultiLineChartActivity.class); - startActivity(i); + i = new Intent(this, BarChartActivityMultiDataset.class); break; case 13: - i = new Intent(this, BarChartActivityMultiDataset.class); - startActivity(i); + i = new Intent(this, HorizontalBarChartActivity.class); break; case 14: - i = new Intent(this, SimpleChartDemo.class); - startActivity(i); + i = new Intent(this, StackedBarActivity.class); break; case 15: - i = new Intent(this, ListViewBarChartActivity.class); - startActivity(i); + i = new Intent(this, BarChartPositiveNegative.class); break; case 16: - i = new Intent(this, ListViewMultiChartActivity.class); - startActivity(i); + i = new Intent(this, StackedBarActivityNegative.class); break; case 17: - i = new Intent(this, InvertedLineChartActivity.class); - startActivity(i); - break; - case 18: - i = new Intent(this, CandleStickChartActivity.class); - startActivity(i); + i = new Intent(this, BarChartActivitySinus.class); break; case 19: - i = new Intent(this, CubicLineChartActivity.class); - startActivity(i); + i = new Intent(this, PieChartActivity.class); break; case 20: - i = new Intent(this, RadarChartActivity.class); - startActivity(i); + i = new Intent(this, PiePolylineChartActivity.class); break; case 21: - i = new Intent(this, LineChartActivityColored.class); - startActivity(i); - break; - case 22: - i = new Intent(this, RealtimeLineChartActivity.class); - startActivity(i); + i = new Intent(this, HalfPieChartActivity.class); break; case 23: - i = new Intent(this, DynamicalAddingActivity.class); - startActivity(i); + i = new Intent(this, CombinedChartActivity.class); break; case 24: - i = new Intent(this, PerformanceLineChart.class); - startActivity(i); + i = new Intent(this, ScatterChartActivity.class); break; case 25: - i = new Intent(this, BarChartActivitySinus.class); - startActivity(i); + i = new Intent(this, BubbleChartActivity.class); break; case 26: - i = new Intent(this, ScrollViewActivity.class); - startActivity(i); + i = new Intent(this, CandleStickChartActivity.class); break; case 27: - i = new Intent(this, BarChartPositiveNegative.class); - startActivity(i); - break; - case 28: - i = new Intent(this, RealmMainActivity.class); - startActivity(i); + i = new Intent(this, RadarChartActivity.class); break; case 29: - i = new Intent(this, LineChartTime.class); - startActivity(i); + i = new Intent(this, ListViewMultiChartActivity.class); break; case 30: - i = new Intent(this, FilledLineActivity.class); - startActivity(i); + i = new Intent(this, SimpleChartDemo.class); break; case 31: - i = new Intent(this, HalfPieChartActivity.class); - startActivity(i); + i = new Intent(this, ScrollViewActivity.class); break; - + case 32: + i = new Intent(this, ListViewBarChartActivity.class); + break; + case 34: + i = new Intent(this, DynamicalAddingActivity.class); + break; + case 35: + i = new Intent(this, RealtimeLineChartActivity.class); + break; + case 36: + i = new Intent(this, LineChartTime.class); + break; + /*case 37: + i = new Intent(this, RealmMainActivity.class); + break;*/ } + if (i != null) startActivity(i); + overridePendingTransition(R.anim.move_right_in_activity, R.anim.move_left_out_activity); } @@ -304,7 +248,7 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - Intent i = null; + Intent i; switch (item.getItemId()) { case R.id.viewGithub: @@ -319,11 +263,6 @@ public boolean onOptionsItemSelected(MenuItem item) { i.putExtra(Intent.EXTRA_TEXT, "Your error report here..."); startActivity(Intent.createChooser(i, "Report Problem")); break; - case R.id.blog: - i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse("http://www.xxmassdeveloper.com/")); - startActivity(i); - break; case R.id.website: i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse("http://at.linkedin.com/in/philippjahoda")); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java index 5b424e88a5..d9fc14a609 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java @@ -1,7 +1,10 @@ package com.xxmassdeveloper.mpchartexample.notimportant; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Typeface; +import androidx.annotation.NonNull; + import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -27,36 +30,40 @@ public MyAdapter(Context context, List objects) { mTypeFaceRegular = Typeface.createFromAsset(context.getAssets(), "OpenSans-Regular.ttf"); } + @SuppressLint("InflateParams") + @NonNull @Override - public View getView(int position, View convertView, ViewGroup parent) { + public View getView(int position, View convertView, @NonNull ViewGroup parent) { ContentItem c = getItem(position); - ViewHolder holder = null; - - if (convertView == null) { + ViewHolder holder; - holder = new ViewHolder(); + holder = new ViewHolder(); + if (c != null && c.isSection) { + convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item_section, null); + } else { convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item, null); - holder.tvName = convertView.findViewById(R.id.tvName); - holder.tvDesc = convertView.findViewById(R.id.tvDesc); - holder.tvNew = convertView.findViewById(R.id.tvNew); + } - convertView.setTag(holder); + holder.tvName = convertView.findViewById(R.id.tvName); + holder.tvDesc = convertView.findViewById(R.id.tvDesc); + holder.tvNew = convertView.findViewById(R.id.tvNew); - } else { - holder = (ViewHolder) convertView.getTag(); - } + convertView.setTag(holder); holder.tvNew.setTypeface(mTypeFaceRegular); - holder.tvName.setTypeface(mTypeFaceLight); + if (c != null && c.isSection) + holder.tvName.setTypeface(mTypeFaceRegular); + else + holder.tvName.setTypeface(mTypeFaceLight); holder.tvDesc.setTypeface(mTypeFaceLight); - holder.tvName.setText(c.name); - holder.tvDesc.setText(c.desc); + holder.tvName.setText(c != null ? c.name : null); + holder.tvDesc.setText(c != null ? c.desc : null); - if(c.isNew) + if(c != null && c.isNew) holder.tvNew.setVisibility(View.VISIBLE); else holder.tvNew.setVisibility(View.GONE); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java index 9b98f00f92..690eb6cf3a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -19,6 +19,7 @@ /** * Created by Philipp Jahoda on 05/11/15. */ +@SuppressWarnings("SameParameterValue") public abstract class RealmBaseActivity extends DemoBase { protected Realm mRealm; @@ -139,8 +140,7 @@ protected void writeToDBCandle(int objectCount) { for (int i = 0; i < objectCount; i++) { - float mult = 50; - float val = (float) (Math.random() * 40) + mult; + float val = (float) (Math.random() * 40) + 50f; float high = (float) (Math.random() * 9) + 8f; float low = (float) (Math.random() * 9) + 8f; @@ -199,4 +199,7 @@ protected void writeToDBPie() { mRealm.commitTransaction(); } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java index 1e5d5cb8d5..f7cba47340 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java @@ -49,12 +49,12 @@ private void setData() { RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet(result, "xValue", "yValue"); // stacked entries - set.setColors(new int[] {ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); + //RealmBarDataSet set = new RealmBarDataSet<>(result, "stackValues", "xIndex"); // normal entries + RealmBarDataSet set = new RealmBarDataSet<>(result, "xValue", "yValue"); // stacked entries + set.setColors(ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")); set.setLabel("Realm BarDataSet"); - ArrayList dataSets = new ArrayList(); + ArrayList dataSets = new ArrayList<>(); dataSets.add(set); // add the dataset // create a data object with the dataset list diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java index ad65a3de18..3493ade94f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java @@ -53,11 +53,11 @@ private void setData() { RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - RealmBubbleDataSet set = new RealmBubbleDataSet(result, "xValue", "yValue", "bubbleSize"); + RealmBubbleDataSet set = new RealmBubbleDataSet<>(result, "xValue", "yValue", "bubbleSize"); set.setLabel("Realm BubbleDataSet"); set.setColors(ColorTemplate.COLORFUL_COLORS, 110); - ArrayList dataSets = new ArrayList(); + ArrayList dataSets = new ArrayList<>(); dataSets.add(set); // add the dataset // create a data object with the dataset list diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java index 96fbade855..49d8903c6e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java @@ -53,7 +53,7 @@ private void setData() { RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - RealmCandleDataSet set = new RealmCandleDataSet(result, "xValue", "high", "low", "open", "close"); + RealmCandleDataSet set = new RealmCandleDataSet<>(result, "xValue", "high", "low", "open", "close"); set.setLabel("Realm CandleDataSet"); set.setShadowColor(Color.DKGRAY); set.setShadowWidth(0.7f); @@ -63,7 +63,7 @@ private void setData() { set.setIncreasingPaintStyle(Paint.Style.STROKE); set.setNeutralColor(Color.BLUE); - ArrayList dataSets = new ArrayList(); + ArrayList dataSets = new ArrayList<>(); dataSets.add(set); // add the dataset // create a data object with the dataset list diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java index b5e3345134..680ee1e83c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java @@ -53,13 +53,13 @@ private void setData() { RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet(result, "xValue", "stackValues", "floatValue"); // stacked entries - set.setColors(new int[]{ColorTemplate.rgb("#8BC34A"), ColorTemplate.rgb("#FFC107"), ColorTemplate.rgb("#9E9E9E")}); + //RealmBarDataSet set = new RealmBarDataSet<>(result, "stackValues", "xIndex"); // normal entries + RealmBarDataSet set = new RealmBarDataSet<>(result, "xValue", "stackValues", "floatValue"); // stacked entries + set.setColors(ColorTemplate.rgb("#8BC34A"), ColorTemplate.rgb("#FFC107"), ColorTemplate.rgb("#9E9E9E")); set.setLabel("Mobile OS distribution"); set.setStackLabels(new String[]{"iOS", "Android", "Other"}); - ArrayList dataSets = new ArrayList(); + ArrayList dataSets = new ArrayList<>(); dataSets.add(set); // add the dataset // create a data object with the dataset list diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java index 7d2e49fdff..c0bbd3caf6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java @@ -55,7 +55,7 @@ private void setData() { RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - RealmLineDataSet set = new RealmLineDataSet(result, "xValue", "yValue"); + RealmLineDataSet set = new RealmLineDataSet<>(result, "xValue", "yValue"); set.setMode(LineDataSet.Mode.CUBIC_BEZIER); set.setLabel("Realm LineDataSet"); set.setDrawCircleHole(false); @@ -64,7 +64,7 @@ private void setData() { set.setLineWidth(1.8f); set.setCircleRadius(3.6f); - ArrayList dataSets = new ArrayList(); + ArrayList dataSets = new ArrayList<>(); dataSets.add(set); // add the dataset // create a data object with the dataset list diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java index 9fad49c617..46af4cda31 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java @@ -53,7 +53,7 @@ private void setData() { RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - RealmPieDataSet set = new RealmPieDataSet(result, "yValue", "label"); + RealmPieDataSet set = new RealmPieDataSet<>(result, "yValue", "label"); set.setColors(ColorTemplate.VORDIPLOM_COLORS); set.setLabel("Example market share"); set.setSliceSpace(2); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java index 02f3ac0492..faef4e28bc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java @@ -21,23 +21,23 @@ */ public class RealmDatabaseActivityRadar extends RealmBaseActivity { - private RadarChart mChart; + private RadarChart chart; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_radarchart_noseekbar); + setContentView(R.layout.activity_radarchart); - mChart = findViewById(R.id.chart1); - setup(mChart); + chart = findViewById(R.id.chart1); + setup(chart); - mChart.getYAxis().setEnabled(false); - mChart.getXAxis().setEnabled(false); - mChart.setWebAlpha(180); - mChart.setWebColorInner(Color.DKGRAY); - mChart.setWebColor(Color.GRAY); + chart.getYAxis().setEnabled(false); + chart.getXAxis().setEnabled(false); + chart.setWebAlpha(180); + chart.setWebColorInner(Color.DKGRAY); + chart.setWebColor(Color.GRAY); } @Override @@ -55,8 +55,8 @@ private void setData() { RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmRadarDataSet set = new RealmRadarDataSet(result, "yValue"); // stacked entries + //RealmBarDataSet set = new RealmBarDataSet<>(result, "stackValues", "xIndex"); // normal entries + RealmRadarDataSet set = new RealmRadarDataSet<>(result, "yValue"); // stacked entries set.setLabel("Realm RadarDataSet"); set.setDrawFilled(true); set.setColor(ColorTemplate.rgb("#009688")); @@ -64,7 +64,7 @@ private void setData() { set.setFillAlpha(130); set.setLineWidth(2f); - ArrayList dataSets = new ArrayList(); + ArrayList dataSets = new ArrayList<>(); dataSets.add(set); // add the dataset // create a data object with the dataset list @@ -72,7 +72,7 @@ private void setData() { styleData(data); // set data - mChart.setData(data); - mChart.animateY(1400); + chart.setData(data); + chart.animateY(1400); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java index 9da64cbf1d..01e0151933 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java @@ -53,13 +53,13 @@ private void setData() { RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - RealmScatterDataSet set = new RealmScatterDataSet(result, "xValue", "yValue"); + RealmScatterDataSet set = new RealmScatterDataSet<>(result, "xValue", "yValue"); set.setLabel("Realm ScatterDataSet"); set.setScatterShapeSize(9f); set.setColor(ColorTemplate.rgb("#CDDC39")); set.setScatterShape(ScatterChart.ScatterShape.CIRCLE); - ArrayList dataSets = new ArrayList(); + ArrayList dataSets = new ArrayList<>(); dataSets.add(set); // add the dataset // create a data object with the dataset list diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java index 1776a8bd7e..674cef458e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java @@ -1,5 +1,6 @@ package com.xxmassdeveloper.mpchartexample.realm; +import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -12,7 +13,6 @@ import com.xxmassdeveloper.mpchartexample.R; import com.xxmassdeveloper.mpchartexample.notimportant.ContentItem; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import com.xxmassdeveloper.mpchartexample.notimportant.MyAdapter; import java.util.ArrayList; @@ -23,7 +23,7 @@ /** * Created by Philipp Jahoda on 07/12/15. */ -public class RealmMainActivity extends DemoBase implements AdapterView.OnItemClickListener { +public class RealmMainActivity extends Activity implements AdapterView.OnItemClickListener { @Override protected void onCreate(Bundle savedInstanceState) { @@ -34,7 +34,7 @@ protected void onCreate(Bundle savedInstanceState) { setTitle("Realm.io Examples"); - ArrayList objects = new ArrayList(); + ArrayList objects = new ArrayList<>(); objects.add(new ContentItem("Line Chart", "Creating a LineChart with Realm.io database")); objects.add(new ContentItem("Bar Chart", diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java index f223be6093..35212728cd 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java @@ -88,14 +88,15 @@ private void setData() { IAxisValueFormatter formatter = new IAxisValueFormatter() { @Override public String getFormattedValue(float value, AxisBase axis) { - return results.get((int) value).getPlayerName(); + Score result = results.get((int) value); + return result != null ? result.playerName : ""; } }; lineChart.getXAxis().setValueFormatter(formatter); barChart.getXAxis().setValueFormatter(formatter); - RealmLineDataSet lineDataSet = new RealmLineDataSet(results, "scoreNr", "totalScore"); + RealmLineDataSet lineDataSet = new RealmLineDataSet<>(results, "scoreNr", "totalScore"); lineDataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER); lineDataSet.setLabel("Result Scores"); lineDataSet.setDrawCircleHole(false); @@ -104,7 +105,7 @@ public String getFormattedValue(float value, AxisBase axis) { lineDataSet.setLineWidth(1.8f); lineDataSet.setCircleRadius(3.6f); - ArrayList dataSets = new ArrayList(); + ArrayList dataSets = new ArrayList<>(); dataSets.add(lineDataSet); LineData lineData = new LineData(dataSets); @@ -116,11 +117,11 @@ public String getFormattedValue(float value, AxisBase axis) { // BAR-CHART - RealmBarDataSet barDataSet = new RealmBarDataSet(results, "scoreNr", "totalScore"); - barDataSet.setColors(new int[]{ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); + RealmBarDataSet barDataSet = new RealmBarDataSet<>(results, "scoreNr", "totalScore"); + barDataSet.setColors(ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")); barDataSet.setLabel("Realm BarDataSet"); - ArrayList barDataSets = new ArrayList(); + ArrayList barDataSets = new ArrayList<>(); barDataSets.add(barDataSet); BarData barData = new BarData(barDataSets); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java index 870e371491..0d1f806616 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java @@ -6,16 +6,16 @@ /** * our data object */ +@SuppressWarnings({"WeakerAccess", "unused"}) public class Score extends RealmObject { - private float totalScore; + public float totalScore; - private float scoreNr; + public float scoreNr; - private String playerName; + public String playerName; - public Score() { - } + public Score() {} public Score(float totalScore, float scoreNr, String playerName) { this.scoreNr = scoreNr; @@ -23,29 +23,4 @@ public Score(float totalScore, float scoreNr, String playerName) { this.totalScore = totalScore; } - // all getters and setters... - - public float getTotalScore() { - return totalScore; - } - - public void setTotalScore(float totalScore) { - this.totalScore = totalScore; - } - - public float getScoreNr() { - return scoreNr; - } - - public void setScoreNr(float scoreNr) { - this.scoreNr = scoreNr; - } - - public String getPlayerName() { - return playerName; - } - - public void setPlayerName(String playerName) { - this.playerName = playerName; - } -} \ No newline at end of file +} diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index 19c06befb3..83fc126bff 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -4,13 +4,12 @@ apply plugin: 'maven' //apply plugin: 'realm-android' android { - compileSdkVersion 27 - buildToolsVersion '27.0.3' + compileSdkVersion 28 + buildToolsVersion '28.0.3' // resourcePrefix 'mpcht' defaultConfig { - //noinspection MinSdkTooLow - minSdkVersion 9 - targetSdkVersion 27 + minSdkVersion 14 + targetSdkVersion 28 versionCode 3 versionName '3.0.3' } @@ -36,7 +35,7 @@ repositories { dependencies { //provided 'io.realm:realm-android:0.87.5' // "optional" dependency to realm-database API - implementation 'com.android.support:support-annotations:27.1.1' + implementation 'androidx.annotation:annotation:1.0.0' testImplementation 'junit:junit:4.12' testImplementation "org.mockito:mockito-core:1.10.19" } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java index 026a1b30d3..b33b3fb69d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java @@ -3,7 +3,7 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.support.annotation.RequiresApi; +import androidx.annotation.RequiresApi; import com.github.mikephil.charting.animation.Easing.EasingFunction; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java index 631e313b10..2c64777a6a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java @@ -2,7 +2,7 @@ package com.github.mikephil.charting.animation; import android.animation.TimeInterpolator; -import android.support.annotation.RequiresApi; +import androidx.annotation.RequiresApi; /** * Easing options. @@ -62,7 +62,6 @@ public enum EasingOption { * @param easing EasingOption to get * @return EasingFunction */ - @SuppressWarnings("deprecation") @Deprecated public static EasingFunction getEasingFunctionFromOption(EasingOption easing) { switch (easing) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 35ec2ec1e0..718d7e2acb 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -17,7 +17,7 @@ import android.os.Build; import android.os.Environment; import android.provider.MediaStore.Images; -import android.support.annotation.RequiresApi; +import androidx.annotation.RequiresApi; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; @@ -1527,6 +1527,8 @@ public Bitmap getChartBitmap() { */ public boolean saveToPath(String title, String pathOnSD) { + + Bitmap b = getChartBitmap(); OutputStream stream = null; @@ -1642,7 +1644,18 @@ public boolean saveToGallery(String fileName, String subFolderPath, String fileD * @return returns true if saving was successful, false if not */ public boolean saveToGallery(String fileName, int quality) { - return saveToGallery(fileName, "", "MPAndroidChart-Library Save", Bitmap.CompressFormat.JPEG, quality); + return saveToGallery(fileName, "", "MPAndroidChart-Library Save", Bitmap.CompressFormat.PNG, quality); + } + + /** + * Saves the current state of the chart to the gallery as a PNG image. + * NOTE: Needs permission WRITE_EXTERNAL_STORAGE + * + * @param fileName e.g. "my_image" + * @return returns true if saving was successful, false if not + */ + public boolean saveToGallery(String fileName) { + return saveToGallery(fileName, "", "MPAndroidChart-Library Save", Bitmap.CompressFormat.PNG, 40); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java index 2381dbf868..030603f55a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java @@ -370,6 +370,7 @@ public boolean needsOffset() { /** * Returns true if autoscale restriction for axis min value is enabled */ + @Deprecated public boolean isUseAutoScaleMinRestriction( ) { return mUseAutoScaleRestrictionMin; } @@ -377,6 +378,7 @@ public boolean isUseAutoScaleMinRestriction( ) { /** * Sets autoscale restriction for axis min value as enabled/disabled */ + @Deprecated public void setUseAutoScaleMinRestriction( boolean isEnabled ) { mUseAutoScaleRestrictionMin = isEnabled; } @@ -384,6 +386,7 @@ public void setUseAutoScaleMinRestriction( boolean isEnabled ) { /** * Returns true if autoscale restriction for axis max value is enabled */ + @Deprecated public boolean isUseAutoScaleMaxRestriction() { return mUseAutoScaleRestrictionMax; } @@ -391,6 +394,7 @@ public boolean isUseAutoScaleMaxRestriction() { /** * Sets autoscale restriction for axis max value as enabled/disabled */ + @Deprecated public void setUseAutoScaleMaxRestriction( boolean isEnabled ) { mUseAutoScaleRestrictionMax = isEnabled; } @@ -402,24 +406,6 @@ public void calculate(float dataMin, float dataMax) { float min = dataMin; float max = dataMax; - // if custom, use value as is, else use data value - if( mCustomAxisMin ) { - if( mUseAutoScaleRestrictionMin ) { - min = Math.min( dataMin, mAxisMinimum ); - } else { - min = mAxisMinimum; - } - } - - if( mCustomAxisMax ) { - if( mUseAutoScaleRestrictionMax ) { - max = Math.max( max, mAxisMaximum ); - } else { - max = mAxisMaximum; - } - } - - // temporary range (before calculations) float range = Math.abs(max - min); // in case all values are equal @@ -428,13 +414,13 @@ public void calculate(float dataMin, float dataMax) { min = min - 1f; } - float bottomSpace = range / 100f * getSpaceBottom(); - this.mAxisMinimum = (min - bottomSpace); - - float topSpace = range / 100f * getSpaceTop(); - this.mAxisMaximum = (max + topSpace); + // recalculate + range = Math.abs(max - min); + + // calc extra spacing + this.mAxisMinimum = mCustomAxisMin ? this.mAxisMinimum : min - (range / 100f) * getSpaceBottom(); + this.mAxisMaximum = mCustomAxisMax ? this.mAxisMaximum : max + (range / 100f) * getSpaceTop(); - // calc actual range - this.mAxisRange = Math.abs(this.mAxisMaximum - this.mAxisMinimum); + this.mAxisRange = Math.abs(this.mAxisMinimum - this.mAxisMaximum); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java index 527617472a..d3527f924a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java @@ -45,6 +45,7 @@ public boolean onTouch(View v, MotionEvent event) { return true; // if rotation by touch is enabled + // TODO: Also check if the pie itself is being touched, rather than the entire chart area if (mChart.isRotationEnabled()) { float x = event.getX(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index 17bba048b9..d53dcd4785 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -67,6 +67,9 @@ protected float getShapeSize(float entrySize, float maxSize, float reference, bo protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { + if (dataSet.getEntryCount() < 1) + return; + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); float phaseY = mAnimator.getPhaseY(); @@ -131,7 +134,7 @@ public void drawValues(Canvas c) { IBubbleDataSet dataSet = dataSets.get(i); - if (!shouldDrawValues(dataSet)) + if (!shouldDrawValues(dataSet) || dataSet.getEntryCount() < 1) continue; // apply the text-styling defined by the DataSet diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index e4c06fe46c..991b702117 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -264,7 +264,7 @@ public void drawValues(Canvas c) { ICandleDataSet dataSet = dataSets.get(i); - if (!shouldDrawValues(dataSet)) + if (!shouldDrawValues(dataSet) || dataSet.getEntryCount() < 1) continue; // apply the text-styling defined by the DataSet diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index a0e1777569..744bf654c0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -497,12 +497,12 @@ private void generateFilledPath(final ILineDataSet dataSet, final int startIndex // create a new path Entry currentEntry = null; - Entry previousEntry = null; + Entry previousEntry = entry; for (int x = startIndex + 1; x <= endIndex; x++) { currentEntry = dataSet.getEntryForIndex(x); - if (isDrawSteppedEnabled && previousEntry != null) { + if (isDrawSteppedEnabled) { filled.lineTo(currentEntry.getX(), previousEntry.getY() * phaseY); } @@ -530,7 +530,7 @@ public void drawValues(Canvas c) { ILineDataSet dataSet = dataSets.get(i); - if (!shouldDrawValues(dataSet)) + if (!shouldDrawValues(dataSet) || dataSet.getEntryCount() < 1) continue; // apply the text-styling defined by the DataSet diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index 36a38a53c5..ccd077e55c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -49,6 +49,9 @@ public void drawData(Canvas c) { protected void drawDataSet(Canvas c, IScatterDataSet dataSet) { + if (dataSet.getEntryCount() < 1) + return; + ViewPortHandler viewPortHandler = mViewPortHandler; Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); @@ -101,7 +104,7 @@ public void drawValues(Canvas c) { IScatterDataSet dataSet = dataSets.get(i); - if (!shouldDrawValues(dataSet)) + if (!shouldDrawValues(dataSet) || dataSet.getEntryCount() < 1) continue; // apply the text-styling defined by the DataSet diff --git a/build.gradle b/build.gradle index 65ca5186cf..fb18d8fe7d 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { classpath "io.realm:realm-gradle-plugin:4.2.0" - classpath 'com.android.tools.build:gradle:3.1.2' + classpath 'com.android.tools.build:gradle:3.2.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' } } diff --git a/gradle.properties b/gradle.properties index 4a9594aeec..0c0632ee2f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,3 @@ -org.gradle.jvmargs=-Xmx2048M \ No newline at end of file +android.enableJetifier=true +android.useAndroidX=true +org.gradle.jvmargs=-Xmx2048M diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 02b0428be0..f48e20880d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Apr 25 08:04:33 CEST 2018 +#Mon Oct 22 19:51:30 MDT 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip From db7dda2b7e42060e90b7ac780fd7f60ac30d6ef3 Mon Sep 17 00:00:00 2001 From: almic Date: Sat, 27 Oct 2018 13:40:38 -0600 Subject: [PATCH 222/291] More Linting Lots of things in the example app are now marked with the 'final' modifier, and saveToGallery() is protected in classes that implement it. As well, new suppressions are in a few places. NEW text is now removed completely. Looks like this has been unused for a long time and it's just stuck around anyway. --- .../res/drawable/new_background.xml | 9 --------- MPChartExample/res/layout/list_item.xml | 19 +------------------ .../res/layout/list_item_section.xml | 16 ---------------- MPChartExample/res/values/strings.xml | 1 - .../mpchartexample/AnotherBarActivity.java | 2 +- .../mpchartexample/BarChartActivity.java | 4 ++-- .../BarChartActivityMultiDataset.java | 2 +- .../mpchartexample/BarChartActivitySinus.java | 2 +- .../BarChartPositiveNegative.java | 8 ++++---- .../mpchartexample/BubbleChartActivity.java | 2 +- .../CandleStickChartActivity.java | 2 +- .../mpchartexample/CombinedChartActivity.java | 6 +++--- .../CubicLineChartActivity.java | 2 +- .../mpchartexample/DrawChartActivity.java | 2 +- .../DynamicalAddingActivity.java | 2 +- .../HorizontalBarChartActivity.java | 4 ++-- .../InvertedLineChartActivity.java | 2 +- .../mpchartexample/LineChartActivity1.java | 2 +- .../mpchartexample/LineChartActivity2.java | 2 +- .../LineChartActivityColored.java | 2 +- .../mpchartexample/LineChartTime.java | 4 ++-- .../MultiLineChartActivity.java | 2 +- .../mpchartexample/PieChartActivity.java | 2 +- .../PiePolylineChartActivity.java | 2 +- .../mpchartexample/RadarChartActivity.java | 2 +- .../RealtimeLineChartActivity.java | 2 +- .../mpchartexample/ScatterChartActivity.java | 2 +- .../mpchartexample/StackedBarActivity.java | 2 +- .../StackedBarActivityNegative.java | 6 +++--- .../custom/DayAxisValueFormatter.java | 2 +- .../custom/MyAxisValueFormatter.java | 2 +- .../custom/MyCustomXAxisValueFormatter.java | 4 ++-- .../mpchartexample/custom/MyMarkerView.java | 2 +- .../custom/MyValueFormatter.java | 4 ++-- .../custom/RadarMarkerView.java | 4 ++-- .../mpchartexample/custom/XYMarkerView.java | 6 +++--- .../fragments/SimpleFragment.java | 2 +- .../listviewitems/BarChartItem.java | 2 +- .../listviewitems/LineChartItem.java | 2 +- .../listviewitems/PieChartItem.java | 4 ++-- .../notimportant/ContentItem.java | 5 ++--- .../mpchartexample/notimportant/DemoBase.java | 2 +- .../notimportant/MyAdapter.java | 12 ++---------- 43 files changed, 58 insertions(+), 110 deletions(-) delete mode 100644 MPChartExample/res/drawable/new_background.xml diff --git a/MPChartExample/res/drawable/new_background.xml b/MPChartExample/res/drawable/new_background.xml deleted file mode 100644 index c2d3b9cfc5..0000000000 --- a/MPChartExample/res/drawable/new_background.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/layout/list_item.xml b/MPChartExample/res/layout/list_item.xml index 2b6069b4f4..a6e5b5f3eb 100644 --- a/MPChartExample/res/layout/list_item.xml +++ b/MPChartExample/res/layout/list_item.xml @@ -23,23 +23,6 @@ android:layout_marginTop="3dp" android:text="Small Text" android:textSize="12sp" - android:layout_marginRight="10dp" - android:layout_toLeftOf="@+id/tvNew" - android:layout_toStartOf="@+id/tvNew" /> - - + android:layout_marginRight="10dp" /> diff --git a/MPChartExample/res/layout/list_item_section.xml b/MPChartExample/res/layout/list_item_section.xml index f71901d1fb..19707f1777 100644 --- a/MPChartExample/res/layout/list_item_section.xml +++ b/MPChartExample/res/layout/list_item_section.xml @@ -23,20 +23,4 @@ android:textSize="12sp" android:visibility="gone" /> - - diff --git a/MPChartExample/res/values/strings.xml b/MPChartExample/res/values/strings.xml index d81d9b1a0c..2426d92a78 100644 --- a/MPChartExample/res/values/strings.xml +++ b/MPChartExample/res/values/strings.xml @@ -50,6 +50,5 @@ - START OF SCROLLVIEW END OF SCROLLVIEW - NEW diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 5916619645..69b5a96c42 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -202,7 +202,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "AnotherBarActivity"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index fcdaae364d..865bc92ea2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -297,7 +297,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "BarChartActivity"); } @@ -307,7 +307,7 @@ public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} - private RectF onValueSelectedRectF = new RectF(); + private final RectF onValueSelectedRectF = new RectF(); @Override public void onValueSelected(Entry e, Highlight h) { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index d0c0bc453d..7f795046e3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -266,7 +266,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "BarChartActivityMultiDataset"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index d7c16df440..95443e8214 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -226,7 +226,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "BarChartActivitySinus"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index ce73317860..4fec7dd6ab 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -148,9 +148,9 @@ private void setData(List dataList) { */ private class Data { - String xAxisValue; - float yValue; - float xValue; + final String xAxisValue; + final float yValue; + final float xValue; Data(float xValue, float yValue, String xAxisValue) { this.xAxisValue = xAxisValue; @@ -162,7 +162,7 @@ private class Data { private class ValueFormatter implements IValueFormatter { - private DecimalFormat mFormat; + private final DecimalFormat mFormat; ValueFormatter() { mFormat = new DecimalFormat("######.0"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java index 098c8f242b..6288dda2be 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -226,7 +226,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "BubbleChartActivity"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java index b0b29dfbe4..ecf7167a62 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -226,7 +226,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "CandleStickChartActivity"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 7526dc1d59..0308b9a891 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -170,7 +170,7 @@ private BarData generateBarData() { return d; } - ScatterData generateScatterData() { + private ScatterData generateScatterData() { ScatterData d = new ScatterData(); @@ -189,7 +189,7 @@ ScatterData generateScatterData() { return d; } - CandleData generateCandleData() { + private CandleData generateCandleData() { CandleData d = new CandleData(); @@ -209,7 +209,7 @@ CandleData generateCandleData() { return d; } - BubbleData generateBubbleData() { + private BubbleData generateBubbleData() { BubbleData bd = new BubbleData(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 25134f71fd..f30732e6eb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -309,7 +309,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "CubicLineChartActivity"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java index 348b9ad0fc..7a32e5329c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java @@ -147,7 +147,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "DrawChartActivity"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index 501090af62..84de449283 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -236,7 +236,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "DynamicalAddingActivity"); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 6e4e9275bf..f93baaa376 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -257,7 +257,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "HorizontalBarChartActivity"); } @@ -267,7 +267,7 @@ public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} - private RectF mOnValueSelectedRectF = new RectF(); + private final RectF mOnValueSelectedRectF = new RectF(); @Override public void onValueSelected(Entry e, Highlight h) { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index cdc3188854..999d17e7b5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -263,7 +263,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "InvertedLineChartActivity"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 4a970df995..9505838810 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -430,7 +430,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "LineChartActivity1"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index c0f72d87fa..60cab539a9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -381,7 +381,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "LineChartActivity2"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index 7bb95b83ed..6a12e8f95d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -22,7 +22,7 @@ @SuppressWarnings("SameParameterValue") public class LineChartActivityColored extends DemoBase { - private LineChart[] charts = new LineChart[4]; + private final LineChart[] charts = new LineChart[4]; @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index a88842c5d0..47d0a79046 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -96,7 +96,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setGranularity(1f); // one hour xAxis.setValueFormatter(new IAxisValueFormatter() { - private SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm", Locale.ENGLISH); + private final SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm", Locale.ENGLISH); @Override public String getFormattedValue(float value, AxisBase axis) { @@ -308,7 +308,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "LineChartTime"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index c5cbd570c6..11d69d8dae 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -288,7 +288,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "MultiLineChartActivity"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 9d77dcc8b7..e22b828d56 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -267,7 +267,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "PieChartActivity"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index 80ca82cde9..ebeb43e9cf 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -262,7 +262,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "PiePolylineChartActivity"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java index c2a5eb3d0a..883eb7dfc1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java @@ -254,7 +254,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "RadarChartActivity"); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 606d750efe..e26c460ffa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -224,7 +224,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "RealtimeLineChartActivity"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index 9b441793f0..e51755ad0b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -225,7 +225,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "ScatterChartActivity"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index c98abb68aa..676e0e62b0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -248,7 +248,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "StackedBarActivity"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index b9df66981a..7af58c85ca 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -82,7 +82,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setGranularity(10f); xAxis.setValueFormatter(new IAxisValueFormatter() { - private DecimalFormat format = new DecimalFormat("###"); + private final DecimalFormat format = new DecimalFormat("###"); @Override public String getFormattedValue(float value, AxisBase axis) { @@ -226,7 +226,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void saveToGallery() { + protected void saveToGallery() { saveToGallery(chart, "StackedBarActivityNegative"); } @@ -244,7 +244,7 @@ public void onNothingSelected() { private class CustomFormatter implements IValueFormatter, IAxisValueFormatter { - private DecimalFormat mFormat; + private final DecimalFormat mFormat; CustomFormatter() { mFormat = new DecimalFormat("###"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java index a27cff89c9..ba4d860d92 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -14,7 +14,7 @@ public class DayAxisValueFormatter implements IAxisValueFormatter "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - private BarLineChartBase chart; + private final BarLineChartBase chart; public DayAxisValueFormatter(BarLineChartBase chart) { this.chart = chart; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java index e8456675a9..e7cdbfcd10 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java @@ -8,7 +8,7 @@ public class MyAxisValueFormatter implements IAxisValueFormatter { - private DecimalFormat mFormat; + private final DecimalFormat mFormat; public MyAxisValueFormatter() { mFormat = new DecimalFormat("###,###,###,##0.0"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index e9662aca78..bea4908ef2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -15,8 +15,8 @@ public class MyCustomXAxisValueFormatter implements IAxisValueFormatter { - private DecimalFormat mFormat; - private ViewPortHandler mViewPortHandler; + private final DecimalFormat mFormat; + private final ViewPortHandler mViewPortHandler; public MyCustomXAxisValueFormatter(ViewPortHandler viewPortHandler) { mViewPortHandler = viewPortHandler; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java index 25d3195cb5..2c1da9b4e9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java @@ -21,7 +21,7 @@ @SuppressLint("ViewConstructor") public class MyMarkerView extends MarkerView { - private TextView tvContent; + private final TextView tvContent; public MyMarkerView(Context context, int layoutResource) { super(context, layoutResource); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java index cbf5fd56c8..ec1c119818 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java @@ -9,8 +9,8 @@ public class MyValueFormatter implements IValueFormatter { - private DecimalFormat mFormat; - + private final DecimalFormat mFormat; + public MyValueFormatter() { mFormat = new DecimalFormat("###,###,###,##0.0"); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java index 6fb2161577..ca057a5aa3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java @@ -22,8 +22,8 @@ @SuppressLint("ViewConstructor") public class RadarMarkerView extends MarkerView { - private TextView tvContent; - private DecimalFormat format = new DecimalFormat("##0"); + private final TextView tvContent; + private final DecimalFormat format = new DecimalFormat("##0"); public RadarMarkerView(Context context, int layoutResource) { super(context, layoutResource); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java index f85f538988..51e4247d35 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java @@ -22,10 +22,10 @@ @SuppressLint("ViewConstructor") public class XYMarkerView extends MarkerView { - private TextView tvContent; - private IAxisValueFormatter xAxisValueFormatter; + private final TextView tvContent; + private final IAxisValueFormatter xAxisValueFormatter; - private DecimalFormat format; + private final DecimalFormat format; public XYMarkerView(Context context, IAxisValueFormatter xAxisValueFormatter) { super(context, R.layout.custom_marker_view); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java index 5caa290178..ab70041e60 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java @@ -30,7 +30,7 @@ import java.util.ArrayList; -@SuppressWarnings("SameParameterValue") +@SuppressWarnings({"SameParameterValue", "WeakerAccess"}) public abstract class SimpleFragment extends Fragment { private Typeface tf; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java index b49daa3be4..eeb1791fe1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java @@ -16,7 +16,7 @@ public class BarChartItem extends ChartItem { - private Typeface mTf; + private final Typeface mTf; public BarChartItem(ChartData cd, Context c) { super(cd); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java index e4c11869ac..d69b010322 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java @@ -17,7 +17,7 @@ public class LineChartItem extends ChartItem { - private Typeface mTf; + private final Typeface mTf; public LineChartItem(ChartData cd, Context c) { super(cd); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java index 0c5e56bd3b..916f8dc5e9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java @@ -21,8 +21,8 @@ public class PieChartItem extends ChartItem { - private Typeface mTf; - private SpannableString mCenterText; + private final Typeface mTf; + private final SpannableString mCenterText; public PieChartItem(ChartData cd, Context c) { super(cd); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java index 41f05afc10..a6625bae9a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java @@ -5,9 +5,8 @@ */ public class ContentItem { - String name; - String desc; - boolean isNew = false; + final String name; + final String desc; boolean isSection = false; public ContentItem(String n) { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java index ea0d1aa008..a963609aff 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java @@ -95,5 +95,5 @@ protected void saveToGallery(Chart chart, String name) { .show(); } - abstract public void saveToGallery(); + protected abstract void saveToGallery(); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java index d9fc14a609..4ddac475bc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java @@ -20,8 +20,8 @@ */ public class MyAdapter extends ArrayAdapter { - private Typeface mTypeFaceLight; - private Typeface mTypeFaceRegular; + private final Typeface mTypeFaceLight; + private final Typeface mTypeFaceRegular; public MyAdapter(Context context, List objects) { super(context, 0, objects); @@ -49,11 +49,9 @@ public View getView(int position, View convertView, @NonNull ViewGroup parent) { holder.tvName = convertView.findViewById(R.id.tvName); holder.tvDesc = convertView.findViewById(R.id.tvDesc); - holder.tvNew = convertView.findViewById(R.id.tvNew); convertView.setTag(holder); - holder.tvNew.setTypeface(mTypeFaceRegular); if (c != null && c.isSection) holder.tvName.setTypeface(mTypeFaceRegular); else @@ -63,17 +61,11 @@ public View getView(int position, View convertView, @NonNull ViewGroup parent) { holder.tvName.setText(c != null ? c.name : null); holder.tvDesc.setText(c != null ? c.desc : null); - if(c != null && c.isNew) - holder.tvNew.setVisibility(View.VISIBLE); - else - holder.tvNew.setVisibility(View.GONE); - return convertView; } private class ViewHolder { TextView tvName, tvDesc; - TextView tvNew; } } From 5030b36c86cfa2d076bb6b86fa8fcb37ce58557f Mon Sep 17 00:00:00 2001 From: almic Date: Sat, 27 Oct 2018 18:00:06 -0600 Subject: [PATCH 223/291] More Cleanup I've removed the realm examples, mainly because the library was buggy. In the previous commit, I already removed the examples from the list since they didn't add anything to the overall demonstration. I thought that if anyone wants to use realm.io with the charts, they can see the actual library. The file structure has been updated to match how Android Studio does things. Some files added way back when have been deleted as they don't do anything but cause the linter to freak out about 'unused properties' All 'build.gradle' files have been refreshed as well. Small addition to the README file to add a quick way to reach sections of the page. --- ...springsource.ide.eclipse.gradle.core.prefs | 4 - ...springsource.ide.eclipse.gradle.core.prefs | 3 - MPChartExample/build.gradle | 25 --- MPChartExample/proguard-project.txt | 20 -- MPChartExample/proguard-rules.pro | 21 ++ MPChartExample/project.properties | 15 -- .../layout/activity_bubblechart_noseekbar.xml | 11 - .../layout/activity_candlechart_noseekbar.xml | 11 - .../activity_horizontalbarchart_noseekbar.xml | 11 - .../layout/activity_piechart_noseekbar.xml | 11 - .../res/layout/activity_realm_wiki.xml | 19 -- .../activity_scatterchart_noseekbar.xml | 11 - MPChartExample/res/menu/realm.xml | 13 -- .../mpchartexample/custom/RealmDemoData.java | 173 --------------- .../mpchartexample/custom/RealmFloat.java | 28 --- .../realm/RealmBaseActivity.java | 205 ------------------ .../realm/RealmDatabaseActivityBar.java | 69 ------ .../realm/RealmDatabaseActivityBubble.java | 71 ------ .../realm/RealmDatabaseActivityCandle.java | 77 ------- .../RealmDatabaseActivityHorizontalBar.java | 74 ------- .../realm/RealmDatabaseActivityLine.java | 78 ------- .../realm/RealmDatabaseActivityPie.java | 82 ------- .../realm/RealmDatabaseActivityRadar.java | 78 ------- .../realm/RealmDatabaseActivityScatter.java | 73 ------- .../realm/RealmMainActivity.java | 133 ------------ .../realm/RealmWikiExample.java | 134 ------------ .../mpchartexample/realm/Score.java | 26 --- .../{ => src/main}/AndroidManifest.xml | 22 +- .../{ => src/main}/assets/OpenSans-Bold.ttf | Bin .../main}/assets/OpenSans-BoldItalic.ttf | Bin .../main}/assets/OpenSans-ExtraBold.ttf | Bin .../main}/assets/OpenSans-ExtraBoldItalic.ttf | Bin .../{ => src/main}/assets/OpenSans-Italic.ttf | Bin .../{ => src/main}/assets/OpenSans-Light.ttf | Bin .../main}/assets/OpenSans-LightItalic.ttf | Bin .../main}/assets/OpenSans-Regular.ttf | Bin .../main}/assets/OpenSans-Semibold.ttf | Bin .../main}/assets/OpenSans-SemiboldItalic.ttf | Bin .../{ => src/main}/assets/cosine.txt | 0 .../{ => src/main}/assets/hugecosine.txt | 0 .../{ => src/main}/assets/hugesine.txt | 0 MPChartExample/{ => src/main}/assets/n.txt | 0 .../{ => src/main}/assets/nlogn.txt | 0 .../{ => src/main}/assets/othersine.txt | 0 MPChartExample/{ => src/main}/assets/sine.txt | 0 .../{ => src/main}/assets/square.txt | 0 .../{ => src/main}/assets/stacked_bars.txt | 0 .../{ => src/main}/assets/three.txt | 0 .../{ => src/main}/ic_launcher-web.png | Bin .../mpchartexample/AnotherBarActivity.java | 0 .../mpchartexample/BarChartActivity.java | 1 - .../BarChartActivityMultiDataset.java | 1 + .../mpchartexample/BarChartActivitySinus.java | 0 .../BarChartPositiveNegative.java | 0 .../mpchartexample/BubbleChartActivity.java | 0 .../CandleStickChartActivity.java | 0 .../mpchartexample/CombinedChartActivity.java | 0 .../CubicLineChartActivity.java | 1 - .../mpchartexample/DrawChartActivity.java | 0 .../DynamicalAddingActivity.java | 0 .../mpchartexample/FilledLineActivity.java | 0 .../mpchartexample/HalfPieChartActivity.java | 0 .../HorizontalBarChartActivity.java | 1 - .../InvertedLineChartActivity.java | 6 +- .../mpchartexample/LineChartActivity1.java | 0 .../mpchartexample/LineChartActivity2.java | 1 - .../LineChartActivityColored.java | 0 .../mpchartexample/LineChartTime.java | 2 - .../ListViewBarChartActivity.java | 0 .../ListViewMultiChartActivity.java | 0 .../MultiLineChartActivity.java | 0 .../mpchartexample/PerformanceLineChart.java | 0 .../mpchartexample/PieChartActivity.java | 1 - .../PiePolylineChartActivity.java | 1 - .../mpchartexample/RadarChartActivity.java | 0 .../RealtimeLineChartActivity.java | 0 .../mpchartexample/ScatterChartActivity.java | 0 .../mpchartexample/ScrollViewActivity.java | 0 .../mpchartexample/StackedBarActivity.java | 0 .../StackedBarActivityNegative.java | 0 .../custom/CustomScatterShapeRenderer.java | 0 .../custom/DayAxisValueFormatter.java | 0 .../custom/MyAxisValueFormatter.java | 0 .../custom/MyCustomXAxisValueFormatter.java | 0 .../custom/MyFillFormatter.java | 0 .../mpchartexample/custom/MyMarkerView.java | 0 .../custom/MyValueFormatter.java | 0 .../custom/RadarMarkerView.java | 0 .../custom/StackedBarsMarkerView.java | 0 .../mpchartexample/custom/XYMarkerView.java | 0 .../custom/YearXAxisFormatter.java | 0 .../fragments/BarChartFrag.java | 0 .../fragments/ComplexityFragment.java | 0 .../fragments/PieChartFrag.java | 0 .../fragments/ScatterChartFrag.java | 0 .../fragments/SimpleChartDemo.java | 0 .../fragments/SimpleFragment.java | 0 .../fragments/SineCosineFragment.java | 0 .../listviewitems/BarChartItem.java | 0 .../listviewitems/ChartItem.java | 0 .../listviewitems/LineChartItem.java | 0 .../listviewitems/PieChartItem.java | 0 .../notimportant/ContentItem.java | 6 +- .../mpchartexample/notimportant/DemoBase.java | 0 .../notimportant/MainActivity.java | 0 .../notimportant/MyAdapter.java | 4 +- .../main}/res/anim/move_left_in_activity.xml | 0 .../main}/res/anim/move_left_out_activity.xml | 0 .../main}/res/anim/move_right_in_activity.xml | 0 .../res/anim/move_right_out_activity.xml | 0 .../main}/res/drawable-hdpi/ic_launcher.png | Bin .../{ => src/main}/res/drawable-hdpi/star.png | Bin .../main}/res/drawable-mdpi/ic_launcher.png | Bin .../main}/res/drawable-nodpi/marker2.png | Bin .../main}/res/drawable-nodpi/radar_marker.png | Bin .../main}/res/drawable-xhdpi/ic_launcher.png | Bin .../main}/res/drawable-xxhdpi/ic_launcher.png | Bin .../{ => src/main}/res/drawable/fade_red.xml | 0 .../res/layout/activity_age_distribution.xml | 0 .../res/layout/activity_awesomedesign.xml | 0 .../main}/res/layout/activity_barchart.xml | 0 .../layout/activity_barchart_noseekbar.xml | 0 .../res/layout/activity_barchart_sinus.xml | 0 .../main}/res/layout/activity_bubblechart.xml | 0 .../main}/res/layout/activity_candlechart.xml | 0 .../res/layout/activity_colored_lines.xml | 0 .../main}/res/layout/activity_combined.xml | 0 .../main}/res/layout/activity_draw_chart.xml | 0 .../layout/activity_horizontalbarchart.xml | 0 .../main}/res/layout/activity_linechart.xml | 0 .../layout/activity_linechart_noseekbar.xml | 0 .../res/layout/activity_linechart_time.xml | 0 .../res/layout/activity_listview_chart.xml | 0 .../main}/res/layout/activity_main.xml | 0 .../layout/activity_performance_linechart.xml | 0 .../main}/res/layout/activity_piechart.xml | 0 .../res/layout/activity_piechart_half.xml | 0 .../main}/res/layout/activity_radarchart.xml | 0 .../layout/activity_realtime_linechart.xml | 0 .../res/layout/activity_scatterchart.xml | 0 .../main}/res/layout/activity_scrollview.xml | 0 .../main}/res/layout/custom_marker_view.xml | 0 .../main}/res/layout/frag_simple_bar.xml | 0 .../main}/res/layout/frag_simple_line.xml | 0 .../main}/res/layout/frag_simple_pie.xml | 0 .../main}/res/layout/frag_simple_scatter.xml | 0 .../{ => src/main}/res/layout/list_item.xml | 0 .../main}/res/layout/list_item_barchart.xml | 0 .../main}/res/layout/list_item_linechart.xml | 0 .../main}/res/layout/list_item_piechart.xml | 0 .../main}/res/layout/list_item_section.xml | 0 .../main}/res/layout/radar_markerview.xml | 0 .../{ => src/main}/res/menu/bar.xml | 0 .../{ => src/main}/res/menu/bubble.xml | 0 .../{ => src/main}/res/menu/candle.xml | 0 .../{ => src/main}/res/menu/combined.xml | 0 .../{ => src/main}/res/menu/draw.xml | 0 .../{ => src/main}/res/menu/dynamical.xml | 0 .../{ => src/main}/res/menu/line.xml | 0 .../{ => src/main}/res/menu/main.xml | 0 .../{ => src/main}/res/menu/only_github.xml | 0 .../{ => src/main}/res/menu/pie.xml | 0 .../{ => src/main}/res/menu/radar.xml | 0 .../{ => src/main}/res/menu/realtime.xml | 0 .../{ => src/main}/res/menu/scatter.xml | 0 .../{ => src/main}/res/values/strings.xml | 1 - .../{ => src/main}/res/values/styles.xml | 0 ...springsource.ide.eclipse.gradle.core.prefs | 4 - MPChartLib/build.gradle | 26 +-- MPChartLib/proguard-project.txt | 20 -- MPChartLib/project.properties | 15 -- README.md | 55 +++-- build.gradle | 3 +- 173 files changed, 71 insertions(+), 1576 deletions(-) delete mode 100644 .settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs delete mode 100644 MPChartExample/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs delete mode 100644 MPChartExample/proguard-project.txt create mode 100644 MPChartExample/proguard-rules.pro delete mode 100644 MPChartExample/project.properties delete mode 100644 MPChartExample/res/layout/activity_bubblechart_noseekbar.xml delete mode 100644 MPChartExample/res/layout/activity_candlechart_noseekbar.xml delete mode 100644 MPChartExample/res/layout/activity_horizontalbarchart_noseekbar.xml delete mode 100644 MPChartExample/res/layout/activity_piechart_noseekbar.xml delete mode 100644 MPChartExample/res/layout/activity_realm_wiki.xml delete mode 100644 MPChartExample/res/layout/activity_scatterchart_noseekbar.xml delete mode 100644 MPChartExample/res/menu/realm.xml delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java rename MPChartExample/{ => src/main}/AndroidManifest.xml (68%) rename MPChartExample/{ => src/main}/assets/OpenSans-Bold.ttf (100%) rename MPChartExample/{ => src/main}/assets/OpenSans-BoldItalic.ttf (100%) rename MPChartExample/{ => src/main}/assets/OpenSans-ExtraBold.ttf (100%) rename MPChartExample/{ => src/main}/assets/OpenSans-ExtraBoldItalic.ttf (100%) rename MPChartExample/{ => src/main}/assets/OpenSans-Italic.ttf (100%) rename MPChartExample/{ => src/main}/assets/OpenSans-Light.ttf (100%) rename MPChartExample/{ => src/main}/assets/OpenSans-LightItalic.ttf (100%) rename MPChartExample/{ => src/main}/assets/OpenSans-Regular.ttf (100%) rename MPChartExample/{ => src/main}/assets/OpenSans-Semibold.ttf (100%) rename MPChartExample/{ => src/main}/assets/OpenSans-SemiboldItalic.ttf (100%) rename MPChartExample/{ => src/main}/assets/cosine.txt (100%) rename MPChartExample/{ => src/main}/assets/hugecosine.txt (100%) rename MPChartExample/{ => src/main}/assets/hugesine.txt (100%) rename MPChartExample/{ => src/main}/assets/n.txt (100%) rename MPChartExample/{ => src/main}/assets/nlogn.txt (100%) rename MPChartExample/{ => src/main}/assets/othersine.txt (100%) rename MPChartExample/{ => src/main}/assets/sine.txt (100%) rename MPChartExample/{ => src/main}/assets/square.txt (100%) rename MPChartExample/{ => src/main}/assets/stacked_bars.txt (100%) rename MPChartExample/{ => src/main}/assets/three.txt (100%) rename MPChartExample/{ => src/main}/ic_launcher-web.png (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/BarChartActivity.java (99%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java (99%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java (99%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java (99%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java (98%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java (99%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/LineChartTime.java (99%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/PieChartActivity.java (99%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java (99%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java (73%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java (100%) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java (93%) rename MPChartExample/{ => src/main}/res/anim/move_left_in_activity.xml (100%) rename MPChartExample/{ => src/main}/res/anim/move_left_out_activity.xml (100%) rename MPChartExample/{ => src/main}/res/anim/move_right_in_activity.xml (100%) rename MPChartExample/{ => src/main}/res/anim/move_right_out_activity.xml (100%) rename MPChartExample/{ => src/main}/res/drawable-hdpi/ic_launcher.png (100%) rename MPChartExample/{ => src/main}/res/drawable-hdpi/star.png (100%) rename MPChartExample/{ => src/main}/res/drawable-mdpi/ic_launcher.png (100%) rename MPChartExample/{ => src/main}/res/drawable-nodpi/marker2.png (100%) rename MPChartExample/{ => src/main}/res/drawable-nodpi/radar_marker.png (100%) rename MPChartExample/{ => src/main}/res/drawable-xhdpi/ic_launcher.png (100%) rename MPChartExample/{ => src/main}/res/drawable-xxhdpi/ic_launcher.png (100%) rename MPChartExample/{ => src/main}/res/drawable/fade_red.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_age_distribution.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_awesomedesign.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_barchart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_barchart_noseekbar.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_barchart_sinus.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_bubblechart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_candlechart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_colored_lines.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_combined.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_draw_chart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_horizontalbarchart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_linechart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_linechart_noseekbar.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_linechart_time.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_listview_chart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_main.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_performance_linechart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_piechart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_piechart_half.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_radarchart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_realtime_linechart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_scatterchart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/activity_scrollview.xml (100%) rename MPChartExample/{ => src/main}/res/layout/custom_marker_view.xml (100%) rename MPChartExample/{ => src/main}/res/layout/frag_simple_bar.xml (100%) rename MPChartExample/{ => src/main}/res/layout/frag_simple_line.xml (100%) rename MPChartExample/{ => src/main}/res/layout/frag_simple_pie.xml (100%) rename MPChartExample/{ => src/main}/res/layout/frag_simple_scatter.xml (100%) rename MPChartExample/{ => src/main}/res/layout/list_item.xml (100%) rename MPChartExample/{ => src/main}/res/layout/list_item_barchart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/list_item_linechart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/list_item_piechart.xml (100%) rename MPChartExample/{ => src/main}/res/layout/list_item_section.xml (100%) rename MPChartExample/{ => src/main}/res/layout/radar_markerview.xml (100%) rename MPChartExample/{ => src/main}/res/menu/bar.xml (100%) rename MPChartExample/{ => src/main}/res/menu/bubble.xml (100%) rename MPChartExample/{ => src/main}/res/menu/candle.xml (100%) rename MPChartExample/{ => src/main}/res/menu/combined.xml (100%) rename MPChartExample/{ => src/main}/res/menu/draw.xml (100%) rename MPChartExample/{ => src/main}/res/menu/dynamical.xml (100%) rename MPChartExample/{ => src/main}/res/menu/line.xml (100%) rename MPChartExample/{ => src/main}/res/menu/main.xml (100%) rename MPChartExample/{ => src/main}/res/menu/only_github.xml (100%) rename MPChartExample/{ => src/main}/res/menu/pie.xml (100%) rename MPChartExample/{ => src/main}/res/menu/radar.xml (100%) rename MPChartExample/{ => src/main}/res/menu/realtime.xml (100%) rename MPChartExample/{ => src/main}/res/menu/scatter.xml (100%) rename MPChartExample/{ => src/main}/res/values/strings.xml (97%) rename MPChartExample/{ => src/main}/res/values/styles.xml (100%) delete mode 100644 MPChartLib/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs delete mode 100644 MPChartLib/proguard-project.txt delete mode 100644 MPChartLib/project.properties diff --git a/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs b/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs deleted file mode 100644 index 9383f623c6..0000000000 --- a/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -#org.springsource.ide.eclipse.gradle.core.preferences.GradleProjectPreferences -#Mon Jan 18 23:02:46 CET 2016 -build.family.org.gradle.tooling.model.eclipse.HierarchicalEclipseProject=;MPChartExample;MPChartLib; -org.springsource.ide.eclipse.gradle.rootprojectloc= diff --git a/MPChartExample/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs b/MPChartExample/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs deleted file mode 100644 index be975eb2db..0000000000 --- a/MPChartExample/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs +++ /dev/null @@ -1,3 +0,0 @@ -#org.springsource.ide.eclipse.gradle.core.preferences.GradleProjectPreferences -#Mon Jan 18 23:02:46 CET 2016 -org.springsource.ide.eclipse.gradle.rootprojectloc=.. diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 2856bc0109..647aca2b51 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'com.android.application' -apply plugin: 'realm-android' android { compileSdkVersion 28 @@ -10,15 +9,6 @@ android { versionCode 56 versionName '3.0.3' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - - sourceSets { - main { - java.srcDirs = ['src'] - res.srcDirs = ['res'] - assets.srcDirs = ['assets'] - manifest.srcFile 'AndroidManifest.xml' - } - } } buildTypes { @@ -27,25 +17,10 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - - lintOptions { - abortOnError false - } } dependencies { implementation "androidx.appcompat:appcompat:1.0.0" implementation 'com.google.android.material:material:1.0.0' - //implementation "androidx.legacy:legacy-support-v4:1.0.0" - //implementation "androidx.legacy:legacy-support-v13:1.0.0" - //compile project(':MPChartLib-Realm') // clone "https://github.com/PhilJay/MPAndroidChart-Realm" to get this or uncomment the gradle dependency below: - implementation 'com.github.PhilJay:MPAndroidChart-Realm:v2.0.2@aar' implementation project(':MPChartLib') } - -repositories { - maven { url "https://jitpack.io/" } - maven { // this is for realm-db - url 'http://oss.jfrog.org/artifactory/oss-snapshot-local' - } -} diff --git a/MPChartExample/proguard-project.txt b/MPChartExample/proguard-project.txt deleted file mode 100644 index f2fe1559a2..0000000000 --- a/MPChartExample/proguard-project.txt +++ /dev/null @@ -1,20 +0,0 @@ -# To enable ProGuard in your project, edit project.properties -# to define the proguard.config property as described in that file. -# -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in ${sdk.dir}/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/MPChartExample/proguard-rules.pro b/MPChartExample/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/MPChartExample/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/MPChartExample/project.properties b/MPChartExample/project.properties deleted file mode 100644 index b32d807be6..0000000000 --- a/MPChartExample/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-23 -android.library.reference.1=../MPChartLib diff --git a/MPChartExample/res/layout/activity_bubblechart_noseekbar.xml b/MPChartExample/res/layout/activity_bubblechart_noseekbar.xml deleted file mode 100644 index 6251c70e74..0000000000 --- a/MPChartExample/res/layout/activity_bubblechart_noseekbar.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/MPChartExample/res/layout/activity_candlechart_noseekbar.xml b/MPChartExample/res/layout/activity_candlechart_noseekbar.xml deleted file mode 100644 index 3f726c6673..0000000000 --- a/MPChartExample/res/layout/activity_candlechart_noseekbar.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/MPChartExample/res/layout/activity_horizontalbarchart_noseekbar.xml b/MPChartExample/res/layout/activity_horizontalbarchart_noseekbar.xml deleted file mode 100644 index c571afc967..0000000000 --- a/MPChartExample/res/layout/activity_horizontalbarchart_noseekbar.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/MPChartExample/res/layout/activity_piechart_noseekbar.xml b/MPChartExample/res/layout/activity_piechart_noseekbar.xml deleted file mode 100644 index a92eec3c48..0000000000 --- a/MPChartExample/res/layout/activity_piechart_noseekbar.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/MPChartExample/res/layout/activity_realm_wiki.xml b/MPChartExample/res/layout/activity_realm_wiki.xml deleted file mode 100644 index a9ba53fa6f..0000000000 --- a/MPChartExample/res/layout/activity_realm_wiki.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - diff --git a/MPChartExample/res/layout/activity_scatterchart_noseekbar.xml b/MPChartExample/res/layout/activity_scatterchart_noseekbar.xml deleted file mode 100644 index c0ea204e93..0000000000 --- a/MPChartExample/res/layout/activity_scatterchart_noseekbar.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/MPChartExample/res/menu/realm.xml b/MPChartExample/res/menu/realm.xml deleted file mode 100644 index ce149bef91..0000000000 --- a/MPChartExample/res/menu/realm.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java deleted file mode 100644 index 7a8584f8ef..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.custom; - - -import io.realm.RealmList; -import io.realm.RealmObject; - -/** - * Demo class that encapsulates data stored in realm.io database. - * This class represents data suitable for all chart-types. - */ -@SuppressWarnings("unused") -public class RealmDemoData extends RealmObject { - - private float yValue; - private float xValue; - - private float open, close, high, low; - - private float bubbleSize; - - private RealmList stackValues; - - private String someStringField; - - /** - * label for pie entries - */ - private String label; - - public RealmDemoData() {} - - public RealmDemoData(float xValue, float yValue) { - this.xValue = xValue; - this.yValue = yValue; - } - - /** - * Constructor for stacked bars. - * - * @param xValue x position for bars - * @param stackValues values of bars in the stack - */ - public RealmDemoData(float xValue, float[] stackValues) { - this.xValue = xValue; - this.stackValues = new RealmList<>(); - - for (float val : stackValues) { - this.stackValues.add(new RealmFloat(val)); - } - } - - /** - * Constructor for candles. - * - * @param xValue x position of candle - * @param high high value for candle - * @param low low value for candle - * @param open open value for candle - * @param close close value for candle - */ - public RealmDemoData(float xValue, float high, float low, float open, float close) { - this.yValue = (high + low) / 2f; - this.high = high; - this.low = low; - this.open = open; - this.close = close; - this.xValue = xValue; - } - - /** - * Constructor for bubbles. - * - * @param xValue x position of bubble - * @param yValue y position of bubble - * @param bubbleSize size of bubble - */ - public RealmDemoData(float xValue, float yValue, float bubbleSize) { - this.xValue = xValue; - this.yValue = yValue; - this.bubbleSize = bubbleSize; - } - - /** - * Constructor for pie chart. - * - * @param yValue size of pie slice - * @param label label for pie slice - */ - public RealmDemoData(float yValue, String label) { - this.yValue = yValue; - this.label = label; - } - - public float getYValue() { - return yValue; - } - - public void setYValue(float yValue) { - this.yValue = yValue; - } - - public float getXValue() { - return xValue; - } - - public void setXValue(float xValue) { - this.xValue = xValue; - } - - public RealmList getStackValues() { - return stackValues; - } - - public void setStackValues(RealmList stackValues) { - this.stackValues = stackValues; - } - - public float getOpen() { - return open; - } - - public void setOpen(float open) { - this.open = open; - } - - public float getClose() { - return close; - } - - public void setClose(float close) { - this.close = close; - } - - public float getHigh() { - return high; - } - - public void setHigh(float high) { - this.high = high; - } - - public float getLow() { - return low; - } - - public void setLow(float low) { - this.low = low; - } - - public float getBubbleSize() { - return bubbleSize; - } - - public void setBubbleSize(float bubbleSize) { - this.bubbleSize = bubbleSize; - } - - public String getSomeStringField() { - return someStringField; - } - - public void setSomeStringField(String someStringField) { - this.someStringField = someStringField; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java deleted file mode 100644 index a8310dc9b5..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.custom; - -import io.realm.RealmObject; - -/** - * Created by Philipp Jahoda on 09/11/15. - */ -@SuppressWarnings("unused") -public class RealmFloat extends RealmObject { - - private float floatValue; - - public RealmFloat() { - - } - - public RealmFloat(float floatValue) { - this.floatValue = floatValue; - } - - public float getFloatValue() { - return floatValue; - } - - public void setFloatValue(float value) { - this.floatValue = value; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java deleted file mode 100644 index 690eb6cf3a..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.os.Bundle; - -import com.github.mikephil.charting.charts.BarLineChartBase; -import com.github.mikephil.charting.charts.Chart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.formatter.PercentFormatter; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import io.realm.Realm; -import io.realm.RealmConfiguration; - -/** - * Created by Philipp Jahoda on 05/11/15. - */ -@SuppressWarnings("SameParameterValue") -public abstract class RealmBaseActivity extends DemoBase { - - protected Realm mRealm; - - protected Typeface mTf; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setTitle("Realm.io Examples"); - } - - protected void setup(Chart chart) { - - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - // no description text - chart.getDescription().setEnabled(false); - - // enable touch gestures - chart.setTouchEnabled(true); - - if (chart instanceof BarLineChartBase) { - - BarLineChartBase mChart = (BarLineChartBase) chart; - - mChart.setDrawGridBackground(false); - - // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - - // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(false); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.removeAllLimitLines(); // reset all limit lines to avoid overlapping lines - leftAxis.setTypeface(mTf); - leftAxis.setTextSize(8f); - leftAxis.setTextColor(Color.DKGRAY); - leftAxis.setValueFormatter(new PercentFormatter()); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setTypeface(mTf); - xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); - xAxis.setTextSize(8f); - xAxis.setTextColor(Color.DKGRAY); - - mChart.getAxisRight().setEnabled(false); - } - } - - protected void styleData(ChartData data) { - data.setValueTypeface(mTf); - data.setValueTextSize(8f); - data.setValueTextColor(Color.DKGRAY); - data.setValueFormatter(new PercentFormatter()); - } - - @Override - protected void onResume() { - super.onResume(); - - // Create a RealmConfiguration that saves the Realm file in the app's "files" directory. - RealmConfiguration realmConfig = new RealmConfiguration.Builder().build(); - Realm.setDefaultConfiguration(realmConfig); - - mRealm = Realm.getDefaultInstance(); - } - - @Override - protected void onPause() { - super.onPause(); - mRealm.close(); - } - - protected void writeToDB(int objectCount) { - - mRealm.beginTransaction(); - - mRealm.delete(RealmDemoData.class); - - for (int i = 0; i < objectCount; i++) { - - float value = 40f + (float) (Math.random() * 60f); - - RealmDemoData d = new RealmDemoData(i, value); - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } - - protected void writeToDBStack(int objectCount) { - - mRealm.beginTransaction(); - - mRealm.delete(RealmDemoData.class); - - for (int i = 0; i < objectCount; i++) { - - float val1 = 34f + (float) (Math.random() * 12.0f); - float val2 = 34f + (float) (Math.random() * 12.0f); - float[] stack = new float[]{val1, val2, 100 - val1 - val2}; - - RealmDemoData d = new RealmDemoData(i, stack); - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } - - protected void writeToDBCandle(int objectCount) { - - mRealm.beginTransaction(); - - mRealm.delete(RealmDemoData.class); - - for (int i = 0; i < objectCount; i++) { - - float val = (float) (Math.random() * 40) + 50f; - - float high = (float) (Math.random() * 9) + 8f; - float low = (float) (Math.random() * 9) + 8f; - - float open = (float) (Math.random() * 6) + 1f; - float close = (float) (Math.random() * 6) + 1f; - - boolean even = i % 2 == 0; - - RealmDemoData d = new RealmDemoData(i, val + high, val - low, even ? val + open : val - open, - even ? val - close : val + close); - - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } - - protected void writeToDBBubble(int objectCount) { - - mRealm.beginTransaction(); - - mRealm.delete(RealmDemoData.class); - - for (int i = 0; i < objectCount; i++) { - - float value = 30f + (float) (Math.random() * 100.0); - float size = 15f + (float) (Math.random() * 20.0); - - RealmDemoData d = new RealmDemoData(i, value, size); - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } - - protected void writeToDBPie() { - - mRealm.beginTransaction(); - - mRealm.delete(RealmDemoData.class); - - float value1 = 15f + (float) (Math.random() * 8f); - float value2 = 15f + (float) (Math.random() * 8f); - float value3 = 15f + (float) (Math.random() * 8f); - float value4 = 15f + (float) (Math.random() * 8f); - float value5 = 100f - value1 - value2 - value3 - value4; - - float[] values = new float[]{value1, value2, value3, value4, value5}; - String[] labels = new String[]{"iOS", "Android", "WP 10", "BlackBerry", "Other"}; - - for (int i = 0; i < values.length; i++) { - RealmDemoData d = new RealmDemoData(values[i], labels[i]); - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } - - @Override - public void saveToGallery() { /* Intentionally left empty */ } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java deleted file mode 100644 index f7cba47340..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityBar extends RealmBaseActivity { - - private BarChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_barchart_noseekbar); - - mChart = findViewById(R.id.chart1); - setup(mChart); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDB(20); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - //RealmBarDataSet set = new RealmBarDataSet<>(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet<>(result, "xValue", "yValue"); // stacked entries - set.setColors(ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")); - set.setLabel("Realm BarDataSet"); - - ArrayList dataSets = new ArrayList<>(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - BarData data = new BarData(dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.setFitBars(true); - mChart.animateY(1400, Easing.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java deleted file mode 100644 index 3493ade94f..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.BubbleChart; -import com.github.mikephil.charting.data.BubbleData; -import com.github.mikephil.charting.data.realm.implementation.RealmBubbleDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityBubble extends RealmBaseActivity { - - private BubbleChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_bubblechart_noseekbar); - - mChart = findViewById(R.id.chart1); - setup(mChart); - - mChart.getXAxis().setDrawGridLines(false); - mChart.getAxisLeft().setDrawGridLines(false); - mChart.setPinchZoom(true); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDBBubble(10); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - RealmBubbleDataSet set = new RealmBubbleDataSet<>(result, "xValue", "yValue", "bubbleSize"); - set.setLabel("Realm BubbleDataSet"); - set.setColors(ColorTemplate.COLORFUL_COLORS, 110); - - ArrayList dataSets = new ArrayList<>(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - BubbleData data = new BubbleData(dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java deleted file mode 100644 index 49d8903c6e..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.graphics.Paint; -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.CandleStickChart; -import com.github.mikephil.charting.data.CandleData; -import com.github.mikephil.charting.data.realm.implementation.RealmCandleDataSet; -import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityCandle extends RealmBaseActivity { - - private CandleStickChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_candlechart_noseekbar); - - mChart = findViewById(R.id.chart1); - setup(mChart); - - mChart.getAxisLeft().setDrawGridLines(false); - mChart.getXAxis().setDrawGridLines(false); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDBCandle(50); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - RealmCandleDataSet set = new RealmCandleDataSet<>(result, "xValue", "high", "low", "open", "close"); - set.setLabel("Realm CandleDataSet"); - set.setShadowColor(Color.DKGRAY); - set.setShadowWidth(0.7f); - set.setDecreasingColor(Color.RED); - set.setDecreasingPaintStyle(Paint.Style.FILL); - set.setIncreasingColor(Color.rgb(122, 242, 84)); - set.setIncreasingPaintStyle(Paint.Style.STROKE); - set.setNeutralColor(Color.BLUE); - - ArrayList dataSets = new ArrayList<>(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - CandleData data = new CandleData(dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java deleted file mode 100644 index 680ee1e83c..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.HorizontalBarChart; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityHorizontalBar extends RealmBaseActivity { - - private HorizontalBarChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_horizontalbarchart_noseekbar); - - mChart = findViewById(R.id.chart1); - setup(mChart); - - mChart.getAxisLeft().setAxisMinimum(0f); - mChart.setDrawValueAboveBar(false); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDBStack(50); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - //RealmBarDataSet set = new RealmBarDataSet<>(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet<>(result, "xValue", "stackValues", "floatValue"); // stacked entries - set.setColors(ColorTemplate.rgb("#8BC34A"), ColorTemplate.rgb("#FFC107"), ColorTemplate.rgb("#9E9E9E")); - set.setLabel("Mobile OS distribution"); - set.setStackLabels(new String[]{"iOS", "Android", "Other"}); - - ArrayList dataSets = new ArrayList<>(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - BarData data = new BarData(dataSets); - styleData(data); - data.setValueTextColor(Color.WHITE); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java deleted file mode 100644 index c0bbd3caf6..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityLine extends RealmBaseActivity { - - private LineChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_linechart_noseekbar); - - mChart = findViewById(R.id.chart1); - setup(mChart); - - mChart.getAxisLeft().setAxisMaximum(150f); - mChart.getAxisLeft().setAxisMinimum(0f); - mChart.getAxisLeft().setDrawGridLines(false); - mChart.getXAxis().setDrawGridLines(false); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDB(40); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - RealmLineDataSet set = new RealmLineDataSet<>(result, "xValue", "yValue"); - set.setMode(LineDataSet.Mode.CUBIC_BEZIER); - set.setLabel("Realm LineDataSet"); - set.setDrawCircleHole(false); - set.setColor(ColorTemplate.rgb("#FF5722")); - set.setCircleColor(ColorTemplate.rgb("#FF5722")); - set.setLineWidth(1.8f); - set.setCircleRadius(3.6f); - - ArrayList dataSets = new ArrayList<>(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - LineData data = new LineData(dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java deleted file mode 100644 index 46af4cda31..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.graphics.Typeface; -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.view.WindowManager; - -import com.github.mikephil.charting.charts.PieChart; -import com.github.mikephil.charting.data.PieData; -import com.github.mikephil.charting.data.realm.implementation.RealmPieDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityPie extends RealmBaseActivity { - - private PieChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_piechart_noseekbar); - - mChart = findViewById(R.id.chart1); - setup(mChart); - - mChart.setCenterText(generateCenterSpannableText()); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDBPie(); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - RealmPieDataSet set = new RealmPieDataSet<>(result, "yValue", "label"); - set.setColors(ColorTemplate.VORDIPLOM_COLORS); - set.setLabel("Example market share"); - set.setSliceSpace(2); - - // create a data object with the dataset list - PieData data = new PieData(set); - styleData(data); - data.setValueTextColor(Color.WHITE); - data.setValueTextSize(12f); - - // set data - mChart.setData(data); - mChart.animateY(1400); - } - - private SpannableString generateCenterSpannableText() { - - SpannableString s = new SpannableString("Realm.io\nmobile database"); - s.setSpan(new ForegroundColorSpan(Color.rgb(240, 115, 126)), 0, 8, 0); - s.setSpan(new RelativeSizeSpan(2.2f), 0, 8, 0); - s.setSpan(new StyleSpan(Typeface.ITALIC), 9, s.length(), 0); - s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), 9, s.length(), 0); - s.setSpan(new RelativeSizeSpan(0.85f), 9, s.length(), 0); - return s; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java deleted file mode 100644 index faef4e28bc..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.data.RadarData; -import com.github.mikephil.charting.data.realm.implementation.RealmRadarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityRadar extends RealmBaseActivity { - - private RadarChart chart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_radarchart); - - chart = findViewById(R.id.chart1); - setup(chart); - - chart.getYAxis().setEnabled(false); - chart.getXAxis().setEnabled(false); - chart.setWebAlpha(180); - chart.setWebColorInner(Color.DKGRAY); - chart.setWebColor(Color.GRAY); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDB(7); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - //RealmBarDataSet set = new RealmBarDataSet<>(result, "stackValues", "xIndex"); // normal entries - RealmRadarDataSet set = new RealmRadarDataSet<>(result, "yValue"); // stacked entries - set.setLabel("Realm RadarDataSet"); - set.setDrawFilled(true); - set.setColor(ColorTemplate.rgb("#009688")); - set.setFillColor(ColorTemplate.rgb("#009688")); - set.setFillAlpha(130); - set.setLineWidth(2f); - - ArrayList dataSets = new ArrayList<>(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - RadarData data = new RadarData(dataSets); - styleData(data); - - // set data - chart.setData(data); - chart.animateY(1400); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java deleted file mode 100644 index 01e0151933..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.ScatterChart; -import com.github.mikephil.charting.data.ScatterData; -import com.github.mikephil.charting.data.realm.implementation.RealmScatterDataSet; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityScatter extends RealmBaseActivity { - - private ScatterChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_scatterchart_noseekbar); - - mChart = findViewById(R.id.chart1); - setup(mChart); - - mChart.getAxisLeft().setDrawGridLines(false); - mChart.getXAxis().setDrawGridLines(false); - mChart.setPinchZoom(true); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDB(45); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.where(RealmDemoData.class).findAll(); - - RealmScatterDataSet set = new RealmScatterDataSet<>(result, "xValue", "yValue"); - set.setLabel("Realm ScatterDataSet"); - set.setScatterShapeSize(9f); - set.setColor(ColorTemplate.rgb("#CDDC39")); - set.setScatterShape(ScatterChart.ScatterShape.CIRCLE); - - ArrayList dataSets = new ArrayList<>(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - ScatterData data = new ScatterData(dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java deleted file mode 100644 index 674cef458e..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.WindowManager; -import android.widget.AdapterView; -import android.widget.ListView; - -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.notimportant.ContentItem; -import com.xxmassdeveloper.mpchartexample.notimportant.MyAdapter; - -import java.util.ArrayList; - -import io.realm.Realm; -import io.realm.RealmConfiguration; - -/** - * Created by Philipp Jahoda on 07/12/15. - */ -public class RealmMainActivity extends Activity implements AdapterView.OnItemClickListener { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_main); - - setTitle("Realm.io Examples"); - - ArrayList objects = new ArrayList<>(); - - objects.add(new ContentItem("Line Chart", "Creating a LineChart with Realm.io database")); - objects.add(new ContentItem("Bar Chart", - "Creating a BarChart with Realm.io database")); - objects.add(new ContentItem("Horizontal Bar Chart", - "Creating a HorizontalBarChart with Realm.io database")); - objects.add(new ContentItem("Scatter Chart", - "Creating a ScatterChart with Realm.io database")); - objects.add(new ContentItem("Candle Stick Chart", "Creating a CandleStickChart with Realm.io database")); - objects.add(new ContentItem("Bubble Chart", "Creating a BubbleChart with Realm.io database")); - objects.add(new ContentItem("Pie Chart", "Creating a PieChart with Realm.io database")); - objects.add(new ContentItem("Radar Chart", "Creating a RadarChart with Realm.io database")); - objects.add(new ContentItem("Realm Wiki", "This is the code related to the wiki entry about realm.io on the MPAndroidChart github page.")); - - MyAdapter adapter = new MyAdapter(this, objects); - - ListView lv = findViewById(R.id.listView1); - lv.setAdapter(adapter); - - lv.setOnItemClickListener(this); - - Realm.init(this); - - // Create a RealmConfiguration that saves the Realm file in the app's "files" directory. - RealmConfiguration realmConfig = new RealmConfiguration.Builder().build(); - Realm.setDefaultConfiguration(realmConfig); - - Realm realm = Realm.getDefaultInstance(); - realm.beginTransaction(); - realm.deleteAll(); - realm.commitTransaction(); - } - - @Override - public void onItemClick(AdapterView av, View v, int pos, long arg3) { - - Intent i; - - switch (pos) { - case 0: - i = new Intent(this, RealmDatabaseActivityLine.class); - startActivity(i); - break; - case 1: - i = new Intent(this, RealmDatabaseActivityBar.class); - startActivity(i); - break; - case 2: - i = new Intent(this, RealmDatabaseActivityHorizontalBar.class); - startActivity(i); - break; - case 3: - i = new Intent(this, RealmDatabaseActivityScatter.class); - startActivity(i); - break; - case 4: - i = new Intent(this, RealmDatabaseActivityCandle.class); - startActivity(i); - break; - case 5: - i = new Intent(this, RealmDatabaseActivityBubble.class); - startActivity(i); - break; - case 6: - i = new Intent(this, RealmDatabaseActivityPie.class); - startActivity(i); - break; - case 7: - i = new Intent(this, RealmDatabaseActivityRadar.class); - startActivity(i); - break; - case 8: - i = new Intent(this, RealmWikiExample.class); - startActivity(i); - break; - } - - overridePendingTransition(R.anim.move_right_in_activity, R.anim.move_left_out_activity); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.realm, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse("https://realm.io/")); - startActivity(i); - - return super.onOptionsItemSelected(item); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java deleted file mode 100644 index 35212728cd..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; -import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 18/12/15. - */ -public class RealmWikiExample extends RealmBaseActivity { - - private LineChart lineChart; - private BarChart barChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_realm_wiki); - - lineChart = findViewById(R.id.lineChart); - barChart = findViewById(R.id.barChart); - setup(lineChart); - setup(barChart); - - lineChart.setExtraBottomOffset(5f); - barChart.setExtraBottomOffset(5f); - - lineChart.getAxisLeft().setDrawGridLines(false); - lineChart.getXAxis().setDrawGridLines(false); - lineChart.getXAxis().setLabelCount(5); - lineChart.getXAxis().setGranularity(1f); - barChart.getAxisLeft().setDrawGridLines(false); - barChart.getXAxis().setDrawGridLines(false); - barChart.getXAxis().setLabelCount(5); - barChart.getXAxis().setGranularity(1f); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - mRealm.beginTransaction(); - - // write some demo-data into the realm.io database - Score score1 = new Score(100f, 0f, "Peter"); - mRealm.copyToRealm(score1); - Score score2 = new Score(110f, 1f, "Lisa"); - mRealm.copyToRealm(score2); - Score score3 = new Score(130f, 2f, "Dennis"); - mRealm.copyToRealm(score3); - Score score4 = new Score(70f, 3f, "Luke"); - mRealm.copyToRealm(score4); - Score score5 = new Score(80f, 4f, "Sarah"); - mRealm.copyToRealm(score5); - - mRealm.commitTransaction(); - - // add data to the chart - setData(); - } - - private void setData() { - - // LINE-CHART - final RealmResults results = mRealm.where(Score.class).findAll(); - - - IAxisValueFormatter formatter = new IAxisValueFormatter() { - @Override - public String getFormattedValue(float value, AxisBase axis) { - Score result = results.get((int) value); - return result != null ? result.playerName : ""; - } - }; - - lineChart.getXAxis().setValueFormatter(formatter); - barChart.getXAxis().setValueFormatter(formatter); - - RealmLineDataSet lineDataSet = new RealmLineDataSet<>(results, "scoreNr", "totalScore"); - lineDataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER); - lineDataSet.setLabel("Result Scores"); - lineDataSet.setDrawCircleHole(false); - lineDataSet.setColor(ColorTemplate.rgb("#FF5722")); - lineDataSet.setCircleColor(ColorTemplate.rgb("#FF5722")); - lineDataSet.setLineWidth(1.8f); - lineDataSet.setCircleRadius(3.6f); - - ArrayList dataSets = new ArrayList<>(); - dataSets.add(lineDataSet); - - LineData lineData = new LineData(dataSets); - styleData(lineData); - - // set data - lineChart.setData(lineData); - lineChart.animateY(1400, Easing.EaseInOutQuart); - - - // BAR-CHART - RealmBarDataSet barDataSet = new RealmBarDataSet<>(results, "scoreNr", "totalScore"); - barDataSet.setColors(ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")); - barDataSet.setLabel("Realm BarDataSet"); - - ArrayList barDataSets = new ArrayList<>(); - barDataSets.add(barDataSet); - - BarData barData = new BarData(barDataSets); - styleData(barData); - - barChart.setData(barData); - barChart.setFitBars(true); - barChart.animateY(1400, Easing.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java deleted file mode 100644 index 0d1f806616..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - - -import io.realm.RealmObject; - -/** - * our data object - */ -@SuppressWarnings({"WeakerAccess", "unused"}) -public class Score extends RealmObject { - - public float totalScore; - - public float scoreNr; - - public String playerName; - - public Score() {} - - public Score(float totalScore, float scoreNr, String playerName) { - this.scoreNr = scoreNr; - this.playerName = playerName; - this.totalScore = totalScore; - } - -} diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/src/main/AndroidManifest.xml similarity index 68% rename from MPChartExample/AndroidManifest.xml rename to MPChartExample/src/main/AndroidManifest.xml index 292e0933a2..28c55b89b2 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/src/main/AndroidManifest.xml @@ -19,9 +19,9 @@ - + - + @@ -31,7 +31,7 @@ - + @@ -42,25 +42,15 @@ - - - - - - - - - - - - - + + + diff --git a/MPChartExample/assets/OpenSans-Bold.ttf b/MPChartExample/src/main/assets/OpenSans-Bold.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-Bold.ttf rename to MPChartExample/src/main/assets/OpenSans-Bold.ttf diff --git a/MPChartExample/assets/OpenSans-BoldItalic.ttf b/MPChartExample/src/main/assets/OpenSans-BoldItalic.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-BoldItalic.ttf rename to MPChartExample/src/main/assets/OpenSans-BoldItalic.ttf diff --git a/MPChartExample/assets/OpenSans-ExtraBold.ttf b/MPChartExample/src/main/assets/OpenSans-ExtraBold.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-ExtraBold.ttf rename to MPChartExample/src/main/assets/OpenSans-ExtraBold.ttf diff --git a/MPChartExample/assets/OpenSans-ExtraBoldItalic.ttf b/MPChartExample/src/main/assets/OpenSans-ExtraBoldItalic.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-ExtraBoldItalic.ttf rename to MPChartExample/src/main/assets/OpenSans-ExtraBoldItalic.ttf diff --git a/MPChartExample/assets/OpenSans-Italic.ttf b/MPChartExample/src/main/assets/OpenSans-Italic.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-Italic.ttf rename to MPChartExample/src/main/assets/OpenSans-Italic.ttf diff --git a/MPChartExample/assets/OpenSans-Light.ttf b/MPChartExample/src/main/assets/OpenSans-Light.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-Light.ttf rename to MPChartExample/src/main/assets/OpenSans-Light.ttf diff --git a/MPChartExample/assets/OpenSans-LightItalic.ttf b/MPChartExample/src/main/assets/OpenSans-LightItalic.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-LightItalic.ttf rename to MPChartExample/src/main/assets/OpenSans-LightItalic.ttf diff --git a/MPChartExample/assets/OpenSans-Regular.ttf b/MPChartExample/src/main/assets/OpenSans-Regular.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-Regular.ttf rename to MPChartExample/src/main/assets/OpenSans-Regular.ttf diff --git a/MPChartExample/assets/OpenSans-Semibold.ttf b/MPChartExample/src/main/assets/OpenSans-Semibold.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-Semibold.ttf rename to MPChartExample/src/main/assets/OpenSans-Semibold.ttf diff --git a/MPChartExample/assets/OpenSans-SemiboldItalic.ttf b/MPChartExample/src/main/assets/OpenSans-SemiboldItalic.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-SemiboldItalic.ttf rename to MPChartExample/src/main/assets/OpenSans-SemiboldItalic.ttf diff --git a/MPChartExample/assets/cosine.txt b/MPChartExample/src/main/assets/cosine.txt similarity index 100% rename from MPChartExample/assets/cosine.txt rename to MPChartExample/src/main/assets/cosine.txt diff --git a/MPChartExample/assets/hugecosine.txt b/MPChartExample/src/main/assets/hugecosine.txt similarity index 100% rename from MPChartExample/assets/hugecosine.txt rename to MPChartExample/src/main/assets/hugecosine.txt diff --git a/MPChartExample/assets/hugesine.txt b/MPChartExample/src/main/assets/hugesine.txt similarity index 100% rename from MPChartExample/assets/hugesine.txt rename to MPChartExample/src/main/assets/hugesine.txt diff --git a/MPChartExample/assets/n.txt b/MPChartExample/src/main/assets/n.txt similarity index 100% rename from MPChartExample/assets/n.txt rename to MPChartExample/src/main/assets/n.txt diff --git a/MPChartExample/assets/nlogn.txt b/MPChartExample/src/main/assets/nlogn.txt similarity index 100% rename from MPChartExample/assets/nlogn.txt rename to MPChartExample/src/main/assets/nlogn.txt diff --git a/MPChartExample/assets/othersine.txt b/MPChartExample/src/main/assets/othersine.txt similarity index 100% rename from MPChartExample/assets/othersine.txt rename to MPChartExample/src/main/assets/othersine.txt diff --git a/MPChartExample/assets/sine.txt b/MPChartExample/src/main/assets/sine.txt similarity index 100% rename from MPChartExample/assets/sine.txt rename to MPChartExample/src/main/assets/sine.txt diff --git a/MPChartExample/assets/square.txt b/MPChartExample/src/main/assets/square.txt similarity index 100% rename from MPChartExample/assets/square.txt rename to MPChartExample/src/main/assets/square.txt diff --git a/MPChartExample/assets/stacked_bars.txt b/MPChartExample/src/main/assets/stacked_bars.txt similarity index 100% rename from MPChartExample/assets/stacked_bars.txt rename to MPChartExample/src/main/assets/stacked_bars.txt diff --git a/MPChartExample/assets/three.txt b/MPChartExample/src/main/assets/three.txt similarity index 100% rename from MPChartExample/assets/three.txt rename to MPChartExample/src/main/assets/three.txt diff --git a/MPChartExample/ic_launcher-web.png b/MPChartExample/src/main/ic_launcher-web.png similarity index 100% rename from MPChartExample/ic_launcher-web.png rename to MPChartExample/src/main/ic_launcher-web.png diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java similarity index 99% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 865bc92ea2..77875972c3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -131,7 +131,6 @@ protected void onCreate(Bundle savedInstanceState) { // setting data seekBarY.setProgress(50); seekBarX.setProgress(12); - setData(12, 50); // chart.setDrawLegend(false); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java similarity index 99% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 7f795046e3..075af0edbc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -57,6 +57,7 @@ protected void onCreate(Bundle savedInstanceState) { tvY = findViewById(R.id.tvYMax); seekBarX = findViewById(R.id.seekBar1); + seekBarX.setMax(50); seekBarX.setOnSeekBarChangeListener(this); seekBarY = findViewById(R.id.seekBar2); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java similarity index 99% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index f30732e6eb..996e088f43 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -93,7 +93,6 @@ protected void onCreate(Bundle savedInstanceState) { seekBarX.setProgress(45); seekBarY.setProgress(100); - setData(45, 100); chart.getLegend().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java similarity index 99% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index f93baaa376..27f7f29627 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -102,7 +102,6 @@ protected void onCreate(Bundle savedInstanceState) { yr.setAxisMinimum(0f); // this replaces setStartAtZero(true) // yr.setInverted(true); - setData(12, 50); chart.setFitBars(true); chart.animateY(2500); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java similarity index 98% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index 999d17e7b5..08983710f2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -56,9 +56,6 @@ protected void onCreate(Bundle savedInstanceState) { seekBarX = findViewById(R.id.seekBar1); seekBarY = findViewById(R.id.seekBar2); - seekBarX.setProgress(45); - seekBarY.setProgress(100); - seekBarY.setOnSeekBarChangeListener(this); seekBarX.setOnSeekBarChangeListener(this); @@ -100,7 +97,8 @@ protected void onCreate(Bundle savedInstanceState) { rightAxis.setEnabled(false); // add data - setData(25, 50); + seekBarX.setProgress(25); + seekBarY.setProgress(50); // // restrain the maximum scale-out factor // chart.setScaleMinima(3f, 3f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java similarity index 99% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 60cab539a9..9e0c98f89b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -91,7 +91,6 @@ protected void onCreate(Bundle savedInstanceState) { // add data seekBarX.setProgress(20); seekBarY.setProgress(30); - setData(20, 30); chart.animateX(1500); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java similarity index 99% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 47d0a79046..212b90ff87 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -77,8 +77,6 @@ protected void onCreate(Bundle savedInstanceState) { // add data seekBarX.setProgress(100); - setData(100, 30); - chart.invalidate(); // get the legend (only possible after setting data) Legend l = chart.getLegend(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java similarity index 99% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index e22b828d56..3c94e2ad5d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -97,7 +97,6 @@ protected void onCreate(Bundle savedInstanceState) { seekBarX.setProgress(4); seekBarY.setProgress(10); - setData(4, 100); chart.animateY(1400, Easing.EaseInOutQuad); // chart.spin(2000, 0, 360); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java similarity index 99% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index ebeb43e9cf..dc26f48297 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -102,7 +102,6 @@ protected void onCreate(Bundle savedInstanceState) { seekBarX.setProgress(4); seekBarY.setProgress(100); - setData(4, 100); chart.animateY(1400, Easing.EaseInOutQuad); // chart.spin(2000, 0, 360); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java similarity index 73% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java index a6625bae9a..f322090d72 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java @@ -3,19 +3,19 @@ /** * Created by Philipp Jahoda on 07/12/15. */ -public class ContentItem { +class ContentItem { final String name; final String desc; boolean isSection = false; - public ContentItem(String n) { + ContentItem(String n) { name = n; desc = ""; isSection = true; } - public ContentItem(String n, String d) { + ContentItem(String n, String d) { name = n; desc = d; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java similarity index 100% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java similarity index 93% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java index 4ddac475bc..9a22b51c9a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java @@ -18,12 +18,12 @@ /** * Created by Philipp Jahoda on 07/12/15. */ -public class MyAdapter extends ArrayAdapter { +class MyAdapter extends ArrayAdapter { private final Typeface mTypeFaceLight; private final Typeface mTypeFaceRegular; - public MyAdapter(Context context, List objects) { + MyAdapter(Context context, List objects) { super(context, 0, objects); mTypeFaceLight = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); diff --git a/MPChartExample/res/anim/move_left_in_activity.xml b/MPChartExample/src/main/res/anim/move_left_in_activity.xml similarity index 100% rename from MPChartExample/res/anim/move_left_in_activity.xml rename to MPChartExample/src/main/res/anim/move_left_in_activity.xml diff --git a/MPChartExample/res/anim/move_left_out_activity.xml b/MPChartExample/src/main/res/anim/move_left_out_activity.xml similarity index 100% rename from MPChartExample/res/anim/move_left_out_activity.xml rename to MPChartExample/src/main/res/anim/move_left_out_activity.xml diff --git a/MPChartExample/res/anim/move_right_in_activity.xml b/MPChartExample/src/main/res/anim/move_right_in_activity.xml similarity index 100% rename from MPChartExample/res/anim/move_right_in_activity.xml rename to MPChartExample/src/main/res/anim/move_right_in_activity.xml diff --git a/MPChartExample/res/anim/move_right_out_activity.xml b/MPChartExample/src/main/res/anim/move_right_out_activity.xml similarity index 100% rename from MPChartExample/res/anim/move_right_out_activity.xml rename to MPChartExample/src/main/res/anim/move_right_out_activity.xml diff --git a/MPChartExample/res/drawable-hdpi/ic_launcher.png b/MPChartExample/src/main/res/drawable-hdpi/ic_launcher.png similarity index 100% rename from MPChartExample/res/drawable-hdpi/ic_launcher.png rename to MPChartExample/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/MPChartExample/res/drawable-hdpi/star.png b/MPChartExample/src/main/res/drawable-hdpi/star.png similarity index 100% rename from MPChartExample/res/drawable-hdpi/star.png rename to MPChartExample/src/main/res/drawable-hdpi/star.png diff --git a/MPChartExample/res/drawable-mdpi/ic_launcher.png b/MPChartExample/src/main/res/drawable-mdpi/ic_launcher.png similarity index 100% rename from MPChartExample/res/drawable-mdpi/ic_launcher.png rename to MPChartExample/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/MPChartExample/res/drawable-nodpi/marker2.png b/MPChartExample/src/main/res/drawable-nodpi/marker2.png similarity index 100% rename from MPChartExample/res/drawable-nodpi/marker2.png rename to MPChartExample/src/main/res/drawable-nodpi/marker2.png diff --git a/MPChartExample/res/drawable-nodpi/radar_marker.png b/MPChartExample/src/main/res/drawable-nodpi/radar_marker.png similarity index 100% rename from MPChartExample/res/drawable-nodpi/radar_marker.png rename to MPChartExample/src/main/res/drawable-nodpi/radar_marker.png diff --git a/MPChartExample/res/drawable-xhdpi/ic_launcher.png b/MPChartExample/src/main/res/drawable-xhdpi/ic_launcher.png similarity index 100% rename from MPChartExample/res/drawable-xhdpi/ic_launcher.png rename to MPChartExample/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/MPChartExample/res/drawable-xxhdpi/ic_launcher.png b/MPChartExample/src/main/res/drawable-xxhdpi/ic_launcher.png similarity index 100% rename from MPChartExample/res/drawable-xxhdpi/ic_launcher.png rename to MPChartExample/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/MPChartExample/res/drawable/fade_red.xml b/MPChartExample/src/main/res/drawable/fade_red.xml similarity index 100% rename from MPChartExample/res/drawable/fade_red.xml rename to MPChartExample/src/main/res/drawable/fade_red.xml diff --git a/MPChartExample/res/layout/activity_age_distribution.xml b/MPChartExample/src/main/res/layout/activity_age_distribution.xml similarity index 100% rename from MPChartExample/res/layout/activity_age_distribution.xml rename to MPChartExample/src/main/res/layout/activity_age_distribution.xml diff --git a/MPChartExample/res/layout/activity_awesomedesign.xml b/MPChartExample/src/main/res/layout/activity_awesomedesign.xml similarity index 100% rename from MPChartExample/res/layout/activity_awesomedesign.xml rename to MPChartExample/src/main/res/layout/activity_awesomedesign.xml diff --git a/MPChartExample/res/layout/activity_barchart.xml b/MPChartExample/src/main/res/layout/activity_barchart.xml similarity index 100% rename from MPChartExample/res/layout/activity_barchart.xml rename to MPChartExample/src/main/res/layout/activity_barchart.xml diff --git a/MPChartExample/res/layout/activity_barchart_noseekbar.xml b/MPChartExample/src/main/res/layout/activity_barchart_noseekbar.xml similarity index 100% rename from MPChartExample/res/layout/activity_barchart_noseekbar.xml rename to MPChartExample/src/main/res/layout/activity_barchart_noseekbar.xml diff --git a/MPChartExample/res/layout/activity_barchart_sinus.xml b/MPChartExample/src/main/res/layout/activity_barchart_sinus.xml similarity index 100% rename from MPChartExample/res/layout/activity_barchart_sinus.xml rename to MPChartExample/src/main/res/layout/activity_barchart_sinus.xml diff --git a/MPChartExample/res/layout/activity_bubblechart.xml b/MPChartExample/src/main/res/layout/activity_bubblechart.xml similarity index 100% rename from MPChartExample/res/layout/activity_bubblechart.xml rename to MPChartExample/src/main/res/layout/activity_bubblechart.xml diff --git a/MPChartExample/res/layout/activity_candlechart.xml b/MPChartExample/src/main/res/layout/activity_candlechart.xml similarity index 100% rename from MPChartExample/res/layout/activity_candlechart.xml rename to MPChartExample/src/main/res/layout/activity_candlechart.xml diff --git a/MPChartExample/res/layout/activity_colored_lines.xml b/MPChartExample/src/main/res/layout/activity_colored_lines.xml similarity index 100% rename from MPChartExample/res/layout/activity_colored_lines.xml rename to MPChartExample/src/main/res/layout/activity_colored_lines.xml diff --git a/MPChartExample/res/layout/activity_combined.xml b/MPChartExample/src/main/res/layout/activity_combined.xml similarity index 100% rename from MPChartExample/res/layout/activity_combined.xml rename to MPChartExample/src/main/res/layout/activity_combined.xml diff --git a/MPChartExample/res/layout/activity_draw_chart.xml b/MPChartExample/src/main/res/layout/activity_draw_chart.xml similarity index 100% rename from MPChartExample/res/layout/activity_draw_chart.xml rename to MPChartExample/src/main/res/layout/activity_draw_chart.xml diff --git a/MPChartExample/res/layout/activity_horizontalbarchart.xml b/MPChartExample/src/main/res/layout/activity_horizontalbarchart.xml similarity index 100% rename from MPChartExample/res/layout/activity_horizontalbarchart.xml rename to MPChartExample/src/main/res/layout/activity_horizontalbarchart.xml diff --git a/MPChartExample/res/layout/activity_linechart.xml b/MPChartExample/src/main/res/layout/activity_linechart.xml similarity index 100% rename from MPChartExample/res/layout/activity_linechart.xml rename to MPChartExample/src/main/res/layout/activity_linechart.xml diff --git a/MPChartExample/res/layout/activity_linechart_noseekbar.xml b/MPChartExample/src/main/res/layout/activity_linechart_noseekbar.xml similarity index 100% rename from MPChartExample/res/layout/activity_linechart_noseekbar.xml rename to MPChartExample/src/main/res/layout/activity_linechart_noseekbar.xml diff --git a/MPChartExample/res/layout/activity_linechart_time.xml b/MPChartExample/src/main/res/layout/activity_linechart_time.xml similarity index 100% rename from MPChartExample/res/layout/activity_linechart_time.xml rename to MPChartExample/src/main/res/layout/activity_linechart_time.xml diff --git a/MPChartExample/res/layout/activity_listview_chart.xml b/MPChartExample/src/main/res/layout/activity_listview_chart.xml similarity index 100% rename from MPChartExample/res/layout/activity_listview_chart.xml rename to MPChartExample/src/main/res/layout/activity_listview_chart.xml diff --git a/MPChartExample/res/layout/activity_main.xml b/MPChartExample/src/main/res/layout/activity_main.xml similarity index 100% rename from MPChartExample/res/layout/activity_main.xml rename to MPChartExample/src/main/res/layout/activity_main.xml diff --git a/MPChartExample/res/layout/activity_performance_linechart.xml b/MPChartExample/src/main/res/layout/activity_performance_linechart.xml similarity index 100% rename from MPChartExample/res/layout/activity_performance_linechart.xml rename to MPChartExample/src/main/res/layout/activity_performance_linechart.xml diff --git a/MPChartExample/res/layout/activity_piechart.xml b/MPChartExample/src/main/res/layout/activity_piechart.xml similarity index 100% rename from MPChartExample/res/layout/activity_piechart.xml rename to MPChartExample/src/main/res/layout/activity_piechart.xml diff --git a/MPChartExample/res/layout/activity_piechart_half.xml b/MPChartExample/src/main/res/layout/activity_piechart_half.xml similarity index 100% rename from MPChartExample/res/layout/activity_piechart_half.xml rename to MPChartExample/src/main/res/layout/activity_piechart_half.xml diff --git a/MPChartExample/res/layout/activity_radarchart.xml b/MPChartExample/src/main/res/layout/activity_radarchart.xml similarity index 100% rename from MPChartExample/res/layout/activity_radarchart.xml rename to MPChartExample/src/main/res/layout/activity_radarchart.xml diff --git a/MPChartExample/res/layout/activity_realtime_linechart.xml b/MPChartExample/src/main/res/layout/activity_realtime_linechart.xml similarity index 100% rename from MPChartExample/res/layout/activity_realtime_linechart.xml rename to MPChartExample/src/main/res/layout/activity_realtime_linechart.xml diff --git a/MPChartExample/res/layout/activity_scatterchart.xml b/MPChartExample/src/main/res/layout/activity_scatterchart.xml similarity index 100% rename from MPChartExample/res/layout/activity_scatterchart.xml rename to MPChartExample/src/main/res/layout/activity_scatterchart.xml diff --git a/MPChartExample/res/layout/activity_scrollview.xml b/MPChartExample/src/main/res/layout/activity_scrollview.xml similarity index 100% rename from MPChartExample/res/layout/activity_scrollview.xml rename to MPChartExample/src/main/res/layout/activity_scrollview.xml diff --git a/MPChartExample/res/layout/custom_marker_view.xml b/MPChartExample/src/main/res/layout/custom_marker_view.xml similarity index 100% rename from MPChartExample/res/layout/custom_marker_view.xml rename to MPChartExample/src/main/res/layout/custom_marker_view.xml diff --git a/MPChartExample/res/layout/frag_simple_bar.xml b/MPChartExample/src/main/res/layout/frag_simple_bar.xml similarity index 100% rename from MPChartExample/res/layout/frag_simple_bar.xml rename to MPChartExample/src/main/res/layout/frag_simple_bar.xml diff --git a/MPChartExample/res/layout/frag_simple_line.xml b/MPChartExample/src/main/res/layout/frag_simple_line.xml similarity index 100% rename from MPChartExample/res/layout/frag_simple_line.xml rename to MPChartExample/src/main/res/layout/frag_simple_line.xml diff --git a/MPChartExample/res/layout/frag_simple_pie.xml b/MPChartExample/src/main/res/layout/frag_simple_pie.xml similarity index 100% rename from MPChartExample/res/layout/frag_simple_pie.xml rename to MPChartExample/src/main/res/layout/frag_simple_pie.xml diff --git a/MPChartExample/res/layout/frag_simple_scatter.xml b/MPChartExample/src/main/res/layout/frag_simple_scatter.xml similarity index 100% rename from MPChartExample/res/layout/frag_simple_scatter.xml rename to MPChartExample/src/main/res/layout/frag_simple_scatter.xml diff --git a/MPChartExample/res/layout/list_item.xml b/MPChartExample/src/main/res/layout/list_item.xml similarity index 100% rename from MPChartExample/res/layout/list_item.xml rename to MPChartExample/src/main/res/layout/list_item.xml diff --git a/MPChartExample/res/layout/list_item_barchart.xml b/MPChartExample/src/main/res/layout/list_item_barchart.xml similarity index 100% rename from MPChartExample/res/layout/list_item_barchart.xml rename to MPChartExample/src/main/res/layout/list_item_barchart.xml diff --git a/MPChartExample/res/layout/list_item_linechart.xml b/MPChartExample/src/main/res/layout/list_item_linechart.xml similarity index 100% rename from MPChartExample/res/layout/list_item_linechart.xml rename to MPChartExample/src/main/res/layout/list_item_linechart.xml diff --git a/MPChartExample/res/layout/list_item_piechart.xml b/MPChartExample/src/main/res/layout/list_item_piechart.xml similarity index 100% rename from MPChartExample/res/layout/list_item_piechart.xml rename to MPChartExample/src/main/res/layout/list_item_piechart.xml diff --git a/MPChartExample/res/layout/list_item_section.xml b/MPChartExample/src/main/res/layout/list_item_section.xml similarity index 100% rename from MPChartExample/res/layout/list_item_section.xml rename to MPChartExample/src/main/res/layout/list_item_section.xml diff --git a/MPChartExample/res/layout/radar_markerview.xml b/MPChartExample/src/main/res/layout/radar_markerview.xml similarity index 100% rename from MPChartExample/res/layout/radar_markerview.xml rename to MPChartExample/src/main/res/layout/radar_markerview.xml diff --git a/MPChartExample/res/menu/bar.xml b/MPChartExample/src/main/res/menu/bar.xml similarity index 100% rename from MPChartExample/res/menu/bar.xml rename to MPChartExample/src/main/res/menu/bar.xml diff --git a/MPChartExample/res/menu/bubble.xml b/MPChartExample/src/main/res/menu/bubble.xml similarity index 100% rename from MPChartExample/res/menu/bubble.xml rename to MPChartExample/src/main/res/menu/bubble.xml diff --git a/MPChartExample/res/menu/candle.xml b/MPChartExample/src/main/res/menu/candle.xml similarity index 100% rename from MPChartExample/res/menu/candle.xml rename to MPChartExample/src/main/res/menu/candle.xml diff --git a/MPChartExample/res/menu/combined.xml b/MPChartExample/src/main/res/menu/combined.xml similarity index 100% rename from MPChartExample/res/menu/combined.xml rename to MPChartExample/src/main/res/menu/combined.xml diff --git a/MPChartExample/res/menu/draw.xml b/MPChartExample/src/main/res/menu/draw.xml similarity index 100% rename from MPChartExample/res/menu/draw.xml rename to MPChartExample/src/main/res/menu/draw.xml diff --git a/MPChartExample/res/menu/dynamical.xml b/MPChartExample/src/main/res/menu/dynamical.xml similarity index 100% rename from MPChartExample/res/menu/dynamical.xml rename to MPChartExample/src/main/res/menu/dynamical.xml diff --git a/MPChartExample/res/menu/line.xml b/MPChartExample/src/main/res/menu/line.xml similarity index 100% rename from MPChartExample/res/menu/line.xml rename to MPChartExample/src/main/res/menu/line.xml diff --git a/MPChartExample/res/menu/main.xml b/MPChartExample/src/main/res/menu/main.xml similarity index 100% rename from MPChartExample/res/menu/main.xml rename to MPChartExample/src/main/res/menu/main.xml diff --git a/MPChartExample/res/menu/only_github.xml b/MPChartExample/src/main/res/menu/only_github.xml similarity index 100% rename from MPChartExample/res/menu/only_github.xml rename to MPChartExample/src/main/res/menu/only_github.xml diff --git a/MPChartExample/res/menu/pie.xml b/MPChartExample/src/main/res/menu/pie.xml similarity index 100% rename from MPChartExample/res/menu/pie.xml rename to MPChartExample/src/main/res/menu/pie.xml diff --git a/MPChartExample/res/menu/radar.xml b/MPChartExample/src/main/res/menu/radar.xml similarity index 100% rename from MPChartExample/res/menu/radar.xml rename to MPChartExample/src/main/res/menu/radar.xml diff --git a/MPChartExample/res/menu/realtime.xml b/MPChartExample/src/main/res/menu/realtime.xml similarity index 100% rename from MPChartExample/res/menu/realtime.xml rename to MPChartExample/src/main/res/menu/realtime.xml diff --git a/MPChartExample/res/menu/scatter.xml b/MPChartExample/src/main/res/menu/scatter.xml similarity index 100% rename from MPChartExample/res/menu/scatter.xml rename to MPChartExample/src/main/res/menu/scatter.xml diff --git a/MPChartExample/res/values/strings.xml b/MPChartExample/src/main/res/values/strings.xml similarity index 97% rename from MPChartExample/res/values/strings.xml rename to MPChartExample/src/main/res/values/strings.xml index 2426d92a78..1e96d8b6b6 100644 --- a/MPChartExample/res/values/strings.xml +++ b/MPChartExample/src/main/res/values/strings.xml @@ -7,7 +7,6 @@ Problem Report Developer Website Save to Gallery - realm.io website Animate X Animate Y diff --git a/MPChartExample/res/values/styles.xml b/MPChartExample/src/main/res/values/styles.xml similarity index 100% rename from MPChartExample/res/values/styles.xml rename to MPChartExample/src/main/res/values/styles.xml diff --git a/MPChartLib/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs b/MPChartLib/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs deleted file mode 100644 index 77dc3a2d85..0000000000 --- a/MPChartLib/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -#org.springsource.ide.eclipse.gradle.core.preferences.GradleProjectPreferences -#Mon Jan 18 23:02:46 CET 2016 -org.springsource.ide.eclipse.gradle.linkedresources= -org.springsource.ide.eclipse.gradle.rootprojectloc=.. diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index 83fc126bff..881cfd5365 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -1,12 +1,11 @@ apply plugin: 'com.android.library' -apply plugin: 'maven' -//apply plugin: 'com.github.dcendents.android-maven' -//apply plugin: 'realm-android' +apply plugin: 'com.github.dcendents.android-maven' + +group='com.github.philjay' android { compileSdkVersion 28 buildToolsVersion '28.0.3' - // resourcePrefix 'mpcht' defaultConfig { minSdkVersion 14 targetSdkVersion 28 @@ -19,33 +18,14 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - lintOptions { - abortOnError false - } testOptions { unitTests.returnDefaultValues = true // this prevents "not mocked" error } } -repositories { - maven { - url 'http://oss.jfrog.org/artifactory/oss-snapshot-local' - } -} - dependencies { - //provided 'io.realm:realm-android:0.87.5' // "optional" dependency to realm-database API implementation 'androidx.annotation:annotation:1.0.0' testImplementation 'junit:junit:4.12' - testImplementation "org.mockito:mockito-core:1.10.19" -} - -android.libraryVariants.all { variant -> - def name = variant.buildType.name - def task = project.tasks.create "jar${name.capitalize()}", Jar - task.dependsOn variant.javaCompiler - task.from variant.javaCompiler.destinationDir - artifacts.add('archives', task) } task sourcesJar(type: Jar) { diff --git a/MPChartLib/proguard-project.txt b/MPChartLib/proguard-project.txt deleted file mode 100644 index f2fe1559a2..0000000000 --- a/MPChartLib/proguard-project.txt +++ /dev/null @@ -1,20 +0,0 @@ -# To enable ProGuard in your project, edit project.properties -# to define the proguard.config property as described in that file. -# -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in ${sdk.dir}/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/MPChartLib/project.properties b/MPChartLib/project.properties deleted file mode 100644 index b2ef7dccc5..0000000000 --- a/MPChartLib/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-23 -android.library=true diff --git a/README.md b/README.md index 96d9ac9039..d6c9ea60d2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ![banner](https://raw.github.com/PhilJay/MPChart/master/design/feature_graphic_smaller.png) [![Release](https://img.shields.io/github/release/PhilJay/MPAndroidChart.svg?style=flat)](https://jitpack.io/#PhilJay/MPAndroidChart) -[![API](https://img.shields.io/badge/API-8%2B-green.svg?style=flat)](https://android-arsenal.com/api?level=8) +[![API](https://img.shields.io/badge/API-14%2B-green.svg?style=flat)](https://android-arsenal.com/api?level=14) [![Android Arsenal](http://img.shields.io/badge/Android%20Arsenal-MPAndroidChart-orange.svg?style=flat)](http://android-arsenal.com/details/1/741) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/PhilJay/MPAndroidChart?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) [![Twitter](https://img.shields.io/badge/Twitter-@mpandroidchart-blue.svg?style=flat)](http://twitter.com/mpandroidchart) @@ -10,6 +10,19 @@ [**Charts**](https://github.com/danielgindi/Charts) is the iOS version of this library +## Table of Contents +1. [Quick Start](#quick-start) + 1. [Gradle](#gradle-setup) + 1. [Maven](#maven-setup) +1. [Documentation](#documentation) +1. [Examples](#examples) +1. [Questions](#report) +1. [Donate](#donate) +1. [Social Media](#social) +1. [More Examples](#more-examples) +1. [License](#licence) +1. [Creators](#creators) + ## [Realtime Graphing Solution | SciChart](https://scichart.com/android-chart-features?source=MPAndroidChart) @@ -28,26 +41,23 @@ All MPAndroidChart users are entitled to a special **discount of 5%** off the Quick Start :chart_with_upwards_trend: + +Add the library to your Android project, then check out the examples below! -**Gradle** +### Gradle Setup -- **Project level `build.gradle`** ```gradle -allprojects { - repositories { - maven { url 'https://jitpack.io/' } - } +repositories { + maven { url 'https://jitpack.io/' } } -``` -- **App level `build.gradle`** -```gradle + dependencies { implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3' } ``` -**Maven** +### Maven Setup ```xml @@ -56,7 +66,6 @@ dependencies { https://jitpack.io - com.github.PhilJay @@ -67,7 +76,7 @@ dependencies {
    -## Documentation :notebook_with_decorative_cover: +

    Documentation :notebook_with_decorative_cover:

    See the [**documentation**](https://github.com/PhilJay/MPAndroidChart/wiki) for examples and general use of MPAndroidChart. @@ -75,14 +84,14 @@ See the [**javadocs**](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0
    -## Examples :eyes: +

    Examples :eyes:

    Download the [MPAndroidChart Example App](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample) or look at the [source code](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample). [![ScreenShot](https://github.com/PhilJay/MPAndroidChart/blob/master/design/video_thumbnail.png)](https://www.youtube.com/watch?v=ufaK_Hd6BpI)
    -## Questions & Issues :thinking: +

    Questions & Issues :thinking:

    This repository's issue tracker is only for bugs and feature requests. The maintainers ask that you refrain from asking questions about how to use MPAndroidChart through the issue tracker. @@ -90,7 +99,7 @@ Please read the [**documentation**](https://github.com/PhilJay/MPAndroidChart/wi
    -## Donations :heart: + **This project needs you!** If you would like to support this project's further development, the creator of this project or the continuous maintenance of this project, **feel free to donate**. Your donation is highly appreciated (and I love food, coffee and beer). Thank you! @@ -106,13 +115,13 @@ Please read the [**documentation**](https://github.com/PhilJay/MPAndroidChart/wi - [**Donate 5 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7G52RA87ED8NY): Thank's for creating this project, here's a coffee (or some beer) for you! - [**Donate 10 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4C9TPE67F5PUQ): Wow, I am stunned. Let me take you to the movies! -- [**Donate 15 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YKMPTFMVK3JMC): I really appreciate your work, let's grab some lunch! +- [**Donate 15 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YKMPTFMVK3JMC): I really appreciate your work, let's grab some lunch! - [**Donate 25 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H9JA4QX7UHXCY): That's some awesome stuff you did right there, dinner is on me! - Or you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome!
    -## Social Media :fire: +

    Social Media :fire:

    If you like this library, please tell others about it :two_hearts: :two_hearts: @@ -124,7 +133,7 @@ You can follow me on Twitter [**@PhilippJahoda**](https://twitter.com/PhilippJah
    -## More Examples :+1: +

    More Examples :+1:


    @@ -194,7 +203,7 @@ You can follow me on Twitter [**@PhilippJahoda**](https://twitter.com/PhilippJah
    -# License :page_facing_up: +

    License :page_facing_up:

    Copyright 2018 Philipp Jahoda @@ -202,7 +211,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +> http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -212,7 +221,7 @@ limitations under the License.
    -## Special Thanks :heart: +

    Special Thanks :heart:

    These people rock! diff --git a/build.gradle b/build.gradle index fb18d8fe7d..58f50401d3 100644 --- a/build.gradle +++ b/build.gradle @@ -4,9 +4,8 @@ buildscript { google() } dependencies { - classpath "io.realm:realm-gradle-plugin:4.2.0" classpath 'com.android.tools.build:gradle:3.2.1' - classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' } } From 9148f37f9eca975335bf64a9096808e659343610 Mon Sep 17 00:00:00 2001 From: almic Date: Mon, 29 Oct 2018 13:23:18 -0600 Subject: [PATCH 224/291] Bump version to 3.1.0-alpha --- .github/ISSUE_TEMPLATE.md | 4 ++-- .github/ISSUE_TEMPLATE/Bug_report.md | 4 ++-- .github/ISSUE_TEMPLATE/Support_help.md | 2 +- MPChartExample/build.gradle | 2 +- .../xxmassdeveloper/mpchartexample/LineChartActivity1.java | 2 +- .../xxmassdeveloper/mpchartexample/LineChartActivity2.java | 2 +- MPChartLib/build.gradle | 2 +- README.md | 6 +++--- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 49ed0dfc1d..b75838b907 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -28,10 +28,10 @@ cannot answer support questions here. We will close your issue without a respons **Device (please complete the following information):** - Device: [e.g. Google Pixel] - Android Version [e.g. 7.0] - - Library Version (e.g. 3.0.3) + - Library Version (e.g. 3.1.0-alpha) **Additional Context** diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 496d0a0d8d..7989a9d655 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -33,10 +33,10 @@ support questions here. We will close your issue without a response. **Device (please complete the following information):** - Device: [e.g. Google Pixel] - Android Version [e.g. 7.0] - - Library Version (e.g. 3.0.3) + - Library Version (e.g. 3.1.0-alpha) **Additional Context** diff --git a/.github/ISSUE_TEMPLATE/Support_help.md b/.github/ISSUE_TEMPLATE/Support_help.md index 64c87763df..5a2c5fd41b 100644 --- a/.github/ISSUE_TEMPLATE/Support_help.md +++ b/.github/ISSUE_TEMPLATE/Support_help.md @@ -14,7 +14,7 @@ Instead, do the following: 1. Download the [Example App](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample) and check out the [source code](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample). 90% of the time there is an example that does exactly what you are trying to do. -1. Look at the [Wiki](https://github.com/PhilJay/MPAndroidChart/wiki) for the official documentation for MPAndroidChart. You can also browse the [javadoc](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.3/javadoc/) for a more detailed tutorial of the API. +1. Look at the [Wiki](https://github.com/PhilJay/MPAndroidChart/wiki) for the official documentation for MPAndroidChart. You can also browse the [javadoc](https://jitpack.io/com/github/philjay/mpandroidchart/v3.1.0-alpha/javadoc/) for a more detailed tutorial of the API. 1. Go to [StackOverflow](https://stackoverflow.com/questions/tagged/mpandroidchart) and ask your questions there. The community will be much more helpful and willing to offer guidance. diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 647aca2b51..0a60389ede 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -7,7 +7,7 @@ android { minSdkVersion 16 targetSdkVersion 28 versionCode 56 - versionName '3.0.3' + versionName '3.1.0' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 9505838810..dd43b056eb 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -45,7 +45,7 @@ * Example of a heavily customized {@link LineChart} with limit lines, custom line shapes, etc. * * @since 1.7.4 - * @version 3.0.3 + * @version 3.1.0 */ public class LineChartActivity1 extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 9e0c98f89b..6b9cbb5f22 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -38,7 +38,7 @@ * Example of a dual axis {@link LineChart} with multiple data sets. * * @since 1.7.4 - * @version 3.0.3 + * @version 3.1.0 */ public class LineChartActivity2 extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index 881cfd5365..0fb6dc7036 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -10,7 +10,7 @@ android { minSdkVersion 14 targetSdkVersion 28 versionCode 3 - versionName '3.0.3' + versionName '3.1.0' } buildTypes { release { diff --git a/README.md b/README.md index d6c9ea60d2..2e031bdf93 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ repositories { } dependencies { - implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3' + implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0-alpha' } ``` @@ -70,7 +70,7 @@ dependencies { com.github.PhilJay MPAndroidChart - v3.0.3 + v3.1.0-alpha ``` @@ -80,7 +80,7 @@ dependencies { See the [**documentation**](https://github.com/PhilJay/MPAndroidChart/wiki) for examples and general use of MPAndroidChart. -See the [**javadocs**](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.0.3/javadoc/) for more advanced documentation. +See the [**javadocs**](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.1.0-alpha/javadoc/) for more advanced documentation.
    From 608d9e29f59ebd63e234e13a4b4e1f3b3158e8c7 Mon Sep 17 00:00:00 2001 From: Mick A Date: Tue, 30 Oct 2018 08:59:58 -0600 Subject: [PATCH 225/291] Fix link error --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2e031bdf93..53f278aba7 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ See the [**javadocs**](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.1

    Examples :eyes:

    + Download the [MPAndroidChart Example App](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample) or look at the [source code](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample). [![ScreenShot](https://github.com/PhilJay/MPAndroidChart/blob/master/design/video_thumbnail.png)](https://www.youtube.com/watch?v=ufaK_Hd6BpI) From e5b66192e7b303d7d25fc172b1878c055b554047 Mon Sep 17 00:00:00 2001 From: almic Date: Wed, 7 Nov 2018 12:41:53 -0700 Subject: [PATCH 226/291] New ValueFormatter I created a simplified value formatter class, which is an abstract class rather than an interface. The switch was chosen because the new format has all the methods predefined (something an interface wouldn't allow) meaning you can extend it and only change what you want. This also means that you only need one value formatting class for labels rather than two different classes, it just makes more sense. Please check the method signatures to learn how to use them, I'm sure you'll find this new format is much more customizable and faster to use. I've made the class abstract even though there are no abstract methods or fields, this is because it would certainly be a mistake to create a ValueFormatter and not override any methods. To convert existing code, just use 'extends' instead of 'implements' and change the names to 'ValueFormatter'. You'll need to change the methods you overwrite as well, just check the class and use the one you need. --- .gitignore | 6 +- .../mpchartexample/BarChartActivity.java | 9 +- .../BarChartActivityMultiDataset.java | 8 +- .../BarChartPositiveNegative.java | 19 +-- .../mpchartexample/CombinedChartActivity.java | 8 +- .../mpchartexample/LineChartTime.java | 8 +- .../mpchartexample/PieChartActivity.java | 2 +- .../mpchartexample/RadarChartActivity.java | 8 +- .../mpchartexample/StackedBarActivity.java | 6 +- .../StackedBarActivityNegative.java | 21 +-- .../custom/DayAxisValueFormatter.java | 7 +- .../custom/MyAxisValueFormatter.java | 21 --- .../custom/MyCustomXAxisValueFormatter.java | 7 +- .../custom/MyValueFormatter.java | 27 +++- .../mpchartexample/custom/XYMarkerView.java | 9 +- .../custom/YearXAxisFormatter.java | 6 +- .../mikephil/charting/charts/Chart.java | 5 +- .../charting/components/AxisBase.java | 11 +- .../mikephil/charting/data/BaseDataSet.java | 8 +- .../mikephil/charting/data/ChartData.java | 5 +- .../formatter/DefaultAxisValueFormatter.java | 8 +- .../formatter/DefaultValueFormatter.java | 8 +- .../formatter/IAxisValueFormatter.java | 6 + .../charting/formatter/IValueFormatter.java | 10 +- .../formatter/IndexAxisValueFormatter.java | 12 +- .../formatter/LargeValueFormatter.java | 16 +- .../charting/formatter/PercentFormatter.java | 39 +++-- .../formatter/StackedValueFormatter.java | 24 ++- .../charting/formatter/ValueFormatter.java | 137 ++++++++++++++++++ .../dataprovider/ChartInterface.java | 4 +- .../interfaces/datasets/IDataSet.java | 7 +- .../charting/renderer/BarChartRenderer.java | 25 ++-- .../renderer/BubbleChartRenderer.java | 13 +- .../renderer/CandleStickChartRenderer.java | 20 +-- .../renderer/CombinedChartRenderer.java | 7 +- .../charting/renderer/DataRenderer.java | 21 +-- .../renderer/HorizontalBarChartRenderer.java | 17 +-- .../charting/renderer/LineChartRenderer.java | 13 +- .../charting/renderer/PieChartRenderer.java | 29 ++-- .../charting/renderer/RadarChartRenderer.java | 20 +-- .../renderer/ScatterChartRenderer.java | 19 +-- .../charting/renderer/XAxisRenderer.java | 3 +- .../XAxisRendererHorizontalBarChart.java | 7 +- .../renderer/XAxisRendererRadarChart.java | 4 +- .../github/mikephil/charting/utils/Utils.java | 24 ++- .../test/LargeValueFormatterTest.java | 50 +++---- .../charting/test/ObjectPoolTest.java | 2 +- 47 files changed, 408 insertions(+), 338 deletions(-) delete mode 100644 MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java diff --git a/.gitignore b/.gitignore index a340b1d6b3..1120426ac8 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ bin/ gen/ generated/ -docs/ finalOutput/ build.xml @@ -23,6 +22,8 @@ local.properties # Eclipse project files .classpath .project +.settings/ +.vscode/ # Proguard folder generated by Eclipse proguard/ @@ -31,7 +32,8 @@ proguard/ *.iml *.ipr *.iws -.idea/ +/.idea/* +!/.idea/runConfigurations .directory diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 77875972c3..4af0441ddb 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -1,4 +1,3 @@ - package com.xxmassdeveloper.mpchartexample; import android.Manifest; @@ -28,7 +27,7 @@ import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.IDataSet; @@ -36,7 +35,7 @@ import com.github.mikephil.charting.model.GradientColor; import com.github.mikephil.charting.utils.MPPointF; import com.xxmassdeveloper.mpchartexample.custom.DayAxisValueFormatter; -import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; +import com.xxmassdeveloper.mpchartexample.custom.MyValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.XYMarkerView; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -86,7 +85,7 @@ protected void onCreate(Bundle savedInstanceState) { chart.setDrawGridBackground(false); // chart.setDrawYLabels(false); - IAxisValueFormatter xAxisFormatter = new DayAxisValueFormatter(chart); + ValueFormatter xAxisFormatter = new DayAxisValueFormatter(chart); XAxis xAxis = chart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); @@ -96,7 +95,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setLabelCount(7); xAxis.setValueFormatter(xAxisFormatter); - IAxisValueFormatter custom = new MyAxisValueFormatter(); + ValueFormatter custom = new MyValueFormatter("$"); YAxis leftAxis = chart.getAxisLeft(); leftAxis.setTypeface(tfLight); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 075af0edbc..3369dbf6e2 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -1,4 +1,3 @@ - package com.xxmassdeveloper.mpchartexample; import android.Manifest; @@ -17,7 +16,6 @@ import android.widget.TextView; import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; @@ -25,8 +23,8 @@ import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.formatter.LargeValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; @@ -100,9 +98,9 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTypeface(tfLight); xAxis.setGranularity(1f); xAxis.setCenterAxisLabels(true); - xAxis.setValueFormatter(new IAxisValueFormatter() { + xAxis.setValueFormatter(new ValueFormatter() { @Override - public String getFormattedValue(float value, AxisBase axis) { + public String getFormattedValue(float value) { return String.valueOf((int) value); } }); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 4fec7dd6ab..8960dc770f 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -1,4 +1,3 @@ - package com.xxmassdeveloper.mpchartexample; import android.content.Intent; @@ -10,17 +9,13 @@ import android.view.WindowManager; import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; -import com.github.mikephil.charting.formatter.IValueFormatter; -import com.github.mikephil.charting.utils.ViewPortHandler; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.text.DecimalFormat; @@ -88,9 +83,9 @@ protected void onCreate(Bundle savedInstanceState) { data.add(new Data(3f, -442.3f, "01-01")); data.add(new Data(4f, -2280.1f, "01-02")); - xAxis.setValueFormatter(new IAxisValueFormatter() { + xAxis.setValueFormatter(new ValueFormatter() { @Override - public String getFormattedValue(float value, AxisBase axis) { + public String getFormattedValue(float value) { return data.get(Math.min(Math.max((int) value, 0), data.size()-1)).xAxisValue; } }); @@ -135,7 +130,7 @@ private void setData(List dataList) { BarData data = new BarData(set); data.setValueTextSize(13f); data.setValueTypeface(tfRegular); - data.setValueFormatter(new ValueFormatter()); + data.setValueFormatter(new Formatter()); data.setBarWidth(0.8f); chart.setData(data); @@ -159,17 +154,17 @@ private class Data { } } - private class ValueFormatter implements IValueFormatter + private class Formatter extends ValueFormatter { private final DecimalFormat mFormat; - ValueFormatter() { + Formatter() { mFormat = new DecimalFormat("######.0"); } @Override - public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + public String getFormattedValue(float value) { return mFormat.format(value); } } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 0308b9a891..53dd3806bc 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -1,4 +1,3 @@ - package com.xxmassdeveloper.mpchartexample; import android.content.Intent; @@ -11,7 +10,6 @@ import com.github.mikephil.charting.charts.CombinedChart; import com.github.mikephil.charting.charts.CombinedChart.DrawOrder; -import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; @@ -31,7 +29,7 @@ import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -83,9 +81,9 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setPosition(XAxisPosition.BOTH_SIDED); xAxis.setAxisMinimum(0f); xAxis.setGranularity(1f); - xAxis.setValueFormatter(new IAxisValueFormatter() { + xAxis.setValueFormatter(new ValueFormatter() { @Override - public String getFormattedValue(float value, AxisBase axis) { + public String getFormattedValue(float value) { return months[(int) value % months.length]; } }); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 212b90ff87..e9ae3c0e43 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -1,4 +1,3 @@ - package com.xxmassdeveloper.mpchartexample; import android.Manifest; @@ -16,7 +15,6 @@ import android.widget.TextView; import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; @@ -24,7 +22,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -92,12 +90,12 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTextColor(Color.rgb(255, 192, 56)); xAxis.setCenterAxisLabels(true); xAxis.setGranularity(1f); // one hour - xAxis.setValueFormatter(new IAxisValueFormatter() { + xAxis.setValueFormatter(new ValueFormatter() { private final SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm", Locale.ENGLISH); @Override - public String getFormattedValue(float value, AxisBase axis) { + public String getFormattedValue(float value) { long millis = TimeUnit.HOURS.toMillis((long) value); return mFormat.format(new Date(millis)); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 3c94e2ad5d..48bd4306c3 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -160,7 +160,7 @@ private void setData(int count, float range) { //dataSet.setSelectionShift(0f); PieData data = new PieData(dataSet); - data.setValueFormatter(new PercentFormatter()); + data.setValueFormatter(new PercentFormatter(chart)); data.setValueTextSize(11f); data.setValueTextColor(Color.WHITE); data.setValueTypeface(tfLight); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java index 883eb7dfc1..9fdae983d0 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java @@ -1,4 +1,3 @@ - package com.xxmassdeveloper.mpchartexample; import android.Manifest; @@ -14,7 +13,6 @@ import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.MarkerView; import com.github.mikephil.charting.components.XAxis; @@ -22,7 +20,7 @@ import com.github.mikephil.charting.data.RadarData; import com.github.mikephil.charting.data.RadarDataSet; import com.github.mikephil.charting.data.RadarEntry; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; import com.xxmassdeveloper.mpchartexample.custom.RadarMarkerView; @@ -69,12 +67,12 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTextSize(9f); xAxis.setYOffset(0f); xAxis.setXOffset(0f); - xAxis.setValueFormatter(new IAxisValueFormatter() { + xAxis.setValueFormatter(new ValueFormatter() { private final String[] mActivities = new String[]{"Burger", "Steak", "Salad", "Pasta", "Pizza"}; @Override - public String getFormattedValue(float value, AxisBase axis) { + public String getFormattedValue(float value) { return mActivities[(int) value % mActivities.length]; } }); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 676e0e62b0..1def86e8ef 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -24,11 +24,11 @@ import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.StackedValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.MyValueFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -78,7 +78,7 @@ protected void onCreate(Bundle savedInstanceState) { // change the position of the y-labels YAxis leftAxis = chart.getAxisLeft(); - leftAxis.setValueFormatter(new MyAxisValueFormatter()); + leftAxis.setValueFormatter(new MyValueFormatter("K")); leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) chart.getAxisRight().setEnabled(false); @@ -142,7 +142,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { dataSets.add(set1); BarData data = new BarData(dataSets); - data.setValueFormatter(new MyValueFormatter()); + data.setValueFormatter(new StackedValueFormatter(false, "", 1)); data.setValueTextColor(Color.WHITE); chart.setData(data); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 7af58c85ca..a4e510a20f 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -1,4 +1,3 @@ - package com.xxmassdeveloper.mpchartexample; import android.Manifest; @@ -14,7 +13,6 @@ import android.view.WindowManager; import com.github.mikephil.charting.charts.HorizontalBarChart; -import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; @@ -23,12 +21,10 @@ import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.IValueFormatter; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ViewPortHandler; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.text.DecimalFormat; @@ -80,12 +76,12 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setCenterAxisLabels(true); xAxis.setLabelCount(12); xAxis.setGranularity(10f); - xAxis.setValueFormatter(new IAxisValueFormatter() { + xAxis.setValueFormatter(new ValueFormatter() { private final DecimalFormat format = new DecimalFormat("###"); @Override - public String getFormattedValue(float value, AxisBase axis) { + public String getFormattedValue(float value) { return format.format(value) + "-" + format.format(value + 10); } }); @@ -242,7 +238,7 @@ public void onNothingSelected() { Log.i("NOTING SELECTED", ""); } - private class CustomFormatter implements IValueFormatter, IAxisValueFormatter { + private class CustomFormatter extends ValueFormatter { private final DecimalFormat mFormat; @@ -250,15 +246,8 @@ private class CustomFormatter implements IValueFormatter, IAxisValueFormatter { mFormat = new DecimalFormat("###"); } - // data - @Override - public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormat.format(Math.abs(value)) + "m"; - } - - // YAxis @Override - public String getFormattedValue(float value, AxisBase axis) { + public String getFormattedValue(float value) { return mFormat.format(Math.abs(value)) + "m"; } } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java index ba4d860d92..1fba5cc98e 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -1,13 +1,12 @@ package com.xxmassdeveloper.mpchartexample.custom; import com.github.mikephil.charting.charts.BarLineChartBase; -import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; /** * Created by philipp on 02/06/16. */ -public class DayAxisValueFormatter implements IAxisValueFormatter +public class DayAxisValueFormatter extends ValueFormatter { private final String[] mMonths = new String[]{ @@ -21,7 +20,7 @@ public DayAxisValueFormatter(BarLineChartBase chart) { } @Override - public String getFormattedValue(float value, AxisBase axis) { + public String getFormattedValue(float value) { int days = (int) value; diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java deleted file mode 100644 index e7cdbfcd10..0000000000 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.custom; - -import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; - -import java.text.DecimalFormat; - -public class MyAxisValueFormatter implements IAxisValueFormatter -{ - - private final DecimalFormat mFormat; - - public MyAxisValueFormatter() { - mFormat = new DecimalFormat("###,###,###,##0.0"); - } - - @Override - public String getFormattedValue(float value, AxisBase axis) { - return mFormat.format(value) + " $"; - } -} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index bea4908ef2..2cf2eab7ba 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -1,7 +1,6 @@ package com.xxmassdeveloper.mpchartexample.custom; -import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.utils.ViewPortHandler; import java.text.DecimalFormat; @@ -12,7 +11,7 @@ * @deprecated The {@link MyAxisValueFormatter} does exactly the same thing and is more functional. */ @Deprecated -public class MyCustomXAxisValueFormatter implements IAxisValueFormatter +public class MyCustomXAxisValueFormatter extends ValueFormatter { private final DecimalFormat mFormat; @@ -25,7 +24,7 @@ public MyCustomXAxisValueFormatter(ViewPortHandler viewPortHandler) { } @Override - public String getFormattedValue(float value, AxisBase axis) { + public String getFormattedValue(float value) { //Log.i("TRANS", "x: " + viewPortHandler.getTransX() + ", y: " + viewPortHandler.getTransY()); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java index ec1c119818..0b0bf2f2ab 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java @@ -1,22 +1,35 @@ package com.xxmassdeveloper.mpchartexample.custom; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.IValueFormatter; -import com.github.mikephil.charting.utils.ViewPortHandler; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.formatter.ValueFormatter; import java.text.DecimalFormat; -public class MyValueFormatter implements IValueFormatter +public class MyValueFormatter extends ValueFormatter { private final DecimalFormat mFormat; + private String suffix; - public MyValueFormatter() { + public MyValueFormatter(String suffix) { mFormat = new DecimalFormat("###,###,###,##0.0"); + this.suffix = suffix; } @Override - public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormat.format(value) + " $"; + public String getFormattedValue(float value) { + return mFormat.format(value) + suffix; + } + + @Override + public String getAxisLabel(float value, AxisBase axis) { + if (axis instanceof XAxis) { + return mFormat.format(value); + } else if (value > 0) { + return mFormat.format(value) + suffix; + } else { + return mFormat.format(value); + } } } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java index 51e4247d35..ed9dcb8a23 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java @@ -1,4 +1,3 @@ - package com.xxmassdeveloper.mpchartexample.custom; import android.annotation.SuppressLint; @@ -7,7 +6,7 @@ import com.github.mikephil.charting.components.MarkerView; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.utils.MPPointF; import com.xxmassdeveloper.mpchartexample.R; @@ -23,11 +22,11 @@ public class XYMarkerView extends MarkerView { private final TextView tvContent; - private final IAxisValueFormatter xAxisValueFormatter; + private final ValueFormatter xAxisValueFormatter; private final DecimalFormat format; - public XYMarkerView(Context context, IAxisValueFormatter xAxisValueFormatter) { + public XYMarkerView(Context context, ValueFormatter xAxisValueFormatter) { super(context, R.layout.custom_marker_view); this.xAxisValueFormatter = xAxisValueFormatter; @@ -40,7 +39,7 @@ public XYMarkerView(Context context, IAxisValueFormatter xAxisValueFormatter) { @Override public void refreshContent(Entry e, Highlight highlight) { - tvContent.setText(String.format("x: %s, y: %s", xAxisValueFormatter.getFormattedValue(e.getX(), null), format.format(e.getY()))); + tvContent.setText(String.format("x: %s, y: %s", xAxisValueFormatter.getFormattedValue(e.getX()), format.format(e.getY()))); super.refreshContent(e, highlight); } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java index 7122e0d80c..d45853f8d4 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java @@ -1,13 +1,13 @@ package com.xxmassdeveloper.mpchartexample.custom; import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; /** * Created by Philipp Jahoda on 14/09/15. */ @SuppressWarnings("unused") -public class YearXAxisFormatter implements IAxisValueFormatter +public class YearXAxisFormatter extends ValueFormatter { private final String[] mMonths = new String[]{ @@ -19,7 +19,7 @@ public YearXAxisFormatter() { } @Override - public String getFormattedValue(float value, AxisBase axis) { + public String getAxisLabel(float value, AxisBase axis) { float percent = value / axis.mAxisRange; return mMonths[(int) (mMonths.length * percent)]; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 718d7e2acb..5c82f9ab0e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.charts; import android.animation.ValueAnimator; @@ -35,7 +34,7 @@ import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.formatter.DefaultValueFormatter; -import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.ChartHighlighter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.highlight.IHighlighter; @@ -1015,7 +1014,7 @@ public XAxis getXAxis() { * * @return */ - public IValueFormatter getDefaultValueFormatter() { + public ValueFormatter getDefaultValueFormatter() { return mDefaultValueFormatter; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index 3c8028c24b..c1f02828be 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.components; import android.graphics.Color; @@ -6,7 +5,7 @@ import android.util.Log; import com.github.mikephil.charting.formatter.DefaultAxisValueFormatter; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; @@ -22,7 +21,7 @@ public abstract class AxisBase extends ComponentBase { /** * custom formatter that is used instead of the auto-formatter if set */ - protected IAxisValueFormatter mAxisValueFormatter; + protected ValueFormatter mAxisValueFormatter; private int mGridColor = Color.GRAY; @@ -486,7 +485,7 @@ public String getFormattedLabel(int index) { if (index < 0 || index >= mEntries.length) return ""; else - return getValueFormatter().getFormattedValue(mEntries[index], this); + return getValueFormatter().getAxisLabel(mEntries[index], this); } /** @@ -498,7 +497,7 @@ public String getFormattedLabel(int index) { * * @param f */ - public void setValueFormatter(IAxisValueFormatter f) { + public void setValueFormatter(ValueFormatter f) { if (f == null) mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals); @@ -511,7 +510,7 @@ public void setValueFormatter(IAxisValueFormatter f) { * * @return */ - public IAxisValueFormatter getValueFormatter() { + public ValueFormatter getValueFormatter() { if (mAxisValueFormatter == null || (mAxisValueFormatter instanceof DefaultAxisValueFormatter && diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java index 7800986dcd..8ca3e68d42 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -7,7 +7,7 @@ import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.model.GradientColor; import com.github.mikephil.charting.utils.ColorTemplate; @@ -56,7 +56,7 @@ public abstract class BaseDataSet implements IDataSet { /** * custom formatter that is used instead of the auto-formatter if set */ - protected transient IValueFormatter mValueFormatter; + protected transient ValueFormatter mValueFormatter; /** * the typeface used for the value text @@ -313,7 +313,7 @@ public boolean isHighlightEnabled() { } @Override - public void setValueFormatter(IValueFormatter f) { + public void setValueFormatter(ValueFormatter f) { if (f == null) return; @@ -322,7 +322,7 @@ public void setValueFormatter(IValueFormatter f) { } @Override - public IValueFormatter getValueFormatter() { + public ValueFormatter getValueFormatter() { if (needsFormatter()) return Utils.getDefaultValueFormatter(); return mValueFormatter; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java index 60d89f4753..9bd460290d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java @@ -1,11 +1,10 @@ - package com.github.mikephil.charting.data; import android.graphics.Typeface; import android.util.Log; import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IDataSet; @@ -659,7 +658,7 @@ public T getFirstRight(List sets) { * * @param f */ - public void setValueFormatter(IValueFormatter f) { + public void setValueFormatter(ValueFormatter f) { if (f == null) return; else { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java index 552c150e69..c8834c3e45 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java @@ -1,13 +1,11 @@ package com.github.mikephil.charting.formatter; -import com.github.mikephil.charting.components.AxisBase; - import java.text.DecimalFormat; /** * Created by philipp on 02/06/16. */ -public class DefaultAxisValueFormatter implements IAxisValueFormatter +public class DefaultAxisValueFormatter extends ValueFormatter { /** @@ -18,7 +16,7 @@ public class DefaultAxisValueFormatter implements IAxisValueFormatter /** * the number of decimal digits this formatter uses */ - protected int digits = 0; + protected int digits; /** * Constructor that specifies to how many digits the value should be @@ -40,7 +38,7 @@ public DefaultAxisValueFormatter(int digits) { } @Override - public String getFormattedValue(float value, AxisBase axis) { + public String getFormattedValue(float value) { // avoid memory allocations here (for performance) return mFormat.format(value); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java index e2fea4b079..40668b91ab 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java @@ -1,9 +1,5 @@ - package com.github.mikephil.charting.formatter; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.utils.ViewPortHandler; - import java.text.DecimalFormat; /** @@ -12,7 +8,7 @@ * * @author Philipp Jahoda */ -public class DefaultValueFormatter implements IValueFormatter +public class DefaultValueFormatter extends ValueFormatter { /** @@ -52,7 +48,7 @@ public void setup(int digits) { } @Override - public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + public String getFormattedValue(float value) { // put more logic here ... // avoid memory allocations here (for performance reasons) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java index 51939b5432..970ea6fca8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java @@ -6,7 +6,10 @@ * Created by Philipp Jahoda on 20/09/15. * Custom formatter interface that allows formatting of * axis labels before they are being drawn. + * + * @deprecated Extend {@link ValueFormatter} instead */ +@Deprecated public interface IAxisValueFormatter { @@ -18,6 +21,9 @@ public interface IAxisValueFormatter * @param value the value to be formatted * @param axis the axis the value belongs to * @return + * + * @deprecated Extend {@link ValueFormatter} and use {@link ValueFormatter#getAxisLabel(float, AxisBase)} */ + @Deprecated String getFormattedValue(float value, AxisBase axis); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java index 75d2363f26..0dde7012e3 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java @@ -4,13 +4,12 @@ import com.github.mikephil.charting.utils.ViewPortHandler; /** - * Interface that allows custom formatting of all values inside the chart before they are - * being drawn to the screen. Simply create your own formatting class and let - * it implement IValueFormatter. Then override the getFormattedValue(...) method - * and return whatever you want. + * Interface to format all values before they are drawn as labels. * * @author Philipp Jahoda + * @deprecated Extend {@link ValueFormatter} instead */ +@Deprecated public interface IValueFormatter { @@ -24,6 +23,9 @@ public interface IValueFormatter * @param dataSetIndex the index of the DataSet the entry in focus belongs to * @param viewPortHandler provides information about the current chart state (scale, translation, ...) * @return the formatted label ready for being drawn + * + * @deprecated Extend {@link ValueFormatter} and override an appropriate method */ + @Deprecated String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java index 07349a6a0e..7ab7bdbe7d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java @@ -1,18 +1,11 @@ - package com.github.mikephil.charting.formatter; -import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.text.DecimalFormat; -import java.util.Arrays; import java.util.Collection; /** * This formatter is used for passing an array of x-axis labels, on whole x steps. */ -public class IndexAxisValueFormatter implements IAxisValueFormatter +public class IndexAxisValueFormatter extends ValueFormatter { private String[] mValues = new String[] {}; private int mValueCount = 0; @@ -44,7 +37,8 @@ public IndexAxisValueFormatter(Collection values) { setValues(values.toArray(new String[values.size()])); } - public String getFormattedValue(float value, AxisBase axis) { + @Override + public String getFormattedValue(float value) { int index = Math.round(value); if (index < 0 || index >= mValueCount || index != (int)value) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java index 211401ad8a..4870a4cff4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java @@ -1,10 +1,5 @@ - package com.github.mikephil.charting.formatter; -import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.utils.ViewPortHandler; - import java.text.DecimalFormat; /** @@ -17,7 +12,7 @@ * @author Philipp Jahoda * @author Oleksandr Tyshkovets */ -public class LargeValueFormatter implements IValueFormatter, IAxisValueFormatter +public class LargeValueFormatter extends ValueFormatter { private String[] mSuffix = new String[]{ @@ -41,15 +36,8 @@ public LargeValueFormatter(String appendix) { mText = appendix; } - // IValueFormatter - @Override - public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return makePretty(value) + mText; - } - - // IAxisValueFormatter @Override - public String getFormattedValue(float value, AxisBase axis) { + public String getFormattedValue(float value) { return makePretty(value) + mText; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java index de8a10255a..6bf1bd3c33 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java @@ -1,9 +1,7 @@ - package com.github.mikephil.charting.formatter; -import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.utils.ViewPortHandler; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.data.PieEntry; import java.text.DecimalFormat; @@ -13,37 +11,36 @@ * * @author Philipp Jahoda */ -public class PercentFormatter implements IValueFormatter, IAxisValueFormatter +public class PercentFormatter extends ValueFormatter { - protected DecimalFormat mFormat; + public DecimalFormat mFormat; + private PieChart pieChart; public PercentFormatter() { mFormat = new DecimalFormat("###,###,##0.0"); } - /** - * Allow a custom decimalformat - * - * @param format - */ - public PercentFormatter(DecimalFormat format) { - this.mFormat = format; + // Can be used to remove percent signs if the chart isn't in percent mode + public PercentFormatter(PieChart pieChart) { + this(); + this.pieChart = pieChart; } - // IValueFormatter @Override - public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + public String getFormattedValue(float value) { return mFormat.format(value) + " %"; } - // IAxisValueFormatter @Override - public String getFormattedValue(float value, AxisBase axis) { - return mFormat.format(value) + " %"; + public String getPieLabel(float value, PieEntry pieEntry) { + if (pieChart != null && pieChart.isUsePercentValuesEnabled()) { + // Converted to percent + return getFormattedValue(value); + } else { + // raw value, skip percent sign + return mFormat.format(value); + } } - public int getDecimalDigits() { - return 1; - } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java index 0e8351634f..7c69dcf5d6 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java @@ -1,8 +1,6 @@ package com.github.mikephil.charting.formatter; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.utils.ViewPortHandler; import java.text.DecimalFormat; @@ -12,7 +10,7 @@ * A formatter specifically for stacked BarChart that allows to specify whether the all stack values * or just the top value should be drawn. */ -public class StackedValueFormatter implements IValueFormatter +public class StackedValueFormatter extends ValueFormatter { /** @@ -23,7 +21,7 @@ public class StackedValueFormatter implements IValueFormatter /** * a string that should be appended behind the value */ - private String mAppendix; + private String mSuffix; private DecimalFormat mFormat; @@ -31,12 +29,12 @@ public class StackedValueFormatter implements IValueFormatter * Constructor. * * @param drawWholeStack if true, all stack values of the stacked bar entry are drawn, else only top - * @param appendix a string that should be appended behind the value + * @param suffix a string that should be appended behind the value * @param decimals the number of decimal digits to use */ - public StackedValueFormatter(boolean drawWholeStack, String appendix, int decimals) { + public StackedValueFormatter(boolean drawWholeStack, String suffix, int decimals) { this.mDrawWholeStack = drawWholeStack; - this.mAppendix = appendix; + this.mSuffix = suffix; StringBuffer b = new StringBuffer(); for (int i = 0; i < decimals; i++) { @@ -49,12 +47,10 @@ public StackedValueFormatter(boolean drawWholeStack, String appendix, int decima } @Override - public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + public String getBarStackedLabel(float value, BarEntry entry) { + if (!mDrawWholeStack) { - if (!mDrawWholeStack && entry instanceof BarEntry) { - - BarEntry barEntry = (BarEntry) entry; - float[] vals = barEntry.getYVals(); + float[] vals = entry.getYVals(); if (vals != null) { @@ -62,7 +58,7 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View if (vals[vals.length - 1] == value) { // return the "sum" across all stack values - return mFormat.format(barEntry.getY()) + mAppendix; + return mFormat.format(entry.getY()) + mSuffix; } else { return ""; // return empty } @@ -70,6 +66,6 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View } // return the "proposed" value - return mFormat.format(value) + mAppendix; + return mFormat.format(value) + mSuffix; } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java new file mode 100644 index 0000000000..d2f53cb78b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java @@ -0,0 +1,137 @@ +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.BubbleEntry; +import com.github.mikephil.charting.data.CandleEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.data.RadarEntry; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Class to format all values before they are drawn as labels. + */ +public abstract class ValueFormatter implements IAxisValueFormatter, IValueFormatter{ + + /** + * DO NOT USE, only for backwards compatibility and will be removed in future versions. + * + * @param value the value to be formatted + * @param axis the axis the value belongs to + * @return formatted string label + */ + @Override + @Deprecated + public String getFormattedValue(float value, AxisBase axis) { + return getFormattedValue(value); + } + + /** + * DO NOT USE, only for backwards compatibility and will be removed in future versions. + * @param value the value to be formatted + * @param entry the entry the value belongs to - in e.g. BarChart, this is of class BarEntry + * @param dataSetIndex the index of the DataSet the entry in focus belongs to + * @param viewPortHandler provides information about the current chart state (scale, translation, ...) + * @return formatted string label + */ + @Override + @Deprecated + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return getFormattedValue(value); + } + + /** + * Called when drawing any label, used to change numbers into formatted strings. + * + * @param value float to be formatted + * @return formatted string label + */ + public String getFormattedValue(float value) { + return String.valueOf(value); + } + + /** + * Used to draw axis labels, calls {@link #getFormattedValue(float)} by default. + * + * @param value float to be formatted + * @param axis axis being labeled + * @return formatted string label + */ + public String getAxisLabel(float value, AxisBase axis) { + return getFormattedValue(value); + } + + /** + * Used to draw bar labels, calls {@link #getFormattedValue(float)} by default. + * + * @param barEntry bar being labeled + * @return formatted string label + */ + public String getBarLabel(BarEntry barEntry) { + return getFormattedValue(barEntry.getY()); + } + + /** + * Used to draw stacked bar labels, calls {@link #getFormattedValue(float)} by default. + * + * @param value current value to be formatted + * @param stackedEntry stacked entry being labeled, contains all Y values + * @return formatted string label + */ + public String getBarStackedLabel(float value, BarEntry stackedEntry) { + return getFormattedValue(value); + } + + /** + * Used to draw line and scatter labels, calls {@link #getFormattedValue(float)} by default. + * + * @param entry point being labeled, contains X value + * @return formatted string label + */ + public String getPointLabel(Entry entry) { + return getFormattedValue(entry.getY()); + } + + /** + * Used to draw pie value labels, calls {@link #getFormattedValue(float)} by default. + * + * @param value float to be formatted, may have been converted to percentage + * @param pieEntry slice being labeled, contains original, non-percentage Y value + * @return formatted string label + */ + public String getPieLabel(float value, PieEntry pieEntry) { + return getFormattedValue(value); + } + + /** + * Used to draw radar value labels, calls {@link #getFormattedValue(float)} by default. + * + * @param radarEntry entry being labeled + * @return formatted string label + */ + public String getRadarLabel(RadarEntry radarEntry) { + return getFormattedValue(radarEntry.getY()); + } + + /** + * Used to draw bubble size labels, calls {@link #getFormattedValue(float)} by default. + * + * @param bubbleEntry bubble being labeled, also contains X and Y values + * @return formatted string label + */ + public String getBubbleLabel(BubbleEntry bubbleEntry) { + return getFormattedValue(bubbleEntry.getSize()); + } + + /** + * Used to draw high labels, calls {@link #getFormattedValue(float)} by default. + * + * @param candleEntry candlestick being labeled + * @return formatted string label + */ + public String getCandleLabel(CandleEntry candleEntry) { + return getFormattedValue(candleEntry.getHigh()); + } + +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java index 219b46bd82..182aa28757 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java @@ -3,7 +3,7 @@ import android.graphics.RectF; import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.utils.MPPointF; /** @@ -61,7 +61,7 @@ public interface ChartInterface { RectF getContentRect(); - IValueFormatter getDefaultValueFormatter(); + ValueFormatter getDefaultValueFormatter(); ChartData getData(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index f64db706e0..73a54470c8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -1,14 +1,13 @@ package com.github.mikephil.charting.interfaces.datasets; import android.graphics.DashPathEffect; -import android.graphics.PointF; import android.graphics.Typeface; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.model.GradientColor; @@ -341,14 +340,14 @@ public interface IDataSet { * * @param f */ - void setValueFormatter(IValueFormatter f); + void setValueFormatter(ValueFormatter f); /** * Returns the formatter used for drawing the values inside the chart. * * @return */ - IValueFormatter getValueFormatter(); + ValueFormatter getValueFormatter(); /** * Returns true if the valueFormatter object of this DataSet is null. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index d3f71af02c..b5de65b02e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -11,6 +10,7 @@ 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.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.highlight.Range; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; @@ -254,6 +254,8 @@ public void drawValues(Canvas c) { final float phaseY = mAnimator.getPhaseY(); + ValueFormatter formatter = dataSet.getValueFormatter(); + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); @@ -276,8 +278,7 @@ public void drawValues(Canvas c) { float val = entry.getY(); if (dataSet.isDrawValuesEnabled()) { - drawValue(c, dataSet.getValueFormatter(), val, entry, i, x, - val >= 0 ? + drawValue(c, formatter.getBarLabel(entry), x, val >= 0 ? (buffer.buffer[j + 1] + posOffset) : (buffer.buffer[j + 3] + negOffset), dataSet.getValueTextColor(j / 4)); @@ -335,8 +336,7 @@ public void drawValues(Canvas c) { continue; if (dataSet.isDrawValuesEnabled()) { - drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, - buffer.buffer[bufferIndex + 1] + + drawValue(c, formatter.getBarLabel(entry), x, buffer.buffer[bufferIndex + 1] + (entry.getY() >= 0 ? posOffset : negOffset), color); } @@ -407,14 +407,7 @@ public void drawValues(Canvas c) { continue; if (dataSet.isDrawValuesEnabled()) { - drawValue(c, - dataSet.getValueFormatter(), - vals[k / 2], - entry, - i, - x, - y, - color); + drawValue(c, formatter.getBarStackedLabel(val, entry), x, y, color); } if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { @@ -442,6 +435,12 @@ public void drawValues(Canvas c) { } } + @Override + public void drawValue(Canvas c, String valueText, float x, float y, int color) { + mValuePaint.setColor(color); + c.drawText(valueText, x, y, mValuePaint); + } + @Override public void drawHighlighted(Canvas c, Highlight[] indices) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index d53dcd4785..57b81c1d9c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -9,6 +8,7 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.data.BubbleData; import com.github.mikephil.charting.data.BubbleEntry; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; @@ -150,6 +150,8 @@ public void drawValues(Canvas c) { final float alpha = phaseX == 1 ? phaseY : phaseX; + ValueFormatter formatter = dataSet.getValueFormatter(); + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); @@ -172,8 +174,7 @@ public void drawValues(Canvas c) { BubbleEntry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); if (dataSet.isDrawValuesEnabled()) { - drawValue(c, dataSet.getValueFormatter(), entry.getSize(), entry, i, x, - y + (0.5f * lineHeight), valueTextColor); + drawValue(c, formatter.getBubbleLabel(entry), x, y + (0.5f * lineHeight), valueTextColor); } if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { @@ -195,6 +196,12 @@ public void drawValues(Canvas c) { } } + @Override + public void drawValue(Canvas c, String valueText, float x, float y, int color) { + mValuePaint.setColor(color); + c.drawText(valueText, x, y, mValuePaint); + } + @Override public void drawExtras(Canvas c) { } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index 991b702117..027dda31d8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -8,6 +7,7 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.data.CandleData; import com.github.mikephil.charting.data.CandleEntry; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; @@ -279,6 +279,8 @@ public void drawValues(Canvas c) { float yOffset = Utils.convertDpToPixel(5f); + ValueFormatter formatter = dataSet.getValueFormatter(); + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); @@ -297,15 +299,7 @@ public void drawValues(Canvas c) { CandleEntry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); if (dataSet.isDrawValuesEnabled()) { - drawValue(c, - dataSet.getValueFormatter(), - entry.getHigh(), - entry, - i, - x, - y - yOffset, - dataSet - .getValueTextColor(j / 2)); + drawValue(c, formatter.getCandleLabel(entry), x, y - yOffset, dataSet.getValueTextColor(j / 2)); } if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { @@ -327,6 +321,12 @@ public void drawValues(Canvas c) { } } + @Override + public void drawValue(Canvas c, String valueText, float x, float y, int color) { + mValuePaint.setColor(color); + c.drawText(valueText, x, y, mValuePaint); + } + @Override public void drawExtras(Canvas c) { } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java index 6d0d4d3da0..8f6be3c054 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.renderer; import android.graphics.Canvas; +import android.util.Log; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.charts.Chart; @@ -9,7 +10,6 @@ import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.CombinedData; import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; import com.github.mikephil.charting.utils.ViewPortHandler; import java.lang.ref.WeakReference; @@ -89,6 +89,11 @@ public void drawData(Canvas c) { renderer.drawData(c); } + @Override + public void drawValue(Canvas c, String valueText, float x, float y, int color) { + Log.e("MPAndroidChart", "Erroneous call to drawValue() in CombinedChartRenderer!"); + } + @Override public void drawValues(Canvas c) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java index e8e5446f4d..da4a26edca 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -6,15 +5,11 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Paint.Style; -import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -138,19 +133,13 @@ protected void applyValueTextStyle(IDataSet set) { /** * Draws the value of the given entry by using the provided IValueFormatter. * - * @param c canvas - * @param formatter formatter for custom value-formatting - * @param value the value to be drawn - * @param entry the entry the value belongs to - * @param dataSetIndex the index of the DataSet the drawn Entry belongs to - * @param x position - * @param y position + * @param c canvas + * @param valueText label to draw + * @param x position + * @param y position * @param color */ - public void drawValue(Canvas c, IValueFormatter formatter, float value, Entry entry, int dataSetIndex, float x, float y, int color) { - mValuePaint.setColor(color); - c.drawText(formatter.getFormattedValue(value, entry, dataSetIndex, mViewPortHandler), x, y, mValuePaint); - } + public abstract void drawValue(Canvas c, String valueText, float x, float y, int color); /** * Draws any kind of additional information (e.g. line-circles). diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index a1e1650865..7607abdd92 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -11,7 +10,7 @@ import com.github.mikephil.charting.buffer.HorizontalBarBuffer; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; @@ -167,7 +166,7 @@ public void drawValues(Canvas c) { applyValueTextStyle(dataSet); final float halfTextHeight = Utils.calcTextHeight(mValuePaint, "10") / 2f; - IValueFormatter formatter = dataSet.getValueFormatter(); + ValueFormatter formatter = dataSet.getValueFormatter(); // get the buffer BarBuffer buffer = mBarBuffers[i]; @@ -196,7 +195,7 @@ public void drawValues(Canvas c) { BarEntry entry = dataSet.getEntryForIndex(j / 4); float val = entry.getY(); - String formattedValue = formatter.getFormattedValue(val, entry, i, mViewPortHandler); + String formattedValue = formatter.getBarLabel(entry); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); @@ -265,9 +264,7 @@ public void drawValues(Canvas c) { if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[bufferIndex + 1])) continue; - float val = entry.getY(); - String formattedValue = formatter.getFormattedValue(val, - entry, i, mViewPortHandler); + String formattedValue = formatter.getBarLabel(entry); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); @@ -337,8 +334,7 @@ public void drawValues(Canvas c) { for (int k = 0; k < transformed.length; k += 2) { final float val = vals[k / 2]; - String formattedValue = formatter.getFormattedValue(val, - entry, i, mViewPortHandler); + String formattedValue = formatter.getBarStackedLabel(val, entry); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); @@ -396,7 +392,8 @@ public void drawValues(Canvas c) { } } - protected void drawValue(Canvas c, String valueText, float x, float y, int color) { + @Override + public void drawValue(Canvas c, String valueText, float x, float y, int color) { mValuePaint.setColor(color); c.drawText(valueText, x, y, mValuePaint); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 7beb6ca5be..ead9d6d701 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -12,6 +12,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; import com.github.mikephil.charting.interfaces.datasets.IDataSet; @@ -294,7 +295,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { int entryCount = dataSet.getEntryCount(); - final boolean isDrawSteppedEnabled = dataSet.isDrawSteppedEnabled(); + final boolean isDrawSteppedEnabled = dataSet.getMode() == LineDataSet.Mode.STEPPED; final int pointsPerEntryPair = isDrawSteppedEnabled ? 4 : 2; Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); @@ -547,6 +548,7 @@ public void drawValues(Canvas c) { float[] positions = trans.generateTransformedValuesLine(dataSet, mAnimator.getPhaseX(), mAnimator .getPhaseY(), mXBounds.min, mXBounds.max); + ValueFormatter formatter = dataSet.getValueFormatter(); MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); @@ -566,8 +568,7 @@ public void drawValues(Canvas c) { Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); if (dataSet.isDrawValuesEnabled()) { - drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, - y - valOffset, dataSet.getValueTextColor(j / 2)); + drawValue(c, formatter.getPointLabel(entry), x, y - valOffset, dataSet.getValueTextColor(j / 2)); } if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { @@ -589,6 +590,12 @@ public void drawValues(Canvas c) { } } + @Override + public void drawValue(Canvas c, String valueText, float x, float y, int color) { + mValuePaint.setColor(color); + c.drawText(valueText, x, y, mValuePaint); + } + @Override public void drawExtras(Canvas c) { drawCircles(c); 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 8c37a0b83d..b14657cefc 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.renderer; import android.graphics.Bitmap; @@ -22,7 +21,7 @@ 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.IValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -438,7 +437,7 @@ public void drawValues(Canvas c) { float lineHeight = Utils.calcTextHeight(mValuePaint, "Q") + Utils.convertDpToPixel(4f); - IValueFormatter formatter = dataSet.getValueFormatter(); + ValueFormatter formatter = dataSet.getValueFormatter(); int entryCount = dataSet.getEntryCount(); @@ -472,6 +471,7 @@ public void drawValues(Canvas c) { float value = mChart.isUsePercentValuesEnabled() ? entry.getY() / yValueSum * 100f : entry.getY(); + String formattedValue = formatter.getPieLabel(value, entry); final float sliceXBase = (float) Math.cos(transformedAngle * Utils.FDEG2RAD); final float sliceYBase = (float) Math.sin(transformedAngle * Utils.FDEG2RAD); @@ -550,14 +550,7 @@ public void drawValues(Canvas c) { // draw everything, depending on settings if (drawXOutside && drawYOutside) { - drawValue(c, - formatter, - value, - entry, - 0, - labelPtx, - labelPty, - dataSet.getValueTextColor(j)); + drawValue(c, formattedValue, labelPtx, labelPty, dataSet.getValueTextColor(j)); if (j < data.getEntryCount() && entry.getLabel() != null) { drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight); @@ -569,8 +562,7 @@ public void drawValues(Canvas c) { } } else if (drawYOutside) { - drawValue(c, formatter, value, entry, 0, labelPtx, labelPty + lineHeight / 2.f, dataSet - .getValueTextColor(j)); + drawValue(c, formattedValue, labelPtx, labelPty + lineHeight / 2.f, dataSet.getValueTextColor(j)); } } @@ -584,7 +576,7 @@ public void drawValues(Canvas c) { // draw everything, depending on settings if (drawXInside && drawYInside) { - drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j)); + drawValue(c, formattedValue, x, y, dataSet.getValueTextColor(j)); if (j < data.getEntryCount() && entry.getLabel() != null) { drawEntryLabel(c, entry.getLabel(), x, y + lineHeight); @@ -595,8 +587,7 @@ public void drawValues(Canvas c) { drawEntryLabel(c, entry.getLabel(), x, y + lineHeight / 2f); } } else if (drawYInside) { - - drawValue(c, formatter, value, entry, 0, x, y + lineHeight / 2f, dataSet.getValueTextColor(j)); + drawValue(c, formattedValue, x, y + lineHeight / 2f, dataSet.getValueTextColor(j)); } } @@ -626,6 +617,12 @@ public void drawValues(Canvas c) { c.restore(); } + @Override + public void drawValue(Canvas c, String valueText, float x, float y, int color) { + mValuePaint.setColor(color); + c.drawText(valueText, x, y, mValuePaint); + } + /** * Draws an entry label at the specified position. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java index dbf0e8f807..3f932f8725 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -11,6 +10,7 @@ import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.data.RadarData; import com.github.mikephil.charting.data.RadarEntry; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -174,6 +174,8 @@ public void drawValues(Canvas c) { // apply the text-styling defined by the DataSet applyValueTextStyle(dataSet); + ValueFormatter formatter = dataSet.getValueFormatter(); + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); @@ -189,15 +191,7 @@ public void drawValues(Canvas c) { pOut); if (dataSet.isDrawValuesEnabled()) { - drawValue(c, - dataSet.getValueFormatter(), - entry.getY(), - entry, - i, - pOut.x, - pOut.y - yoffset, - dataSet.getValueTextColor - (j)); + drawValue(c, formatter.getRadarLabel(entry), pOut.x, pOut.y - yoffset, dataSet.getValueTextColor(j)); } if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { @@ -231,6 +225,12 @@ public void drawValues(Canvas c) { MPPointF.recycleInstance(pIcon); } + @Override + public void drawValue(Canvas c, String valueText, float x, float y, int color) { + mValuePaint.setColor(color); + c.drawText(valueText, x, y, mValuePaint); + } + @Override public void drawExtras(Canvas c) { drawWeb(c); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index ccd077e55c..98dddb93f9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -8,6 +7,7 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; @@ -118,6 +118,8 @@ public void drawValues(Canvas c) { float shapeSize = Utils.convertDpToPixel(dataSet.getScatterShapeSize()); + ValueFormatter formatter = dataSet.getValueFormatter(); + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); @@ -135,14 +137,7 @@ public void drawValues(Canvas c) { Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); if (dataSet.isDrawValuesEnabled()) { - drawValue(c, - dataSet.getValueFormatter(), - entry.getY(), - entry, - i, - positions[j], - positions[j + 1] - shapeSize, - dataSet.getValueTextColor(j / 2 + mXBounds.min)); + drawValue(c, formatter.getPointLabel(entry), positions[j], positions[j + 1] - shapeSize, dataSet.getValueTextColor(j / 2 + mXBounds.min)); } if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { @@ -164,6 +159,12 @@ public void drawValues(Canvas c) { } } + @Override + public void drawValue(Canvas c, String valueText, float x, float y, int color) { + mValuePaint.setColor(color); + c.drawText(valueText, x, y, mValuePaint); + } + @Override public void drawExtras(Canvas c) { } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java index 8adb56c73a..046f3469bc 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -202,7 +201,7 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { if (mViewPortHandler.isInBoundsX(x)) { - String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis); + String label = mXAxis.getValueFormatter().getAxisLabel(mXAxis.mEntries[i / 2], mXAxis); if (mXAxis.isAvoidFirstLastClippingEnabled()) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java index 86047cf1b8..9054dcb679 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -57,10 +56,10 @@ public void computeAxis(float min, float max, boolean inverted) { computeAxisValues(min, max); } - + @Override protected void computeSize() { - + mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); @@ -156,7 +155,7 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { if (mViewPortHandler.isInBoundsY(y)) { - String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis); + String label = mXAxis.getValueFormatter().getAxisLabel(mXAxis.mEntries[i / 2], mXAxis); drawLabel(c, label, pos, y, anchor, labelRotationAngleDegrees); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java index 956e8c7d5c..6d83cf59e4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java @@ -1,8 +1,6 @@ - package com.github.mikephil.charting.renderer; import android.graphics.Canvas; -import android.graphics.PointF; import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.components.XAxis; @@ -43,7 +41,7 @@ public void renderAxisLabels(Canvas c) { MPPointF pOut = MPPointF.getInstance(0,0); for (int i = 0; i < mChart.getData().getMaxEntryCountSet().getEntryCount(); i++) { - String label = mXAxis.getValueFormatter().getFormattedValue(i, mXAxis); + String label = mXAxis.getValueFormatter().getAxisLabel(i, mXAxis); float angle = (sliceangle * i + mChart.getRotationAngle()) % 360f; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java index c302673919..60ff6ba3da 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.utils; import android.annotation.SuppressLint; @@ -7,7 +6,6 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.text.Layout; @@ -15,14 +13,13 @@ import android.text.TextPaint; import android.util.DisplayMetrics; import android.util.Log; -import android.util.SizeF; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import com.github.mikephil.charting.formatter.DefaultValueFormatter; -import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.formatter.ValueFormatter; import java.util.List; @@ -229,15 +226,14 @@ public static void calcTextSize(Paint paint, String demoText, FSize outputFSize) 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; - private static IValueFormatter mDefaultValueFormatter = generateDefaultValueFormatter(); + private static ValueFormatter mDefaultValueFormatter = generateDefaultValueFormatter(); - private static IValueFormatter generateDefaultValueFormatter() { - final DefaultValueFormatter formatter = new DefaultValueFormatter(1); - return formatter; + private static ValueFormatter generateDefaultValueFormatter() { + return new DefaultValueFormatter(1); } /// - returns: The default value formatter used for all chart components that needs a default - public static IValueFormatter getDefaultValueFormatter() + public static ValueFormatter getDefaultValueFormatter() { return mDefaultValueFormatter; } @@ -353,11 +349,11 @@ public static String formatNumber(float number, int digitCount, boolean separate * @return */ public static float roundToNextSignificant(double number) { - if (Double.isInfinite(number) || - Double.isNaN(number) || + if (Double.isInfinite(number) || + Double.isNaN(number) || number == 0.0) return 0; - + final float d = (float) Math.ceil((float) Math.log10(number < 0 ? -number : number)); final int pw = 1 - (int) d; final float magnitude = (float) Math.pow(10, pw); @@ -375,10 +371,10 @@ public static float roundToNextSignificant(double number) { public static int getDecimals(float number) { float i = roundToNextSignificant(number); - + if (Float.isInfinite(i)) return 0; - + return (int) Math.ceil(-Math.log10(i)) + 2; } diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java index f1e1e0279e..fc7eb93e75 100644 --- a/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java @@ -16,80 +16,80 @@ public void test() { LargeValueFormatter formatter = new LargeValueFormatter(); - String result = formatter.getFormattedValue(5f, null); + String result = formatter.getFormattedValue(5f); assertEquals("5", result); - result = formatter.getFormattedValue(5.5f, null); + result = formatter.getFormattedValue(5.5f); assertEquals("5.5", result); - result = formatter.getFormattedValue(50f, null); + result = formatter.getFormattedValue(50f); assertEquals("50", result); - result = formatter.getFormattedValue(50.5f, null); + result = formatter.getFormattedValue(50.5f); assertEquals("50.5", result); - result = formatter.getFormattedValue(500f, null); + result = formatter.getFormattedValue(500f); assertEquals("500", result); - result = formatter.getFormattedValue(1100f, null); + result = formatter.getFormattedValue(1100f); assertEquals("1.1k", result); - result = formatter.getFormattedValue(10000f, null); + result = formatter.getFormattedValue(10000f); assertEquals("10k", result); - result = formatter.getFormattedValue(10500f, null); + result = formatter.getFormattedValue(10500f); assertEquals("10.5k", result); - result = formatter.getFormattedValue(100000f, null); + result = formatter.getFormattedValue(100000f); assertEquals("100k", result); - result = formatter.getFormattedValue(1000000f, null); + result = formatter.getFormattedValue(1000000f); assertEquals("1m", result); - result = formatter.getFormattedValue(1500000f, null); + result = formatter.getFormattedValue(1500000f); assertEquals("1.5m", result); - result = formatter.getFormattedValue(9500000f, null); + result = formatter.getFormattedValue(9500000f); assertEquals("9.5m", result); - result = formatter.getFormattedValue(22200000f, null); + result = formatter.getFormattedValue(22200000f); assertEquals("22.2m", result); - result = formatter.getFormattedValue(222000000f, null); + result = formatter.getFormattedValue(222000000f); assertEquals("222m", result); - result = formatter.getFormattedValue(1000000000f, null); + result = formatter.getFormattedValue(1000000000f); assertEquals("1b", result); - result = formatter.getFormattedValue(9900000000f, null); + result = formatter.getFormattedValue(9900000000f); assertEquals("9.9b", result); - result = formatter.getFormattedValue(99000000000f, null); + result = formatter.getFormattedValue(99000000000f); assertEquals("99b", result); - result = formatter.getFormattedValue(99500000000f, null); + result = formatter.getFormattedValue(99500000000f); assertEquals("99.5b", result); - result = formatter.getFormattedValue(999000000000f, null); + result = formatter.getFormattedValue(999000000000f); assertEquals("999b", result); - result = formatter.getFormattedValue(1000000000000f, null); + result = formatter.getFormattedValue(1000000000000f); assertEquals("1t", result); formatter.setSuffix(new String[]{"", "k", "m", "b", "t", "q"}); // quadrillion support - result = formatter.getFormattedValue(1000000000000000f, null); + result = formatter.getFormattedValue(1000000000000000f); assertEquals("1q", result); - result = formatter.getFormattedValue(1100000000000000f, null); + result = formatter.getFormattedValue(1100000000000000f); assertEquals("1.1q", result); - result = formatter.getFormattedValue(10000000000000000f, null); + result = formatter.getFormattedValue(10000000000000000f); assertEquals("10q", result); - result = formatter.getFormattedValue(13300000000000000f, null); + result = formatter.getFormattedValue(13300000000000000f); assertEquals("13.3q", result); - result = formatter.getFormattedValue(100000000000000000f, null); + result = formatter.getFormattedValue(100000000000000000f); assertEquals("100q", result); } } diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java index e1dbe81be9..44946cf4da 100644 --- a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java @@ -2,7 +2,7 @@ import com.github.mikephil.charting.utils.ObjectPool; -import junit.framework.Assert; +import org.junit.Assert; import org.junit.Test; From fc0e2342984758849384199f0225e0c6b5555168 Mon Sep 17 00:00:00 2001 From: almic Date: Wed, 7 Nov 2018 14:52:25 -0700 Subject: [PATCH 227/291] Remove Deprecated Things Long deprecated Legend constructor and positioning has been removed, it was replaced with a new way to position the Legend. The old Easing options have been removed now, accessing them is as easy as removing the `EasingOption` part, such that the names look like `Easing.Linear` or `Easing.EaseInOutQuad` now. --- .../charting/animation/ChartAnimator.java | 93 --------------- .../mikephil/charting/animation/Easing.java | 107 ----------------- .../mikephil/charting/charts/Chart.java | 54 --------- .../mikephil/charting/components/Legend.java | 110 ------------------ 4 files changed, 364 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java index b33b3fb69d..e5b82db0b6 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.animation; import android.animation.ObjectAnimator; @@ -160,98 +159,6 @@ public void animateY(int durationMillis, EasingFunction easing) { animatorY.start(); } - /** - * Animates the drawing / rendering of the chart on both x- and y-axis with - * the specified animation time. If animate(...) is called, no further - * calling of invalidate() is necessary to refresh the chart. - * - * @param durationMillisX animation duration along the X axis - * @param durationMillisY animation duration along the Y axis - * @param easingX EasingFunction for the X axis - * @param easingY EasingFunction for the Y axis - * - * @deprecated Use {@link #animateXY(int, int, EasingFunction, EasingFunction)} - * @see #animateXY(int, int, EasingFunction, EasingFunction) - */ - @SuppressWarnings("deprecation") - @Deprecated - public void animateXY(int durationMillisX, int durationMillisY, Easing.EasingOption easingX, - Easing.EasingOption easingY) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - animatorY.setInterpolator(Easing.getEasingFunctionFromOption(easingY)); - animatorY.setDuration( - durationMillisY); - ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - animatorX.setInterpolator(Easing.getEasingFunctionFromOption(easingX)); - animatorX.setDuration( - durationMillisX); - - // make sure only one animator produces update-callbacks (which then - // call invalidate()) - if (durationMillisX > durationMillisY) { - animatorX.addUpdateListener(mListener); - } else { - animatorY.addUpdateListener(mListener); - } - - animatorX.start(); - animatorY.start(); - } - - /** - * Animates the rendering of the chart on the x-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. - * - * @param durationMillis animation duration - * @param easing EasingFunction - * - * @deprecated Use {@link #animateX(int, EasingFunction)} - * @see #animateX(int, EasingFunction) - */ - @SuppressWarnings("deprecation") - @Deprecated - public void animateX(int durationMillis, Easing.EasingOption easing) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - animatorX.setInterpolator(Easing.getEasingFunctionFromOption(easing)); - animatorX.setDuration(durationMillis); - animatorX.addUpdateListener(mListener); - animatorX.start(); - } - - /** - * Animates the rendering of the chart on the y-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. - * - * @param durationMillis animation duration - * @param easing EasingFunction - * - * @deprecated Use {@link #animateY(int, EasingFunction)} - * @see #animateY(int, EasingFunction) - */ - @SuppressWarnings("deprecation") - @Deprecated - public void animateY(int durationMillis, Easing.EasingOption easing) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - animatorY.setInterpolator(Easing.getEasingFunctionFromOption(easing)); - animatorY.setDuration(durationMillis); - animatorY.addUpdateListener(mListener); - animatorY.start(); - } - /** * Gets the Y axis phase of the animation. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java index 2c64777a6a..acb7dcc965 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.animation; import android.animation.TimeInterpolator; @@ -19,112 +18,6 @@ public interface EasingFunction extends TimeInterpolator { float getInterpolation(float input); } - /** - * Enum holding EasingOption constants - * - * @deprecated Use Easing.Linear instead of Easing.EasingOption.Linear - */ - @Deprecated - public enum EasingOption { - Linear, - EaseInQuad, - EaseOutQuad, - EaseInOutQuad, - EaseInCubic, - EaseOutCubic, - EaseInOutCubic, - EaseInQuart, - EaseOutQuart, - EaseInOutQuart, - EaseInSine, - EaseOutSine, - EaseInOutSine, - EaseInExpo, - EaseOutExpo, - EaseInOutExpo, - EaseInCirc, - EaseOutCirc, - EaseInOutCirc, - EaseInElastic, - EaseOutElastic, - EaseInOutElastic, - EaseInBack, - EaseOutBack, - EaseInOutBack, - EaseInBounce, - EaseOutBounce, - EaseInOutBounce, - } - - /** - * Returns the EasingFunction of the given EasingOption - * - * @param easing EasingOption to get - * @return EasingFunction - */ - @Deprecated - public static EasingFunction getEasingFunctionFromOption(EasingOption easing) { - switch (easing) { - default: - case Linear: - return Easing.Linear; - case EaseInQuad: - return Easing.EaseInQuad; - case EaseOutQuad: - return Easing.EaseOutQuad; - case EaseInOutQuad: - return Easing.EaseInOutQuad; - case EaseInCubic: - return Easing.EaseInCubic; - case EaseOutCubic: - return Easing.EaseOutCubic; - case EaseInOutCubic: - return Easing.EaseInOutCubic; - case EaseInQuart: - return Easing.EaseInQuart; - case EaseOutQuart: - return Easing.EaseOutQuart; - case EaseInOutQuart: - return Easing.EaseInOutQuart; - case EaseInSine: - return Easing.EaseInSine; - case EaseOutSine: - return Easing.EaseOutSine; - case EaseInOutSine: - return Easing.EaseInOutSine; - case EaseInExpo: - return Easing.EaseInExpo; - case EaseOutExpo: - return Easing.EaseOutExpo; - case EaseInOutExpo: - return Easing.EaseInOutExpo; - case EaseInCirc: - return Easing.EaseInCirc; - case EaseOutCirc: - return Easing.EaseOutCirc; - case EaseInOutCirc: - return Easing.EaseInOutCirc; - case EaseInElastic: - return Easing.EaseInElastic; - case EaseOutElastic: - return Easing.EaseOutElastic; - case EaseInOutElastic: - return Easing.EaseInOutElastic; - case EaseInBack: - return Easing.EaseInBack; - case EaseOutBack: - return Easing.EaseOutBack; - case EaseInOutBack: - return Easing.EaseInOutBack; - case EaseInBounce: - return Easing.EaseInBounce; - case EaseOutBounce: - return Easing.EaseOutBounce; - case EaseInOutBounce: - return Easing.EaseInOutBounce; - } - } - private static final float DOUBLE_PI = 2f * (float) Math.PI; @SuppressWarnings("unused") diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 5c82f9ab0e..0a1869e543 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -891,60 +891,6 @@ public void animateY(int durationMillis, EasingFunction easing) { */ /** CODE BELOW FOR PREDEFINED EASING OPTIONS */ - /** - * Animates the drawing / rendering of the chart on both x- and y-axis with - * the specified animation time. If animate(...) is called, no further - * calling of invalidate() is necessary to refresh the chart. ANIMATIONS - * ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. - * - * @param durationMillisX - * @param durationMillisY - * @param easingX a predefined easing option - * @param easingY a predefined easing option - * - * @deprecated Use {@link #animateXY(int, int, EasingFunction, EasingFunction)} - * @see #animateXY(int, int, EasingFunction, EasingFunction) - */ - @Deprecated - public void animateXY(int durationMillisX, int durationMillisY, Easing.EasingOption easingX, - Easing.EasingOption easingY) { - mAnimator.animateXY(durationMillisX, durationMillisY, easingX, easingY); - } - - /** - * Animates the rendering of the chart on the x-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. ANIMATIONS ONLY WORK FOR - * API LEVEL 11 (Android 3.0.x) AND HIGHER. - * - * @param durationMillis - * @param easing a predefined easing option - * - * @deprecated Use {@link #animateX(int, EasingFunction)} - * @see #animateX(int, EasingFunction) - */ - @Deprecated - public void animateX(int durationMillis, Easing.EasingOption easing) { - mAnimator.animateX(durationMillis, easing); - } - - /** - * Animates the rendering of the chart on the y-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. ANIMATIONS ONLY WORK FOR - * API LEVEL 11 (Android 3.0.x) AND HIGHER. - * - * @param durationMillis - * @param easing a predefined easing option - * - * @deprecated Use {@link #animateY(int, EasingFunction)} - * @see #animateY(int, EasingFunction) - */ - @Deprecated - public void animateY(int durationMillis, Easing.EasingOption easing) { - mAnimator.animateY(durationMillis, easing); - } - /** * ################ ################ ################ ################ * ANIMATIONS ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java index fb1afc8bba..e3782e7cdd 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java @@ -1,10 +1,8 @@ - package com.github.mikephil.charting.components; import android.graphics.DashPathEffect; import android.graphics.Paint; -import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.FSize; import com.github.mikephil.charting.utils.Utils; @@ -214,11 +212,6 @@ else if (entry.formColor == ColorTemplate.COLOR_NONE || mEntries = entries.toArray(new LegendEntry[entries.size()]); } - @Deprecated - public Legend(List colors, List labels) { - this(Utils.convertIntegers(colors), Utils.convertStrings(labels)); - } - /** * This method sets the automatically computed colors for the legend. Use setCustom(...) to set custom colors. * @@ -423,109 +416,6 @@ public boolean isLegendCustom() { return mIsLegendCustom; } - /** - * This property is deprecated - Use `horizontalAlignment`, `verticalAlignment`, `orientation`, `drawInside`, - * `direction`. - */ - @Deprecated - public LegendPosition getPosition() { - - if (mOrientation == LegendOrientation.VERTICAL - && mHorizontalAlignment == LegendHorizontalAlignment.CENTER - && mVerticalAlignment == LegendVerticalAlignment.CENTER) { - return LegendPosition.PIECHART_CENTER; - } else if (mOrientation == LegendOrientation.HORIZONTAL) { - if (mVerticalAlignment == LegendVerticalAlignment.TOP) - return mHorizontalAlignment == LegendHorizontalAlignment.LEFT - ? LegendPosition.ABOVE_CHART_LEFT - : (mHorizontalAlignment == LegendHorizontalAlignment.RIGHT - ? LegendPosition.ABOVE_CHART_RIGHT - : LegendPosition.ABOVE_CHART_CENTER); - else - return mHorizontalAlignment == LegendHorizontalAlignment.LEFT - ? LegendPosition.BELOW_CHART_LEFT - : (mHorizontalAlignment == LegendHorizontalAlignment.RIGHT - ? LegendPosition.BELOW_CHART_RIGHT - : LegendPosition.BELOW_CHART_CENTER); - } else { - if (mHorizontalAlignment == LegendHorizontalAlignment.LEFT) - return mVerticalAlignment == LegendVerticalAlignment.TOP && mDrawInside - ? LegendPosition.LEFT_OF_CHART_INSIDE - : (mVerticalAlignment == LegendVerticalAlignment.CENTER - ? LegendPosition.LEFT_OF_CHART_CENTER - : LegendPosition.LEFT_OF_CHART); - else - return mVerticalAlignment == LegendVerticalAlignment.TOP && mDrawInside - ? LegendPosition.RIGHT_OF_CHART_INSIDE - : (mVerticalAlignment == LegendVerticalAlignment.CENTER - ? LegendPosition.RIGHT_OF_CHART_CENTER - : LegendPosition.RIGHT_OF_CHART); - } - } - - /** - * This property is deprecated - Use `horizontalAlignment`, `verticalAlignment`, `orientation`, `drawInside`, - * `direction`. - */ - @Deprecated - public void setPosition(LegendPosition newValue) { - - switch (newValue) { - case LEFT_OF_CHART: - case LEFT_OF_CHART_INSIDE: - case LEFT_OF_CHART_CENTER: - mHorizontalAlignment = LegendHorizontalAlignment.LEFT; - mVerticalAlignment = newValue == LegendPosition.LEFT_OF_CHART_CENTER - ? LegendVerticalAlignment.CENTER - : LegendVerticalAlignment.TOP; - mOrientation = LegendOrientation.VERTICAL; - break; - - case RIGHT_OF_CHART: - case RIGHT_OF_CHART_INSIDE: - case RIGHT_OF_CHART_CENTER: - mHorizontalAlignment = LegendHorizontalAlignment.RIGHT; - mVerticalAlignment = newValue == LegendPosition.RIGHT_OF_CHART_CENTER - ? LegendVerticalAlignment.CENTER - : LegendVerticalAlignment.TOP; - mOrientation = LegendOrientation.VERTICAL; - break; - - case ABOVE_CHART_LEFT: - case ABOVE_CHART_CENTER: - case ABOVE_CHART_RIGHT: - mHorizontalAlignment = newValue == LegendPosition.ABOVE_CHART_LEFT - ? LegendHorizontalAlignment.LEFT - : (newValue == LegendPosition.ABOVE_CHART_RIGHT - ? LegendHorizontalAlignment.RIGHT - : LegendHorizontalAlignment.CENTER); - mVerticalAlignment = LegendVerticalAlignment.TOP; - mOrientation = LegendOrientation.HORIZONTAL; - break; - - case BELOW_CHART_LEFT: - case BELOW_CHART_CENTER: - case BELOW_CHART_RIGHT: - mHorizontalAlignment = newValue == LegendPosition.BELOW_CHART_LEFT - ? LegendHorizontalAlignment.LEFT - : (newValue == LegendPosition.BELOW_CHART_RIGHT - ? LegendHorizontalAlignment.RIGHT - : LegendHorizontalAlignment.CENTER); - mVerticalAlignment = LegendVerticalAlignment.BOTTOM; - mOrientation = LegendOrientation.HORIZONTAL; - break; - - case PIECHART_CENTER: - mHorizontalAlignment = LegendHorizontalAlignment.CENTER; - mVerticalAlignment = LegendVerticalAlignment.CENTER; - mOrientation = LegendOrientation.VERTICAL; - break; - } - - mDrawInside = newValue == LegendPosition.LEFT_OF_CHART_INSIDE - || newValue == LegendPosition.RIGHT_OF_CHART_INSIDE; - } - /** * returns the horizontal alignment of the legend * From 29f4cc5c2c5c7e5468759bb5ad414e9855cd09c6 Mon Sep 17 00:00:00 2001 From: almic Date: Sun, 11 Nov 2018 09:40:37 -0700 Subject: [PATCH 228/291] Remove unnecessary API checks Also added run configuration for the MPChartExample. Removed deprecated Legend stuff. --- .idea/runConfigurations/MPChartExample.xml | 52 ++++++++++ .../charting/charts/BarLineChartBase.java | 56 ++++------- .../mikephil/charting/charts/Chart.java | 34 +++---- .../charting/charts/PieRadarChartBase.java | 3 - .../mikephil/charting/components/Legend.java | 94 ------------------- 5 files changed, 84 insertions(+), 155 deletions(-) create mode 100644 .idea/runConfigurations/MPChartExample.xml diff --git a/.idea/runConfigurations/MPChartExample.xml b/.idea/runConfigurations/MPChartExample.xml new file mode 100644 index 0000000000..e6bcf50331 --- /dev/null +++ b/.idea/runConfigurations/MPChartExample.xml @@ -0,0 +1,52 @@ + + + + + \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index 0b0219e445..71f8a2d8a8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -708,20 +708,14 @@ public void zoomToCenter(float scaleX, float scaleY) { public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis, long duration) { - if (android.os.Build.VERSION.SDK_INT >= 11) { + MPPointD origin = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - MPPointD origin = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - - Runnable job = AnimatedZoomJob.getInstance(mViewPortHandler, this, getTransformer(axis), getAxis(axis), mXAxis - .mAxisRange, scaleX, scaleY, mViewPortHandler.getScaleX(), mViewPortHandler.getScaleY(), - xValue, yValue, (float) origin.x, (float) origin.y, duration); - addViewportJob(job); - - MPPointD.recycleInstance(origin); + Runnable job = AnimatedZoomJob.getInstance(mViewPortHandler, this, getTransformer(axis), getAxis(axis), mXAxis + .mAxisRange, scaleX, scaleY, mViewPortHandler.getScaleX(), mViewPortHandler.getScaleY(), + xValue, yValue, (float) origin.x, (float) origin.y, duration); + addViewportJob(job); - } else { - Log.e(LOG_TAG, "Unable to execute zoomAndCenterAnimated(...) on API level < 11"); - } + MPPointD.recycleInstance(origin); } protected Matrix mFitScreenMatrixBuffer = new Matrix(); @@ -874,21 +868,16 @@ public void moveViewTo(float xValue, float yValue, AxisDependency axis) { @TargetApi(11) public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration) { - if (android.os.Build.VERSION.SDK_INT >= 11) { + MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - - float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); - Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f, - getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); + Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f, + getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); - addViewportJob(job); + addViewportJob(job); - MPPointD.recycleInstance(bounds); - } else { - Log.e(LOG_TAG, "Unable to execute moveViewToAnimated(...) on API level < 11"); - } + MPPointD.recycleInstance(bounds); } /** @@ -941,23 +930,18 @@ public void centerViewTo(float xValue, float yValue, AxisDependency axis) { @TargetApi(11) public void centerViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration) { - if (android.os.Build.VERSION.SDK_INT >= 11) { - - MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); + MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); - float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); - Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, - xValue - xInView / 2f, yValue + yInView / 2f, - getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); + Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, + xValue - xInView / 2f, yValue + yInView / 2f, + getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); - addViewportJob(job); + addViewportJob(job); - MPPointD.recycleInstance(bounds); - } else { - Log.e(LOG_TAG, "Unable to execute centerViewToAnimated(...) on API level < 11"); - } + MPPointD.recycleInstance(bounds); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 0a1869e543..1889a9f6d4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -208,18 +208,14 @@ protected void init() { setWillNotDraw(false); // setLayerType(View.LAYER_TYPE_HARDWARE, null); - if (Build.VERSION.SDK_INT < 11) { - mAnimator = new ChartAnimator(); - } else { - mAnimator = new ChartAnimator(new AnimatorUpdateListener() { - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - // ViewCompat.postInvalidateOnAnimation(Chart.this); - postInvalidate(); - } - }); - } + mAnimator = new ChartAnimator(new AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + // ViewCompat.postInvalidateOnAnimation(Chart.this); + postInvalidate(); + } + }); // initialize the utils Utils.init(getContext()); @@ -1697,16 +1693,10 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { */ public void setHardwareAccelerationEnabled(boolean enabled) { - if (android.os.Build.VERSION.SDK_INT >= 11) { - - if (enabled) - setLayerType(View.LAYER_TYPE_HARDWARE, null); - else - setLayerType(View.LAYER_TYPE_SOFTWARE, null); - } else { - Log.e(LOG_TAG, - "Cannot enable/disable hardware acceleration for devices below API level 11."); - } + if (enabled) + setLayerType(View.LAYER_TYPE_HARDWARE, null); + else + setLayerType(View.LAYER_TYPE_SOFTWARE, null); } @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java index 618de18a34..e6d38d3a01 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java @@ -480,9 +480,6 @@ public float getYChartMin() { @SuppressLint("NewApi") public void spin(int durationmillis, float fromangle, float toangle, EasingFunction easing) { - if (android.os.Build.VERSION.SDK_INT < 11) - return; - setRotationAngle(fromangle); ObjectAnimator spinAnimator = ObjectAnimator.ofFloat(this, "rotationAngle", fromangle, diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java index e3782e7cdd..b785098881 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java @@ -20,19 +20,6 @@ */ public class Legend extends ComponentBase { - /** - * This property is deprecated - Use `horizontalAlignment`, `verticalAlignment`, `orientation`, `drawInside`, - * `direction`. - */ - @Deprecated - public enum LegendPosition { - RIGHT_OF_CHART, RIGHT_OF_CHART_CENTER, RIGHT_OF_CHART_INSIDE, - LEFT_OF_CHART, LEFT_OF_CHART_CENTER, LEFT_OF_CHART_INSIDE, - BELOW_CHART_LEFT, BELOW_CHART_RIGHT, BELOW_CHART_CENTER, - ABOVE_CHART_LEFT, ABOVE_CHART_RIGHT, ABOVE_CHART_CENTER, - PIECHART_CENTER - } - public enum LegendForm { /** * Avoid drawing a form @@ -180,38 +167,6 @@ public Legend(LegendEntry[] entries) { this.mEntries = entries; } - @Deprecated - public Legend(int[] colors, String[] labels) { - this(); - - if (colors == null || labels == null) { - throw new IllegalArgumentException("colors array or labels array is NULL"); - } - - if (colors.length != labels.length) { - throw new IllegalArgumentException( - "colors array and labels array need to be of same size"); - } - - List entries = new ArrayList<>(); - - for (int i = 0; i < Math.min(colors.length, labels.length); i++) { - final LegendEntry entry = new LegendEntry(); - entry.formColor = colors[i]; - entry.label = labels[i]; - - if (entry.formColor == ColorTemplate.COLOR_SKIP) - entry.form = LegendForm.NONE; - else if (entry.formColor == ColorTemplate.COLOR_NONE || - entry.formColor == 0) - entry.form = LegendForm.EMPTY; - - entries.add(entry); - } - - mEntries = entries.toArray(new LegendEntry[entries.size()]); - } - /** * This method sets the automatically computed colors for the legend. Use setCustom(...) to set custom colors. * @@ -280,50 +235,6 @@ public float getMaximumEntryHeight(Paint p) { return max; } - @Deprecated - public int[] getColors() { - - int[] old = new int[mEntries.length]; - for (int i = 0; i < mEntries.length; i++) { - old[i] = mEntries[i].form == LegendForm.NONE ? ColorTemplate.COLOR_SKIP : - (mEntries[i].form == LegendForm.EMPTY ? ColorTemplate.COLOR_NONE : - mEntries[i].formColor); - } - return old; - } - - @Deprecated - public String[] getLabels() { - - String[] old = new String[mEntries.length]; - for (int i = 0; i < mEntries.length; i++) { - old[i] = mEntries[i].label; - } - return old; - } - - @Deprecated - public int[] getExtraColors() { - - int[] old = new int[mExtraEntries.length]; - for (int i = 0; i < mExtraEntries.length; i++) { - old[i] = mExtraEntries[i].form == LegendForm.NONE ? ColorTemplate.COLOR_SKIP : - (mExtraEntries[i].form == LegendForm.EMPTY ? ColorTemplate.COLOR_NONE : - mExtraEntries[i].formColor); - } - return old; - } - - @Deprecated - public String[] getExtraLabels() { - - String[] old = new String[mExtraEntries.length]; - for (int i = 0; i < mExtraEntries.length; i++) { - old[i] = mExtraEntries[i].label; - } - return old; - } - public LegendEntry[] getExtraEntries() { return mExtraEntries; @@ -339,11 +250,6 @@ public void setExtra(LegendEntry[] entries) { mExtraEntries = entries; } - @Deprecated - public void setExtra(List colors, List labels) { - setExtra(Utils.convertIntegers(colors), Utils.convertStrings(labels)); - } - /** * Entries that will be appended to the end of the auto calculated * entries after calculating the legend. From 42cdba535f5af8076f3a376afba543c7e41eb9d0 Mon Sep 17 00:00:00 2001 From: almic Date: Mon, 12 Nov 2018 10:58:17 -0700 Subject: [PATCH 229/291] Add Curved Slices to Pie Chart Finally added support for rounded slices, and somewhat improved the code for the PieChartRenderer class. --- .../mpchartexample/PieChartActivity.java | 12 + .../PiePolylineChartActivity.java | 12 + MPChartExample/src/main/res/menu/pie.xml | 4 + .../src/main/res/values/strings.xml | 1 + .../mikephil/charting/charts/PieChart.java | 10 + .../charting/renderer/PieChartRenderer.java | 273 ++++++++++-------- 6 files changed, 193 insertions(+), 119 deletions(-) diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 48bd4306c3..993941268c 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -210,6 +210,18 @@ public boolean onOptionsItemSelected(MenuItem item) { 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); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index dc26f48297..157b93a638 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -205,6 +205,18 @@ public boolean onOptionsItemSelected(MenuItem item) { 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); diff --git a/MPChartExample/src/main/res/menu/pie.xml b/MPChartExample/src/main/res/menu/pie.xml index b2de043409..56a892ed07 100644 --- a/MPChartExample/src/main/res/menu/pie.xml +++ b/MPChartExample/src/main/res/menu/pie.xml @@ -25,6 +25,10 @@ android:id="@+id/actionToggleHole" android:title="@string/actionToggleHole"> + + diff --git a/MPChartExample/src/main/res/values/strings.xml b/MPChartExample/src/main/res/values/strings.xml index 1e96d8b6b6..4afde3b1e4 100644 --- a/MPChartExample/src/main/res/values/strings.xml +++ b/MPChartExample/src/main/res/values/strings.xml @@ -41,6 +41,7 @@ Toggle Percent Toggle Hole + Toggle Curved Slices Draw Center Text Toggle Highlight Circle Toggle Rotation 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 index 660fd29bdc..0ed4502769 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java @@ -650,6 +650,16 @@ public void setEntryLabelTextSize(float size) { ((PieChartRenderer) mRenderer).getPaintEntryLabels().setTextSize(Utils.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". 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 b14657cefc..c9f653e808 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 @@ -230,6 +230,9 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { final float userInnerRadius = drawInnerArc ? radius * (mChart.getHoleRadius() / 100.f) : 0.f; + final float roundedRadius = (radius - (radius * mChart.getHoleRadius() / 100f)) / 2f; + final RectF roundedCircleBox = new RectF(); + final boolean drawRoundedSlices = drawInnerArc && mChart.isDrawRoundedSlicesEnabled(); int visibleAngleCount = 0; for (int j = 0; j < entryCount; j++) { @@ -249,133 +252,151 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { Entry e = dataSet.getEntryForIndex(j); // draw only if the value is greater than zero - if ((Math.abs(e.getY()) > Utils.FLOAT_EPSILON)) { + if (!(Math.abs(e.getY()) > Utils.FLOAT_EPSILON)) { + angle += sliceAngle * phaseX; + continue; + } - if (!mChart.needsHighlight(j)) { + // Don't draw if it's highlighted, unless the chart uses rounded slices + if (mChart.needsHighlight(j) && !drawRoundedSlices) { + angle += sliceAngle * phaseX; + continue; + } - final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; + final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; - mRenderPaint.setColor(dataSet.getColor(j)); + mRenderPaint.setColor(dataSet.getColor(j)); - final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? - 0.f : - sliceSpace / (Utils.FDEG2RAD * radius); - final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY; - float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY; - if (sweepAngleOuter < 0.f) { - sweepAngleOuter = 0.f; - } + final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * radius); + final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY; + float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY; + if (sweepAngleOuter < 0.f) { + sweepAngleOuter = 0.f; + } - mPathBuffer.reset(); + mPathBuffer.reset(); - float arcStartPointX = center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD); - float arcStartPointY = center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD); + if (drawRoundedSlices) { + float x = center.x + (radius - roundedRadius) * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD); + float y = center.y + (radius - roundedRadius) * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD); + roundedCircleBox.set(x - roundedRadius, y - roundedRadius, x + roundedRadius, y + roundedRadius); + } - if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { - // Android is doing "mod 360" - mPathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW); - } else { + float arcStartPointX = center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD); + float arcStartPointY = center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD); - mPathBuffer.moveTo(arcStartPointX, arcStartPointY); + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { + // Android is doing "mod 360" + mPathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW); + } else { - mPathBuffer.arcTo( - circleBox, - startAngleOuter, - sweepAngleOuter - ); - } + if (drawRoundedSlices) { + mPathBuffer.arcTo(roundedCircleBox, startAngleOuter + 180, -180); + } - // API < 21 does not receive floats in addArc, but a RectF - mInnerRectBuffer.set( - center.x - innerRadius, - center.y - innerRadius, - center.x + innerRadius, - center.y + innerRadius); - - if (drawInnerArc && - (innerRadius > 0.f || accountForSliceSpacing)) { - - if (accountForSliceSpacing) { - float minSpacedRadius = - calculateMinimumRadiusForSpacedSlice( - center, radius, - sliceAngle * phaseY, - arcStartPointX, arcStartPointY, - startAngleOuter, - sweepAngleOuter); - - if (minSpacedRadius < 0.f) - minSpacedRadius = -minSpacedRadius; - - innerRadius = Math.max(innerRadius, minSpacedRadius); - } + mPathBuffer.arcTo( + circleBox, + startAngleOuter, + sweepAngleOuter + ); + } - final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ? - 0.f : - sliceSpace / (Utils.FDEG2RAD * innerRadius); - final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY; - float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY; - if (sweepAngleInner < 0.f) { - sweepAngleInner = 0.f; - } - final float endAngleInner = startAngleInner + sweepAngleInner; - - if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { - // Android is doing "mod 360" - mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW); - } else { - - mPathBuffer.lineTo( - center.x + innerRadius * (float) Math.cos(endAngleInner * Utils.FDEG2RAD), - center.y + innerRadius * (float) Math.sin(endAngleInner * Utils.FDEG2RAD)); - - mPathBuffer.arcTo( - mInnerRectBuffer, - endAngleInner, - -sweepAngleInner - ); - } - } else { + // API < 21 does not receive floats in addArc, but a RectF + mInnerRectBuffer.set( + center.x - innerRadius, + center.y - innerRadius, + center.x + innerRadius, + center.y + innerRadius); - if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) { - if (accountForSliceSpacing) { - - float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f; - - float sliceSpaceOffset = - calculateMinimumRadiusForSpacedSlice( - center, - radius, - sliceAngle * phaseY, - arcStartPointX, - arcStartPointY, - startAngleOuter, - sweepAngleOuter); - - float arcEndPointX = center.x + - sliceSpaceOffset * (float) Math.cos(angleMiddle * Utils.FDEG2RAD); - float arcEndPointY = center.y + - sliceSpaceOffset * (float) Math.sin(angleMiddle * Utils.FDEG2RAD); - - mPathBuffer.lineTo( - arcEndPointX, - arcEndPointY); - - } else { - mPathBuffer.lineTo( - center.x, - center.y); - } - } + if (drawInnerArc && (innerRadius > 0.f || accountForSliceSpacing)) { - } + if (accountForSliceSpacing) { + float minSpacedRadius = + calculateMinimumRadiusForSpacedSlice( + center, radius, + sliceAngle * phaseY, + arcStartPointX, arcStartPointY, + startAngleOuter, + sweepAngleOuter); - mPathBuffer.close(); + if (minSpacedRadius < 0.f) + minSpacedRadius = -minSpacedRadius; - mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint); + innerRadius = Math.max(innerRadius, minSpacedRadius); } + + final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * innerRadius); + final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY; + float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY; + if (sweepAngleInner < 0.f) { + sweepAngleInner = 0.f; + } + final float endAngleInner = startAngleInner + sweepAngleInner; + + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { + // Android is doing "mod 360" + mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW); + } else { + + if (drawRoundedSlices) { + float x = center.x + (radius - roundedRadius) * (float) Math.cos(endAngleInner * Utils.FDEG2RAD); + float y = center.y + (radius - roundedRadius) * (float) Math.sin(endAngleInner * Utils.FDEG2RAD); + roundedCircleBox.set(x - roundedRadius, y - roundedRadius, x + roundedRadius, y + roundedRadius); + mPathBuffer.arcTo(roundedCircleBox, endAngleInner, 180); + } else + mPathBuffer.lineTo( + center.x + innerRadius * (float) Math.cos(endAngleInner * Utils.FDEG2RAD), + center.y + innerRadius * (float) Math.sin(endAngleInner * Utils.FDEG2RAD)); + + mPathBuffer.arcTo( + mInnerRectBuffer, + endAngleInner, + -sweepAngleInner + ); + } + } else { + + if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) { + if (accountForSliceSpacing) { + + float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f; + + float sliceSpaceOffset = + calculateMinimumRadiusForSpacedSlice( + center, + radius, + sliceAngle * phaseY, + arcStartPointX, + arcStartPointY, + startAngleOuter, + sweepAngleOuter); + + float arcEndPointX = center.x + + sliceSpaceOffset * (float) Math.cos(angleMiddle * Utils.FDEG2RAD); + float arcEndPointY = center.y + + sliceSpaceOffset * (float) Math.sin(angleMiddle * Utils.FDEG2RAD); + + mPathBuffer.lineTo( + arcEndPointX, + arcEndPointY); + + } else { + mPathBuffer.lineTo( + center.x, + center.y); + } + } + } + mPathBuffer.close(); + + mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint); + angle += sliceAngle * phaseX; } @@ -396,11 +417,17 @@ public void drawValues(Canvas c) { float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); + final float roundedRadius = (radius - (radius * mChart.getHoleRadius() / 100f)) / 2f; final float holeRadiusPercent = mChart.getHoleRadius() / 100.f; float labelRadiusOffset = radius / 10f * 3.6f; if (mChart.isDrawHoleEnabled()) { labelRadiusOffset = (radius - (radius * holeRadiusPercent)) / 2f; + + if (!mChart.isDrawSlicesUnderHoleEnabled() && mChart.isDrawRoundedSlicesEnabled()) { + // Add curved circle slice and spacing to rotation angle, so that it sits nicely inside + rotationAngle += roundedRadius * 360 / (Math.PI * 2 * radius); + } } final float labelRadius = radius - labelRadiusOffset; @@ -472,6 +499,7 @@ public void drawValues(Canvas c) { float value = mChart.isUsePercentValuesEnabled() ? entry.getY() / yValueSum * 100f : entry.getY(); String formattedValue = formatter.getPieLabel(value, entry); + String entryLabel = entry.getLabel(); final float sliceXBase = (float) Math.cos(transformedAngle * Utils.FDEG2RAD); final float sliceYBase = (float) Math.sin(transformedAngle * Utils.FDEG2RAD); @@ -552,13 +580,13 @@ public void drawValues(Canvas c) { drawValue(c, formattedValue, labelPtx, labelPty, dataSet.getValueTextColor(j)); - if (j < data.getEntryCount() && entry.getLabel() != null) { - drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight); + if (j < data.getEntryCount() && entryLabel != null) { + drawEntryLabel(c, entryLabel, labelPtx, labelPty + lineHeight); } } else if (drawXOutside) { - if (j < data.getEntryCount() && entry.getLabel() != null) { - drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight / 2.f); + if (j < data.getEntryCount() && entryLabel != null) { + drawEntryLabel(c, entryLabel, labelPtx, labelPty + lineHeight / 2.f); } } else if (drawYOutside) { @@ -578,13 +606,13 @@ public void drawValues(Canvas c) { drawValue(c, formattedValue, x, y, dataSet.getValueTextColor(j)); - if (j < data.getEntryCount() && entry.getLabel() != null) { - drawEntryLabel(c, entry.getLabel(), x, y + lineHeight); + if (j < data.getEntryCount() && entryLabel != null) { + drawEntryLabel(c, entryLabel, x, y + lineHeight); } } else if (drawXInside) { - if (j < data.getEntryCount() && entry.getLabel() != null) { - drawEntryLabel(c, entry.getLabel(), x, y + lineHeight / 2f); + if (j < data.getEntryCount() && entryLabel != null) { + drawEntryLabel(c, entryLabel, x, y + lineHeight / 2f); } } else if (drawYInside) { drawValue(c, formattedValue, x, y + lineHeight / 2f, dataSet.getValueTextColor(j)); @@ -637,7 +665,6 @@ protected void drawEntryLabel(Canvas c, String label, float x, float y) { @Override public void drawExtras(Canvas c) { - // drawCircles(c); drawHole(c); c.drawBitmap(mDrawBitmap.get(), 0, 0, null); drawCenterText(c); @@ -763,6 +790,15 @@ protected void drawCenterText(Canvas c) { @Override public void drawHighlighted(Canvas c, Highlight[] indices) { + /* Skip entirely if using rounded circle slices, because it doesn't make sense to highlight + * in this way. + * TODO: add support for changing slice color with highlighting rather than only shifting the slice + */ + + final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled(); + if (drawInnerArc && mChart.isDrawRoundedSlicesEnabled()) + return; + float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); @@ -773,7 +809,6 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { float[] absoluteAngles = mChart.getAbsoluteAngles(); final MPPointF center = mChart.getCenterCircleBox(); final float radius = mChart.getRadius(); - final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled(); final float userInnerRadius = drawInnerArc ? radius * (mChart.getHoleRadius() / 100.f) : 0.f; From aea2ff3417e30d6d4b1ce7e777cbd8bc83e1c95d Mon Sep 17 00:00:00 2001 From: almic Date: Thu, 15 Nov 2018 09:51:46 -0700 Subject: [PATCH 230/291] Add option to set minimum angles Allows to force minimum slice angles when drawing charts, which means that any slices with angles lower than the minimum will be drawn with the minimum angle. When changing this setting on the fly, you have to call `notifyDataSetChanged()` and `invalidate()` for the minimum angle to take effect. This only functions if all slices can be drawn with the minimum angle. For example if a chart has 5 slices, the largest functioning minimum angle is `72f` degrees on a 360 degree chart, or 20% of the chart's `maxAngle`. --- .../mpchartexample/PieChartActivity.java | 9 +++ .../PiePolylineChartActivity.java | 9 +++ MPChartExample/src/main/res/menu/pie.xml | 4 ++ .../src/main/res/values/strings.xml | 1 + .../mikephil/charting/charts/PieChart.java | 66 ++++++++++++++++++- 5 files changed, 88 insertions(+), 1 deletion(-) diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 993941268c..4aeb1b3e9f 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -210,6 +210,15 @@ public boolean onOptionsItemSelected(MenuItem item) { 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); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index 157b93a638..b276806c7d 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -205,6 +205,15 @@ public boolean onOptionsItemSelected(MenuItem item) { 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); diff --git a/MPChartExample/src/main/res/menu/pie.xml b/MPChartExample/src/main/res/menu/pie.xml index 56a892ed07..09a05a9ccd 100644 --- a/MPChartExample/src/main/res/menu/pie.xml +++ b/MPChartExample/src/main/res/menu/pie.xml @@ -21,6 +21,10 @@ android:id="@+id/actionTogglePercent" android:title="@string/actionTogglePercent"> + + diff --git a/MPChartExample/src/main/res/values/strings.xml b/MPChartExample/src/main/res/values/strings.xml index 4afde3b1e4..91a25bb89e 100644 --- a/MPChartExample/src/main/res/values/strings.xml +++ b/MPChartExample/src/main/res/values/strings.xml @@ -40,6 +40,7 @@ Clear chart Toggle Percent + Toggle Minimum Angles Toggle Hole Toggle Curved Slices Draw Center Text 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 index 0ed4502769..de11b3a844 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java @@ -94,6 +94,12 @@ public class PieChart extends PieRadarChartBase { 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); } @@ -228,7 +234,12 @@ private void calcAngles() { 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++) { @@ -236,7 +247,20 @@ private void calcAngles() { for (int j = 0; j < set.getEntryCount(); j++) { - mDrawAngles[cnt] = calcAngle(Math.abs(set.getEntryForIndex(j).getY()), yValueSum); + 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]; @@ -248,6 +272,20 @@ private void calcAngles() { } } + 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; + } } /** @@ -729,6 +767,32 @@ public void setMaxAngle(float maxangle) { 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 From c886bb342accd1596ed7211314081f776f257c14 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 20 Feb 2019 22:51:10 +0100 Subject: [PATCH 231/291] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 53f278aba7..f26034da24 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,9 @@ Please read the [**documentation**](https://github.com/PhilJay/MPAndroidChart/wi 0x04ef098bf9f91871391363e3caf791afa3adc39b +[**Lightning Network (tippin.me)**](https://tippin.me/@PhilippJahoda) + + **PayPal** - [**Donate 5 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7G52RA87ED8NY): Thank's for creating this project, here's a coffee (or some beer) for you! From 59028d3bf3fcc7a6f337772ea8f7faa338e793c1 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 21 Feb 2019 09:04:28 +0100 Subject: [PATCH 232/291] Update Bug_report.md --- .github/ISSUE_TEMPLATE/Bug_report.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 7989a9d655..3a5938eee9 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -40,3 +40,5 @@ support questions here. We will close your issue without a response. If you have source code demonstrating this bug, create a Gist: https://help.github.com/articles/creating-gists/ and link to it here. --> + +ADD A REWARD using [Speed](speedoss.com) to SOLVE this issue QUICKLY and SUPPORT this project. From 7df8a4cedc17c5bda07e8edb38010dcd8e39b74f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 21 Feb 2019 09:04:40 +0100 Subject: [PATCH 233/291] Update Feature_request.md --- .github/ISSUE_TEMPLATE/Feature_request.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md index 823b940961..54b30c983b 100644 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -31,3 +31,4 @@ support questions here. We will close your issue without a response. **Additional context** +ADD A REWARD using [Speed](speedoss.com) to SOLVE this issue QUICKLY and SUPPORT this project. From 971640b29dc698c1c636549f4d94e254cf659d03 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 21 Feb 2019 09:05:06 +0100 Subject: [PATCH 234/291] Update Support_help.md --- .github/ISSUE_TEMPLATE/Support_help.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/Support_help.md b/.github/ISSUE_TEMPLATE/Support_help.md index 5a2c5fd41b..70a086d3d7 100644 --- a/.github/ISSUE_TEMPLATE/Support_help.md +++ b/.github/ISSUE_TEMPLATE/Support_help.md @@ -22,3 +22,6 @@ Instead, do the following: ### You have been warned! From now on, any issues asking for help will get closed with a link to this file. + +### Additional Context +ADD A REWARD using [Speed](speedoss.com) to SOLVE this issue QUICKLY and SUPPORT this project. From 2058f7bf5dd1c365f78174b09a91db3e4c2e480c Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 16 Mar 2019 11:42:04 +0100 Subject: [PATCH 235/291] Minor changes to project and example --- .gitignore | 1 + MPChartExample/src/main/res/layout/list_item.xml | 1 + build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 1120426ac8..1f0e3083c2 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ bin/ gen/ generated/ finalOutput/ +projectFilesBackup/ build.xml diff --git a/MPChartExample/src/main/res/layout/list_item.xml b/MPChartExample/src/main/res/layout/list_item.xml index a6e5b5f3eb..420add1193 100644 --- a/MPChartExample/src/main/res/layout/list_item.xml +++ b/MPChartExample/src/main/res/layout/list_item.xml @@ -12,6 +12,7 @@ android:layout_alignParentTop="true" android:layout_marginLeft="4dp" android:text="Medium Text" + android:textColor="@android:color/black" android:textSize="16sp"/> Date: Sat, 16 Mar 2019 11:53:42 +0100 Subject: [PATCH 236/291] New example app release --- MPChartExample/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 0a60389ede..2d607e9991 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -6,7 +6,7 @@ android { applicationId "com.xxmassdeveloper.mpchartexample" minSdkVersion 16 targetSdkVersion 28 - versionCode 56 + versionCode 57 versionName '3.1.0' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -20,7 +20,7 @@ android { } dependencies { - implementation "androidx.appcompat:appcompat:1.0.0" + implementation "androidx.appcompat:appcompat:1.0.2" implementation 'com.google.android.material:material:1.0.0' implementation project(':MPChartLib') } From e95c1eb26a533e41017dbd49cc3d750457cdd435 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 20 Mar 2019 17:56:58 +0100 Subject: [PATCH 237/291] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f26034da24..d358d90f17 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ repositories { } dependencies { - implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0-alpha' + implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' } ``` @@ -70,7 +70,7 @@ dependencies { com.github.PhilJay MPAndroidChart - v3.1.0-alpha + v3.1.0 ``` @@ -80,7 +80,7 @@ dependencies { See the [**documentation**](https://github.com/PhilJay/MPAndroidChart/wiki) for examples and general use of MPAndroidChart. -See the [**javadocs**](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.1.0-alpha/javadoc/) for more advanced documentation. +See the [**javadocs**](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.1.0/javadoc/) for more advanced documentation.
    From adb56e75bd5f5a2f1c3cf9dec556c22dc97b4ac0 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 7 Apr 2019 16:20:14 +0200 Subject: [PATCH 238/291] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d358d90f17..fa7a1c8386 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ dependencies {

    Documentation :notebook_with_decorative_cover:

    -See the [**documentation**](https://github.com/PhilJay/MPAndroidChart/wiki) for examples and general use of MPAndroidChart. +See the [**documentation**](https://weeklycoding.com/mpandroidchart/) for examples and general use of MPAndroidChart. See the [**javadocs**](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.1.0/javadoc/) for more advanced documentation. @@ -96,7 +96,7 @@ Download the [MPAndroidChart Example App](https://play.google.com/store/apps/det This repository's issue tracker is only for bugs and feature requests. The maintainers ask that you refrain from asking questions about how to use MPAndroidChart through the issue tracker. -Please read the [**documentation**](https://github.com/PhilJay/MPAndroidChart/wiki) first, then ask all your questions on [stackoverflow.com](https://stackoverflow.com/questions/tagged/mpandroidchart) for the fastest answer. +Please read the [**documentation**](https://weeklycoding.com/mpandroidchart/) first, then ask all your questions on [stackoverflow.com](https://stackoverflow.com/questions/tagged/mpandroidchart) for the fastest answer.
    @@ -209,7 +209,7 @@ You can follow me on Twitter [**@PhilippJahoda**](https://twitter.com/PhilippJah

    License :page_facing_up:

    -Copyright 2018 Philipp Jahoda +Copyright 2019 Philipp Jahoda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 12b4351d0c862212ddb77f73716799cb97f58c04 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 7 Apr 2019 16:27:59 +0200 Subject: [PATCH 239/291] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fa7a1c8386..4603c7b44e 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,9 @@ All MPAndroidChart users are entitled to a special **discount of 5%** off the
    -## [Daily Coding Newsletter](https://philjay.substack.com/subscribe) +## [Bi-Weekly Coding Newsletter](https://weeklycoding.com) -Sign up for my [daily coding newsletter](https://philjay.substack.com/subscribe) to get quick updates on Kotlin and Android development related topics. +Sign up for my [coding newsletter](https://weeklycoding.com) to get quick updates on Kotlin and Android development related topics.

    Quick Start :chart_with_upwards_trend:

    From ed8876cef283249b63f12e2bb8931dd9e444ee17 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 7 Apr 2019 16:37:14 +0200 Subject: [PATCH 240/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4603c7b44e..044a84f9a6 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ If you like this library, please tell others about it :two_hearts: :two_hearts: [![Share on Google+](https://github.com/PhilJay/MPAndroidChart/blob/master/design/googleplus_icon.png)](https://plus.google.com/share?url=https://github.com/PhilJay/MPAndroidChart) [![Share on Facebook](https://github.com/PhilJay/MPAndroidChart/blob/master/design/facebook_icon.png)](https://www.facebook.com/sharer/sharer.php?u=https://github.com/PhilJay/MPAndroidChart) -You can follow me on Twitter [**@PhilippJahoda**](https://twitter.com/PhilippJahoda) or sign up for my [**daily coding newsletter**](https://philjay.substack.com/subscribe). +You can follow me on Twitter [**@PhilippJahoda**](https://twitter.com/PhilippJahoda) or sign up for my [**coding newsletter**](https://weeklycoding.com).
    From 0563fb48b0fdc713ab1789c7544db413b26b1ae9 Mon Sep 17 00:00:00 2001 From: duchampdev Date: Sat, 27 Apr 2019 16:06:10 +0200 Subject: [PATCH 241/291] PercentFormatter: make space between number and percent sign optional --- .../mikephil/charting/formatter/PercentFormatter.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java index 6bf1bd3c33..3ee1447ecc 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java @@ -16,9 +16,11 @@ public class PercentFormatter extends ValueFormatter public DecimalFormat mFormat; private PieChart pieChart; + private boolean percentSignSeparated; public PercentFormatter() { mFormat = new DecimalFormat("###,###,##0.0"); + percentSignSeparated = true; } // Can be used to remove percent signs if the chart isn't in percent mode @@ -27,9 +29,15 @@ public PercentFormatter(PieChart pieChart) { this.pieChart = pieChart; } + // Can be used to remove percent signs if the chart isn't in percent mode + public PercentFormatter(PieChart pieChart, boolean percentSignSeparated) { + this(pieChart); + this.percentSignSeparated = percentSignSeparated; + } + @Override public String getFormattedValue(float value) { - return mFormat.format(value) + " %"; + return mFormat.format(value) + (percentSignSeparated ? " %" : "%"); } @Override From c5667d4f141962c29f6a8ffd59958026d1a10663 Mon Sep 17 00:00:00 2001 From: duchampdev Date: Sat, 27 Apr 2019 16:06:37 +0200 Subject: [PATCH 242/291] fix little typo --- .../github/mikephil/charting/formatter/PercentFormatter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java index 3ee1447ecc..012fab77f9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java @@ -7,7 +7,7 @@ /** * This IValueFormatter is just for convenience and simply puts a "%" sign after - * each value. (Recommeded for PieChart) + * each value. (Recommended for PieChart) * * @author Philipp Jahoda */ From fffe9a297eb3972a353c7c4e70941c0c90d09344 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 27 Apr 2019 17:38:37 +0200 Subject: [PATCH 243/291] Update gradle --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index f083210162..cee7a83e80 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.2' + classpath 'com.android.tools.build:gradle:3.4.0' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c54dbf0954..0f8bc4e375 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Mar 16 11:37:10 CET 2019 +#Sat Apr 27 15:48:29 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip From dc5917152436d5e231f9b687c4862d4a30cb072b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 8 Oct 2019 14:15:21 +0200 Subject: [PATCH 244/291] Create FUNDING.yml --- .github/FUNDING.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..41e266eed3 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: https://www.patreon.com/mpandroidchart +open_collective: philippjahoda +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From cb26ed4ef4ecab63390d73eaff1fb28ef40e0c98 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 8 Oct 2019 14:15:52 +0200 Subject: [PATCH 245/291] Update FUNDING.yml --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 41e266eed3..0a213d6792 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,7 +1,7 @@ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: https://www.patreon.com/mpandroidchart +patreon: mpandroidchart open_collective: philippjahoda ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel From d86f39cc917ced800fdd93dafe3da886686822f2 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 20 Jan 2020 11:15:12 +0100 Subject: [PATCH 246/291] Update README.md --- README.md | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/README.md b/README.md index 044a84f9a6..c4325f61e5 100644 --- a/README.md +++ b/README.md @@ -23,21 +23,7 @@ 1. [License](#licence) 1. [Creators](#creators) -## [Realtime Graphing Solution | SciChart](https://scichart.com/android-chart-features?source=MPAndroidChart) - - - - -MPAndroidChart is free software, as a result **dynamic & realtime data is not officially supported**. If you are looking for an enterprise-grade chart solution with extreme realtime performance and tech support, we recommend -
    SciChart Android. - - - -All MPAndroidChart users are entitled to a special **discount of 5%** off the SciChart store, using the following discount code: **MPANDROIDCHART** - -
    - -## [Bi-Weekly Coding Newsletter](https://weeklycoding.com) +## [Coding Newsletter](https://weeklycoding.com) Sign up for my [coding newsletter](https://weeklycoding.com) to get quick updates on Kotlin and Android development related topics. From 95027fa6a7f4762d52ffa23cabd45135d7d26ff4 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 09:55:00 +0200 Subject: [PATCH 247/291] Safe guards These will be even more important when moving to Kotlin ranges --- .../main/java/com/github/mikephil/charting/data/DataSet.java | 2 ++ .../com/github/mikephil/charting/renderer/LegendRenderer.java | 1 + 2 files changed, 3 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java index 3c69d9c58f..b474bfd894 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -86,6 +86,8 @@ public void calcMinMaxY(float fromX, float toX) { int indexFrom = getEntryIndex(fromX, Float.NaN, Rounding.DOWN); int indexTo = getEntryIndex(toX, Float.NaN, Rounding.UP); + if (indexTo < indexFrom) return; + for (int i = indexFrom; i <= indexTo; i++) { // only recalculate y diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java index 85597db6a1..4e7e5d6448 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -90,6 +90,7 @@ public void computeLegend(ChartData data) { for (int i = 0; i < data.getDataSetCount(); i++) { IDataSet dataSet = data.getDataSetByIndex(i); + if (dataSet == null) continue; List clrs = dataSet.getColors(); int entryCount = dataSet.getEntryCount(); From 6ebf3fa57a2d3ae0a7c7cf57dfd7a31aa1758dab Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 10:27:55 +0200 Subject: [PATCH 248/291] Added highlightColor parameter for pie charts https://github.com/danielgindi/Charts/pull/2961 --- .../mikephil/charting/data/PieDataSet.java | 17 +++++++++++++++++ .../interfaces/datasets/IPieDataSet.java | 8 ++++++++ .../charting/renderer/PieChartRenderer.java | 5 ++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java index e592399513..c473e0a752 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java @@ -3,6 +3,7 @@ import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; import com.github.mikephil.charting.utils.Utils; +import android.support.annotation.Nullable; import java.util.ArrayList; import java.util.List; @@ -29,6 +30,7 @@ public class PieDataSet extends DataSet implements IPieDataSet { private float mValueLinePart1Length = 0.3f; private float mValueLinePart2Length = 0.4f; private boolean mValueLineVariableLength = true; + private Integer mHighlightColor = null; public PieDataSet(List yVals, String label) { super(yVals, label); @@ -218,6 +220,21 @@ public void setValueLineVariableLength(boolean valueLineVariableLength) { this.mValueLineVariableLength = valueLineVariableLength; } + /** Gets the color for the highlighted sector */ + @Override + @Nullable + public Integer getHighlightColor() + { + return mHighlightColor; + } + + /** Sets the color for the highlighted sector (null for using entry color) */ + public void setHighlightColor(@Nullable Integer color) + { + this.mHighlightColor = color; + } + + public enum ValuePosition { INSIDE_SLICE, OUTSIDE_SLICE diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java index 1698ef786b..1e1ca23532 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java @@ -1,5 +1,7 @@ package com.github.mikephil.charting.interfaces.datasets; +import android.support.annotation.Nullable; + import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.data.PieEntry; @@ -70,5 +72,11 @@ public interface IPieDataSet extends IDataSet { * */ boolean isValueLineVariableLength(); + /** + * Gets the color for the highlighted sector + * */ + @Nullable + Integer getHighlightColor(); + } 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 c9f653e808..be3593adcb 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 @@ -857,7 +857,10 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; - mRenderPaint.setColor(set.getColor(index)); + Integer highlightColor = set.getHighlightColor(); + if (highlightColor == null) + highlightColor = set.getColor(index); + mRenderPaint.setColor(highlightColor); final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? 0.f : From 1987d7eb64cfe07e378e1a79db6c9637138d7d54 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 11:04:57 +0200 Subject: [PATCH 249/291] Consider axis dependency in Combined chart https://github.com/danielgindi/Charts/pull/2874 --- .../mikephil/charting/data/CombinedData.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java index 39625b30f9..0b36aa3bef 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java @@ -3,6 +3,7 @@ import android.util.Log; +import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; @@ -91,18 +92,26 @@ public void calcMinMax() { if (data.getXMin() < mXMin) mXMin = data.getXMin(); - if (data.mLeftAxisMax > mLeftAxisMax) - mLeftAxisMax = data.mLeftAxisMax; - - if (data.mLeftAxisMin < mLeftAxisMin) - mLeftAxisMin = data.mLeftAxisMin; - - if (data.mRightAxisMax > mRightAxisMax) - mRightAxisMax = data.mRightAxisMax; - - if (data.mRightAxisMin < mRightAxisMin) - mRightAxisMin = data.mRightAxisMin; - + for (IBarLineScatterCandleBubbleDataSet dataset : sets) { + if (dataset.getAxisDependency() == YAxis.AxisDependency.LEFT) { + if (dataset.getYMax() > mLeftAxisMax) { + mLeftAxisMax = dataset.getYMax(); + } + + if (dataset.getYMin() < mLeftAxisMin) { + mLeftAxisMin = dataset.getYMin(); + } + } + else { + if (dataset.getYMax() > mRightAxisMax) { + mRightAxisMax = dataset.getYMax(); + } + + if (dataset.getYMin() < mRightAxisMin) { + mRightAxisMin = dataset.getYMin(); + } + } + } } } From 634a690d6a318aa3b3fa4e277f29359901e47b8b Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 11:44:27 +0200 Subject: [PATCH 250/291] Added an implementation of Douglas Peucker with resultCount input https://github.com/danielgindi/Charts/pull/2848 --- .../charting/data/filter/ApproximatorN.java | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.java new file mode 100644 index 0000000000..9351341c76 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.java @@ -0,0 +1,146 @@ + +package com.github.mikephil.charting.data.filter; + +import java.util.ArrayList; + +/** + * Implemented according to modified Douglas Peucker {@link} + * http://psimpl.sourceforge.net/douglas-peucker.html + */ +public class ApproximatorN +{ + public float[] reduceWithDouglasPeucker(float[] points, float resultCount) { + + int pointCount = points.length / 2; + + // if a shape has 2 or less points it cannot be reduced + if (resultCount <= 2 || resultCount >= pointCount) + return points; + + boolean[] keep = new boolean[pointCount]; + + // first and last always stay + keep[0] = true; + keep[pointCount - 1] = true; + + int currentStoredPoints = 2; + + ArrayList queue = new ArrayList<>(); + Line line = new Line(0, pointCount - 1, points); + queue.add(line); + + do { + line = queue.remove(queue.size() - 1); + + // store the key + keep[line.index] = true; + + // check point count tolerance + currentStoredPoints += 1; + + if (currentStoredPoints == resultCount) + break; + + // split the polyline at the key and recurse + Line left = new Line(line.start, line.index, points); + if (left.index > 0) { + int insertionIndex = insertionIndex(left, queue); + queue.add(insertionIndex, left); + } + + Line right = new Line(line.index, line.end, points); + if (right.index > 0) { + int insertionIndex = insertionIndex(right, queue); + queue.add(insertionIndex, right); + } + } while (queue.isEmpty()); + + float[] reducedEntries = new float[currentStoredPoints * 2]; + + for (int i = 0, i2 = 0, r2 = 0; i < currentStoredPoints; i++, r2 += 2) { + if (keep[i]) { + reducedEntries[i2++] = points[r2]; + reducedEntries[i2++] = points[r2 + 1]; + } + } + + return reducedEntries; + } + + private static float distanceToLine( + float ptX, float ptY, float[] + fromLinePoint1, float[] fromLinePoint2) { + float dx = fromLinePoint2[0] - fromLinePoint1[0]; + float dy = fromLinePoint2[1] - fromLinePoint1[1]; + + float dividend = Math.abs( + dy * ptX - + dx * ptY - + fromLinePoint1[0] * fromLinePoint2[1] + + fromLinePoint2[0] * fromLinePoint1[1]); + double divisor = Math.sqrt(dx * dx + dy * dy); + + return (float)(dividend / divisor); + } + + private static class Line { + int start; + int end; + + float distance = 0; + int index = 0; + + Line(int start, int end, float[] points) { + this.start = start; + this.end = end; + + float[] startPoint = new float[]{points[start * 2], points[start * 2 + 1]}; + float[] endPoint = new float[]{points[end * 2], points[end * 2 + 1]}; + + if (end <= start + 1) return; + + for (int i = start + 1, i2 = i * 2; i < end; i++, i2 += 2) { + float distance = distanceToLine( + points[i2], points[i2 + 1], + startPoint, endPoint); + + if (distance > this.distance) { + this.index = i; + this.distance = distance; + } + } + } + + boolean equals(final Line rhs) { + return (start == rhs.start) && (end == rhs.end) && index == rhs.index; + } + + boolean lessThan(final Line rhs) { + return distance < rhs.distance; + } + } + + private static int insertionIndex(Line line, ArrayList queue) { + int min = 0; + int max = queue.size(); + + while (!queue.isEmpty()) { + int midIndex = min + (max - min) / 2; + Line midLine = queue.get(midIndex); + + if (midLine.equals(line)) { + return midIndex; + } + else if (line.lessThan(midLine)) { + // perform search in left half + max = midIndex; + } + else { + // perform search in right half + min = midIndex + 1; + } + } + + return min; + } +} From a4ca1f3fba59f7fe0865298968f564be9e4d0a1e Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 11:47:45 +0200 Subject: [PATCH 251/291] Fixed axis label disappearing when zooming in https://github.com/danielgindi/Charts/pull/3132 --- .../com/github/mikephil/charting/renderer/AxisRenderer.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java index 90528a1359..8c21f452a9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java @@ -214,11 +214,14 @@ protected void computeAxisValues(float min, float max) { double f; int i; - if (interval != 0.0) { + if (interval != 0.0 && last != first) { for (f = first; f <= last; f += interval) { ++n; } } + else if (last == first && n == 0) { + n = 1; + } mAxis.mEntryCount = n; From 2e725e49d3429a58e731efc5257c649fbfbc3fc7 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 11:54:08 +0200 Subject: [PATCH 252/291] Make min/max axis labels configurable https://github.com/danielgindi/Charts/pull/2894 --- .../charting/components/AxisBase.java | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index c1f02828be..96f706a3a1 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -151,6 +151,39 @@ public abstract class AxisBase extends ComponentBase { */ public float mAxisRange = 0f; + private int mAxisMinLabels = 2; + private int mAxisMaxLabels = 25; + + /** + * The minumum number of labels on the axis + */ + public int getAxisMinLabels() { + return mAxisMinLabels; + } + + /** + * The minumum number of labels on the axis + */ + public void setAxisMinLabels(int labels) { + if (labels > 0) + mAxisMinLabels = labels; + } + + /** + * The maximum number of labels on the axis + */ + public int getAxisMaxLabels() { + return mAxisMaxLabels; + } + + /** + * The maximum number of labels on the axis + */ + public void setAxisMaxLabels(int labels) { + if (labels > 0) + mAxisMaxLabels = labels; + } + /** * default constructor */ @@ -314,10 +347,10 @@ public boolean isDrawLabelsEnabled() { */ public void setLabelCount(int count) { - if (count > 25) - count = 25; - if (count < 2) - count = 2; + if (count > getAxisMaxLabels()) + count = getAxisMaxLabels(); + if (count < getAxisMinLabels()) + count = getAxisMinLabels(); mLabelCount = count; mForceLabels = false; From 3f5475077e549d08a5da6f95fcf3b4b6fe91eb2e Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 11:59:43 +0200 Subject: [PATCH 253/291] Avoid race condition for interval/intervalMagnitude https://github.com/danielgindi/Charts/pull/2377 --- .../github/mikephil/charting/renderer/AxisRenderer.java | 9 ++++++--- .../charting/renderer/YAxisRendererRadarChart.java | 8 +++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java index 8c21f452a9..72ea2d17c8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java @@ -174,9 +174,12 @@ protected void computeAxisValues(float min, float max) { double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10(interval))); int intervalSigDigit = (int) (interval / intervalMagnitude); if (intervalSigDigit > 5) { - // Use one order of magnitude higher, to avoid intervals like 0.9 or - // 90 - interval = Math.floor(10 * intervalMagnitude); + // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 + // if it's 0.0 after floor(), we use the old value + interval = Math.floor(10.0 * intervalMagnitude) == 0.0 + ? interval + : Math.floor(10.0 * intervalMagnitude); + } int n = mAxis.isCenterAxisLabelsEnabled() ? 1 : 0; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java index ee7392e928..e3f69d7965 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -52,9 +52,11 @@ protected void computeAxisValues(float min, float max) { double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10(interval))); int intervalSigDigit = (int) (interval / intervalMagnitude); if (intervalSigDigit > 5) { - // Use one order of magnitude higher, to avoid intervals like 0.9 or - // 90 - interval = Math.floor(10 * intervalMagnitude); + // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 + // if it's 0.0 after floor(), we use the old value + interval = Math.floor(10.0 * intervalMagnitude) == 0.0 + ? interval + : Math.floor(10.0 * intervalMagnitude); } boolean centeringEnabled = mAxis.isCenterAxisLabelsEnabled(); From 912427e54378602f44db00b4579dd2db50a23404 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 12:07:37 +0200 Subject: [PATCH 254/291] Custom text alignment for no-data https://github.com/danielgindi/Charts/pull/3199 --- .../mikephil/charting/charts/Chart.java | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 1889a9f6d4..5d3401c430 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -398,8 +398,23 @@ protected void onDraw(Canvas canvas) { boolean hasText = !TextUtils.isEmpty(mNoDataText); if (hasText) { - MPPointF c = getCenter(); - canvas.drawText(mNoDataText, c.x, c.y, mInfoPaint); + MPPointF pt = getCenter(); + + switch (mInfoPaint.getTextAlign()) { + case LEFT: + pt.x = 0; + canvas.drawText(mNoDataText, pt.x, pt.y, mInfoPaint); + break; + + case RIGHT: + pt.x *= 2.0; + canvas.drawText(mNoDataText, pt.x, pt.y, mInfoPaint); + break; + + default: + canvas.drawText(mNoDataText, pt.x, pt.y, mInfoPaint); + break; + } } return; @@ -1162,6 +1177,15 @@ public void setNoDataTextTypeface(Typeface tf) { mInfoPaint.setTypeface(tf); } + /** + * alignment of the no data text + * + * @param align + */ + public void setNoDataTextAlignment(Align align) { + mInfoPaint.setTextAlign(align); + } + /** * Set this to false to disable all gestures and touches on the chart, * default: true From 4549ae17b74967671ee218daf4a271c0283a98c1 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 12:15:02 +0200 Subject: [PATCH 255/291] Select correct axis for legend distance calculation in horz bar chart https://github.com/danielgindi/Charts/pull/2214 --- .../charting/charts/BarLineChartBase.java | 124 +++++++++--------- .../charting/charts/HorizontalBarChart.java | 78 +++++++++++ 2 files changed, 142 insertions(+), 60 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index 71f8a2d8a8..c7a593736f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -394,66 +394,70 @@ protected void calculateLegendOffsets(RectF offsets) { offsets.top = 0.f; offsets.bottom = 0.f; - // setup offsets for legend - if (mLegend != null && mLegend.isEnabled() && !mLegend.isDrawInsideEnabled()) { - 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(); - break; - - case BOTTOM: - offsets.bottom += Math.min(mLegend.mNeededHeight, - mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) - + mLegend.getYOffset(); - break; - - default: - break; - } - break; - } + 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(); + + + break; + + case BOTTOM: + offsets.bottom += Math.min(mLegend.mNeededHeight, + mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()) + + mLegend.getYOffset(); + + + break; + + default: + break; + } + break; } } 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 index e4ec309d9f..9aac1ce97c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java @@ -60,6 +60,84 @@ protected void init() { private 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() { From bafb0fbbe4a787bad4d64a87bf0df5cc573ca90a Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 12:16:48 +0200 Subject: [PATCH 256/291] Use correct color index for bubble chart https://github.com/danielgindi/Charts/pull/3202 --- .../github/mikephil/charting/renderer/BubbleChartRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index 57b81c1d9c..be141c46a0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -108,7 +108,7 @@ protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) break; - final int color = dataSet.getColor((int) entry.getX()); + final int color = dataSet.getColor(j); mRenderPaint.setColor(color); c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mRenderPaint); From ea816e8d6df174d060cdbab47c993d996b6f156d Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 12:25:34 +0200 Subject: [PATCH 257/291] Added dataIndex param for highlightValue (combined charts) https://github.com/danielgindi/Charts/pull/2852 --- .../mikephil/charting/charts/Chart.java | 60 +++++++++++++++++-- .../charting/highlight/Highlight.java | 8 +++ 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 5d3401c430..b104935d30 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -563,6 +563,18 @@ public void highlightValues(Highlight[] highs) { invalidate(); } + /** + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * This method will call the listener. + * @param x The x-value to highlight + * @param dataSetIndex The dataset index to search in + * @param dataIndex The data index to search in (only used in CombinedChartView currently) + */ + public void highlightValue(float x, int dataSetIndex, int dataIndex) { + highlightValue(x, dataSetIndex, dataIndex, true); + } + /** * Highlights any y-value at the given x-value in the given DataSet. * Provide -1 as the dataSetIndex to undo all highlighting. @@ -571,7 +583,20 @@ public void highlightValues(Highlight[] highs) { * @param dataSetIndex The dataset index to search in */ public void highlightValue(float x, int dataSetIndex) { - highlightValue(x, dataSetIndex, true); + highlightValue(x, dataSetIndex, -1, true); + } + + /** + * Highlights the value at the given x-value and y-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * This method will call the listener. + * @param x The x-value to highlight + * @param y The y-value to highlight. Supply `NaN` for "any" + * @param dataSetIndex The dataset index to search in + * @param dataIndex The data index to search in (only used in CombinedChartView currently) + */ + public void highlightValue(float x, float y, int dataSetIndex, int dataIndex) { + highlightValue(x, y, dataSetIndex, dataIndex, true); } /** @@ -583,7 +608,19 @@ public void highlightValue(float x, int dataSetIndex) { * @param dataSetIndex The dataset index to search in */ public void highlightValue(float x, float y, int dataSetIndex) { - highlightValue(x, y, dataSetIndex, true); + highlightValue(x, y, dataSetIndex, -1, true); + } + + /** + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * @param x The x-value to highlight + * @param dataSetIndex The dataset index to search in + * @param dataIndex The data index to search in (only used in CombinedChartView currently) + * @param callListener Should the listener be called for this change + */ + public void highlightValue(float x, int dataSetIndex, int dataIndex, boolean callListener) { + highlightValue(x, Float.NaN, dataSetIndex, dataIndex, callListener); } /** @@ -594,7 +631,7 @@ public void highlightValue(float x, float y, int dataSetIndex) { * @param callListener Should the listener be called for this change */ public void highlightValue(float x, int dataSetIndex, boolean callListener) { - highlightValue(x, Float.NaN, dataSetIndex, callListener); + highlightValue(x, Float.NaN, dataSetIndex, -1, callListener); } /** @@ -603,17 +640,30 @@ public void highlightValue(float x, int dataSetIndex, boolean callListener) { * @param x The x-value to highlight * @param y The y-value to highlight. Supply `NaN` for "any" * @param dataSetIndex The dataset index to search in + * @param dataIndex The data index to search in (only used in CombinedChartView currently) * @param callListener Should the listener be called for this change */ - public void highlightValue(float x, float y, int dataSetIndex, boolean callListener) { + public void highlightValue(float x, float y, int dataSetIndex, int dataIndex, boolean callListener) { if (dataSetIndex < 0 || dataSetIndex >= mData.getDataSetCount()) { highlightValue(null, callListener); } else { - highlightValue(new Highlight(x, y, dataSetIndex), callListener); + highlightValue(new Highlight(x, y, dataSetIndex, dataIndex), callListener); } } + /** + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * @param x The x-value to highlight + * @param y The y-value to highlight. Supply `NaN` for "any" + * @param dataSetIndex The dataset index to search in + * @param callListener Should the listener be called for this change + */ + public void highlightValue(float x, float y, int dataSetIndex, boolean callListener) { + highlightValue(x, y, dataSetIndex, -1, callListener); + } + /** * Highlights the values represented by the provided Highlight object * This method *will not* call the listener. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java index 032698d5e5..62307cbeaf 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java @@ -60,10 +60,18 @@ public class Highlight { */ private float mDrawY; + public Highlight(float x, float y, int dataSetIndex, int dataIndex) { + this.mX = x; + this.mY = y; + this.mDataSetIndex = dataSetIndex; + this.mDataIndex = dataIndex; + } + public Highlight(float x, float y, int dataSetIndex) { this.mX = x; this.mY = y; this.mDataSetIndex = dataSetIndex; + this.mDataIndex = -1; } public Highlight(float x, int dataSetIndex, int stackIndex) { From 34c3ceaa0599e27f8cb3fb4532988e02eac9a453 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 12:29:47 +0200 Subject: [PATCH 258/291] Reset min/max when clearing ChartDataSet https://github.com/danielgindi/Charts/pull/3265 --- .../com/github/mikephil/charting/data/DataSet.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java index b474bfd894..f28f92581b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -61,14 +61,14 @@ public DataSet(List values, String label) { @Override public void calcMinMax() { - if (mValues == null || mValues.isEmpty()) - return; - mYMax = -Float.MAX_VALUE; mYMin = Float.MAX_VALUE; mXMax = -Float.MAX_VALUE; mXMin = Float.MAX_VALUE; + if (mValues == null || mValues.isEmpty()) + return; + for (T e : mValues) { calcMinMax(e); } @@ -76,12 +76,11 @@ public void calcMinMax() { @Override public void calcMinMaxY(float fromX, float toX) { - - if (mValues == null || mValues.isEmpty()) - return; - mYMax = -Float.MAX_VALUE; mYMin = Float.MAX_VALUE; + + if (mValues == null || mValues.isEmpty()) + return; int indexFrom = getEntryIndex(fromX, Float.NaN, Rounding.DOWN); int indexTo = getEntryIndex(toX, Float.NaN, Rounding.UP); From 8df9eda7af8025eb0702baa5ae2f5dd1dcd8e4b1 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 12:39:03 +0200 Subject: [PATCH 259/291] Call notifyDataChanged for an opportunity for subclasses --- .../java/com/github/mikephil/charting/data/ChartData.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java index 9bd460290d..95d439a71d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java @@ -399,7 +399,7 @@ public boolean removeDataSet(T d) { // if a DataSet was removed if (removed) { - calcMinMax(); + notifyDataChanged(); } return removed; @@ -526,7 +526,7 @@ public boolean removeEntry(Entry e, int dataSetIndex) { boolean removed = set.removeEntry(e); if (removed) { - calcMinMax(); + notifyDataChanged(); } return removed; From 4ce14e6cc90fbe8706fb4d0c5fbec4184927f6c3 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 12:42:32 +0200 Subject: [PATCH 260/291] Add a warning message if pie chart has more than one data set https://github.com/danielgindi/Charts/pull/3286 --- .../com/github/mikephil/charting/data/PieData.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java index db7972a3fb..96667aede6 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java @@ -1,6 +1,8 @@ package com.github.mikephil.charting.data; +import android.util.Log; + import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; @@ -46,6 +48,18 @@ public IPieDataSet getDataSet() { return mDataSets.get(0); } + @Override + public List getDataSets() { + List dataSets = super.getDataSets(); + + if (dataSets.size() < 1) { + Log.e("MPAndroidChart", + "Found multiple data sets while pie chart only allows one"); + } + + return dataSets; + } + /** * The PieData object can only have one DataSet. Use getDataSet() method instead. * From 58545bbbfa04b053d10784df9e041c4fc3d08b9d Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 12:46:37 +0200 Subject: [PATCH 261/291] Add option to disable clipping data to contentRect https://github.com/danielgindi/Charts/pull/3360 --- .../charting/charts/BarLineChartBase.java | 31 +++++++++++++++++-- .../mikephil/charting/data/PieData.java | 2 +- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index c7a593736f..0926dff244 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -100,6 +100,8 @@ public abstract class BarLineChartBase getDataSets() { Log.e("MPAndroidChart", "Found multiple data sets while pie chart only allows one"); } - + return dataSets; } From 7752efef7e09a7b782c490ee034bb0295d51f004 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 13:04:11 +0200 Subject: [PATCH 262/291] Support for labelXOffset for YAxis label --- .../mikephil/charting/charts/RadarChart.java | 1 + .../mikephil/charting/components/YAxis.java | 21 +++++++++++++++++++ .../charting/renderer/YAxisRenderer.java | 7 ++++++- .../YAxisRendererHorizontalBarChart.java | 7 ++++++- .../renderer/YAxisRendererRadarChart.java | 4 +++- 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java index 3c9aec0dde..8c0885395d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java @@ -84,6 +84,7 @@ protected void init() { super.init(); mYAxis = new YAxis(AxisDependency.LEFT); + mYAxis.setLabelXOffset(10f); mWebLineWidth = Utils.convertDpToPixel(1.5f); mInnerWebLineWidth = Utils.convertDpToPixel(0.75f); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java index 030603f55a..a4e58c1b68 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java @@ -73,6 +73,11 @@ public class YAxis extends AxisBase { */ private YAxisLabelPosition mPosition = YAxisLabelPosition.OUTSIDE_CHART; + /** + * the horizontal offset of the y-label + */ + private float mXLabelOffset = 0.0f; + /** * enum for the position of the y-labels relative to the chart */ @@ -174,6 +179,22 @@ public void setPosition(YAxisLabelPosition pos) { mPosition = pos; } + /** + * returns the horizontal offset of the y-label + */ + public float getLabelXOffset() { + return mXLabelOffset; + } + + /** + * sets the horizontal offset of the y-label + * + * @param xOffset + */ + public void setLabelXOffset(float xOffset) { + mXLabelOffset = xOffset; + } + /** * returns true if drawing the top y-axis label entry is enabled * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java index a2bf679777..53cca7ee03 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -119,12 +119,17 @@ protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, flo ? mYAxis.mEntryCount : (mYAxis.mEntryCount - 1); + float xOffset = mYAxis.getLabelXOffset(); + // draw for (int i = from; i < to; i++) { String text = mYAxis.getFormattedLabel(i); - c.drawText(text, fixedPosition, positions[i * 2 + 1] + offset, mAxisLabelPaint); + c.drawText(text, + fixedPosition + xOffset, + positions[i * 2 + 1] + offset, + mAxisLabelPaint); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java index 71275b03c3..fedf8054a1 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java @@ -142,11 +142,16 @@ protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, flo ? mYAxis.mEntryCount : (mYAxis.mEntryCount - 1); + float xOffset = mYAxis.getLabelXOffset(); + for (int i = from; i < to; i++) { String text = mYAxis.getFormattedLabel(i); - c.drawText(text, positions[i * 2], fixedPosition - offset, mAxisLabelPaint); + c.drawText(text, + positions[i * 2], + fixedPosition - offset + xOffset, + mAxisLabelPaint); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java index e3f69d7965..f7b1ad9e87 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -163,6 +163,8 @@ public void renderAxisLabels(Canvas c) { ? mYAxis.mEntryCount : (mYAxis.mEntryCount - 1); + float xOffset = mYAxis.getLabelXOffset(); + for (int j = from; j < to; j++) { float r = (mYAxis.mEntries[j] - mYAxis.mAxisMinimum) * factor; @@ -171,7 +173,7 @@ public void renderAxisLabels(Canvas c) { String label = mYAxis.getFormattedLabel(j); - c.drawText(label, pOut.x + 10, pOut.y, mAxisLabelPaint); + c.drawText(label, pOut.x + xOffset, pOut.y, mAxisLabelPaint); } MPPointF.recycleInstance(center); MPPointF.recycleInstance(pOut); From ae59e7a19e70b53d419eacd3d9520d76ff4553cd Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 13:12:04 +0200 Subject: [PATCH 263/291] This is for the inline bubble selection https://github.com/danielgindi/Charts/pull/3548 --- .../main/java/com/github/mikephil/charting/data/DataSet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java index f28f92581b..08fd76ba01 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -367,7 +367,7 @@ public int getEntryIndex(float xValue, float closestToY, Rounding rounding) { if (value.getX() != closestXValue) break; - if (Math.abs(value.getY() - closestToY) < Math.abs(closestYValue - closestToY)) { + if (Math.abs(value.getY() - closestToY) <= Math.abs(closestYValue - closestToY)) { closestYValue = closestToY; closestYIndex = closest; } From c97c8d247f0a882ce1a034d2f08700a5550564f4 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 13:23:12 +0200 Subject: [PATCH 264/291] Fixed index out of bounds issue when using stacked bar chart https://github.com/danielgindi/Charts/commit/b03cf16ec47437c066e17b5b8f77322111695e6a --- .../github/mikephil/charting/data/BarDataSet.java | 4 +--- .../mikephil/charting/renderer/LegendRenderer.java | 13 +++++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java index 496f4046f8..ed076dc94a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java @@ -38,9 +38,7 @@ public class BarDataSet extends BarLineScatterCandleBubbleDataSet impl /** * array of labels used to describe the different values of the stacked bars */ - private String[] mStackLabels = new String[]{ - "Stack" - }; + private String[] mStackLabels = new String[]{}; public BarDataSet(List yVals, String label) { super(yVals, label); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java index 4e7e5d6448..5d49580561 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -101,10 +101,19 @@ public void computeLegend(ChartData data) { IBarDataSet bds = (IBarDataSet) dataSet; String[] sLabels = bds.getStackLabels(); - for (int j = 0; j < clrs.size() && j < bds.getStackSize(); j++) { + int minEntries = Math.min(clrs.size(), bds.getStackSize()); + + for (int j = 0; j < minEntries; j++) { + String label; + if (sLabels.length > 0) { + int labelIndex = j % minEntries; + label = labelIndex < sLabels.length ? sLabels[labelIndex] : null; + } else { + label = null; + } computedEntries.add(new LegendEntry( - sLabels[j % sLabels.length], + label, dataSet.getForm(), dataSet.getFormSize(), dataSet.getFormLineWidth(), From 13aee592b189fdfc803b6380318dba4853d4c476 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 13:43:42 +0200 Subject: [PATCH 265/291] Improve min/max calculation https://github.com/danielgindi/Charts/pull/3650 --- .../mikephil/charting/components/YAxis.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java index a4e58c1b68..d2071ec5a8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java @@ -427,6 +427,26 @@ public void calculate(float dataMin, float dataMax) { float min = dataMin; float max = dataMax; + // Make sure max is greater than min + // Discussion: https://github.com/danielgindi/Charts/pull/3650#discussion_r221409991 + if (min > max) + { + if (mCustomAxisMax && mCustomAxisMin) + { + float t = min; + min = max; + max = t; + } + else if (mCustomAxisMax) + { + min = max < 0f ? max * 1.5f : max * 0.5f; + } + else if (mCustomAxisMin) + { + max = min < 0f ? min * 0.5f : min * 1.5f; + } + } + float range = Math.abs(max - min); // in case all values are equal From fcc5af71ce2991e62aeebe48705e1f4076649a27 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 13:49:10 +0200 Subject: [PATCH 266/291] Call onChartScale listener after double-tap-zoom https://github.com/danielgindi/Charts/pull/3770 --- .../charting/listener/BarLineChartTouchListener.java | 9 ++++++++- .../charting/listener/OnChartGestureListener.java | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java index 53ab12a369..5685d32fa0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java @@ -580,12 +580,19 @@ public boolean onDoubleTap(MotionEvent e) { MPPointF trans = getTrans(e.getX(), e.getY()); - mChart.zoom(mChart.isScaleXEnabled() ? 1.4f : 1f, mChart.isScaleYEnabled() ? 1.4f : 1f, trans.x, trans.y); + float scaleX = mChart.isScaleXEnabled() ? 1.4f : 1f; + float scaleY = mChart.isScaleYEnabled() ? 1.4f : 1f; + + mChart.zoom(scaleX, scaleY, trans.x, trans.y); if (mChart.isLogEnabled()) Log.i("BarlineChartTouch", "Double-Tap, Zooming In, x: " + trans.x + ", y: " + trans.y); + if (l != null) { + l.onChartScale(e, scaleX, scaleY); + } + MPPointF.recycleInstance(trans); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java index a17cdde941..da0c5ed180 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java @@ -57,7 +57,7 @@ public interface OnChartGestureListener { void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY); /** - * Callbacks when the chart is scaled / zoomed via pinch zoom gesture. + * Callbacks when the chart is scaled / zoomed via pinch zoom / double-tap gesture. * * @param me * @param scaleX scalefactor on the x-axis From e02e9be2fa52d9faf8518f4f9ed81248c8a87346 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 14:13:31 +0200 Subject: [PATCH 267/291] Multiple colors for valueline https://github.com/danielgindi/Charts/pull/3709 --- .../PiePolylineChartActivity.java | 1 - .../mikephil/charting/data/PieDataSet.java | 31 +++++++++++++++---- .../interfaces/datasets/IPieDataSet.java | 8 ++--- .../charting/renderer/PieChartRenderer.java | 15 ++++++--- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index b276806c7d..dd3bd575da 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -156,7 +156,6 @@ private void setData(int count, float range) { dataSet.setValueLinePart1OffsetPercentage(80.f); dataSet.setValueLinePart1Length(0.2f); dataSet.setValueLinePart2Length(0.4f); - //dataSet.setUsingSliceColorAsValueLineColor(true); //dataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); dataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java index c473e0a752..8aea673dba 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java @@ -23,8 +23,8 @@ public class PieDataSet extends DataSet implements IPieDataSet { private ValuePosition mXValuePosition = ValuePosition.INSIDE_SLICE; private ValuePosition mYValuePosition = ValuePosition.INSIDE_SLICE; - private boolean mUsingSliceColorAsValueLineColor = false; private int mValueLineColor = 0xff000000; + private boolean mUseValueColorForLine = false; private float mValueLineWidth = 1.0f; private float mValueLinePart1OffsetPercentage = 75.f; private float mValueLinePart1Length = 0.3f; @@ -137,15 +137,23 @@ public void setYValuePosition(ValuePosition yValuePosition) { } /** - * When valuePosition is OutsideSlice, use slice colors as line color if true + * This method is deprecated. + * Use isUseValueColorForLineEnabled() instead. */ - @Override + @Deprecated public boolean isUsingSliceColorAsValueLineColor() { - return mUsingSliceColorAsValueLineColor; + return isUseValueColorForLineEnabled(); } - public void setUsingSliceColorAsValueLineColor(boolean usingSliceColorAsValueLineColor) { - this.mUsingSliceColorAsValueLineColor = usingSliceColorAsValueLineColor; + /** + * This method is deprecated. + * Use setUseValueColorForLine(...) instead. + * + * @param enabled + */ + @Deprecated + public void setUsingSliceColorAsValueLineColor(boolean enabled) { + setUseValueColorForLine(enabled); } /** @@ -160,6 +168,17 @@ public void setValueLineColor(int valueLineColor) { this.mValueLineColor = valueLineColor; } + @Override + public boolean isUseValueColorForLineEnabled() + { + return mUseValueColorForLine; + } + + public void setUseValueColorForLine(boolean enabled) + { + mUseValueColorForLine = enabled; + } + /** * When valuePosition is OutsideSlice, indicates line width */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java index 1e1ca23532..3720394f25 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java @@ -38,14 +38,14 @@ public interface IPieDataSet extends IDataSet { PieDataSet.ValuePosition getYValuePosition(); /** - * When valuePosition is OutsideSlice, use slice colors as line color if true + * When valuePosition is OutsideSlice, indicates line color * */ - boolean isUsingSliceColorAsValueLineColor(); + int getValueLineColor(); /** - * When valuePosition is OutsideSlice, indicates line color + * When valuePosition is OutsideSlice and enabled, line will have the same color as the slice * */ - int getValueLineColor(); + boolean isUseValueColorForLineEnabled(); /** * When valuePosition is OutsideSlice, indicates line width 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 be3593adcb..82654d4816 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 @@ -468,7 +468,9 @@ public void drawValues(Canvas c) { int entryCount = dataSet.getEntryCount(); - mValueLinePaint.setColor(dataSet.getValueLineColor()); + boolean isUseValueColorForLineEnabled = dataSet.isUseValueColorForLineEnabled(); + int valueLineColor = dataSet.getValueLineColor(); + mValueLinePaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getValueLineWidth())); final float sliceSpace = getSliceSpace(dataSet); @@ -565,12 +567,15 @@ public void drawValues(Canvas c) { labelPty = pt2y; } - if (dataSet.getValueLineColor() != ColorTemplate.COLOR_NONE) { + int lineColor = ColorTemplate.COLOR_NONE; - if (dataSet.isUsingSliceColorAsValueLineColor()) { - mValueLinePaint.setColor(dataSet.getColor(j)); - } + if (isUseValueColorForLineEnabled) + lineColor = dataSet.getColor(j); + else if (valueLineColor != ColorTemplate.COLOR_NONE) + lineColor = valueLineColor; + if (lineColor != ColorTemplate.COLOR_NONE) { + mValueLinePaint.setColor(lineColor); c.drawLine(pt0x, pt0y, pt1x, pt1y, mValueLinePaint); c.drawLine(pt1x, pt1y, pt2x, pt2y, mValueLinePaint); } From 14456f475fcfab0875e3f81604c4c7d69eea5fe0 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 14:24:37 +0200 Subject: [PATCH 268/291] Renamed values -> entries for consistency https://github.com/danielgindi/Charts/pull/3847 --- .../mikephil/charting/data/BarDataSet.java | 4 +- .../mikephil/charting/data/BubbleDataSet.java | 4 +- .../mikephil/charting/data/CandleDataSet.java | 4 +- .../mikephil/charting/data/DataSet.java | 114 +++++++++++------- .../mikephil/charting/data/LineDataSet.java | 4 +- .../mikephil/charting/data/PieDataSet.java | 4 +- .../mikephil/charting/data/RadarDataSet.java | 4 +- .../charting/data/ScatterDataSet.java | 4 +- 8 files changed, 82 insertions(+), 60 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java index ed076dc94a..7b7ee5f916 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java @@ -52,8 +52,8 @@ public BarDataSet(List yVals, String label) { @Override public DataSet copy() { List entries = new ArrayList(); - for (int i = 0; i < mValues.size(); i++) { - entries.add(mValues.get(i).copy()); + for (int i = 0; i < mEntries.size(); i++) { + entries.add(mEntries.get(i).copy()); } BarDataSet copied = new BarDataSet(entries, getLabel()); copy(copied); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java index 1f88272dd9..9ef87fb2d5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java @@ -42,8 +42,8 @@ protected void calcMinMax(BubbleEntry e) { @Override public DataSet copy() { List entries = new ArrayList(); - for (int i = 0; i < mValues.size(); i++) { - entries.add(mValues.get(i).copy()); + for (int i = 0; i < mEntries.size(); i++) { + entries.add(mEntries.get(i).copy()); } BubbleDataSet copied = new BubbleDataSet(entries, getLabel()); copy(copied); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java index c7f8362803..dcd5b76cea 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java @@ -80,8 +80,8 @@ public CandleDataSet(List yVals, String label) { @Override public DataSet copy() { List entries = new ArrayList(); - for (int i = 0; i < mValues.size(); i++) { - entries.add(mValues.get(i).copy()); + for (int i = 0; i < mEntries.size(); i++) { + entries.add(mEntries.get(i).copy()); } CandleDataSet copied = new CandleDataSet(entries, getLabel()); copy(copied); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java index 08fd76ba01..fda07efef2 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -17,7 +17,7 @@ public abstract class DataSet extends BaseDataSet { /** * the entries that this DataSet represents / holds together */ - protected List mValues = null; + protected List mEntries; /** * maximum y-value in the value array @@ -45,15 +45,15 @@ public abstract class DataSet extends BaseDataSet { * label that describes the DataSet can be specified. The label can also be * used to retrieve the DataSet from a ChartData object. * - * @param values + * @param entries * @param label */ - public DataSet(List values, String label) { + public DataSet(List entries, String label) { super(label); - this.mValues = values; + this.mEntries = entries; - if (mValues == null) - mValues = new ArrayList(); + if (mEntries == null) + mEntries = new ArrayList(); calcMinMax(); } @@ -66,10 +66,10 @@ public void calcMinMax() { mXMax = -Float.MAX_VALUE; mXMin = Float.MAX_VALUE; - if (mValues == null || mValues.isEmpty()) + if (mEntries == null || mEntries.isEmpty()) return; - for (T e : mValues) { + for (T e : mEntries) { calcMinMax(e); } } @@ -79,7 +79,7 @@ public void calcMinMaxY(float fromX, float toX) { mYMax = -Float.MAX_VALUE; mYMin = Float.MAX_VALUE; - if (mValues == null || mValues.isEmpty()) + if (mEntries == null || mEntries.isEmpty()) return; int indexFrom = getEntryIndex(fromX, Float.NaN, Rounding.DOWN); @@ -90,7 +90,7 @@ public void calcMinMaxY(float fromX, float toX) { for (int i = indexFrom; i <= indexTo; i++) { // only recalculate y - calcMinMaxY(mValues.get(i)); + calcMinMaxY(mEntries.get(i)); } } @@ -129,25 +129,47 @@ protected void calcMinMaxY(T e) { @Override public int getEntryCount() { - return mValues.size(); + return mEntries.size(); } /** - * Returns the array of entries that this DataSet represents. + * This method is deprecated. + * Use getEntries() instead. * * @return */ + @Deprecated public List getValues() { - return mValues; + return mEntries; } /** - * Sets the array of entries that this DataSet represents, and calls notifyDataSetChanged() + * Returns the array of entries that this DataSet represents. * * @return */ + public List getEntries() { + return mEntries; + } + + /** + * This method is deprecated. + * Use setEntries(...) instead. + * + * @param values + */ + @Deprecated public void setValues(List values) { - mValues = values; + setEntries(values); + } + + /** + * Sets the array of entries that this DataSet represents, and calls notifyDataSetChanged() + * + * @return + */ + public void setEntries(List entries) { + mEntries = entries; notifyDataSetChanged(); } @@ -170,8 +192,8 @@ protected void copy(DataSet dataSet) { public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append(toSimpleString()); - for (int i = 0; i < mValues.size(); i++) { - buffer.append(mValues.get(i).toString() + " "); + for (int i = 0; i < mEntries.size(); i++) { + buffer.append(mEntries.get(i).toString() + " "); } return buffer.toString(); } @@ -184,7 +206,7 @@ public String toString() { */ public String toSimpleString() { StringBuffer buffer = new StringBuffer(); - buffer.append("DataSet, label: " + (getLabel() == null ? "" : getLabel()) + ", entries: " + mValues.size() + + buffer.append("DataSet, label: " + (getLabel() == null ? "" : getLabel()) + ", entries: " + mEntries.size() + "\n"); return buffer.toString(); } @@ -215,23 +237,23 @@ public void addEntryOrdered(T e) { if (e == null) return; - if (mValues == null) { - mValues = new ArrayList(); + if (mEntries == null) { + mEntries = new ArrayList(); } calcMinMax(e); - if (mValues.size() > 0 && mValues.get(mValues.size() - 1).getX() > e.getX()) { + if (mEntries.size() > 0 && mEntries.get(mEntries.size() - 1).getX() > e.getX()) { int closestIndex = getEntryIndex(e.getX(), e.getY(), Rounding.UP); - mValues.add(closestIndex, e); + mEntries.add(closestIndex, e); } else { - mValues.add(e); + mEntries.add(e); } } @Override public void clear() { - mValues.clear(); + mEntries.clear(); notifyDataSetChanged(); } @@ -241,9 +263,9 @@ public boolean addEntry(T e) { if (e == null) return false; - List values = getValues(); + List values = getEntries(); if (values == null) { - values = new ArrayList(); + values = new ArrayList<>(); } calcMinMax(e); @@ -258,11 +280,11 @@ public boolean removeEntry(T e) { if (e == null) return false; - if (mValues == null) + if (mEntries == null) return false; // remove the entry - boolean removed = mValues.remove(e); + boolean removed = mEntries.remove(e); if (removed) { calcMinMax(); @@ -273,7 +295,7 @@ public boolean removeEntry(T e) { @Override public int getEntryIndex(Entry e) { - return mValues.indexOf(e); + return mEntries.indexOf(e); } @Override @@ -281,7 +303,7 @@ public T getEntryForXValue(float xValue, float closestToY, Rounding rounding) { int index = getEntryIndex(xValue, closestToY, rounding); if (index > -1) - return mValues.get(index); + return mEntries.get(index); return null; } @@ -292,24 +314,24 @@ public T getEntryForXValue(float xValue, float closestToY) { @Override public T getEntryForIndex(int index) { - return mValues.get(index); + return mEntries.get(index); } @Override public int getEntryIndex(float xValue, float closestToY, Rounding rounding) { - if (mValues == null || mValues.isEmpty()) + if (mEntries == null || mEntries.isEmpty()) return -1; int low = 0; - int high = mValues.size() - 1; + int high = mEntries.size() - 1; int closest = high; while (low < high) { int m = (low + high) / 2; - final float d1 = mValues.get(m).getX() - xValue, - d2 = mValues.get(m + 1).getX() - xValue, + final float d1 = mEntries.get(m).getX() - xValue, + d2 = mEntries.get(m + 1).getX() - xValue, ad1 = Math.abs(d1), ad2 = Math.abs(d2); if (ad2 < ad1) { @@ -336,10 +358,10 @@ public int getEntryIndex(float xValue, float closestToY, Rounding rounding) { } if (closest != -1) { - float closestXValue = mValues.get(closest).getX(); + float closestXValue = mEntries.get(closest).getX(); if (rounding == Rounding.UP) { // If rounding up, and found x-value is lower than specified x, and we can go upper... - if (closestXValue < xValue && closest < mValues.size() - 1) { + if (closestXValue < xValue && closest < mEntries.size() - 1) { ++closest; } } else if (rounding == Rounding.DOWN) { @@ -351,18 +373,18 @@ public int getEntryIndex(float xValue, float closestToY, Rounding rounding) { // Search by closest to y-value if (!Float.isNaN(closestToY)) { - while (closest > 0 && mValues.get(closest - 1).getX() == closestXValue) + while (closest > 0 && mEntries.get(closest - 1).getX() == closestXValue) closest -= 1; - float closestYValue = mValues.get(closest).getY(); + float closestYValue = mEntries.get(closest).getY(); int closestYIndex = closest; while (true) { closest += 1; - if (closest >= mValues.size()) + if (closest >= mEntries.size()) break; - final Entry value = mValues.get(closest); + final Entry value = mEntries.get(closest); if (value.getX() != closestXValue) break; @@ -386,22 +408,22 @@ public List getEntriesForXValue(float xValue) { List entries = new ArrayList(); int low = 0; - int high = mValues.size() - 1; + int high = mEntries.size() - 1; while (low <= high) { int m = (high + low) / 2; - T entry = mValues.get(m); + T entry = mEntries.get(m); // if we have a match if (xValue == entry.getX()) { - while (m > 0 && mValues.get(m - 1).getX() == xValue) + while (m > 0 && mEntries.get(m - 1).getX() == xValue) m--; - high = mValues.size(); + high = mEntries.size(); // loop over all "equal" entries for (; m < high; m++) { - entry = mValues.get(m); + entry = mEntries.get(m); if (entry.getX() == xValue) { entries.add(entry); } else { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java index c1018d1fb4..10d1837ecd 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java @@ -85,8 +85,8 @@ public LineDataSet(List yVals, String label) { @Override public DataSet copy() { List entries = new ArrayList(); - for (int i = 0; i < mValues.size(); i++) { - entries.add(mValues.get(i).copy()); + for (int i = 0; i < mEntries.size(); i++) { + entries.add(mEntries.get(i).copy()); } LineDataSet copied = new LineDataSet(entries, getLabel()); copy(copied); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java index 8aea673dba..38a5d0b89b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java @@ -40,8 +40,8 @@ public PieDataSet(List yVals, String label) { @Override public DataSet copy() { List entries = new ArrayList<>(); - for (int i = 0; i < mValues.size(); i++) { - entries.add(mValues.get(i).copy()); + for (int i = 0; i < mEntries.size(); i++) { + entries.add(mEntries.get(i).copy()); } PieDataSet copied = new PieDataSet(entries, getLabel()); copy(copied); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java index 09c94b417d..8a9740b6b5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java @@ -102,8 +102,8 @@ public void setHighlightCircleStrokeWidth(float strokeWidth) { @Override public DataSet copy() { List entries = new ArrayList(); - for (int i = 0; i < mValues.size(); i++) { - entries.add(mValues.get(i).copy()); + for (int i = 0; i < mEntries.size(); i++) { + entries.add(mEntries.get(i).copy()); } RadarDataSet copied = new RadarDataSet(entries, getLabel()); copy(copied); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java index d234c751a0..85ed808160 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java @@ -48,8 +48,8 @@ public ScatterDataSet(List yVals, String label) { @Override public DataSet copy() { List entries = new ArrayList(); - for (int i = 0; i < mValues.size(); i++) { - entries.add(mValues.get(i).copy()); + for (int i = 0; i < mEntries.size(); i++) { + entries.add(mEntries.get(i).copy()); } ScatterDataSet copied = new ScatterDataSet(entries, getLabel()); copy(copied); From 45240c3723387a0980074a5b4a72e1d2166192a4 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 22 Jan 2020 16:01:42 +0200 Subject: [PATCH 269/291] Improved negative offset for horz bar chart https://github.com/danielgindi/Charts/pull/3854 --- .../HorizontalBarNegativeChartActivity.java | 288 ++++++++++++++++++ MPChartExample/src/main/AndroidManifest.xml | 1 + .../notimportant/MainActivity.java | 83 ++--- .../renderer/HorizontalBarChartRenderer.java | 3 +- 4 files changed, 335 insertions(+), 40 deletions(-) create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java new file mode 100644 index 0000000000..c3de5fa68f --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java @@ -0,0 +1,288 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.annotation.SuppressLint; +import android.graphics.RectF; +import android.os.Bundle; +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 android.widget.Toast; + +import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +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.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.MPPointF; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.List; + +public class HorizontalBarNegativeChartActivity extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + protected HorizontalBarChart mChart; + private SeekBar mSeekBarX, mSeekBarY; + 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_horizontalbarchart); + + tvX = findViewById(R.id.tvXMax); + tvY = (TextView) findViewById(R.id.tvYMax); + + mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + + mChart = (HorizontalBarChart) findViewById(R.id.chart1); + mChart.setOnChartValueSelectedListener(this); + // mChart.setHighlightEnabled(false); + + mChart.setDrawBarShadow(false); + + mChart.setDrawValueAboveBar(true); + + mChart.getDescription().setEnabled(false); + + // if more than 60 entries are displayed in the chart, no values will be + // drawn + mChart.setMaxVisibleValueCount(60); + + // scaling can now only be done on x- and y-axis separately + mChart.setPinchZoom(false); + + // draw shadows for each bar that show the maximum value + // mChart.setDrawBarShadow(true); + + mChart.setDrawGridBackground(false); + + XAxis xl = mChart.getXAxis(); + xl.setPosition(XAxisPosition.BOTTOM); + xl.setTypeface(mTfLight); + xl.setDrawAxisLine(true); + xl.setDrawGridLines(false); + xl.setGranularity(10f); + + YAxis yl = mChart.getAxisLeft(); + yl.setTypeface(mTfLight); + yl.setDrawAxisLine(true); + yl.setDrawGridLines(true); + yl.setDrawZeroLine(true); // draw a zero line +// yl.setInverted(true); + + YAxis yr = mChart.getAxisRight(); + yr.setTypeface(mTfLight); + yr.setDrawAxisLine(true); + yr.setDrawGridLines(false); +// yr.setInverted(true); + + setData(12, 50); + mChart.setFitBars(true); + mChart.animateY(2500); + + // setting data + mSeekBarY.setProgress(50); + mSeekBarX.setProgress(12); + + mSeekBarY.setOnSeekBarChangeListener(this); + mSeekBarX.setOnSeekBarChangeListener(this); + + Legend l = mChart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setFormSize(8f); + l.setXEntrySpace(4f); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.bar, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.actionToggleValues: { + List sets = mChart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + IBarDataSet set = (BarDataSet) iSet; + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + mChart.invalidate(); + break; + } + case R.id.actionToggleIcons: { + List sets = mChart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + IBarDataSet set = (BarDataSet) iSet; + set.setDrawIcons(!set.isDrawIconsEnabled()); + } + + mChart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if(mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (mChart.isPinchZoomEnabled()) + mChart.setPinchZoom(false); + else + mChart.setPinchZoom(true); + + mChart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); + mChart.notifyDataSetChanged(); + break; + } + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : mChart.getData().getDataSets()) + ((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + + mChart.invalidate(); + break; + } + case R.id.animateX: { + mChart.animateX(3000); + break; + } + case R.id.animateY: { + mChart.animateY(3000); + break; + } + case R.id.animateXY: { + + mChart.animateXY(3000, 3000); + break; + } + case R.id.actionSave: { + if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { + Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", + Toast.LENGTH_SHORT).show(); + } else + Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) + .show(); + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText("" + (mSeekBarX.getProgress() + 1)); + tvY.setText("" + (mSeekBarY.getProgress())); + + setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); + mChart.setFitBars(true); + mChart.invalidate(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } + + private void setData(int count, float range) { + + float barWidth = 9f; + float spaceForBar = 10f; + ArrayList yVals1 = new ArrayList(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range - range / 2); + yVals1.add(new BarEntry(i * spaceForBar, val, + getResources().getDrawable(R.drawable.star))); + } + + BarDataSet set1; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); + set1.setValues(yVals1); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(yVals1, "DataSet 1"); + + set1.setDrawIcons(false); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); + + BarData data = new BarData(dataSets); + data.setValueTextSize(10f); + data.setValueTypeface(mTfLight); + data.setBarWidth(barWidth); + mChart.setData(data); + } + } + + protected RectF mOnValueSelectedRectF = new RectF(); + @SuppressLint("NewApi") + @Override + public void onValueSelected(Entry e, Highlight h) { + + if (e == null) + return; + + RectF bounds = mOnValueSelectedRectF; + mChart.getBarBounds((BarEntry) e, bounds); + + MPPointF position = mChart.getPosition(e, mChart.getData().getDataSetByIndex(h.getDataSetIndex()) + .getAxisDependency()); + + Log.i("bounds", bounds.toString()); + Log.i("position", position.toString()); + + MPPointF.recycleInstance(position); + } + + @Override + public void onNothingSelected() { + }; +} diff --git a/MPChartExample/src/main/AndroidManifest.xml b/MPChartExample/src/main/AndroidManifest.xml index 28c55b89b2..99334e601a 100644 --- a/MPChartExample/src/main/AndroidManifest.xml +++ b/MPChartExample/src/main/AndroidManifest.xml @@ -24,6 +24,7 @@ + diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index 67749e742f..88e5dc8d8b 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -26,6 +26,7 @@ import com.xxmassdeveloper.mpchartexample.FilledLineActivity; import com.xxmassdeveloper.mpchartexample.HalfPieChartActivity; import com.xxmassdeveloper.mpchartexample.HorizontalBarChartActivity; +import com.xxmassdeveloper.mpchartexample.HorizontalBarNegativeChartActivity; import com.xxmassdeveloper.mpchartexample.InvertedLineChartActivity; import com.xxmassdeveloper.mpchartexample.LineChartActivity1; import com.xxmassdeveloper.mpchartexample.LineChartActivity2; @@ -87,40 +88,41 @@ protected void onCreate(Bundle savedInstanceState) { objects.add(13, new ContentItem("Horizontal", "Render bar chart horizontally.")); objects.add(14, new ContentItem("Stacked", "Stacked bar chart.")); objects.add(15, new ContentItem("Negative", "Positive and negative values with unique colors.")); - objects.add(16, new ContentItem("Stacked 2", "Stacked bar chart with negative values.")); - objects.add(17, new ContentItem("Sine", "Sine function in bar chart format.")); + objects.add(16, new ContentItem("Negative Horizontal", "demonstrates how to create a HorizontalBarChart with positive and negative values.")); + objects.add(17, new ContentItem("Stacked 2", "Stacked bar chart with negative values.")); + objects.add(18, new ContentItem("Sine", "Sine function in bar chart format.")); //// - objects.add(18, new ContentItem("Pie Charts")); + objects.add(19, new ContentItem("Pie Charts")); - objects.add(19, new ContentItem("Basic", "Simple pie chart.")); - objects.add(20, new ContentItem("Value Lines", "Stylish lines drawn outward from slices.")); - objects.add(21, new ContentItem("Half Pie", "180° (half) pie chart.")); + objects.add(20, new ContentItem("Basic", "Simple pie chart.")); + objects.add(21, new ContentItem("Value Lines", "Stylish lines drawn outward from slices.")); + objects.add(22, new ContentItem("Half Pie", "180° (half) pie chart.")); //// - objects.add(22, new ContentItem("Other Charts")); + objects.add(23, new ContentItem("Other Charts")); - objects.add(23, new ContentItem("Combined Chart", "Bar and line chart together.")); - objects.add(24, new ContentItem("Scatter Plot", "Simple scatter plot.")); - objects.add(25, new ContentItem("Bubble Chart", "Simple bubble chart.")); - objects.add(26, new ContentItem("Candlestick", "Simple financial chart.")); - objects.add(27, new ContentItem("Radar Chart", "Simple web chart.")); + objects.add(24, new ContentItem("Combined Chart", "Bar and line chart together.")); + objects.add(25, new ContentItem("Scatter Plot", "Simple scatter plot.")); + objects.add(26, new ContentItem("Bubble Chart", "Simple bubble chart.")); + objects.add(27, new ContentItem("Candlestick", "Simple financial chart.")); + objects.add(28, new ContentItem("Radar Chart", "Simple web chart.")); //// - objects.add(28, new ContentItem("Scrolling Charts")); + objects.add(29, new ContentItem("Scrolling Charts")); - objects.add(29, new ContentItem("Multiple", "Various types of charts as fragments.")); - objects.add(30, new ContentItem("View Pager", "Swipe through different charts.")); - objects.add(31, new ContentItem("Tall Bar Chart", "Bars bigger than your screen!")); - objects.add(32, new ContentItem("Many Bar Charts", "More bars than your screen can handle!")); + objects.add(30, new ContentItem("Multiple", "Various types of charts as fragments.")); + objects.add(31, new ContentItem("View Pager", "Swipe through different charts.")); + objects.add(32, new ContentItem("Tall Bar Chart", "Bars bigger than your screen!")); + objects.add(33, new ContentItem("Many Bar Charts", "More bars than your screen can handle!")); //// - objects.add(33, new ContentItem("Even More Line Charts")); + objects.add(34, new ContentItem("Even More Line Charts")); - objects.add(34, new ContentItem("Dynamic", "Build a line chart by adding points and sets.")); - objects.add(35, new ContentItem("Realtime", "Add data points in realtime.")); - objects.add(36, new ContentItem("Hourly", "Uses the current time to add a data point for each hour.")); - //objects.add(37, new ContentItem("Realm.io Examples", "See more examples that use Realm.io mobile database.")); + objects.add(35, new ContentItem("Dynamic", "Build a line chart by adding points and sets.")); + objects.add(36, new ContentItem("Realtime", "Add data points in realtime.")); + objects.add(37, new ContentItem("Hourly", "Uses the current time to add a data point for each hour.")); + //objects.add(38, new ContentItem("Realm.io Examples", "See more examples that use Realm.io mobile database.")); MyAdapter adapter = new MyAdapter(this, objects); @@ -179,57 +181,60 @@ public void onItemClick(AdapterView av, View v, int pos, long arg3) { i = new Intent(this, BarChartPositiveNegative.class); break; case 16: - i = new Intent(this, StackedBarActivityNegative.class); + i = new Intent(this, HorizontalBarNegativeChartActivity.class); break; case 17: + i = new Intent(this, StackedBarActivityNegative.class); + break; + case 18: i = new Intent(this, BarChartActivitySinus.class); break; - case 19: + case 20: i = new Intent(this, PieChartActivity.class); break; - case 20: + case 21: i = new Intent(this, PiePolylineChartActivity.class); break; - case 21: + case 22: i = new Intent(this, HalfPieChartActivity.class); break; - case 23: + case 24: i = new Intent(this, CombinedChartActivity.class); break; - case 24: + case 25: i = new Intent(this, ScatterChartActivity.class); break; - case 25: + case 26: i = new Intent(this, BubbleChartActivity.class); break; - case 26: + case 27: i = new Intent(this, CandleStickChartActivity.class); break; - case 27: + case 28: i = new Intent(this, RadarChartActivity.class); break; - case 29: + case 30: i = new Intent(this, ListViewMultiChartActivity.class); break; - case 30: + case 31: i = new Intent(this, SimpleChartDemo.class); break; - case 31: + case 32: i = new Intent(this, ScrollViewActivity.class); break; - case 32: + case 33: i = new Intent(this, ListViewBarChartActivity.class); break; - case 34: + case 35: i = new Intent(this, DynamicalAddingActivity.class); break; - case 35: + case 36: i = new Intent(this, RealtimeLineChartActivity.class); break; - case 36: + case 37: i = new Intent(this, LineChartTime.class); break; - /*case 37: + /*case 38: i = new Intent(this, RealmMainActivity.class); break;*/ } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index 7607abdd92..b692a1f90b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -200,7 +200,8 @@ public void drawValues(Canvas c) { // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); - negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); + negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus) + - (buffer.buffer[j + 2] - buffer.buffer[j]); if (isInverted) { posOffset = -posOffset - valueTextWidth; From 34fefd28e1f50d8d0406e0aac4cb3909c94eb193 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 23 Jan 2020 10:18:28 +0200 Subject: [PATCH 270/291] maxHeight didn't account for the last label https://github.com/danielgindi/Charts/pull/3900 --- .../java/com/github/mikephil/charting/components/Legend.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java index b785098881..708129259b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java @@ -703,8 +703,7 @@ else if (wasStacked) { width += Utils.calcTextWidth(labelpaint, label); - if (i < entryCount - 1) - maxHeight += labelLineHeight + yEntrySpace; + maxHeight += labelLineHeight + yEntrySpace; } else { wasStacked = true; width += formSize; From 0668d30a6bcf5e3c0b1af945bd25128c36d63d31 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 23 Jan 2020 10:54:28 +0200 Subject: [PATCH 271/291] Fixed a bug where a pie slice without highlight enabled is hidden https://github.com/danielgindi/Charts/pull/3969 --- .../github/mikephil/charting/renderer/PieChartRenderer.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java index 82654d4816..f427ffe5d3 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 @@ -258,7 +258,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { } // Don't draw if it's highlighted, unless the chart uses rounded slices - if (mChart.needsHighlight(j) && !drawRoundedSlices) { + if (dataSet.isHighlightEnabled() && mChart.needsHighlight(j) && !drawRoundedSlices) { angle += sliceAngle * phaseX; continue; } @@ -830,8 +830,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { continue; IPieDataSet set = mChart.getData() - .getDataSetByIndex(indices[i] - .getDataSetIndex()); + .getDataSetByIndex(indices[i].getDataSetIndex()); if (set == null || !set.isHighlightEnabled()) continue; From 1de836ac650e45ac1dd7e905368016c8fdd6aeef Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 23 Jan 2020 11:19:07 +0200 Subject: [PATCH 272/291] Remove unexpected dash line during linear animation https://github.com/danielgindi/Charts/pull/4094 --- .../github/mikephil/charting/renderer/LineChartRenderer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index ead9d6d701..2b3c524133 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -326,7 +326,9 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { if (mLineBuffer.length <= pointsPerEntryPair * 2) mLineBuffer = new float[pointsPerEntryPair * 4]; - for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) { + int max = mXBounds.min + mXBounds.range; + + for (int j = mXBounds.min; j < max; j++) { Entry e = dataSet.getEntryForIndex(j); if (e == null) continue; From 5e4a32eb414b074a61b6655c55fa713105eb0195 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 23 Jan 2020 12:09:06 +0200 Subject: [PATCH 273/291] Corrected check for line in vertical bounds https://github.com/danielgindi/Charts/pull/4100 --- .../charting/renderer/LineChartRenderer.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 2b3c524133..849bc4a7aa 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -323,8 +323,10 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { // more than 1 color if (dataSet.getColors().size() > 1) { - if (mLineBuffer.length <= pointsPerEntryPair * 2) - mLineBuffer = new float[pointsPerEntryPair * 4]; + int numberOfFloats = pointsPerEntryPair * 2; + + if (mLineBuffer.length <= numberOfFloats) + mLineBuffer = new float[numberOfFloats * 2]; int max = mXBounds.min + mXBounds.range; @@ -359,16 +361,26 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { mLineBuffer[3] = mLineBuffer[1]; } + // Determine the start and end coordinates of the line, and make sure they differ. + float firstCoordinateX = mLineBuffer[0]; + float firstCoordinateY = mLineBuffer[1]; + float lastCoordinateX = mLineBuffer[numberOfFloats - 2]; + float lastCoordinateY = mLineBuffer[numberOfFloats - 1]; + + if (firstCoordinateX == lastCoordinateX && + firstCoordinateY == lastCoordinateY) + continue; + trans.pointValuesToPixel(mLineBuffer); - if (!mViewPortHandler.isInBoundsRight(mLineBuffer[0])) + if (!mViewPortHandler.isInBoundsRight(firstCoordinateX)) break; // make sure the lines don't do shitty things outside // bounds - if (!mViewPortHandler.isInBoundsLeft(mLineBuffer[2]) - || (!mViewPortHandler.isInBoundsTop(mLineBuffer[1]) && !mViewPortHandler - .isInBoundsBottom(mLineBuffer[3]))) + if (!mViewPortHandler.isInBoundsLeft(lastCoordinateX) || + !mViewPortHandler.isInBoundsTop(lastCoordinateY) || + !mViewPortHandler.isInBoundsBottom(firstCoordinateY)) continue; // get the color that is set for this line-segment From f05337768d87bdd57eaed378fd2fe1a6346b0300 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 23 Jan 2020 15:45:57 +0200 Subject: [PATCH 274/291] Finalized vertical line collision check --- .../github/mikephil/charting/renderer/LineChartRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index 849bc4a7aa..a00860b5a6 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -379,8 +379,8 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { // make sure the lines don't do shitty things outside // bounds if (!mViewPortHandler.isInBoundsLeft(lastCoordinateX) || - !mViewPortHandler.isInBoundsTop(lastCoordinateY) || - !mViewPortHandler.isInBoundsBottom(firstCoordinateY)) + !mViewPortHandler.isInBoundsTop(Math.max(firstCoordinateY, lastCoordinateY)) || + !mViewPortHandler.isInBoundsBottom(Math.min(firstCoordinateY, lastCoordinateY))) continue; // get the color that is set for this line-segment From 351e341ee7498e956c41d307dc2b288223a31821 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Fri, 24 Jan 2020 11:42:03 +0200 Subject: [PATCH 275/291] Fixed merge residue --- .../HorizontalBarNegativeChartActivity.java | 232 +++++++++--------- .../mikephil/charting/data/PieDataSet.java | 2 +- .../interfaces/datasets/IPieDataSet.java | 2 +- 3 files changed, 120 insertions(+), 116 deletions(-) rename MPChartExample/src/{ => main/java}/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java (54%) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java similarity index 54% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java index c3de5fa68f..86d578cc43 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java @@ -1,8 +1,12 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; import android.annotation.SuppressLint; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.RectF; +import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -13,6 +17,8 @@ import android.widget.TextView; import android.widget.Toast; +import androidx.core.content.ContextCompat; + import com.github.mikephil.charting.charts.HorizontalBarChart; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; @@ -34,8 +40,8 @@ public class HorizontalBarNegativeChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - protected HorizontalBarChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private HorizontalBarChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -45,66 +51,66 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_horizontalbarchart); + setTitle("HorizontalBarChartActivity"); + tvX = findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); - mChart = (HorizontalBarChart) findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - // mChart.setHighlightEnabled(false); + seekBarY.setOnSeekBarChangeListener(this); + seekBarX.setOnSeekBarChangeListener(this); - mChart.setDrawBarShadow(false); + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + // chart.setHighlightEnabled(false); - mChart.setDrawValueAboveBar(true); + chart.setDrawBarShadow(false); - mChart.getDescription().setEnabled(false); + chart.setDrawValueAboveBar(true); + + chart.getDescription().setEnabled(false); // if more than 60 entries are displayed in the chart, no values will be // drawn - mChart.setMaxVisibleValueCount(60); + chart.setMaxVisibleValueCount(60); // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); + chart.setPinchZoom(false); // draw shadows for each bar that show the maximum value - // mChart.setDrawBarShadow(true); + // chart.setDrawBarShadow(true); - mChart.setDrawGridBackground(false); + chart.setDrawGridBackground(false); - XAxis xl = mChart.getXAxis(); + XAxis xl = chart.getXAxis(); xl.setPosition(XAxisPosition.BOTTOM); - xl.setTypeface(mTfLight); + xl.setTypeface(tfLight); xl.setDrawAxisLine(true); xl.setDrawGridLines(false); xl.setGranularity(10f); - YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(mTfLight); + YAxis yl = chart.getAxisLeft(); + yl.setTypeface(tfLight); yl.setDrawAxisLine(true); yl.setDrawGridLines(true); - yl.setDrawZeroLine(true); // draw a zero line // yl.setInverted(true); - YAxis yr = mChart.getAxisRight(); - yr.setTypeface(mTfLight); + YAxis yr = chart.getAxisRight(); + yr.setTypeface(tfLight); yr.setDrawAxisLine(true); yr.setDrawGridLines(false); // yr.setInverted(true); - setData(12, 50); - mChart.setFitBars(true); - mChart.animateY(2500); + chart.setFitBars(true); + chart.animateY(2500); // setting data - mSeekBarY.setProgress(50); - mSeekBarX.setProgress(12); - - mSeekBarY.setOnSeekBarChangeListener(this); - mSeekBarX.setOnSeekBarChangeListener(this); + seekBarY.setProgress(50); + seekBarX.setProgress(12); - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); l.setOrientation(Legend.LegendOrientation.HORIZONTAL); @@ -113,6 +119,42 @@ protected void onCreate(Bundle savedInstanceState) { l.setXEntrySpace(4f); } + private void setData(int count, float range) { + + float barWidth = 9f; + float spaceForBar = 10f; + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range - range / 2); + values.add(new BarEntry(i * spaceForBar, val, + getResources().getDrawable(R.drawable.star))); + } + + BarDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(values, "DataSet 1"); + + set1.setDrawIcons(false); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); + + BarData data = new BarData(dataSets); + data.setValueTextSize(10f); + data.setValueTypeface(tfLight); + data.setBarWidth(barWidth); + chart.setData(data); + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.bar, menu); @@ -123,80 +165,80 @@ public boolean onCreateOptionsMenu(Menu menu) { 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/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (IBarDataSet iSet : sets) { - - IBarDataSet set = (BarDataSet) iSet; - set.setDrawValues(!set.isDrawValuesEnabled()); + iSet.setDrawValues(!iSet.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleIcons: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (IBarDataSet iSet : sets) { - - IBarDataSet set = (BarDataSet) iSet; - set.setDrawIcons(!set.isDrawIconsEnabled()); + iSet.setDrawIcons(!iSet.isDrawIconsEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionToggleBarBorders: { - for (IBarDataSet set : mChart.getData().getDataSets()) + for (IBarDataSet set : chart.getData().getDataSets()) ((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -206,64 +248,27 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); - mChart.setFitBars(true); - mChart.invalidate(); + setData(seekBarX.getProgress(), seekBarY.getProgress()); + chart.setFitBars(true); + chart.invalidate(); } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - + protected void saveToGallery() { + saveToGallery(chart, "HorizontalBarChartActivity"); } @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - private void setData(int count, float range) { - - float barWidth = 9f; - float spaceForBar = 10f; - ArrayList yVals1 = new ArrayList(); - - for (int i = 0; i < count; i++) { - float val = (float) (Math.random() * range - range / 2); - yVals1.add(new BarEntry(i * spaceForBar, val, - getResources().getDrawable(R.drawable.star))); - } - - BarDataSet set1; - - if (mChart.getData() != null && - mChart.getData().getDataSetCount() > 0) { - set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); - set1.setValues(yVals1); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); - } else { - set1 = new BarDataSet(yVals1, "DataSet 1"); - - set1.setDrawIcons(false); + public void onStartTrackingTouch(SeekBar seekBar) {} - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} - BarData data = new BarData(dataSets); - data.setValueTextSize(10f); - data.setValueTypeface(mTfLight); - data.setBarWidth(barWidth); - mChart.setData(data); - } - } + private final RectF mOnValueSelectedRectF = new RectF(); - protected RectF mOnValueSelectedRectF = new RectF(); - @SuppressLint("NewApi") @Override public void onValueSelected(Entry e, Highlight h) { @@ -271,9 +276,9 @@ public void onValueSelected(Entry e, Highlight h) { return; RectF bounds = mOnValueSelectedRectF; - mChart.getBarBounds((BarEntry) e, bounds); + chart.getBarBounds((BarEntry) e, bounds); - MPPointF position = mChart.getPosition(e, mChart.getData().getDataSetByIndex(h.getDataSetIndex()) + MPPointF position = chart.getPosition(e, chart.getData().getDataSetByIndex(h.getDataSetIndex()) .getAxisDependency()); Log.i("bounds", bounds.toString()); @@ -283,6 +288,5 @@ public void onValueSelected(Entry e, Highlight h) { } @Override - public void onNothingSelected() { - }; + public void onNothingSelected() {} } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java index 38a5d0b89b..c83b24547b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java @@ -3,7 +3,7 @@ import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; import com.github.mikephil.charting.utils.Utils; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import java.util.ArrayList; import java.util.List; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java index 3720394f25..b228fca0e4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java @@ -1,6 +1,6 @@ package com.github.mikephil.charting.interfaces.datasets; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.data.PieEntry; From c0e7f56b5d816c08fd6e238c9e08c6dc1fd6142c Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Fri, 24 Jan 2020 11:35:47 +0200 Subject: [PATCH 276/291] Implement a more generic Fill class instead of GradientColor Support HorizontalBarChart too. --- .../mpchartexample/BarChartActivity.java | 22 +- .../mikephil/charting/data/BarDataSet.java | 64 ++++ .../mikephil/charting/data/BaseDataSet.java | 41 --- .../charting/data/LineRadarDataSet.java | 1 + .../interfaces/datasets/IBarDataSet.java | 7 + .../interfaces/datasets/IDataSet.java | 23 -- .../charting/model/GradientColor.java | 73 +++- .../charting/renderer/BarChartRenderer.java | 47 +-- .../renderer/HorizontalBarChartRenderer.java | 21 +- .../github/mikephil/charting/utils/Fill.java | 342 ++++++++++++++++++ 10 files changed, 514 insertions(+), 127 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/utils/Fill.java diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 4af0441ddb..89ec00a892 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -32,7 +32,7 @@ import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.model.GradientColor; +import com.github.mikephil.charting.utils.Fill; import com.github.mikephil.charting.utils.MPPointF; import com.xxmassdeveloper.mpchartexample.custom.DayAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.MyValueFormatter; @@ -164,12 +164,6 @@ private void setData(int count, float range) { set1.setDrawIcons(false); -// set1.setColors(ColorTemplate.MATERIAL_COLORS); - - /*int startColor = ContextCompat.getColor(this, android.R.color.holo_blue_dark); - int endColor = ContextCompat.getColor(this, android.R.color.holo_blue_bright); - set1.setGradientColor(startColor, endColor);*/ - int startColor1 = ContextCompat.getColor(this, android.R.color.holo_orange_light); int startColor2 = ContextCompat.getColor(this, android.R.color.holo_blue_light); int startColor3 = ContextCompat.getColor(this, android.R.color.holo_orange_light); @@ -181,14 +175,14 @@ private void setData(int count, float range) { int endColor4 = ContextCompat.getColor(this, android.R.color.holo_red_dark); int endColor5 = ContextCompat.getColor(this, android.R.color.holo_orange_dark); - List gradientColors = new ArrayList<>(); - gradientColors.add(new GradientColor(startColor1, endColor1)); - gradientColors.add(new GradientColor(startColor2, endColor2)); - gradientColors.add(new GradientColor(startColor3, endColor3)); - gradientColors.add(new GradientColor(startColor4, endColor4)); - gradientColors.add(new GradientColor(startColor5, endColor5)); + List gradientFills = new ArrayList<>(); + gradientFills.add(new Fill(startColor1, endColor1)); + gradientFills.add(new Fill(startColor2, endColor2)); + gradientFills.add(new Fill(startColor3, endColor3)); + gradientFills.add(new Fill(startColor4, endColor4)); + gradientFills.add(new Fill(startColor5, endColor5)); - set1.setGradientColors(gradientColors); + set1.setFills(gradientFills); ArrayList dataSets = new ArrayList<>(); dataSets.add(set1); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java index 7b7ee5f916..e65638805b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java @@ -4,6 +4,7 @@ import android.graphics.Color; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.Fill; import java.util.ArrayList; import java.util.List; @@ -40,6 +41,8 @@ public class BarDataSet extends BarLineScatterCandleBubbleDataSet impl */ private String[] mStackLabels = new String[]{}; + protected List mFills = null; + public BarDataSet(List yVals, String label) { super(yVals, label); @@ -69,6 +72,67 @@ protected void copy(BarDataSet barDataSet) { barDataSet.mHighLightAlpha = mHighLightAlpha; } + @Override + public List getFills() { + return mFills; + } + + @Override + public Fill getFill(int index) { + return mFills.get(index % mFills.size()); + } + + /** + * This method is deprecated. + * Use getFills() instead. + */ + @Deprecated + public List getGradients() { + return mFills; + } + + /** + * This method is deprecated. + * Use getFill(...) instead. + * + * @param index + */ + @Deprecated + public Fill getGradient(int index) { + return getFill(index); + } + + /** + * Sets the start and end color for gradient color, ONLY color that should be used for this DataSet. + * + * @param startColor + * @param endColor + */ + public void setGradientColor(int startColor, int endColor) { + mFills.clear(); + mFills.add(new Fill(startColor, endColor)); + } + + /** + * This method is deprecated. + * Use setFills(...) instead. + * + * @param gradientColors + */ + @Deprecated + public void setGradientColors(List gradientColors) { + this.mFills = gradientColors; + } + + /** + * Sets the fills for the bars in this dataset. + * + * @param fills + */ + public void setFills(List fills) { + this.mFills = fills; + } + /** * Calculates the total number of entries this DataSet represents, including * stacks. All values belonging to a stack are calculated separately. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java index 8ca3e68d42..a4279629ad 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -9,7 +9,6 @@ import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.model.GradientColor; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; @@ -29,10 +28,6 @@ public abstract class BaseDataSet implements IDataSet { */ protected List mColors = null; - protected GradientColor mGradientColor = null; - - protected List mGradientColors = null; - /** * List representing all colors that are used for drawing the actual values for this DataSet */ @@ -146,21 +141,6 @@ public int getColor(int index) { return mColors.get(index % mColors.size()); } - @Override - public GradientColor getGradientColor() { - return mGradientColor; - } - - @Override - public List getGradientColors() { - return mGradientColors; - } - - @Override - public GradientColor getGradientColor(int index) { - return mGradientColors.get(index % mGradientColors.size()); - } - /** * ###### ###### COLOR SETTING RELATED METHODS ##### ###### */ @@ -236,25 +216,6 @@ public void setColor(int color) { mColors.add(color); } - /** - * Sets the start and end color for gradient color, ONLY color that should be used for this DataSet. - * - * @param startColor - * @param endColor - */ - public void setGradientColor(int startColor, int endColor) { - mGradientColor = new GradientColor(startColor, endColor); - } - - /** - * Sets the start and end color for gradient colors, ONLY color that should be used for this DataSet. - * - * @param gradientColors - */ - public void setGradientColors(List gradientColors) { - this.mGradientColors = gradientColors; - } - /** * Sets a color with a specific alpha value. * @@ -534,8 +495,6 @@ protected void copy(BaseDataSet baseDataSet) { baseDataSet.mFormLineDashEffect = mFormLineDashEffect; baseDataSet.mFormLineWidth = mFormLineWidth; baseDataSet.mFormSize = mFormSize; - baseDataSet.mGradientColor = mGradientColor; - baseDataSet.mGradientColors = mGradientColors; baseDataSet.mHighlightEnabled = mHighlightEnabled; baseDataSet.mIconsOffset = mIconsOffset; baseDataSet.mValueColors = mValueColors; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java index 688585cbdd..b4347e4647 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java @@ -17,6 +17,7 @@ */ public abstract class LineRadarDataSet extends LineScatterCandleRadarDataSet implements ILineRadarDataSet { + // TODO: Move to using `Fill` class /** * the color that is used for filling the line surface */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java index fbdfd79531..5e82a48420 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java @@ -1,12 +1,19 @@ package com.github.mikephil.charting.interfaces.datasets; import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.utils.Fill; + +import java.util.List; /** * Created by philipp on 21/10/15. */ public interface IBarDataSet extends IBarLineScatterCandleBubbleDataSet { + List getFills(); + + Fill getFill(int index); + /** * Returns true if this DataSet is stacked (stacksize > 1) or not. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index 73a54470c8..ccd4cb4f70 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -9,7 +9,6 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.utils.MPPointF; -import com.github.mikephil.charting.model.GradientColor; import java.util.List; @@ -285,28 +284,6 @@ public interface IDataSet { */ int getColor(); - /** - * Returns the Gradient color model - * - * @return - */ - GradientColor getGradientColor(); - - /** - * Returns the Gradient colors - * - * @return - */ - List getGradientColors(); - - /** - * Returns the Gradient colors - * - * @param index - * @return - */ - GradientColor getGradientColor(int index); - /** * Returns the color at the given index of the DataSet's color array. * Performs a IndexOutOfBounds check by modulus. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/model/GradientColor.java b/MPChartLib/src/main/java/com/github/mikephil/charting/model/GradientColor.java index 1162c01198..b5c8715a08 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/model/GradientColor.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/model/GradientColor.java @@ -1,28 +1,69 @@ package com.github.mikephil.charting.model; -public class GradientColor { +import com.github.mikephil.charting.utils.Fill; - private int startColor; - private int endColor; - - public GradientColor(int startColor, int endColor) { - this.startColor = startColor; - this.endColor = endColor; +/** + * Deprecated. Use `Fill` + */ +@Deprecated +public class GradientColor extends Fill +{ + /** + * Deprecated. Use `Fill.getGradientColors()` + */ + @Deprecated + public int getStartColor() + { + return getGradientColors()[0]; } - public int getStartColor() { - return startColor; + /** + * Deprecated. Use `Fill.setGradientColors(...)` + */ + @Deprecated + public void setStartColor(int startColor) + { + if (getGradientColors() == null || getGradientColors().length != 2) + { + setGradientColors(new int[]{ + startColor, + getGradientColors() != null && getGradientColors().length > 1 + ? getGradientColors()[1] + : 0 + }); + } else + { + getGradientColors()[0] = startColor; + } } - public void setStartColor(int startColor) { - this.startColor = startColor; + /** + * Deprecated. Use `Fill.getGradientColors()` + */ + @Deprecated + public int getEndColor() + { + return getGradientColors()[1]; } - public int getEndColor() { - return endColor; + /** + * Deprecated. Use `Fill.setGradientColors(...)` + */ + @Deprecated + public void setEndColor(int endColor) + { + if (getGradientColors() == null || getGradientColors().length != 2) + { + setGradientColors(new int[]{ + getGradientColors() != null && getGradientColors().length > 0 + ? getGradientColors()[0] + : 0, + endColor + }); + } else + { + getGradientColors()[1] = endColor; + } } - public void setEndColor(int endColor) { - this.endColor = endColor; - } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index b5de65b02e..d6ce3898e1 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -15,12 +15,11 @@ 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.Fill; import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; -import android.graphics.LinearGradient; -import com.github.mikephil.charting.model.GradientColor; import java.util.List; @@ -145,13 +144,15 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { trans.pointValuesToPixel(buffer.buffer); + final boolean isCustomFill = dataSet.getFills().size() > 0; final boolean isSingleColor = dataSet.getColors().size() == 1; + final boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); if (isSingleColor) { mRenderPaint.setColor(dataSet.getColor()); } - for (int j = 0; j < buffer.size(); j += 4) { + for (int j = 0, pos = 0; j < buffer.size(); j += 4, pos++) { if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) continue; @@ -162,38 +163,24 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { if (!isSingleColor) { // Set the color for the currently drawn value. If the index // is out of bounds, reuse colors. - mRenderPaint.setColor(dataSet.getColor(j / 4)); + mRenderPaint.setColor(dataSet.getColor(pos)); } - if (dataSet.getGradientColor() != null) { - GradientColor gradientColor = dataSet.getGradientColor(); - mRenderPaint.setShader( - new LinearGradient( - buffer.buffer[j], - buffer.buffer[j + 3], - buffer.buffer[j], - buffer.buffer[j + 1], - gradientColor.getStartColor(), - gradientColor.getEndColor(), - android.graphics.Shader.TileMode.MIRROR)); + if (isCustomFill) { + dataSet.getFill(pos) + .fillRect( + c, mRenderPaint, + buffer.buffer[j], + buffer.buffer[j + 1], + buffer.buffer[j + 2], + buffer.buffer[j + 3], + isInverted ? Fill.Direction.DOWN : Fill.Direction.UP); } - - if (dataSet.getGradientColors() != null) { - mRenderPaint.setShader( - new LinearGradient( - buffer.buffer[j], - buffer.buffer[j + 3], - buffer.buffer[j], - buffer.buffer[j + 1], - dataSet.getGradientColor(j / 4).getStartColor(), - dataSet.getGradientColor(j / 4).getEndColor(), - android.graphics.Shader.TileMode.MIRROR)); + else { + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); } - - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - if (drawBorder) { c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], buffer.buffer[j + 3], mBarBorderPaint); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index b692a1f90b..f9431702ad 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -15,6 +15,7 @@ import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.Fill; import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; @@ -111,13 +112,15 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { trans.pointValuesToPixel(buffer.buffer); + final boolean isCustomFill = dataSet.getFills().size() > 0; final boolean isSingleColor = dataSet.getColors().size() == 1; + final boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); if (isSingleColor) { mRenderPaint.setColor(dataSet.getColor()); } - for (int j = 0; j < buffer.size(); j += 4) { + for (int j = 0, pos = 0; j < buffer.size(); j += 4, pos++) { if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) break; @@ -131,8 +134,20 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { mRenderPaint.setColor(dataSet.getColor(j / 4)); } - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); + if (isCustomFill) { + dataSet.getFill(pos) + .fillRect( + c, mRenderPaint, + buffer.buffer[j], + buffer.buffer[j + 1], + buffer.buffer[j + 2], + buffer.buffer[j + 3], + isInverted ? Fill.Direction.LEFT : Fill.Direction.RIGHT); + } + else { + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } if (drawBorder) { c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Fill.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Fill.java new file mode 100644 index 0000000000..d12e1fb8d7 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Fill.java @@ -0,0 +1,342 @@ +package com.github.mikephil.charting.utils; + +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class Fill +{ + public enum Type + { + EMPTY, COLOR, LINEAR_GRADIENT, DRAWABLE + } + + public enum Direction + { + DOWN, UP, RIGHT, LEFT + } + + /** + * the type of fill + */ + private Type mType = Type.EMPTY; + + /** + * the color that is used for filling + */ + @Nullable + private Integer mColor = null; + + private Integer mFinalColor = null; + + /** + * the drawable to be used for filling + */ + @Nullable + protected Drawable mDrawable; + + @Nullable + private int[] mGradientColors; + + @Nullable + private float[] mGradientPositions; + + /** + * transparency used for filling + */ + private int mAlpha = 255; + + public Fill() + { + } + + public Fill(int color) + { + this.mType = Type.COLOR; + this.mColor = color; + calculateFinalColor(); + } + + public Fill(int startColor, int endColor) + { + this.mType = Type.LINEAR_GRADIENT; + this.mGradientColors = new int[]{startColor, endColor}; + } + + public Fill(@NonNull int[] gradientColors) + { + this.mType = Type.LINEAR_GRADIENT; + this.mGradientColors = gradientColors; + } + + public Fill(@NonNull int[] gradientColors, @NonNull float[] gradientPositions) + { + this.mType = Type.LINEAR_GRADIENT; + this.mGradientColors = gradientColors; + this.mGradientPositions = gradientPositions; + } + + public Fill(@NonNull Drawable drawable) + { + this.mType = Type.DRAWABLE; + this.mDrawable = drawable; + } + + public Type getType() + { + return mType; + } + + public void setType(Type type) + { + this.mType = type; + } + + @Nullable + public Integer getColor() + { + return mColor; + } + + public void setColor(int color) + { + this.mColor = color; + calculateFinalColor(); + } + + public int[] getGradientColors() + { + return mGradientColors; + } + + public void setGradientColors(int[] colors) + { + this.mGradientColors = colors; + } + + public float[] getGradientPositions() + { + return mGradientPositions; + } + + public void setGradientPositions(float[] positions) + { + this.mGradientPositions = positions; + } + + public void setGradientColors(int startColor, int endColor) + { + this.mGradientColors = new int[]{startColor, endColor}; + } + + public int getAlpha() + { + return mAlpha; + } + + public void setAlpha(int alpha) + { + this.mAlpha = alpha; + calculateFinalColor(); + } + + private void calculateFinalColor() + { + if (mColor == null) + { + mFinalColor = null; + } else + { + int alpha = (int) Math.floor(((mColor >> 24) / 255.0) * (mAlpha / 255.0) * 255.0); + mFinalColor = (alpha << 24) | (mColor & 0xffffff); + } + } + + public void fillRect(Canvas c, Paint paint, + float left, float top, float right, float bottom, + Direction gradientDirection) + { + switch (mType) + { + case EMPTY: + return; + + case COLOR: + { + if (mFinalColor == null) return; + + if (isClipPathSupported()) + { + int save = c.save(); + + c.clipRect(left, top, right, bottom); + c.drawColor(mFinalColor); + + c.restoreToCount(save); + } + else + { + // save + Paint.Style previous = paint.getStyle(); + int previousColor = paint.getColor(); + + // set + paint.setStyle(Paint.Style.FILL); + paint.setColor(mFinalColor); + + c.drawRect(left, top, right, bottom, paint); + + // restore + paint.setColor(previousColor); + paint.setStyle(previous); + } + } + break; + + case LINEAR_GRADIENT: + { + if (mGradientColors == null) return; + + LinearGradient gradient = new LinearGradient( + (int) (gradientDirection == Direction.RIGHT + ? right + : gradientDirection == Direction.LEFT + ? left + : left), + (int) (gradientDirection == Direction.UP + ? bottom + : gradientDirection == Direction.DOWN + ? top + : top), + (int) (gradientDirection == Direction.RIGHT + ? left + : gradientDirection == Direction.LEFT + ? right + : left), + (int) (gradientDirection == Direction.UP + ? top + : gradientDirection == Direction.DOWN + ? bottom + : top), + mGradientColors, + mGradientPositions, + android.graphics.Shader.TileMode.MIRROR); + + paint.setShader(gradient); + + c.drawRect(left, top, right, bottom, paint); + } + break; + + case DRAWABLE: + { + if (mDrawable == null) return; + + mDrawable.setBounds((int) left, (int) top, (int) right, (int) bottom); + mDrawable.draw(c); + } + break; + } + } + + public void fillPath(Canvas c, Path path, Paint paint, + @Nullable RectF clipRect) + { + switch (mType) + { + case EMPTY: + return; + + case COLOR: + { + if (mFinalColor == null) return; + + if (clipRect != null && isClipPathSupported()) + { + int save = c.save(); + + c.clipPath(path); + c.drawColor(mFinalColor); + + c.restoreToCount(save); + } + else + { + // save + Paint.Style previous = paint.getStyle(); + int previousColor = paint.getColor(); + + // set + paint.setStyle(Paint.Style.FILL); + paint.setColor(mFinalColor); + + c.drawPath(path, paint); + + // restore + paint.setColor(previousColor); + paint.setStyle(previous); + } + } + break; + + case LINEAR_GRADIENT: + { + if (mGradientColors == null) return; + + LinearGradient gradient = new LinearGradient( + 0, + 0, + c.getWidth(), + c.getHeight(), + mGradientColors, + mGradientPositions, + android.graphics.Shader.TileMode.MIRROR); + + paint.setShader(gradient); + + c.drawPath(path, paint); + } + break; + + case DRAWABLE: + { + if (mDrawable == null) return; + + ensureClipPathSupported(); + + int save = c.save(); + c.clipPath(path); + + mDrawable.setBounds( + clipRect == null ? 0 : (int) clipRect.left, + clipRect == null ? 0 : (int) clipRect.top, + clipRect == null ? c.getWidth() : (int) clipRect.right, + clipRect == null ? c.getHeight() : (int) clipRect.bottom); + mDrawable.draw(c); + + c.restoreToCount(save); + } + break; + } + } + + private boolean isClipPathSupported() + { + return Utils.getSDKInt() >= 18; + } + + private void ensureClipPathSupported() + { + if (Utils.getSDKInt() < 18) + { + throw new RuntimeException("Fill-drawables not (yet) supported below API level 18, " + + "this code was run on API level " + Utils.getSDKInt() + "."); + } + } +} From 55df9ad63662f36bf8765d9b4f442bc75084ee6d Mon Sep 17 00:00:00 2001 From: Nathan Fiscaletti Date: Sat, 1 Feb 2020 14:02:27 -0600 Subject: [PATCH 277/291] Update LICENSE The LICENSE file was not properly filled out. It was missing some templates that were supposed to be filled in at the end of the license. Additionally, the entire Apache 2.0 license is not required on a project that makes use of it. Only this disclaimer is required. See http://www.apache.org/licenses/LICENSE-2.0#apply under the "How to apply the Apache License to your work" for more information. --- LICENSE | 190 +------------------------------------------------------- 1 file changed, 1 insertion(+), 189 deletions(-) diff --git a/LICENSE b/LICENSE index 8dada3edaf..ba648a557d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,192 +1,4 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} + Copyright 2020 Philipp Jahoda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From cef967fd71ff18b0908ffa21c795faefde3ff120 Mon Sep 17 00:00:00 2001 From: Anirut Teerabut Date: Fri, 7 Feb 2020 17:53:40 +0700 Subject: [PATCH 278/291] fix NPE when use solid color with barchart --- .../github/mikephil/charting/renderer/BarChartRenderer.java | 6 +++++- .../charting/renderer/HorizontalBarChartRenderer.java | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index d6ce3898e1..99a1249932 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -144,7 +144,11 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { trans.pointValuesToPixel(buffer.buffer); - final boolean isCustomFill = dataSet.getFills().size() > 0; + boolean isCustomFill = false; + if(dataSet.getFills() != null) { + isCustomFill = !dataSet.getFills().isEmpty(); + } + final boolean isSingleColor = dataSet.getColors().size() == 1; final boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index f9431702ad..6ca6ff0e8d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -112,7 +112,10 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { trans.pointValuesToPixel(buffer.buffer); - final boolean isCustomFill = dataSet.getFills().size() > 0; + boolean isCustomFill = false; + if(dataSet.getFills() != null) { + isCustomFill = !dataSet.getFills().isEmpty(); + } final boolean isSingleColor = dataSet.getColors().size() == 1; final boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); From 4b67673da908378fda8fd7449a5a19c30d45c8d2 Mon Sep 17 00:00:00 2001 From: Anirut Teerabut Date: Fri, 7 Feb 2020 22:03:20 +0700 Subject: [PATCH 279/291] Update BarChartRenderer.java --- .../github/mikephil/charting/renderer/BarChartRenderer.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index 99a1249932..ca8b8c1504 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -144,10 +144,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { trans.pointValuesToPixel(buffer.buffer); - boolean isCustomFill = false; - if(dataSet.getFills() != null) { - isCustomFill = !dataSet.getFills().isEmpty(); - } + boolean isCustomFill = dataSet.getFills() != null && !dataSet.getFills().isEmpty(); final boolean isSingleColor = dataSet.getColors().size() == 1; final boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); From 33240f9225bea0b23d8d82a786867f8446fd5dfd Mon Sep 17 00:00:00 2001 From: Anirut Teerabut Date: Fri, 7 Feb 2020 22:09:07 +0700 Subject: [PATCH 280/291] Update HorizontalBarChartRenderer.java --- .../charting/renderer/HorizontalBarChartRenderer.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index 6ca6ff0e8d..b42ef1284a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -112,10 +112,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { trans.pointValuesToPixel(buffer.buffer); - boolean isCustomFill = false; - if(dataSet.getFills() != null) { - isCustomFill = !dataSet.getFills().isEmpty(); - } + final boolean isCustomFill = dataSet.getFills() != null && !dataSet.getFills().isEmpty(); final boolean isSingleColor = dataSet.getColors().size() == 1; final boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); From 90e523042cfae25c8271b60f2cf68a947c46e018 Mon Sep 17 00:00:00 2001 From: Anirut Teerabut Date: Fri, 7 Feb 2020 22:09:57 +0700 Subject: [PATCH 281/291] Update BarChartRenderer.java --- .../github/mikephil/charting/renderer/BarChartRenderer.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index ca8b8c1504..18975557cd 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -144,8 +144,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { trans.pointValuesToPixel(buffer.buffer); - boolean isCustomFill = dataSet.getFills() != null && !dataSet.getFills().isEmpty(); - + final boolean isCustomFill = dataSet.getFills() != null && !dataSet.getFills().isEmpty(); final boolean isSingleColor = dataSet.getColors().size() == 1; final boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); From 05f658d4bd32a95a89cbd5a6de1fc40b1fba08c2 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 12 Feb 2020 11:14:20 +0100 Subject: [PATCH 282/291] Update README.md --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c4325f61e5..881f66b6bd 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,9 @@ 1. [License](#licence) 1. [Creators](#creators) -## [Coding Newsletter](https://weeklycoding.com) +## [Available for Hire](https://weeklycoding.com/about/) -Sign up for my [coding newsletter](https://weeklycoding.com) to get quick updates on Kotlin and Android development related topics. - -

    Quick Start :chart_with_upwards_trend:

    - -Add the library to your Android project, then check out the examples below! +I'm available for hire, if you are interested in working with me, just [send me an email](mailto:philjay.librarysup@gmail.com). ### Gradle Setup From f7509d54d64459fb8272f519ff124037b749df46 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 5 Mar 2020 09:23:41 +0100 Subject: [PATCH 283/291] Update README.md --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 881f66b6bd..f0580756dc 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,6 @@ 1. [License](#licence) 1. [Creators](#creators) -## [Available for Hire](https://weeklycoding.com/about/) - -I'm available for hire, if you are interested in working with me, just [send me an email](mailto:philjay.librarysup@gmail.com). - ### Gradle Setup ```gradle From 423fc679a336b84d7a74244eab50c318250306ff Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 12 May 2020 12:39:16 +0200 Subject: [PATCH 284/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0580756dc..46006a1e06 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ You can follow me on Twitter [**@PhilippJahoda**](https://twitter.com/PhilippJah

    License :page_facing_up:

    -Copyright 2019 Philipp Jahoda +Copyright 2020 Philipp Jahoda Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 8e4dccf3f8ea76a3ff4d84079054bb8cb2af6602 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 26 May 2020 07:39:12 +0200 Subject: [PATCH 285/291] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index ba648a557d..c1551a9dce 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ Copyright 2020 Philipp Jahoda Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. + you may not use this software except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 From bc0be2cfe13e32a5d8adefaa507e3512d22d85fb Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 21 Sep 2020 17:40:18 +0300 Subject: [PATCH 286/291] Revert: e5b66192 - bring back polymorphism to value formatters If anyone does not know how to add a space or change the format in anyway, please learn how to subclass. --- .gitignore | 6 +- .../mpchartexample/BarChartActivity.java | 9 +- .../BarChartActivityMultiDataset.java | 8 +- .../BarChartPositiveNegative.java | 19 ++- .../mpchartexample/CombinedChartActivity.java | 8 +- .../mpchartexample/LineChartTime.java | 8 +- .../mpchartexample/PieChartActivity.java | 2 +- .../mpchartexample/RadarChartActivity.java | 8 +- .../mpchartexample/StackedBarActivity.java | 6 +- .../StackedBarActivityNegative.java | 21 ++- .../custom/DayAxisValueFormatter.java | 7 +- .../custom/MyAxisValueFormatter.java | 21 +++ .../custom/MyCustomXAxisValueFormatter.java | 7 +- .../custom/MyValueFormatter.java | 27 +--- .../mpchartexample/custom/XYMarkerView.java | 9 +- .../custom/YearXAxisFormatter.java | 6 +- .../mikephil/charting/charts/Chart.java | 5 +- .../charting/components/AxisBase.java | 11 +- .../mikephil/charting/data/BaseDataSet.java | 8 +- .../mikephil/charting/data/ChartData.java | 5 +- .../formatter/DefaultAxisValueFormatter.java | 8 +- .../formatter/DefaultValueFormatter.java | 8 +- .../formatter/IAxisValueFormatter.java | 6 - .../charting/formatter/IValueFormatter.java | 10 +- .../formatter/IndexAxisValueFormatter.java | 12 +- .../formatter/LargeValueFormatter.java | 16 +- .../charting/formatter/PercentFormatter.java | 51 +++---- .../formatter/StackedValueFormatter.java | 24 +-- .../charting/formatter/ValueFormatter.java | 137 ------------------ .../dataprovider/ChartInterface.java | 4 +- .../interfaces/datasets/IDataSet.java | 7 +- .../charting/renderer/BarChartRenderer.java | 25 ++-- .../renderer/BubbleChartRenderer.java | 13 +- .../renderer/CandleStickChartRenderer.java | 20 +-- .../renderer/CombinedChartRenderer.java | 7 +- .../charting/renderer/DataRenderer.java | 21 ++- .../renderer/HorizontalBarChartRenderer.java | 17 ++- .../charting/renderer/LineChartRenderer.java | 13 +- .../charting/renderer/PieChartRenderer.java | 29 ++-- .../charting/renderer/RadarChartRenderer.java | 20 +-- .../renderer/ScatterChartRenderer.java | 19 ++- .../charting/renderer/XAxisRenderer.java | 3 +- .../XAxisRendererHorizontalBarChart.java | 7 +- .../renderer/XAxisRendererRadarChart.java | 4 +- .../github/mikephil/charting/utils/Utils.java | 24 +-- .../test/LargeValueFormatterTest.java | 50 +++---- .../charting/test/ObjectPoolTest.java | 2 +- 47 files changed, 340 insertions(+), 418 deletions(-) create mode 100644 MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java diff --git a/.gitignore b/.gitignore index 1f0e3083c2..feed37b27d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ bin/ gen/ generated/ +docs/ finalOutput/ projectFilesBackup/ @@ -23,8 +24,6 @@ local.properties # Eclipse project files .classpath .project -.settings/ -.vscode/ # Proguard folder generated by Eclipse proguard/ @@ -33,8 +32,7 @@ proguard/ *.iml *.ipr *.iws -/.idea/* -!/.idea/runConfigurations +.idea/ .directory diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 89ec00a892..0d83e3444a 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -1,3 +1,4 @@ + package com.xxmassdeveloper.mpchartexample; import android.Manifest; @@ -27,7 +28,7 @@ import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.IDataSet; @@ -35,7 +36,7 @@ import com.github.mikephil.charting.utils.Fill; import com.github.mikephil.charting.utils.MPPointF; import com.xxmassdeveloper.mpchartexample.custom.DayAxisValueFormatter; -import com.xxmassdeveloper.mpchartexample.custom.MyValueFormatter; +import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.XYMarkerView; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -85,7 +86,7 @@ protected void onCreate(Bundle savedInstanceState) { chart.setDrawGridBackground(false); // chart.setDrawYLabels(false); - ValueFormatter xAxisFormatter = new DayAxisValueFormatter(chart); + IAxisValueFormatter xAxisFormatter = new DayAxisValueFormatter(chart); XAxis xAxis = chart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); @@ -95,7 +96,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setLabelCount(7); xAxis.setValueFormatter(xAxisFormatter); - ValueFormatter custom = new MyValueFormatter("$"); + IAxisValueFormatter custom = new MyAxisValueFormatter(); YAxis leftAxis = chart.getAxisLeft(); leftAxis.setTypeface(tfLight); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 3369dbf6e2..075af0edbc 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -1,3 +1,4 @@ + package com.xxmassdeveloper.mpchartexample; import android.Manifest; @@ -16,6 +17,7 @@ import android.widget.TextView; import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; @@ -23,8 +25,8 @@ import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.formatter.LargeValueFormatter; -import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; @@ -98,9 +100,9 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTypeface(tfLight); xAxis.setGranularity(1f); xAxis.setCenterAxisLabels(true); - xAxis.setValueFormatter(new ValueFormatter() { + xAxis.setValueFormatter(new IAxisValueFormatter() { @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, AxisBase axis) { return String.valueOf((int) value); } }); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 8960dc770f..4fec7dd6ab 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -1,3 +1,4 @@ + package com.xxmassdeveloper.mpchartexample; import android.content.Intent; @@ -9,13 +10,17 @@ import android.view.WindowManager; import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.ViewPortHandler; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.text.DecimalFormat; @@ -83,9 +88,9 @@ protected void onCreate(Bundle savedInstanceState) { data.add(new Data(3f, -442.3f, "01-01")); data.add(new Data(4f, -2280.1f, "01-02")); - xAxis.setValueFormatter(new ValueFormatter() { + xAxis.setValueFormatter(new IAxisValueFormatter() { @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, AxisBase axis) { return data.get(Math.min(Math.max((int) value, 0), data.size()-1)).xAxisValue; } }); @@ -130,7 +135,7 @@ private void setData(List dataList) { BarData data = new BarData(set); data.setValueTextSize(13f); data.setValueTypeface(tfRegular); - data.setValueFormatter(new Formatter()); + data.setValueFormatter(new ValueFormatter()); data.setBarWidth(0.8f); chart.setData(data); @@ -154,17 +159,17 @@ private class Data { } } - private class Formatter extends ValueFormatter + private class ValueFormatter implements IValueFormatter { private final DecimalFormat mFormat; - Formatter() { + ValueFormatter() { mFormat = new DecimalFormat("######.0"); } @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { return mFormat.format(value); } } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 53dd3806bc..0308b9a891 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -1,3 +1,4 @@ + package com.xxmassdeveloper.mpchartexample; import android.content.Intent; @@ -10,6 +11,7 @@ import com.github.mikephil.charting.charts.CombinedChart; import com.github.mikephil.charting.charts.CombinedChart.DrawOrder; +import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; @@ -29,7 +31,7 @@ import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -81,9 +83,9 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setPosition(XAxisPosition.BOTH_SIDED); xAxis.setAxisMinimum(0f); xAxis.setGranularity(1f); - xAxis.setValueFormatter(new ValueFormatter() { + xAxis.setValueFormatter(new IAxisValueFormatter() { @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, AxisBase axis) { return months[(int) value % months.length]; } }); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java index e9ae3c0e43..212b90ff87 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -1,3 +1,4 @@ + package com.xxmassdeveloper.mpchartexample; import android.Manifest; @@ -15,6 +16,7 @@ import android.widget.TextView; import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; @@ -22,7 +24,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -90,12 +92,12 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTextColor(Color.rgb(255, 192, 56)); xAxis.setCenterAxisLabels(true); xAxis.setGranularity(1f); // one hour - xAxis.setValueFormatter(new ValueFormatter() { + xAxis.setValueFormatter(new IAxisValueFormatter() { private final SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm", Locale.ENGLISH); @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, AxisBase axis) { long millis = TimeUnit.HOURS.toMillis((long) value); return mFormat.format(new Date(millis)); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 4aeb1b3e9f..830025fbb1 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -160,7 +160,7 @@ private void setData(int count, float range) { //dataSet.setSelectionShift(0f); PieData data = new PieData(dataSet); - data.setValueFormatter(new PercentFormatter(chart)); + data.setValueFormatter(new PercentFormatter()); data.setValueTextSize(11f); data.setValueTextColor(Color.WHITE); data.setValueTypeface(tfLight); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java index 9fdae983d0..883eb7dfc1 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java @@ -1,3 +1,4 @@ + package com.xxmassdeveloper.mpchartexample; import android.Manifest; @@ -13,6 +14,7 @@ import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.MarkerView; import com.github.mikephil.charting.components.XAxis; @@ -20,7 +22,7 @@ import com.github.mikephil.charting.data.RadarData; import com.github.mikephil.charting.data.RadarDataSet; import com.github.mikephil.charting.data.RadarEntry; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; import com.xxmassdeveloper.mpchartexample.custom.RadarMarkerView; @@ -67,12 +69,12 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTextSize(9f); xAxis.setYOffset(0f); xAxis.setXOffset(0f); - xAxis.setValueFormatter(new ValueFormatter() { + xAxis.setValueFormatter(new IAxisValueFormatter() { private final String[] mActivities = new String[]{"Burger", "Steak", "Salad", "Pasta", "Pizza"}; @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, AxisBase axis) { return mActivities[(int) value % mActivities.length]; } }); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 1def86e8ef..676e0e62b0 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -24,11 +24,11 @@ import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.StackedValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.MyValueFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -78,7 +78,7 @@ protected void onCreate(Bundle savedInstanceState) { // change the position of the y-labels YAxis leftAxis = chart.getAxisLeft(); - leftAxis.setValueFormatter(new MyValueFormatter("K")); + leftAxis.setValueFormatter(new MyAxisValueFormatter()); leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) chart.getAxisRight().setEnabled(false); @@ -142,7 +142,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { dataSets.add(set1); BarData data = new BarData(dataSets); - data.setValueFormatter(new StackedValueFormatter(false, "", 1)); + data.setValueFormatter(new MyValueFormatter()); data.setValueTextColor(Color.WHITE); chart.setData(data); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index a4e510a20f..7af58c85ca 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -1,3 +1,4 @@ + package com.xxmassdeveloper.mpchartexample; import android.Manifest; @@ -13,6 +14,7 @@ import android.view.WindowManager; import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; @@ -21,10 +23,12 @@ import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ViewPortHandler; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.text.DecimalFormat; @@ -76,12 +80,12 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setCenterAxisLabels(true); xAxis.setLabelCount(12); xAxis.setGranularity(10f); - xAxis.setValueFormatter(new ValueFormatter() { + xAxis.setValueFormatter(new IAxisValueFormatter() { private final DecimalFormat format = new DecimalFormat("###"); @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, AxisBase axis) { return format.format(value) + "-" + format.format(value + 10); } }); @@ -238,7 +242,7 @@ public void onNothingSelected() { Log.i("NOTING SELECTED", ""); } - private class CustomFormatter extends ValueFormatter { + private class CustomFormatter implements IValueFormatter, IAxisValueFormatter { private final DecimalFormat mFormat; @@ -246,8 +250,15 @@ private class CustomFormatter extends ValueFormatter { mFormat = new DecimalFormat("###"); } + // data + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return mFormat.format(Math.abs(value)) + "m"; + } + + // YAxis @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, AxisBase axis) { return mFormat.format(Math.abs(value)) + "m"; } } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java index 1fba5cc98e..ba4d860d92 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -1,12 +1,13 @@ package com.xxmassdeveloper.mpchartexample.custom; import com.github.mikephil.charting.charts.BarLineChartBase; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; /** * Created by philipp on 02/06/16. */ -public class DayAxisValueFormatter extends ValueFormatter +public class DayAxisValueFormatter implements IAxisValueFormatter { private final String[] mMonths = new String[]{ @@ -20,7 +21,7 @@ public DayAxisValueFormatter(BarLineChartBase chart) { } @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, AxisBase axis) { int days = (int) value; diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java new file mode 100644 index 0000000000..e7cdbfcd10 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java @@ -0,0 +1,21 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; + +import java.text.DecimalFormat; + +public class MyAxisValueFormatter implements IAxisValueFormatter +{ + + private final DecimalFormat mFormat; + + public MyAxisValueFormatter() { + mFormat = new DecimalFormat("###,###,###,##0.0"); + } + + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mFormat.format(value) + " $"; + } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index 2cf2eab7ba..bea4908ef2 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -1,6 +1,7 @@ package com.xxmassdeveloper.mpchartexample.custom; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.utils.ViewPortHandler; import java.text.DecimalFormat; @@ -11,7 +12,7 @@ * @deprecated The {@link MyAxisValueFormatter} does exactly the same thing and is more functional. */ @Deprecated -public class MyCustomXAxisValueFormatter extends ValueFormatter +public class MyCustomXAxisValueFormatter implements IAxisValueFormatter { private final DecimalFormat mFormat; @@ -24,7 +25,7 @@ public MyCustomXAxisValueFormatter(ViewPortHandler viewPortHandler) { } @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, AxisBase axis) { //Log.i("TRANS", "x: " + viewPortHandler.getTransX() + ", y: " + viewPortHandler.getTransY()); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java index 0b0bf2f2ab..ec1c119818 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java @@ -1,35 +1,22 @@ package com.xxmassdeveloper.mpchartexample.custom; -import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.ViewPortHandler; import java.text.DecimalFormat; -public class MyValueFormatter extends ValueFormatter +public class MyValueFormatter implements IValueFormatter { private final DecimalFormat mFormat; - private String suffix; - public MyValueFormatter(String suffix) { + public MyValueFormatter() { mFormat = new DecimalFormat("###,###,###,##0.0"); - this.suffix = suffix; } @Override - public String getFormattedValue(float value) { - return mFormat.format(value) + suffix; - } - - @Override - public String getAxisLabel(float value, AxisBase axis) { - if (axis instanceof XAxis) { - return mFormat.format(value); - } else if (value > 0) { - return mFormat.format(value) + suffix; - } else { - return mFormat.format(value); - } + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return mFormat.format(value) + " $"; } } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java index ed9dcb8a23..51e4247d35 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java @@ -1,3 +1,4 @@ + package com.xxmassdeveloper.mpchartexample.custom; import android.annotation.SuppressLint; @@ -6,7 +7,7 @@ import com.github.mikephil.charting.components.MarkerView; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.utils.MPPointF; import com.xxmassdeveloper.mpchartexample.R; @@ -22,11 +23,11 @@ public class XYMarkerView extends MarkerView { private final TextView tvContent; - private final ValueFormatter xAxisValueFormatter; + private final IAxisValueFormatter xAxisValueFormatter; private final DecimalFormat format; - public XYMarkerView(Context context, ValueFormatter xAxisValueFormatter) { + public XYMarkerView(Context context, IAxisValueFormatter xAxisValueFormatter) { super(context, R.layout.custom_marker_view); this.xAxisValueFormatter = xAxisValueFormatter; @@ -39,7 +40,7 @@ public XYMarkerView(Context context, ValueFormatter xAxisValueFormatter) { @Override public void refreshContent(Entry e, Highlight highlight) { - tvContent.setText(String.format("x: %s, y: %s", xAxisValueFormatter.getFormattedValue(e.getX()), format.format(e.getY()))); + tvContent.setText(String.format("x: %s, y: %s", xAxisValueFormatter.getFormattedValue(e.getX(), null), format.format(e.getY()))); super.refreshContent(e, highlight); } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java index d45853f8d4..7122e0d80c 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java @@ -1,13 +1,13 @@ package com.xxmassdeveloper.mpchartexample.custom; import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; /** * Created by Philipp Jahoda on 14/09/15. */ @SuppressWarnings("unused") -public class YearXAxisFormatter extends ValueFormatter +public class YearXAxisFormatter implements IAxisValueFormatter { private final String[] mMonths = new String[]{ @@ -19,7 +19,7 @@ public YearXAxisFormatter() { } @Override - public String getAxisLabel(float value, AxisBase axis) { + public String getFormattedValue(float value, AxisBase axis) { float percent = value / axis.mAxisRange; return mMonths[(int) (mMonths.length * percent)]; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index b104935d30..5cf49ea9d1 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -1,3 +1,4 @@ + package com.github.mikephil.charting.charts; import android.animation.ValueAnimator; @@ -34,7 +35,7 @@ import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.formatter.DefaultValueFormatter; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.ChartHighlighter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.highlight.IHighlighter; @@ -1021,7 +1022,7 @@ public XAxis getXAxis() { * * @return */ - public ValueFormatter getDefaultValueFormatter() { + public IValueFormatter getDefaultValueFormatter() { return mDefaultValueFormatter; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index 96f706a3a1..c90b4fc9b9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -1,3 +1,4 @@ + package com.github.mikephil.charting.components; import android.graphics.Color; @@ -5,7 +6,7 @@ import android.util.Log; import com.github.mikephil.charting.formatter.DefaultAxisValueFormatter; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; @@ -21,7 +22,7 @@ public abstract class AxisBase extends ComponentBase { /** * custom formatter that is used instead of the auto-formatter if set */ - protected ValueFormatter mAxisValueFormatter; + protected IAxisValueFormatter mAxisValueFormatter; private int mGridColor = Color.GRAY; @@ -518,7 +519,7 @@ public String getFormattedLabel(int index) { if (index < 0 || index >= mEntries.length) return ""; else - return getValueFormatter().getAxisLabel(mEntries[index], this); + return getValueFormatter().getFormattedValue(mEntries[index], this); } /** @@ -530,7 +531,7 @@ public String getFormattedLabel(int index) { * * @param f */ - public void setValueFormatter(ValueFormatter f) { + public void setValueFormatter(IAxisValueFormatter f) { if (f == null) mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals); @@ -543,7 +544,7 @@ public void setValueFormatter(ValueFormatter f) { * * @return */ - public ValueFormatter getValueFormatter() { + public IAxisValueFormatter getValueFormatter() { if (mAxisValueFormatter == null || (mAxisValueFormatter instanceof DefaultAxisValueFormatter && diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java index a4279629ad..7e7445cac7 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -7,7 +7,7 @@ import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.MPPointF; @@ -51,7 +51,7 @@ public abstract class BaseDataSet implements IDataSet { /** * custom formatter that is used instead of the auto-formatter if set */ - protected transient ValueFormatter mValueFormatter; + protected transient IValueFormatter mValueFormatter; /** * the typeface used for the value text @@ -274,7 +274,7 @@ public boolean isHighlightEnabled() { } @Override - public void setValueFormatter(ValueFormatter f) { + public void setValueFormatter(IValueFormatter f) { if (f == null) return; @@ -283,7 +283,7 @@ public void setValueFormatter(ValueFormatter f) { } @Override - public ValueFormatter getValueFormatter() { + public IValueFormatter getValueFormatter() { if (needsFormatter()) return Utils.getDefaultValueFormatter(); return mValueFormatter; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java index 95d439a71d..bfc5ed7016 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java @@ -1,10 +1,11 @@ + package com.github.mikephil.charting.data; import android.graphics.Typeface; import android.util.Log; import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IDataSet; @@ -658,7 +659,7 @@ public T getFirstRight(List sets) { * * @param f */ - public void setValueFormatter(ValueFormatter f) { + public void setValueFormatter(IValueFormatter f) { if (f == null) return; else { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java index c8834c3e45..552c150e69 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java @@ -1,11 +1,13 @@ package com.github.mikephil.charting.formatter; +import com.github.mikephil.charting.components.AxisBase; + import java.text.DecimalFormat; /** * Created by philipp on 02/06/16. */ -public class DefaultAxisValueFormatter extends ValueFormatter +public class DefaultAxisValueFormatter implements IAxisValueFormatter { /** @@ -16,7 +18,7 @@ public class DefaultAxisValueFormatter extends ValueFormatter /** * the number of decimal digits this formatter uses */ - protected int digits; + protected int digits = 0; /** * Constructor that specifies to how many digits the value should be @@ -38,7 +40,7 @@ public DefaultAxisValueFormatter(int digits) { } @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, AxisBase axis) { // avoid memory allocations here (for performance) return mFormat.format(value); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java index 40668b91ab..e2fea4b079 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java @@ -1,5 +1,9 @@ + package com.github.mikephil.charting.formatter; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + import java.text.DecimalFormat; /** @@ -8,7 +12,7 @@ * * @author Philipp Jahoda */ -public class DefaultValueFormatter extends ValueFormatter +public class DefaultValueFormatter implements IValueFormatter { /** @@ -48,7 +52,7 @@ public void setup(int digits) { } @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { // put more logic here ... // avoid memory allocations here (for performance reasons) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java index 970ea6fca8..51939b5432 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java @@ -6,10 +6,7 @@ * Created by Philipp Jahoda on 20/09/15. * Custom formatter interface that allows formatting of * axis labels before they are being drawn. - * - * @deprecated Extend {@link ValueFormatter} instead */ -@Deprecated public interface IAxisValueFormatter { @@ -21,9 +18,6 @@ public interface IAxisValueFormatter * @param value the value to be formatted * @param axis the axis the value belongs to * @return - * - * @deprecated Extend {@link ValueFormatter} and use {@link ValueFormatter#getAxisLabel(float, AxisBase)} */ - @Deprecated String getFormattedValue(float value, AxisBase axis); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java index 0dde7012e3..75d2363f26 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java @@ -4,12 +4,13 @@ import com.github.mikephil.charting.utils.ViewPortHandler; /** - * Interface to format all values before they are drawn as labels. + * Interface that allows custom formatting of all values inside the chart before they are + * being drawn to the screen. Simply create your own formatting class and let + * it implement IValueFormatter. Then override the getFormattedValue(...) method + * and return whatever you want. * * @author Philipp Jahoda - * @deprecated Extend {@link ValueFormatter} instead */ -@Deprecated public interface IValueFormatter { @@ -23,9 +24,6 @@ public interface IValueFormatter * @param dataSetIndex the index of the DataSet the entry in focus belongs to * @param viewPortHandler provides information about the current chart state (scale, translation, ...) * @return the formatted label ready for being drawn - * - * @deprecated Extend {@link ValueFormatter} and override an appropriate method */ - @Deprecated String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java index 7ab7bdbe7d..07349a6a0e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java @@ -1,11 +1,18 @@ + package com.github.mikephil.charting.formatter; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; +import java.util.Arrays; import java.util.Collection; /** * This formatter is used for passing an array of x-axis labels, on whole x steps. */ -public class IndexAxisValueFormatter extends ValueFormatter +public class IndexAxisValueFormatter implements IAxisValueFormatter { private String[] mValues = new String[] {}; private int mValueCount = 0; @@ -37,8 +44,7 @@ public IndexAxisValueFormatter(Collection values) { setValues(values.toArray(new String[values.size()])); } - @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, AxisBase axis) { int index = Math.round(value); if (index < 0 || index >= mValueCount || index != (int)value) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java index 4870a4cff4..211401ad8a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java @@ -1,5 +1,10 @@ + package com.github.mikephil.charting.formatter; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + import java.text.DecimalFormat; /** @@ -12,7 +17,7 @@ * @author Philipp Jahoda * @author Oleksandr Tyshkovets */ -public class LargeValueFormatter extends ValueFormatter +public class LargeValueFormatter implements IValueFormatter, IAxisValueFormatter { private String[] mSuffix = new String[]{ @@ -36,8 +41,15 @@ public LargeValueFormatter(String appendix) { mText = appendix; } + // IValueFormatter + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return makePretty(value) + mText; + } + + // IAxisValueFormatter @Override - public String getFormattedValue(float value) { + public String getFormattedValue(float value, AxisBase axis) { return makePretty(value) + mText; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java index 012fab77f9..de8a10255a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java @@ -1,54 +1,49 @@ + package com.github.mikephil.charting.formatter; -import com.github.mikephil.charting.charts.PieChart; -import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; import java.text.DecimalFormat; /** * This IValueFormatter is just for convenience and simply puts a "%" sign after - * each value. (Recommended for PieChart) + * each value. (Recommeded for PieChart) * * @author Philipp Jahoda */ -public class PercentFormatter extends ValueFormatter +public class PercentFormatter implements IValueFormatter, IAxisValueFormatter { - public DecimalFormat mFormat; - private PieChart pieChart; - private boolean percentSignSeparated; + protected DecimalFormat mFormat; public PercentFormatter() { mFormat = new DecimalFormat("###,###,##0.0"); - percentSignSeparated = true; - } - - // Can be used to remove percent signs if the chart isn't in percent mode - public PercentFormatter(PieChart pieChart) { - this(); - this.pieChart = pieChart; } - // Can be used to remove percent signs if the chart isn't in percent mode - public PercentFormatter(PieChart pieChart, boolean percentSignSeparated) { - this(pieChart); - this.percentSignSeparated = percentSignSeparated; + /** + * Allow a custom decimalformat + * + * @param format + */ + public PercentFormatter(DecimalFormat format) { + this.mFormat = format; } + // IValueFormatter @Override - public String getFormattedValue(float value) { - return mFormat.format(value) + (percentSignSeparated ? " %" : "%"); + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return mFormat.format(value) + " %"; } + // IAxisValueFormatter @Override - public String getPieLabel(float value, PieEntry pieEntry) { - if (pieChart != null && pieChart.isUsePercentValuesEnabled()) { - // Converted to percent - return getFormattedValue(value); - } else { - // raw value, skip percent sign - return mFormat.format(value); - } + public String getFormattedValue(float value, AxisBase axis) { + return mFormat.format(value) + " %"; } + public int getDecimalDigits() { + return 1; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java index 7c69dcf5d6..0e8351634f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java @@ -1,6 +1,8 @@ package com.github.mikephil.charting.formatter; import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; import java.text.DecimalFormat; @@ -10,7 +12,7 @@ * A formatter specifically for stacked BarChart that allows to specify whether the all stack values * or just the top value should be drawn. */ -public class StackedValueFormatter extends ValueFormatter +public class StackedValueFormatter implements IValueFormatter { /** @@ -21,7 +23,7 @@ public class StackedValueFormatter extends ValueFormatter /** * a string that should be appended behind the value */ - private String mSuffix; + private String mAppendix; private DecimalFormat mFormat; @@ -29,12 +31,12 @@ public class StackedValueFormatter extends ValueFormatter * Constructor. * * @param drawWholeStack if true, all stack values of the stacked bar entry are drawn, else only top - * @param suffix a string that should be appended behind the value + * @param appendix a string that should be appended behind the value * @param decimals the number of decimal digits to use */ - public StackedValueFormatter(boolean drawWholeStack, String suffix, int decimals) { + public StackedValueFormatter(boolean drawWholeStack, String appendix, int decimals) { this.mDrawWholeStack = drawWholeStack; - this.mSuffix = suffix; + this.mAppendix = appendix; StringBuffer b = new StringBuffer(); for (int i = 0; i < decimals; i++) { @@ -47,10 +49,12 @@ public StackedValueFormatter(boolean drawWholeStack, String suffix, int decimals } @Override - public String getBarStackedLabel(float value, BarEntry entry) { - if (!mDrawWholeStack) { + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - float[] vals = entry.getYVals(); + if (!mDrawWholeStack && entry instanceof BarEntry) { + + BarEntry barEntry = (BarEntry) entry; + float[] vals = barEntry.getYVals(); if (vals != null) { @@ -58,7 +62,7 @@ public String getBarStackedLabel(float value, BarEntry entry) { if (vals[vals.length - 1] == value) { // return the "sum" across all stack values - return mFormat.format(entry.getY()) + mSuffix; + return mFormat.format(barEntry.getY()) + mAppendix; } else { return ""; // return empty } @@ -66,6 +70,6 @@ public String getBarStackedLabel(float value, BarEntry entry) { } // return the "proposed" value - return mFormat.format(value) + mSuffix; + return mFormat.format(value) + mAppendix; } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java deleted file mode 100644 index d2f53cb78b..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.github.mikephil.charting.formatter; - -import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.BubbleEntry; -import com.github.mikephil.charting.data.CandleEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.PieEntry; -import com.github.mikephil.charting.data.RadarEntry; -import com.github.mikephil.charting.utils.ViewPortHandler; - -/** - * Class to format all values before they are drawn as labels. - */ -public abstract class ValueFormatter implements IAxisValueFormatter, IValueFormatter{ - - /** - * DO NOT USE, only for backwards compatibility and will be removed in future versions. - * - * @param value the value to be formatted - * @param axis the axis the value belongs to - * @return formatted string label - */ - @Override - @Deprecated - public String getFormattedValue(float value, AxisBase axis) { - return getFormattedValue(value); - } - - /** - * DO NOT USE, only for backwards compatibility and will be removed in future versions. - * @param value the value to be formatted - * @param entry the entry the value belongs to - in e.g. BarChart, this is of class BarEntry - * @param dataSetIndex the index of the DataSet the entry in focus belongs to - * @param viewPortHandler provides information about the current chart state (scale, translation, ...) - * @return formatted string label - */ - @Override - @Deprecated - public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return getFormattedValue(value); - } - - /** - * Called when drawing any label, used to change numbers into formatted strings. - * - * @param value float to be formatted - * @return formatted string label - */ - public String getFormattedValue(float value) { - return String.valueOf(value); - } - - /** - * Used to draw axis labels, calls {@link #getFormattedValue(float)} by default. - * - * @param value float to be formatted - * @param axis axis being labeled - * @return formatted string label - */ - public String getAxisLabel(float value, AxisBase axis) { - return getFormattedValue(value); - } - - /** - * Used to draw bar labels, calls {@link #getFormattedValue(float)} by default. - * - * @param barEntry bar being labeled - * @return formatted string label - */ - public String getBarLabel(BarEntry barEntry) { - return getFormattedValue(barEntry.getY()); - } - - /** - * Used to draw stacked bar labels, calls {@link #getFormattedValue(float)} by default. - * - * @param value current value to be formatted - * @param stackedEntry stacked entry being labeled, contains all Y values - * @return formatted string label - */ - public String getBarStackedLabel(float value, BarEntry stackedEntry) { - return getFormattedValue(value); - } - - /** - * Used to draw line and scatter labels, calls {@link #getFormattedValue(float)} by default. - * - * @param entry point being labeled, contains X value - * @return formatted string label - */ - public String getPointLabel(Entry entry) { - return getFormattedValue(entry.getY()); - } - - /** - * Used to draw pie value labels, calls {@link #getFormattedValue(float)} by default. - * - * @param value float to be formatted, may have been converted to percentage - * @param pieEntry slice being labeled, contains original, non-percentage Y value - * @return formatted string label - */ - public String getPieLabel(float value, PieEntry pieEntry) { - return getFormattedValue(value); - } - - /** - * Used to draw radar value labels, calls {@link #getFormattedValue(float)} by default. - * - * @param radarEntry entry being labeled - * @return formatted string label - */ - public String getRadarLabel(RadarEntry radarEntry) { - return getFormattedValue(radarEntry.getY()); - } - - /** - * Used to draw bubble size labels, calls {@link #getFormattedValue(float)} by default. - * - * @param bubbleEntry bubble being labeled, also contains X and Y values - * @return formatted string label - */ - public String getBubbleLabel(BubbleEntry bubbleEntry) { - return getFormattedValue(bubbleEntry.getSize()); - } - - /** - * Used to draw high labels, calls {@link #getFormattedValue(float)} by default. - * - * @param candleEntry candlestick being labeled - * @return formatted string label - */ - public String getCandleLabel(CandleEntry candleEntry) { - return getFormattedValue(candleEntry.getHigh()); - } - -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java index 182aa28757..219b46bd82 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java @@ -3,7 +3,7 @@ import android.graphics.RectF; import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.utils.MPPointF; /** @@ -61,7 +61,7 @@ public interface ChartInterface { RectF getContentRect(); - ValueFormatter getDefaultValueFormatter(); + IValueFormatter getDefaultValueFormatter(); ChartData getData(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index ccd4cb4f70..fd8af7064b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -1,13 +1,14 @@ package com.github.mikephil.charting.interfaces.datasets; import android.graphics.DashPathEffect; +import android.graphics.PointF; import android.graphics.Typeface; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.utils.MPPointF; import java.util.List; @@ -317,14 +318,14 @@ public interface IDataSet { * * @param f */ - void setValueFormatter(ValueFormatter f); + void setValueFormatter(IValueFormatter f); /** * Returns the formatter used for drawing the values inside the chart. * * @return */ - ValueFormatter getValueFormatter(); + IValueFormatter getValueFormatter(); /** * Returns true if the valueFormatter object of this DataSet is null. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index 18975557cd..1656a3a37f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -1,3 +1,4 @@ + package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -10,7 +11,6 @@ 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.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.highlight.Range; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; @@ -241,8 +241,6 @@ public void drawValues(Canvas c) { final float phaseY = mAnimator.getPhaseY(); - ValueFormatter formatter = dataSet.getValueFormatter(); - MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); @@ -265,7 +263,8 @@ public void drawValues(Canvas c) { float val = entry.getY(); if (dataSet.isDrawValuesEnabled()) { - drawValue(c, formatter.getBarLabel(entry), x, val >= 0 ? + drawValue(c, dataSet.getValueFormatter(), val, entry, i, x, + val >= 0 ? (buffer.buffer[j + 1] + posOffset) : (buffer.buffer[j + 3] + negOffset), dataSet.getValueTextColor(j / 4)); @@ -323,7 +322,8 @@ public void drawValues(Canvas c) { continue; if (dataSet.isDrawValuesEnabled()) { - drawValue(c, formatter.getBarLabel(entry), x, buffer.buffer[bufferIndex + 1] + + drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, + buffer.buffer[bufferIndex + 1] + (entry.getY() >= 0 ? posOffset : negOffset), color); } @@ -394,7 +394,14 @@ public void drawValues(Canvas c) { continue; if (dataSet.isDrawValuesEnabled()) { - drawValue(c, formatter.getBarStackedLabel(val, entry), x, y, color); + drawValue(c, + dataSet.getValueFormatter(), + vals[k / 2], + entry, + i, + x, + y, + color); } if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { @@ -422,12 +429,6 @@ public void drawValues(Canvas c) { } } - @Override - public void drawValue(Canvas c, String valueText, float x, float y, int color) { - mValuePaint.setColor(color); - c.drawText(valueText, x, y, mValuePaint); - } - @Override public void drawHighlighted(Canvas c, Highlight[] indices) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index be141c46a0..5ce19c2c9f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -1,3 +1,4 @@ + package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -8,7 +9,6 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.data.BubbleData; import com.github.mikephil.charting.data.BubbleEntry; -import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; @@ -150,8 +150,6 @@ public void drawValues(Canvas c) { final float alpha = phaseX == 1 ? phaseY : phaseX; - ValueFormatter formatter = dataSet.getValueFormatter(); - MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); @@ -174,7 +172,8 @@ public void drawValues(Canvas c) { BubbleEntry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); if (dataSet.isDrawValuesEnabled()) { - drawValue(c, formatter.getBubbleLabel(entry), x, y + (0.5f * lineHeight), valueTextColor); + drawValue(c, dataSet.getValueFormatter(), entry.getSize(), entry, i, x, + y + (0.5f * lineHeight), valueTextColor); } if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { @@ -196,12 +195,6 @@ public void drawValues(Canvas c) { } } - @Override - public void drawValue(Canvas c, String valueText, float x, float y, int color) { - mValuePaint.setColor(color); - c.drawText(valueText, x, y, mValuePaint); - } - @Override public void drawExtras(Canvas c) { } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index 027dda31d8..991b702117 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -1,3 +1,4 @@ + package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -7,7 +8,6 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.data.CandleData; import com.github.mikephil.charting.data.CandleEntry; -import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; @@ -279,8 +279,6 @@ public void drawValues(Canvas c) { float yOffset = Utils.convertDpToPixel(5f); - ValueFormatter formatter = dataSet.getValueFormatter(); - MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); @@ -299,7 +297,15 @@ public void drawValues(Canvas c) { CandleEntry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); if (dataSet.isDrawValuesEnabled()) { - drawValue(c, formatter.getCandleLabel(entry), x, y - yOffset, dataSet.getValueTextColor(j / 2)); + drawValue(c, + dataSet.getValueFormatter(), + entry.getHigh(), + entry, + i, + x, + y - yOffset, + dataSet + .getValueTextColor(j / 2)); } if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { @@ -321,12 +327,6 @@ public void drawValues(Canvas c) { } } - @Override - public void drawValue(Canvas c, String valueText, float x, float y, int color) { - mValuePaint.setColor(color); - c.drawText(valueText, x, y, mValuePaint); - } - @Override public void drawExtras(Canvas c) { } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java index 8f6be3c054..6d0d4d3da0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java @@ -1,7 +1,6 @@ package com.github.mikephil.charting.renderer; import android.graphics.Canvas; -import android.util.Log; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.charts.Chart; @@ -10,6 +9,7 @@ import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.CombinedData; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; import com.github.mikephil.charting.utils.ViewPortHandler; import java.lang.ref.WeakReference; @@ -89,11 +89,6 @@ public void drawData(Canvas c) { renderer.drawData(c); } - @Override - public void drawValue(Canvas c, String valueText, float x, float y, int color) { - Log.e("MPAndroidChart", "Erroneous call to drawValue() in CombinedChartRenderer!"); - } - @Override public void drawValues(Canvas c) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java index da4a26edca..e8e5446f4d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java @@ -1,3 +1,4 @@ + package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -5,11 +6,15 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Paint.Style; +import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -133,13 +138,19 @@ protected void applyValueTextStyle(IDataSet set) { /** * Draws the value of the given entry by using the provided IValueFormatter. * - * @param c canvas - * @param valueText label to draw - * @param x position - * @param y position + * @param c canvas + * @param formatter formatter for custom value-formatting + * @param value the value to be drawn + * @param entry the entry the value belongs to + * @param dataSetIndex the index of the DataSet the drawn Entry belongs to + * @param x position + * @param y position * @param color */ - public abstract void drawValue(Canvas c, String valueText, float x, float y, int color); + public void drawValue(Canvas c, IValueFormatter formatter, float value, Entry entry, int dataSetIndex, float x, float y, int color) { + mValuePaint.setColor(color); + c.drawText(formatter.getFormattedValue(value, entry, dataSetIndex, mViewPortHandler), x, y, mValuePaint); + } /** * Draws any kind of additional information (e.g. line-circles). diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index b42ef1284a..0cd72345fb 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -1,3 +1,4 @@ + package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -10,7 +11,7 @@ import com.github.mikephil.charting.buffer.HorizontalBarBuffer; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; @@ -181,7 +182,7 @@ public void drawValues(Canvas c) { applyValueTextStyle(dataSet); final float halfTextHeight = Utils.calcTextHeight(mValuePaint, "10") / 2f; - ValueFormatter formatter = dataSet.getValueFormatter(); + IValueFormatter formatter = dataSet.getValueFormatter(); // get the buffer BarBuffer buffer = mBarBuffers[i]; @@ -210,7 +211,7 @@ public void drawValues(Canvas c) { BarEntry entry = dataSet.getEntryForIndex(j / 4); float val = entry.getY(); - String formattedValue = formatter.getBarLabel(entry); + String formattedValue = formatter.getFormattedValue(val, entry, i, mViewPortHandler); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); @@ -280,7 +281,9 @@ public void drawValues(Canvas c) { if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[bufferIndex + 1])) continue; - String formattedValue = formatter.getBarLabel(entry); + float val = entry.getY(); + String formattedValue = formatter.getFormattedValue(val, + entry, i, mViewPortHandler); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); @@ -350,7 +353,8 @@ public void drawValues(Canvas c) { for (int k = 0; k < transformed.length; k += 2) { final float val = vals[k / 2]; - String formattedValue = formatter.getBarStackedLabel(val, entry); + String formattedValue = formatter.getFormattedValue(val, + entry, i, mViewPortHandler); // calculate the correct offset depending on the draw position of the value float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); @@ -408,8 +412,7 @@ public void drawValues(Canvas c) { } } - @Override - public void drawValue(Canvas c, String valueText, float x, float y, int color) { + protected void drawValue(Canvas c, String valueText, float x, float y, int color) { mValuePaint.setColor(color); c.drawText(valueText, x, y, mValuePaint); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java index a00860b5a6..a86c16f76b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -12,7 +12,6 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; import com.github.mikephil.charting.interfaces.datasets.IDataSet; @@ -295,7 +294,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { int entryCount = dataSet.getEntryCount(); - final boolean isDrawSteppedEnabled = dataSet.getMode() == LineDataSet.Mode.STEPPED; + final boolean isDrawSteppedEnabled = dataSet.isDrawSteppedEnabled(); final int pointsPerEntryPair = isDrawSteppedEnabled ? 4 : 2; Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); @@ -562,7 +561,6 @@ public void drawValues(Canvas c) { float[] positions = trans.generateTransformedValuesLine(dataSet, mAnimator.getPhaseX(), mAnimator .getPhaseY(), mXBounds.min, mXBounds.max); - ValueFormatter formatter = dataSet.getValueFormatter(); MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); @@ -582,7 +580,8 @@ public void drawValues(Canvas c) { Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); if (dataSet.isDrawValuesEnabled()) { - drawValue(c, formatter.getPointLabel(entry), x, y - valOffset, dataSet.getValueTextColor(j / 2)); + drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, + y - valOffset, dataSet.getValueTextColor(j / 2)); } if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { @@ -604,12 +603,6 @@ public void drawValues(Canvas c) { } } - @Override - public void drawValue(Canvas c, String valueText, float x, float y, int color) { - mValuePaint.setColor(color); - c.drawText(valueText, x, y, mValuePaint); - } - @Override public void drawExtras(Canvas c) { drawCircles(c); 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 f427ffe5d3..f35c775d45 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -1,3 +1,4 @@ + package com.github.mikephil.charting.renderer; import android.graphics.Bitmap; @@ -21,7 +22,7 @@ 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.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -464,7 +465,7 @@ public void drawValues(Canvas c) { float lineHeight = Utils.calcTextHeight(mValuePaint, "Q") + Utils.convertDpToPixel(4f); - ValueFormatter formatter = dataSet.getValueFormatter(); + IValueFormatter formatter = dataSet.getValueFormatter(); int entryCount = dataSet.getEntryCount(); @@ -500,7 +501,6 @@ public void drawValues(Canvas c) { float value = mChart.isUsePercentValuesEnabled() ? entry.getY() / yValueSum * 100f : entry.getY(); - String formattedValue = formatter.getPieLabel(value, entry); String entryLabel = entry.getLabel(); final float sliceXBase = (float) Math.cos(transformedAngle * Utils.FDEG2RAD); @@ -583,7 +583,14 @@ else if (valueLineColor != ColorTemplate.COLOR_NONE) // draw everything, depending on settings if (drawXOutside && drawYOutside) { - drawValue(c, formattedValue, labelPtx, labelPty, dataSet.getValueTextColor(j)); + drawValue(c, + formatter, + value, + entry, + 0, + labelPtx, + labelPty, + dataSet.getValueTextColor(j)); if (j < data.getEntryCount() && entryLabel != null) { drawEntryLabel(c, entryLabel, labelPtx, labelPty + lineHeight); @@ -595,7 +602,8 @@ else if (valueLineColor != ColorTemplate.COLOR_NONE) } } else if (drawYOutside) { - drawValue(c, formattedValue, labelPtx, labelPty + lineHeight / 2.f, dataSet.getValueTextColor(j)); + drawValue(c, formatter, value, entry, 0, labelPtx, labelPty + lineHeight / 2.f, dataSet + .getValueTextColor(j)); } } @@ -609,7 +617,7 @@ else if (valueLineColor != ColorTemplate.COLOR_NONE) // draw everything, depending on settings if (drawXInside && drawYInside) { - drawValue(c, formattedValue, x, y, dataSet.getValueTextColor(j)); + drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j)); if (j < data.getEntryCount() && entryLabel != null) { drawEntryLabel(c, entryLabel, x, y + lineHeight); @@ -620,7 +628,8 @@ else if (valueLineColor != ColorTemplate.COLOR_NONE) drawEntryLabel(c, entryLabel, x, y + lineHeight / 2f); } } else if (drawYInside) { - drawValue(c, formattedValue, x, y + lineHeight / 2f, dataSet.getValueTextColor(j)); + + drawValue(c, formatter, value, entry, 0, x, y + lineHeight / 2f, dataSet.getValueTextColor(j)); } } @@ -650,12 +659,6 @@ else if (valueLineColor != ColorTemplate.COLOR_NONE) c.restore(); } - @Override - public void drawValue(Canvas c, String valueText, float x, float y, int color) { - mValuePaint.setColor(color); - c.drawText(valueText, x, y, mValuePaint); - } - /** * Draws an entry label at the specified position. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java index 3f932f8725..dbf0e8f807 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java @@ -1,3 +1,4 @@ + package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -10,7 +11,6 @@ import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.data.RadarData; import com.github.mikephil.charting.data.RadarEntry; -import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -174,8 +174,6 @@ public void drawValues(Canvas c) { // apply the text-styling defined by the DataSet applyValueTextStyle(dataSet); - ValueFormatter formatter = dataSet.getValueFormatter(); - MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); @@ -191,7 +189,15 @@ public void drawValues(Canvas c) { pOut); if (dataSet.isDrawValuesEnabled()) { - drawValue(c, formatter.getRadarLabel(entry), pOut.x, pOut.y - yoffset, dataSet.getValueTextColor(j)); + drawValue(c, + dataSet.getValueFormatter(), + entry.getY(), + entry, + i, + pOut.x, + pOut.y - yoffset, + dataSet.getValueTextColor + (j)); } if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { @@ -225,12 +231,6 @@ public void drawValues(Canvas c) { MPPointF.recycleInstance(pIcon); } - @Override - public void drawValue(Canvas c, String valueText, float x, float y, int color) { - mValuePaint.setColor(color); - c.drawText(valueText, x, y, mValuePaint); - } - @Override public void drawExtras(Canvas c) { drawWeb(c); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index 98dddb93f9..ccd077e55c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -1,3 +1,4 @@ + package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -7,7 +8,6 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.ScatterData; -import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; @@ -118,8 +118,6 @@ public void drawValues(Canvas c) { float shapeSize = Utils.convertDpToPixel(dataSet.getScatterShapeSize()); - ValueFormatter formatter = dataSet.getValueFormatter(); - MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); @@ -137,7 +135,14 @@ public void drawValues(Canvas c) { Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); if (dataSet.isDrawValuesEnabled()) { - drawValue(c, formatter.getPointLabel(entry), positions[j], positions[j + 1] - shapeSize, dataSet.getValueTextColor(j / 2 + mXBounds.min)); + drawValue(c, + dataSet.getValueFormatter(), + entry.getY(), + entry, + i, + positions[j], + positions[j + 1] - shapeSize, + dataSet.getValueTextColor(j / 2 + mXBounds.min)); } if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { @@ -159,12 +164,6 @@ public void drawValues(Canvas c) { } } - @Override - public void drawValue(Canvas c, String valueText, float x, float y, int color) { - mValuePaint.setColor(color); - c.drawText(valueText, x, y, mValuePaint); - } - @Override public void drawExtras(Canvas c) { } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java index 046f3469bc..8adb56c73a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -1,3 +1,4 @@ + package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -201,7 +202,7 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { if (mViewPortHandler.isInBoundsX(x)) { - String label = mXAxis.getValueFormatter().getAxisLabel(mXAxis.mEntries[i / 2], mXAxis); + String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis); if (mXAxis.isAvoidFirstLastClippingEnabled()) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java index 9054dcb679..86047cf1b8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java @@ -1,3 +1,4 @@ + package com.github.mikephil.charting.renderer; import android.graphics.Canvas; @@ -56,10 +57,10 @@ public void computeAxis(float min, float max, boolean inverted) { computeAxisValues(min, max); } - + @Override protected void computeSize() { - + mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); @@ -155,7 +156,7 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { if (mViewPortHandler.isInBoundsY(y)) { - String label = mXAxis.getValueFormatter().getAxisLabel(mXAxis.mEntries[i / 2], mXAxis); + String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis); drawLabel(c, label, pos, y, anchor, labelRotationAngleDegrees); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java index 6d83cf59e4..956e8c7d5c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java @@ -1,6 +1,8 @@ + package com.github.mikephil.charting.renderer; import android.graphics.Canvas; +import android.graphics.PointF; import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.components.XAxis; @@ -41,7 +43,7 @@ public void renderAxisLabels(Canvas c) { MPPointF pOut = MPPointF.getInstance(0,0); for (int i = 0; i < mChart.getData().getMaxEntryCountSet().getEntryCount(); i++) { - String label = mXAxis.getValueFormatter().getAxisLabel(i, mXAxis); + String label = mXAxis.getValueFormatter().getFormattedValue(i, mXAxis); float angle = (sliceangle * i + mChart.getRotationAngle()) % 360f; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java index 60ff6ba3da..c302673919 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java @@ -1,3 +1,4 @@ + package com.github.mikephil.charting.utils; import android.annotation.SuppressLint; @@ -6,6 +7,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.text.Layout; @@ -13,13 +15,14 @@ import android.text.TextPaint; import android.util.DisplayMetrics; import android.util.Log; +import android.util.SizeF; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import com.github.mikephil.charting.formatter.DefaultValueFormatter; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import java.util.List; @@ -226,14 +229,15 @@ public static void calcTextSize(Paint paint, String demoText, FSize outputFSize) 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; - private static ValueFormatter mDefaultValueFormatter = generateDefaultValueFormatter(); + private static IValueFormatter mDefaultValueFormatter = generateDefaultValueFormatter(); - private static ValueFormatter generateDefaultValueFormatter() { - return new DefaultValueFormatter(1); + private static IValueFormatter generateDefaultValueFormatter() { + final DefaultValueFormatter formatter = new DefaultValueFormatter(1); + return formatter; } /// - returns: The default value formatter used for all chart components that needs a default - public static ValueFormatter getDefaultValueFormatter() + public static IValueFormatter getDefaultValueFormatter() { return mDefaultValueFormatter; } @@ -349,11 +353,11 @@ public static String formatNumber(float number, int digitCount, boolean separate * @return */ public static float roundToNextSignificant(double number) { - if (Double.isInfinite(number) || - Double.isNaN(number) || + if (Double.isInfinite(number) || + Double.isNaN(number) || number == 0.0) return 0; - + final float d = (float) Math.ceil((float) Math.log10(number < 0 ? -number : number)); final int pw = 1 - (int) d; final float magnitude = (float) Math.pow(10, pw); @@ -371,10 +375,10 @@ public static float roundToNextSignificant(double number) { public static int getDecimals(float number) { float i = roundToNextSignificant(number); - + if (Float.isInfinite(i)) return 0; - + return (int) Math.ceil(-Math.log10(i)) + 2; } diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java index fc7eb93e75..f1e1e0279e 100644 --- a/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java @@ -16,80 +16,80 @@ public void test() { LargeValueFormatter formatter = new LargeValueFormatter(); - String result = formatter.getFormattedValue(5f); + String result = formatter.getFormattedValue(5f, null); assertEquals("5", result); - result = formatter.getFormattedValue(5.5f); + result = formatter.getFormattedValue(5.5f, null); assertEquals("5.5", result); - result = formatter.getFormattedValue(50f); + result = formatter.getFormattedValue(50f, null); assertEquals("50", result); - result = formatter.getFormattedValue(50.5f); + result = formatter.getFormattedValue(50.5f, null); assertEquals("50.5", result); - result = formatter.getFormattedValue(500f); + result = formatter.getFormattedValue(500f, null); assertEquals("500", result); - result = formatter.getFormattedValue(1100f); + result = formatter.getFormattedValue(1100f, null); assertEquals("1.1k", result); - result = formatter.getFormattedValue(10000f); + result = formatter.getFormattedValue(10000f, null); assertEquals("10k", result); - result = formatter.getFormattedValue(10500f); + result = formatter.getFormattedValue(10500f, null); assertEquals("10.5k", result); - result = formatter.getFormattedValue(100000f); + result = formatter.getFormattedValue(100000f, null); assertEquals("100k", result); - result = formatter.getFormattedValue(1000000f); + result = formatter.getFormattedValue(1000000f, null); assertEquals("1m", result); - result = formatter.getFormattedValue(1500000f); + result = formatter.getFormattedValue(1500000f, null); assertEquals("1.5m", result); - result = formatter.getFormattedValue(9500000f); + result = formatter.getFormattedValue(9500000f, null); assertEquals("9.5m", result); - result = formatter.getFormattedValue(22200000f); + result = formatter.getFormattedValue(22200000f, null); assertEquals("22.2m", result); - result = formatter.getFormattedValue(222000000f); + result = formatter.getFormattedValue(222000000f, null); assertEquals("222m", result); - result = formatter.getFormattedValue(1000000000f); + result = formatter.getFormattedValue(1000000000f, null); assertEquals("1b", result); - result = formatter.getFormattedValue(9900000000f); + result = formatter.getFormattedValue(9900000000f, null); assertEquals("9.9b", result); - result = formatter.getFormattedValue(99000000000f); + result = formatter.getFormattedValue(99000000000f, null); assertEquals("99b", result); - result = formatter.getFormattedValue(99500000000f); + result = formatter.getFormattedValue(99500000000f, null); assertEquals("99.5b", result); - result = formatter.getFormattedValue(999000000000f); + result = formatter.getFormattedValue(999000000000f, null); assertEquals("999b", result); - result = formatter.getFormattedValue(1000000000000f); + result = formatter.getFormattedValue(1000000000000f, null); assertEquals("1t", result); formatter.setSuffix(new String[]{"", "k", "m", "b", "t", "q"}); // quadrillion support - result = formatter.getFormattedValue(1000000000000000f); + result = formatter.getFormattedValue(1000000000000000f, null); assertEquals("1q", result); - result = formatter.getFormattedValue(1100000000000000f); + result = formatter.getFormattedValue(1100000000000000f, null); assertEquals("1.1q", result); - result = formatter.getFormattedValue(10000000000000000f); + result = formatter.getFormattedValue(10000000000000000f, null); assertEquals("10q", result); - result = formatter.getFormattedValue(13300000000000000f); + result = formatter.getFormattedValue(13300000000000000f, null); assertEquals("13.3q", result); - result = formatter.getFormattedValue(100000000000000000f); + result = formatter.getFormattedValue(100000000000000000f, null); assertEquals("100q", result); } } diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java index 44946cf4da..e1dbe81be9 100644 --- a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java @@ -2,7 +2,7 @@ import com.github.mikephil.charting.utils.ObjectPool; -import org.junit.Assert; +import junit.framework.Assert; import org.junit.Test; From 696ebd06c9311b814428d7ebc2b3a44b19b49ce7 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 29 Oct 2020 16:30:36 +0100 Subject: [PATCH 287/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 46006a1e06..7c9c40e45c 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ If you like this library, please tell others about it :two_hearts: :two_hearts: [![Share on Google+](https://github.com/PhilJay/MPAndroidChart/blob/master/design/googleplus_icon.png)](https://plus.google.com/share?url=https://github.com/PhilJay/MPAndroidChart) [![Share on Facebook](https://github.com/PhilJay/MPAndroidChart/blob/master/design/facebook_icon.png)](https://www.facebook.com/sharer/sharer.php?u=https://github.com/PhilJay/MPAndroidChart) -You can follow me on Twitter [**@PhilippJahoda**](https://twitter.com/PhilippJahoda) or sign up for my [**coding newsletter**](https://weeklycoding.com). +If you like, you can follow me on Twitter [**@PhilippJahoda**].
    From 5a732b04278a983d32856c75ad552f2dcbbd922d Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 29 Oct 2020 16:31:19 +0100 Subject: [PATCH 288/291] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c9c40e45c..9a31698504 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ If you like this library, please tell others about it :two_hearts: :two_hearts: [![Share on Google+](https://github.com/PhilJay/MPAndroidChart/blob/master/design/googleplus_icon.png)](https://plus.google.com/share?url=https://github.com/PhilJay/MPAndroidChart) [![Share on Facebook](https://github.com/PhilJay/MPAndroidChart/blob/master/design/facebook_icon.png)](https://www.facebook.com/sharer/sharer.php?u=https://github.com/PhilJay/MPAndroidChart) -If you like, you can follow me on Twitter [**@PhilippJahoda**]. +If you like, you can follow me on Twitter [**@PhilippJahoda**](https://twitter.com/PhilippJahoda).
    From 9ba46d8618f988bb124f0cb8df23dac84da5c386 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 21 Jun 2021 15:16:32 +0200 Subject: [PATCH 289/291] Update README.md --- README.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.md b/README.md index 9a31698504..1ab6eb779f 100644 --- a/README.md +++ b/README.md @@ -82,14 +82,6 @@ Please read the [**documentation**](https://weeklycoding.com/mpandroidchart/) fi **This project needs you!** If you would like to support this project's further development, the creator of this project or the continuous maintenance of this project, **feel free to donate**. Your donation is highly appreciated (and I love food, coffee and beer). Thank you! -**My Bitcoin Wallet** (Bitcoin only) - -1G8G6tqQ3oh38BvDH3xq8o6gGVMvBTkcUg - -**My Ethereum Wallet** (Ethereum only) - -0x04ef098bf9f91871391363e3caf791afa3adc39b - [**Lightning Network (tippin.me)**](https://tippin.me/@PhilippJahoda) From 0550d3f7907c635744c08ad8ff9c45e1b6281cc7 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 21 Jun 2021 15:16:58 +0200 Subject: [PATCH 290/291] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 1ab6eb779f..a3253e3ff9 100644 --- a/README.md +++ b/README.md @@ -82,9 +82,6 @@ Please read the [**documentation**](https://weeklycoding.com/mpandroidchart/) fi **This project needs you!** If you would like to support this project's further development, the creator of this project or the continuous maintenance of this project, **feel free to donate**. Your donation is highly appreciated (and I love food, coffee and beer). Thank you! -[**Lightning Network (tippin.me)**](https://tippin.me/@PhilippJahoda) - - **PayPal** - [**Donate 5 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7G52RA87ED8NY): Thank's for creating this project, here's a coffee (or some beer) for you! From 3aa02094e270bc87940ff872ddcd69edf6152d16 Mon Sep 17 00:00:00 2001 From: Uriel Frankel Date: Sun, 12 May 2024 11:47:39 +0300 Subject: [PATCH 291/291] Make MPAndroidChart compatible with Android Studio Iguana (2024) --- MPChartLib/build.gradle | 1 - build.gradle | 7 +++---- gradle/wrapper/gradle-wrapper.properties | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index 0fb6dc7036..16d3e3ce10 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'com.android.library' -apply plugin: 'com.github.dcendents.android-maven' group='com.github.philjay' diff --git a/build.gradle b/build.gradle index cee7a83e80..ac31f01cf5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,17 +1,16 @@ buildscript { repositories { - jcenter() google() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.0' - classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' + classpath 'com.android.tools.build:gradle:7.0.4' } } allprojects { repositories { - jcenter() google() + mavenCentral() } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0f8bc4e375..ffed3a254e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sat Apr 27 15:48:29 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip