From acd046d7874e142edb3439cf2a065c367c0d5b3f Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 2 Mar 2016 00:14:33 +0200 Subject: [PATCH 001/606] Fixed some bugs calculating offset center point for space pie slice (Fixes #1528) --- .../charting/renderer/PieChartRenderer.java | 157 +++++++++++++----- 1 file changed, 115 insertions(+), 42 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java index 4d47b5cba7..fd7cb3c034 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -149,7 +149,6 @@ protected float calculateMinimumRadiusForSpacedSlice( float arcMidPointX = center.x + radius * (float) Math.cos(angleMiddle * Utils.FDEG2RAD); float arcMidPointY = center.y + radius * (float) Math.sin(angleMiddle * Utils.FDEG2RAD); - // Middle point on straight line between the two point. // This is the base of the contained triangle double basePointsDistance = Math.sqrt( Math.pow(arcEndPointX - arcStartPointX, 2) + @@ -187,7 +186,8 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { float sliceSpace = dataSet.getSliceSpace(); final PointF center = mChart.getCenterCircleBox(); final float radius = mChart.getRadius(); - final float userInnerRadius = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled() + final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled(); + final float userInnerRadius = drawInnerArc ? radius * (mChart.getHoleRadius() / 100.f) : 0.f; @@ -214,11 +214,11 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { mRenderPaint.setColor(dataSet.getColor(j)); - final float sliceSpaceOuterAngle = visibleAngleCount == 1 ? + final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? 0.f : sliceSpace / (Utils.FDEG2RAD * radius); - final float startAngleOuter = rotationAngle + (angle + sliceSpaceOuterAngle / 2.f) * phaseY; - float sweepAngleOuter = (sliceAngle - sliceSpaceOuterAngle) * phaseY; + final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY; + float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY; if (sweepAngleOuter < 0.f) { sweepAngleOuter = 0.f; @@ -245,16 +245,6 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { ); } - if (sliceSpace > 0.f) { - innerRadius = Math.max(innerRadius, - calculateMinimumRadiusForSpacedSlice( - center, radius, - sliceAngle * phaseY, - arcStartPointX, arcStartPointY, - startAngleOuter, - sweepAngleOuter)); - } - // API < 21 does not receive floats in addArc, but a RectF mInnerRectBuffer.set( center.x - innerRadius, @@ -262,13 +252,29 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { center.x + innerRadius, center.y + innerRadius); - if (innerRadius > 0.0) - { - final float sliceSpaceInnerAngle = visibleAngleCount == 1 ? + if (drawInnerArc && + (innerRadius > 0.f || sliceSpace > 0.f)) { + + if (sliceSpace > 0.f) { + float minSpacedRadius = + calculateMinimumRadiusForSpacedSlice( + center, radius, + sliceAngle * phaseY, + arcStartPointX, arcStartPointY, + startAngleOuter, + sweepAngleOuter); + + if (minSpacedRadius < 0.f) + minSpacedRadius = -minSpacedRadius; + + innerRadius = Math.max(innerRadius, minSpacedRadius); + } + + final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ? 0.f : sliceSpace / (Utils.FDEG2RAD * innerRadius); - final float startAngleInner = rotationAngle + (angle + sliceSpaceInnerAngle / 2.f) * phaseY; - float sweepAngleInner = (sliceAngle - sliceSpaceInnerAngle) * phaseY; + final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY; + float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY; if (sweepAngleInner < 0.f) { sweepAngleInner = 0.f; @@ -294,9 +300,34 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { else { if (sweepAngleOuter % 360f != 0.f) { - mPathBuffer.lineTo( - center.x, - center.y); + if (sliceSpace > 0.f) { + + 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); + } } } @@ -544,7 +575,8 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { float[] absoluteAngles = mChart.getAbsoluteAngles(); final PointF center = mChart.getCenterCircleBox(); final float radius = mChart.getRadius(); - final float userInnerRadius = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled() + final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled(); + final float userInnerRadius = drawInnerArc ? radius * (mChart.getHoleRadius() / 100.f) : 0.f; @@ -581,9 +613,6 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { float sliceSpace = set.getSliceSpace(); float sliceAngle = drawAngles[xIndex]; - final float sliceSpaceOuterAngle = visibleAngleCount == 1 ? - 0.f : - sliceSpace / (Utils.FDEG2RAD * radius); float innerRadius = userInnerRadius; float shift = set.getSelectionShift(); @@ -593,13 +622,28 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { mRenderPaint.setColor(set.getColor(xIndex)); - final float startAngleOuter = rotationAngle + (angle + sliceSpaceOuterAngle / 2.f) * phaseY; - float sweepAngleOuter = (sliceAngle - sliceSpaceOuterAngle) * phaseY; + final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * radius); + + final float sliceSpaceAngleShifted = visibleAngleCount == 1 ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * highlightedRadius); + + final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY; + float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY; if (sweepAngleOuter < 0.f) { sweepAngleOuter = 0.f; } + final float startAngleShifted = rotationAngle + (angle + sliceSpaceAngleShifted / 2.f) * phaseY; + float sweepAngleShifted = (sliceAngle - sliceSpaceAngleShifted) * phaseY; + if (sweepAngleShifted < 0.f) + { + sweepAngleShifted = 0.f; + } + mPathBuffer.reset(); if (sweepAngleOuter % 360f == 0.f) { @@ -608,25 +652,26 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } else { mPathBuffer.moveTo( - center.x + highlightedRadius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD), - center.y + highlightedRadius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD)); + center.x + highlightedRadius * (float) Math.cos(startAngleShifted * Utils.FDEG2RAD), + center.y + highlightedRadius * (float) Math.sin(startAngleShifted * Utils.FDEG2RAD)); mPathBuffer.arcTo( highlightedCircleBox, - startAngleOuter, - sweepAngleOuter + startAngleShifted, + sweepAngleShifted ); } + float sliceSpaceRadius = 0.f; if (sliceSpace > 0.f) { - innerRadius = Math.max(innerRadius, + sliceSpaceRadius = calculateMinimumRadiusForSpacedSlice( center, radius, sliceAngle * phaseY, center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD), center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD), startAngleOuter, - sweepAngleOuter)); + sweepAngleOuter); } // API < 21 does not receive floats in addArc, but a RectF @@ -636,12 +681,23 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { center.x + innerRadius, center.y + innerRadius); - if (innerRadius > 0.0) { - final float sliceSpaceInnerAngle = visibleAngleCount == 1 ? + if (drawInnerArc && + (innerRadius > 0.f || sliceSpace > 0.f)) { + + if (sliceSpace > 0.f) { + float minSpacedRadius = sliceSpaceRadius; + + if (minSpacedRadius < 0.f) + minSpacedRadius = -minSpacedRadius; + + innerRadius = Math.max(innerRadius, minSpacedRadius); + } + + final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ? 0.f : sliceSpace / (Utils.FDEG2RAD * innerRadius); - final float startAngleInner = rotationAngle + (angle + sliceSpaceInnerAngle / 2.f) * phaseY; - float sweepAngleInner = (sliceAngle - sliceSpaceInnerAngle) * phaseY; + final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY; + float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY; if (sweepAngleInner < 0.f) { sweepAngleInner = 0.f; @@ -667,9 +723,26 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { else { if (sweepAngleOuter % 360f != 0.f) { - mPathBuffer.lineTo( - center.x, - center.y); + + if (sliceSpace > 0.0) { + final float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f; + + final float arcEndPointX = center.x + + sliceSpaceRadius * (float) Math.cos(angleMiddle * Utils.FDEG2RAD); + final float arcEndPointY = center.y + + sliceSpaceRadius * (float) Math.sin(angleMiddle * Utils.FDEG2RAD); + + mPathBuffer.lineTo( + arcEndPointX, + arcEndPointY); + + } else { + + mPathBuffer.lineTo( + center.x, + center.y); + } + } } From 2600018ff8acd58dc7b640bc784999557cbd8902 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 2 Mar 2016 23:14:42 +0100 Subject: [PATCH 002/606] Fix #1535 --- .../mikephil/charting/highlight/BarHighlighter.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java index 1fa547884f..853e2d1773 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java @@ -105,8 +105,10 @@ protected Highlight getStackedHighlight(Highlight old, IBarDataSet set, int xInd Range[] ranges = getRanges(entry); int stackIndex = getClosestStackIndex(ranges, (float) yValue); - Highlight h = new Highlight(xIndex, dataSetIndex, stackIndex, ranges[stackIndex]); - return h; + if(ranges.length > 0) + return new Highlight(xIndex, dataSetIndex, stackIndex, ranges[stackIndex]); + else + return null; } /** @@ -187,15 +189,14 @@ protected float getBase(float x) { protected Range[] getRanges(BarEntry entry) { float[] values = entry.getVals(); + Range[] ranges = new Range[values.length]; if (values == null || values.length == 0) - return null; + return ranges; float negRemain = -entry.getNegativeSum(); float posRemain = 0f; - Range[] ranges = new Range[values.length]; - for (int i = 0; i < ranges.length; i++) { float value = values[i]; From f1d79dc67b5fda60af7581f08ecea33053db5f5c Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 3 Mar 2016 00:29:43 +0200 Subject: [PATCH 003/606] Fixes circle-with-hole drawing hiccup (Closes #1528) --- .../github/mikephil/charting/renderer/PieChartRenderer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java index fd7cb3c034..a725c2afbf 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -183,7 +183,6 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { final int entryCount = dataSet.getEntryCount(); final float[] drawAngles = mChart.getDrawAngles(); - float sliceSpace = dataSet.getSliceSpace(); final PointF center = mChart.getCenterCircleBox(); final float radius = mChart.getRadius(); final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled(); @@ -199,6 +198,8 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { } } + final float sliceSpace = visibleAngleCount <= 1 ? 0.f : dataSet.getSliceSpace(); + for (int j = 0; j < entryCount; j++) { float sliceAngle = drawAngles[j]; @@ -610,7 +611,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { else angle = absoluteAngles[xIndex - 1] * phaseX; - float sliceSpace = set.getSliceSpace(); + final float sliceSpace = visibleAngleCount <= 1 ? 0.f : set.getSliceSpace(); float sliceAngle = drawAngles[xIndex]; float innerRadius = userInnerRadius; From 298e97381919717a397874c7980498ccf413e1d7 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 2 Mar 2016 23:42:58 +0100 Subject: [PATCH 004/606] Minor adjustment of previous fix. --- .../github/mikephil/charting/highlight/BarHighlighter.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java index 853e2d1773..a852aa041c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java @@ -189,10 +189,11 @@ protected float getBase(float x) { protected Range[] getRanges(BarEntry entry) { float[] values = entry.getVals(); - Range[] ranges = new Range[values.length]; if (values == null || values.length == 0) - return ranges; + return new Range[0]; + + Range[] ranges = new Range[values.length]; float negRemain = -entry.getNegativeSum(); float posRemain = 0f; From 420d17391ada2c3d64dcfaa13c3cd0665844c61f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 4 Mar 2016 22:24:59 +0100 Subject: [PATCH 005/606] Add dp conversion to fix issue #1540 --- .../listener/BarLineChartTouchListener.java | 59 ++++++++++++------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java b/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java index 34771283c6..72c1bb06ed 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java +++ b/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java @@ -23,21 +23,29 @@ /** * TouchListener for Bar-, Line-, Scatter- and CandleStickChart with handles all * touch interaction. Longpress == Zoom out. Double-Tap == Zoom in. - * + * * @author Philipp Jahoda */ public class BarLineChartTouchListener extends ChartTouchListener>>> { - /** the original touch-matrix from the chart */ + /** + * the original touch-matrix from the chart + */ private Matrix mMatrix = new Matrix(); - /** matrix for saving the original matrix state */ + /** + * matrix for saving the original matrix state + */ private Matrix mSavedMatrix = new Matrix(); - /** point where the touch action started */ + /** + * point where the touch action started + */ private PointF mTouchStartPoint = new PointF(); - /** center between two pointers (fingers on the display) */ + /** + * center between two pointers (fingers on the display) + */ private PointF mTouchPointCenter = new PointF(); private float mSavedXDist = 1f; @@ -46,16 +54,26 @@ public class BarLineChartTouchListener extends ChartTouchListener>> chart, Matrix touchMatrix) { super(chart); this.mMatrix = touchMatrix; + + // this equals to about 9 pixels on a 5.5" FHD screen + this.mDragTriggerDist = Utils.convertDpToPixel(3f); } @SuppressLint("ClickableViewAccessibility") @@ -142,7 +160,7 @@ public boolean onTouch(View v, MotionEvent event) { } else if (mTouchMode == NONE && Math.abs(distance(event.getX(), mTouchStartPoint.x, event.getY(), - mTouchStartPoint.y)) > 5f) { + mTouchStartPoint.y)) > mDragTriggerDist) { if (mChart.hasNoDragOffset()) { @@ -237,7 +255,7 @@ public boolean onTouch(View v, MotionEvent event) { /** * Saves the current Matrix state and the touch-start point. - * + * * @param event */ private void saveTouchStart(MotionEvent event) { @@ -250,7 +268,7 @@ private void saveTouchStart(MotionEvent event) { /** * Performs all necessary operations needed for dragging. - * + * * @param event */ private void performDrag(MotionEvent event) { @@ -275,8 +293,7 @@ private void performDrag(MotionEvent event) { dX = event.getX() - mTouchStartPoint.x; dY = -(event.getY() - mTouchStartPoint.y); } - } - else { + } else { dX = event.getX() - mTouchStartPoint.x; dY = event.getY() - mTouchStartPoint.y; } @@ -289,7 +306,7 @@ private void performDrag(MotionEvent event) { /** * Performs the all operations necessary for pinch and axis zoom. - * + * * @param event */ private void performZoom(MotionEvent event) { @@ -374,7 +391,7 @@ private void performZoom(MotionEvent event) { /** * Highlights upon dragging, generates callbacks for the selection-listener. - * + * * @param e */ private void performHighlightDrag(MotionEvent e) { @@ -395,7 +412,7 @@ private void performHighlightDrag(MotionEvent e) { /** * Determines the center point between two pointer touch points. - * + * * @param point * @param event */ @@ -407,7 +424,7 @@ private static void midPoint(PointF point, MotionEvent event) { /** * returns the distance between two pointer touch points - * + * * @param event * @return */ @@ -420,7 +437,7 @@ private static float spacing(MotionEvent event) { /** * calculates the distance on the x-axis between two pointers (fingers on * the display) - * + * * @param e * @return */ @@ -432,7 +449,7 @@ private static float getXDist(MotionEvent e) { /** * calculates the distance on the y-axis between two pointers (fingers on * the display) - * + * * @param e * @return */ @@ -444,7 +461,7 @@ private static float getYDist(MotionEvent e) { /** * returns the correct translation depending on the provided x and y touch * points - * + * * @param x * @param y * @return @@ -474,7 +491,7 @@ public PointF getTrans(float x, float y) { /** * returns the matrix object the listener holds - * + * * @return */ public Matrix getMatrix() { @@ -531,7 +548,7 @@ public boolean onSingleTapUp(MotionEvent e) { l.onChartSingleTapped(e); } - if(!mChart.isHighlightPerTapEnabled()) { + if (!mChart.isHighlightPerTapEnabled()) { return false; } @@ -585,7 +602,7 @@ public void computeScroll() { mDecelerationVelocity.x *= mChart.getDragDecelerationFrictionCoef(); mDecelerationVelocity.y *= mChart.getDragDecelerationFrictionCoef(); - final float timeInterval = (float)(currentTime - mDecelerationLastTime) / 1000.f; + final float timeInterval = (float) (currentTime - mDecelerationLastTime) / 1000.f; float distanceX = mDecelerationVelocity.x * timeInterval; float distanceY = mDecelerationVelocity.y * timeInterval; From 6e35cf1c685bbf1de9f430f1b5dcf226d523ffff Mon Sep 17 00:00:00 2001 From: Cory Charlton Date: Sat, 5 Mar 2016 18:33:34 -0800 Subject: [PATCH 006/606] Added Chart.setNoDataTextColor(int color) --- .../src/com/github/mikephil/charting/charts/Chart.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java index 647d0f944f..f224e9cf6e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java @@ -1178,6 +1178,15 @@ public void setNoDataText(String text) { mNoDataText = text; } + /** + * Sets the color of the no data text. + * + * @param color + */ + public void setNoDataTextColor(int color) { + mInfoPaint.setColor(color); + } + /** * Sets descriptive text to explain to the user why there is no chart * available Defaults to empty if not set From f2771a6c88870217c78d6e04a1f94b3e32b9696c Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 6 Mar 2016 14:08:42 +0100 Subject: [PATCH 007/606] Update README.md --- README.md | 101 +++++++++++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 50da145069..27b8175cc5 100644 --- a/README.md +++ b/README.md @@ -84,57 +84,6 @@ Features - Gradle support - Plotting data directly from [**Realm.io**](https://realm.io) mobile database -**Chart types:** - - - **LineChart (with legend, simple design)** -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart4.png) - - **LineChart (with legend, simple design)** -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart3.png) - - - **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) - - - **Combined-Chart (bar- and linechart in this case)** -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/combined_chart.png) - - - **BarChart (with legend, simple design)** - -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_barchart3.png) - - - **BarChart (grouped DataSets)** - -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/groupedbarchart.png) - - - **Horizontal-BarChart** - -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/horizontal_barchart.png) - - - - **PieChart (with selection, ...)** - -![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/simpledesign_piechart1.png) - - - **ScatterChart** (with squares, triangles, circles, ... and more) - -![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/scatterchart.png) - - - **CandleStickChart** (for financial data) - -![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/candlestickchart.png) - - - **BubbleChart** (area covered by bubbles indicates the value) - -![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/bubblechart.png) - - - **RadarChart** (spider web chart) - -![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/radarchart.png) - - - Usage ======= @@ -190,6 +139,56 @@ Furthermore, you can also rely on the [**MPChartExample**](https://github.com/Ph 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) + +**Chart types:** + + - **LineChart (with legend, simple design)** +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart4.png) + - **LineChart (with legend, simple design)** +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart3.png) + + - **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) + + - **Combined-Chart (bar- and linechart in this case)** +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/combined_chart.png) + + - **BarChart (with legend, simple design)** + +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_barchart3.png) + + - **BarChart (grouped DataSets)** + +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/groupedbarchart.png) + + - **Horizontal-BarChart** + +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/horizontal_barchart.png) + + + - **PieChart (with selection, ...)** + +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/simpledesign_piechart1.png) + + - **ScatterChart** (with squares, triangles, circles, ... and more) + +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/scatterchart.png) + + - **CandleStickChart** (for financial data) + +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/candlestickchart.png) + + - **BubbleChart** (area covered by bubbles indicates the value) + +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/bubblechart.png) + + - **RadarChart** (spider web chart) + +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/radarchart.png) + License ======= From 3b70c23a06124f8b6b033dd03c94d1dc8a953aa6 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 6 Mar 2016 17:55:31 +0100 Subject: [PATCH 008/606] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 27b8175cc5..6e24af64a7 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ If you are having questions or problems, you should: Please do not expect answers to your questions if you have not considered all above mentioned points in advance. Features -======= +----- **Core features:** - 8 different chart types @@ -85,7 +85,7 @@ Features - Plotting data directly from [**Realm.io**](https://realm.io) mobile database Usage -======= +----- In order to use the library, there are 4 different options: @@ -132,7 +132,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/v2.2.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). @@ -140,7 +140,8 @@ Furthermore, you can also rely on the [**MPChartExample**](https://github.com/Ph 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) -**Chart types:** +Chart types +----- - **LineChart (with legend, simple design)** ![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart4.png) From 4845e1b0e3269e1b4e76f9fb393a9fd160b4c45f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 6 Mar 2016 18:29:08 +0100 Subject: [PATCH 009/606] Fix issue regarding corrupted translation (issue #1551) --- .../mpchartexample/LineChartActivity2.java | 16 +++--- .../listener/BarLineChartTouchListener.java | 55 ++++++++++++------- .../charting/utils/ViewPortHandler.java | 43 +++++++++++++-- 3 files changed, 80 insertions(+), 34 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 7c4236f60c..2d1f3baf29 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -59,14 +59,14 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (LineChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); - + // no description text mChart.setDescription(""); mChart.setNoDataTextDescription("You need to provide data for the chart."); // enable touch gestures mChart.setTouchEnabled(true); - + mChart.setDragDecelerationFrictionCoef(0.9f); // enable scaling and dragging @@ -114,7 +114,7 @@ protected void onCreate(Bundle savedInstanceState) { leftAxis.setAxisMaxValue(200f); leftAxis.setAxisMinValue(0f); leftAxis.setDrawGridLines(true); - + YAxis rightAxis = mChart.getAxisRight(); rightAxis.setTypeface(tf); rightAxis.setTextColor(Color.RED); @@ -148,7 +148,7 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { + if (mChart.getData() != null) { mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); mChart.invalidate(); } @@ -266,8 +266,8 @@ 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); + // ((mult * + // 0.1) / 10); yVals1.add(new Entry(val, i)); } @@ -292,8 +292,8 @@ private void setData(int count, float range) { for (int i = 0; i < count; i++) { float mult = range; float val = (float) (Math.random() * mult) + 450;// + (float) - // ((mult * - // 0.1) / 10); + // ((mult * + // 0.1) / 10); yVals2.add(new Entry(val, i)); } diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java b/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java index 72c1bb06ed..71168adebf 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java +++ b/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java @@ -68,12 +68,19 @@ public class BarLineChartTouchListener extends ChartTouchListener>> chart, Matrix touchMatrix) { super(chart); this.mMatrix = touchMatrix; // this equals to about 9 pixels on a 5.5" FHD screen this.mDragTriggerDist = Utils.convertDpToPixel(3f); + + this.mMinScalePointerDistance = Utils.convertDpToPixel(3.5f); } @SuppressLint("ClickableViewAccessibility") @@ -162,7 +169,6 @@ public boolean onTouch(View v, MotionEvent event) { && Math.abs(distance(event.getX(), mTouchStartPoint.x, event.getY(), mTouchStartPoint.y)) > mDragTriggerDist) { - if (mChart.hasNoDragOffset()) { if (!mChart.isFullyZoomedOut() && mChart.isDragEnabled()) { @@ -241,8 +247,7 @@ public boolean onTouch(View v, MotionEvent event) { break; } - // Perform the transformation, update the chart - // if (needsRefresh()) + // perform the transformation, update the chart mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, true); return true; // indicate event was handled @@ -311,21 +316,20 @@ private void performDrag(MotionEvent event) { */ private void performZoom(MotionEvent event) { - if (event.getPointerCount() >= 2) { + if (event.getPointerCount() >= 2) { // two finger zoom OnChartGestureListener l = mChart.getOnChartGestureListener(); - // get the distance between the pointers of the touch - // event + // get the distance between the pointers of the touch event float totalDist = spacing(event); - if (totalDist > 10f) { + if (totalDist > mMinScalePointerDistance) { // get the translation PointF t = getTrans(mTouchPointCenter.x, mTouchPointCenter.y); + ViewPortHandler h = mChart.getViewPortHandler(); - // take actions depending on the activated touch - // mode + // take actions depending on the activated touch mode if (mTouchMode == PINCH_ZOOM) { mLastGesture = ChartGesture.PINCH_ZOOM; @@ -333,14 +337,19 @@ private void performZoom(MotionEvent event) { float scale = totalDist / mSavedDist; // total scale boolean isZoomingOut = (scale < 1); + boolean canZoomMoreX = isZoomingOut ? - mChart.getViewPortHandler().canZoomOutMoreX() : - mChart.getViewPortHandler().canZoomInMoreX(); + h.canZoomOutMoreX() : + h.canZoomInMoreX(); + + boolean canZoomMoreY = isZoomingOut ? + h.canZoomOutMoreY() : + h.canZoomInMoreY(); float scaleX = (mChart.isScaleXEnabled()) ? scale : 1f; float scaleY = (mChart.isScaleYEnabled()) ? scale : 1f; - if (mChart.isScaleYEnabled() || canZoomMoreX) { + if (canZoomMoreY || canZoomMoreX) { mMatrix.set(mSavedMatrix); mMatrix.postScale(scaleX, scaleY, t.x, t.y); @@ -358,8 +367,8 @@ private void performZoom(MotionEvent event) { boolean isZoomingOut = (scaleX < 1); boolean canZoomMoreX = isZoomingOut ? - mChart.getViewPortHandler().canZoomOutMoreX() : - mChart.getViewPortHandler().canZoomInMoreX(); + h.canZoomOutMoreX() : + h.canZoomInMoreX(); if (canZoomMoreX) { @@ -377,13 +386,19 @@ private void performZoom(MotionEvent event) { float yDist = getYDist(event); float scaleY = yDist / mSavedYDist; // y-axis scale - mMatrix.set(mSavedMatrix); + boolean isZoomingOut = (scaleY < 1); + boolean canZoomMoreY = isZoomingOut ? + h.canZoomOutMoreY() : + h.canZoomInMoreY(); + + if (canZoomMoreY) { - // y-axis comes from top to bottom, revert y - mMatrix.postScale(1f, scaleY, t.x, t.y); + mMatrix.set(mSavedMatrix); + mMatrix.postScale(1f, scaleY, t.x, t.y); - if (l != null) - l.onChartScale(event, 1f, scaleY); + if (l != null) + l.onChartScale(event, 1f, scaleY); + } } } } @@ -400,7 +415,7 @@ private void performHighlightDrag(MotionEvent e) { if (h != null && !h.equalTo(mLastHighlighted)) { mLastHighlighted = h; - mChart.highlightTouch(h); + mChart.highlightValue(h, true); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java b/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java index 0a220cf508..44797c3f40 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java @@ -352,7 +352,7 @@ public void centerViewPort(final float[] transformedPts, final View view) { } /** - * buffer for storing matrix values + * buffer for storing the 9 matrix values of a 3x3 matrix */ protected final float[] matrixBuffer = new float[9]; @@ -391,7 +391,7 @@ public void limitTransAndScale(Matrix matrix, RectF content) { float curTransY = matrixBuffer[Matrix.MTRANS_Y]; float curScaleY = matrixBuffer[Matrix.MSCALE_Y]; - // min scale-x is 1f, max is the max float + // min scale-x is 1f mScaleX = Math.min(Math.max(mMinScaleX, curScaleX), mMaxScaleX); // min scale-y is 1f @@ -406,12 +406,10 @@ public void limitTransAndScale(Matrix matrix, RectF content) { } float maxTransX = -width * (mScaleX - 1f); - float newTransX = Math.min(Math.max(curTransX, maxTransX - mTransOffsetX), mTransOffsetX); - mTransX = newTransX; + mTransX = Math.min(Math.max(curTransX, maxTransX - mTransOffsetX), mTransOffsetX); float maxTransY = height * (mScaleY - 1f); - float newTransY = Math.max(Math.min(curTransY, maxTransY + mTransOffsetY), -mTransOffsetY); - mTransY = newTransY; + mTransY = Math.max(Math.min(curTransY, maxTransY + mTransOffsetY), -mTransOffsetY); matrixBuffer[Matrix.MTRANS_X] = mTransX; matrixBuffer[Matrix.MSCALE_X] = mScaleX; @@ -562,6 +560,22 @@ public float getScaleY() { return mScaleY; } + public float getMinScaleX() { + return mMinScaleX; + } + + public float getMaxScaleX() { + return mMaxScaleX; + } + + public float getMinScaleY() { + return mMinScaleY; + } + + public float getMaxScaleY() { + return mMaxScaleY; + } + /** * Returns the translation (drag / pan) distance on the x-axis * @@ -665,4 +679,21 @@ public boolean canZoomInMoreX() { return (mScaleX < mMaxScaleX); } + /** + * Returns true if the chart is not yet fully zoomed out on the y-axis + * + * @return + */ + public boolean canZoomOutMoreY() { + return (mScaleY > mMinScaleY); + } + + /** + * Returns true if the chart is not yet fully zoomed in on the y-axis + * + * @return + */ + public boolean canZoomInMoreY() { + return (mScaleY < mMaxScaleY); + } } From d4500e28ae466169853975a37eca150bbf7ce7b8 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 9 Mar 2016 23:31:32 +0200 Subject: [PATCH 010/606] Localized buffers for line rendering To simplify rendering flow, and avoid excessive memory usage --- .../charting/buffer/CircleBuffer.java | 31 --- .../mikephil/charting/buffer/LineBuffer.java | 57 ------ .../charting/renderer/LineChartRenderer.java | 177 +++++++++++------- 3 files changed, 111 insertions(+), 154 deletions(-) delete mode 100644 MPChartLib/src/com/github/mikephil/charting/buffer/CircleBuffer.java delete mode 100644 MPChartLib/src/com/github/mikephil/charting/buffer/LineBuffer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/CircleBuffer.java b/MPChartLib/src/com/github/mikephil/charting/buffer/CircleBuffer.java deleted file mode 100644 index 2e8f753af7..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/CircleBuffer.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.ILineDataSet; - -public class CircleBuffer extends AbstractBuffer { - - public CircleBuffer(int size) { - super(size); - } - - protected void addCircle(float x, float y) { - buffer[index++] = x; - buffer[index++] = y; - } - - @Override - public void feed(ILineDataSet data) { - - int size = (int)Math.ceil((mTo - mFrom) * phaseX + mFrom); - - for (int i = mFrom; i < size; i++) { - - Entry e = data.getEntryForIndex(i); - addCircle(e.getXIndex(), e.getVal() * phaseY); - } - - reset(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/LineBuffer.java b/MPChartLib/src/com/github/mikephil/charting/buffer/LineBuffer.java deleted file mode 100644 index 723124f72d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/LineBuffer.java +++ /dev/null @@ -1,57 +0,0 @@ - -package com.github.mikephil.charting.buffer; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; - -public class LineBuffer extends AbstractBuffer { - - public LineBuffer(int size) { - super((size < 4) ? 4 : size); - } - - public void moveTo(float x, float y) { - - if (index != 0) - return; - - buffer[index++] = x; - buffer[index++] = y; - - // in case just one entry, this is overwritten when lineTo is called - buffer[index] = x; - buffer[index + 1] = y; - } - - public void lineTo(float x, float y) { - - if (index == 2) { - buffer[index++] = x; - buffer[index++] = y; - } else { - - float prevX = buffer[index - 2]; - float prevY = buffer[index - 1]; - buffer[index++] = prevX; - buffer[index++] = prevY; - buffer[index++] = x; - buffer[index++] = y; - } - } - - @Override - public void feed(ILineDataSet data) { - moveTo(data.getEntryForIndex(mFrom).getXIndex(), data.getEntryForIndex(mFrom).getVal() * phaseY); - - int size = (int) Math.ceil((mTo - mFrom) * phaseX + mFrom); - int from = mFrom + 1; - - for (int i = from; i < size; i++) { - - Entry e = data.getEntryForIndex(i); - lineTo(e.getXIndex(), e.getVal() * phaseY); - } - - reset(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index ec266ec789..e384d1b4da 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -8,8 +8,6 @@ import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.buffer.CircleBuffer; -import com.github.mikephil.charting.buffer.LineBuffer; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; @@ -52,10 +50,6 @@ public class LineChartRenderer extends LineRadarRenderer { protected Path cubicPath = new Path(); protected Path cubicFillPath = new Path(); - protected LineBuffer[] mLineBuffers; - - protected CircleBuffer[] mCircleBuffers; - public LineChartRenderer(LineDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { super(animator, viewPortHandler); @@ -69,15 +63,6 @@ public LineChartRenderer(LineDataProvider chart, ChartAnimator animator, @Override public void initBuffers() { - LineData lineData = mChart.getLineData(); - mLineBuffers = new LineBuffer[lineData.getDataSetCount()]; - mCircleBuffers = new CircleBuffer[lineData.getDataSetCount()]; - - for (int i = 0; i < mLineBuffers.length; i++) { - ILineDataSet set = lineData.getDataSetByIndex(i); - mLineBuffers[i] = new LineBuffer(set.getEntryCount() * 4 - 4); - mCircleBuffers[i] = new CircleBuffer(set.getEntryCount() * 2); - } } @Override @@ -148,8 +133,8 @@ protected void drawCubic(Canvas c, ILineDataSet dataSet) { Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); int diff = (entryFrom == entryTo) ? 1 : 0; - int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff - 1, 0); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, entryCount); + int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); + int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); @@ -230,7 +215,7 @@ protected void drawCubic(Canvas c, ILineDataSet dataSet) { cubicFillPath.addPath(cubicPath); // create a new path, this is bad for performance drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, - entryFrom.getXIndex(), entryFrom.getXIndex() + size); + minx, size); } mRenderPaint.setColor(dataSet.getColor()); @@ -278,6 +263,8 @@ protected void drawCubicFill(Canvas c, ILineDataSet dataSet, Path spline, Transf } } + private float[] mLineBuffer = new float[4]; + /** * Draws a normal line. * @@ -288,8 +275,6 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { int entryCount = dataSet.getEntryCount(); - int dataSetIndex = mChart.getLineData().getIndexOfDataSet(dataSet); - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); float phaseX = mAnimator.getPhaseX(); @@ -309,50 +294,102 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, DataSet.Rounding.DOWN); Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); - int minx = Math.max(dataSet.getEntryIndex(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, entryCount); - - int range = (maxx - minx) * 4 - 4; - - LineBuffer buffer = mLineBuffers[dataSetIndex]; - buffer.setPhases(phaseX, phaseY); - buffer.limitFrom(minx); - buffer.limitTo(maxx); - buffer.feed(dataSet); + int diff = (entryFrom == entryTo) ? 1 : 0; + int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); + int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); - trans.pointValuesToPixel(buffer.buffer); + final int count = (int)(Math.ceil((float)(maxx - minx) * phaseX + (float)(minx))); // more than 1 color if (dataSet.getColors().size() > 1) { - for (int j = 0; j < range; j += 4) { + if (mLineBuffer.length != 2 * 2) + mLineBuffer = new float[2 * 2]; + + for (int j = minx; + j < count; + j++) { + + if (count > 1 && j == count - 1) { + // Last point, we have already drawn a line to this point + break; + } + + Entry e = dataSet.getEntryForIndex(j); + if (e == null) continue; + + mLineBuffer[0] = e.getXIndex(); + mLineBuffer[1] = e.getVal() * phaseY; + + if (j + 1 < count) { + + e = dataSet.getEntryForIndex(j + 1); + + if (e == null) break; + + mLineBuffer[2] = e.getXIndex(); + mLineBuffer[3] = e.getVal() * phaseY; + } else { + mLineBuffer[2] = mLineBuffer[0]; + mLineBuffer[3] = mLineBuffer[1]; + } + + trans.pointValuesToPixel(mLineBuffer); - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) + if (!mViewPortHandler.isInBoundsRight(mLineBuffer[0])) break; // make sure the lines don't do shitty things outside // bounds - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2]) - || (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 1]) && !mViewPortHandler - .isInBoundsBottom(buffer.buffer[j + 3])) - || (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 1]) && !mViewPortHandler - .isInBoundsBottom(buffer.buffer[j + 3]))) + if (!mViewPortHandler.isInBoundsLeft(mLineBuffer[2]) + || (!mViewPortHandler.isInBoundsTop(mLineBuffer[1]) && !mViewPortHandler + .isInBoundsBottom(mLineBuffer[3])) + || (!mViewPortHandler.isInBoundsTop(mLineBuffer[1]) && !mViewPortHandler + .isInBoundsBottom(mLineBuffer[3]))) continue; // get the color that is set for this line-segment - mRenderPaint.setColor(dataSet.getColor(j / 4 + minx)); + mRenderPaint.setColor(dataSet.getColor(j)); - canvas.drawLine(buffer.buffer[j], buffer.buffer[j + 1], - buffer.buffer[j + 2], buffer.buffer[j + 3], mRenderPaint); + canvas.drawLine(mLineBuffer[0], mLineBuffer[1], + mLineBuffer[2], mLineBuffer[3], mRenderPaint); } } else { // only one color per dataset - mRenderPaint.setColor(dataSet.getColor()); + if (mLineBuffer.length != Math.max((entryCount - 1) * 2, 2) * 2) + mLineBuffer = new float[Math.max((entryCount - 1) * 2, 2) * 2]; + + Entry e1, e2; + + e1 = dataSet.getEntryForIndex(minx); + + if (e1 != null) { + + for (int x = count > 1 ? minx + 1 : minx, j = 0; x < count; x++) { + + e1 = dataSet.getEntryForIndex(x == 0 ? 0 : (x - 1)); + e2 = dataSet.getEntryForIndex(x); + + if (e1 == null || e2 == null) continue; + + mLineBuffer[j] = e1.getXIndex(); + mLineBuffer[j + 1] = e1.getVal() * phaseY; + mLineBuffer[j + 2] = e2.getXIndex(); + mLineBuffer[j + 3] = e2.getVal() * phaseY; + j += 4; + + } + + trans.pointValuesToPixel(mLineBuffer); + + final int size = Math.max((count - minx - 1) * 2, 2) * 2; - // c.drawLines(buffer.buffer, mRenderPaint); - canvas.drawLines(buffer.buffer, 0, range, - mRenderPaint); + mRenderPaint.setColor(dataSet.getColor()); + + canvas.drawLines(mLineBuffer, 0, size, + mRenderPaint); + } } mRenderPaint.setPathEffect(null); @@ -447,11 +484,13 @@ public void drawValues(Canvas c) { int entryCount = dataSet.getEntryCount(); - Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, DataSet.Rounding.DOWN); + Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, + DataSet.Rounding.DOWN); Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); - int minx = Math.max(dataSet.getEntryIndex(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, entryCount); + int diff = (entryFrom == entryTo) ? 1 : 0; + int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); + int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); float[] positions = trans.generateTransformedValuesLine( dataSet, mAnimator.getPhaseX(), mAnimator.getPhaseY(), minx, maxx); @@ -488,6 +527,8 @@ protected void drawCircles(Canvas c) { float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); + float[] circlesBuffer = new float[2]; + List dataSets = mChart.getLineData().getDataSets(); for (int i = 0; i < dataSets.size(); i++) { @@ -504,45 +545,49 @@ protected void drawCircles(Canvas c) { int entryCount = dataSet.getEntryCount(); - Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, DataSet.Rounding.DOWN); + Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, + DataSet.Rounding.DOWN); Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); - int minx = Math.max(dataSet.getEntryIndex(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, entryCount); + int diff = (entryFrom == entryTo) ? 1 : 0; + int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); + int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); - CircleBuffer buffer = mCircleBuffers[i]; - buffer.setPhases(phaseX, phaseY); - buffer.limitFrom(minx); - buffer.limitTo(maxx); - buffer.feed(dataSet); + float halfsize = dataSet.getCircleRadius() / 2f; - trans.pointValuesToPixel(buffer.buffer); + for (int j = minx, + count = (int) Math.ceil((maxx - minx) * phaseX + minx); + j < count; + j ++) { - float halfsize = dataSet.getCircleRadius() / 2f; + Entry e = dataSet.getEntryForIndex(j); + + if (e == null) break; - for (int j = 0, count = (int) Math.ceil((maxx - minx) * phaseX + minx) * 2; j < count; j += 2) { + circlesBuffer[0] = e.getXIndex(); + circlesBuffer[1] = e.getVal() * phaseY; - float x = buffer.buffer[j]; - float y = buffer.buffer[j + 1]; + trans.pointValuesToPixel(circlesBuffer); - if (!mViewPortHandler.isInBoundsRight(x)) + if (!mViewPortHandler.isInBoundsRight(circlesBuffer[0])) break; // make sure the circles don't do shitty things outside // bounds - if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) + if (!mViewPortHandler.isInBoundsLeft(circlesBuffer[0]) || + !mViewPortHandler.isInBoundsY(circlesBuffer[1])) continue; - int circleColor = dataSet.getCircleColor(j / 2 + minx); + int circleColor = dataSet.getCircleColor(j); mRenderPaint.setColor(circleColor); - c.drawCircle(x, y, dataSet.getCircleRadius(), + c.drawCircle(circlesBuffer[0], circlesBuffer[1], dataSet.getCircleRadius(), mRenderPaint); if (dataSet.isDrawCircleHoleEnabled() && circleColor != mCirclePaintInner.getColor()) - c.drawCircle(x, y, + c.drawCircle(circlesBuffer[0], circlesBuffer[1], halfsize, mCirclePaintInner); } From 7b8203020de566ecd7b748848e62b5c489735532 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 9 Mar 2016 23:56:18 +0200 Subject: [PATCH 011/606] Added stepped line chart support --- MPChartExample/res/menu/line.xml | 6 +- .../mpchartexample/LineChartActivity1.java | 15 +++++ .../mpchartexample/LineChartActivity2.java | 15 +++++ .../mikephil/charting/data/LineDataSet.java | 20 +++++++ .../implementation/RealmLineDataSet.java | 22 ++++++++ .../interfaces/datasets/ILineDataSet.java | 7 +++ .../charting/renderer/LineChartRenderer.java | 55 ++++++++++++++----- 7 files changed, 125 insertions(+), 15 deletions(-) diff --git a/MPChartExample/res/menu/line.xml b/MPChartExample/res/menu/line.xml index 42ca211c06..8d1437029b 100644 --- a/MPChartExample/res/menu/line.xml +++ b/MPChartExample/res/menu/line.xml @@ -13,10 +13,14 @@ android:id="@+id/actionToggleCircles" android:title="Toggle Circles"> - + + diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 0c331f1738..1b10e12188 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -242,6 +242,21 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleStepped: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawSteppedEnabled()) + set.setDrawStepped(false); + else + set.setDrawStepped(true); + } + mChart.invalidate(); + break; + } case R.id.actionTogglePinch: { if (mChart.isPinchZoomEnabled()) mChart.setPinchZoom(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 2d1f3baf29..8efad8692d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -200,6 +200,21 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleStepped: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawSteppedEnabled()) + set.setDrawStepped(false); + else + set.setDrawStepped(true); + } + mChart.invalidate(); + break; + } case R.id.actionTogglePinch: { if (mChart.isPinchZoomEnabled()) mChart.setPinchZoom(false); diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java index 0f22668500..7d9da1ac55 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java @@ -41,6 +41,9 @@ public class LineDataSet extends LineRadarDataSet implements ILineDataSet /** if true, cubic lines are drawn instead of linear */ private boolean mDrawCubic = false; + /** if true, stepped lines are drawn instead of linear */ + private boolean mDrawStepped = false; + private boolean mDrawCircleHole = true; @@ -201,6 +204,23 @@ public boolean isDrawCubicEnabled() { return mDrawCubic; } + /** + * If set to true, the linechart lines are drawn in stepped-style instead of + * linear. + * This does not work with cubic lines, of course. + * Default: false + * + * @param enabled + */ + public void setDrawStepped(boolean enabled) { + mDrawStepped = enabled; + } + + @Override + public boolean isDrawSteppedEnabled() { + return mDrawStepped; + } + /** ALL CODE BELOW RELATED TO CIRCLE-COLORS */ /** diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java index 775ab665ff..ccea3823d4 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java @@ -63,6 +63,11 @@ public class RealmLineDataSet extends RealmLineRadarDataS */ private boolean mDrawCubic = false; + /** + * if true, stepped lines are drawn instead of linear + */ + private boolean mDrawStepped = false; + private boolean mDrawCircleHole = true; /** @@ -203,6 +208,23 @@ public boolean isDrawCubicEnabled() { return mDrawCubic; } + /** + * If set to true, the linechart lines are drawn in stepped-style instead of + * linear. + * This does not work with cubic lines, of course. + * Default: false + * + * @param enabled + */ + public void setDrawStepped(boolean enabled) { + mDrawStepped = enabled; + } + + @Override + public boolean isDrawSteppedEnabled() { + return mDrawStepped; + } + /** ALL CODE BELOW RELATED TO CIRCLE-COLORS */ /** diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java index 9b7dd0558e..16a8f061cb 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java @@ -26,6 +26,13 @@ public interface ILineDataSet extends ILineRadarDataSet { */ boolean isDrawCubicEnabled(); + /** + * Returns true if drawing stepped lines is enabled, false if not. + * + * @return + */ + boolean isDrawSteppedEnabled(); + /** * Returns the size of the drawn circles. */ diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index e384d1b4da..dc2facdf14 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -275,6 +275,9 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { int entryCount = dataSet.getEntryCount(); + final boolean isDrawSteppedEnabled = dataSet.isDrawSteppedEnabled(); + final int pointsPerEntryPair = isDrawSteppedEnabled ? 4 : 2; + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); float phaseX = mAnimator.getPhaseX(); @@ -303,8 +306,8 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { // more than 1 color if (dataSet.getColors().size() > 1) { - if (mLineBuffer.length != 2 * 2) - mLineBuffer = new float[2 * 2]; + if (mLineBuffer.length != pointsPerEntryPair * 2) + mLineBuffer = new float[pointsPerEntryPair * 2]; for (int j = minx; j < count; @@ -327,8 +330,18 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { if (e == null) break; - mLineBuffer[2] = e.getXIndex(); - mLineBuffer[3] = e.getVal() * phaseY; + if (isDrawSteppedEnabled) { + mLineBuffer[2] = e.getXIndex(); + mLineBuffer[3] = mLineBuffer[1]; + mLineBuffer[4] = mLineBuffer[2]; + mLineBuffer[5] = mLineBuffer[3]; + mLineBuffer[6] = e.getXIndex(); + mLineBuffer[7] = e.getVal() * phaseY; + } else { + mLineBuffer[2] = e.getXIndex(); + mLineBuffer[3] = e.getVal() * phaseY; + } + } else { mLineBuffer[2] = mLineBuffer[0]; mLineBuffer[3] = mLineBuffer[1]; @@ -351,14 +364,13 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { // get the color that is set for this line-segment mRenderPaint.setColor(dataSet.getColor(j)); - canvas.drawLine(mLineBuffer[0], mLineBuffer[1], - mLineBuffer[2], mLineBuffer[3], mRenderPaint); + canvas.drawLines(mLineBuffer, 0, pointsPerEntryPair * 2, mRenderPaint); } } else { // only one color per dataset - if (mLineBuffer.length != Math.max((entryCount - 1) * 2, 2) * 2) - mLineBuffer = new float[Math.max((entryCount - 1) * 2, 2) * 2]; + if (mLineBuffer.length != Math.max((entryCount - 1) * pointsPerEntryPair, pointsPerEntryPair) * 2) + mLineBuffer = new float[Math.max((entryCount - 1) * pointsPerEntryPair, pointsPerEntryPair) * 2]; Entry e1, e2; @@ -373,17 +385,23 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { if (e1 == null || e2 == null) continue; - mLineBuffer[j] = e1.getXIndex(); - mLineBuffer[j + 1] = e1.getVal() * phaseY; - mLineBuffer[j + 2] = e2.getXIndex(); - mLineBuffer[j + 3] = e2.getVal() * phaseY; - j += 4; + mLineBuffer[j++] = e1.getXIndex(); + mLineBuffer[j++] = e1.getVal() * phaseY; + if (isDrawSteppedEnabled) { + mLineBuffer[j++] = e2.getXIndex(); + mLineBuffer[j++] = e1.getVal() * phaseY; + mLineBuffer[j++] = e2.getXIndex(); + mLineBuffer[j++] = e1.getVal() * phaseY; + } + + mLineBuffer[j++] = e2.getXIndex(); + mLineBuffer[j++] = e2.getVal() * phaseY; } trans.pointValuesToPixel(mLineBuffer); - final int size = Math.max((count - minx - 1) * 2, 2) * 2; + final int size = Math.max((count - minx - 1) * pointsPerEntryPair, pointsPerEntryPair) * 2; mRenderPaint.setColor(dataSet.getColor()); @@ -430,6 +448,7 @@ private Path generateFilledPath(ILineDataSet dataSet, int from, int to) { float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart); float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); + final boolean isDrawSteppedEnabled = dataSet.isDrawSteppedEnabled(); Path filled = new Path(); Entry entry = dataSet.getEntryForIndex(from); @@ -441,6 +460,14 @@ private Path generateFilledPath(ILineDataSet dataSet, int from, int to) { for (int x = from + 1, count = (int) Math.ceil((to - from) * phaseX + from); x < count; x++) { Entry e = dataSet.getEntryForIndex(x); + + if (isDrawSteppedEnabled) { + final Entry ePrev = dataSet.getEntryForIndex(x - 1); + if (ePrev == null) continue; + + filled.lineTo(e.getXIndex(), ePrev.getVal() * phaseY); + } + filled.lineTo(e.getXIndex(), e.getVal() * phaseY); } From 3995941c3cf22e8d0af0df5af4cadfb055d31f05 Mon Sep 17 00:00:00 2001 From: Leo Mehlig Date: Thu, 10 Mar 2016 13:44:16 +0100 Subject: [PATCH 012/606] Keep position on rotation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I’ve added a flag (keepPositionOnRotation) to BarLineChartBase indicating whether the chart should stay at its current position or not. --- .../charting/charts/BarLineChartBase.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index bc810343d2..5665553fc2 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -106,6 +106,11 @@ public abstract class BarLineChartBase Date: Thu, 10 Mar 2016 17:09:27 +0100 Subject: [PATCH 013/606] Add contribution file --- CONTRIBUTING.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..a6fb7cac57 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,49 @@ +# How to contribute + +Bug-fixes and features often come from users of the Charts framework, and improving 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. + +## Simple issues and bug reports + +If you are reporting a bug which can be observed visually, please add to your issue either: + +* Screenshots, if the bug is easily explainable +* A working sample project that we can compile, run, and immediately observe the issue + +## Getting Started with Contributions + +* 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 + +## Making Changes + +* 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. + +```` + First line must be up to 50 chars (Fixes #1234) + + 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. +```` + +* 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. + +## Making Trivial Changes + +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. \ No newline at end of file From a6ce45f9ac608972aec11b340c0a2cef050d796d Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 10 Mar 2016 17:20:36 +0100 Subject: [PATCH 014/606] Fix issue #1561 --- .../charting/renderer/BarChartRenderer.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java index 763fb87986..3ec7209b48 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -96,6 +96,23 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { trans.pointValuesToPixel(buffer.buffer); + // draw the bar shadow before the values + if (mChart.isDrawBarShadowEnabled()) { + + 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], mViewPortHandler.contentTop(), + buffer.buffer[j + 2], + mViewPortHandler.contentBottom(), mShadowPaint); + } + } + // if multiple colors if (dataSet.getColors().size() > 1) { @@ -107,12 +124,6 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) break; - if (mChart.isDrawBarShadowEnabled()) { - c.drawRect(buffer.buffer[j], mViewPortHandler.contentTop(), - buffer.buffer[j + 2], - mViewPortHandler.contentBottom(), mShadowPaint); - } - // Set the color for the currently drawn value. If the index // is // out of bounds, reuse colors. @@ -132,12 +143,6 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) break; - if (mChart.isDrawBarShadowEnabled()) { - c.drawRect(buffer.buffer[j], mViewPortHandler.contentTop(), - buffer.buffer[j + 2], - mViewPortHandler.contentBottom(), mShadowPaint); - } - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], buffer.buffer[j + 3], mRenderPaint); } From 21f43ed3f0e452d7598bd6f915f76628a0ca5d71 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 10 Mar 2016 23:36:30 +0100 Subject: [PATCH 015/606] Minor fix related to #1565 --- .../mikephil/charting/charts/BarLineChartBase.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index 5665553fc2..6b7ecc71b9 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -304,9 +304,12 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { //Superclass transforms chart. super.onSizeChanged(w, h, oldw, oldh); - //Restoring old position of chart. - getTransformer(AxisDependency.LEFT).pointValuesToPixel(pts); - mViewPortHandler.centerViewPort(pts, this); + if(mKeepPositionOnRotation) { + + //Restoring old position of chart. + getTransformer(AxisDependency.LEFT).pointValuesToPixel(pts); + mViewPortHandler.centerViewPort(pts, this); + } } /** @@ -1238,7 +1241,7 @@ public boolean isKeepPositionOnRotation() { } /** - * Sets the whether the chart should keep its position after a rotation. + * Sets whether the chart should keep its position (zoom / scroll) after a rotation (orientation change) */ public void setKeepPositionOnRotation(boolean keepPositionOnRotation) { mKeepPositionOnRotation = keepPositionOnRotation; From 7009cc8b967bac46a88535005aec66598d8336a4 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 10 Mar 2016 23:37:06 +0100 Subject: [PATCH 016/606] Move onSizeChanged(...) --- .../charting/charts/BarLineChartBase.java | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index 6b7ecc71b9..044fb2e5fd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -293,25 +293,6 @@ protected void onDraw(Canvas canvas) { } } - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - //Saving current position of chart. - float[] pts = new float[2]; - pts[0] = mViewPortHandler.contentLeft(); - pts[1] = mViewPortHandler.contentTop(); - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - - //Superclass transforms chart. - super.onSizeChanged(w, h, oldw, oldh); - - if(mKeepPositionOnRotation) { - - //Restoring old position of chart. - getTransformer(AxisDependency.LEFT).pointValuesToPixel(pts); - mViewPortHandler.centerViewPort(pts, this); - } - } - /** * RESET PERFORMANCE TRACKING FIELDS */ @@ -1614,4 +1595,23 @@ public Paint getPaint(int which) { return null; } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + //Saving current position of chart. + float[] pts = new float[2]; + pts[0] = mViewPortHandler.contentLeft(); + pts[1] = mViewPortHandler.contentTop(); + getTransformer(AxisDependency.LEFT).pixelsToValue(pts); + + //Superclass transforms chart. + super.onSizeChanged(w, h, oldw, oldh); + + if(mKeepPositionOnRotation) { + + //Restoring old position of chart. + getTransformer(AxisDependency.LEFT).pointValuesToPixel(pts); + mViewPortHandler.centerViewPort(pts, this); + } + } } From 07d7813ccb44ab9369c0d1d162dc861d140a1a69 Mon Sep 17 00:00:00 2001 From: Anant Singh Date: Sat, 12 Mar 2016 23:25:51 +0530 Subject: [PATCH 017/606] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6e24af64a7..90736a4aa2 100644 --- a/README.md +++ b/README.md @@ -72,14 +72,14 @@ Features - Dragging / Panning (with touch-gesture) - Combined-Charts (line-, bar-, scatter-, candle-data) - Dual (separate) Axes - - Customizeable Axes (both x- and y-axis) - - Highlighting values (with customizeable popup-views) + - 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, customizeable) + - Legends (generated automatically, customizable) - Animations (build up animations, on both x- and y-axis) - Limit lines (providing additional information, maximums, ...) - - Fully customizeable (paints, typefaces, legends, colors, background, gestures, dashed lines, ...) + - 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 - Gradle support - Plotting data directly from [**Realm.io**](https://realm.io) mobile database From f1bc0d05d9e588d35194985fca065f26e963f271 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 13 Mar 2016 13:44:10 +0100 Subject: [PATCH 018/606] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 90736a4aa2..058aa739f1 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,14 @@ Remember: *It's all about the looks.* [**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. -As an additional feature, this library allows cross-platform development between Android and iOS as an iOS version of this library is also available: [**ios-charts**](https://github.com/danielgindi/ios-charts) :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: [**ios-charts**](https://github.com/danielgindi/ios-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). Spread the word ----- -If you like this library, please tell others about it :two_hearts: +If you like this library, please tell others about it :two_hearts: :two_hearts: From 677d8ef2d45d8973af57ce106c10e17b61135aa5 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 15 Mar 2016 23:37:02 +0100 Subject: [PATCH 019/606] Add documentation --- .../charting/interfaces/datasets/ILineRadarDataSet.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java index 4ed43f03e8..ce89822716 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java @@ -47,8 +47,10 @@ public interface ILineRadarDataSet extends ILineScatterCandleRa /** * Set to true if the DataSet should be drawn filled (surface), and not just - * as a line, disabling this will give great performance boost! default: - * false + * as a line, disabling this will give great performance boost. Please note that this method + * uses the canvas.clipPath(...) method for drawing the filled area. + * For devices with API level < 18 (Android 4.3), hardware acceleration of the chart should + * be turned off. Default: false * * @param enabled */ From 71fede97b32f58219ee1fc1f8c8fc51c4d4ac5e9 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 16 Mar 2016 14:48:09 +0100 Subject: [PATCH 020/606] Fix #1584 --- .../src/com/github/mikephil/charting/data/CandleDataSet.java | 2 +- .../charting/data/realm/implementation/RealmCandleDataSet.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java index 03f09251bf..c63ccee1df 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java @@ -171,7 +171,7 @@ public float getShadowWidth() { * * @param showCandleBar */ - public void setShadowWidth(boolean showCandleBar) { + public void setShowCandleBar(boolean showCandleBar) { mShowCandleBar = showCandleBar; } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java index 8c7c9c8e78..def4e4e935 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java @@ -215,7 +215,7 @@ public float getShadowWidth() { * * @param showCandleBar */ - public void setShadowWidth(boolean showCandleBar) { + public void setShowCandleBar(boolean showCandleBar) { mShowCandleBar = showCandleBar; } From 20dd3b8ef1d6968f8160e13085d0cabdab2c9fed Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 17 Mar 2016 00:18:21 +0100 Subject: [PATCH 021/606] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 058aa739f1..5513d354e8 100644 --- a/README.md +++ b/README.md @@ -55,13 +55,14 @@ Questions & Issues 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/v2.2.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) -Please do not expect answers to your questions if you have not considered all above mentioned points in advance. +Please do not expect answers to your questions if you have not considered all above mentioned approaches in advance. Features ----- From 1ba4b413e86a9e60921c087a09fd71f45848219a Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 17 Mar 2016 09:11:11 +0100 Subject: [PATCH 022/606] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5513d354e8..78b802778e 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Questions & Issues 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). + - 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/v2.2.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) From dd50c2e49e2508d13c42eb8318756be8959b1f0b Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 17 Mar 2016 12:19:36 +0200 Subject: [PATCH 023/606] Avoid extra computed when not needed --- .../mikephil/charting/charts/BarLineChartBase.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index 044fb2e5fd..8364e804aa 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -1598,16 +1598,19 @@ public Paint getPaint(int which) { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { - //Saving current position of chart. + + // Saving current position of chart. float[] pts = new float[2]; - pts[0] = mViewPortHandler.contentLeft(); - pts[1] = mViewPortHandler.contentTop(); - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); + if (mKeepPositionOnRotation) { + pts[0] = mViewPortHandler.contentLeft(); + pts[1] = mViewPortHandler.contentTop(); + getTransformer(AxisDependency.LEFT).pixelsToValue(pts); + } //Superclass transforms chart. super.onSizeChanged(w, h, oldw, oldh); - if(mKeepPositionOnRotation) { + if (mKeepPositionOnRotation) { //Restoring old position of chart. getTransformer(AxisDependency.LEFT).pointValuesToPixel(pts); From a7da427e26ae9f759af6de6fcec27ad2f3b791ce Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 17 Mar 2016 12:41:21 +0200 Subject: [PATCH 024/606] Fixed a bug where after rotation/resize the matrix stays out of bounds This bug existed before "keepPositionOnRotation". In my opinion - keepPositionOnRotation should be default... --- .../com/github/mikephil/charting/charts/BarLineChartBase.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index 8364e804aa..a96eb08487 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -1615,6 +1615,8 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { //Restoring old position of chart. getTransformer(AxisDependency.LEFT).pointValuesToPixel(pts); mViewPortHandler.centerViewPort(pts, this); + } else { + mViewPortHandler.refresh(mViewPortHandler.getMatrixTouch(), this, true); } } } From 6347d680c8d8c7f3fa2fbf8747c2504ddbca3048 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 17 Mar 2016 15:24:59 +0200 Subject: [PATCH 025/606] Added feature for highlight circle in radar chart --- MPChartExample/res/menu/radar.xml | 4 + .../mpchartexample/RadarChartActivitry.java | 11 ++ .../mikephil/charting/data/RadarDataSet.java | 103 +++++++++++++++++- .../implementation/RealmRadarDataSet.java | 101 +++++++++++++++++ .../interfaces/datasets/IRadarDataSet.java | 19 ++++ .../charting/renderer/RadarChartRenderer.java | 59 ++++++++++ .../charting/utils/ColorTemplate.java | 4 + 7 files changed, 300 insertions(+), 1 deletion(-) diff --git a/MPChartExample/res/menu/radar.xml b/MPChartExample/res/menu/radar.xml index 8dbcf05e9c..3565065baa 100644 --- a/MPChartExample/res/menu/radar.xml +++ b/MPChartExample/res/menu/radar.xml @@ -13,6 +13,10 @@ android:id="@+id/actionToggleHighlight" android:title="Toggle Highlight"> + + diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index dbbdc695c8..e637a37efe 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -124,6 +124,17 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleHighlightCircle: { + + ArrayList sets = (ArrayList) mChart.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!", diff --git a/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java index 189ac032a9..61ea26d5a7 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java @@ -1,17 +1,118 @@ package com.github.mikephil.charting.data; +import android.graphics.Color; + import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; import java.util.ArrayList; import java.util.List; public class RadarDataSet extends LineRadarDataSet implements IRadarDataSet { - + + /// flag indicating whether highlight circle should be drawn or not + protected boolean mDrawHighlightCircleEnabled = false; + + protected int mHighlightCircleFillColor = Color.WHITE; + + /// The stroke color for highlight circle. + /// 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 float mHighlightCircleInnerRadius = 3.0f; + protected float mHighlightCircleOuterRadius = 4.0f; + protected float mHighlightCircleStrokeWidth = 2.0f; + public RadarDataSet(List yVals, String label) { super(yVals, label); } + /// Returns true if highlight circle should be drawn, false if not + @Override + public boolean isDrawHighlightCircleEnabled() + { + return mDrawHighlightCircleEnabled; + } + + /// Sets whether highlight circle should be drawn or not + @Override + public void setDrawHighlightCircleEnabled(boolean enabled) + { + mDrawHighlightCircleEnabled = enabled; + } + + @Override + public int getHighlightCircleFillColor() + { + return mHighlightCircleFillColor; + } + + 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() + { + 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) + { + mHighlightCircleStrokeColor = color; + } + + @Override + public int getHighlightCircleStrokeAlpha() + { + return mHighlightCircleStrokeAlpha; + } + + public void setHighlightCircleStrokeAlpha(int alpha) + { + mHighlightCircleStrokeAlpha = alpha; + } + + @Override + public float getHighlightCircleInnerRadius() + { + return mHighlightCircleInnerRadius; + } + + public void setHighlightCircleInnerRadius(float radius) + { + mHighlightCircleInnerRadius = radius; + } + + @Override + public float getHighlightCircleOuterRadius() + { + return mHighlightCircleOuterRadius; + } + + public void setHighlightCircleOuterRadius(float radius) + { + mHighlightCircleOuterRadius = radius; + } + + @Override + public float getHighlightCircleStrokeWidth() + { + return mHighlightCircleStrokeWidth; + } + + public void setHighlightCircleStrokeWidth(float strokeWidth) + { + mHighlightCircleStrokeWidth = strokeWidth; + } + @Override public DataSet copy() { diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java index 2c0d8457a8..aa2dc2fdfc 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java @@ -1,7 +1,10 @@ package com.github.mikephil.charting.data.realm.implementation; +import android.graphics.Color; + import com.github.mikephil.charting.data.realm.base.RealmLineRadarDataSet; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; import io.realm.RealmObject; import io.realm.RealmResults; @@ -11,6 +14,20 @@ */ public class RealmRadarDataSet extends RealmLineRadarDataSet implements IRadarDataSet { + /// flag indicating whether highlight circle should be drawn or not + protected boolean mDrawHighlightCircleEnabled = false; + + protected int mHighlightCircleFillColor = Color.WHITE; + + /// The stroke color for highlight circle. + /// 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 float mHighlightCircleInnerRadius = 3.0f; + protected float mHighlightCircleOuterRadius = 4.0f; + protected float mHighlightCircleStrokeWidth = 2.0f; + /** * Constructor for creating a RadarDataSet with realm data. * @@ -38,6 +55,90 @@ public RealmRadarDataSet(RealmResults result, String yValuesField, String xIn calcMinMax(0, results.size()); } + /// Returns true if highlight circle should be drawn, false if not + @Override + public boolean isDrawHighlightCircleEnabled() + { + return mDrawHighlightCircleEnabled; + } + + /// Sets whether highlight circle should be drawn or not + @Override + public void setDrawHighlightCircleEnabled(boolean enabled) + { + mDrawHighlightCircleEnabled = enabled; + } + + @Override + public int getHighlightCircleFillColor() + { + return mHighlightCircleFillColor; + } + + 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() + { + 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) + { + mHighlightCircleStrokeColor = color; + } + + @Override + public int getHighlightCircleStrokeAlpha() + { + return mHighlightCircleStrokeAlpha; + } + + public void setHighlightCircleStrokeAlpha(int alpha) + { + mHighlightCircleStrokeAlpha = alpha; + } + + @Override + public float getHighlightCircleInnerRadius() + { + return mHighlightCircleInnerRadius; + } + + public void setHighlightCircleInnerRadius(float radius) + { + mHighlightCircleInnerRadius = radius; + } + + @Override + public float getHighlightCircleOuterRadius() + { + return mHighlightCircleOuterRadius; + } + + public void setHighlightCircleOuterRadius(float radius) + { + mHighlightCircleOuterRadius = radius; + } + + @Override + public float getHighlightCircleStrokeWidth() + { + return mHighlightCircleStrokeWidth; + } + + public void setHighlightCircleStrokeWidth(float strokeWidth) + { + mHighlightCircleStrokeWidth = strokeWidth; + } + @Override public void build(RealmResults results) { super.build(results); diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java index 22ea37dc72..6d725df7d0 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java @@ -7,5 +7,24 @@ */ public interface IRadarDataSet extends ILineRadarDataSet { + /// flag indicating whether highlight circle should be drawn or not + boolean isDrawHighlightCircleEnabled(); + + /// Sets whether highlight circle should be drawn or not + void setDrawHighlightCircleEnabled(boolean enabled); + + int getHighlightCircleFillColor(); + + /// The stroke color for highlight circle. + /// If Utils.COLOR_NONE, the color of the dataset is taken. + int getHighlightCircleStrokeColor(); + + int getHighlightCircleStrokeAlpha(); + + float getHighlightCircleInnerRadius(); + + float getHighlightCircleOuterRadius(); + + float getHighlightCircleStrokeWidth(); } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java index 3888df9bef..03a333a616 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java @@ -14,6 +14,7 @@ import com.github.mikephil.charting.data.RadarData; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -25,6 +26,7 @@ public class RadarChartRenderer extends LineRadarRenderer { * paint for drawing the web */ protected Paint mWebPaint; + protected Paint mHighlightCirclePaint; public RadarChartRenderer(RadarChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { @@ -38,6 +40,8 @@ public RadarChartRenderer(RadarChart chart, ChartAnimator animator, mWebPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mWebPaint.setStyle(Paint.Style.STROKE); + + mHighlightCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); } public Paint getWebPaint() { @@ -289,7 +293,62 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { // draw the lines drawHighlightLines(c, pts, set); + + if (set.isDrawHighlightCircleEnabled()) { + + if (!Float.isNaN(pts[0]) && !Float.isNaN(pts[1])) { + + int strokeColor = set.getHighlightCircleStrokeColor(); + if (strokeColor == ColorTemplate.COLOR_NONE) { + strokeColor = set.getColor(0); + } + + if (set.getHighlightCircleStrokeAlpha() < 255) { + strokeColor = ColorTemplate.getColorWithAlphaComponent(strokeColor, set.getHighlightCircleStrokeAlpha()); + } + + drawHighlightCircle(c, + p, + set.getHighlightCircleInnerRadius(), + set.getHighlightCircleOuterRadius(), + set.getHighlightCircleFillColor(), + strokeColor, + set.getHighlightCircleStrokeWidth()); + } + } } } + public void drawHighlightCircle(Canvas c, + PointF point, + float innerRadius, + float outerRadius, + int fillColor, + int strokeColor, + float strokeWidth) { + c.save(); + + outerRadius = Utils.convertDpToPixel(outerRadius); + innerRadius = Utils.convertDpToPixel(innerRadius); + + if (fillColor != ColorTemplate.COLOR_NONE) { + Path p = new Path(); + p.addCircle(point.x, point.y, outerRadius, Path.Direction.CW); + if (innerRadius > 0.f) { + p.addCircle(point.x, point.y, innerRadius, Path.Direction.CCW); + } + mHighlightCirclePaint.setColor(fillColor); + mHighlightCirclePaint.setStyle(Paint.Style.FILL); + c.drawPath(p, mHighlightCirclePaint); + } + + if (strokeColor != ColorTemplate.COLOR_NONE) { + mHighlightCirclePaint.setColor(strokeColor); + mHighlightCirclePaint.setStyle(Paint.Style.STROKE); + mHighlightCirclePaint.setStrokeWidth(Utils.convertDpToPixel(strokeWidth)); + c.drawCircle(point.x, point.y, outerRadius, mHighlightCirclePaint); + } + + c.restore(); + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java b/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java index 441d77b484..f881d11629 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java @@ -75,6 +75,10 @@ public static int getHoloBlue() { return Color.rgb(51, 181, 229); } + public static int getColorWithAlphaComponent(int color, int alpha) { + return (color & 0xffffff) | ((alpha & 0xff) << 24); + } + /** * turn an array of resource-colors (contains resource-id integers) into an * array list of actual color integers From d430dd61fba373fcf425b9474596657627e13a45 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 17 Mar 2016 16:21:32 +0200 Subject: [PATCH 026/606] Prevent repeated y-axis values by controlling granularity --- .../mpchartexample/LineChartActivity2.java | 2 + .../mikephil/charting/components/YAxis.java | 43 +++++++++++++++++++ .../charting/renderer/YAxisRenderer.java | 8 ++++ 3 files changed, 53 insertions(+) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 8efad8692d..d7df678923 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -114,6 +114,7 @@ protected void onCreate(Bundle savedInstanceState) { leftAxis.setAxisMaxValue(200f); leftAxis.setAxisMinValue(0f); leftAxis.setDrawGridLines(true); + leftAxis.setGranularityEnabled(true); YAxis rightAxis = mChart.getAxisRight(); rightAxis.setTypeface(tf); @@ -122,6 +123,7 @@ protected void onCreate(Bundle savedInstanceState) { rightAxis.setAxisMinValue(-200); rightAxis.setDrawGridLines(false); rightAxis.setDrawZeroLine(false); + rightAxis.setGranularityEnabled(false); } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java index ce0f19f0a8..bebaffe5c8 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java @@ -123,6 +123,19 @@ public enum YAxisLabelPosition { */ private AxisDependency mAxisDependency; + /** + * When true, axis labels are controlled by the `granularity` property. + * When false, axis values could possibly be repeated. + * This could happen if two adjacent axis values are rounded to same value. + * If using granularity this could be avoided by having fewer axis values visible. + */ + protected boolean mGranularityEnabled = true; + + /** + * the minimum interval between axis values + */ + protected float mGranularity = 1.0f; + /** * Enum that specifies the axis a DataSet should be plotted against, either LEFT or RIGHT. * @@ -148,6 +161,36 @@ public AxisDependency getAxisDependency() { return mAxisDependency; } + /** + * @return true if granularity is enabled + */ + public boolean isGranularityEnabled() { + return mGranularityEnabled; + } + + /** + * Enabled/disable granularity control on axis value intervals + * @param enabled + */ + public void setGranularityEnabled(boolean enabled) { + mGranularityEnabled = true; + } + + /** + * @return the minimum interval between axis values + */ + public float getGranularity() { + return mGranularity; + } + + /** + * set the minimum interval between axis values + * @param granularity + */ + public void setGranularity(float granularity) { + mGranularity = granularity; + } + /** * returns the position of the y-labels */ diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java index cfd46fed07..b9a03ef8ec 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -85,8 +85,16 @@ protected void computeAxisValues(float min, float max) { return; } + // Find out how much spacing (in y value space) between axis values double rawInterval = range / labelCount; double interval = Utils.roundToNextSignificant(rawInterval); + + // If granularity is enabled, then do not allow the interval to go below specified granularity. + // This is used to avoid repeated values when rounding values for display. + if (mYAxis.isGranularityEnabled()) + interval = interval < mYAxis.getGranularity() ? mYAxis.getGranularity() : interval; + + // Normalize interval double intervalMagnitude = Math.pow(10, (int) Math.log10(interval)); int intervalSigDigit = (int) (interval / intervalMagnitude); if (intervalSigDigit > 5) { From a19ffad0461b15388e2a631198c93c5aad5c7e14 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 17 Mar 2016 17:15:34 +0200 Subject: [PATCH 027/606] Added feature for min/max width limits on yAxis --- .../mikephil/charting/components/YAxis.java | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java index bebaffe5c8..dd23bb8d9a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java @@ -123,6 +123,20 @@ public enum YAxisLabelPosition { */ private AxisDependency mAxisDependency; + /** + * the minimum width that the axis should take (in dp). + * + * default: 0.0 + */ + protected float mMinWidth = 0.f; + + /** + * the maximum width that the axis can take (in dp). + * use Inifinity for disabling the maximum + * default: Float.POSITIVE_INFINITY (no maximum specified) + */ + protected float mMaxWidth = Float.POSITIVE_INFINITY; + /** * When true, axis labels are controlled by the `granularity` property. * When false, axis values could possibly be repeated. @@ -161,6 +175,36 @@ public AxisDependency getAxisDependency() { return mAxisDependency; } + /** + * @return the minimum width that the axis should take (in dp). + */ + public float getMinWidth() { + return mMinWidth; + } + + /** + * Sets the minimum width that the axis should take (in dp). + * @param minWidth + */ + public void setMinWidth(float minWidth) { + mMinWidth = minWidth; + } + + /** + * @return the maximum width that the axis can take (in dp). + */ + public float getMaxWidth() { + return mMaxWidth; + } + + /** + * Sets the maximum width that the axis can take (in dp). + * @param maxWidth + */ + public void setMaxWidth(float maxWidth) { + mMaxWidth = maxWidth; + } + /** * @return true if granularity is enabled */ @@ -447,7 +491,20 @@ public float getRequiredWidthSpace(Paint p) { p.setTextSize(mTextSize); String label = getLongestLabel(); - return (float) Utils.calcTextWidth(p, label) + getXOffset() * 2f; + float width = (float) Utils.calcTextWidth(p, label) + getXOffset() * 2f; + + float minWidth = getMinWidth(); + float maxWidth = getMaxWidth(); + + if (minWidth > 0.f) + minWidth = Utils.convertDpToPixel(minWidth); + + if (maxWidth > 0.f && maxWidth != Float.POSITIVE_INFINITY) + maxWidth = Utils.convertDpToPixel(maxWidth); + + width = Math.max(minWidth, Math.min(width, maxWidth > 0.0 ? maxWidth : width)); + + return width; } /** From f002a3a9cc562b3883e226ed7e09765c10610ada Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 17 Mar 2016 17:53:36 +0200 Subject: [PATCH 028/606] Fixed cubic line "leak" on first point. Since lately we've done many fixes to the calculation for first/last points in cubic drawing - we not do not need that first "add curve". Related issue: https://github.com/danielgindi/ios-charts/issues/683 --- .../mikephil/charting/renderer/LineChartRenderer.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index dc2facdf14..edf3b99b73 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -160,17 +160,6 @@ protected void drawCubic(Canvas c, ILineDataSet dataSet) { // let the spline start cubicPath.moveTo(cur.getXIndex(), cur.getVal() * phaseY); - prevDx = (cur.getXIndex() - prev.getXIndex()) * intensity; - prevDy = (cur.getVal() - prev.getVal()) * intensity; - - curDx = (next.getXIndex() - cur.getXIndex()) * intensity; - curDy = (next.getVal() - cur.getVal()) * intensity; - - // the first cubic - cubicPath.cubicTo(prev.getXIndex() + prevDx, (prev.getVal() + prevDy) * phaseY, - cur.getXIndex() - curDx, - (cur.getVal() - curDy) * phaseY, cur.getXIndex(), cur.getVal() * phaseY); - for (int j = minx + 1, count = Math.min(size, entryCount - 1); j < count; j++) { prevPrev = dataSet.getEntryForIndex(j == 1 ? 0 : j - 2); From 06e63af1e1927a99c424b0da4957bdef7849cd3b Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 17 Mar 2016 22:14:47 +0200 Subject: [PATCH 029/606] Do not let slice spacing make the chart go haywire. (Fixes #1582) If the user provides data that does not allow the spacing to stay even - he will need to manually fix that (i.e by giving a larger inner hole) Even with a hole - with very small slices it will not help. Spacing could be reduces, and small slices can be removed from the dataset. --- .../charting/renderer/PieChartRenderer.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java index a725c2afbf..227337e517 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -213,6 +213,8 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { if (!mChart.needsHighlight(e.getXIndex(), mChart.getData().getIndexOfDataSet(dataSet))) { + final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; + mRenderPaint.setColor(dataSet.getColor(j)); final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? @@ -254,9 +256,9 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { center.y + innerRadius); if (drawInnerArc && - (innerRadius > 0.f || sliceSpace > 0.f)) { + (innerRadius > 0.f || accountForSliceSpacing)) { - if (sliceSpace > 0.f) { + if (accountForSliceSpacing) { float minSpacedRadius = calculateMinimumRadiusForSpacedSlice( center, radius, @@ -301,7 +303,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { else { if (sweepAngleOuter % 360f != 0.f) { - if (sliceSpace > 0.f) { + if (accountForSliceSpacing) { float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f; @@ -621,6 +623,8 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { highlightedCircleBox.set(mChart.getCircleBox()); highlightedCircleBox.inset(-shift, -shift); + final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; + mRenderPaint.setColor(set.getColor(xIndex)); final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? @@ -664,7 +668,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } float sliceSpaceRadius = 0.f; - if (sliceSpace > 0.f) { + if (accountForSliceSpacing) { sliceSpaceRadius = calculateMinimumRadiusForSpacedSlice( center, radius, @@ -683,9 +687,9 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { center.y + innerRadius); if (drawInnerArc && - (innerRadius > 0.f || sliceSpace > 0.f)) { + (innerRadius > 0.f || accountForSliceSpacing)) { - if (sliceSpace > 0.f) { + if (accountForSliceSpacing) { float minSpacedRadius = sliceSpaceRadius; if (minSpacedRadius < 0.f) @@ -725,7 +729,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (sweepAngleOuter % 360f != 0.f) { - if (sliceSpace > 0.0) { + if (accountForSliceSpacing) { final float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f; final float arcEndPointX = center.x + From 1eebf84001a9ce438bdceb58a4b4937d028d717c Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 18 Mar 2016 12:00:42 +0100 Subject: [PATCH 030/606] Change zero line drawing default to false --- .../src/com/github/mikephil/charting/components/YAxis.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java index dd23bb8d9a..60cf2f7d95 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java @@ -66,7 +66,7 @@ public class YAxis extends AxisBase { /** * flag that indicates if the zero-line should be drawn regardless of other grid lines */ - protected boolean mDrawZeroLine = true; + protected boolean mDrawZeroLine = false; /** * Color of the zero line @@ -446,7 +446,7 @@ public boolean isDrawZeroLineEnabled() { /** * Set this to true to draw the zero-line regardless of weather other - * grid-lines are enabled or not. + * grid-lines are enabled or not. Default: false * * @param mDrawZeroLine */ From 7e7b6e397e07f8c5afb0bac1b9ccc7f3abcbc276 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 22 Mar 2016 23:27:38 +0100 Subject: [PATCH 031/606] Add documentation, set granularity in example --- .../xxmassdeveloper/mpchartexample/BarChartActivitySinus.java | 2 ++ .../src/com/github/mikephil/charting/components/YAxis.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index 62d9b43096..6ed4009640 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -89,6 +89,7 @@ protected void onCreate(Bundle savedInstanceState) { leftAxis.setLabelCount(6, false); leftAxis.setAxisMinValue(-2.5f); leftAxis.setAxisMaxValue(2.5f); + leftAxis.setGranularity(0.1f); YAxis rightAxis = mChart.getAxisRight(); rightAxis.setDrawGridLines(false); @@ -96,6 +97,7 @@ protected void onCreate(Bundle savedInstanceState) { rightAxis.setLabelCount(6, false); rightAxis.setAxisMinValue(-2.5f); rightAxis.setAxisMaxValue(2.5f); + rightAxis.setGranularity(0.1f); mSeekBarX.setOnSeekBarChangeListener(this); mSeekBarX.setProgress(150); // set data diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java index 60cf2f7d95..dc995a3c46 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java @@ -228,7 +228,7 @@ public float getGranularity() { } /** - * set the minimum interval between axis values + * Set the minimum interval between axis values. This can be used to avoid label duplicating when zooming in. * @param granularity */ public void setGranularity(float granularity) { From 303ea3d764ef3e755fb0b9e09111fe5177ff563b Mon Sep 17 00:00:00 2001 From: = Date: Fri, 25 Mar 2016 00:04:16 +0100 Subject: [PATCH 032/606] Change values or SKIP and NONE color --- .../src/com/github/mikephil/charting/utils/ColorTemplate.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java b/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java index f881d11629..0818d05a9f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java @@ -19,13 +19,13 @@ public class ColorTemplate { /** * an "invalid" color that indicates that no color is set */ - public static final int COLOR_NONE = -1; + public static final int COLOR_NONE = 0x00112233; /** * this "color" is used for the Legend creation and indicates that the next * form should be skipped */ - public static final int COLOR_SKIP = -2; + public static final int COLOR_SKIP = 0x00112234; /** * THE COLOR THEMES ARE PREDEFINED (predefined color integer arrays), FEEL From fc72fbece172eb8df94ab8bca0777045def7010b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 25 Mar 2016 22:19:51 +0100 Subject: [PATCH 033/606] Fix #1323 --- .../com/github/mikephil/charting/renderer/YAxisRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java index b9a03ef8ec..f1d8f8288e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -95,7 +95,7 @@ protected void computeAxisValues(float min, float max) { interval = interval < mYAxis.getGranularity() ? mYAxis.getGranularity() : interval; // Normalize interval - double intervalMagnitude = Math.pow(10, (int) Math.log10(interval)); + 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 From 0d1894770ce889a20a3d90108b5a72818a5d0bca Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 25 Mar 2016 22:50:03 +0100 Subject: [PATCH 034/606] Fix issue #1601 --- .../charting/renderer/LineRadarRenderer.java | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineRadarRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineRadarRenderer.java index bea77bdad9..1121e37966 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineRadarRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineRadarRenderer.java @@ -1,7 +1,10 @@ package com.github.mikephil.charting.renderer; import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; @@ -24,16 +27,22 @@ public LineRadarRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler * @param drawable */ protected void drawFilledPath(Canvas c, Path filledPath, Drawable drawable) { - c.save(); - c.clipPath(filledPath); - drawable.setBounds((int) mViewPortHandler.contentLeft(), - (int) mViewPortHandler.contentTop(), - (int) mViewPortHandler.contentRight(), - (int) mViewPortHandler.contentBottom()); - drawable.draw(c); + if (clipPathSupported()) { - c.restore(); + c.save(); + c.clipPath(filledPath); + + drawable.setBounds((int) mViewPortHandler.contentLeft(), + (int) mViewPortHandler.contentTop(), + (int) mViewPortHandler.contentRight(), + (int) mViewPortHandler.contentBottom()); + drawable.draw(c); + + c.restore(); + } else { + throw new RuntimeException("Fill-drawables not (yet) supported below API level 18"); + } } /** @@ -46,11 +55,35 @@ protected void drawFilledPath(Canvas c, Path filledPath, Drawable drawable) { * @param fillAlpha */ protected void drawFilledPath(Canvas c, Path filledPath, int fillColor, int fillAlpha) { - c.save(); - c.clipPath(filledPath); int color = (fillAlpha << 24) | (fillColor & 0xffffff); - c.drawColor(color); - c.restore(); + + if (clipPathSupported()) { + + c.save(); + c.clipPath(filledPath); + + c.drawColor(color); + c.restore(); + } else { + + // save + Paint.Style previous = mRenderPaint.getStyle(); + int previousColor = mRenderPaint.getColor(); + + // set + mRenderPaint.setStyle(Paint.Style.FILL); + mRenderPaint.setColor(color); + + c.drawPath(filledPath, mRenderPaint); + + // restore + mRenderPaint.setColor(previousColor); + mRenderPaint.setStyle(previous); + } + } + + private boolean clipPathSupported() { + return android.os.Build.VERSION.SDK_INT >= 18; } } From 96c518d3d96f780f472c6b86e8eaca6bd230f677 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 26 Mar 2016 11:16:31 +0100 Subject: [PATCH 035/606] Provide version checks --- .../mpchartexample/LineChartActivity1.java | 9 +++++++-- .../mikephil/charting/animation/ChartAnimator.java | 1 - .../mikephil/charting/data/LineRadarDataSet.java | 2 ++ .../mikephil/charting/renderer/LineRadarRenderer.java | 11 +++++++++-- .../src/com/github/mikephil/charting/utils/Utils.java | 3 +++ 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 1b10e12188..e3ea20a844 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -32,6 +32,7 @@ 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; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -354,10 +355,14 @@ private void setData(int count, float range) { set1.setCircleRadius(3f); set1.setDrawCircleHole(false); set1.setValueTextSize(9f); - Drawable drawable = ContextCompat.getDrawable(this, R.drawable.fade_red); - set1.setFillDrawable(drawable); set1.setDrawFilled(true); + 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); + } + ArrayList dataSets = new ArrayList(); dataSets.add(set1); // add the datasets diff --git a/MPChartLib/src/com/github/mikephil/charting/animation/ChartAnimator.java b/MPChartLib/src/com/github/mikephil/charting/animation/ChartAnimator.java index b325eba738..639442a4c9 100644 --- a/MPChartLib/src/com/github/mikephil/charting/animation/ChartAnimator.java +++ b/MPChartLib/src/com/github/mikephil/charting/animation/ChartAnimator.java @@ -11,7 +11,6 @@ * * @author Philipp Jahoda */ -@SuppressLint("NewApi") public class ChartAnimator { /** object that is updated upon animation update */ diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineRadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/LineRadarDataSet.java index 2289113dbb..deced96ee9 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineRadarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/LineRadarDataSet.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.data; +import android.annotation.TargetApi; import android.graphics.Color; import android.graphics.drawable.Drawable; @@ -72,6 +73,7 @@ public Drawable getFillDrawable() { * * @param drawable */ + @TargetApi(18) public void setFillDrawable(Drawable drawable) { this.mFillDrawable = drawable; } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineRadarRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineRadarRenderer.java index 1121e37966..f29c77513e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineRadarRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineRadarRenderer.java @@ -8,6 +8,7 @@ import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; /** @@ -41,7 +42,8 @@ protected void drawFilledPath(Canvas c, Path filledPath, Drawable drawable) { c.restore(); } else { - throw new RuntimeException("Fill-drawables not (yet) supported below API level 18"); + throw new RuntimeException("Fill-drawables not (yet) supported below API level 18, " + + "this code was run on API level " + Utils.getSDKInt() + "."); } } @@ -83,7 +85,12 @@ protected void drawFilledPath(Canvas c, Path filledPath, int fillColor, int fill } } + /** + * Clip path with hardware acceleration only working properly on API level 18 and above. + * + * @return + */ private boolean clipPathSupported() { - return android.os.Build.VERSION.SDK_INT >= 18; + return Utils.getSDKInt() >= 18; } } diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java index b8e6cd7964..ca19784347 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java @@ -710,4 +710,7 @@ public static FSize getSizeOfRotatedRectangleByRadians(float rectangleWidth, flo ); } + public static int getSDKInt() { + return android.os.Build.VERSION.SDK_INT; + } } From 893994526753c6db1cdc57ab5125dfad50cc48dc Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 26 Mar 2016 19:02:13 +0100 Subject: [PATCH 036/606] Refactoring and change of axis min/max calculation and custom axis values --- .../charting/charts/BarLineChartBase.java | 52 +------------- .../mikephil/charting/charts/RadarChart.java | 25 +------ .../mikephil/charting/components/YAxis.java | 69 +++++++++++++++---- .../renderer/YAxisRendererRadarChart.java | 2 +- 4 files changed, 60 insertions(+), 88 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index a96eb08487..d2c78579cc 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -353,58 +353,12 @@ protected void calcMinMax() { if (mAutoScaleMinMaxEnabled) mData.calcMinMax(getLowestVisibleXIndex(), getHighestVisibleXIndex()); - float minLeft = !Float.isNaN(mAxisLeft.getAxisMinValue()) - ? mAxisLeft.getAxisMinValue() - : mData.getYMin(AxisDependency.LEFT); - float maxLeft = !Float.isNaN(mAxisLeft.getAxisMaxValue()) - ? mAxisLeft.getAxisMaxValue() - : mData.getYMax(AxisDependency.LEFT); - float minRight = !Float.isNaN(mAxisRight.getAxisMinValue()) - ? mAxisRight.getAxisMinValue() - : mData.getYMin(AxisDependency.RIGHT); - float maxRight = !Float.isNaN(mAxisRight.getAxisMaxValue()) - ? mAxisRight.getAxisMaxValue() - : mData.getYMax(AxisDependency.RIGHT); - - float leftRange = Math.abs(maxLeft - minLeft); - float rightRange = Math.abs(maxRight - minRight); - - // in case all values are equal - if (leftRange == 0f) { - maxLeft = maxLeft + 1f; - minLeft = minLeft - 1f; - } - - if (rightRange == 0f) { - maxRight = maxRight + 1f; - minRight = minRight - 1f; - } - - float topSpaceLeft = leftRange / 100f * mAxisLeft.getSpaceTop(); - float topSpaceRight = rightRange / 100f * mAxisRight.getSpaceTop(); - float bottomSpaceLeft = leftRange / 100f * mAxisLeft.getSpaceBottom(); - float bottomSpaceRight = rightRange / 100f * mAxisRight.getSpaceBottom(); - mXChartMax = mData.getXVals().size() - 1; mDeltaX = Math.abs(mXChartMax - mXChartMin); - // Use the values as they are - mAxisLeft.mAxisMinimum = !Float.isNaN(mAxisLeft.getAxisMinValue()) - ? mAxisLeft.getAxisMinValue() - : (minLeft - bottomSpaceLeft); - mAxisLeft.mAxisMaximum = !Float.isNaN(mAxisLeft.getAxisMaxValue()) - ? mAxisLeft.getAxisMaxValue() - : (maxLeft + topSpaceLeft); - - mAxisRight.mAxisMinimum = !Float.isNaN(mAxisRight.getAxisMinValue()) - ? mAxisRight.getAxisMinValue() - : (minRight - bottomSpaceRight); - mAxisRight.mAxisMaximum = !Float.isNaN(mAxisRight.getAxisMaxValue()) - ? mAxisRight.getAxisMaxValue() - : (maxRight + topSpaceRight); - - mAxisLeft.mAxisRange = Math.abs(mAxisLeft.mAxisMaximum - mAxisLeft.mAxisMinimum); - mAxisRight.mAxisRange = Math.abs(mAxisRight.mAxisMaximum - mAxisRight.mAxisMinimum); + // calculate axis range (min / max) according to provided data + mAxisLeft.calcMinMax(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); + mAxisRight.calcMinMax(mData.getYMin(AxisDependency.RIGHT), mData.getYMax(AxisDependency.RIGHT)); } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java index ea012e7abe..a3f9820f42 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java @@ -107,33 +107,10 @@ protected void init() { protected void calcMinMax() { super.calcMinMax(); - float minLeft = !Float.isNaN(mYAxis.getAxisMinValue()) - ? mYAxis.getAxisMinValue() - : mData.getYMin(AxisDependency.LEFT); - float maxLeft = !Float.isNaN(mYAxis.getAxisMaxValue()) - ? mYAxis.getAxisMaxValue() - : mData.getYMax(AxisDependency.LEFT); - mXChartMax = mData.getXVals().size() - 1; mDeltaX = Math.abs(mXChartMax - mXChartMin); - float leftRange = Math.abs(maxLeft - minLeft); - - float topSpaceLeft = leftRange / 100f * mYAxis.getSpaceTop(); - float bottomSpaceLeft = leftRange / 100f * mYAxis.getSpaceBottom(); - - mXChartMax = mData.getXVals().size() - 1; - mDeltaX = Math.abs(mXChartMax - mXChartMin); - - // Use the values as they are - mYAxis.mAxisMinimum = !Float.isNaN(mYAxis.getAxisMinValue()) - ? mYAxis.getAxisMinValue() - : (minLeft - bottomSpaceLeft); - mYAxis.mAxisMaximum = !Float.isNaN(mYAxis.getAxisMaxValue()) - ? mYAxis.getAxisMaxValue() - : (maxLeft + topSpaceLeft); - - mYAxis.mAxisRange = Math.abs(mYAxis.mAxisMaximum - mYAxis.mAxisMinimum); + mYAxis.calcMinMax(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java index dc995a3c46..1ea8d946d6 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java @@ -79,14 +79,14 @@ public class YAxis extends AxisBase { protected float mZeroLineWidth = 1f; /** - * custom minimum value this axis represents + * flag indicating that the axis-min value has been customized */ - protected float mCustomAxisMin = Float.NaN; + protected boolean mCustomAxisMin = false; /** - * custom maximum value this axis represents + * flag indicating that the axis-max value has been customized */ - protected float mCustomAxisMax = Float.NaN; + protected boolean mCustomAxisMax = false; /** * axis space from the largest value to the top in percent of the total axis range @@ -359,10 +359,6 @@ public void setStartAtZero(boolean startAtZero) { resetAxisMinValue(); } - public float getAxisMinValue() { - return mCustomAxisMin; - } - /** * Set a custom minimum value for this axis. If set, this value will not be calculated automatically depending on * the provided data. Use resetAxisMinValue() to undo this. Do not forget to call setStartAtZero(false) if you use @@ -371,7 +367,8 @@ public float getAxisMinValue() { * @param min */ public void setAxisMinValue(float min) { - mCustomAxisMin = min; + mCustomAxisMin = true; + mAxisMinimum = min; } /** @@ -379,11 +376,11 @@ public void setAxisMinValue(float min) { * done automatically. */ public void resetAxisMinValue() { - mCustomAxisMin = Float.NaN; + mCustomAxisMin = false; } - public float getAxisMaxValue() { - return mCustomAxisMax; + public boolean isAxisMinCustom() { + return mCustomAxisMin; } /** @@ -393,7 +390,8 @@ public float getAxisMaxValue() { * @param max */ public void setAxisMaxValue(float max) { - mCustomAxisMax = max; + mCustomAxisMax = true; + mAxisMaximum = max; } /** @@ -401,7 +399,11 @@ public void setAxisMaxValue(float max) { * done automatically. */ public void resetAxisMaxValue() { - mCustomAxisMax = Float.NaN; + mCustomAxisMax = false; + } + + public boolean isAxisMaxCustom() { + return mCustomAxisMax; } /** @@ -604,4 +606,43 @@ public boolean needsOffset() { else return false; } + + /** + * Calculates the minimum, maximum and range values of the YAxis with the given + * minimum and maximum values from the chart data. + * @param dataMin the y-min value according to chart data + * @param dataMax the y-max value according to chart data + */ + public void calcMinMax(float dataMin, float dataMax) { + + // if custom, use value as is, else use data value + float min = mCustomAxisMin ? mAxisMinimum : dataMin; + float max = mCustomAxisMax ? mAxisMaximum : dataMax; + + // temporary range (before calculations) + float range = Math.abs(max - min); + + // in case all values are equal + if (range == 0f) { + max = max + 1f; + 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); + } + + // calc actual range + this.mAxisRange = Math.abs(this.mAxisMaximum - this.mAxisMinimum); + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java index 1dbefe08ae..66ddd3ceea 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -97,7 +97,7 @@ protected void computeAxisValues(float min, float max) { ++n; } - if (Float.isNaN(mYAxis.getAxisMaxValue())) + if (!mYAxis.isAxisMaxCustom()) n += 1; mYAxis.mEntryCount = n; From 676e148ea1c5db07232f983057f9156c4e4915a3 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 26 Mar 2016 19:11:16 +0100 Subject: [PATCH 037/606] Move axis min/max related code to AxisBase --- .../charting/components/AxisBase.java | 84 ++++++++++++++++++- .../mikephil/charting/components/YAxis.java | 65 -------------- 2 files changed, 83 insertions(+), 66 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java index 26f26b361f..bbaebf506c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java @@ -55,6 +55,24 @@ public abstract class AxisBase extends ComponentBase { */ protected boolean mDrawLimitLineBehindData = false; + /** + * flag indicating that the axis-min value has been customized + */ + protected boolean mCustomAxisMin = false; + + /** + * flag indicating that the axis-max value has been customized + */ + protected boolean mCustomAxisMax = false; + + public float mAxisMaximum = 0f; + public float mAxisMinimum = 0f; + + /** + * the total range of values this axis covers + */ + public float mAxisRange = 0f; + /** * default constructor */ @@ -206,7 +224,8 @@ public void addLimitLine(LimitLine l) { if (mLimitLines.size() > 6) { Log.e("MPAndroiChart", - "Warning! You have more than 6 LimitLines on your axis, do you really want that?"); + "Warning! You have more than 6 LimitLines on your axis, do you really want " + + "that?"); } } @@ -296,4 +315,67 @@ public boolean isGridDashedLineEnabled() { public DashPathEffect getGridDashPathEffect() { return mGridDashPathEffect; } + + /** + * ###### BELOW CODE RELATED TO CUSTOM AXIS VALUES ###### + */ + + /** + * By calling this method, any custom maximum value that has been previously set is reseted, + * and the calculation is + * done automatically. + */ + public void resetAxisMaxValue() { + mCustomAxisMax = false; + } + + /** + * Returns true if the axis max value has been customized (and is not calculated automatically) + * + * @return + */ + public boolean isAxisMaxCustom() { + return mCustomAxisMax; + } + + /** + * By calling this method, any custom minimum value that has been previously set is reseted, + * and the calculation is + * done automatically. + */ + public void resetAxisMinValue() { + mCustomAxisMin = false; + } + + /** + * Returns true if the axis min value has been customized (and is not calculated automatically) + * + * @return + */ + public boolean isAxisMinCustom() { + return mCustomAxisMin; + } + + /** + * Set a custom minimum value for this axis. If set, this value will not be calculated automatically depending on + * the provided data. Use resetAxisMinValue() to undo this. Do not forget to call setStartAtZero(false) if you use + * this method. Otherwise, the axis-minimum value will still be forced to 0. + * + * @param min + */ + public void setAxisMinValue(float min) { + mCustomAxisMin = true; + mAxisMinimum = min; + } + + /** + * Set a custom maximum value for this axis. If set, this value will not be calculated automatically depending on + * the provided data. Use resetAxisMaxValue() to undo this. + * + * @param max + */ + public void setAxisMaxValue(float max) { + mCustomAxisMax = true; + mAxisMaximum = max; + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java index 1ea8d946d6..6e8c0486f4 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java @@ -78,16 +78,6 @@ public class YAxis extends AxisBase { */ protected float mZeroLineWidth = 1f; - /** - * flag indicating that the axis-min value has been customized - */ - protected boolean mCustomAxisMin = false; - - /** - * flag indicating that the axis-max value has been customized - */ - protected boolean mCustomAxisMax = false; - /** * axis space from the largest value to the top in percent of the total axis range */ @@ -98,14 +88,6 @@ public class YAxis extends AxisBase { */ protected float mSpacePercentBottom = 10f; - public float mAxisMaximum = 0f; - public float mAxisMinimum = 0f; - - /** - * the total range of values this axis covers - */ - public float mAxisRange = 0f; - /** * the position of the y-labels relative to the chart */ @@ -359,53 +341,6 @@ public void setStartAtZero(boolean startAtZero) { resetAxisMinValue(); } - /** - * Set a custom minimum value for this axis. If set, this value will not be calculated automatically depending on - * the provided data. Use resetAxisMinValue() to undo this. Do not forget to call setStartAtZero(false) if you use - * this method. Otherwise, the axis-minimum value will still be forced to 0. - * - * @param min - */ - public void setAxisMinValue(float min) { - mCustomAxisMin = true; - mAxisMinimum = min; - } - - /** - * By calling this method, any custom minimum value that has been previously set is reseted, and the calculation is - * done automatically. - */ - public void resetAxisMinValue() { - mCustomAxisMin = false; - } - - public boolean isAxisMinCustom() { - return mCustomAxisMin; - } - - /** - * Set a custom maximum value for this axis. If set, this value will not be calculated automatically depending on - * the provided data. Use resetAxisMaxValue() to undo this. - * - * @param max - */ - public void setAxisMaxValue(float max) { - mCustomAxisMax = true; - mAxisMaximum = max; - } - - /** - * By calling this method, any custom maximum value that has been previously set is reseted, and the calculation is - * done automatically. - */ - public void resetAxisMaxValue() { - mCustomAxisMax = false; - } - - public boolean isAxisMaxCustom() { - return mCustomAxisMax; - } - /** * Sets the top axis space in percent of the full range. Default 10f * From 7475b1292d4037cc518d354872476aede62ba2ce Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 26 Mar 2016 19:28:08 +0100 Subject: [PATCH 038/606] Remove redundant deltaX value from chart --- .../mikephil/charting/charts/BarChart.java | 10 +++---- .../charting/charts/BarLineChartBase.java | 28 ++++++++----------- .../mikephil/charting/charts/BubbleChart.java | 20 ++++++------- .../charting/charts/CandleStickChart.java | 6 ++-- .../mikephil/charting/charts/Chart.java | 18 ++++++------ .../charting/charts/CombinedChart.java | 20 ++++++------- .../charting/charts/HorizontalBarChart.java | 4 +-- .../mikephil/charting/charts/LineChart.java | 4 +-- .../charting/charts/PieRadarChartBase.java | 2 +- .../mikephil/charting/charts/RadarChart.java | 11 ++------ .../charting/charts/ScatterChart.java | 10 +++---- 11 files changed, 62 insertions(+), 71 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java index 19aaa2fe54..93e0fe7d57 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java @@ -56,7 +56,7 @@ protected void init() { setHighlighter(new BarHighlighter(this)); - mXChartMin = -0.5f; + mXAxis.mAxisMinimum = -0.5f; } @Override @@ -64,14 +64,14 @@ protected void calcMinMax() { super.calcMinMax(); // increase deltax by 1 because the bars have a width of 1 - mDeltaX += 0.5f; + mXAxis.mAxisRange += 0.5f; // extend xDelta to make space for multiple datasets (if ther are one) - mDeltaX *= mData.getDataSetCount(); + mXAxis.mAxisRange *= mData.getDataSetCount(); float groupSpace = mData.getGroupSpace(); - mDeltaX += mData.getXValCount() * groupSpace; - mXChartMax = mDeltaX - mXChartMin; + mXAxis.mAxisRange += mData.getXValCount() * groupSpace; + mXAxis.mAxisMaximum = mXAxis.mAxisRange - mXAxis.mAxisMinimum; } /** diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index d2c78579cc..f14709a806 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -126,11 +126,6 @@ public abstract class BarLineChartBase 0) - mDeltaX = 1; + if (mXAxis.mAxisRange == 0 && mData.getYValCount() > 0) + mXAxis.mAxisRange = 1; - mXChartMin = -0.5f; - mXChartMax = (float) mData.getXValCount() - 0.5f; + mXAxis.mAxisMinimum = -0.5f; + mXAxis.mAxisMaximum = (float) mData.getXValCount() - 0.5f; if (mRenderer != null) { for (IBubbleDataSet set : mData.getDataSets()) { @@ -54,15 +54,15 @@ protected void calcMinMax() { final float xmin = set.getXMin(); final float xmax = set.getXMax(); - if (xmin < mXChartMin) - mXChartMin = xmin; + if (xmin < mXAxis.mAxisMinimum) + mXAxis.mAxisMinimum = xmin; - if (xmax > mXChartMax) - mXChartMax = xmax; + if (xmax > mXAxis.mAxisMaximum) + mXAxis.mAxisMaximum = xmax; } } - mDeltaX = Math.abs(mXChartMax - mXChartMin); + mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); } public BubbleData getBubbleData() { diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/CandleStickChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/CandleStickChart.java index 12f8dad9fd..87008d4a83 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/CandleStickChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/CandleStickChart.java @@ -32,15 +32,15 @@ protected void init() { super.init(); mRenderer = new CandleStickChartRenderer(this, mAnimator, mViewPortHandler); - mXChartMin = -0.5f; + mXAxis.mAxisMinimum = -0.5f; } @Override protected void calcMinMax() { super.calcMinMax(); - mXChartMax += 0.5f; - mDeltaX = Math.abs(mXChartMax - mXChartMin); + mXAxis.mAxisMaximum += 0.5f; + mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java index 647d0f944f..3fa81c03db 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java @@ -30,6 +30,7 @@ import com.github.mikephil.charting.animation.EasingFunction; 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; import com.github.mikephil.charting.formatter.DefaultValueFormatter; @@ -117,12 +118,9 @@ public abstract class Chart mXChartMax) - mXChartMax = xmax; + if (xmax > mXAxis.mAxisMaximum) + mXAxis.mAxisMaximum = xmax; } } } - mDeltaX = Math.abs(mXChartMax - mXChartMin); + mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); - if (mDeltaX == 0.f && getLineData() != null && getLineData().getYValCount() > 0) { - mDeltaX = 1.f; + if (mXAxis.mAxisRange == 0.f && getLineData() != null && getLineData().getYValCount() > 0) { + mXAxis.mAxisRange = 1.f; } } diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java index f7c4501710..2ff098b1ad 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java @@ -153,8 +153,8 @@ public void calculateOffsets() { @Override protected void prepareValuePxMatrix() { - mRightAxisTransformer.prepareMatrixValuePx(mAxisRight.mAxisMinimum, mAxisRight.mAxisRange, mDeltaX, mXChartMin); - mLeftAxisTransformer.prepareMatrixValuePx(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisRange, mDeltaX, mXChartMin); + mRightAxisTransformer.prepareMatrixValuePx(mAxisRight.mAxisMinimum, mAxisRight.mAxisRange, mXAxis.mAxisRange, mXAxis.mAxisMinimum); + mLeftAxisTransformer.prepareMatrixValuePx(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisRange, mXAxis.mAxisRange, mXAxis.mAxisMinimum); } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java index c354217a04..9acfaafe17 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java @@ -38,8 +38,8 @@ protected void init() { protected void calcMinMax() { super.calcMinMax(); - if (mDeltaX == 0 && mData.getYValCount() > 0) - mDeltaX = 1; + if (mXAxis.mAxisRange == 0 && mData.getYValCount() > 0) + mXAxis.mAxisRange = 1; } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java index 64dc6abb01..a5c3bf130a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java @@ -66,7 +66,7 @@ protected void init() { @Override protected void calcMinMax() { - mDeltaX = mData.getXVals().size() - 1; + mXAxis.mAxisRange = mData.getXVals().size() - 1; } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java index a3f9820f42..3601ddad11 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java @@ -67,11 +67,6 @@ public class RadarChart extends PieRadarChartBase { */ private YAxis mYAxis; - /** - * the object representing the x-axis labels - */ - private XAxis mXAxis; - protected YAxisRendererRadarChart mYAxisRenderer; protected XAxisRendererRadarChart mXAxisRenderer; @@ -92,7 +87,6 @@ protected void init() { super.init(); mYAxis = new YAxis(AxisDependency.LEFT); - mXAxis = new XAxis(); mXAxis.setSpaceBetweenLabels(0); mWebLineWidth = Utils.convertDpToPixel(1.5f); @@ -107,8 +101,9 @@ protected void init() { protected void calcMinMax() { super.calcMinMax(); - mXChartMax = mData.getXVals().size() - 1; - mDeltaX = Math.abs(mXChartMax - mXChartMin); + // calculate / set x-axis range + mXAxis.mAxisMaximum = mData.getXVals().size() - 1; + mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); mYAxis.calcMinMax(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); } diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java index 76a10cb894..292568a6b3 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java @@ -41,18 +41,18 @@ protected void init() { super.init(); mRenderer = new ScatterChartRenderer(this, mAnimator, mViewPortHandler); - mXChartMin = -0.5f; + mXAxis.mAxisMinimum = -0.5f; } @Override protected void calcMinMax() { super.calcMinMax(); - if (mDeltaX == 0 && mData.getYValCount() > 0) - mDeltaX = 1; + if (mXAxis.mAxisRange == 0 && mData.getYValCount() > 0) + mXAxis.mAxisRange = 1; - mXChartMax += 0.5f; - mDeltaX = Math.abs(mXChartMax - mXChartMin); + mXAxis.mAxisMaximum += 0.5f; + mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); } /** From 8b4aa77237d4686fee68a86191ad352a032005b9 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 27 Mar 2016 12:22:20 +0200 Subject: [PATCH 039/606] Move getXAxis() method --- .../charting/charts/BarLineChartBase.java | 11 ---------- .../mikephil/charting/charts/Chart.java | 12 ++++++++++ .../mikephil/charting/charts/PieChart.java | 22 ++++++++++++++++--- .../mikephil/charting/charts/RadarChart.java | 10 --------- 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index f14709a806..569d3b19fb 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -1378,17 +1378,6 @@ public boolean isInverted(AxisDependency axis) { return getAxis(axis).isInverted(); } - /** - * Returns the object representing all x-labels, this method can be used to - * acquire the XAxis object and modify it (e.g. change the position of the - * labels) - * - * @return - */ - public XAxis getXAxis() { - return mXAxis; - } - /** * If set to true, both x and y axis can be scaled simultaneously with 2 fingers, if false, * x and y axis can be scaled separately. default: false diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java index 3fa81c03db..055bdf7312 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java @@ -924,6 +924,18 @@ public void animateXY(int durationMillisX, int durationMillisY) { */ /** BELOW THIS ONLY GETTERS AND SETTERS */ + + /** + * Returns the object representing all x-labels, this method can be used to + * acquire the XAxis object and modify it (e.g. change the position of the + * labels, styling, etc.) + * + * @return + */ + public XAxis getXAxis() { + return mXAxis; + } + /** * Returns the default ValueFormatter that has been determined by the chart * considering the provided minimum and maximum values. diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java index 8219a453a7..1bee5a147d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java @@ -9,6 +9,7 @@ import android.graphics.Typeface; import android.util.AttributeSet; +import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.highlight.Highlight; @@ -109,6 +110,7 @@ protected void init() { super.init(); mRenderer = new PieChartRenderer(this, mAnimator, mViewPortHandler); + mXAxis = null; } @Override @@ -275,6 +277,17 @@ private float calcAngle(float value, float yValueSum) { return value / yValueSum * mMaxAngle; } + /** + * This will throw an exception, PieChart has no XAxis object. + * + * @return + */ + @Deprecated + @Override + public XAxis getXAxis() { + throw new RuntimeException("PieChart has no XAxis"); + } + @Override public int getIndexForAngle(float angle) { @@ -536,7 +549,8 @@ public float getTransparentCircleRadius() { } /** - * Sets the amount of transparency the transparent circle should have 0 = fully transparent, 255 = fully opaque. + * Sets the amount of transparency the transparent circle should have 0 = fully transparent, + * 255 = fully opaque. * Default value is 100. * * @param alpha 0-255 @@ -594,7 +608,8 @@ public boolean isUsePercentValuesEnabled() { } /** - * the rectangular radius of the bounding box for the center text, as a percentage of the pie hole + * the rectangular radius of the bounding box for the center text, as a percentage of the pie + * hole * default 1.f (100%) */ public void setCenterTextRadiusPercent(float percent) { @@ -602,7 +617,8 @@ public void setCenterTextRadiusPercent(float percent) { } /** - * the rectangular radius of the bounding box for the center text, as a percentage of the pie hole + * the rectangular radius of the bounding box for the center text, as a percentage of the pie + * hole * default 1.f (100%) */ public float getCenterTextRadiusPercent() { diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java index 3601ddad11..8f6c1cb982 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java @@ -218,16 +218,6 @@ public YAxis getYAxis() { return mYAxis; } - /** - * Returns the object that represents all x-labels that are placed around - * the RadarChart. - * - * @return - */ - public XAxis getXAxis() { - return mXAxis; - } - /** * Sets the width of the web lines that come from the center. * From 395742f09f19ae4ad901b78af4b1be750270d4eb Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 27 Mar 2016 14:56:26 +0200 Subject: [PATCH 040/606] Set fill color if drawable is not supported --- .../com/xxmassdeveloper/mpchartexample/LineChartActivity1.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index e3ea20a844..836afcd7f2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -361,6 +361,8 @@ private void setData(int count, float range) { // 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(); From 8e00d1f2e6921664135f27f9fccd6ce42d236047 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 27 Mar 2016 15:03:12 +0200 Subject: [PATCH 041/606] Fix issue #1550 --- .../mpchartexample/CubicLineChartActivity.java | 6 ++++-- .../github/mikephil/charting/charts/BarLineChartBase.java | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 31f951a379..26220e5309 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -3,7 +3,9 @@ import android.graphics.Color; import android.graphics.Typeface; +import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.support.v4.content.ContextCompat; import android.view.Menu; import android.view.MenuItem; import android.view.WindowManager; @@ -57,7 +59,7 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarX.setOnSeekBarChangeListener(this); mChart = (LineChart) findViewById(R.id.chart1); - mChart.setViewPortOffsets(0, 20, 0, 0); + mChart.setViewPortOffsets(0, 0, 0, 0); mChart.setBackgroundColor(Color.rgb(104, 241, 175)); // no description text @@ -274,7 +276,7 @@ public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProv return -10; } }); - + // create a data object with the datasets LineData data = new LineData(xVals, set1); data.setValueTypeface(tf); diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index 569d3b19fb..609ff478cb 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -652,7 +652,7 @@ public void zoomOut() { * @param y */ public void zoom(float scaleX, float scaleY, float x, float y) { - Matrix save = mViewPortHandler.zoom(scaleX, scaleY, x, -y); + Matrix save = mViewPortHandler.zoom(scaleX, scaleY, x, y); mViewPortHandler.refresh(save, this, false); // Range might have changed, which means that Y-axis labels From 8e4d28d0dfbb6b22e43be46ef48432816fd9218d Mon Sep 17 00:00:00 2001 From: Konstantin Schubert Date: Mon, 28 Mar 2016 22:05:22 +0200 Subject: [PATCH 042/606] Attempt to fix issue #1622 Remove call to super.calcMinMax() in PieChart. --- .../src/com/github/mikephil/charting/charts/PieChart.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java index 1bee5a147d..be8a51e140 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java @@ -161,8 +161,7 @@ public void calculateOffsets() { @Override protected void calcMinMax() { - super.calcMinMax(); - + calcAngles(); } From e221117b8f599c6ebee3753cb5eb6b74f9663ac3 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 28 Mar 2016 22:39:39 +0200 Subject: [PATCH 043/606] Fix #1605 --- MPChartLib/src/com/github/mikephil/charting/data/ChartData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java index d9ef299dcd..71663eb2cd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java @@ -174,7 +174,7 @@ private void checkLegal() { if (mDataSets == null) return; - if (this instanceof ScatterData) + if (this instanceof ScatterData || this instanceof CombinedData) return; for (int i = 0; i < mDataSets.size(); i++) { From 35845ced6297af37b3f8476022bf6df51ab844da Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 29 Mar 2016 18:26:13 +0200 Subject: [PATCH 044/606] Update realm dependency --- MPChartExample/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 300386b1f4..500771a9f5 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -38,6 +38,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:1.5.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 } @@ -54,6 +55,6 @@ dependencies { //compile fileTree(dir: 'libs', include: ['*.jar']) compile project(':MPChartLib') // remove this if you only imported the example project compile 'com.android.support:appcompat-v7:23.1.1' - compile 'io.realm:realm-android:0.87.5' // dependency for realm-database API (http://realm.io) + //compile 'io.realm:realm-android:0.87.5' // dependency for realm-database API (http://realm.io) //compile 'com.github.PhilJay:MPAndroidChart:v2.2.0' } From b28f4aa8b8c41ec88f0027239d2f2518339c33c5 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 29 Mar 2016 23:41:15 +0200 Subject: [PATCH 045/606] Modify realm dependency --- MPChartExample/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 500771a9f5..b38f687550 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -38,7 +38,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:1.5.0' - classpath "io.realm:realm-gradle-plugin:0.88.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 } @@ -55,6 +55,6 @@ dependencies { //compile fileTree(dir: 'libs', include: ['*.jar']) compile project(':MPChartLib') // remove this if you only imported the example project compile 'com.android.support:appcompat-v7:23.1.1' - //compile 'io.realm:realm-android:0.87.5' // dependency for realm-database API (http://realm.io) + compile 'io.realm:realm-android:0.87.5' // dependency for realm-database API (http://realm.io) //compile 'com.github.PhilJay:MPAndroidChart:v2.2.0' } From 9569cf577f82acc8d2373a805f95d5a82586eff7 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 30 Mar 2016 22:24:00 +0200 Subject: [PATCH 046/606] Add methods for getting axis min and max value --- .../com/github/mikephil/charting/components/AxisBase.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java index bbaebf506c..f0c4e4bc2d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java @@ -320,6 +320,14 @@ public DashPathEffect getGridDashPathEffect() { * ###### BELOW CODE RELATED TO CUSTOM AXIS VALUES ###### */ + public float getAxisMaximum() { + return mAxisMaximum; + } + + public float getAxisMinimum() { + return mAxisMinimum; + } + /** * By calling this method, any custom maximum value that has been previously set is reseted, * and the calculation is From 0ee538c73474619caa7e25adf684ce01b334f12b Mon Sep 17 00:00:00 2001 From: = Date: Wed, 30 Mar 2016 22:31:17 +0200 Subject: [PATCH 047/606] Fix issue #1631 --- .../com/github/mikephil/charting/renderer/LegendRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java index a9ac981c35..66e80e3bea 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -232,7 +232,7 @@ public void renderLegend(Canvas c) { if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) originPosX -= mLegend.mNeededWidth; } else // BELOW_CHART_CENTER || ABOVE_CHART_CENTER - originPosX = mViewPortHandler.contentLeft() + contentWidth / 2.f; + originPosX = mViewPortHandler.contentLeft() + contentWidth / 2.f - mLegend.mNeededWidth / 2f; FSize[] calculatedLineSizes = mLegend.getCalculatedLineSizes(); FSize[] calculatedLabelSizes = mLegend.getCalculatedLabelSizes(); From 528dd490d47c5024cf7b4997202e13ff07104d52 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 31 Mar 2016 10:36:13 +0200 Subject: [PATCH 048/606] Fix issue #1529 --- .../charting/charts/BarLineChartBase.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index 609ff478cb..3313bd10bf 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -246,15 +246,6 @@ protected void onDraw(Canvas canvas) { mRenderer.drawData(canvas); - if (!mXAxis.isDrawLimitLinesBehindDataEnabled()) - mXAxisRenderer.renderLimitLines(canvas); - - if (!mAxisLeft.isDrawLimitLinesBehindDataEnabled()) - mAxisRendererLeft.renderLimitLines(canvas); - - if (!mAxisRight.isDrawLimitLinesBehindDataEnabled()) - mAxisRendererRight.renderLimitLines(canvas); - // if highlighting is enabled if (valuesToHighlight()) mRenderer.drawHighlighted(canvas, mIndicesToHighlight); @@ -264,6 +255,15 @@ protected void onDraw(Canvas canvas) { mRenderer.drawExtras(canvas); + if (!mXAxis.isDrawLimitLinesBehindDataEnabled()) + mXAxisRenderer.renderLimitLines(canvas); + + if (!mAxisLeft.isDrawLimitLinesBehindDataEnabled()) + mAxisRendererLeft.renderLimitLines(canvas); + + if (!mAxisRight.isDrawLimitLinesBehindDataEnabled()) + mAxisRendererRight.renderLimitLines(canvas); + mXAxisRenderer.renderAxisLabels(canvas); mAxisRendererLeft.renderAxisLabels(canvas); mAxisRendererRight.renderAxisLabels(canvas); From badb73607069c3e6442209dd0505f4e7022876d2 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 31 Mar 2016 11:22:13 +0200 Subject: [PATCH 049/606] Add documentation to axis --- .../mikephil/charting/components/AxisBase.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java index f0c4e4bc2d..caac60b999 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java @@ -11,7 +11,7 @@ import java.util.List; /** - * Baseclass of all labels. + * Base-class of all axes (previously called labels). * * @author Philipp Jahoda */ @@ -65,7 +65,14 @@ public abstract class AxisBase extends ComponentBase { */ protected boolean mCustomAxisMax = false; + /** + * don't touch this direclty, use setter + */ public float mAxisMaximum = 0f; + + /** + * don't touch this directly, use setter + */ public float mAxisMinimum = 0f; /** @@ -365,8 +372,10 @@ public boolean isAxisMinCustom() { } /** - * Set a custom minimum value for this axis. If set, this value will not be calculated automatically depending on - * the provided data. Use resetAxisMinValue() to undo this. Do not forget to call setStartAtZero(false) if you use + * Set a custom minimum value for this axis. If set, this value will not be calculated + * automatically depending on + * the provided data. Use resetAxisMinValue() to undo this. Do not forget to call + * setStartAtZero(false) if you use * this method. Otherwise, the axis-minimum value will still be forced to 0. * * @param min @@ -377,7 +386,8 @@ public void setAxisMinValue(float min) { } /** - * Set a custom maximum value for this axis. If set, this value will not be calculated automatically depending on + * Set a custom maximum value for this axis. If set, this value will not be calculated + * automatically depending on * the provided data. Use resetAxisMaxValue() to undo this. * * @param max From 3fd2a8a17bc80a71485d653e94b839896725a39a Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 31 Mar 2016 11:26:05 +0200 Subject: [PATCH 050/606] Update build.gradle and manifest --- MPChartExample/AndroidManifest.xml | 4 ++-- MPChartExample/build.gradle | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index 6895b904fd..250757b885 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="50" + android:versionName="2.2.4" > Date: Thu, 31 Mar 2016 11:35:01 +0200 Subject: [PATCH 051/606] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 78b802778e..3d35de050b 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,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/v2.2.3/javadoc/) + - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v2.2.4/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) @@ -99,7 +99,7 @@ repositories { } dependencies { - compile 'com.github.PhilJay:MPAndroidChart:v2.2.3' + compile 'com.github.PhilJay:MPAndroidChart:v2.2.4' } ``` @@ -114,7 +114,7 @@ dependencies { com.github.PhilJay MPAndroidChart - v2.2.3 + v2.2.4 ``` From 543075027a110809b3cd2fd460b7ab3ef5ada995 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 31 Mar 2016 11:35:57 +0200 Subject: [PATCH 052/606] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3d35de050b..197c1b4881 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,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/v2.2.3/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/v2.2.4/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 2e417148ce2e6b6fbc5708a3a7288f35506cc4a6 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 31 Mar 2016 22:31:32 +0300 Subject: [PATCH 053/606] Corrected legend to center while wrapping in both "above" and "below" Fixes the bug introduced by 0ee538c --- .../github/mikephil/charting/renderer/LegendRenderer.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java index 66e80e3bea..45ae8b1939 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -232,7 +232,7 @@ public void renderLegend(Canvas c) { if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) originPosX -= mLegend.mNeededWidth; } else // BELOW_CHART_CENTER || ABOVE_CHART_CENTER - originPosX = mViewPortHandler.contentLeft() + contentWidth / 2.f - mLegend.mNeededWidth / 2f; + originPosX = mViewPortHandler.contentLeft() + contentWidth / 2.f; FSize[] calculatedLineSizes = mLegend.getCalculatedLineSizes(); FSize[] calculatedLabelSizes = mLegend.getCalculatedLabelSizes(); @@ -256,7 +256,10 @@ public void renderLegend(Canvas c) { posY += labelLineHeight + labelLineSpacing; } - if (posX == originPosX && legendPosition == Legend.LegendPosition.BELOW_CHART_CENTER && lineIndex < calculatedLineSizes.length) { + if (posX == originPosX && + (legendPosition == Legend.LegendPosition.BELOW_CHART_CENTER || + legendPosition == Legend.LegendPosition.ABOVE_CHART_CENTER) && + lineIndex < calculatedLineSizes.length) { posX += (direction == Legend.LegendDirection.RIGHT_TO_LEFT ? calculatedLineSizes[lineIndex].width : -calculatedLineSizes[lineIndex].width) / 2.f; lineIndex++; } From 135f9aa09ea7670dd78c2501bf4ca02dc8acbe7b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 1 Apr 2016 09:50:37 +0200 Subject: [PATCH 054/606] Minor changes --- .gitignore | 4 ++++ MPChartExample/libs/android-support-v4.jar | Bin 758727 -> 0 bytes .../charting/data/realm/base/RealmUtils.java | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) delete mode 100644 MPChartExample/libs/android-support-v4.jar diff --git a/.gitignore b/.gitignore index 2152318d78..70e3dabbf8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,10 @@ # generated files bin/ gen/ +generatedJar/ +docs/ + +build.xml # Local configuration file (sdk path, etc) local.properties diff --git a/MPChartExample/libs/android-support-v4.jar b/MPChartExample/libs/android-support-v4.jar deleted file mode 100644 index c31cede47e3efe9342908764db28b9a760b9a90b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 758727 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%0A<(7v*D)E<@03_8EbidqNKuAHLI_ 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/k+ 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 w+jTz#EWX5_5YyoKR`w$kwP`E(nSTNLWi8KN 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%0AEkcx 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 b-;+{*nGO`iNluswDR-BKTh&qeu1; zh##_yX}z>$_wIu(Pw^^Oc9iuR?Se%sQ(&PQnla1I2}@mYP95>y6_XYU+@*2308qDc_k;qigVjz3 zt(HuSg0i3SE{_fCErVSlX`2>o55=NX8dtM(q_amhpv!oA;9zXsc)BXiCgpq(*#TP~ zP4J;(MwHuro*ov*d^k8|FVPD>yGP(6kQu7k*WoCL&-eWyRnmO#`!cWz`a#7PJ$nJ_ zxb$qxz?M2Yy|U!2rrr-fyEk7S{x3Kk1%pzou7e@9PKs4I9l9~PPTRS1%S~=-tTWIz z6wils@ul|S&NV^2Km5Qee0eWF(Y3$O=O^aTy?%<#kw(TXiOZ58prfE^TBVlzsD1qL zU$(+ulu5~KV>WpJZ{jq}k4Wax!S(QTi1`7L5-Z7HUvF7AynXoliG=IzL6k}Ao`&d- z2J5cBU0d!D|0fr0HuM{mvfVK(XEYZsj@OeOdnC83t$!|<&K^HIOJ}#ID-T!8LhJ+P z;!#b`3yh0By}7>xRI73sjLRv8D)Yuf9PXFdbH6FcZ>t6OVtW#EW?o}@9l0YH=G3Ie z4Z%5JD>L0!a2Js>Wl+x(6egf@m4ZmvZI5fcb|Y}qE=hH-17XndPOVy~HigF}HCG-$ z53T>Y#m=JIsLw+IDCN#`++~9q%r{@ zjEEgklB2;!5~#}gc^OAe^KaZ3&+2BOACrO}PD=M##*ThENb4;e-;c7peJ0Rkr*~$v zB(J0FEP_T{ujA~&l4Basb0p)~8}(&zysiM*GJ9lV#_k5lea5v8#6vlmwidxQoaNse zjn4Hh$p|M#HlB(3bkpo5Gfs&==DUt)5(w=~rQCW;Ysw0n2Es!bIy?#uew?5Z2dXCr$8I*8Wec z=v>~L-w19SzW>~YwfBH%_}URQ#VlI-g+Xowqhs%#oG#YG|D^KG|9)p|>xC6lnD|2T zP$}`=W2^$)lHDkY5_z%OR*qqLW#*7PE3yp9Lz$|&)LnHCCX(DPCyNRsd)e#JdET&Dp@rpkm?k*nNLN& zE&RIihU(Njh_~7?#YCRHnJe+B*0eSJv;|9NE~mQ6w7A&si)Sunh9Z{?5y|Ka@#Ce$o@}8>lBv58m zi3ERJtGX*8k2Xxz(`Am%Y#>HjFaOJ=r-7b@(#NxT^heeE99tJNy|kP{mhZ6(!wVVZ zeOQyt<9)Cs)FI;r!*_pww4gIDET9+dwPt%jbvDS)2+!K^!JtR`r{efK*B7mi$B`(T z;G`jx#&)JYF6QxQcS8aB?@3O3# z-8Vn@$0mg%D^@Vxp^+n97TtPn%!s!N`M61WNc6(Sk+D-iXPgc-y`gF-^}@}Ob%V0j z@HxtQ&3K>dsnNpd8!2nGcL??Z+L8D2`mvy>XrU zAoad)^{c=K5g&?wL_8^a-l*Qb@|ygIxLNe~IMVIkAIn}6-))NyRZlW`rlHf+(*B3RNl1m zL&VGOd&abkW@o{l11 z`Nx<>P24Nq>r8=#K2fWZPNk^rOF`HX3SxQd=}eENy{5tF3;*V;BK3cQC*=!?c={JVOuku`K@I3Z`3Xn zzq;rfaamO#K69xHkxTpNUc4hx2b0VT&PBLzf95fe2Z)$?XJH(sfLR=X-MYD zHQJDqDG;p9j$8ZXtudWWb11^q+48&82~MdC(se*T53NRr?+iRMi-5Zoz$0L|NFUWr z5DJBbMi=NcZHs#ZNFX0!QJA+yg4BRg<=g}SUKlop=N%^*2|w{sqMm-GNgdA?VSS%S@u176)K{WL7u0jJOryY*P&2qtJOylN- zs4UOxI2fG#U|WOn=aJ0pCdacF!UPzWfBG#-wt+pJ=%v2&GN`VDmHxsXO@JrB!uLzw z8I~u;3j}Y*+jwlcPBm{6`gRMydGU?037#)qk>n$o-HeTcFzVqLf%{nNH?TG@M1{E3 zZXIA7q9xw811g`UrX|ra4-@ylaJft*CIHRwsAo5uylB!U)aN**z7!sbPvj1W{E_S5 zD2L-^h8aF!!{h$_BCj|y>Gb(~evrTKOjW|w6>;_4Pw=g;FI-gY960879SZyQM1rRv zhs!t~{EZj%cjCSVpB~r+DaYnZd*SElxeN7ivV9PPKiU#L+7dt96F%G%Kb{jl;D0Ld zBR*fo@Am|t3?U^?Fz2H#`r``j_3{5IW!yXez9j+Tl@{V&Y3{o@yljd69iur_J4-*i z&P2(}n8QAGF*mu+76ZmZX_Ha)(S*80!W{zP9^r#8UL11&0H{}Ftmiz#&=1t*(H(=D zj|Eg>o#T4LoPjV`IP7Ikn~Woj(BE0G&n?8I*4}I^eM=#2MgwB)`Q3rMGDT~4wRVh{ z=uDNDf6X~l=DL*0RE-lV95M1CN0&%o7FZ}|T_~1iGHQkdQ_?aeU>fR>OaZG8vh8~HDR!JJ zC7vgWhnHq!CsJ0P+{<^pncrSBPGsS$Jt7{nA;GXtp#0)O$*1QGjX<(jO zT!+S-JG4 zyE-<=vCJ3~RANnKg|V{2407QDwk^edzl4+gogG2f>fkdCKIww-Q~0B`^5qTs?YKhf zF*2`Tj9!B3X;$x?RktL_4VB*el`XY+m*Ob!qP$^?W(8u?VI(y$inQT#>pm;aVMRhS zm?tw&YY;x+C-b?B8FJ=?UV$z7F1@H=cN*Z#*kvr(Wdb3ekP5PKmyYwM^<9WeHM2CN zV${;{6sD6i-p!aipiI|uDv_hkB9|l(M85?&L`&>c?*bRo0xDF4)bEAmZ7J{kII;fx zikX)SgVaKmSqZ8as0ssU(Xv>Swfi)SgZ#aybuSR4XctD>yUs)A@w7^T zYt+3hFwY=_ccDz^7?CU!cEiz%jVUkGETK26l-T*P6Y^p$YSjcfT9II%tXR~`h`yKe zBe`JHtjzmQ*EMIWb6~k!obyGs&rjU=@OY}xMal*&xWjpzJ$(4{W?)-j;sMY$(|SR^ zCp|~l+9gKJPQ^0PV&Po`Wc&*_en(1X3KELw_Z}avrpP9vB#D$Tf z_IywVKrQ6xLax?_9E#l_D<4Z3q&s2aR@m|>Y6Fs6sOPx!LigLjS}j)kao1fbl45Vt zS|R!7Ys2eWXyYv6LPnM@88y4?aRPmed+`Rdkzgr59eoPA&z>nlfimo=eMPlSG%%yR z?RpF3UuexDJT2L-of1LKEU$_@*?^_yOn|?r)hmVqw$DnfshJG0Ni5h4=(b~~2WZ>V zvJAuAsO{3*{>J6p<2oIM;|b5}Q?^9xZq=ktbZDZxwdR~jyIIknn`h;yE&F?yTmE8= zWNhUEs}7YDvnI|U>1Dnky27TB&S}zE4nEx=;N$)T;`keycnYgvN{wx?#+`<1dA>Ou zyIC4*qyzR_qDT-^_}Y~7XJ;|B2DRKGPr=5r2xs9#o%Q$XB8Yf&14lpP1k8AxkMt_o zmU0P{IcaefjOb55v%*ZNau-aWZDD}gdd_H;;fdI69gftlQK}x8dgWr$V5rMV*(Y#Y zG;X`@CUAGl*2%vM!Dt8mD?9jqCYOQ~b9jNq&7bXt{7^ubjuwmSyDXQ#bjo>wRxITE z$Sy5cOL>E@Hk<0P@1HGa`h?f-Zz7(4^OSaj$XnA7G_6zLls-}URCa^ySl12cuT#q` zY#TYQZ_o&Ohm%@fQ%A}V=stOkD!h}~KDB&+v;_M>Q9eZv9sd}$-)K;(GBCQEuguOR z_u$%tf7RK0%dA`+5<5@ZtS$)@Ri z^EYvK%|~@bgDvyn7p$$cRApt+h*>YiyWQ@K{ArJ*YvcAbCVL-?>B(C=4DOkPT4inS zLg-YtZlK3}J@zF5tdmT4*@m!B@U24VSlIt!?H!|I@3wv2*tRoc+qP}nwl!mG#^#J|+qP}nnMqFGwf4SyuXWD5 z+da444^^$IzWsakKE~)6zelA=GcadIyDLhkP**AG_V5fA)zVblZ`FdkM^2FsnnUJ8 z+P{1QJlV>h!!w!gb+qZ(QlMS66S!(m;Cz?ye(imQ!#d>fJcn|qi0g<6x)vjR?{^+o@7%Gc=6}CXWt|J)Mt{FI{Mz3U@vNpNkfNpYiS6ru6 z^NoJz%|2{ijNi?N_+d#*Ae$E}(;J7M$_>V{AA7XkSfiCz_b16L-ok*mozNuw&!5VLl>bW^lQq545k3W=8 z8*2os-LfQ9ITotxN{?#@AI!6s@4+JB9l2>ywWt(ams_k?=_i^WB#qND~ZR7W(Gm@qP9L5aqrVX{a^)5j+j zxBpZd4z1Y8Q-KlvjZ9e_FPT1|v1idZqyXPRVy*+pYYCRgBh?nRQo*unE8RUUTAklK z94I|eLqMCDD)kh9u$F8plWyxjn~)^`5JSeXP}#1GsHCsLT7_#VHm19RA}QLZSh=CUR*_Z z3*qQcrmXf0S_nN}{GGZCy8%7!}YKf`ITuwVpt$ zQ?|OcS+K7G$i>LiA5E~4TSZFrXi7V-vPl$rJCUcNp<*+!KED_T#cGV}xptMj)M&(K z#Kyo$9-=NdDV|zW-J=XoS2d?cp`}7Ku&CPK-3OQW>{y8I1L&kcFJSykQCl^oSN_A1 ze1VNW+!r+QOs$XmgHpYmt=EI|jo{66** zpIqo@J=et#ZYIyPhxO^ZldBKPXT)I+)$SJ`I^rwMkY7r@VFg>`q7RF2=LPn;a>4lm zb4qhF2dA5!#BGvXrfF3V;$EjcQYUE#eeB^-XBOLJF{88_^>j0_SurkSCY^-Fhm*N` z<(mx2!?TEa=u`@hl}ppj4Rqi7R3AHa7q@?_YGXermbPh`-D|DZKAfbg)_%Clh$Kfw1n2d4 z+kg;WtCeya^5%^n?Rq4Z2`U7aoG-avaCG@78^{uT=P$B3=n{LLaHtoiERbB$~63V2hXK$w;7-u{< zbYaFmOgPM?HwCxf6S5f}Spk0_$$Qs1Gp(gYH;)i zrh~tD+rRkCE7!?CzKq00f@S7eT{L7yP@#7vs4S0Bc+xT0B|5s1bDg3)?s15S_CzoD z@*Ro$O)aj_FSKj-`2_p(XV-45`MIy4-;LSLX?G9fhZ@9Lw?OA7SH;rTPo_scgU(NM z?LggLR~)BMgFap&xF>8p_qm(Ca#1q(Y4M-2QByZr@gp(xZ8g+HX|RlK^#r2us``aB zghWY`^mFPn;%3EIx~|Eyk(Df-_2bdJ)(NvIo5l}D?NOf>5=4t)?wU=^65X(_{ z+^ZNvUfBJw5Jn($^rClzLkb&qC^zHLf?cZ^{Ta~?-ega$)8u>bn1daYseO7ZMR$If z^DoPykYBUxn#R~n0}w_QKk7ysbpBuMoF92BFj|Mu5rxl za7F8A%&1j07o^_c!DT9;&-w|Li#wPrHd>1^_5m!(m7z(hxcGp@8ZO+Rwl!x15aOzQ z!ONf@?%BRjb~n`yAR^<;pJ3W1(;><8uQEeW+)gYirh!=g=l;E8+34Y{XdHocu()u56E`9MRramJYdJW(QX zw$vfIL*Z5H^;R zOq4oo;B8J$_IbbtnIFYK$_u5u88N+NA|LE4>LK>YLLG?qy?2~7v}X{>Xwa&teNcoI zXQ?%`R}jgF&`T5$0LGe+MXjr@AFSq*gLtZ}2Nj^1WvhsVM2O{z7+eNT+ALvN$< zdUB>qF?*mr?PmYLtYA(IomMMl;djkY?DGpY&sQ|pRn>;MzL@+zXdStSs0~%s--OB6 zqC{>?gw43|?V#~iq6fCY)YR~dSI#l{Y5Hq)4|};e$o&&;7_PW>b#x9`HEbXG7WEjR z-~`asX#x2^;Y{}z6ol(}z#a`d^vbdWUqCZ{$ix(E_jm~raHdhx&{9RywVD}a_Xh!?eVozh%kQ3oJD8({G`vI`I<_27VRM$e!MS01y zw-{m5(69B#Y^?zAM|aXsw!oVjy>ssyu=l!yltq2mm)(+U>vMyp7`{^OORSi4SGv1$ z-&y>Csd3AiqF1?<7P8u!tl~lpS$7$s5*x{Q744F^b!GCrUoO{w5bYBA;Fk!rdWCHX z4KmVJokTrbT!{8AuS9X%#R$+OAJV+QH`PH@pHN$VM^poWEJb>3Dhl`IF02C6m4N>m z()sAw_|{K-;2yxF<9FK^IWg)8g1TdbVbJwAP=-@lI3?^@ zE$nzX?3nEzUt_<3vnT&7eJy$}4mRm_6NYpXgme>y`27g++XH%qy_5N5t4JYvX7|PJ z-h6GxlYYda8G7gduE3{+dShZsed<%zU~OyeVJ?><#h-_P)Iq`|6-^Olc2V$VX4xr% z^N~TWlRk*YjK%OYC`kh#nxw5t-7|j#N*633dc_?}9Gv|+ropD#BbemQid9jU3HMjY zi(braAf+m-j&ignT^*P+D%xu$ z!fOYoyhag~c4Json{-#+IjjE)Lw z$u8Gw8b2RRbf{eK8{M+3*SbY|)>=1?aqES1lSXVHL8@=SDN5>qZ!kN9Y&JQ%KxT)A zHbL{b1EYv9MR!Qc7x-VGj#m3Ut*meMNnGN8m;U&7a3;gwOMm?D;K%=zpoxwWfa;@1 z5cx81)P#0DUJAxWgb83HhoW5wCJVEvh))k9z1-Dy2Y@FjYH{9&x#xoqdG_SX22c$X z3L+pBbe?lb3a_LFHE^7f)ioZ)*T^!j{FbQ&zR4QmIA1D!WG@>I*3Em}4|+ zVii@3VX+#DHt0@XjQ|krP?A)(7_CH2j%%=0byTU@epF4A4;b!+q8rQn}- z``I;}*FEwjFEzUJ4~(e4S}t_;_p`o#zy3=I<=?GA_kUxJf9jwNYC?LWEMk6b8IR&& z3Ih4tN8lJ<#o2>{xUyx?o5t3(g8;H*S%(-q(>?LeDU~;AdTm?M*-%w>@>l*;jc8n4 z!o#cVr1M(Qw7Pze``lh%-IS;kNPbwpGdi05`s#Y!diJ^gL;8LK{hURd54UukEAs%% zWy^OU$G6vv&s7qc?K&fOQmpd8^v2Q6WAs~3d6w=}gX&{UR^3s|kE($g8f*U;CGLW>oL1VRKxIb*M@d$SE4a(e+-@#Q zM}^}Ir|uZsTF-czs;Z4iJq99?M)+`JB3D__yyn`NCC1{#(n2C9$|83tqo%jIl!gWk zkrp09nX@RPC*jmc-asf)s9;)c378124{-(V7=}HXemCBzu~NEzg*9sd=FmQlsH&Q3 zER;*+pYW(Iz1R}%QZ7WPGE9=>D9!}WV@uIUspY5BG+vf|bBoin$VPK#YjGumgR(g! zNnxjAW)?KX{eHF6_*n{-OYwomT5&?w0dozDa9tkW{NEwDHmS3HbBwesu-)evlT~0?YfwI*ph)Nif&SZP1QCQP3C9@T(4p?9OM}va?I-gfiFyx-WAPmq z1C;*|kf5M_i;ZO7F!SOL}B z`*`~(;vHP5-J91eZdtsfbku5mIKb-ADZj@HxP5Y__{D>*1Aq+*hs`Q8>K@6t==hiY z375DpQ?8OeIkK+5OlCxq5do--y(U~TFUyJQ~K4z`sf8>WOW?%$tV*i@L-;S z+43VpQ&bJ9$1UgIgNIJ8K-DkpJO*+Iv;8X#49+jy{iM7{8(aZ^em%n^*4CJURvncs z{p|q=db;$AS~^k}N`rVevZqKB!j+3YcGg^3 z(yVj1pH-8yV=`*TY-aa8O{oeZLWTfKT**RS>y8%p)A_Km=M)XPMDM0#gcnYJrq6M( zic6U)%Q0EQ z4O1S3*hlPPzLDKs3qx`=57c)#BO@06XmUbJJ3Yc&f%nUu&XgWAG>fBE#LRjL?%2Lix-|9V5>`oyHMzlD zG;>^EHnTL`k~uy@;7XV^rho4j(|7OQdMw8V*ep&3YuaSDVvNQb3T`%(-eo3PeHiHW zWF<1S34Pcu?70YOmAIgZNz_X5nh4HQvjb~v$j}lhnj@DQr2O5znNyiTQ>{ujdcMh7 ze1kK>8NB#DHzDvU#Bn3X(Qooxd)Q^INjQJV^&Env4nvWL6F>GbLC{B=-BH)X(s%-D75^1$~i|)&us;be~<>muZYvh{TlD4H zo*7h45as(-E&s>z^NZhPzSljyFBf{WT?@KLaIr!Hm>D9WC{4vcp`ftf<=oO{p?v|RqfDOOeuQF^6j0&Oq{C>;r-)tEXjqYDrHC?!8ygbZ zQ(=PGqsoTF!IOOpqS<$)E7B>_#;>H24a7zJ5Yx@^0VW0E%D z0|cdzigCGzUu*&3-VCV+)G-|N40O%3E9Fm@ar1eIyoH~6^C?BzQJsE+U_TJ_y}}}I z!E+n-74&e{81GP>NPgC0ST7Kx0>@<71F>85f&WxO?hwzg2V>{zEe}NQXp4YDy}`JV zl6f)dp<~-s1L&R$(gXEwgwzB6V~N~__QHwWh4;cj`bTpITlz(GXF2Ugrl*>1mkpqM zHfS5c7)7sBVcg3;u`&*Sz*p*pAX5)Ms3 zy^(K1D)Fk)hcn?1&yxk6<^)-cSi){+hq8W#hCAQ)JLkR%l(nTLv*zSe>M4!$5bvjl zmt!qg>?=E^{y`|63}1kXUu`+sDeGr>W}oDeou)D4>$ZUk5s)Q+^L8EQOW z&r*vr##Y&NfYvCCERkH3WSL{tq;OHnILKpBgnyLuX;8YG`*~21$oxaF7VT17adGZZWd`YOj)4|DzYe1SYwT%t`X^u4M4jKUyPe zcMvL#nfdp(U*hAV;O1%FhMgu=zYh;Cm6szNtA5|3&_L^$8GIXQd11qoj>e8h@x~Z+ zHT=Ry+2UETa#IZY0#MWPx>)pGRx`40954U!$-X!6@MLOQAL?#)Hy`|@ux3dl2erTM zSChUwH^ipYYYr08!^_%x?(PPT@^4*J z)w#^!liN8g+Dx2ZR+5(PhLqS?r0e!d60mZzCPMqh*tS^}c|)Rl!vXR0GoTiDm19Lm z`^FrcpElGSgYK$xY*8M15^}d5@I2ep6ITwNl)BQ>wE@}4g44|>>mk{Lg4rWLu`P5H z6`;SiDWuCBXzGaQ=JrMfBfVSJPuhc7o+$`~ocDDWl$G4D7dPZ4ycpM%SD_zFyjuYGi2f`9 zZEmk&PG|_Vp3_aIx-1}>`@KM{BbqgL*dT*7U>RKCUpV)B4&hWW2u{5JLDGV6^G9@F z>2-LVcMh$f2lQm#Bzxb@)OLg;Hf!M}c~+9^UiS|N#IM$2Qb*)3i4oE>($D@FnFE2< zz`A9_Vq9|69n(Z~qM%ChcloA*hx`lvQ=wL{-}r}|iGYdN@=paD;f(4QMwb&(w9sFd z!*H$)t6%>}fgFt$6pCN~0A>jPrxZxw|6K|sV_<7wX5uJhXJc>ROe|zzYh+@rY~W<| zAB;H_FQs{Ygs)8&<~-p_RCxqMRGV*JNe}uU%ONw&OE<-b*J%GF45Yl0d2*c43o@9|@+Pa$Wz06mjAvLHdUBC!QHAFu zo8>a*l(J+#x-!|I{j@#}gf5!Pr9z~xCg2cdg_fejiO3zAKwU?jaXL;y2D(L1T zFV{(k^yrY|(4o5$0xZ~qz2GORb(Flz*7?5QL4Wd_7Z7%LJeeg)nn7phST}&je|LyE ztqUZx8cwaUl)q>qXppT{f+crbXCyAJ>RcBiZFrbz!gAo3q?*nM(wPp;q+ze!MFmg_ zO_NRw&%0VC(+N#^$ZR)|+Gw19+Kjtsiq6c&M#KcN7Z!kba>7Yf!iyeiHs*uUk}E-`#k3{GGKa=jxcVYU4xCf31j)(U$jvMk_44rr{!A9M5)jL5dhv zQ>;^9A3y^r)0bcFlS;j-1}Iyu;zzt0mHgcp)EaL>!ahcHr}!^U$lz@euI|vzjv!!f zsL&Um?-40C90)Rds4!-UAQAgjH0^{X@~^%}i55i2locS`^c_kNHX(a>h%18LNm2V( z_8C(4($NVcM#g85$s>AUreA_tPZD~pU!XSE@w9OV+i?dkVO1l!bOJeduw?-*f|!sa z=(%?y&UeG?gE_V%TkuDN7p2E2>|QnVuklO0w$fftbctHZ?V1t>Wpr&wIAha`v`gmk&67bQrwM z%`(AH2TWvMcX|q=u~5qwt6a!ed3EXc8;ICJ2hkO7#PJG#o}^ppA*YZjdG@yY*3RZ` zte6UG>dNX1JDci?5I*__;h2ZaDiRl_JQ3WCjBt;Tw_FoboK0ufN8=^^8(RAWu1FZX z#qRMRpq1(C;f67VS+J2+>BaBi?vF#=CoOj zNzRykaMp~(J8MF0P_)>D1TQ$~6+Lv^%y(mKB40Zniy!a-b$K*AallhG+}&@w(g?n?RcplPDAG!EL}Etf=zCTY<`IIO?19yXo1Osg&OiwKhX*B-_i4 zh7odYiW-KlTyS1rn|Ua~*6>yu#XqRc_YQ@2tJq6LJ*uvXFkl5rWvDTT4N4G%+A)aL zf8rMnAT%GmJsC(uBnoQVU8}`Ro!Gm$f(fSbg)4HUMD>P>a;Ib<+<}s4Foa3EICi{S zEst7Gm@YO8lHeI~)Tz>GeL^{Ct)s*+0*QN`P|;hUX@7=|E`PX<5;9&}hk(P&2zfD07()pp6N9J821Rznzq?v-;Kvj}@4*#cc3+*A5j@Lg#2?4ixZDlBZ_m zgP+mkxqrm+^MUdL;Db@55v;))1^JFUWA|Zf3AhktQ3wky0sm~rCbl}dx<)Pk zdO%QvC+PE@{rAN}mcNg!|3m3i$=So&|}&r<}mcsJ#M8 zT@aX#`!n&(ec24sJo4F4_>9EkNi<-(&s+oPR^)QFa2v@m>G{81?qIuV+O!Dev4#V= zH&hzZ5|Q!D)id;!W>GYao9O!ECEUi%YDDPP%|D97zo#At9Zd!v$^4&&u=`=qzmhUs z{s_z@$0ly=_ia;YEThuLi}emrA4wM)q_>1?ktWwz>u2?5&tOnKT4>}nJQ-(&yg^mm^!?TvYgm~9p|S;5@|@f*Y1E481)cas&G?0F;uhZsC|Lc6KvBhxqi_q>E>&ubDcK1NrBJto5{eY}{XZbw z^DN0Nyx+3ko&O^1{qNv&#=mz^0V8J%J6k~mM`1?;Hxox?J2SKY^v+84a4l^$)Gr+j zj8qQ-V+gMVW(wj20s}#`dH@OJWO|Aqh(v0Y{qYH4T^v*UEr2}RS{4>dY%7*Z^~-$K zHR8tAAHPc*UBWe++w3ZvmVUc@06&}`b3Au?Fzw9ncYELaJZF1dw;unQ`o;u1ZF7GO zDNO^E^lm%W!nw8AeCvsv;gRuPh5+a6_Da}Z9Uk%8@{UiIM5a$OxO7A345R~eXL4v2Fe>A2gy-h?;(NT{s&U#6Q_Pxq+ zi0@qR+YC3$a-RyfgW~6h6eiTHAgI6!F@=~ohdC#xWGI3w5A93mKYY^Hjumw+VaT30 z)S2DdszE!!hq#{W+l?1W$FewAr;_I-(vu48$;VW4D+xIoXas(okF!(;umh-QWNBL^B=VHgXB||H#A4qe7 zmfToY5jUlwBQd~0SD#h9l{q;1Prss3n^v|`IcJK&Vf{uTko*X>#S4ESSX?dCqJT=3cBRYk{kyMH7MW-WP?8wue+d+uI(tPX%e9F?; zuH%M12!nE0hAt8;;;RyMem)6>HvV`gZ-g+?O&~gI@@=w}CDTAFw0LEL?Uf`(2Bo{7=yiJm+X67SGb1R8JCUd^fMM-IWB24FK_9lp50E@OQ0SwyW`jkL# zzJ!gNaV7=hkObv6Nq!m9IeZ90>n=)iS)f=@22xsk#{G7LJ11a5t=Yhuf3A$XJ zWl49~NeT<=s_L5H(>Y*NJ;h}AN_j`k^n8Lwq*ZESi`fiD`#HATj8{9*-zTewTSiV3 z?)CAUY;KFGE@?>4`NkX_bvHCQI?TVgkf*_D9ZY@pLMcbrkXf9WCee#%1`1OK7*OfA zF&^Y|a^jXMvD+SsxDI=TAh}-|88@?)1%Xbue$aW;t}VFZ%RHBhfz)+OPIhFjRwG+& z!dwq&;ANbBe5swP;N{zC^3QiVoSRh=ZTgbw7K7^q@L0aM!JO^Ni?}I?ndSPhsSFS= zuDZ|n_Sn8fXtFiyHwcpXwINaezTSR(Ky3%~Am3LM$eA=T7e=0BLC*3A!nziY zJ*Sz)tBS8HW~5jyc^k_vE4bjQ%%)5~D3GLl?^NxUd&1OCEy>?HV81zP{HP@OFTN9@-_C+owqF>2pGwX)oE7{P`1ow`YE2 zQ0gEzMIlU0K3r|LUvH=QtQD`?09toA`h_d)4xaHLXXb`Z_{I(=k+BfcolTivF?7U5 zP*K29Go(a8Cl|c zz&g3sB^yN*eYT3>Zd&9(Hka7KEN$QgpZtX`;ub6!LXw;iScXt)EjLw@1{=f~rwIX< zNZ4AyhvVEE0ef6b;Tu@A_Fx?!a7HS(c|^q(twd#y&UM*Zd6gLDYg9d)UWi3pqeq< z%)Q%tE-FzfLT7la@nATdhVm}GyjUP^J;+!at=*iZz5H`Bp8{0A61D`*0Y;Kv2%`xg z>xU8h)6yNFtiFU(@bOy$d~VI0jPP`VI_EV#%Or|L5~?)qKL3NV;(!caADp8&nNB4w zHR`51#Z#W>MGCuj5g7KcCQfe{cWh%z(l1#s$FO4`VUxLw_gg^db>49el5lsn&VwUh zN12_FQ!yk$5G;Zdf!Y(PTmJCM)`2yyW4w!;;kqx%&Pi(tk&nHLfas`pTaz>re|Abp zMVuH4Y)GVE1&C$x`?q8O?ckN(dMcGw&W3W8@IpxVBw8D{$FAE#B@k!drjxVJo21C~ zr7)m^$f$748L&%WT#;B^6X6u76I{vNRxJ|;H%o0v*sY*y+9jNQRr{S*3>qNTz>yB`%+jYWGtZZU42KE9W@2xbDK27@!D@Yg7#5M{=? zu{j+`mHr8pB*_w`uY@pm$HAS97e_hcT?n6_oZs$Hj64`sBb0_ZKGVw_uhEza7c zl<;XG$84*#XCHLJL7kLZYPGt#P-NJ)boK+fsR9-0W{F?<) zo*T{rce(~MBNi7aZ=g>_**hfHBkB4eMS2DcaOMy|v@;EZwwrIbG|R@q0a-(Qq4=5v zr#Pns%r9mMbcv9{cjyUtaL117BX+GwrRh8~B6hhqR_&6t*k7RZK`2QK(M;m=_{Ql= zI?4;oRYV`ie+7cqqylEtcWxC6_CEyz?cWE&e};J4ENo32|LNx}k-bm>{1`#U zZ1WLtxcO!^Qc^ROTK+#!a09OVu1Kkx#)?F%m-4_*@(7=Ia6$}ByRNT40BeTWg)t0; zQ@0x8(JQmt)d(RY$u;m+#I-Cb)~e8K*O5q%>uO6QT=F39R~%i{ig@dGrB_v^Xpr;q zyqQ&0)-)TidsRr@p1YV?tO}?Njvy3jMh8yQ%NyVT0>H`cAc0?K4OUu1cmA;k0Ek4! z<$OO_%lGTQ>K^}2i~1jiqy!C&tiEAoMplX@Ms|+I#B%okQCC!4mmHu+;L%V!uUdTc z?Izj-tq6b*AS@w=7z9=Go^Eo8Zn6+x7vUQx6X=D%DHyPzfyy`k;dJCl%5?B}c?#Nt zhE{&k6BWz>eGg+oa)lJ4sKc~UED7oK4Kqe#=x-ZOvLig@awu~#Mk?;Sw6Dw(y4qw# zLOSk?jOtciZWk_$HfM5n&ztU%#cZpFu?rSNeDAZ9CKG}BZYu_H$TJoyI*=v6FD}LN zenvbPADPWC5=N~BV8yhsbxdY4$NM|!!*siuWY)79OWhK)1Q;9_XLj=CG|4gRcqxt= z=L~6SbQWsF66QcyRVnXL!)Ekp-3k1)E`@S5Ikc0xV;AYMd@; zF1moMK%dare+tmU#H;qvTa1Jd;1Ep`OmJse#p=U*jIobaZM;T7{(2GvBE5eikv?=x zcjSjX2{cU3-@pFLx$E-Jg@OSA0EWID{J;L;e;!r zQz3L^7QI>g9J^}(TL;BlYWo3u-eP(Ze3ctyvo%fdIsaYHmmdwzrUV{FOL*JDi{{uE z)7J&b5tJcJ1cz)#qks5K`%^A!W0$bdU(RBntzHu~kxQ0XL(aO3_##B7VGbvRmuHOg z!ge3fPfRIP^LrTj7q6Da+ZrPX)<<%e(l^v0uSdx&#r7mmIQ+bI6cc<~SE45XL-5*8 zO<=r6eipuuaDfkcJ~N1G7DG#}tFv&x<+1j$a+y>aB?THKIjUVT1e&yer*>EsbJ!AXmcaySm4Q;ekt1j5H?PF()xE6o; zPUWI!_0WWeDrWWEK>is7);$ITGr*v)HAcWyW!_-0M=S@c+9~sA`K`fL{2w8)DGu){T3mFlTgZnc{i*BdfRTrdg`1Lh1{(Uzu{#GQ*L3D-Q{Lq-JxY2~$Ce zVsZ!%bIMJQu;N2Otto3PSBZbHq4pP53*D5w{vU1kvH>f#y3Dh%_4p>wG_OH@Ig36n zxuEn*U8Y|TK_~KhiC_va&cGHXa}(xQb01u)r`zQKq8Snbw^6 z2&n;5rs!~pd59k|>ZD#_U9lL7Z7+xo5ZR3mVu(bqA@tiwQ(O}}J{Ux+Jr+aq^Y=37??|p7{oA0kX__L@j7*)AEYBKNQa7 z!H75~^0$^; z`+dQ3Me|niR>>sW55BY3O`d-G2?RXo(!H*?%m)ccp@S5j%xDDVe=WKpOTI?8u=K(DFXM{*0b zH%A~d1Y|ObYRq7DsUrx5j1}2j%+m85^zDNQn5_;$gzf`uBShG`DKEgi7jqMqK&i%~ zQEks1@o_>jVYBF~XzZEHv^-_0+JXa2xYnLZhM+-hW*r|`c*b!z8O^Jo3~T<3P+C$fPhbW#^cN)>oRn3M%m|a3)^*t|J7ajl)h9gp z+@Yd)^DiVW;UoDuai6O{#RSA+B0Y<2ISQ{)x5Na(OupD-N2wG?H4aWF=oI zD0-Sp71iEw0mS>m6`X!&(&X=K z68<(S7(u{8Wdy<1Ml@biAZY3uxQsX=O^ArBqkfy z-be4M3Q9({*R<1$wEG43XZyOrRFB&MH)Hb04%{ZKFQv(zE5jj~d=AZ-?>>y2uKuECriJ=!7N9~vSLeX~K) zhagi7ev-j+4jaJSVX^r`^4NUmHJ2}!5;t+|s$9^ml;&7~ILj|{6K^BH> zRRm-@WTt2~D?;YW{MNUauJUmE`WSdK%aXI-iC#n-B2tDSaFYiY6oCL+5mKl z&~=DcD7-vA6r!Oj{U1pEKY068*183$TJeA5D}RK2{Nv_CGpx^0)^}&c8uq_$@KXP_ z{&D{O2LHe3yAseE?qm^?p4VDP5>lyvHvB=4 zD!2)v{LU#QbgOz~0-vJ09*L6>3Z;!6%Y(BQ9?I#85%XmN0Wq0_#|J|vEN*@#0xn_1 zj!3#ADm{@=YU5srIaK+@Kza!Xw=+XE){Xjh3ejU9%AP|`q7 zU277tY>D(jGk|>#r_#6r1azPlYZf7~K(hJV-2Ur`C?+gWz4m=PB>I>B+`k{QIsV>* z{xgSEHZT;ob8;qDGH^99{uc~%R)W=E80e7gyc+0pO9_i}(DKH)K@Z-OMyX?;CWBiF zwt4bWh?bfIK1<tnf+rqw>NW0v~JwTr>q z`o?#=1aj*#Ek>>Zc=?{`i#|;;jbIgxbd_cIY0Ghz)g~nHBK6R7XhdsDCsXbr?2x|D zbO&QE=G8(E;xl;HqAB~`p2BybUX~oEVyXsGYkh>UO0N#{Tu}ypS zp|f7vXZ-qX{Ldo8&^+f|nZvdGnDdrAqF|GdO{oqdPtk;31%a)`uaGbUtFBIEZbNql zs=#}&R^?=uX2yGVm{ku7az%r3WgCaGKNu_#T$5FCCNm?2Y<^%*(NPf2XGuM$`;?Dw z;yS(8#>yZKegXaEvEDQ)=u+QjzM_9Q^ZmQW z{(l+{{41{!F|szW_>V%!?}TaF@1<|3TVjKgH6%IFkEVfHJ{KiOpp1W{#~-RtJP!%# z4Alk~8I7Yox$~6C{^vKkmR-SN2hg2-puz}A5;YG^>M3e@`%l-Z#_RC3-8SGxKP*0z z`crMd_*3oKUPNSh(5jfC#C?#wUU#dIeUmdC!GuepWK(G1Cb1uhtCFsTt26Z+ZoJgH zE)C~VB{hVvSKROEJ>`*1goY%^yGmsgk-lv_x1h5_&8I%YR1;3N-7Qn{I_ixj(XRMf zdc|^d!Ej#mQvuS6wIH-wJ8I?3LccOd_@&_mijtCh*^6*`)G~Zm8Cn%S%k>YMl73CW z^BnfWt0s&qhDKmlt;~9n+0fwsBm9YPi6O$ zM1XF68YA)SQ`X*~KD&ir)hH{2&o{4_Q6TL8N;soawUk$k6T2r{ZJiylYlR!f5Z?L| z!$`FZzf>SHvPA0#+SGf}6fgIvrx@ZcFjc^+%d2=;)t#RdzSj4U$9YrGGKh9p`mnX! zji9URYE}u8F?9O}f4M`FajE{1A#>nbc3y%G!d~@&BJv4=iyrpvX-i6n5)=f=Ld9*p}qSZsKh zZ!ot$U$#bmI@r->E_dt>hV<2h3elpiXR3!{Oo2k zZRJrWn$GT5=h`W&MdQyZ3)(SFBCxbE=IJ1#VKnknVJ8)8dY86p=qg_jGK#GsUCA+2 zq>SW(jKytbA|FkzE$4pVzvqB|CC9llaIiEdY_b6ZG!3!ylNfb>Qw8Q*!?GnG)`=@u z6!*SAD*PG)z)#bT81J@%NkTM-%DdnAe)#UV<5%GRVQVjU`kcGr;5|Z5u&Q zB%#>5aU@Hxfu|%IkdrWUu4WH}=7R=bkz5y6K`1g5BLI=bBKrX9HDEH38-v!qhm?51 z0#EElHA|L^)DHQzY*wW$f-p$0Dn@bLX?)Q9*QQ;&IrUZQ+`b^?Z=j&POk>l-MjiV} z@)WE?n#ogyke6wlIOUmBTksS%rbnPQOL1pS$Dgf$-a68Ru!-QE((__l#vFa3(=ZKB zE_BVz79{G}#)Y8JATW)H$eg24xjftb(f|clFsZSk&L>Sh=_R0t)n?rEBob4VZs+^l zkXC^cfeSF6H6Apj6Sc2WYDN9A6I`jkd(L+6jd)lqhF#Ar(Kq@z67uWhPfzfg5l{F= z7JVQHHe@=!@jb*w(lv(quqSS)Sx5hwUI*o_9|3RQ8K^JSE~z=8%L7$~U*EdIb$Zm~ zbx0Irt4~zXG8e-xEt}+{_7Eo*@h+MC=p%xK=~{R_OUe!bwj0#XqdstGK}R30A?H^z z#cYRrGqCIWsJln8Aqv|RC&s3qmPS^zO;BJCO9f=LzO_v!Nm%6G!qvmAUfp7!Z*Mj+ z8!Do|nekXd>Xq+y%Hban&t6lOSc1kfDOWiP#yu8mxp*$d$SmIlxDO(fweOZ=ACySl z3%F@qw+Xp~ilx>}SB7|$-BZY|(=rVQ)oMgeAzwQv!jCF>u?#&##Fy$EeYw*#VqmIU z4A(8mK<4oZmr|x~fLSfKcDF&pg^yS-hUh<2h`qFHLa|%y`_*I}lP9VNxv;FB zSv6!F4b!P4P$Lr?NO=o-P}-C?JuZKZb~pfWPzRNm zn2dih&eT-{q=2RCmq34~)LLW#-K-vK-;v+^l<;t*I34ZBo#?Fk3vC>9?Gz55&PEDvvXa|W z4R9^(S8B!j98kU<3e4kRT;3MF8N^&J?e#OWmMIjMSWGL}&V9#@=0&NVNuIaaQV3|X zr7iJI`{6iZIdby8M^goPxU#jque4I~iKwR9jA8EG!~dp~(TBHzBX@>Pja{eeZ1Ofv z@=?aH_Y2XK!d!2#Uf<^DNC+J70JEU+jGnWOD_GV2da~kt6t%7HZBbRbpdTU$x!OYK zQ(6|p@(GIHtarrtsP2szhMjjn$NKT-U}zjKrD<}3ns9mU4_;`tWqi@Zq9LLp;SphE z`9`7w`NsL%qAb%;`SGIUqMXRdD9OmtByH)IY|~izbr#IkbrHeWdTBl90}Qg|JVC>h zeBaHe$YfvYN-+;vq=7TG40)0VY*DK~dgX~`VExYopkDhWS z-*JhZi{d)K!jUQ^Zvmbl`3ZYWmOID%K$6frr9i-{INsOz7kP4`ywM4@>}lUGnY8$r zQ_F|~@|@m;LZK9O3`L+00`b2u1B>JFf%sHR*R-U&)AT6KYEt{@o(YAczcy zUrpe#mE>i^;LS0GcS~`)bnJM|MFoP%<(HUwA|YhhL41{blOa=j7yP4uev2hzANU}lgfRYd z4E^7hjPvh@2vI8|TL+83MhUWi{rwLeYP6!B%;ygs>h9t~xvFG3HkztH6IACDTE-R$ zmUQPYwi7ba-LW6M?OO(?d4yB35!FTM#B*?|J{J%C59e?D=wI+l5MuCD@v@j&Kv6?U zr&FYvlC%?OS*=2SSx{H>6}yIiHC+A*D_IK3kb? zBG19qqT7V2MVGAlvLck6_q$!gdH0&=iUZO4$qeDl*Qx3ipmt+w^Jg;k&G`Cgf=}wCCpDF)g$Zv*t}H>i|OT@JWNNYtMK!Y>Urtb zX{#XE1f~tmkV~}n;On0)O<+huBK=28L-ddAiR|A; zT+K|y+|BI&jh9v_k4l3wqw%N5#NbQlRNN`$!eVLn&~rlZIaHtDh@Ykc>L>obcLcS!HnwGL~M~K;X;1w@C~Czj<~hk5PEkJvGVBnNps|J{_l{B;&`rj z^}(6gTb+jvAn0QnA`={A57Z3-2A2G9322dNmSDST`~KSn{%oebg)H^g$(~FDlstGfYivjTHzy0^ z-8lp7sQBb>U(KyF1?xo-mQDRf(I}{%T@Q>?OdtP6oDG+H1bl>k{2xRA|G*0WHPJKw zn;HGDRT|qMx?wFWXdMBXuEY$#K3m~h%g9M5+&ePxeG5e)z0l?Ef1M24<~MkN{Dzf> zxGC`kYNC8TU2;Sl$vpgEP)^ck`&b{lz1kB9J)1pdZ6>XS7t&e21`Mv+6xo^b!0YX{k`zc)9xH6~FoT{yqKm4;146A&LJB&xlT3{bMQwyvyP7LPH3z z07R&ONe7?YXq`aelchzAB&mr4X+Sh^Et1<>+#B3SwvA{QJU_Kqkt6YaLJUcBt1Oi! zG1c0@k9~g{V&(mP`v7$KgsVB)-=hbb%%`k5ECS?jti&nBDaE#AI+6pr^w9%c8=HXN z8(+H&Tb7*ou6v!a)3a3@MYc^LEqsbdR>6I8_Ff*!cu#BoYX+l zE;HSj5R)nYwULhW@9;b&yTuK1bL)WHbQ*IzfHInAPi)J+vx8yh5k3^Js+V9jYvsiI zJ@<|7BDN?XBY2%8md!f}wC>%~S)D8>edyKHH}>ST ziQnlK?O(S%gY=LuUi?tG_psGb%#?OsD5P6#^TWWcCHjQEbEj3W_J9KR-xsP8O!V8W z5>gK!jI$3mzXy!ZASUExS{IbKm})h!?pNgo`caaCC#v&16YTO;9cX@oZ2@DyQ#HbS z1+$t#y`WTfz$uaR&;0Bg{;8;r3WCZ8Sw>0!L^H};v|ZV3q&qHS$d^B8Se6M}w^yyU zkE62{!-ODFyzH0v6W9vd;Uk0mIq}<%7O=C$9yWI zE~Wb3OB51u1Ex$SNfvmN+D^`;YkPVo+7TTHc|{eh0416jXlHIAQP27(n?1p-NQLGO zt%an63IKB2o-U+ohZa3lM|-o+oplmK>#->m?^tGKXK0UDG^uBdCPE53-D!tkCVj6> zeVn6Tm9CZ}4esxr&}rgJY;uL2ympS%_cU7u-4zo;X{2LOp zDa;%K99>S9`_9H+xI*yRv6B*MDN(_6g02Eh}>Nt z^=!?=VE<3l2X8w)R1Z-BOWh}3)erFnZbPaIEybv+sxeQB_P6ean<{IC)WmALaRyHK5?X*z+_c%}@9+J44 zQVd*8rPsS`Tqhp65m_kJm{uH~b1{)CL!7_2j#e6m6{B>&zQFZSwzb)r_)3jE+i*dY=S?&$10Y!D)8rvR$qJGAu%FjABKZ3WfW}fI*6m<~;r?ko- zpB_gVsNn0l4pIhAL@^So9!L;{6O}CJ%Z|jGDe(yoq6l(WI=Y_AWbGSwMVW2KpKF;f z+E;l|k)zM2*WNz?4sZ4Gg8cUrV0vnUU(0Hk==wpSDoW8cfsLOR9SeHkZIyq^jp+B$ zM>yo1sT0E#Zp_8cnW?Vo|#}#zrw00bjxdZ19ftFm}GpP5KOtMQ=^o68Lb6=RkpKd zNCfN#n7uYM7-EkIu{pjJaM><2;zyh#kYrh{uaO6G5~uI*&vwz5oul+#sX~aliae5v7%K);(|OxJ}jH zSL_=6+st$CMK1xY;S02|#L#JuurV{lE zeGTI!NCiDqdrY2e^AO5Rk|@ZUG(cuOq3RpOV<31-xk;(<$cqQOhsuR|ey+ zFt1(%lQvx44pV+tI~>86ZBLp_-hyf%=!aePT(15k8dcN|u5LuMLvx%LCm4mZiDb|9 z7SyW>k$|gsuhMKb+#sIa*Gta~lWV)dSf6HlL#4)1#qQK2nj{`|ZSaPhN+zYyaw&PXC?EgI0{(~w0)wgqH?@IYDf%)KF!*sHa3hV{ zYrp{8!7aD!30z_yyDsGD`OHW()@>4-wwZb)B~vjs@Q~{O8z{T;jPx;$mdq}Qr<{ypnfayaG&=SQ@<(rSKcsm;@=+UV{;M|puYm0zx-?k_ zBU3Zyf4AoUSKrk5vE)nFtnoefa>CTUnA3_?FQGw-cYRiB2F)^{*s9J4bp2?9WORJY zj>VP3oI)<}>JzxCuL20Br52hZY{bu*;=vK+9E|;K7y-X$Q)G0IRLasY#9`h|kIS`m zSqk!Bz|+e`2mVd&L;s1VE|-f2h0fb(36LydXhAx@<9?4SZ_91<6QlfVb`g1gQyuyz z+`eD$PI~IecbDwKFTaZc4cuPkfSEkMt9R*#p9qyh-;>8ze>{~WUCVPrKz$`a;jt39 zIoi83shjg)PCnixz(mM8dqBcIje#wnTg^s)?; zpQp>Pb}he~70uWy;VECV`jK0#s8~|=HA8&XEra+aoo9T;s)=ur=+MO#M#_~@DkhqT z{i$?W!bNMnQHs^LXce_R5v+hezIJBR#GJy!3~D8%r~*2m5i`?gJS8pt$aXnPP69PM z5(<4ZF**0edj&OUNWug-Z{ouXI|jNks&6d~GXT$C(3N?e;|y-bxQ+isb+yNDMz zy3tlX{Wz+TEt}&dg>sjgbyRkgQ-T-Oyoq-lj#vEak1DjXbm_(=>q=Hsb4t>v5@XgI zLri+30F#|_Xx6Ogi$>jO{_Ar++whx5aEKGcUfHR-0VUJVC4a3HIgmFrI<8sS4|i##S7UAI;YGnpKiY;GpT=Gp4*`;nj~mIm1z4qG|)(f#}jNWw^d!(GP$a_EVOz;mI!lE=KBY(_N!L{AL-{c=sos zA>-CaJ!E`7L6@Z$M`7q2`^SCM_}#?U_bxCI;a#$-4zm#FwUQO}h7_j9mm_&{OE5)e z9JNCFJKKG_a*6Dy9v$QjY04L8og9Gs@h5tm<9)Dct5ys1>!Gly!&r2()uR689e&Hp zNHR4Zqbi(2`vL0@59#_`_D}KMg^`WgczC2 zRSCSxkKPrWr$0-7R&n3nNIHkuksKt+t$0N+RU65dB?y`FKsAcEr?^R&G`aD`UvQl^ zG^&2MuBS~$%Pq%@)3;sOlI4nTPjS=f+WGOvSxRmJIjK#^%;EwxG_+aKRyH0<*_tWN z5XPK>d+hyEMtuZ>8BAS;qFuxlA$S>aTi*eOzwmGwQbL!Ag)V=9Y$)jg|b|T%pM0)<3=jZbxxK;nxb$E z#}gvYe#EwB`5luPbaBfB!hT2~=wO#82xICPBAfUwD}htdhtAwBgBhW~R)7OKai6`3 zq&J5FfCtArs5Aw)GL&#L!DdO^h_ghRT#Z$TpXZ&nWbATCIBv*>%w~z)IBd>rkhkz% zb|Vs=FD%rzVc>iH5h#RxA8={4_dCtBW=Zh;ZYeUEEDQvv*7!PhS@t~cI2C)gUQo(T zA_TpO&XlvZ(Ui3IU_8@NdOQzOu{6W=p1#}Cm}yXJ3D9VMq(fbX!^W{Wb_JeCC&LNj zbS_)|RwphIYaEQ#H&aF3REf#1$k;rM8^;x)+Jnu_w6Ic?tj4mUs&{07d|kbn#YH>- zPj=8A7Gg2oM;G6P1z(LkwOr`3FnSzS_CnoU-)4$O+hGdg9>;C~AA$hLOqgvmMX_>L zKo4P;TncwrC`oGH6_zhYxWSO|CppqSnjJQOoCBwxoh{c&+wVppmIa;DSDVl1yWWj*U%DT4FG%m zT2{ZqlC6g%*mvB1A9*WsXDapVl(bjJv^@~ewPo;Y2@3L^a(j%6l?~#dIx7h4^)hO9 zg+YA@GbxZU2XT7Z7ie4h&hJWrxfH{8m~Ct^xc7tdr~l(L3`7YoT58N1sTg#4HDT!T zQ#DCzVlW_HfFc;1l$d->FkSoG6zdI(&Gd_=q|=WwS&9@)-+m;Flu9RYb&kg!ZU*g+ zY}DkGlLJ}i0B!_o!a`ezA^FK&88T(H9>-%6kJUps{>ic!kQ>x5mJDvMV8=d4e)8F< zQ?bKB8pGCh35i7KQ&lpjCe*w~5)YqNP! zhE@P0@sw6plCRO#qjm-D#B9sDB&#Vho~!xNSI!J+o+{ObI9C)L>?`bRLnF?CgrAWs zJo+zODh1j(%n~2(M{-S4MtDf*bN$$}`1=$btftp+bQ+G=1(qS-@sM!1+4J;6N`pK? z8drCMF{tDC%{|yKY!t3C2{!7efBuZDlL7W>_{Ur65tBg`O=Sy zQp(5YMCXUhtV+-bPzjZxW2G?{dN&-$SeA`UV8HIpeyC@q<|B&_4C$Gex1Hsh4yCqH z(lwQT7udm4y4)zWb&j6Tq^$&|!a#BKG;GuKpsP!G9I6x4NrB?CICFShcRdFJZqLP!o{hvuG`*s@*{R5(~+2#FekTopj*g#|h?Pl8RlYV}=vMpa6A z@s};A@O2kjP9)Rp5i+QMto)x4qR^Os;PR_E0bXwk=#JnC3>2Jx@9p=aa>YX9fvLM? zY7n{F3*AuMB~A*-K5iL1)C&j4EX(JmibeGErZ+P#W!)k8=0_Q3h*LcDqRX%1Fl&Yu4z$0yi3eEw7^3uDF zO>Oho5dxJp8?M?O|B|weA+`XsZ~7LqodMF%utU=ZN+J$4y0>062}-Na2=1B)MnA={ zoROs20QD9H!$#j;dz^GzgBFwJ^MbC}2nN!IzN%hpZbA|wf_fdUsQcqWyrG(acUub6 zIyy&Js6@{sZnB>N>6?m4qSmpoJg{~N6OAeFO!{`I9PaqSPW;la zOSvPo5iY|)HcDad3Iv$aA?5gvfJvlK_BD5W;TWqMD%mO(Hzmc)b*@4&&z}yc5}x^n ze~~0;E8G%~mEPeEjQN!|ZbNe2c=pDPSAHE+PxM8~85s&m`8~{xkOvVs9?$-@;2pdB5|f^SiR&g`r__n}D^J@&5GVQkdAcx7 zDx(~fiv%BXhU~pVz$FoM!h~3P|6^fo= zQ{{~RFz_ZOKXHQ&;CK?(Lbb3O-S$BbMS8pUwBjLTa^W>ObJ^}XE=_FLg7OZ?XED0> zqG0aeI3WuV%jz=nIMze$%H*ROp!#s}89%0k?OMeT)sqBqieZ4}XLjU=4n^o^kT4WM zIFVN&YfOt@8?BI67u7zNzFeFW1x&h>{?s7LfMp=y?j0nhZ1LO!!}K@C1W#ST#nUzD z!M~RgBBsP%=OJr}*gbC0JubhTVy$R;GVea-51YTUriT^{Rl*%>Fl z!9enY4f6#u$Ar+WO)@f0BIGv`M&8uSv&eTMasCh#82|l*uLn7sw1H-y=$6{TZg|!>sw*6Z?+V| z>qX}2i`jK#8jhQ$J(KH{FVk9m%vGML`aRa#Ij~-e!?hdM3P%GU>4CYhhP4nlH2k9WZUz3PHod(j*soL9u>tx=9#A5X{J@b5n(Z%PJ2XFWmHzo2f8i$4b&gBD7 z)=SZ^c$u-r*AqPn{aiNm7U77tlZlMuiJSzzKh6<&St*v7IpnJf|I^oU>E*`jZXY4U~0v|J)9w5J_9`5<=n zc{1#g{ZczrJ9j&GylgkoV@5mujcTaJ4R(6Pq_NCoM*JPq zFskW4Lj24+(4r8T%er8Y)YDc$woucmY2aR~uOH=h=hNR-oMfs+bS#u zs0%INQ1n%ZvZ6cWrTiO9QBWQ7lwX4g1DcuiI?kn&-rGqjodC4gUp0)E;WMB$Z!PT=79@y}tICYEPngpo_1gnRSHcYp~yqq@DtQCU82(wy* zY<%`vJ6j6ci@l8jt#&%AQ>4)kp+z*AT^Z^zU-;@{as<_c*Q?HsV4IjNYv;S}*(OrO z(&j6ys8lMqby;W))_^0sV9l(O8D?&WLTjL*n9b;lhCPTlA%v$q?z%1JGjY&U{Wa?lD-HAHGvOXU&GOE=BsH4R<>zI?<-{Y)YEPBG;nmZR@pIj& zSlCQZ%;6$!13_7H-P4LY4))RV^4${_9;T~ryY(H9SMA{sZ9AR)5f3q{z5*xWeewe`-$^-kcr*+WaS&8JPr zu31P&v`tk8f*w*&RkPn940i;(xH31JG+CwXnO0=6|W_a&3dgBXYZ%TCq8lD zDTx*LdhAu4V8UzFu1JEdT!9%^7UY(_>m|$cbnOeM-@%Y>%@YXUfu4{^ZJSv?AyfQr zGIqwxK8Z((d#|wP)cCsC*C#PL^8Tm$PaaQfZX4;-r+LbMf`Zxp9=KC+b2M}Q0Dnb{ zY;8XnOivRt$3M2r{0sO^R^(UcXF}sEAr4TaQr$5zG~E&@j~q}}g;rw{!-qP#pipO$ zKKtoL^BV&d1yt}GVTh+x064Ru%%sPm$0c32e@7Qc%NIdm$WA0)Vt4F7+M;A!*0bz^ zSYv8WV%G^&i{uH35>^O*W0v8m$mEpSwk#PR#shHbx23}TH0TPP-{j91LiAXGx~X(_ zn%&5@@+dc7uVFE@>%!{7Y#dHxG2;5n5><71Y<}aTq{t5J=Tl@jC1dN%5uJH5yfI+0+&{3Y%fW*Y_z#@4@V}7vw$A@v;3oX{kNf`* zvkM!!n34T!wGWxNk%_B=v)8{KezxMceBT$WEX2Ig+=%yZeeN&$929s_cc00~c zy|q`+WlrqZ?U}o9APIXyS(?&5L52tLpV=O)KAE!^Y|bit(%>>$GAc!e=_iBo1NaH= zD|OJ2>SB`v6y1u@+Hi0FaA6YnTKiBaCNSBxyfNoG^6ojCdKk12gf)vwDscHR_*H%0 zJWw(fggSqegni`iLL1 zQDk;oM=o53nrveONc--J$yxAFwKIcBvuaGFTQo-pNsyd>$}=b|2}4WPMM)2R=V@;? zD)P*bzg-d`Hu&5R5Lr#xLdh;{hwHke|#M8VTf`r07k>6(`o%PUn(>L+$>GTEh6S{42-d0=St)}!YM9ORnAQL7%i>=wz zQ>Pc^=0{;%aEfR=F|sSXVDp4xsf*47o(SU$|Ds#R_q9zbmqmIF{*Ag|d_Fr!@l7ZM zPOI4zBivq8fb!Qfy%0aZnrc9yZv9h*ym_xivg`GNu)$@J9 zR>yOd;=xZSeWyjl1^qMca*?$PTkz#ALSwdI@7d!%6@Fi&*4NPOo$eIuB>s}aMzQ(0 zDf9Q7hWnfcZe}0zW;r0|nY;*?o`gbvq3~c~1a&1vU2%StTk=3#!43n5>RNw!#EWO# zfCzf*mF6HHLp+uVYGFMu_=A|_yon}uJxKh7%*k?nmee;yJp!g$9FdU~SiD7ao)9?{ zQ4e=;&T*0SqCQ_0j&!Fpz3m6OYYP~wBF&P(nfG8D$4m+z_EGlZpN-cQ;Q^0SE4+OR z$5-UMz7yK7Jq!Ajn#P9aPKj_hZR-^8AW zjtxGI0c<7nk+=Rd9mJ2v_x>T_mijO7`+xO9G=HB^{?pt0Z+XI?>Zv@c64GyZ#`cCv zXVkqNblRrQ>?G=u8+?y}#LF9a43`F0*arHw1e2svC907Bf^%w zY#fH3RsOM+A+yPItx_ZSB}#+t2V4q%kdDq8`@X+r)D!!x$)_OoA9mXqbQfNMbSh@{ z8hXIW6aD&WM_q$WgBh=aJVA6fASQYH^e>fGK9PD)BTA+%9qkoVO$sMWZo@37T`TN-#W9fdOW}LB71UX=@ zc-9qOiEaPr-s5pnN}cT&qHVu3pY&Gzm5scb$Y&|Q@aAk>4lT}yp%owm$6VNk?N4&u ziKjs0)Azl2?R~XSMa=#&MrJn;kP)E<*zX=RS^8~}T_nbOOWDDspd(YnJ}Y8FFHgu0 z`0Xt01=c`yRQJPw%?X9ie)k2d*S_oY2}6fI}?2@14kiu!FAH5Z8+ z+nxxDbH>>+G;$ZGI_RzjRhA}Li=6Pd$dob$i8s8!eT&Kt51dmZOGq|^i~YyQUW2V> zFzJATgblioO@USL0!<)5#|W9Nu%E>8XRz2*9DD@QpzAl@AT1t~0DnQhUDpDzF#faw zy|pm%&*w1-j}U*9*yz*}?zWE;G4Wrk9R8OM`iC`ne`W6fAcX!(-pL=Ttbq?A+ezHU zX8!tZ0a^EmN?ph?4~hh)@9~RRuU;{{Nnv721G>M=6$k^G>!CzAr`s2&eYA|Vti7x( z9|r+}T!T-Q#tY)e1qdt52Zms~7!-7}+G)l|k~O9*&3(11`!rMWa2;fkyOJdv;~Bu+ zrPbMv4I~==t%${t4Tphkh-;3bgky{#&P^Dn?X0{ORPeyOW}0NKF&y^1Nl$vR!fu== zKE6Zk3^!CdoFF(u$A@GTZ^}I>5-2VE?TuEoAG%ZN`A!?2j`9VvFUATPv=Hx1DK$^< zltJwyhy|kM&jFG(`8|CNu&~(-ws0+JLh0oT5s-icrF-G(lMSVF?MG_zH_&WTX+KK6 zr*1mxFr95eSoFAAJHh05=^HjirQ)V$m3^KbX>yZneG%L@4m{y@D(!+sp{$V|P|ba4 zJC7&qRB>aaWrJzCIvZo-yYGm_m~U8?G?lZq_h%#gOQbNEf}dmJ%^i+RFS|w0iX}mU zo`#9xW_1fapp%?2*zunp{uD8+$&qS@eN^b)f2`14f1mmOinFS-k-f`Dfai?XvN--1`5d!R=81kAPmMkPP9KDD0&E&GAN;exc8g)bwg4z9rbE)yWLKW*A?h1 z)ov1-Azb){Ig7)_*u>cCWX%KOM+;S_*z9PV*J~KtPj)Y601=os9s6WUoa7bNIAuOHk+wR9kR0yegOA=v zSq7O9c3)=RiiSSPoU6=F#yf9tjUV;Wt-$M=GWiG2`2J;-)o+mmoP$o)fV?%0EF&%y zkMJT zN&3^`oj|L3E|gxhJ6f*+N+ zGX@4vRy>E2u)42|BPd8qLYMIi4g37*vt z|FkJHpNJD9eLVQ-kMG}0xBg)RxU7|n>t7H5ud)`ctiuTUv3EqgqQXW=Y@Iryd;n!5 z42d`%4SMF`_@Xra+_paTSq~+3Ac$~FBAm5Ci7~SMF~MRv zQ>Mg0G;_Hzqm7=x05MQ8fUGNH{|raJbuumIRgq1oQ^h`H{Q5?{SB05|8CEuU$21zv z3S}s0nW^hfMTUpHIwcdy+{bD|tTJhGhFx<#u;z}7cnX6%{EK-{qQ*6)VmE0x!`g{t zl-_V)zhazuXX>{id}$W+fryD82=*I~PFrEDWvvqL!p#JpnJU^PBiFBrM zHzD|>n~C$m)Dw5&rE^|^1BxxT*<5qi4Zdp!GcVBkNAk6D7`#R+rFoLHiFx<9hOJo_ zV0OxcZxj%BibstS&iQ4wUEkC;rXG?E0Y`H=-nW3`hJ@yD6t1KXP6py@3~m zk9fIDC&{`QOCnY~R*|P(lW!<}NH~~Q581kvwmp)vk5Ye}@1Ont-KJYY$w%xVJ~mbV zef<9+#mXDGtGQa)x{&?#;}5~h9~i>FK?8Lg6;utZuL#h~NE+}$s$Xmx)C1#0s}_XM zz!g`hL`4|_o9^T2jL4VuJUCT->K|U-%u8~g#~I(gSny1jvA$_qJj*=O^7u{Kqx9I; z@}!?#a3A{R9(G>-e!C>}|I8JYE4&mrOPtkP8eE6u`!j5C8EME#zE2b?fh)yPzfTou z7R6jR&WR@sc1HkrAgjdyZW~1(X4^~`qpt?6S0qhh;gAxeE)mrtqJ^%3D0Yx&4yoEu zN)la|N;vVm3=^5I8B~@c`~26GUd+>U^DD$t?hvEX(sJr_WhZ@R$|v6dcaj3{6wKn* zkNsI@vkr{q7$RK#sU^0JhDDL$MqxUxBqi)}%%QMrKhP4mSVkJN$|_n5)mY%x8xlt) z64#g#)W+W)L<+Io_t1T^G9%0p&F$Q@8f!|}Gu7cjjoJA?l=6skW zsIW#1JLTx@c$r1nxUxU^l%v?kY48~>U7Qn({A^VIqIhp>ubhN+KUarvDkic>lTg?^ zu01CcvX;c0XDS!6nj~TqzU>&K$l=^&5%YQfhdL-ak~-|7Su|sXS6~hc#ht{r;%C^D zC>mQIy=T2#R%S$l$BMJE z7iG-Vv6aMTZL3qfg_lTnoz7AXhq1u4YJK|IV73$DbW^U%qim2ucJSq}Yqr~h7qMdm zjro;X%2SjXHwc^a610C(Q<$|1r6PzPdNEkfku$B(@4|OTIUU86Yd8F4cN41nxzX$> z(znVIvxN_RIpT?UIr{jC6N`tIfJ_yVdwW60*p?|f{$tpTy{d~+xGIa9ylRbN6#dej zbdBCsJXCt34xwP|>$sz8esi;RW$Mkt&rQ3Hi@mwSi@muE2`xC%LQRFpf7X#t-GY-} zf2)y1XI6h{q*&^|!1%!TC3D-bpZiGZARPX=SYG$n&XEY)%Cpt(@aFmU#B&BZQC1%Y z!wq@4oSM%0_pgjP3o0C03qOjM*o{uMAn${)tN?kI+yKupQVutrWOF3cwS{$hoEGLu zrr?TSNHD})n&}JMFW5ir_N~Js`>YLY;&sL+ z_7HD$?F)Nu$!5@H9KJLx=X(rfKrgA~cC)Jp)_x%*Uf9VcO;8h|S)1nWg zoQdDV)R_83ALr_-2kviKhgXCQ$XEQ|K2XypTNq7iA#6Uly`I?gpyT-Cw4lF#7PrS) zIk=e0VC%db5kk)vMJ@1#jn$$XTLYc=liG#hfNn>vA~l zLFkFpg97t76`)7pyBhlWj7FUI;%-ZTFoKCBvJ$dDz_V)bO@N?lw-;2v66SF7KSZ=;DKz~t2Xgbl2;(airO*Cv{Z0={Y_w|++qyAMMG-Cu>cd`%T@bD#!7RT?0kFC`V?@B3g%cCr3 z*E{G>T{JUodj|xF`nNj@0+s!d`e&G7iy_;%B-fb)9s9kjU`P#;ELMd&JKhu~rcmhd zO%rP8cH>n#8daN1%Z=C9I%;K!3%M!UMobN`}PI2pEFlSD;u*w^Agj|8Zs*LJQByrP^Bg@B3=>1Ll`|Ablw zfBsGob2_}?`0|CN%mWBdL91XZ*)(9nzp7(`J!>e&_XxXP3qKrYMMi;-gmK6PKbYSM zz_yP$c`J_JKw||Q9JEf8Ij^tH;QOD#8=#1sRcS-(a)=A8DUNI}6P zEH9BCkf6xm{;LR(aCT1*_Ap#$I>wi*h^1Q_zGW>Xw*17ZjooBNG;LSNJBUmtZc{E1 z>o7CRthJiSP|rA2otcU%$U3!|E1kLByW8jO-_0_9(y)=qlt96{s63p~ke3N4x@+wn z#;~6ED1;c3bk@_#ZLdz#@b;FKr$vkMJenw8BA><2TY5O zn}&?G6y2AK?TulBFUh3@Bk=@N@-pdbwWhTK=v0yxI~S=?vnm|bO``zF$yHiknuk}) z#2M}r067!L-Y@5h!=T&*VzjPGOZd&iuwz05JA6sS3HcTKc~nM1OtEUW*am&(ie5>? z>dxXND(L9n>s_faI#G+8Nsqg4G?o`KOW6khVIn!m9H4u%>YH9s`X1 zvT?f5xf4{%2)xbBGy$8j{S+^HgwtSIpd(HlQYc;@UPP7TTtynXld&gm|wRn;PAGSx>5M$vg zJI!}t_}sn{_O*}(aR!AHuKzzrkKqI!|rsX7`pkNcG!!R-@%K@S-y64MK7zDuB z+~5nM<0Yp`_%}Tq(1e#tW-FZJ9d&qUGIp8*Yyz6a(m)z^o+yMx%VIDkyU1N-qI4Ee zUO3h%lX=GI^3sfTmSZKOAr$%W1vjV415HZJgfE1co+;LAz5#h%szXo%`5^0Tvvn6B ziJ0jh_<637P-|%T_f3;Ez^*ZesjhllI!=o1tP&iUs^g+G+v)8mZG1C`x(63|=fIlWWJ@mKq+oda1^5 z`G#c157DJqKoPg#;vf#-Fk-0Ga+NDrmYB}4+3X2k*)M>pMS1RO5!$&_GErLSI9nR) zj5rCqWy8_6XY)pVHlqgQEq_^vx@=;_eU5%V0|{%;hn^fA(pFo}#Dxx94&JS}3D@>F z4bZImBp%(jG-FV<1PkAG-Lz&tR4Z&km)4~NP;s-YZao;8@+BJxpLaD6>m# ze}+nbId_~*_yKMbOi_fht0-h540!cEbW@L@~X-e;h*aM>2=i?gTsi` z<43)x+P6WcJEVp z!=IgjyLDM!S2DpsxSF=@9c(1H=s;l;sv{h+Z75wn`jZ|02Pi(ihmj6Xur57mo6=(gchnW`!`qyUKNa^nOwah`I*Rr2GSIlFkH06~zVr~V%%X^<8rizN!^&1a~ z?a3V||M*RMUT|=0J=Wfy(+Bkhb7mhnH@!YzGzkujmq=PrExdh3jH`k-egEsdQ5V~p z)@Xt58fSeB;Gf!XX~ze zFLL`HlF4wLzKDBjCz+%;aK3ID%(^$a?^*1@7rRWIh&gm8B5Z*n#4)R4d5U38QQrgJ zLnUf85mIWwfb#zzXWtkcX}hjF$;7s8+n(6AZQGgH9UBwdwr$%sC&rw7d#!!yTWi-| zb?Q|2ukOFk``-6+QCxm_>>D@c8`o6uIb*^vM<<8x+&iK)eKLS&n~}k5GY!MY_JcaD zN3w655zllkw;V|*iAurhlQw6W>Z^Pv9)0L5?ZK9=uw5lcvA;Ok0j@22`XAx4Y&BKM!$Pq-EqbVJW#Bl~|p)5v0UGenpuv38*sw#1Z=K)mn7 z4pX4*0cD14!r`+VgV+tFa1ylU)<|-%9_@YieOeO~b*C*$jBpakPN$L0JUi&DSmk3o zB#!uz=2i5eMD)h<6Vm3y>#aLDflauzBP>c3u%NN2{m$Ep-#)^g18P~%RG@9<4IZM=) z4-8a6B#|nPhCY3BN~+`OT7ri=JZ ze9P%euMX1{h|>%FQJmbI-skvzICO*kEu_lySuU4CB?(X!eO;MPL>vE`vUU90vSbwwk6B&=#}7Ui#pIp*Y$7E@~+)40_^uhwrUlGbw0napf*Ew5Bp z+c{|N=A<1BScMH>s10bW&Rl>Z7j@zDS#cZlBG`*F=9)CF93}Fm`LuT}Uu4%AQ*BAJ zT}du*wfUD72`<|r+YK3Bj9dylqVF*Z_}K**+4;~nNH)XcD8s&zJ2sMgxNQi3idYa~ z_S>(3ts#^n$I}to#-SvQ@m;Fd9@`6HoN2wx0@5`o_3^N>cv&CO3Z6Mr-py! zwnx5%m+gXUA8hRl99{pzNL&rM1-bflS31Y~PxmFB|NXx7x9sMB&vE`6il(BYgsp<% zD_i$MprHvNqZO-Z&3F{ZR@l^lRgg@|{Q84jOoAO1$}S#IIj`q_~{rDMfs`#$^FXWJ?JK67*X&-)?!w|)j94u2~|m>o|P zyPZh{M?~`$v_Xokh#YVuOLrmIZMgj+tfBuv8t{PA{^y$f&mP=@b%vLn2nBl0G@d{bYI7-pYf$8nWY#^Sd$uMF^ zmztaQ(2brh8|LA1SgNbFla5WZ70WimlU3TISP!1AdUrd7!H&S2 zr0bsMs_B5tMAiD8F-?eh;4IMbq-5?bmU5F*2$Y0&T2<*6j`r_UA1PCRN|NqN7}BWi z%VZS%&4mt|kwEG2J0k^z*(61Ewj8Zg{Bw;RR@E8nRQk(|?KWB%@VrJ4EewFOwNHhgESt(LblnIB+Wyg#b6Cjwk zg!C`@S|qiOVz!?q9t4$oI$!4R=TVh?1umUFBj5+$)B-Rk;_unUA$E~*ol=(_fY4nw zh5`U`x0tQs%A1)0?w0cRi1A8LB+*LQbE!-<>(+lxB`m{Av(&|_!Q!OWBBHxky%%XUevO9RVa#jNveZ7cU>$C;wz0>X4LjX9wj}2q(|EaOby0_A}*j30pumn z;4YpcsH;jI{P%hsS$?y>TmAUqe3QJMzm$gWs#e|<7694#@j0k90nhJ-(E#{PZ}83? zoY&_it#2JcDHfnpdmFBID6_u7BQTZy&7j@z_g<_-4LhV^hP~y?Gkvzp3R@m9o{cUY z5g@a`)r74&Z+z%wH=W&rS}OwjjuiXT)`S!_;Rx%&erq<)6W_OfV7Rr8d%dMUT(O;t zKv^Wi$T-KAOnxHLR%LmFG)il)3x>iI`@82@IiqHssvG0Zz8pU% z`!mw#Gz04~$KC*ERsJ08QR62NWWIq3JiWvn061{rsSn?(qx0J4f_CTHmtz2|*1k9K zWhelawS(K|YHfqxM3JZeyb^D+)&MXPh%8hnm!RaX<39Ney;ElVnWo`Z=7WEgEW@reSIl4w&p)UQYEMme>NA8+@XE+ed%!gaWOpwM=nbDl@#Z{{_?}?rp-u ze~EM?A^!g2QTjJ-RN#Mq@%&Ha-~WxkBl#y?`cDX7-9jIE8SyilRwHz|*@T852pb3t z2q<2k?hAcj91_6+N^i2Vprx;HOp148g`b|7z z%gl4zY%Z-io)(`d06+a>tHa}dDsC_;9Tzk;p4#= zrnMKwFukVY!{0vb`|?a|%RS#p@beuG7YDk799!LDdMAha0WxCh29DFbbJ6q$QY>#x zyn{#8e4erDyQdDYX(e9 zQB&8;(SS;CdwOQqU}zmb2BB~l$%LA zgjLGb*)AYhUhw7pmMq67qKTX&Xkq7+N-mB~WHtJNPp_0Hb_I;A=PpWaY*4kFOjL0- z(qhb6=J0+ooLMH#GpDkKC8kts%mr%>mm!&^WDSwW=w=*c6mlK4OW{H7mQtkP%^&exdI)5ls*~iSeab4G+IxO;2yuCMPcW`(lNqhbI=erR2tBEv zMx9;Ez!(_|C=Cb8_PN9hv{d||5SH;jJ=_#m(88z}b91-3JiH*5*Gf!GK+|G$nn8l# z^4tqEt=|SQ)g38CaW85>sH7#6XDu|k#W5zj-(#7+X4zfK|)6$uRSB~ zxTNyw60nkZjfm@28IF&T#`|HAvNW=J^-}P?IN5kO%d&`NsW8M{w6R_@#ipFNpupzc z0%sIO&bZ;C#Br4wquJ3_aJ}sQW)+>IcHDy->T%Cu?AAytVT_ZP+8xaG?M7(s&iT=p zmS6G4=v}y1?v@#pUre%>KJ^``Tk*!q9LhJ49OWI2l)n2&eu0oL70!82Oz%)q@356wUu7H{_ zZocPiPMGLyL6|AtY#)>+X0UKZfYq!R`x=QC)k?6hh&1x0a{zSbICO2R_it@VBAOy4 z-*gIj9;1jslte^mZv#joG^n@{>SW1EYw7OKsEDZ4D7A_eiUX*I ztXVPP3CUnhL`z|fz~^)yZ5Yi#I^RPZK@KZmQBSC&wyU{?Kv$B)K+}BgXX1I%jI?F#X?!)k+?ZlEvI}}H4#=76sYb`?f{TC#cJ3` z@(SV-Je61>ULQ131BR%jR9jhBE?+HVysEog)RaR-Ej-k7x-F5==*X1Y#OuJzB6NjN zfn;Sq@#0jFxrb62_eEQ>BZD%uZ z%VBR3nZa>FNiPd#=A%`c>uov>;~SX8gN#@>#v?=Li%LRpI!BQstx1&=+~y^v73~E& zrek^cQwpv%dA-q&v5a#j=4}2Tmqh?|9nRr1g;EKHV>!z}%c&iaMaAXh*`)w+6KHD; z=oZ1n<}Pbv<${_n;oK;$Ezb^h?4;jqgN8IJUV5!(nAo1iMialv);as_S4I#P5K_MzlmJ^z@)o;5`j(ui4@ zlvj-&WYPupmC`bag!iv9i6d<$o6PVM6_W9Xs8b0j+Gd#jqG5Rmv{)S6HgRUgFQm1) zGMOTma|JeY7uI9~*iKfhZbDMJMZ7rj1&b zZ~&}WB#=>OjgXNexz2|eDL7Ap)6;aC+|M-IJeX3Dc4E^n6ONN?H8kso6Qh^%;^%l# zZ5dS9>+d>|()+F}@`sANjMb@$1q^1Weu3*CM?M2a2D&+hXqZJbwxNWu7`HfX~CMIpqM=!}EOwIYFPuPv(=vbNO8gzkDz&kQ3)C zAVO)@~02F5bUi^~)Y1v}EukttGo-c1W=G4*# z^)?KHok+wUzn4+qzqUar@*S}abDBIDxOrzOV6VjbyRNF|c1ao5XopIfg0_DERxdq` zTa9^+O5B6~?GDeeqIltDrdWQ3Mr1}L8BGM61V zxWf}9NHXII=gWc-F3JJ5d$Czh%Sv_&`@{K_rQ%4+gZ6PFG^4Z zQb(-OsxqRAB&Zh~~*i;Iy6169e-ZB(;kOw`k$#MUVAjFB??O=g0Id$?eh_b60~rF^_{70Hhp z>d34;A`A^2S^Bki%Cm3YAMuJygR}%a*9qb0yjPSR3(;Ybm$%VYY0$|5#>C~bA&}z( z>Cs^6Y)iK*17Q7;(9+*cnme%o%S{q@kjvm!WYg_Mr-F8Df>V?QMJz;mZampFA%)~&bSFSu=3l+9Tkj}cNV8pVK&%|_zYTXadf z^G{%=%$EQ;xMpf2z@drvlw#&lc~C*tYJ=7Q%N;P=qggOp131#Vts!s!KmJo+_V#+V zYW!U1*k-_%E){RT$GH#xWq?wg4grna z5g=t=wd>|><|Q{{=lAt}gZnnE&kKjB9_^1A*oaX8IUvdRmE;4$p>-59Gf! zm#k6aSePZ9o4C5R%24SBA`TEBx(pXkBy28KGDG}nOEBY9_5lQ57!`nsLLv)t4Xs*) z@)8Um!lglj0*$E*qI|)xF>ae?$Ifsf>%goee+B99@6_kqWllLCmmDcwf7FKLDhAO& z>(qM)0U`KU)__-RUu1G{yGNy64GARgPoDm{gp%=ha~d z@}N0+U+BPKH={W@^6I>7A{Zi4&~%Wk-Y4FmNGPC`qCtE3bOOYRL*)u9^{RbA_XT5G zFekOMh$qmEtJ!;8`j9uD2|A`64gkS(Bzq>jE||o>6d`06NWz};xlU2@qGAWP?Uz&s zT^aI~b1n?QFC8&2bN5Kx-kq_x9-AkXPlbc2TqAr}NiA?iC0E9iGvpa*Vf8?9T@wTL z0ZKUaB;Z{}+2ED1AWIr&#dJrLNoSMPclK#;@c$WSxg>Or37^cP2%d+m)qYT^;I-XX z@dQ~>RJ92c$ip!0&WbzCZR{QdM#Ym8k?JRsCHo=9WW|@C)DRQeHBcx?w%+pOob-zJ zrfk+{Dr06p)UG1(9XD_CQCn@ZY6aQd4GwRf;YS6EDY3>DbPfGpvf7hP^5K}}NdUvV zWjQVbgb)Z>br(1Ark^8C@>6PbMp|c^k7BccGm55w&LShm&=c6!3VU3m0FH;(_iKS{ z^PQ}bb}V=HyKJzn$c}3NZNaVW9`IP?BsM^Q3sa~#f);r<@C#De#tfL9swZ6HX z%HRqYo=@z*kcicmE+_0S`5?rfRiZNz`uv~ef< zGp%1tvE7=z%*2}S!QY5sUegRLKrmwHUTt`pU8OlqUA;fQJaGR+)8(ev@c?qW=^K&) zPm9bm=u3x`!9HixWt@21UFA>~Jf-7DhG~RT`++jnb1zg_df8|OZ_RuFW{?LA_88X- z5Mhbqn{4!*9vpSY5napZxP5lNxO5Ss<(K$ng;|yjBm9<1O)46sJj27sD=sW}FO`MN zx2v0XIC3uTLKOgrYUEr<5E^trg2c{%S>>B#Xz>a{Y!u@Gh)mS@9sZtj$uy!y)&oFTNzA&SZTS#%B@aVnXus9mtphh$=Gw`TU6Qfx@YDoN=zSqNf-uhN2c*+z?h_lCM9RYdC4NN|kGw zbesB^LboG7NEK@uf|q|)kCG4YqLc)4tM!-M%0 zUuML=8w#ZV_oMm`U#y^kqp+ian~9^cotc@n$^Uf~kSLirOV}D)7#V!IW+iM*?f!W@ zRkrQcl~F!z_XZ?xPSPth>Vb^-f3jGa`C11yt*d9SSc%S|&Lz35M_O%@;g#BO%1Qwd z0s%Mh`;)uxw~9j}A(`_K-ii_Ok=tlCnuv#iuRmowUA1n1simv`yq~9Rf9voMr8IWX z7)%0@Eo&*;yF@|hNEm=1LmnYz3_i7Ylo@;|3Df5Z#&%~9&PY{Y%;-ai*eyrVJW@{V zU(uqXqOyg~G#}UB(n(>yY}XyEwVsF2*4t5jh^sX?q3yRg>C(26V4ksJGH+XI=t-ZJp83OZtqjUCSfYagoVyw0?Hw*k**{GkZd< z-C5!`t=H?@7%sX1I$V;RX;=pyK4-!psN%4y`l;%GbBeAd+D?nf$S<&T<(_>G{yKCB zB)*)+`W|m*jg+`<-f+-yK0-UMqt|KA!1yGM!R-QR98;Cjl1`#dz#cO;en|I7>0Wxzv+WI+UxnZ)&meFH{w6lSEMqWgwUm~hZea0R<63Y>z zaPG6vpK5FQ2I!0`nPmf1C7w4O0TzERI|Oxsbz_*9=}FFU!5*qug$IG+4?k>i%VSwf z{cD(*1VK7HDepv<)SPu~hH5vHf5yufyawKN$x?)`3}3^=`&e<$*>PNcW6ehLmPb=b zGd1f%fn5DFpG5qldKG}m#MG`rSY_SC4MyNq$sDZK6aAK4-fKyC`xQCf$I7zZm9g?mnZou7o_Kjnm+cpk8yrpt&{Gjj zTzO)V6>i7Kj*y|)CC`p@iIcVBCy0yu6Q)kbR(Q0q9rpsKNm$G$as(-Bjhqy){DSQc zhnPuNwJv_YnX^dt8FS)v`fx)AES(p4zH;#K2XgE=arlfGFJ@X{7(MYLh2aJ@zpLm$ zs|!B?dxtPy8#;54^Qx!K0k0akbJl9a>5s$$LkMuE$)_NlVWzg~%W_%|XR$K1b?555`L z8avuq7}Gns*xTDVI@7zd&>Ps>)Bm?dUQ+O%(7KX>-JCqiC!G;W&6u(Egv{njCfdv* z>FZvUsEjlVNHjL{?WH1BJMvo5M*QxHttCmY?5Q{M!A#HI>(5P3tgu-r7es5MRLZ+MndO#k?g@=jOW`wB(KUO(J_4>4R% zwrjQHzDpw&(Hohr#*xs2d5T8#7le(0A0uu)49PwE_QTi$50tS&7MvNyliYU3g0;h~ z>2O_Jaj{bl=8d^-Qd0G^Cr9c^>7_ql*%ZBqyMul$v0@ju^-o-sX)IiaL0(d-smm7W zEMsYy|`m~63%VyDED{pe9>8a002YMX%=Qxi2a84tx!I2Dw@2t*xaQc zZdQINPDo^!sf;V4#ImJ)AMcT(e*?R>hK24%=6<26WV?^07Go*ePdhle1?`xunX_1_ zZFLL9|K8!DCK%;I=1ij?1>{?)aIyrq($*M!jh!)xefW~{35{T}3O>jVQ`Jm1;bOAk z4l_xY?9XbQZ+aqpAoTeN{`d;cf+A{VX6nyO{#}T9YT$^7ni|vxMgvL%ngjPNd$TBV zcONaG)+cm`=5IJ@Y((=V13;@>Y=*c#!+XYJ?dV)>vaA6Y83qeSdVh^7mIMk6^siCX z{4b;G-?ys&(4YEuVp+oWYiDv+{3;Io?;`OmH7{=zHPla2vQgP&2?A^+@qik6arRY~ z@p^&q1!Ik|C0GJT$_7;&SMt#rA4YDeTuYn9rLJ?WUQMaOLKWo*V~HPHH$a-_T3?^H z&E}=4y-TtuP^pJ~e3^(x~_I|*e&r4qbH95{iez~XN;XH0)(eEAV!$McxQzdx%Ok*OR zX|VZ^gYm^upO4e_(1pF@qQyjb$o5jh=oue}J28ZM1CfagiH17eamZKaC=vBc_tjRW zk%_W@@34Ry)I34Kb*<2O#NbGdoyN`ltSUW?W5qe^OLLY@nSYJVoS??BRO)ol*~~wC z<6s#+a26ad36IU342d`|IFJyTZ;M~zoM;l+!!)8azKWgst*kCfHacd?>6^tPxWXJk zjZAiwbQ;QBYE33nAMGefh`}hLjmnv{KVytXP+dMMUtKi%W@rh}D8rQ(o2$!S9ue)4 zDH?8ySN%PsDx*`MyioSVbw&glGi~LKK#DN)%}1cgj%>JIq zJ`5S?DXE~mK&b1Q37>!oVTR1(;4Lajihfy;6~Rt)BSUp%PHV};iDymn(TdEo_9`tB zlPQ1IgriMbfkABJD?{2PtqxSrN@sSBY^DO?sv;|o5+Jj&ov$QC6$*pPIeNX9(3xDL z&PEi$-$*H3oe&7sO2q{J3V*iEB0(Vg05WmYl^9+%kt42!^`x?hP(2-DXs`2*E;b8xaE=ypm$A|C;JrI>`?9#|7Lop8&{Q?zACp%&f07+S*}BTmo6RoQQq z7fh%&%SF{RlY#IjzL$ZqF%s323zZdq97?M2xGW#PFjFFobneb9(zD4er`o;KXbX!h z0wb738z!tqCxv{t7!%x{mbPj39GW`7U zc&`F!Cs+-xmvH0aEhLEFhg8QwZGgKZxf1P!M5KP*%TDSIy^N$ZuGnnZ(l^GQeS_;7H>fJ_3F-Y@fm1s3 zdhQBpHUJOyorR|PCHP$pw)^w>$CB4iF}l4 zwuIU0GfApra!S_GzDK6+pzYl!B;^akdVYDZmuQwps|uC_XVPBd&oHZ1l~0MjUuipf zH_LZcOfl%E*U>{H@34O4TvbTB46_cKr$$;%8=Xy!*JI+TReSi)#o&M9Z*a1YYwlc3 zQA6pdq(+1Ivu00o4ya6G_S7H;`7>wV9hha_2i5um(Mb+&^MX=tm`wUWPh}5MgVb*9 zApi81+lcFM=l@9KASe#W(uAzg~md97g z6Kj#uYe$PyaCG1-PaRG@n2^YJ;)U?aXCME)fk8UrXg;%;D`J_ZZ7vu{utxS{WwJDQ zQQ^gL3~Ng?#bgY(&x{fEk|Ks`Y0{)#lB;(&9y$WJ93o*abM$B7dR{i=kV^tK)48y(Lv-UZp^S}-dHo$Hb!T%`uk&gn< zn#eA9EcDpA8B81@6|kFk)rDt1OHWNPYUPTA|(XgP-c$BuF7Cl3Tz z7yc|1d-EChp>7*;I91tDn6ngsUOX_FI-}W$(A;HXfpE)FX-T;HLq~&hRf}97y%oSy z#co))xi|BZ>|&5F@D;YodVq<|5QrLay?^m5U?t`ecBI;=!7}#3F=(uC<udqDHP3%OXT;mL)7TS^De}5!k-TwJ&)hiAd!V81^_U#(|?{vz)Cye}uqTl}! zI8XAAU;YL`Xh6DaFS>ljm=<_o@mgW7)*U`bur;yFsc+T%Xo^BN&}t&Z9+#?F%{I+0 zV5|?*=!i|HwIGoAPD=`c888o$5Fb3$n`{li0l7kAOz2NC!(x#^CX-|FlT9LnEb({d z+t!45KBDTo%64&krq`E6H~T|^zT^IY`5T&22Z{~Hp$LD7F)K~K1?JSqO#zJD=uM#_ zPqZ0J=EKgoZlZyBznbx98O-$9O(y!Wy{`Q)dLbM=txg06$X@#k!q?d6;Au+{FE&t;kKoiO}~yZ9iF#IXEm4StEczgXVuGv9?n{AO>I z5HHONpVi(wP`Z1puyoOZK4t9vC^`Pnk@yzw!ou1?U1{mQgMTXZSF^s0CU-wD-EM^y?J#RIa$O7a|)GQm34 zO&31IQW?=MRY;oPi!NhLub4$46J^DcK|NuKwlvAEnuUorwPcaXU&Wf79hoVS&G+QY zr0(a5rrT|nj}ATZG6(8Ky@Wt436#gt$vBZhDr-)!l_$c3YaB?RJ)f(UAJ7~{5?juQ z6OX~{U_8wg6&ze@!K_2lAoy_|Hi&`1m>?0^JYH`cf9mh=PPFK#uMx?~Fk`59!D3|k zD=9TaEQB-3HT2EUlq#zRB7;6+q^$C7^1G?jK-5E{_4%qDs1mgrvw_^g5b*@9FdIfp zVTJk_;p8(lT@))^@!Sd7{m(_)B5Bo2T|tkA&4?g)bx3OC?~(bB7ua6EazxsdOo>c_ zh0nPw5-|b6Mm|Ah8)T94qYQ|Z<6Mb~02AtIZTUc_9uSwK@mY^b-b2xjc zN;BZh1a-R>2~el!J}yL7^b~U@V4`=6KBSX)2u-+0LwrXjBWPB=jZ`d$1ji=xt<3)! zqnp{ydPUZ#l;>=;{Qkc)HYFNCATsy#Xn}fM(L)KU4_S4pbESF*;!a0M=STeAc zp#;K9TOa@+ z_L9|mf-qCl40O_Y4(l>+vlN6on!|mfPLR8zVDM>tpApf{Cll7F@Oj7t6gCZA*GMmi z1i%4z3Rn7k1GC~g_Os@0vJyE*N1i+QKrJ@FkoF@l7vL3zWM!oX3;DEAnh)C;1~ zNOq~PACcRoMd*~`+*z%EWu1)#$8Hq?ppH}8E9_2yDtEB6j!D}{j(DW5(`t9&Q&{95 zw1~|&5b(RMV}6KuGz=USw=Xf^fRi6(r-!e@CQ*W!GRVGPomF=Q?;c7Co7~N_eg9N= zCce1j8xA5E^kD!1en%G9ZI=)C%^@EEk;i2ye$oOO>as!AkBIXat5-}`!!L&=62#zw z)9#Ro5dsAj(ul>ysw5B+%mvt2fNwl$2tzv;-~TBqWnL;fexe9ZyKDifQ#60LaF(0I zRhh>o=mQWFBQbnPL!_%ZrJz0>6Z&CAfC_NJ(KX>;-(ip;H&h)tkr~Jr)m z(Z^Wbo3*S7h9%y=A%=J6A@JlS;p&hHdD@$H<_QSJ-5dZ1!`ZATcFn3nnL7;GMZpOj zxOBWVo$a90=9J)YW1A6$x0!2QUAf4m<+2$Jq)E%-D-d8T0xXv*71PD-`8+$}2KMDS zI!DBTYa3$V`qZ|hPRAA)fMf{~BQOZ20hi3PtzG4oJForN40^eCm2kEOQ_?Pibb77@ z?=RKw)-T!%NeF@A<{i~?oukWgoyW`C5?Y6pqR%rvb7s9FmuO|WCLC2!eOI-HF^|CL9HEeNXy6_^e}^Hf z9Zpj&qUej)SQm3>Moednp0UO=Fxz=yNX4CNke5BM*!3~y(#}@L_mYV(27yw-ZUn7f zB^}*2ojB;$TRkz7EE`NrgQzCXC7{k=n}$s)9(s2ZHyUm4(OXpFAo%g+%PU+s25avt z;i~)@B_9XAXN)ZCS{8JA(JE(Xwwxw!#)EPN`?dXz+VV-=Os1AI;TT|9 zO=C+N^?fV1lO%M+`J+P2#lcp6hBK~sE3=dvMc@H7eV3&Hku8dyc_#66xrf@l zx=j4M`QcrqxG0-|Nk>C)))FPWz6hG``#1@-Iy#&*5x2s@6$`%~(#bTL5hkRMxHbbX zh=E#?xP-Hl*6uU{&wk>ae4v7;$m58dwTXWarzIQgR?qCTUwTA5^@(QIwU}n-bhMuJ z0%vwq6t}86CI);xmO8BSOmP2y_u<5Cp|X)U|YB?`{&WyxM(gTR5YC8MJ*zt#(Q7 z3n{=b?0(V3`-Am#3n?W^3xl5N^qD|`DNHJt{Uh;JgX|K-uHi?ET*)T70#{fX-?b7& zyC+7Q-5kQ$hfoQI;Gu=`rGfVLeQLkbt%vme_(V6|r^~M&jnKtA$cgtYszrp-czuaK zXr^o}WsS}h7I=d#)XX>*puk<|q(c}#Xs%6h291HJ_JmR?>n3YzO|#_TQAlkCP-D7{ zLAyqwyrC0rttNWx7?Hb$aD8J~pTD=Ygv0LPx~B5)$r9ld8+rpI?bCVO(rr3Y(z1%NlWT>Q^RfbluMJIJM{8SYubdNWXvY{ssTg9YW~Eis zrTJ)z&k%_!Mk4I}RD-6ui9l@L8V{wI$mNhy#QOrmvSKJ7EX6d62r-&!&2E+1!?3c? zV)FW~OFXX-jt&R1rl%mw{6mWj`DBEU7wbyqXGQC4laCD9UuK0SiG|@!MgFL(I*%jz z_)oU#s>9XMY7BU4ML4XiuUQ&AM#{@ZD#%DNWF^;8P>a@R$C|tdBp>>~jaPZI>M*Iw zWnrmnN>ZOnt`wlXjuz< zf_+sLL4m98-T=}A4064$AXp=vvbdKwwQyly?h8JHaV~SKYu)6hD{m&5{84%G{W%Gi zUutj!s}!YQy6i%eyjdc-^_!H*62F zk^IJ;?~qy|K4YF>>m|Qh@dg9qy*OoUxY_nxlCK%G;%^*ytdD&?18X_+=P$gKXs{Cm zu)z)1J134Gbf|ien~+kZny~wz{@fx$q?jNiJA+Oc`S87rr+Mpxc&NU|BQBUjR8IJm z{=~2;L`YwMFHv;cRH#y(G^A*@p^%XjRALs(l`%*no=%xvI;(Q|CFWC^VUnS7j%HTj z!tGoiR$i~msG};>vR6XI3h;w5kg574<&X$`tY;<0nN^_4$Dk|BSV^b6$YUW-5n2%o zb>PMArFDkh5^LVmZG(N9zjRN%>S1g{j+xhS58xXqeHY@8!XM&%rO+*WyH$F{&@BeP z0fVhG!OxR?<(SMf8Y>&OrZzE*R~uF?CMcJ)my-|jaYys(FQ)4l13gOUz)~SWa4hS#n&U$Y(o>Y6C=%5o+&h(L=L|b5C=EQ`uVb z1ybv@e$&a!N0$e(Y-ocPDZyE4n~v!GKr)Y<8IF!-VZTWeAO~l(B z@WOHVO@TIR)DprM$@j`*n>XwLKJ9kz9VNSi=t;ERmgv|cJ|mvt$dmC7FhiN7)|zcy zI&{xgl=vh#Be@Sv1vKzw2y-S`h142YCD;+48_6a7VPVS>4b^;OTPHXS({k@td5kwq zm(8yqT`mM@gzAly+x6cdohMD{qw$W0A5wyxNL>Krj8J(4ADmS?%ZHMCJ)+UeZ!O*{ zC4pjgnPnCKpknJ~T1owUv4(zb8aIb_R_ST(Uv*e;=7!+Lm3M$^7zc7Nyl7G?9?Jn3 ziEBPxHu>ao5&*Rf1B@We{t&2gC~xh{mtyxV0&VM>4SVSS3bc>@o}RJqUHN@(A?rL- zr*lo``U1-d#nxVc6sp(R6|eE!!Ic?fbLy&cs&WTG5QuwT3rLUt+L-z3{MC`wjlHPG z@|TI3Hz4a42m6HQIO2F#L{BJ4w`cMeL8&kSXVATABLP1186tIqH>@=qYY{Atu!QJx-dekcdi4t3l4 z9HPk3tX8L|4d&ym*80a^ejktAKdHJ#48ayH;I7RK_P=*lnA{Tb%`K7I_*7KNH8d85 zKETkK*{cxX^547;s`yjOU^e8>SaE-R=+Z|S9_Jj5=uWRf8C`cBkF7uldahiKwj4I9 zeE7ovA2usH@3dNG38!@SM2@<5vY^e+0e-5$uS@Dzoq}3gb+?!2aXT&-A3HNU7j_d9 z%T*&67b+pBV-(_(0-oGJFTgHKuEu@!5Z@;`upRFSxXHOLBwwTh<3OW}d)NTYBBX|ygqDI4;{XRIynr(4zR9)} zLm+1WuWG^TEY4TIVrrX_Rl#TM2B77xKUR*%;CA%`MApGo!*=HU5%0pn) zRDxG~kVkD{Bg3{@hI%4a+kAAEqU)4iiKvQ><~*>T*h_4(T`Ac&1#7n67%FDt6Y3v2 zA6Z`+IA+`G6~VsFBe8$-v)qPB2W~_j!6pYm16P~Ck`DIE>+ia6JPWDCaz*WCGD=Fr z)tx(LTUT(hg&I;X}Y?ZdV~9ngIr zDLLNEh*9Q;BX+rHR9Rc-o$}*Bp}7mRyFdPM;Mmq_E<;q<9<6qbT4kwS$YGW;-c8i_ zj)E-LBDu>l%)%&uT_pAJ% zQpot|G~F|5wb4Im`u#R&morl+E>}dGx`Rz-+6b?Y1AnxpPGue`R*by)68?%tHQ2>XalW1zfW zpIY>mDfCzhXwyCVKobg;kX< z<|1=Mz0ZUmn7*-_(C|#=AZ8!r>kP13`n~-Z{AO$VGr!=A)kpn;;QqfN@ct*$`ak@0 z%3t|MS2Ss92cRPY$%H|`hFKHFh(8t(dJHf|f+U2I1Ox_BMu{;qNvAprLar8@mfD=v zPOF+47Mo_q1R50=A}V#P%Uw>pR?1u|UG*A{JD!eAOz#9K`0uVbMun<3 zT~KT0S`BeEXv1RQHsgS*E>*qNne6(C{`a zL$P{pss3)Ha?iNo9wDvN=zNmRmmBpxy5F z2tN;AoK}K~JMN+-H-g(ejxu0;;RA6GcbnbaqoP|=_@8cXe086ju;J~!Hlo2>KV(U} zB17F@ym;Qbp!s)t;dR_oQ8z|@;onZgY(9rW$3VC%=C-6`NR{jZyD)O{rcRQ8tsln4 zJ@&mr0jW`?t0py2&FDuAFs~`McL@qEmfqNtB`DQ>K(dzBoZxv#+scid=04ukWL_%K z;j7F!1Pd1``;oD5q^dY_l9J1pO99HljlC!`l7@}Kafm%%k%TNXa3_&8>yxC-;v)E1 zGsNL5)eCz=_eE|NOIdICYPm(~A~N%;wy_#_j5HP2+0Br#N{rmt5{MW!w^{SU4|3v3 zb!MG4M?(_o0GpT1M>VUNQ_+AZ<2uW?6(*Uym{f(nw1uz=Iy!&Tq}>UERzBuraq7|5*@iskIN6eN5O1Z`#^8mLen_T|3MIx_ z=7N&omDF>)Y%VU#xd^7zbEUaJ-h2sG#y+&jOJHUslLVC@j$HvEL{kmw_Y-#X?PP zK)gP~#$CxDP$}x1DZdzxGTk3*0~sExAR1p=!AHkK<+%UjBGiXweHcTv>m76gtX0e| z=`$pl;oG`qEUA>{tfg1rRvGkbj}g)e0nMB;3=^#(n%0UKdW{JE!4B#YD%7yn{Q6Y~ zTWA+H_W=8PvV)?m-veXmSaW5Yd*FSBBb0d zJktN6>>a}^(UxxEPC91Cwr$(C)#=#k*d49dwr$(CZQDu5$<01^@7epi`#j&feywNy zoU7)ns!^lHcn>NB#iWUTd1ez4`*8`lKTl2e?`Vkw(Z)6zb(=x zj3AHOkNABcQ^!T5r}PmT=~fMLwOXi0GYZnXcay}>^%4G6si*UEgqn*UTZI;>a9zh- znweyh^#L)~h@nKU=GOl!mFl`K`o|73<_<3D&e(ree zK83S!yt1)0!%%rWrwZjxc;AP=pYa$*t0381QOe5ZvXJKd?x5MU{W%PkJN2d-ElaM| zUf~2Bv_AELWNepAgfpmF(3^(Vl|JjZ(I>)APfu zq3yX9tKBVTA&7~Iwot&Lq=DI5+?%YH`_8CK%|LVD{#r0$R8CVtRkE(!`KxV?)oZBK zrDppn8M3+iv#8-S39I-?o8|(AaBVibi=h7ubVR@%v@6ROG)wKrVv}9qih5 zy;?oQc3GHT7LZ+{=TCLZxDdF$M7C^ct8lqJrz5?d5E)(RS1we3uFCraR`a6s%qh`> z(w@vzktyZiGVQm+u0d|>!e6<2z%!FVnS_ZZP;aX=nazIB9dp_tGyrKv(&Q2j#;Y_+ zYr}#sgS81twheB>x|rBs(jS2I5nyfvYg#o#xDQy;+@IDjgI!?M-p@VYEH*seB%LgAiZ>`Y&x}cSiSQyn@ibjRr5rG4pv2REWt}; zP8uLs+_G$hO}M_;RLDyU*p_IA`r;1wM5_AQWj!qVEY=7FYqUM=_zT-XK|8$D;bMJY+$3&S zzD@F6p`e_j0~(ftGEL}Q$1s2^ubECIu40|0xu8U+^SCc$7$@ZL4dWu+2h3lrUb*w= zY`l_;sfxm@5|M>M-L)cn*WKda4VrTorUeV_k9pYJKV4((53UEw>$pqjFYIpw%Xc9r zrdV%MbVJ!&3p@}$0%a_7O;;l{tG`0`_g{sk_f3(o%V~$y3ogKD7QkDkkK-w8@P6)Jm*WB%ND=@a0w+^fR3Q{rq@L zt3V&nkM3fu4zyk{y6cfO_i{S^Tyk%f=4&0gAP}@yl$4y!On*HV1{`1~g|dc44a(j) zGtd@t@XlFo^XhY)8^8p=<=8`D3siA`83+O9XS{>n%fdv*Aq5kG-=1o2kbP7et|qMz zj&B-44BVIpn=p^rYjUs~fC7Pw6VMhz*3peK)h0!uZ3~8zGyS24{)2!BR26)wWPp3+ z#+2^itbnk|9H|0xw-3GdygwDTsY*{P9%0YKI^A%y9J!jpf%*vsxN`&rCTgR1lenUdUy+>Zx((IFTAagS>(Y6=*WDLEfklZn2jixR)OY zHT$L1X}=egK0&qgm5K~EtkyNIRyAT$UE`iy-@DLNuu^XtPaVukWwpvba&IgP$il|T z9y&IOf3I1Vx2Z)#*keL@zT$0JmY!lUW7 zE+JJ?K|+ZV5SpJiavm3&=XOD-8r@x55U#on-Qp%q8?1ZPM5?$mpNo&p5dxDpf|j*o zfr?txh*8iI1Pc@{XTca9KCur9yA2A9FdJ%`2npkqVk87$u$n*^ZvtVRJ!0?=n zvhyRsxF<2ZN-fz)36mN+(Gdnn8^TBn4Zx}zGONc#6E58`8+vrrwZ(<0xB;jFvB?;J z%L)vDvp`!tMl~GvE)|{Yli+oOUcLUqh>YGegXbsomoEg^|E;S3FLn<9+28*Y1Rxf1 z`J_?)Ukasst2Hpgx3qn`gJgV6uV28J)Hx0g93+Z)5KtXmYZ&yLmZOJ4PYuW({JfMQ z@FF=`95wwHWpzv`%a@szOusn5U~~Yy1TmT4PvW+Hy1qlj=g%OxU2009nGjAI$u7z9 z`pc#(P)tHwgx|j2jRn+un6<5x2)h_LSVpKmUS|&JKf}Xi*Rlpk?WK+o>p8e{as|cy za3SVaN%U?vr1qgqQkz_~fsy3&el@;g2QH|5=*&Csvl(^`4cdA0Kfj__{g4BdjM{_9yJ z;{=XBc|bw1S5xY;tE;io!SMU*{pSvkjHOZ3Kn&m%qtBKoqZi(PWgI5KPO3Kq5CWhS zGS{NSIHT{Qn*RzA?yUyt)mEasE?TsLw#jU?s@Y487(V}cF?_|%OzO)vV?usu|51cu zdu!TAW+mk!x>%3LDcQ!BZkn~^8<0Nz;j3ZGQl=qPb@>9XcPL(yB#me?To6}9Zf{8f+Fx;?rbgU{R(45Ri(^#HY zQYvdOfoH+=7vGahilVmI+nL(|H00XOA^Fh5GB99<7g;am6Aqeu9FWb>$Q*{E%s6-^ zk=+@!MfE&1PXpT~YwLb%00M^Rg)TKwS=OejG6EuCVN+oqK1O{_`SieB##O|;*Laah z!!2HPvoH?GQl-cmd9iK8Pna9a+Pj6NP;xmvk+4iGFCwXE5kW7Lea=Stp}fPw>n{eVpNcu^);h ze%JF#diB{+B$ldwWb4bA-_aTjIz-Bhwk(gg2^#h1roTho^!V-rg_r@YM2o zd%nSKA>#H=1E?6Z`^B*rZFPhQ0jg{ABSkR-`c1(IusD(!Y6n{8P2~hwVzDbLB?*_N zLuO419AOnrP4ll(~jIv8R0JD4;sD+Kjceq`PWM#1d-qKdP-EzjoRg4>u6y7~jD@Nq1@q6k=(-%2n4`;$8b#&Ot1GsZu5LNDyBXZ(yWk(pWS7c}Hf^a%W>uCQhjAG?$O3n>yS!at^h90zt+Tq@cr( zuXoe-$GZ#@OhsW3DpQj!7hK!a6F|89j9-fI%RHhLzkEHGgD59@{Tj^zkgm+tgH5Sn zbz4}%+({Kcgn_8x5Jx$`L&Vv`mtdj$(E|<(Oi2*{6lsBZ=r?uv^&`4qkkcf5>di|6H1p66KP>5yv9+nV^TPm|~xok89%@s!Uee2r?-P~dj?LrHd z{y*J%Vxn%RNI!8jz@N5b{udhy|KE)JnQ(vV+5a;`#K-(Y3jkm;TL($-mIb;ZsKq-1 z#NJoZ2kVclQ4n&n6n`#!KD@%dS{2`JQND=sN-Zmn{Rs3*-dBbcg3{+R8jY`B zL{KSvkeNmUD+0oDqA>&P{fL{_o_6)hze|<}BLdLt^{Yn-7|WjE&d=1^%gN$mb-L?e z2damayAuW2n0cEqwrS(uE1$s`I= zW>DF#H`Gwcdk@qHDKVrt#={)NC_^g0Je3V~+nG|%{JbYe@_;CxpjXcgGYl)p;X zi{K}rvRP-BYbYIaQ{-a71;;gaAazKKr6W)mmdQBmfx$qJ7+p=*8}uf>hP!BmUIH9< zXC)o$2ikA?9YdSv_<{Zq~V_fNINe}>6_3|@$pJ|{24@;_}{ z{_R()sw0Q}DVjKOF-A)*CFL_?ZG}R00z7J69;y-&Cn%ss5g=%uLws;{#ve$_bb7{5 zz(6`rwX3EWjwUKog4SLiyfYZEao@9P7&ghlCEZvnQf9ZrO{DyR}mTminKu&@bCTpZtYQhhKOjfNr>Xs+VyO0U8qOt zvDW<$X_DQgXp%}1TGem2z`=Hdj=qpS2Ft2Yev2SsW|e~Afrr?O8Jlb5tM4_`Y81yA zx6~T#q@H5!fr3O$7jv;m2WAvY)X;fS6&5eEDjJomjzIR^4-L*B1G%>M`A`X*P>NacSJls%aYyvnki>dla|0E>eD8P(pxk#IJkntk8wzP`-{|Ilg}ak}U*_ zXRgiJu?vJh#)=_=V)(hEETofH_k_0ky5Ey0ov9e3fO*E}(DH1SRW=HY8)#Y!lr>_d z42>lc*?^bD3<|Cq*(_;Q&7A52l4m4nTvK5&YJJpv%;ke4^T`|R zEpQ4A4kpqbQg z_nVc^qQmN3T?Omym9jtp-$TEo@UdU|>bM6%vc)wepL>9Z<&LUv>WVICeOC*HM;o4C zLZycvDB}H@O4v$m%r|uK%KvFV8>u&27Yg?%B0C#h!;Q+RR$u6%O zQ$aR}=s=$0s!rXhkRm~WGG<1yIXe6P-z9|j{Wm#mK z5TXsyq1Cy^9loogZK*WX%!C;ZPk3IN?aVor(nDO%R-d9g$1)x&0t#jf>{ zrler*j=!8JH~9PZm{*#-dRJ|RkGX!|Kg_lEzCZhDItOiwjc4hq;iEAO1;D_l3TKD2 zWQSklh1_^2Z~!q3vJ0fM8y9_0c@E*<8~U%J2meL)_1_gIv4pj~ zgPyg6**~VufAPCcSJ06H{uDOIEln-ZNcerNC;)FXjVKHvN*9Aq&6D@29(J0A#MwG_ zq3K;(m=);7&qvu(>j?=d$VC6J!Q?!_^DwdcGD`Ie$?_n2pd>sboKonwASH$lG#C;!N;{lLcR^+(6<2###!1$7*CfS;`DKtydx^>oBOgdSi9B?QcFK7_Ckb z8~ql%(Be^!+)J4Hj~}GTVP5VLAZLUkc$TQSRs=#O2|1|QV2u`mVv|4Q_6mN?CX+!s zg;KZn0{3%enhj+~O)Zh@2aAWP^)MP0R>ic@8Zfv{4(Sr5zI>(}aELVTG%m%@6lC<- zG5p^Y^!8;*_%rML&*#7VcmJ7z{s|2T*qSlY|5-LOUmel~X%OXoFrh7>tph;-n*cHq z28<+*AMtB<5)rTgIV)>Yoe@p>e!zT1ji%k5xLGReu_xZ~`dyZDYy!2J>9Ne}UB~5% z^yNzy&9HlI47Guw;q%jMs`tj2`|a;`HYS_bsVDHsFHN#z@CG;;C0k~Cb6dv**n^)$ zjqaml>5Tq$E{yg5qvf!S-lOKQjKRI9HBX>iG)6asTr?(E$k;^2OQW#w6Gtf0--q{9 z##&RSOOx9G!xqVH5yPj+Z9au<(bK6(YrT8p(i=T{<kaNM4JfiyX41a2N2co1YTp@tsp?o6( z9vNuUD{v6wmr+l+xJ%NcEj$FNIG!(yGr%ftN*Et_v0OxFiAk|oTIlf=Pvl?M>}6cL z7qkqDFCeoTou^^j=sj0xEX~QN*zjA?qp>zu<=kK@;COqsa$T1^nLR{PW7p4{X~Eho zH?dU1W4+moZb_T0Mh-J5GcT!{mXBph`^1-kjgbA5} z%}23+vRrUld)!uu#a=fssYzK)XHosGg~~;83@(nZ$>0=6l+blHLXcNSF$9B7yUJ1; zV*`R2!nB{$|GfvZ9<-+-nmdmRo5`nH2{bNdB4&eN6%ja`ghd+kKpH{WCt^B*X zPGbl#fs*}th2EbhttZmeKXFA0wR?osBwaAkK}R1g&~ZT{V;~UdKN|I ztos1=Y7G-Nv3#Z@%qqj00kW^QSKou^&>s3_o%GHE!r-*H&5OuP@ZM}h`lR(@q@A(! z2r;8_#KR#I#(1=ayz0<00Zsgg?9U}+kFaOtWlUUQ-gjuv_Uq&-5Y>X8D_;ERL z;)D$zUSVTU=0Oed<~r{ZjsolHG)zD7bX9tr`+W_u;%y6Z!!}okBDH(9vHd`*D7a<*DM}7{cKkwHx>sxVVgydG+u=}57O^ldy&%b{(PUKvfm|- zRJonomz3{5-X)J{Qm?5fX$E(AuWJ1tP42jnMRhj*l;%ZL=8$?{-9QqQG1yf=gym5j z1r=o{xuLCWa{VBf3gPm)U@C>9);$R?uzU zG~y5Jp4HkE_GFw5r3*Fmu`*>_xQN|D^;b()^nrwM=LxLc?t5D#$Z#o+Crt~EGj~D_ zf`wH|Le!$XPyHpNB6XTjMoyAL*926|=(NoOZqwu7hdR18miCbu=hOK(QY#>R7G?}X z=g#b4FfA5tD0&Ml1Q!{<4%Rr_%++ps_{>*JAkII@b6AG4-_(>YcaA&n{D`h)Z|H1E zqAbWb*=TpnjdarFdLCvM&CP{bc5N4R8_Vaig9L5l9QDmDNSJscJ+GT#U5`jJ>Q#Yk z6eqYm`jb&3?#8EJWr<1N{jVJ-0iu*SQE>d#{=ac9^D?3h{gj}=Mk zDJ2UgJH8&j^&{q5&Q&&Rj$nALY+|S8c$AEQvE-+6ZXR{?7#}D9l;yH(kS^(Am9DpS zB-mBTa&Ak|LMv4tQ^}8rt*DeYZ9v!%s?33O#0K|ND~`K$?+=Npb-+}OT=Z%?IS_nm zG=7C1IPENnyBXLRQOR0ciq*xMH$Gf|jL#b@&}S%~dfMYkvGxN|%Xec4HBq`&x<;6i zzI}DiLw|Nfo0CLaIJ3}4+0;@ZInC-+)$oyM+~J0rySNE))G0G)!*AcppiWhHl94S_ zPgJ9#q5{3yE;9I}&~?e5eW(50x5bdNwd7<(REk$Uh~yd-W24$nL{m9!Qlmt*ASYOu|9)$-uKSc2BBZ z>s=$qpr?}zsSE?-8H-$SZwdia{Je_3@|1MCx4LyVYp~231;@ZCb8sAGn ziy@!W;&ox#RoM$nd@&q+a658qg58CwNne=@ZT{o*eyhFTBuU_b*xh=m;n)ODt*TJT zU}&z-eHh49z3h@y(Ad35(0Dk73B4GGUoUAeRLPPXM*>v51qHIV)bs^hj0Bo80(cyB z$i1_qJ38=n{R+k1xL74&9sMJ7v0gXV;fH^J;{{{-)`PH+Sd?Ovh&XVvsqc;|mNQ2D2v(!XtE=T)`-knwsiNL*J`E%pU1a}I6TpMgg4 z*~wQHPf=#p7Y~zLb;^?(v0S%C_y~<`d(7+L_nm(H90Fu4TSFD|larE?t|!mdnsnHA z*i1M)-!8s8e=(y5Mc|hd5$$*QhHL02Zzl)GM0rt~q{1+mMUCE5g~#Y(aLody+g7kCT1->oT9cW6HCf9>oApK`W~rW2NBGl zX3&SF13Y94hAf%_RxtteV^PO%woLwTPNy2KeriRXXcL6G4#{xAW(s5_75VzndcVtB zi*ooo)l7>03A|2>C^P6i9us*|Pq)Jg>~9d0jzk0j!x_v5=$F~Jc}E>kQzA2B&MBF= z=0BecqT}B7K57EA^vvUR7#=%ASw$e^~)2EdU zZ;MGwu&rMT_{prP1yb%;P(O`LbrF(9upN@6w}v9n>rdp177KTQe2#Y%9@fb(2e9S2 zt0WQTD(4&m(iVi%)5ROO`a+PB&r*4}&CpMP{0RHUoePopq6YT~l%4$7F7vS$}25|pERX^7?{;)vTvQVB)rWG^ZvfTG^(2chh ziOA-Qd7U+(cW`aI;XzMVDL45=w(5@lmfl1?FTN$#)!-;Z8A*eZ=Mkn>Tto49TeOHM zKo65bgp^uXM``?9rG7S}(r{Q~4DM4P>to)j=;?_1MuuHBi`+#(b`@oP75Jtm_$d|u za=v)V!EDrb+ZM$}n97Afc|xdA>L{BaUa{(e1koaI5Vs9ZHxps zScd+F(Plb58)mPsf1LKx$X7^+&%SW)PlM0D?qLgkc8Gr`@AUtNH_q_SgU=5akp0R4 zBhqO$8kdb|?@!xu3@Ae6>NB5{r2f^P)-|5Qsv@34oZjo$4aBZIudMwA(tCNKvE$Wi zqR{3G)flBRin-wWlfhgKgDS+RbLM#AbXvG``3F=Q*Q`{l$zh)pB6#lpb=OxU+yz;! z%&^sViH(VxQAtE=vR2ZcxD;R=T_ZTUNS6`4mKaGaPAOC2?Zg z=o0brg~RaXqb9EOMP=0wWnE&mk`k}6!1tn(S_}K`KC?2ftBs3^vD-0jCg=B;Oulzu zTlh^w1~{_N)~3XMN?x_m74(`%!S(gY9jUU?eV^_mhKvDG%v2cB`nx#2pO-9r`w^3V z0DE!+I@74*87FjR68JrY2Q5NB8o>3*#l-sre7^bpR+QeJQ2;^xK4^xAu(;E9Q1=?# zsjr^?By`{Hf`m+;%JU!;O53$ad1+0bKYKyQBGZW2%rIb(frEr&lb24L2B)fPfmEVf6+ z{f}YZ=VH$r8^e>t1~XP2PK1koI*hcB8|PoqVybL`R`z%SA!z{~t}u+jZeu1f5PH|> zHR!dSdig{66GUctzUATO(uRGS1&BvEH`Q(DC{yzOCUn$yVgCe)jFQYDGte ze*XyTv5jY(pigc3J>sN4?ND+qJ||^27Vl5dV)hhMxAyaGgWedvSM@A}ECccP1o)t- zNW7sQqLLYR-4%px{&jr-BCghw=&~Rjp2kvlEOUnDl1P6KlQcuw$7Zw9kdAV+p4Sic zed3!Jp+fNBksmA*j)?>|Xd7G*YG9ZU$zX@KeSA~VA{W&=aI%5 zub}L+BP|d=_zErmE{=`q;HXbfp5->_EJNm9yG#8z7s#Yk%uQ?4f_5?*%9a}Jv1;w; zqMidsLGK={vW2`mIGzPbH^VmNljOt1sF9wX$R<;2?BDG6nyyeYl{p@jqZcZjckEKp z&OJoG0hxz=4{k%z_Nw-ahGnwp=oa44KIs}tW#gm|yV<8D{dGV2$i{8 z6V3mq;;WnUS2h+i+{U6Dg!a~OIX(GTWL_fdSj1$qG;2b~8;0X#rC@GdFTb&yd#`s1 zD_sK8K*Gxy%F>k9rEsYxdq`(lrV`*mmRtcl=qmTH&Dj2|Lx*d%FsG;$EoM&fOC)dO zW5QEU!y~*i#)aco{@MvA0~WL)^Jqko^-^S1$9@cI5}uS<@;Qjx$usAHy`|7i`V!6l ziF(QXwlY_>chEmBwmV{YrqyTS(FXs|Vh2akmQscuk-W@o$q2IMcY#69n)S zWou442-qClFrWd>oNMRc8E2{6Ll5ovjDs(v51vf11#T3sv3kp>3eNa|o%T(qmzkYt zG`Y>!7+mr7yjbtO&j4+N4?^Sh> zEad8oxP9*CgSYp`XpG}4bGyF)OzNME^(u=2$}e@JRa|s2hOURc3dTM zj3HBwmJDgy>v@dI1x7#EJ21Tf^->;Ow~+l+Jn&!Ok2Hp7bx@DuDfpFEri+EiVpR*u z!YijbrbK&Lp3w|){RKXLBK|s6|AiSOqO5pjugMaimsuDk8>TJMMxl6atxqA_6sAQX z#zrHxnsNYP5b@Uc0zPQ9V}#T|+)H^OA8OtRxNn;Oe1(nKvsp5ew+Sbv#M?fGvs~;^ z0@^ZfI>cMmW~wcZ5rLqPPJq|@deMKAM|g)})gdx?rV`irz-lT`DD07*i0q>M5xsS( z`F#AJ;4bEWokaf^L6v`l^MAyvlbM4n@xOmi)-(B6Mp9grBog?>lb&p-FC7-~@IsSy zAq41OCliVKg%5I@1>y@olgwOgG=pT!>GKBshI1{T-UokW_C~OLqF`_(ft^%R!K6|s zJAXGjx+(UBPhq6HJER|-+Tb1ImfIIb6{n1A>BkVRNZUGsBval*B!=kbF7&eg4}WCJ z><|{UmI=R%s-Q%1C_mzBDUt14t6V_nK(c=4{^vgzFw{xse4Ws)SaZs`R< z6D~~nedPdN_!I6J(ofQYC&ey-j7gh}PDDJS0?C#g_0jGnrCK?891Y!tW2;_X`#W8v zbL)otbathY?g?K$K>`n^$0qzn-o9+__q|XeGdEf93M$=e-pSs6LtKq8_9ermH&Ho& zUGE;Kc740%t>k0fZ8;sHUbt6^5LG>cjq;QkE=O2Od8$Y*|Mil*cAkp|c}5nRF@#{j z(O$I4-Ynlmw{>isSxquX+W^e+s|B!eH9v1?zkpq`VUh{yZIp$&b;&}NPU(VptJ68$ z@#-FgQ&NEF#oifMFkQ+3zFhL!TW!0R5}0t?fQ>G`LC9I z|0CR~C}T56|IKp(Afti$3}h+wL|GK5!!ImuMN$U=Nl4&}N-Mx)YtN=?5VzEAm+57lNt2pQ@-!HrX^g@OD&+<(@#Dx2H<93YwlNj^?v;|~+zh&?Vr>Mi*(sVfjE{KZr zJjbRmBAhT@#S4A)>+wYWW&2t+i;B@B4-kV{6wi?sCovaRB!~e5@yDEUnR#Y$W;&2aR8(Ug*) z+_9hH7>Y%l4LRtM8&MU+k$U$a5Gd(KE7JW;$ACKK#RX3_2|SZ2Slil2XmyyX?y7sf zUcIGvXH9I9y994&>kQh1i#AVA7_6fy?*r99UUO6!AXfs7143uGu8x>pJ)hs!NUy(Y z>mJ~Nj<&fq4oK%>@M7+@FD}?zbR(kqSogXKvPigWAV+1qK+&|4(^UsfJ$MVQ&18GI zX0B`@6R{?;ILZ}+WhZktDt551J!{yiuKqz5Ylu+7X!?u~qdzea{#xcU{(XG>`!VdXeM z4#xE)Ban>8)in4A`A)MDD)E41Y=*nr1;?kbGsna4!{x@yFAGBQA*TThv0nt#c5Gd2mO}X(YlUh8C#u~C()=TOvvN{Se zNd4HTN9s(nWnKP}Bn7XR)D(B@cLXgvOQ~IRF2YT$WKG8owW>d;;wY<=-TreZFs-GI zX7xM0t&y{uaC?@)PI3Us5-RmtO;m4E0Jo}4nTGnHJv3I)bY0~v=1#%SW!yxa7oAi2 zIwB-G^o3bU8ZymZ9ARI8Q2s~1>YFz!jDZ(D@(WNsc}|70V&rDQE<4_Q++9cv6UBuC z%qxP(ayj0u989ctM4g1MG5h`?@(Fk}MC=!^1NyibOreK`yi#d0aLw#20LYXiQZAOw zRO%r6IjXojC@g3B?tH#d)yWM--HF;!=5U^#uOR?CJ2P9nCEo!my^nb=yveVXC<*t; ztWLFrY)baWE5C%<88TS8^zk7Vn}g1W?-;0;0s`X^Jtk%dAOe&K=`e+N5Nls?g=ekM z5(Yk|tR6ieNjuxZkWQcq#Fyk7{47D4x8RWr@(bU9^qS(c!j6-6xM(?_cTJH#N9;fC zF8n8U+>Ae8II}-d9{)PTWd8do{+l7@KjU~_?bIGi4dp|}EWduw3b9ZDYyZ42FXmN# z2eYt232C)lOJNYwPTaQF!pbTQ_C`{pe1@HdCUWZLh{*Gcj88%fKY3jg|7ja1u5pqN z2JcmXoX2V7joO#aRyTFV7=uxIX_nfDjP^g2!klp*aNqVb*$#ou`oRUx`jo#E{s5Z} zn-%cCj>lI_f{Uuzi%Jj@^r+ouU=(&xKnC+t#2dXc@*I7am%&-ETHrxP zO*ugk%lJF&NOeXwb=ZY9aX8P4%&UQ_!i4d9y3QbjnapueT1)8oIP3z(aBPCd6WE8M z7b7X|lrke9MdXfVfP0Lcva9%O*OerXTYeakSAueiD<$ zHJ=95)3e?wF>z-+XK zsP17h=m63Eb};Q%VFL^ZO@->*89|Z8b5zo|;CvTFfCz5nu)r)8zr!L{tl)B7MBi$T zIYkw1NmQF9SV{BVN!i`K-g*YJ;!O~Cm6fM)-`LDbr>LmxsmZ;|1cTD`%<~6dwZ*~G zc^kR(0H>0#B%6N;;a+gY0~^Qh`SSCMkrxwEr8Ym#5{h+Q#n_1fw4Uijy2=a&`;nRS zwDEB~6#V7o;)dlvv8ww_w2IzN7J?%y^-SRd5Il zNd?xq-^=ZKpPy?F(E@&$*Lnd4S9Q09uq;E7_DLs8uq&)Iioh#Al4%gmk{G_eO`F>^$9PYF57K<&Gt z4Rlw;IX5nbivf_RVAc<-s4V;jrSq<1*&5+{*J+90(17qq3Mr1QuFux74 zf_LezQOyviWE`3D`%TnH(*(JDusY)#S*YbV4>F}^8ReQNd>{YCtWm@E7fMpt%gKi~79=$RX7QxUAgFDuu>zQ1y>VnVU2 zAGVg)mKVHmHmFEAyqq`VsE85{h1h;K15BIXc`lq`+mGy7%$FixC>Y4y6MX?@lF?kL z2KMqR6}@aLk~&_zl$l@^U@JEGM}zSq2XeDh4#_9R*j>L$Tb5PGwIwO%2CNe0%Uu z#&*u?kWXCh^9xm+fv|&gkNx}JZ@G?e*`OBAEu24>!tBFlVKi+sP9tYC$fCA9Amp0K zBcg1+Q39^N4YHo~^uAX7p!<4n)-n@FJL2LWm-*rx%O=3hC``mYl=13mvsOeKzgEt6u#sQNl=V{k-J^1{ zJ6Ge-5}NBF;$)3_s>{i7OH1)URL2-bwMo~}SVs|$SfhcHn>B{2#eLgX8<5yX@Qe=X z@TWsVR8kEUUhjiiS|GmKBPkj&BHwUEjo(-`+Fw;BZ~x+c80?$nQv7~cB9YJ;tInf?AN}KQ#I>2emJ%|eEjG%rIkDn}K zu-)U5kYux$UBnUSKs)Jm+xs6twBk>ND7R1K#r{u%pnvUP`2RP#omkCC@28%=rO}^v znDR9tU9|cU-WL<<c__T;Cbfu;S8sr@zw|34E|_%C}bb)>6>R%VvWL<xzl+SlSqzOBOhKT-Zx};&Osz0+S*%AqB0=>vCtwy>2De1?>^{F{ed31G^n_}68`^n!83!R=`({;WMNd8<%3{u(S!TQeX{tCoH+m3?+ z=l$wr@E!B)1c$l&XHg>=e%C2fH;~-NFL8dl{p@n*Uq6Dm$(0dB zcWS%SGWYkZizYf)iF+jR{1{Il^K+H8VQv|RvE;-I$}^NDSJ8qZ@+rIS` z`?2K8)E`C;_EOkLV_`whYIj;*wERNFlO@UNEjQR5HYAL?o-_gQy4k*ID(VV~1j)c|uQCY7zPO}Rgs_l7xz+StQ3A1yW8Ci?+hh1({xOEy zbP6kH=JrYA@v>1oJh)4vB%Tu-JSj3!ziFu+&*Cffqy^^1D2!*FZBs*HnwywfBOSDG zvDs6M5uH&sCiS*py%AwYd`$RZ&D=;3(A9viuxhHvfb>@i@U9bQlW? zg6ne*M_p4Gb1m#Uz7$*^hhOn11 zh0HZ9WQFlr{)z<#dTu-xk<~3>#JO^#du7oH)2z7R$Jdy4>0OLr8WxFi25=nf(T;A< zbq8wx`oZiphV7@quTnA6^SKHPD|Or3X9IE>;^<+P6W;<d+`XY7a)>f zpco?zQLOiXC3+VeN1OnRMWCpa?D;n#Zwolb?!Qe$3A57?syXe28_K;&j;{@}6N*4N zrNlCI5ot)KK|d9UFVUF)Ix`?OmK82gj7?RaBGC|o$stt~G@zA%EvQ%KM3*GduWk@d zaS+CB7^jD?9SkY2vD%|2bQC|%PK1^@9`?6sL=A@=ZRc_x$k~~&hQRrF>lwmIDI%rQ zajkC~GKbA;?31Q7hK}hsY-Dr$3 zH)su7r|I}XA-fD)=}rCnqKOqLE1z zlUX!L1QLotmTv{~x#6~DeTbdQEtkg#cp#B-UE+a%NMmq_XHVZ*&V{;>$@VHG7nT@? zYQ~XOd5!%NW_-EPsj?HTR%H#WW$p-fS$Kz)6wdjbd26%8tQMNug20x2CtkQQIDt{RrTaK_YPN^CmU)KxeCs0(F{F1 zT!5OxT^`oh7##fJZ^+&oHmej(*Ogk{UHhVY85WWQIT8l0m-xCdmDf?+C+b#IsDQx% z)XavTBb;2Z(-O91MI*hTUAmapy++h-nliiszHEGw(OBp7I zs>Sq22)|PuaW676s+E9Q#O&S(I!Pr^R1&o~mK*M~YY>N*P!2qeQrc}qU7xw`x_#9iAl9pB5Y7*E-mk-flI&N$BQdd_^)3}$gizhKv;^@3 zmy!=r@Mje1t>Vl<-Ilen9fh27EBtEP{aO`#*muc%jqsd(5&0PE;1%apgfM4z(s*~R zKl_6GCLftUy9?N`Hdr$Va}T%6WcZ#FDK!jMpLOQ}ZkL#wM*gGzg+A$pe53(a^a+I8 zdK856rfvRw4@rK_2&t+$xUm(-6RF5UywN?`dLHcR3$+JM$y0OEDBBMpn_k3=fYip^ zc;_wYjd16fij-^dj)&6^I0uT2bb(%6e^Aw!H)BoHlnDNFYhJMWWet{KjfbutH_eFc z2%;iuJ5ExD7X%n$@@xm2m!|Mw0U~;+==zC;6+g`Q%r5jZG^sVC1rk94>-~EZ6 zx3||NP3#HP9wr$&1#ZJYxZKq<}PQ|vZl?p4ilasyQ z_uTW}``*1joOatz7z_P z3`oEi`h{^>2wL4I;x14#AN6cpg|8OOa_HxzYluI!~pAo;e%dIGGDYcGRv{-j$F2jf_2b5A5j^&s^V*7}RE* zkncC_9^kZ_MtQ zxGAd+wBE~t)y=iftc1Ci25V~;hzFUwaw%~ zTbh-^=0{=SMt6?`PvP~#3@4D)()E<%)9XTR+wD(}-d7C0!?~t6 zL5^m!VpVi5IDaL%nuGtSG%OOv9K)%DCh{9%ZSPP2DRkqI=mYGACCQE%b$CHu5h#os ziG~KvL6?4nKIyqB<)%*cid+y|PyI`nKqD|&}+$&c_`5-HKsApo5i3MmnQEQvp-LlG+B zpc-g0_YNlMDwW_ZA1g{F%q%bInkIOY9)44zn8 zEO{0pnUg~N+<-eaPfW^yoR}s}_#7FqU->lz50lVEEq24^y_J5JuTmi^pnxm=Zdk?8 ze`C(A@L|+%G+|f|z=>#{CBGaKe*30UxMS=10#Ed~bk4D*?ak=2*v$ zU5DB#7CK(iYt@*cNX4ZJ-EE!cNgnnG7euXj8!r{uG|L!REgR&%3RE=&bH_lDkdI$) zSgmlVqlPrFH-2c~rtn0P{-W8aPK+M7u?!b+zK1ezgak-_>vJzf(YlJr#iipp*#{@L=UTz0 z0kCLv{o$u$@2a>r&7SXD994EV!X-51L@t45f8IWFJKkY@LQ&#?L-9^E3w(?CPS{xn zRUSlUFz}Gzn=wSCKYMePH1CAHkjg!tX#`FxsZE$w@tDH5avb&glX4yt8;x8K8SD5$ zhkWq)aH13 z`o2#IpT+{`8rIx?cREU_59EDc9^*hrEh}vn-PUV}KHFS}s3Te5w+~!K*7}}8c}lrZ z^AjvTFv(x&lMNkGOZIo=es0dq%&rZ#8~hQ)6W9%dQN#l|J>aIvzxZ)f4u+E$=+YP2 z9&Ew{t7dPVv#;7AQ1|0JSWhw`ZX~^3mU}J5`B$uwb_jjAU5O&&UlZ;rbX-#GUz*Fm zVkZ7Q|B?MaKaKwmLivB3ZhxzX%5n;*LWn*?zR|Fqf!+)Tj-W<1WSh(J-y+5`V=@KX zj*Hz&^l*kR97@bdKBW~f5k9_h?(<8+YyvwVcus2mXsFd1EKN?G*Y14NH{ub+%SN=; zM5sgho;2%S9OD`mygq%wmG5h0ES7$5UAT>I08Y)56cn8B+#O@$NYA3Tec9LXTIJO5 zYmmv=$c^riP{QtHq4pq7Hnt=vwLn${m%(-fCz8%sre*?6z0X-Bv95m2kBwsal?!j3T#5`nU`g=bux15sNSN?0KYl^t=3 zWX62otk_;DF2Yx{z#pb_DXS`C;Vr!%?*yF1Z42iG^QkBasbHHR_NSR%qJz!BY=&W9 zvSn6~rB@wBVUE1(X{Qh6P#QZyk_Gh=9D|xVE0>c*__fcxpWW&WCdE3E!y4#uBR~Ova_Kgx zAy5=@-i+@|e)kFV3XDIQb=n!NA!gcp+#ddV^!c$JH+d-_;0LtYFOP1?J1MME;9r1z zDxJ(~V^L1+2g-=c5twsSxLY1F!br*z4ky^bHG-%1O(aA`Y&g7#h8Ri9`!ow)LhHSX-5 z+hJ<^jag6n3{zGK+h_|Q$PTY@KV7U&Wo`e_3ELS$IU=yveK%7vG`i{9?nEp%V}Vpz zF|>++Mx4D*dt;(OQhnRqMwJNdDF4xqllX_EuJP&?eHd&qXb=fVD`vuS(7 zikzvts`f?c@`fFb(yu+TRCePgf2M5&%fbaa*7)z;I&py%aULcqf)!9?D{$vdagHGQ zlB!dI_$PU3VD2&J?{C4Ur1ys3O6)MiVvo}jX!4rt)w-`IgaaQ%uFXM@M;;B+N4wXTQ#LK%uRe1*T!l{FFG-2N|{8^DKWHiN@XYGtb!ZWmM8Rv*R zl%1`LZ^pl&6k2wJTP}wYDPhw|7m8Un`SWI|SYE`k2XT+>@pyD&J;oz^e++tpAQZ14 zlqzE`v6QrSCgzX|qA_O4e*b5J_5>16h1b^>=>1=n!q|1mBq7>^V)1wOghp!%|g(i%EBsv;7Zyqhh~jM8*697K8D|i5#Gr@ zfCUyaDp-84Af5_wXSU#E{B3~i2_ABs45poOZXMkobNxPFaC&KdF^3`16Wbysj5JXP z5;0SU;t7VK)kD-J6CX5%mHIn^DPV(cJIXs@V6Rm@vkVD;EU|{xC)!!z=oeP3>2z7m zjvpizI~r$gbfmYULu_Jn4A|QaINs~&9n{!mcvQ~q+ttsprnZZi?-y8Fxo+>q7S62+ zM`W>W%v(qPX3I)Fr}lb8-L|1FWwH|+IK)I&Q{b>d&S93E5MHtgb^L>SMOUfI-NfY) zzgb5;pi7XKtfk|`J5YS*JfN`5=H{}~%yl3xC6>!eY{~V799o7=05xNdM($6e#};lMU;hZ zja`891peAUAw~a&ug1N`juPNW|K7y_Jv9qv`Xnu35_u%P-i%05$hohS}$!j4SaqzPf)S+s`K7m{ZM!{EL;;Y1xGqhSxD z*#;Ye?_!W8l!*IdwWW(ZrR$XL&JuI8iUw_Ed5XIodkJr+X^#ziliYj&R6V~g$HGXt zp4UiyR*XlF56@7YaJ@@j^`GdOF`@XENXmA{hb@F7!!)-W=5_sXSM4PBQdSt2gP8 zwx*$&yy;s{y@(8yYeM7w4BS9Fr%+%~yS_*nDCGZh3}`xzgI$Sho+~k1F4GH?Ysd-z z!}PMqz1Xq2gzM%qGqTZ)`KMfRSqYV7rO+e7z~97pVVO?Ht=t2p6TEgoP9eAbTYBgd zTK?Uv-xws{;wmc+i5$@vLd)(G>H&v}Nw>&Bk+uO#msk%T{ws1gzKFixWa7zOJ0@vz zx=ZNHGIW@e59E&Y80!rLIU?VHF?G!&XrHXh^PVR$Vb23m>1Eq&piHaV@~_VpM=htYq1&Zi@RQTcW``xhu+cbfUtEm zl28x72eNH44-^pRljSS;&|?fgI26e}^szX}aZ;Hekx!t1SUS`805IRacnegp|E&;E z|Nmj%kuo+FaWplwRrwn7aI-YF`1`f1rlYbTi29MLBP}JQ*dE~~3N1ZY5V#vdj7^6@ zH30HMrlF~Ftc0BE>Nwg@(){P#^*;t?9Cjsx4&~+DdRT?W{6qO)NW7iWFPnWU<-?X4xrHq6wH;<4QO% zwHpFxFgqNTBMusR$ogHn4-$$fFgQ0icJmicGwaCc$HNUxxWW%woZV?bQsCZ@<7|8O z*J4LQ17AIAHuX+Wv1E0qkj(h-#RA`!9gCnqPPCQURiXP=*E1U2%T4fNIlQ}28)2(; zOi$XuFPJcIJ*kC5Q|3^LDmHVlcxjdmNo3M$=(Kb0I$X#1DR$D+82U$)eRx zXu8!1l)jpH=%Q8GB!qcfx6me&j4oDXskbN^tXuJXKfYQiz7-F~Q9fgaU2?jRZY4Cb zmU|MSZDKA^3^nk9Iq6@6&3;W_W%>DPJW?s#u8dY8?RfEkF_DwzCQP)*5LcA@+fZmV zmjBAv0h)*btr$h?mifV>+%bHKkusP#GW7{x#LH)FRws939IYPGr`%nZ%`gj=W#nQ7 z@VYpumsa00N8;t`92EO!IN|_q-H@Z~!9f)w%4Q=Au7~NwI1M??KJo+Z#gcfo6$IAY z!fbBT!UpdZ_MyIjUuLr;?eKwI))5Z0Grgk_kB#j4n)*AM+g% zIoMS=aaM>Cic0th!`X*3{?doc;0K!-U2cwE(K$<^e$A>u-?x|`ixLvk**MVmMrO`9 zSMKlNZV69N|5!|tgOW01UoUaQe{uK!dsY2k9FYGz$DZ_mU;N{R{ntXtPMDGgVL}a2 zusUDVZvFtw$3&qIY980nP(gwy54F&54q4)=luAi52X$w{5cGz9kxq^KltCLu+oh)8 zN2U1rcea6?>j}Q|^wRe!O>V)l=ypVnM##3UD024@bN(=3;$oAQrCP{L9eJOgaV>(T zdA~SD!Cj?DAzz62S`UE9Yo^8-&@HqgwY+IQA(aN%L42k;<9mcbygq<(6H%5^prKc} zb^K|R&hQDBUe`q74Zxh%nAMQEgv2~J+UIOovY31?jrjVmnrMPtp7ep%4Sbb^ldhb7 zNgGR|Q9q%;IVV|+b0s3~!x~F^Y>Lou*->UzTX*-xaG2y%WyJ5w`kN5gbD28Svf@2h zAGcKqco{}VK&YE)oz|pWQy05;=}d68_Fso774Cak%C8!N_v>E#9}vmE-v`?Nys7=0 z6ZNknnGJu%PKcIZV+YQGGE6PJUAMX--Qfs$)ps zvPdno3tRzs0Cw^!2x2!MGnQT2h9dvL-|y_mY3)+?;p|Us&*u|LFQ2zwFDy2gNqxo$ zdd~h8D+EtiEoQ1t#c(zOcV_B~Obsu9-LxnKnMh<#J@}qKju`3SDjLvVe_JvS02;V= zt$Z&~uNLr{Ta!}v>fGKuU)_=eH)Hu(`$|2pdqRIXLM*bz7`on%V(aJ)hFz+lQ%iyCw1dXaWv+*T+QZeS`FHe&~DiJy5IL}!nzjW z3%KCUs20T<)on;G6YWNcazBO?uVkh3 z-V(*)20kV?;vUv+r!X&Agb9*zXJ<(vngx3_5CKkpx6sPM0zOPg4P`OaW2`$@$6j-s zvaXBBjLQ(%=384#3}(?Ok^>$F^1J2iUN#i1G9xdi zEx59m0u{$ZRuF%!TEF1`(sg!fJoq)3%G0ETY{O8#9w#C}T8~-tGkv5vxGUU4m(~br z#c`FsAcT5g>*O$FJIBzodYc9?yRZ8BsF#QIq@ckT#y;@pu9SU^E5;Zhke^R5W@`Nc zB{U0#-So!-hco$eq#=|OC2|hM2`>FE_Is(_f@cKlBNP^3^3Ub>9?Uc_ZJ6L*V`8pe z(-#e1=YBL@um)N#DiPmoM&s}*4+)S-g_krY`{Q1Vwsly2FC}4oO z#h%mtU&k81ttH;FuMPsz|84fj|K0)qNi0<~G&gk=GBLDwHg){_*rEyLjk<*QLGCqa z#sUmQT9+tDOGOVg0h|LS3qy&FLugB=bd_mBVd%_YZpLM!raf!btajb9OpU*|41$1; zSgRdlernqsRvh-L>Cwls=h4iqG0P@%)xhuhJnQlBedf}yi^JpL7X$$iqk<5kgrPNA z>>xYZ5d^>i10x*ZWYnezixCks69q6rO9QXe=u>}N3N(e7YZ0Bx&~fq#%%SJZES*~ z{b*Si^jp+q(Mc1v1(p-7nG4kwz1$Gw_>(J(Wz{69C#-ezHVTKRRgvetdzxK_QMT8K z%W{^p{f7lMDI=cn*j{>$tH~zK%W=f|56&6|IY5h+v3+G^h6R`PJQG%Dc~G}4V@%8( zJ^YAHxvch!@>-TOH#eT4usl{oe0QduXBO(oIx?0Ltu1a&qss&%BRTu~jG8_rFXnYJ-uY~`J_$W?lyia$ z0j$(a#;{qSMFZbj&0WM$Lz+n<6O~3|xb^sW79#_1AedOq*q1IhZ`_W2oiUbP9|2{< zPL-~zx-mFEPo=AB5T`oYrNdDWrJreoK`N=H9N5X@Aja#-nbv-Pc5MsS7nhH zLk}|8`olEX`a}PSs}A*n_+jCWvZJQRN1MRvjj&@*YErTGg&S|hjLm1HA4_)_78yWr z5QG(&zcPLk9ZXhn+*Sv{eA zQ~|#1bdf!lPdp#_fZQjHQ_Dd@ris+!`eAP`%f)PQa)ZK1KWle{OPcgoAskK|S1fd` zlVX#cI$m{~TN@Gj)k9vJ7v8dggRGOu`fPR3l96MMBr=XLE`ZWZ{CwvCscS!bPErXz zLpO@31RqOOXPo_P;fPUx=?z_drjx|(P1pI<4x@#f%wgWS;k=Assp`?X9C@l$GBO=f*`oWMs;p!5t;l#(A6N1;)wYQ& z8%ebBR0CM=D13#?x=puv@~NW@E6C6kXfh6;H6- zsVT2KR+>(wxh{g7q=(*4obJAI1DoT(BUHu=&L^6^|T zIK0hjr{dozPeo7sM^#DcH2Pw?Ir|V^7#&V6xI|>PTY1m|t%8X{O435ffhZ2h5@6Q# z(`fmVr9Au>wm*Eoeb7>${t&B9Up7bbhE<~>0l5-$y_OqN!CaX%CxnsXlnV?}cRY8> zD2XheV6_Y2*vAtzCEcGg^47ngyHFi75`LV1+=+2-C_@*CSwtT*GHRWjPaGnpx9TM6 zgyzrgodqf_fG#y;mLE|)w(u8PeL)P7!kjjd{0+(|q>yxXLwnaR3=RLuygHd@J0z|< zoY7mHLMeB6X%gBmQbYYy_(VTI7^w1`t7`C!^-on^s!ExLJ>_w}HwUOflEUr^8j&_k zm#P1_(EiKi#47^;SrxdbQ!;&u8hVs22&K05W<0rWNp{xC5^inNKv%I zLn4C=k5xzje1e<#eJieI)-X-rKY904w8V&h0`w#9#M)nThOip*m6daW){F=%4dJ!s z3EPVMLe_mj%YhKpUQF3NsuLfn<0zbzN5JnP8dyo1XNuu z(Gj_-A9bTO&D+T-g)6Q(V_M8h2EG#dCUp(b9ek>0v=?JdA^FP|)54u-1`@7LLW zl0kx-%K;m(6#y?&6y@7ZcB3F%IdJ2#%P~y(qf#_IAQW zaA~hyp8RoLq-Sf8d}*a;o;gJ)O@8b!7Hr{3*id7JSi%u5VnaS=;L?U5AO*x)iZv{7 z##nxxY{~&gCRBdn3E6gi+-Hw zjVyOvZ;V^(bb>BsVas6?_EXitUWpo%H5mm7|E_ap;gsa`cqJ7rM2xx#I}=$NDhCvj z{d~R|#*QpYV+i`8wWs9CJ&=$pfnG^pZL@cvp95{`yS&qE-3}+M@!f&H*!rxN=--#Xn{Kw-Tvg_ZqvFaCMN(u2} z`lLl?e7sn9yqK8U$T9$oY6X}Wtx1~@DUfM6Or$e4#IE!NqcvbvZe?mfO?){*5uR8L|MgT!cmZ;6{?(*imZ57#D?_d>}niaL5^x zMcJ%BJ;jke`k4`5#zknTAhIg56!uUqW`R>w_0HFt~FqF8f9?YLYcA@4%Vv8=^%lkATiLna3-W;c&h&#C}9 za58I*2tDKNrs$<#5FFGGeOttpQ3QaReFW2huuOc#l2Itt0$yxt~_9Ek;03bQ);x$8s zIZ;AuFw@M2nF@*BaV;xCv(DN?Qj#xXm3^#ytt$fW5W32iVX;14`J@i~D{opCvlk1F zA)yor=Zr0?yI#1EbydcRLxki>UIdv6tr9ll^O!(nfvc~%8n1AdDS8L94!n3#bS3w9 zOJxr;b&L5=mxygSVOPB#!^)Gx4;h~C7M*)YQlp63j}Q*W75;v#yg8%q6MT*fMtNx; zf=Y}{ff^LqdhZH22WL0E42cQH;F6r&8XN63K?SO?w)Js zWr_PsXZuR$X3XOcu}q%MzEw!@rk(Ov*h$5a&UYNHFG^PE5Y~So%(xniaiFg z$$YpQ!HqWDB_3?ukL}Nc>)u2$_wsft#^h6|qJ$+jacf`e@A z-=bze!B%cg@#n@{lDIa4YnLjQB9F{d#mNfM-_E#%m1nqXe&?cari}AV^!Pbg9%l+# zG$@necizNWD=baiws2TaIrSPnOZQRhHS&h#22I8H9LJmgaqn~E92B2+vHj6tvSh`6 zZjItns@lXCP;cJOsCJ#rBj0EAJ|^96^&A9O*C&{^3C(r$f!gEXQh}m7Gp0IHHZQRR zeT;vpYp^??YP3NvLW;&CnIEo3TT{B=T#@{{_$$z-53slX*%VOt^BlM{R~j3HiuT5Sz1QImhcn;^j+4`3m!5w& zC@}!U`wEu;_NtGl>wDfLL=dNJcB5P;%Wi?ucVIP;M?oRJyB#~svc45X49pW&`^nqx z-jix_f591Xp7fj^YYfVi85=G*P}ib17R(s(<`0b`osdAiO1KQf_LJJ7H*0 zdwdq?_5?Flt6&z)6=idkLC$qe$ifqNKsp%1=~=H{x1-80lr6v>K!~OgFv-rCiQr0v z1Mf#_Lj+t$zCg$U^Q zwOjZD)D^7~yg9T*Q+LJ0N5x7p$tF6v5AE#a4z_iefQD&iU;)O%afPzao@{`q>?X4=`G73Pfb#VXNbBMj$%mhN#%ut}k|7;Xp1UL2tBcM)Q4^h- z$%HMaDt{A$#UCJK#?cqgQZGbK+xB)quLW(xdX}w-ZU-v8DZx%)6c-`%N8$|A06EYx zNtU}yx~aJgpAos6jk(m%e=Wr;!0g=aXBu{^z6dFKATGI(P>yN6I{R^RHe!vL)Sd?InjDdr**64EO zSZ}ENNByTtu;>TT9>=j@DZ$8Ad_9KsvDbWe1dvPH(1+%CKmUtcWcrD-ahYrmsy}&- z&~xTvys?1-CVQAd1U}t^*W4jK5f5`HAO9pv)UsiXZGDN9jBx+0pZ{OnivQBh`|HFK zqhaZVvxMe5f{riMU5_@hX3>|ANz5}GC@7kU|R^U)1xAYw1Lm{{OXnIuMgrh}Z)9EWSIOjFTW#8}PG0E>+ z8;GBRF~^}PRjC86d}KoiYIi`SJsLpw>%;r?spwY=sf5x%xd^tW2)+%u4XK3DP#_T$ zNfpz|L0yy5k}tkS?aml(y*g)2$xX$f2=NkcTcFC}r`dF(;*G;dbe|iPv4l0^k4OvY zCZ4El=t>=SyV9%w;M3}6m_U$yV~kOnoKO*9zBWb?V7gWTVa&V9F@IG-tJH5j1)r}m zL(s0V&8w~ZPN&@-d643?5OC>!zyzXaw@tG~0f*@UvUTCx^9Q|3Wl3CctT17ay0Zmi zU#96oUhP+}<5RE=%0;vq`|K%xy;%C=pMV3jXXWuN8L8_wJ!D7}-*zJOaC4Qu8)@Gq zs)LIAO_Ko{H5;SVeR-WI*Yf*$=kH+aqYZsosTWH)&Du){j$bnqRoZkb9U?S`iwQm` zEMc+fPZ>7e8_*Tq0Rc3*yf)8bokmk?XMLIh7%B_$YmI@SGhy($I$qv%xY)0&_)uqU zzq$JGL4wxU`Vgj=(^Zy`f$q>Tw}Rl@rXS4rO{H|2E!52Db`pGOWkE;qhHsbe0r?+Y zU;Xq%y^~zuUjx_c4JZT+pt#wgy>nU)D|coEn4D4a_Et8wtt$yjMQB+pbtNzJ6Vbj? zH&jgVRBQJImy>JC?Mg_lW?`DAI5~IOy*cILzSyfB+7>t5EUZE@D^nU(%(WCFQ7N3w zO@p_0&doyS@3Ew2A~2I>tXaZ9Vj;P|G062bIDV(03iV}G>Y~9kmaaryEqMF zQ5EPCB8mEDVd~l0JS%G`;GWta=oP8cmk%lW5)#H512#g(<*it4{I7l&G8&o_@2b(- z45d0(CX6l2j+JdKH2^`;>!Z_D8(%$R_Mten?B_UsxoVO_SCi^fhmrlQOxdC6&Vr=S zl^^~KCYRk~67cqlN&^p&hb?F0byZdMZCoSkcZK?GCBDpMEz-0L!0V7drE3(6M>S2T zssq2H$=>h?%}l4ZPgN3`mnCV-q&X5-Qdm0IswrsrMQdB;4xPVgHD>jgUIYt^50m-L znwscZjYpK9xOeqXlz18a^7?)IYBY`o{hSp^^_A0g&g}9u46@I2Mc6TlafP@Nl>D=} zD(LR&3Ji0gGxOM@fJM~EeZ@l;qd=w%i9Cm6YdNHlJC8xboXN8}D_3$@_$ zr|&u9ijuQawqRhHXM`<{103R-{N6a=n@@Xg&i)vm;5&pv`nnFWtUIPe6Ga%)6i%o( zKMKjGM)amF(N2{iN4?kpIUfkiH9vATc6Vy2afQ))4*M_Jjc$bpofCQ4R=UpSEGm$J zbV5uqDFGpPT_b4lK##h=VbNr;p8a^7a{7#vVYx`XB-HWaPrj-uMwM}@ZGI7ThjMG-j0{8;^{ z>_|q0=*1#MM(QEQv1>VqI7aL!1srCt!ZxU>SUX62C2?2nESSfg&&c3$dcAZ$t?czG zv00<+OwLFH63sX=K)m5{x&%REh*aJ&owdKkQMDM@`Qtp)T{4z-8EIkCfBPZAz#0Yt zxZ&bHDOI0k3PauO1JQIxdg{x*h%(R{%Ju?7OuXZH(2tljWkGMh-HB`RknmjhPIxBvs?j?^yVvoap0Cad9dA7lW%@ zEagYTS2>aU<=y{3$~H^)%KuHo#=n&GV#YRxmVc{T*@-*SI6|nyJxPN$9d?y!mF(Im zl_{8(8ba&BMI1&%@!F!-z71=`2`*-9vcdf7KWOg?5N5-WXU;-3D=m9NGProWkK8_{ za!(C{@d zRU*>?s?wB6dcSjKP|#7jR6V>IQ<~?mU3;vDU?;j&#b_U|khh*qw1M;Q@2 z%iiW?V012i2mVx*?tePFFbJzU`@T#NZ~tPX{&y1u<$vz}{105)Uq_~FjU6WxwSPdk zI>uae=Fz38#FWd@OPZ*P{gFi%6zwe`>!}hVwOhx?^j6P?r%HAe-p->Lx?hZOM<{ks z?Mu*j3S+sMfBO|&{)wLE%;}WMOe~qdah&;OKJ(n|bjkN9^LpB|;|sA%xP7|^^fRd* z0t!pPNE%iOp~Up3gfwl=&2FIPq@;TX7?7?_AB($szZ0^VX%i8k@3!B{DE*d@&PH6E zrcHJh<5yd7Rj>!vmZapY=^dn(mUwNQ1}P@8BW$D<_oj16TPmQXleHa>tw}flgESqs zy4e4^z^)DHe9U9&P>CjO1(z+2V>+6}dGTq{A~C;ln5zb@4K&KodI}=+90jAKl(=zD zlD1@6VTPP|Zfx`fsy{|=8NDe!KVwaCzTSlerzEf-Rm+}rLCR7*fyH?%8#h9}lFG$1 zG+&A@E1W5Mh?T`y$CF%@^6)nGJQ#ggSeu_fTTR?gPD*saTbIM-2vo%QJE7QjB^nVE z9pjF%-$R?Yt)4J#0uiI|NHqfi;XmP^IfchdKE{+)s3Vu;r%{Q_j z-d=xDq}H_XmQ2sR+)C}N*_0k=7>pXdn-|;_Sm-Vk0B7|F#B>|Le0kNs%6JXG;xmAZ z3noTO$r)P-D}pm#&1(p@BBZc>qXY8)qF9ZFh>A`wc&WIFqBrHXTXVcAVaigrv z^IjgKTdv2hb2Js$`6yLdn7ox7JTaV@4KvX9F|3dKskt4#dk)v`s&$cvo)+o&Zr$p*V<>20qbGRQ!l0;Wpa*S$<`J%n>ko-VQ_e<|~IJ2|C! zWa{R_3HXmQOpTi5!t$+7T}B$}@QIPQ<<%s5N}7hM~nTHA{d=Dx69ls{1rTf)*MoR*}MtSvY+ z%kG#6+q6LtQx1;cd$gG}6=ax0a8+8i;-Q5_h z4n&UCR|?TU=>^VtFBN7}O?sGw^APzdTvhd)%*Cfk)*)YZPHr?Y@2VX&N6g*Nzbo}X zqW?Jp(U<(rK)v3%w$2%O0xB&exn&Z$m<3gduC+HXsC8~WeEs$*=IpZan+{?h=k9a? zrxy56N|0`ap>Rj>piVmxOF{2r_y@W|7c(qd7May`fR5;EjhM5u^SlYtMrz&rYd*+M zUgh|P*buPa?ESlx+ZBf1;f}M*XB!)+%rR%kf*o9;J8*@ULZKbo-h7Zzw$|98+5P+I=V$Maw*HW~a>V72Vm@Gk@3*tS!~&>Ds>TUVVU#Q#RlJ<^i`( z+MG<(R#RI$@RrDKZy4x1($?L`YK%fzoq{DwE>fSCTcg7nnOAhjf&$)_1VnqBjlh~+ z1A?+0!rHru*BHMjyP#{{n#d<=S41n|uUCE67%rUE7lbbSU!1xBJ`~0ApZ9NNQ`;ZT zrc!qHF3x{75C3f{s#)XN4`l`EvxZK_+9PJl2*>2iKd9bRs>NSf+E$hrs-1Flbrl@A z$zf&Qh(Yf%atV5`_)}oyBpReL2Z)V)e)npTH!Kf6$EoY(&gEn2Nvux0Km_;n^({0sqAS#cJCyf38VKTKZiwg2L++ z75tv465gU*s6o?V!h<(zqZ>K}^C-uJ5afi>XY$<5~szjBvUmd!}x36r;)U>!#->#86p^oBGdtiYv!S zh4#Np=Z*YSxxv*>N^Ppjr&+V;j-NOQ#o}w_p+0b&gYwYlI7m*VNr&>`r^t95tLR`P zk--i*6RLT%J1f37n*Z#NThOD-3^S+%cU2J(?Cc9`SC|=0e3ji4ZdRr%{oXa zC$!Is(^7ldev*+C`g3fz{8QnNyjoC_%5ho&>(r`yS%J>xcV(qi2N7)%H4j@_(>cJQ zWF54V&xOB^nW>ZTG=?gRfN;B{Wl5Ei`fFMxdEtif+@5Mk<`wmHzg)_)l9HJ!nOpBR z=y_!m@-!|Dw2s2GIV_nub;of2gr1L9h)R(77m@-*xyz(aV_vip+*bk{+-%a+BLZ)oWy}!dV)&heI zZU2b^M;LT>Gm}OcL!ZUKjC*X^!BR(eT7i>I<*n) zLb&Vq68lo_lItTP4&ZuYVD15h2?O=?$^iRD7`$FJ`0wntimuzr;rTM`B0Q0VwH$1c9g-R=^XtSn z!!qq0>catlWo^Mc!F*8f{&?aE%dUMX`914x-S>2b0Pl6#v|`Gxa$_%P2vUZpW(BQe zLrQ}~MRAd~vrN6V477}RKFQ8picKT5&iUuUHU)Q3jb#PJfwJ8eu^S)|o-@a$bd5>) z$nA=q%f{%N25NxL3w5=5&YLURsyXWU!|plrmSYsl7nf3(SG}oUwlpSM(A_GocQR;QjMC?ePKl(*^4s6T9p-0zUj{h>aMIgQ6f@z_5Rj(tGXWYL- zcQ}iBYAzS9p>Z#BTYYRXGRkuTn;O(!emsu)_GVF7-Yji)EwJeD$J)sBkoNirl}b*W zQU|Z>u04l?DpQ4BiHIQgkILOu7kYqe+H5L5drT20{TyC3`sZEn3b$-(ilqx(wf<|k zo~PthvS{Zh=jJW+X({uKCv~NA_{sO;_xcBw*jqvf{Ej71rqEv#x|)Sg}U1>-T+u76aQ1@+v=AD;&}C4vbz>?Bv5mwx(SS6^#>1 z6|Ds`kld$R3={1M2|Y#Zx~tQ+>{?;pR%i7Ok_2jTCV-#goyk#)%^Fd(wm$C+7)H^k zr^iFL7DmscWepQUkUFIcH=L812Zy0EOrR)V64ueYOKO_yFS%-X?6d(d*f)d_-Rllo zJt1;+MZ63eyl1ZP;f!YD+23O454FOTz*UVydBnGR6=o=i?L>yB)0fBtFKmA05s6Bx zCz{9lvx(Ln`Y;C5yvg$UuCcEU8Pwz*K8h)h!nGy&=Bl(I^TrHGl&Pm6NX{q)NWzf? zl*uXOiYTEJKVZggNOk8rDh1yoRvW);MAZpL%@i`}@oe@nMgU?JM{2BBc|Gz&3E#cY zXr#1xh2!Cs=T$6-B(fLn3UwJJb;ho^o7UqawTv&c0zhr-qjQru559S1I%CEgN()JF z>ynNMA6bZYP4uUhY6=(`R<4=?E@W0QYntn(gj++-jp9PKvn;y$=|Ike_~rRJJx>FuQ{DR&VPCbxZb|r|H?pBU~}$T_P_(ZioPVkiV2cGPp-9 zr~M!lb?u*wTwVA<4>M;53IISUVE`f!3${jy<==zSi#dR z3NEV*_9982=~yg_6jsDI;6s$zmbP>9#{`$=jFZG7{otclzW#*1|NJLRZ}jE|DbH7a z&L-;rY(*&iTSke6;lhcpCI}#zWGgiP%h+%%l zwuPs;o@aAG5K-R|Jy8&-Aw!H1`0jq16;3sf$|T+cc%Q!UC%4hiK#FnyFUsCAO15yz z+B`_xwr$(CZQI64+jd6UCvDrdZQI#-Z{6yuuI{=$z8Eng_81YrVy{?xulJqLoFV$b z-`kk%*^i(<5V;tIpr{4vmG$G!m{g(xf_ln=W?h*1>kP=7l(J}0y1ap5Sqt>2>Vr!x z$5F`yW|klev-Q{BE9>G7v!w))Hh}P##hoUKBL#}9N=#A%Qn0%6%_DUa?d`TE-jqv8 zunvVjMg7M2J5qTDIw$atuWXX`b;0Z(0?@{CS=T>Ql$e=lTEYw>OYlz3_TVQN|3QmX zCpjQYfB!t%-$VL;t>ygB0sY_5BLD3MkLVwu$UnnU|E0%J9Q#gtK;lj)NE>9nN6t-D zHCIe$5{C{2CIbr-DMKQsP*8O4vp|Rmr(839nYr>uMuz11^OxV?H)j9CG7+-H!VkzP z^exV#OioAB&iD6w^lrp@=<)3;KhNeI7+5wOhYCk4xf1%R|}$bLU@i7UmU7|tISZq?A@;?=WQ z60EiGcsx0Mt1O;6AviVAS;d<37k-4L!Fw3N2em;^28ngHRmh~JJHwM9sEb!MvjV5_ zQH88QHUNPo>y5Q)ywu7_7-hOMb-$ zJ%IrXiF>aTnK@0GSyAo6E{F*#0TH&k5BT%n(hziJRA<|&<1=O^kYGpP>K@1rw~)O8 z>6Y1m>rdEQR_V?(>fwv@jn(6h04sD7mP@x%4q5DSwOyKH5XPZYLN-= z)YgLyR{T>cb{RmBY6~2$c3F1HniV@3*6mVilvBI#C)P<6UH^pYYaP*xA-`dmz3)x5 z|Gm`yjj8p2ZlEdY8;IFDIuU)pi`$sk{-^k7P2t;|UAwr6588n;)K$J8fyVr&?eAMX zz$jEcth3v75PJb2m|UDx9NqgS!)Y*@k>}g^H*t@{JOa!Z?6!7_$MmYpb0C6UV|G_7D=-eDZ6N|DI^NtjP1qTmrf9 zhfUif2XNFj=jFqGa0Js;7y`*MaUrWUr$YDvx}rKZHkzn)4vcg@T;^_4<+e2oZjvPA ze?LrP%I|iUi}3pWyewtAB~P*>8AU(f5xrtuLR!ka!8SK+3@V^s66j5}<@Y*pM=-44 z6Prs;ZJETtG@?q_L7w`VA2r0h{)6C(#8GH}BQO5@r6`!_a(XGo;CJNrDl7JBAGZ`k z9#bWqB7n*8(HpXqR(@2CNSj|*$arXtym^bfeCOvHB2jv8A57ecGXCsfQ=QqW#jG`k zfALKTC>uI#=8zPu9RbdAw-s!4=uZYV+@W;#9cU+D#m$V@WCW?Za zk-^;ewjZJy-n?VaimMPZR?hIa;FbzbJOu?-Ld8r#J#~o zpuhREC(IF0vSWwYb(slgY~5<#q5&?Y_e!nY@a(kOI6?$glJ**1&n$UKh;8Yh8qO5k z`0xx=vJj?vguy|UYG@|EeenoSD1R}dBg_)-{lWioUBUIgn^h$>TBW{#H+~g+M2ZQ< z2jobXN%jpTn_$26RZ~6|%5eBLcqbw9?A9y)RazpvDSOfo8tSyni|?VDMOU*!L-`sh zM`!W|;VhY@l5UWNYLq#2(|XA4n+!V^AsN<0*HzlNcF<)`%e~1x)IHPA5dadbEqRN3 zNOI9lOFQ-L;QnjxrJ6DbIfQ}TixgE4ZnD;z;KYLHq?tMwAfpnsSW~R@38nHH-amxq z7?90@;1->6Ki=OMMCC9YV)U~KtPp(@kw)0I?H;`1{rAX4Qu_^X@S*r1mrCXi@W7&B z6-Ft+J9mD_PagI1nptK6?$F8*wc;lJFATlx zORN`@>mvsF!9Ar{)_+{tZSZ)X*}e-h_usZ#;{UxM|FanXqZBK>z`c|f9==+hCOq#L z-0_W!!N;miiU0gT0)zV{Mkho>^(#*%9v^*pygv;*;!VN2;gZu#ySzC9;%C2im`bIJ z^~LF|ie`g_^+M~~7W>o2w@t}`Gy?BeSB~su+Zy|mXR_xd%kBFUEH}gt#Ux=Kw}5i> z*fC~__Qc~vBl+Ds{dj5iK2YCXb{(GKA;%Xz+^M}6CB^OExU~1T1O|#yz*d;OAqXSi z+W5-@VjZ4d&;hN57qQpq*%z~{H*(HFP+_l%l$g8Q8zBzA5)uW5ks(QU^n+1w{-6}q z)An#PlC$`HVI^1HN0;0Y>dCu7mTumi zq_a=jB<<1*^1NH&k>5o70UTT9 zci#I68%ixX6tHp*a!0W86ZjNd6?dYcU-?RIQb#d!Pv90l@eZ=`m*%6~f-N|s+>$Lv zWKWW#+_EjGWKRSKKEf?2WKRqSKGHvQ0PE#SPwA7sffmWl+iU<^M%)a_?lxu!>Q>+#{tql5)37jCL1 zC(WqjJDfe7RSD+hH;^D)U4SeNYr0|1*+ECr1+3%v@+gC~RG?z5Z2Atn7x|n;@n%&v z5<`OstR1bGe+{nRx;q|D;IBtLl%HB+o-Qr1;`IDkU>q)uNEfIAcX9$oj?@p`)NFFF zzbG4sC>chBs;bmf?ke(GXx+1Zz-nWzGoi=r6^S~^&m}* z*MUooqUifiETFBT%`A(H!hiNkQm@PSDyUQS=msbH{U#?*3*3wtS2UyJX(K2DNL_&U z!dlHNW9jA_8$mQ@^3G;LdMN{Tj#U8E*mw3=s2R0=k_Iz&f_kF$l>Awzgr!_A5<-Ua zl(g-1)3v}23$=ytl)wo+J4BfWeqy=QXohktxOOXbIM_++99nDfVAjyY)2qfGaU^X7 z+Vag+-19T5me1>S7(XfFp^KbMIy;k`f_1XM0`1}zW&$`Y2|H+^0qF#Rrt)yYR-|E$ z@*bxalMl&w=wlUe7+M&iw6G$rp_JZBfxGzi+%@HpPEK$U6?TSpp8lDMp_Kr1sEdmg z(f&Mbrnv>Wq>`>sViSB!duxb6xsMv46L05Cjea*tL88N4J3*#tj7ar#vZ;cJdY@US zmUs>t5WIas`JSF+YPOs_dQekq8Vfx77(WG#Mi6H+4-K2&5{t{oHZX%Kz=0UIn0eHG z(ae5op`*34-vQWwV$+a{AK^(+rF_f&90Hok2~~2=jviBKJ{v#>H;t+LP{GFgk?EUA z;DPqf-LUAJ#Mw|lajUcNY$rsSB9mZ|)>eq0_|Y|!je}+e;z&c)D5>lxS?j9!rld{- zjeMfkl9F^dO|G|k&3uYT#Tn+Jrn5>xHW9#&umc%V^a;CRA|MIAvB>l|(9vl#11P}} zk{kCCI?1@jf3h;eYnyPI^0oqJUwtb1yyjz9bkC6Nf2Fj@weuJRvFyx)N%!Gl9P1O56&0*HV;U^2KP$N^hS?|7Zt2;Q z3T?$8dTHb zM6op+OV%=lYS=Ri^A|Mclu-3NeMJ*@i)pS3B!7t@o2JV$3^}gny~dFv^^1yd z=?s(XUz>oM@3g%XlSVZS*?Lu(%*LOk;&QW`wM!8(p&%M!`xhA)qO_4`=2-T!{2*t1 z>G0_45$3#X7~%-TFF>jphC{#k8<(O%s4BM<9uS>g2qoQsE9(ShpFKqzyhe1lsHZlV${~O zxJNMZ^%*QL^;wf2OV_11cRwqMrh+_%YcGaB$Cu+*!(((;f;3C)s(ES`GWpSB>@>+> zQsT@6z@j{IrY&!QDh^Kvt6=WdLmWvl@VSb%(L_ciw7X5Ne^03Z$TSe7l2%k5+DeFJ z#?9sK6cPsg<@I_IB<)Y-j-o{N<9zNRjFvR5NU{> zvL+grvmB88bp$2&@(~daKg@t;k^QRJ2DEyyF8a;TqnL!6sE3&G8wHQO;YX1pF6oq0 zjFZgeyR~D8Mf6mCZ(y7Q4tW;tpPg=+=?WyBjj37}kSeAK)_7WWgMpxlE4?Uv?u=%78Lsc_ttUHz+af%jm&1S)M zy9v8EV?0iKV@&y&#trc1D-=hL0Q%LxB&cYM=B}IH0&$_##kEL?f!ZonFD9b^sF0Dk zc={Tmkul~==;X>qJ0>RU>#wFCf{e_YIKt0;L`#06PzBgDVe5N>64jeD986iWIKShi zX()vik{EPMHPZ`ovrNfZre|vUD#lWr&2K#AJcwluTd1*bcSn|pqGk6-&B_Wgq)4_H z)qu;HwjrOp_4vX>M#z$ltHTndj2WTbP(N+1VV`;F4cM#L+TR5OeEWGHZ++3<5kGAM zzCwI0uBV=lj&nZPYJ0xMx~^|&p+AwoR`npf#=5t*qF@5jKC%Fe9n4T8cKUzBR7K5T zo)P7{tYuyjM9sQ-x^}>9UafrDs)>(dTnGF=QT&gJA?bbXI6-RN3VD&9TwR!512rf< zLT6$`QQvn<7jdCnNnD;akG=607yR}6bIf3g6h5FE1mt`?K7ksnwno+gCe$_Ng7R{hpNn$kxJ~k( zDCZFfJC}8pSx#uAI0@P;a)riZ1-tZUhFJMCR=Wk?Hr-Tp2@pBf)mjdHAdTyKl*=4Mbf^#athdmzYPejH`QOOEJC+>(t91tQO|aj2WI5%Wkmkc|`>QF6}e70WTebTTCV zDx8)J7h~=#aV8S%7_Wj9A|ZZ1MAvk)m0Pz<+YG*$!sQplGqH*ySq-2c>)QjNnE{Jmi2<6&AtpLjFBW!b5_0M`<1KVjR6 zxDe{o9Bok4y>-?gEN3Z1O(Pnbp5-^QV9R}qmo=5+wb&yNz4chFNZ3kpB^8P;e|Edm zXC=Mapl2St^;w|rkUWLWeE%8iv*f27%=^INKj77M7nT>C9g9WT8f)(F29!WUnp&(2 z2H!YOs2=6^b@nh#dOW*zZz0BY=ObH!F+_g<^l>lTL3%6=jhYXGNH8 z41{*3T{QTIc8Ah~N8Gcy0`Nqh!YXn8$qb!jx!Lqwh+?7$bd7mV{m24C=)&XkmtjWl zOYdrD(0TjH94-A#Q?f|m7~O&_AyGk)oKSR2dZw;oYuWVDP-2WuhbTV-%h1JQmi#~> z2%tov4APdhpyQcX!zYy6 zicFDCUv~ODM?%VDxh|L?%G;(IbvYu~0qt#8kG3WS+L(jnTzYgOcfEX`0};_BmYN~4 zFG)T4me9BnFpf_X0}a)+ObY+mB6QCNFOBhMdPXL%6o34VEKPRgm%$BDP3k%=n;zI-zy2EdgVs~Me8FOy&Sui?^jz6Y)V z=J5{_8fS@Ds|#D1m2B#E3G7w7TvJi5(a@u}Fq^+3Hm+xa4DG4$Hf_a-~+{wj1ZE-Ci?PPt&N#acEpR zfHM+Sg$k>NHLffeqbUthex|GWM`!|AIv+QU>h6r@iVA}4!c8Rl>H`0z+WpXWkO z=60U9GnPW;+Pbl%(ANzx&g(~WSRR^|WSi(>_4TB*KON$cZ{zjEYS+(LdI+R9m8Pw9 zFvIIbnkvI>080hC_pGb0LH9J&n>zgmwx`l&frVOtHK7+FXjty&I%a73o`zj^6B& z7Q3>lWzOj)qln*}zzqj#0@d8N!W$Wg5R`Sn=d2%2g5m|a`LcomQM78U~s(-{}3-EzYn4Pbl9ru{%A>Ob_l4t^+%t}?V7~po# zH!axBZSIsD7@7Z82G2z@aFlL(5z&gdNVOf0Nv;M@>dx}CL}x<fxpbZDH{^`rO zMv?N{acTjD`sC*o3VPBbYlp?vd&$Xr$4}<1P2wHA?Xo(Mt$w%FeBo95oLBjT8v8a& z`x>~1q?+?}LqOX4Yy5)wtm4%ba7E4amA^Hee&J+F)ET}KQ)S$*nsA}>vY?*){JPJo z=>z>T5x2#En?ZR3Qpa^OVG6vfNj4_zkZG`D8~P5D(|6D$XHd6R|rCY`t?UZLbQX4InPl_5`$T!Ao0L6!A~LolePKx?kdvCh3vOFJOOCaov90 zhuaK$V0lF;PYKAovlfKfi+xVppanw(AHWtD043O<$$zKz@;rVuVNT1|p$cFlgve zaJa*?Yt_kAJ>vTFaY>z8lMLDmGdWDqc$8+0(%MB`<{`-_<=61Q->=Jq(G7oJ@}HFo?GruPEF!Pu>FA*ZX`gRz7uOkwtMyF+YSgn)lA(DVHGKea)OpI zsttD(S8^h`#_We)Og<#<&zEy?wBGDs_;YlmB2bByy6Qx3TgMjO|wED?@&6bOpj;(UQ66|tnMfvd|lG|PVcaCf4_u$U*NPK2~+z&gAzUw zQ+qz48_pHyKTPeA-ms$eb-QoQB{L*63aM7Aqbm){gCrK4OMq_Jm3q@6ByN{AB#B*i z)uX2;oK?MX45e!u6mkTmJ`S+_+0Sl|E<@6XMk(o_iZkhPO$o1mUEc}}?+wU}hb;GF z8mN1PhKdqxax`uN>-!<@kf{b@F4x4f5y2<7;GO%mhj-BZ1@*e;FCJ{B+X`#JqF2Q` zq#!$3U>P??Ib>-3UJFHAxj6@7Kc86+DNbOw2GIH}P*G53Ov1!rPis~QB4gwK!Au?(4NeF!zYX#`Dju&A3Rj%`5 zrO8pt*I`9g@8kbW06B1HVJmZzjb~{Pq9k!yLK4)D6^$SoT80mZ=Nb z@Xk?k#2bSAE`oWyfO1T;#9rM?esfWUVB3&f?;hVAt5pFCvU7BtbxDcYH8og1z1uLc zYYbXDEAG{eJnW*nOG0{sSk=#|6{gA_rt3t^Gm_-iknqe})laI~e@0}_+&Y?`g{Ix6 ze?e2z!}0y+I-Y|*qw7p>d1zxOd4yg<_X?*RgqGQT(x%TomGG)acs5ksozn_aCcP=H zikLh(hZ5%scval^fa$%mmiJd@%&1|!ff9urR!50|cG#dGUm?$Q5{}VoV+rpL%^29j zqD+d1Tp86PQ-IKGRxraX>BlGu%%TPvHRN?czGn-`9fecnpQ@2-9I0lDR?>3#Z6gK0 zTvH^wpT@Jp81Q@(9aZX|VP=fAPqqgTO8a4$q=30=kFslju2q_zqZS~tpL<}0f#t*n zO%d6S7z|@}e!}($h2=>S?a7u%UT1BXqG^~?mor*3RW}$tQbS5;YK;oTe})v=?8Zp* zfaY$$jtCa5q7#4r;tgjP-30}aNGN}Ku&s&#kYFEW`82Y8A3678|D4jQEM%{27+lam zt%fANIncW#ix$~E#F;*+GtHH(F~zNq9Yo#5wRY9K8OL4+o$wC>7RtOyjM`R?{w>J< z^8l(_UjlMmK?A@KzWM{l`e!@XRIm6&x@n_1ub!`*PEVVDf+I~JJUTmxKmB(wGRF zcU`_uS`#*g^mp{Ov<(7x>b_c0JG;@hGHAjkH@ip3Qt3&X0rFwtOO^`4$hOKk*)6m! z{>Uqn?$r6Ci*uNQ6U@CPbh^!v%XqXa6GLJVfT#=Xq2q+F`L>Rx2W~A3XnqFbz(5-p zPe%D_$C=NwgvXMBy|xgckv--W=yE`O6Sn~NUhIJ4GN(XIhe`RjYjwle+ftS3uafLA zZ*x_>dxi-6ryuCDqJxY+V0#~nm1i#uoiuQfW71i z7cv*ycT$CwjfNLe7u&b;eT0P<5)s!&{C!l*W|aiP%6($@+qE`!?{>?PIl2il>s-UB z_Ds+J$fM+b{YQwqO|5=vc0GjWYVUP+qCwqDZ};O)YV#3$dOXQLwxQDEl@qN11?jH~ zinWpPK}xIv@h}vVdFvn&DhdcittO)l3bXM7oj4u9T&@JSZQVyLm=4@ee1Ibzw8Iuy z5%Iz>>LCo54gzhWKFTSU=c)>xm_ckc{XZA-)^nfd)sfdeS$n$~E!y`}h z!{U+18eMe+^UE zgE~!B1l$H;gXrW3FZ61RXeDap_)T?Mh6cQI2Igi~bgV?RY{*1GtUF;BN%EV2#H4AG^+ zxg-4`^eh+Evt*HZPy()!=TgJqOqr2qW9T@4Q6a+}My)7oQof+LEb_p#Se8ARcZzP6 z;eo;_&7GlLmfcq+KV{rh!mICEdQ<~Cf44$-I1&=6~c@`D>`WW%r8e@`kV;*C)G#RaN<qiUqDTC{-MmUt`C@v@ZP2fXGF)Y?GFBON0k@vdNlqU%n)Iu#}831u>1v5>xj&) z)r0rB)gNE#n^0CP#<5Rpt{O4Ju1QZfpXH|(x%M{&@MH=8^Uyx(di zw7@SDRFB9D>fk9$rv9j2`0<}Q%H@6{1eZ+_o}%h?z9P(*()KYtAe$-a1}&UgR?B=) zGnM}p6#EEhNKy;dc#HKWVrp5L-8XTE1Ae5(5t0^X9dWK-#xGSk}^E6)fNtEq?+@Cf_B5-{~KAxjX~0(xMy4~8o{Xq4zU4;%PS1^T>H(@bHRVgpn(N3L4q{GCdP%6 z(<1ii5toaTeNGRQMSeU{ zWS_ldl+s!tsS-d`g#mc{O2M4kAgS*`SV_o}vG>;uzH}o(GKZ#9(2O3{^c&^GD6*j3 zp|g&fP&857HW}_t|o=W&}|`3mxa#WX6GH#%VHBHRW%u z&HNLna>)7x)f^UTRgA~Ei3o397nQgbgKJ;R=k8yOz&rte;U4qWY}=*!GQ%a;DZq~kJGa|2(I?%(j=TaLfA+S(?Pvws(TK36 zwDi8@03Y$UaIU6;94$Gq)+0WNjsCGEGw-XlqiwI1Y=7B%y+6I&mv~4wES#+ps#e8c zwwx|?$y3~^pRO8MamF5BtP0V%0AW*TgIBR+HLUKMpkCg?p(XELP{iDmn0ALO~*7HNUH*c)(mnc$yK7zZ3e9Db`G}{xuw8vg~{~=f+YDiww zA^OJ3wf~(yRP_H}u=EdmDi?Dn_wVc(8$nwe6LV8%2Yn}VTN`m3M<;!oZ%OyRB%=zF zmTP=)pNsYt^UfKfXDlagaALE93j(k~sekB!pu#=vM!cyilb~;_)GDFaA6Orhj`4Z% zc*~qz3?;-6F^2i?vTZxDCkC!`kACp%Xk$vcq3<{cvi_n>W0 zU{5MtLp3XOJ{XULoI_MLQnB#c2y`-u8r?0Uku=oi`odb0LB3I&7!`P|EUY7>X!+<- zJ4n~EF6=&zw(g+h8l0ray80SB?+%cqK42`>CZ6t~0=N4~CZBZlbx`DXMM9#Ke6#b5 zu3Iz+Af*A4vl;M0oe=-WTPWSZJOm0sq|rly(iN||zRi_t8Y?vtwJDv9TJ*t+;Nm-m zcJ_-RLBU>1#oZU)sugj-s7XWwHZr@cy>UNrMo^o5wsc?1w4|Xvz`ty?E#T_{(8U&g zp-G?SZSjdD5w2DsFMHGS}JUPXK%rM7EyD1W@^Nu z04w21n?>wQ-$N7i1d~XY@rb>4c4)2!8UAfqTfFa@1!BJWNA6ZQohK9h&H&H(`8g}& z*8B2{=i3|XpQdGMz#nK-$z`XTUz#vF9Bv2O%m6bHib`w*3~oRlkqRJ3W^j*<{5Qq(1?lwrM-7F=r zGW!}gE%^2QRghr0FIhLvMUiVF#NJK+G_V(s{1|JU5aMi0cqN*(GIvP_F9|zzZIj9Y z+lc0g58T{C+Wk&b9vsPN1NWdIstzgF8z9PR-U06^^vx-Bi3!Z~0Iol4|ZLl(VkrBQI1pOFxI?a8$y(e(ZmA1_nHQK+5+y-};s_{+Dw6e^MT7|NA-r z@07=XV5nlYHpcFTw$=v!(F(FuUThIXkv>W6cbCz{{s_>^Nsx#19V9EP?%EL*{ZUMi z!}{f?bGA`aSG~!6<}BbL=dtx9ex>-)kW^MVFOl?$sPI)%c6G7Z1H8hlwtD4#XL8uV zYP&i9eC6x&h1k9EOdmbLhA^OprTvX1P&tf8GhmvgZXeQ{z%p`9QcYRKuYd=W+hiNL zgs(8w%|V%>;plJVkK-6Vvh^WCVVpsXJAnl7(<7lvtaJ8_8?lYqMfVtxiRz2)fZfAn zG@e`_9?QT;g9|Z41AssriYf;!5BwcS8~laS3)w}ND#$08TeMY0+R^JMsws%u`4PZ#D(PLdiVJW3uMfbu+=P5gw$oWQ^|^^`doYk5p(@7;V= z8a0~ud<~7Z>t{w+h0DU4O;3*!$IRi>=4eEfcwOX(ku+8GX8h4b{1&WliRjcS)Kzp` zlWH*$4U!L&m6FGqNOxDI;_H4DTGg9qfV3s6uc_D!&Xy?)A2fFw2eZr<#|F7=mbSEV z7&&AwHjvL_X9|Q71?QDaPk+@-2u?{hmq@skh>shn8AR{=Iwa2S#|bOGuJ31DrnZNl zlF<4#e3-h6MZM1JmvmF#MQ*3K!8p?~bqm;$ZYREBsdK%q>i2}YV)W{6g}sUEA9@zr zRSX`3%Gq%Yqpg+cMWNDP*u9{L%z8BWJNyp}@1YDGrMf>`@0j%2iCbKI^dI$Jd2R~zY9OPk0vlRQA1 zG50y$W|!CB?t2AA^Wz>-XI68%Z$wTypk*;bRvpGQR52RrBNSt#OOG&SU6{`+3=ZG) z<>O~m#~hNgnG6sp%IHi|O}IHNv(6p5ps35v%t|cK6@a(xPb>E;yR_zy1oRMD`t&Q% zz-Ot{g(?v;PmP$(7nGebD#@3tV=3`EV{4gNOSzzIcQ5#tZ*BEaQ|1!}L}WmLC@&R& z*}nFe2&xXl{;^>L=e46Z-MyDcfmE!6Bhc9`DSaAcDHkmI3Z> zA`ovX{F=m0Ia4oEV!19Z_U*F z{1wu%Z)>H7SuvalSVQ5&&GSs=-u}PZ9NquMKzr_nHOEBp(o*r_7@1Rl_EzBmC)uUl ze|eb3_E9#6cnEc04qWQpBd?2AaOyoFMgX1vJO|9vOB$xmj#|+^;$u{Q^uPXc`9T;O z*QFD=+td98z)SCAL+clOLjCZa#CxRuC+?*sZMXj|2tS4V@10cPfADg=|0kXF|2tCa zyP-NFvLbyZvf1yF5%CGqPYaNTux|ti^k{{V&zFjm!R(wi+>B24dMs5ef!`NANnZtQ zvT~cQrcJsOWu4%5%f3W#c+0wAp$-ljo8F(yC2Y>k%_Y>Qe7)S+{%G1u0W)Aj8#Dv5 zW}ATnE*}Y9zMta?y+3Gdev6`>d9#%xo8el zV~iO_)o3#GnI_dOP2J=AcIe^X9hWOu0nG=^*l#i`+3=+ec-8Q15LiHbN$Hoj`ItQ6 zL2uygU?g}35#+4RV)YR8)UJy%+AV2iJRgBIThWn*NxKpaiuP2DtaTXXfPq*4LuF0r9 zx^HACAt5+L-gI8gpX~%xe)lX10O5El$(l3aA<@AExz)OFtQ;w= z6t+L9r@$En#@%L9v2d1ukE(Pb{a1tq*Foy2s!opsRE|Hsj9gl-9BdNxno)}JsC{Be zeUyTD+~H_&oVKzuAdL{c>D+k1Ct|PdakaEF&6UppdohHbxO!JZ@p{@ktd5QJh72dc z4XcePTh9$aRyK+I8V}ZN4RCbKO7tZZpvhH|*z6UWxa> z9N!St+>fjx6 z%icNBri*<$;?TXic;=df$_w*lmBa0w--5_2r&!~$j^hsOMOf|iNjdi??|%y?tS9QC zGp+X}kyAvn+9jJXOw|)8J7rpQDOZj_tPkKvW-UC#nbaOP3izc*TWJ&zB5NmP9^{rf zH7uPgm_gqT5vpF1&cBnvsnIi4P326rvvP0E?w24b(mF{O_djjTHOwh8F0a)CygASw z;)94*d>0sNn^yWO|IvW&*A43_7q6KuMzD@n#2yOwsk1!)vYlx-mc~q8B&Fyl;DcM{qmpUJZ8iMBh3h)GjQY;ZLK`3vP)GkW!Gw0)Ykj|0o;2VdNEc(o z=nJ@DLpR&H<`nqJQF2;<05dJD?D_{xnR_PsN4ywqlKaosl2cf^oHAe~6onT2?jOn# z9NF!mcE<<;7e8%`_PY)3t0Y_(9+ET>L;*`39a%DC*` z=Hz8!*3ZK20pf~ixn#vz6Kld5n~TP<7N(7)YtmK&sz33Fa5ifNCwRO_CB8?HenGgH zzrwvN`(~eZ537gP!L;SQy2PZgs@aNjGW4b6^!3jSR+)f9KIU)7mHhvQ;|kEvWufn; z{C_|G4Uqev*v`L<)&9Zc5;6YAU2c+M&o__@=bcetj}0X)QSzr=4sW_2++GqARgPFg zUO&-aNG`6~8Y`i7*>Y@CXtypFA(rj>=R2JLZ-Yf31%CLb!YuTu$;nQ4#CVVJQHI0Y;@k50XoUlGsAp2;XDFz_eH85$GhQg5ysK4MpPbVTQ&p zPHgOlN?M-n$ll1Tn;B5wZr72hk8a31uM{mtmAVR6Ou%JKQ^YU}IAHH(9eli^?h zOn(QOKyTEVPV)@_Q=HvrzZaW*a#(gWLQ-sar2^8N*3e|%RK|zLWd{-U5IHl7mUdjU zPV(m1%ZUSdYBsW%aiqmbgdfjYx3VAe?BsMYT}4N32_k)7X}mN@xS>LFO~)xc!($+*Eimj*s>6)zniyJ; zatOkrFb{sStDw~Q1k2aRLPUp>GPF>MP?zTPMXXSR0(s1&z=b(4St8D9W`5}*lfutn zpfcV9Sk)|z-unUI!odQOO3h2TNB6?_8W^cT4H zoN|Yrb-Rn#2tTKr!~Q-Xv3fDqZckZn)iEfqhkHnnRL-# z)q_8UC~W%lt8Z5oR-dxG!nDI7HIchJM+=FlEr1h-ShcchS%DJPZ1J?xH5`2TY`Uf4 zO=FTMf%TKDIH>^Uu^-_=szzhd99$bkVoQH!51}u_k?pGQ$K-Q$Br@>t$D$$3LY7Wf z=I82g*-}I8$12$GF$;Jj1Y$bGbNKZdSP6JWRR#c?kb1iVv8hi3d#TaWUjzPV^UKxn zv^U*BT0B=JaJ>T9J3M%NNfYetD)N>yq02JIrkQZfoxh=4KFr%O}VXZSvPQO6Nb7 z(hORx7A$IH?`PTB%DUNI?X?=}G$T@`kl=N>X+;oqnM=LB&i{74oO28n0}KSc#1rlV z5u(=gtzsu+nIUz83Ij#i2;wM^CN;&6jI9Rs38c3O!xijL)|=IQh3yLTG)B!{x&CX% z<|_nRdf)`AW8p!)jkV@y>(o}7mvRzBw}Zm`JRo=ka_PA zlJ&do7~%@w@$=buLdvef1{ zRuVYO?E7IYv&-qe!ph9sK@Rp2xo^GI@7r)~bspHqIyEWe(9f&2S%*S~DFUrn2 zxUy(l_ua9L9iwC0=-9Sx+qRRA*|BYRv}4=0-Em&dIrqHx?yb7_)vH>ytM!x^=9F zfJD+NHqnMe=KIq^p^2BPz#*X!lw7E%(sWF?M){3m<_qRW#PK{8L~rSuM<@^2&ZfW9 z9c6JcH}L!Uynffm*MdRYHy(B`oIW!Gm#f_#&yjR5COkjDAf=IVrz4umJ-;zhSZipb z1OtbWRq8jfou^T%I2bpzEijpP7T8d9aI^C9GgA}n^{4}Dk%=Pm zZF{0^DHl9K*CBZau}u2g*xq}QA~Ic5Q$yD{H=$Z!py_()PBerQv)&9m6YHx@%qHDe zgXY$rhMUke0m!%@XY=Vpp-*5V|4n5T>a@U932wS#V1i4Ypjtv~E#kcLWia-e9*#Nx zJk~$9@cfGFtfr&a?EU6)B4H$VZU~j7jj~YC3H^z4hPCQQ>m#b zf+o{XCR50rCYcKGy*8S&iD~he(5D?4mnxIs5M_# z#rbis3t%`ZRYVq272$^&mL_6~u!zC`^^M&?OduZ~JT5Wm|Z!5Q-Z2&q^jE}8gUS~rYr3Z-;)l z5{l>h76nyDI&6*gwUkd)_7KRvySb*Sr$u4?WrQ!!lO^F9R|11(^NP(I#{`>1b_qSF z6vQJ_Lmxv``c9=K5M4z)xu`uD9JXL8J-jK687UpCmL z-Ml>YVFrM|owxNoszy7-@gh4jmr%kHWlAc%JSO#PznKJ!QtwwZ3zITlZ!|UVdyXqD zX#N(muN%Y(Gch@xo0wL*q^V9j=lv{)02ot>c$GBzBz+*!>cUR#+drf7T`ywC*1yV! zldn4Vzh623Ll%OdfsxhMdim#%v&nxf8g&bO}VoZ-{m9yPP#ZVY0X1wsFiBPF=>?u%`|BhnO`s8VdHGqN;=25bltJV zyhPZ!M6yk_?+9M|v5voUiFPTwbBS_Ew{wYcX-ogOtHk8389Ifw+W?^K!+OdJ^`SpXVfOKl(u ziH)|N&}=@uTx~CIY|;l6nt>XWu!%nhgQMfPQaMt=JLoEvcmGXgLMtf3c5hwa$EumU z!O3^>_Lf1E#n$@zPA#k$O(!)eB{{Ry&?6H;PU;kSjz(SpJgK261(FnmEHv3mw0$kR zwX!j|yPiP{Clb775VU9O%X^X9esf~b4aXHC94$Q9-c8S_{ubwks%dR>sA$9(46~(X z3=9s{)|s*uF$WT3{^F`W3x__*D)EhdKjh|D1TPRJFSPtY+SrLkxWKG2Q=uS;h!KU? zu!@9g5fiQkkxV+>3^RG;4?<3Tb4K*RF6E10?!6eAC4V$3Ozb8}jknwsxmUq<41#ET zw7B&!)XkuJmJoSZ2Bl@`ar9W#4dBaV;$e?*;f|g*@I>UnrU7ZnVgX)|B9V%|=vSJg zBC{d2P-DLgd4j=7ytH9w3NzL_T}vkFvU8n{4!fNwA-gaWAd9y(=%((r_ppJ1fCJiF3ty6Q;Umg3XKm^_K;uVqdUb&QQK(I_ z`ln*gEC1wmInOBDo__2a6w8;QKhF4xQFjNO07(q5B-}a`ZsT~SPPY}FLuN>Hhi!)i zzUK4^+P8d<)nVVvU8jG_?uarevqV+ zu_1BoYE*z3_ZOb)6R!6=*XDqo$rGN_MI7)w%J>$8Xh5AD-p^09rt(vl=OZZg3+T-C z5uK_|@JJezf`!0BiqeaBVLFRKziGsm8{7?wtxxoG~`6p7|bj!yxmq)ikz6i+(FN&W4OiJ zH7Ci)CyUxJvSzP>s~9r6Nx8`;PCsPB0}XZ8UQu1(<3wt7IuyXCmcByui)k)3@e-z| zs@NC$nHbh2bAAnz%9x~+TTiuB!0#p907L@Y|E@bkSXc6tp~mi#y|CeU+MW`7n9@*h z$8?v}*360=6|GUvVV601B(~k(WCYRTd3r1TR-Tf4CTmDcX&{`QMEb1gI<46JgiSVL zR^`k8{ET|X>ak`79PHEMISh;GeFUV=$K$mJ!HpZ$7qZUIo23f{E=s<_+k*+^N#x=- zr0KgjsO4(2FQjU1>L_qmxG|tiUASCl*Ii6?%IxVospQyd?OawOFX7)W5Ge>$rCFNj(FWosC?(M{-gn5c4M+hB5VF z*Y|^t50-~QbUAi*%v}5)d@00?F8I)B+)_Ay2mKjuLySFQ=vp_OwYRlEn`~O=yuCbI zKy${l(i&^wxs(yu8+XDfj>)$-ig4qP%o#m)nC}o?h+25feb(-$BZax2gr2kL>??j{ z@By0)jObt{lZ_&mHIvOsaW=KF#ba*U%tpO#vm$t%6}Ldu|dnv*gKhE~d;dfJ0>98)u=4w*qq>OLF$0-*3J2z}swUfp4LlfkQiK{a=Djyp{Tbc6lOJOSW z2ajfmDX93DDA4XqRMKZP`ujDh*T_gm%^4n=Qe>QWwE%fvM?w53;)YVI`pd{Zr&kf&>>F$ zIW#NMvt_Ca=g#smulH!u-?dHkkZMjbjV-W`DV3OcXA}$76o_idOg=6-XlIaqF3>p6 z8i|hMv+9fcJFs8j>c+!+RkyMwyL56-*a&|PIG?p!32aPMQBzGDgvJTwt!(kK9MiVQ z;y73S@#$5bvfH3^4NWU!5IfyQnFO8vyRTEz3KaG-tNDUdW$Qw5~o z5{FI+bJwv6m3yJ7Rd6Gkdf}o|#vZf8B(?sy`77YqlBUmNALa&wk?P<$Ko9(U*HPh- z)q9?9Je87JACr0}je3T61%+0b-BhYkd_zCA0CaA_xG^y4!K^o?wG3}(GSm@Mb7;FJ?KhaDeN){F zbiILbgU+VQEtX=;V)aSTIsG|9p&4&E$^(Tlpb>&bywyDs`L!iQExIeZI zB6H*RcN?~8;r-`723z7oZCBV|joJt5|5S4R53=dMO+#|_|8NR9{mUirugbGYWlizR z8N!PSB4s!L1!ng%Iv+w<6}&T#BBBL6m`WfC(`DE;VaC|a$}Lc{`>bJ#?-}wGHc9pQ zD_+iI${0S}T!lua+_l}SV~UeEJKg*J@dVbNnSzV~WUl9ggwrfB++e;(9ii zz!yO>2(PU_ES+4aj(Q91qz^d?Rb8l&#sny)oN)=%_4Q9fW3`vRl_^z*%vQ*@<&Ub+ z^-KGvbhvIquhDo^J8N5DEWBvTsxRvA>8RiN(8nLHrj7V)tq-c-m-D5f!rkPXal?6; zhMbKrlvB&r4y#e2on$>ocGeGo=DksvC!I>eLHbe$ez8AL*Ifk1O!YB@`aI~2_uVRr zF`!o4j_kgaDN{A8J`Yp6nV^^FsMuA(8jK`y>m`Kn)296Z@9TbbvbNP$dTR~S<)ZAh zSan*ZR3FK6!}LesLH=#UX&85zZLlinWvU1z3#_1O+b8>;#4y3WR*N?`1FMrptDr1X z<341nd3im}F(k_@jZVis4C|6=!Q?(W&~u=$KeFs_O7`6NB{YmaHF~NnHQb4-$m_k$+*5V{m;sRAdcTAB~CVs-a+S7*66a!`@ zb2N4?%Q^qt4p2&fI>-L~Y;?#sM5Nhh?a-f?nt@(z#HgO)sXxT9ke(2i|2vTF=LRl~ zXc>rJHaw4p zBBsSsT#~YfQ3t$|-jM%T zqIi*|20~x1E#j}d*8kHL{@2L{HM%-~%@GGMK+MYC9+~uiSvI8}z=)gIL)!SP`+49KL;6 zU*hpy&PK6?gw2D{meXU_-`mcmtdEDkHGe>tgXj~F2g+2IBj*Gsp**-E8r;c7veO69 z9P7u%zHpr^Ft?mPxVa;mILPh2grHLZIZiX==GnI06ERSevttT|)h#@Nc8PKVLpsb!zd?K8$51z;E zRtV`<-BYkIo{Sltd}sP|PU~72p z5t!&LwJfSaoeCEc#m=f*1{ThDovdm$Vk*0A#gK?s)+`*myB))9D>YQn4J+|uVi3WvbHNe+27@XZo zQnTq{hb{*PeMU|b`$njx&mY;?)O}@zJ`WTcat-ue4kI;2oX0A031O$?Wvek^b56UX?AA)J; zm~&bLYuFFg6-N*d(4sCyR#oPVhaaA$8dutl52ylI4LLnF=U47jX36@uvK?5*Y|E&l zUMw)kjYlg8f*|r8`nr9*hhLN+&E6f8L2)qiO4@3JdIQmCC?tM}@fcCpc}_gkz#* zwV{d~5Bi?~7vh9SwS=tJP@BHac6K_mufJ0xDhASp`#g`Vi^aQjRErd=8IhmzLOukG z@v$f)08k$gJPzRXZ?ZmS=T92^fDxnvcP4mP@T?&?y7=DUoHRn z5U)FI@|5!|q-C{buoNI1C;4yu45LwLlnI{5?OWliYelbS^1njb?x6Ymclj5={DPes zy!2PUintRoyw2dU{~6h=L&bj9@kc=L4+QVyMt;hZaM?Nr(~@$t0aDn4Pv&iT#49-? z8s%0|v_jltKe3G-m5r!7j>ViknAndIu_sKc`EA0zZkHno|VoZaFIn9CL# z*SlA6kF?xb21IgoFqDkMNWa^E&Igj#g>D_b(q8lb69x_Bzf9`6|NHaoUl?UU14m&; z12+>#Wjix7YZDS-6KfMQga561%J)iwG9qO!x34XhFW`f9m0JtI1?KRmMAZ_)Xm-iD zh1p1E1e0ieP6QEbi$$y`YSQ4dgmrq*Guz=mzj=Os^M^eitVQj|53&{Te@{ z7OJ5Qnk#gRqiWII@2oW+BzEFyc?$g@;ay8njfKmbJQ_t=WJ#6u;h_$OUMpK`PSZ#sKpj*A6zJL)e0 zZnb6*>`ERTjKmRYmI2%=T=in&6dfmazaJhqetaQ_&$UhEsB1aeidMv=!c(;D!31e} zAb%`bk_Uv@rv?h6e(m>#vIVU9T>K|feT5zDDxxLl!rG zJxs!SrA`nkkHez0a#%p0I)W7BP?3H1D@uV>B#h`jFeOE|vB*piQC!ji5oX{!cQ2{D zdB7~H=X6kWl4Ej#TORrKge9$1v$5glOPH(cX6N(agx6N5M@5)k*BOVu5{Ytepxnq) zW8Wh)jP^1;3FLXJbnnAcW%N|U#}V)vb6^dgxfYy#l!GD^k+O6U6Syn`bN=+}c$20b zboa&tOHV;QtR|9D5A~&G`NjpjWtT0$xg)S2(6|?FeiiRf5?BOKWQZ=O^Ftg{tTFnqMiY4ew_~pq0MU0E3LzS4EMB3Sz)S$Pr?1`q{&nPjvs8Fv6 zx`9o5=_th^IxZVc&D5J8bR?cAR9_y>f%8`7vn6dt+RZ%}W5pTwDN*PJZO-hO-Slit znu@iX@L-S4yC9v*>9+576x(aN@E((nukV`((03a-xptZS^M`kKl-Tuq3eeQBqJZqB=kY%8m!7AVF7>@7k;62_i zn-Q*3;{CLymoD#G;hDL-19F$HE{IPhMD#;_F7Pg^a5Zus2b**lc@2XJu@{+Uggbv^ zOdi9@xBo!iR60+y>7X;gJx&8A$|lM4uROWZc7`z)tXAT>vZ2PbfwX_Ch7tDK)g;TD zJ!#mTkwN{@n9h9e1E(nZC#%!mnc=)lm}M6RR)J@aDXf&OrDi&b$13Hh#C)W%A{eGb zH6tf2kq*dhhfkC(Lxx%wUE$}$$sm_cUuJSj-e-+weZDS>oz3UMAz<{qmOJ@4ae765 z5jI9?5TiCnSQs-ea?r)E*)BB)CUO|Ykfcx3x+EnsvYRrgzo56)CUUSKePaKX1mc3k zx0qd$sxmXyrnxpj6>4@HRXxQ#n7q+|SRr~QGI~)c^x&}7X!n>Ms*To`gb-Q^+uP(s zf4O`RmIMVOX(^?)ISDJVpZ<7;`LHeBT{SBa=<>!`jxZ+@!IAu?Nzh;4IIs=^?r?p} zd@MtoTu#14Pm_#V#mK8muRRg=>$foIg7Kv4_u)%opIJD@nl#?mUj=Zh-hHn$iAWe` zbDq7>R&s@JA_`jT3=(K`gm|sWk_8b?x1JG=6|^{U1HlD{WDO^qAa8gYyiyHXIK{s9 zKoPT??|X(>gDdQW04Q4A#krH0CaWdv%11_eTbQA7)KV~%6=ik1IG9g5O4vo>^)ljb z{43llQp(zqyt}x~{H6f>eVr9aIH}@* zKbd-ChVIv^*H+&agW-fj3*1k=VMq5H;#UqHm~RnG+$H*`oln_-77Tg9KO{16;fS>l+!E?nfG^CAk8*@dln`Joh)HY_=Ft{TU zq>a%fQ3~)F=&?YR3kefP#{S7Ms@k|OJA90(i01C76^#nt;^F1?Zl|~Bb0?1`X>!}L z*=*8hecoKm+t<1il`-V@?P7?v*Nsb+a@vXLOr%z+E2zOV2;s{q;segK zx}lHGR$oE1678<@*v!y0G(%*2nH82br7@1b?`m#f+sq%#X%zn?Zk3F4zF7mjV68dD zywR8+pcG>^cx-0e3wetl?Dt~p=SeIE=>rdHdw_C>8tVS8)*-i(%cZ;kG-g+qH~ed>baiJ7aaEbd){UKE!{ z!1UlDfa8N9Xsx151d`3{*5F1(^#q|D(=&U4D+;`T(2jdz?*U9tTS+q|A+e^^hS!gS zwfcX<_&znFYBrZ$>dbZZ)1=A<>b9$PE26yLV?a~=Ir_Q`)ry^c$trhhjEFFlug4wr ztTx5qdn&^c=bW~ft0Zh#Zgc=u=#y%Om)7^REXc$JmN)92#|(W1%P4*xRRlwbswzuO zXSj0oNn~X=*t0MOCxtl%Q}_Z?>%8>AB!gzLI*_4gV9J61ZB&);C{&I+>X_!!q~MHp za}akboSwr3(Kwy99iWL+wIlP8pyc-@Wq4odZxW20ce2|RVT>17&k_$Pr@@XRfn6SD z{W@N|x((+z_YSyJ9E^rsR)?*A4`S15Rx!rE-!Nu(+Zhq7um*XsdHq*A36^mCC6WOI zw_;Ft6e;ztb_S$$`C$5V04_&9jGWR20NZj_oz}C zG}UIyMad#7(y@iJYMA6I1=YHj?5|&?@edjYP9{j>txEgt0L+(bl*mMBS3Hp_ef_L5 zO%-9&_TTDVzAZ*-4c?rYtlmoY-elSZBP31sLf#PNYkmcSMfk$2vG(zY4_;2bLhN`W zIf+dhGgWv1oI4uQ@mpRquk?i6xHChi9n%O63#d6%v3{@R9s*xh?J^6vQwN{1g%k*RS7 zbqc-_T9;xxGKLN5AUQD}REpKhB`M1rhy|)Ho3{~PRdUWEq{=>kdDJhGTb@1 zkhjK@AfG8<*q9xwQBa=9zMaC`t?MLt!#*h0%GtCPz|q+}B9ym#Tu9a;98@2HYe6-F z_Yl@}_-dnb+8YX{}qcb{2p}AJSh0^(MHz(3W*-Z~#xk1fZ5t zsAwhXH$3F1bXC!HF-<~5x+5o?{)SymViVGnT20R$tn9-#g{F67W4qL<*95D@L(^81 zS$);|d`_upI&G54zlt~&Pt03Xd~{=P*6$XIq z2{b)!tq>f+e@KzKM4rW%?5U4eCJSl&-ab4VfzGrnRue;Y zKRku+#a`;xgbgWh#(B1hGIrLkaMEvZCNPfwoS#;x79c?%`~78RJeO}|cy51quy;Of z1VM>KE+=8h>8f{gRsim3IAcH0C$!k7*CVpZ6@^YesS`w6*k5Hdx(o(bu~-B>uPV}Ch6aX)@$;(#XNc>7R46&* z9O!q+DbAIe>@Z-g3mIh|Z;mz8O*>bbaEb?IaPO@Bsk zCWtONSS7JaK$ofyyv{1yz;4Gg;V2@wtB5YS3lD|*CPU~NnR==&iK^xhl2``MN>W7QCpxu>J7LT%0*(_ctY$OAwG5U1dcPxH3FO>$~crZz>S@SMg0OlA!RZ2900x zCJ~K)j0ZymA|Pm5V4rY8fDJz%?qnh)Gphsh`f z79K4SfCZrTA)Un~Z&K7yO_4cJCStrtO03w^mPh$U7yDq#Odosf617$j7xQfFgY}z}1g;kUNxw`J(*5)R9GP z+Y)NyBiqD?KdCv?MXt%&Y^xTD+0~-MIhc>lZcl+Tq?@i_U37r^5G#;P@c~Q$d-mW! z);#^pKV8)4F)u>B?fN9A@Xf;jLUXC|qaUrOa<|oYst}#MkE6xr%LLZHW2fU%N9K>2 z>~hD3EYkCh|j^kZNN$V2zmzLuBsMXsfHLYxKIyEk^PO z2Upve0vE;SW<#1Pu&rQU)^!1V$piF5 z6?AtyHiNZ0rKx+-BhTJvcBnYo*9j@f;%`Ex@Bvt0y zczX>O?xQRHy(<&BhRnxfF8eC_p^Y{7fC;nbnXq2pnLs+qYvl=XI$HM&n(x&ER64VI z@Qg-f9_d~R(Xyprm9+P#=VoSTb?id-HOkegznn7r{4PArUFiEP8@J62t8dD5YtV(O zi`4v|NrDSg@3uVsPSQ2paALI7`2`ImO?5_h5tHl}X*HIn5a4zw0UWH*6vC_Q<*i6Q z1m59&!~R&2WX)%Du)|Us{4*zq7c0 z$QUS~I^D8)ECTR=vQodpieZ8g)%#^#>C`@;j;?%e43xvSx)0Uz{=R}8x*h4h^sKq3 z8?K_Qe^By0$$j?j`K%{de1AnDB;S81SQdmk^LNM&G=eA84ky)$;P3_oX!jBbe7s6yzJQ8s3q4ioVqkeflyAi&f4I zWJrpO8)2^XJ}V}sY6B&ume*D6xFJaPgm%d%>?L*3I04cpHbxXEG3c=9v9(T)}%c?H>>)esFlxtPO$2S!VuK*PA|Lt;xl}jDYPz0&x@;D06PB)LHdo9-XzKQi^I!zqt^@ zaEX_UZW9}5wk3$aaT&Gf#aW7HteYTVK>R5o8AL9l`3sJRW@9>fG-`8l*AjKt72NCU8!lg&%xrb{&)b0qnAj#8%I$P{yVh(sYJPZP zHjzJt8!=>bIxMad!vGOLrfkPh5vb9X_L}XJp;!`YPpRpRYiNfICavo#QsgA8LsI3& z^sZ9Aa9Y};O#^3GJI9l#=z}KlVb3@M_cd2eQC+imyhM?!Y%2eBIane&Hw6#v^=lvQ z*-~v(mPhV|bcz+%$aS;D#m?k@E-#=P`EwZU3$iUI&fFV$bE@r$K0^Y4pK0-tOaMv!2b*mU zj2rtveWYCbT_l?M6S|iI*|jts+>ip7RXX68z9GwPUXpFdb&>z-yejnfOg(VV+4I|T zq5S(NAI0%;kdKL z)W?+*iR_%vtBd<+*{NA4E?Hnd=Yn-YCKfku%^d7~zAXLBn5(BqiP{M(_q#^PlE}Q> zbS769X~#QTmr;qEi>I8cm$0GL8OV3N4t9Aw+bfg|nwsjlS7++Je5xtI*wGyEk{aOF z;>8Kf%k$ewM8m!vZOduaV5*lp?4o_n_Kjz>_h!Q;flj{?&xdLgzadto6Tx&Cmya;P z**)gC{EEo#vJ%kAOm=(O>~HDZfHWJYQIrOxStOlL{#;2uw_nke^9zpY@ja(^%^aqp zH}vQ13H8Y0~ooHM5?rzSdw8fkcf(pr1Eqg0V6PjMvxUiGB)`wI78 zOr;xj50p73-HJvlRA-y~LE8_(PI1H0zpgg*)p*4+byi9)7xwMR6JnXgR${fQ<|>^H ztIm|B;*3g>8j9mzbth$cQQ8lY7&IpjEYQ-0CJzw(ZZz0|ym+(1>IDli2jL?;Q|NRp zyQSF0CeKIVRY6MZV+>P)<BqxzFLo)77)wJkS^(r_6W*I^q zcg8DLW6o6?+^S0{bF5jd1cu@}sAk^9aOCae1eVgL4qA7{-!`q zKtg9VDE5bCcC07X92C%tX|Wh`$7V7-Od@J&l$oy?QXm*rN=49 z=7ff}PA%=Wir9()DWzLDFL^Bh29aSLKi**SuKD_V+gkOAPwYy(4F}=A!lAH-e}p>2 zzOlV{MeuKf!w11FOjwUDx)|h_oALo4WJxZf%{gvboE&;i;PSr*wt2aR~ypbg2=OD6<@=QdWB)bRI} zq?}_D9?4N1cTowQYz|7V<)zsT;tZ~70trt43Nh{An=X3D%AugmUCjhzOjUmq?`XYi z1hiKuk+*z>n=^X!BBzzTra{K3la#gs$x{T+HX?!CBT4k$V%+KTm(XO!7T3wyXUYJI zY9mM};Z|9l-m<@z{c-GXa*EtZ?41a0?u@u8Gfbhvik`a(EABLkg2Sh!u!BcOu7%oQ z_WGCdbzGZvTv;?X_M@)Ihve?!veXFort?FY+RZIZtOm>lh`wfpwsZB|f@j{2F=eGA z#|$czhNZu%gmnl_>P0DCnSYp)Rt0URzwg=7-l)%E*Pnu0Z0RolrW!jj7E|w!D0=$E zkyePA-sk@NgxZU50#0I&xhgfiuMmp@Td?(L@ zVmbnET{4sKkisFYXhuq2HEL!1^P5v*JLSC>_ubkWR02KP2ve~JZ46WF=<=ZxP|fD# zT+U8a#RGGV{?JKC?`9ScKG|S1H;Jv@PCYZVIOW{8pHua`m}6uJk+77uu8TDS=T$^^ zGs3=5hPo`8IA#bZq7W85MZ@;t$!WB-d|7F9sdIa;wPLVtr9o!&P~birw0W@$*0dk@ zHI$cp+M)JIzYjmMlk=4{d4+u4gu7G^U$j=hJf1=9jAL$;GZvdkV<{;Da*-c0IEja0 zUvlS#hXC=ZWh@&s=(^lXODHfnImLu7ZO@finUWw^U^OgzCcbfZ!KX>{bVXA51 z(H?&j=MhcMqac8TICUCW-6HuJr_O1RitR#BnU%091fxz5uVr4nNnO4_}WfygZm6r8~y@ zP;01GIm;lg))zpw8O_A`75%h1&KG~hu`D15RIE~(^|!DXKSFvpkX0*D_u4JxyhV~# zlo&d8-$m@-fqg>xrp*BM0sRRQ>^FvZLkJ1c-y(70!1ICUZP)bKzsFC~^f`7xkhsb( zP|Xu!&#Q*dF`p{$yASDjP|}@#xMb^#cgQ~W#x-Hr{Jbz>$3Jcm|Gt0-;nbELMpqrT z)9sDhWAeR4H3%NeO5%jBXO{QpSX=%D7|s%N%2nWaw*FDMP`O9Dq1<^Co?{rrr*E&NxgrKEZ2JUe(i#JjpfG ztjFc%-;D`@zFZBV7BF!hF7Rh!+u`6m;6qxenwlP>x^E+X>Nf!H3-C6vHjxhA41n_n zK&Y6sGC%f)5B%!wO0f>Wbra1S^AS}LfeW>!r==-h1`mOTsBm{mm#^(7JBE}-&;|v! zrvDH4hk!Lg_~iLF^7H+d>O!4k zyL=H345nT@AO*OEti=i)fQn(vkbwq$HAwnVNP4;-X7#602v8Uywzn?2m#jvDY&|vI z8plr!dRUPz3jm6!#Ki;g<58e_{>iT}7Y~>`gV>)Ia^U5W>kS#b0ZnS+Wni9er41*E z111a}$LUdO4O1lvEa(q2RwczcSb-Gm zm|O3Y22fCi(@6;{2-Gn1rl#BT7E@R2~#hsI0>_ATMugi#hHz zV->f+mJ{qgFX6On$72O?)_17Lwm1>&cU*XBPKw;|(#r)v=w_~U8w zx?P*)t(`Pq1w*h}Bz(cu-2{KAQLRtn*-OJ=P!nfImsr47rcR3BySG;>>n_f5USb;6s8*&4 zC%bOu3d5?avSvzox>5Pr6F?Uwq=gUJNUYk)h&w*YktTEajV%atNgF{sYoN~%L5-RL zHCBwM0Vg^Sq}Z6PqXVmzAkJ8q*?`VBY=Otp zta}s4sSb*8c*&VHR-iGywj%Au_}b#JYZ&wUY^p^vlgBuNZqL0==>uU8s=Ynwq6&Cz zSoL;O=anc2w~E4N6AmW6-xCQR9}<&=gbNyE&j<%3m9X_mJ6Jrsra)bg7*CGhH6Yk; zMTUX7VE<|B0k_I7fbm+wS}b%+j{8fh6RSGdn?ZeX0A|Psua23)GcI2U!6q1LW3_`92?eN8 zB-^VA|0GQSRCU_HaW!WoZ>!Sw{w5luOWETZ+?b#oDPusM@~3(;LHW&0!)K;W=L&(} z&xFaBX&)h>6(+GJo|r3`w^2yGB}|~`>_)cCF6@mDnF*Tz*&0&$AdN+ zcs6@zHCDSapk-zX^G;%1!=BhY-?agQ}$RAbb>rP|F_KSk1Wm&_rUTOH; znqlP1maYlQn=w`?Ly1Xdz5m;#nLeUFEI}~QlRU~n2*v?54VnxY40{1bQ%kVtkL9tk zzl@l2wLo6zpqnxhFk9GON!VUxr87K;o;j53EaLCpSm#fYC~#p#7ehNYWYJ#goFwd( zhu(&oKwXY#gY0f(7~?RxBPd4*K?|ail0W8jv`16vzo;q(C?NhQ!`GR|PMDD@`@(gQ z_p|^u*0Nr^`d{Daq|SH&u>?IFZ%D>M<3| z&k5xbx|TG-s4M@Aw6_eaYfHLD1Hs+hHMqOGySuwfa7b_n?(XjH?yehm4ek&i*d;mL z?|1s#?$h_Z-}k1XodkLKvy>r-?p$TL4GOQ`702a@;oXYJ@xmGMPhZ-%!vdBid>?jXXh3 zwT!C7g*yX#*JHZ$TJ2k)BV6LZsmR!mno_&fk4Ee(y4~(^u&%uqYmK!T_Bo|`dyNSf z0gO%7iX*ZZCyf%)^YQ$CJ9L*?3*Ro($9(h?zZiG?3{AK9fO6f+_07Ym!K}7!qauWlI!M;u7+gN|*`~8aJ6$tAU z2-gQyCHIO^TT}fiqdnUs>6)k@&bo=>bWF z1DKPI7i!QanfA>%lTa5}vPXXJ)=Y(dpDRt5f!<_og0HKd^kO4`H03CyYXQZN_LrZ^! zgEMjLjMvJ#LtBNGX}p*_6%i+_A{v+u<=$&!3A!o= zV_RjvZqV3U+AwdOQK9LYS}|s1ujX}!Kha6mGF6>KEF;;NLflDW*vP~%n-G+J4OJ7+ zHh&hV&@}{7FN9X0q0ocf$OKlSQ%yxUVbVSOUOY-oBh0c5$Z zg<{Ylz^Z7+f>LIp#62-27JeuYSadwh?XkP?|hj zSlqaNA17+Xqor`-$pg_bmP{ARwE46Gfz6&2hSrkF)ffH5USJcnCidL>fYzE>RcqtR zy$F5npo%iEH9(o>rE93PJ2j?~Vp6Gw;`TY;juzP}S~Y0KW8U0kG}yhYHlgLUyj6X^ zsb*nK6f_;K-Sqg;ias!O33l0;4kq_Wed?Z{VjUj&2#7rqFJS%_0p!p?T`a2m=oUj; z`itSz8+*Sw9k;~OmW>B+XCZmaA;oxZQB6o@kEBN;{N|JaE>Be05vaB--HxRP>~o7RxP}ZDCV#tLqh{2Yyx#{H6O(2))9nE0&)in>kaL zMnAEAWV3ep4xyjny+_2NtA=Iul;FJkxL^Fi0Gk#NFKI!5rqM|>C%i|Pxor-|^r5K7 zkb9~c{AV1=t3~axd_rrzgB?3}XC~)ecel;SMM#gW2f59x;h`|B_9n+ zj=0XO!yKIssMO$0@!7|8s_NpFb#-g7H?|}Ph%djBRohg#VQet>-fAf^K`~bDS2Ft) zZ-@&r*#`I)nC#o8_bd%5^lUczfSDExMq$101*_9PiT~X`rL^1kTr>A(ULhXlfkyM6a6R9BQ&>JUD zh|NnaNhsS_CsxxGLGO0GChzYgub$TI_yRu4DLAmyFsYasuyDFWt)W7O4Itnaa z6UaIFYK;XORuE9|!ZnyT!FyIjR;U!G5-XR)QC5csBx}EmHIMOWbPkMp|35h z4_qvXTTda8o;?6r+C~(5t9Avn_Cdj-5Fb-eR}w6DLcBN2V0*K_=tm~x=Nk4LAdyP= zsU?V)z4Mv77;U`7MAS3CD5Xk*dQDRoJeWB&{utGyczJX!XS6i(&n2)bqF>!$b*+dx zK*EJ~`VCus1GQxR@|w}l9VY``4Y?TDT!m`Jyaz*hAQW5?HHnSQhxi*ClOZ*GXZ1!Y zr(uxmjVhk(L=6#HW>7Nc0=N|$awxMv+Tp6$Mv(VWT}C57Jj=b74tckwH6yY7M<0`$ zBy@rsQiKlJiYN~p%8`edM9914ZBWCfWsSYfYlIV~S}Kg|1qE6}^f)%Mr6C-cHq2*h zq2hN+$0$@~S_m&~nBaiE`}QQV)0433k-BJtBO@BmIM)WIkF(;*DhAD{QaT{NFG=(4 z=03!su|D(1ne^f{iMmiHqpLbfd99JaSyXy?%?3xP;bj}q zb1<00a>Od2HAGSYp0qGwdw|jyr<5bi1q%m|4dz%$spjVzYn9Pwq#nVskrcK3qOU}z z(k6n78(gy%4glIl3p`VO0Nb0HfYa+izhqfG#CUzh1uHuRA#k|6$I1xpG6-%?+a2mi z(Rcvk5s(vDcmw*?qC?$Bv?EUZ;RC+ppIdZZKK$#>Pp1FeqN8YHW9MulVQc@{`H#VM z0ecHZ`hPU)sIIGFt026slSIQW%)&?8X=&A`fj|&7iho4|wjlPU>{;;BOWOkuGhka^ zum8F6)=jSGeU*^8E1|`6U3~DcrfhU7~uUJ|3ykyVyvXO_*2}LD~%3=L?vN%?APejwVShrAEw+dg_s(QS;Pe z(-}HCYZk5WabYUd%Y!x9;6EN!^sSq+!dC9k=%t{@dzo3p z{sc>GRUz9*t;h8f#gczcw{VIPx4b>#8WY&nZnQ5_pYV#!3O zR^&=eh=Zq`{nUH`v0UIH-9hj^Q%@;y!+b^tcvKwmif_!9PFv9B;k)HnMt}eu&)!EC z#AMkvJQ7-s-Cz@3=KW+eu4>Nw%9@HBtBSO`4uYE18oe%VqIf5We0OXYe8G3H!kc)4 zIUl!d;q`fSB8;KjHr>=SS##N-p|YfEJijRpXdduQ)h_G%N1xVCwrft60n*mTTmQF} zL$M54V4-|ca};iDPpGQ^6#Nas<#X8|TSO8Cru?)U1q=aW@+WCu3dmiOB3}YoYw)9R zxr3*IQQy|&Pq)m%hl0ooKN1UFb@HS7paG%TP_Jl!WD(Crt{HVb?00<;{eeA07pI4? z_5sMINIZjiOdSle59cV%xxvUd4#hjVJTD~XjN>@UA-sDkg#H^tl5OgzNaPJ!?V`5t zAR-VB_!UfUf_T8pm2(*VaIgIACt0}04}hAU(D{e0i;y+^irifa6s(QX9T?aQmGpvb zZPWvcW_b}c)b*AjUZQKWXOfsQ6hTrV21yef9gv!&Et;54Y5ru0zJr!eyhIOX^e0GH z4D_iP_F?pLmqQRPAzh$!Di@d=8+-~KCFrGWfl=$nGQ=92=>_uXWe_&!@5=^-E)-sf zZ7(%iDW+OHtk(E`#o*Xo|W@FG2jA@zWacxt@dqs-ZTDu z{XOZw-WVEY_r5vt;a|4Qw{|QFzf0KvJb$x{kPLb1Mm0)!LG|-V_K!3xBHxsVpNoHGr0~6 zVKapX2tPtHkQXEmZgEc|#G!%azalL82YcU!26mD4P7Kz~&jGbYB$0{h44u5ZH*|tS zJUt>0!M!(kcKl0&=U>t0eaHPj?5X^>Xp{Wu?H>n8hN6NT3IoEcM|snl0~8Us$0y0& zK3qcEa9#vv(PcMChdf9#x9*~ijlIhJ#maTs(&Bz?{bllg2ivrsrUESi`u}s1P>_#1rG)f;Vb$m%Qj> z5-`*HhA%(DDk9`K`RH_;At%Yb033@a8giPXH$evFebQvNoHA{7oH&!CYD8}PFyo1r z^D}j<2AYln;;IGNOXax5?zel6FzolSoA&8fnwrc4TRM)|A;#I+@H}7F(eM0vWa!!kBG^&PFOSmb zHY6ee-^2N+>fP(Ll4B}kVXe0nkIQP&GrG{8nx!{;sj&$0Ag%S96o8?-pxybnbK`ZY zDzR;L7jNv|O^@_(Szb-{i-qDvYHY60Df0B&YutgaBG6gJYit;SsUWI@(5f`~#G6C4 z_!0E+piwsl4Fg@Pl?|}RP%F1=QBQPDxV(_Z%dgs}hG5YYgw*Y{f0LpoJGi6doe&`Z zVw3dO;bisyOp1R$ocwR!{#r3EX<}{dZfIg)E2H=iKcxy~898JH6rL&?TurfJ0)kJH zDD?$_7QTUn=D7&5sgx2Ttd+V&Q`f`Jri@J4&oJnf+K=$gqz>EgmtvR~X_0aAWQ8(I z?NOc=90%)B?VDeGUV-KNRPi*pM}?H65J?-#OAE>kWcpgc{0ZA=Y%sZv#L=aKVN!65 zh%w%$CDE$nJZ!dcFB?S?OA3t~Pog~)*NKhD*kgJ7MxB_VBVYVH+wk7_5sFqDg!2wG z=>knM8bF)$8dx!IgKVJfhITsXAY7*RT!x&Chc4x`7q5VKoyWE+tw?5R;7JwDlFz?w zZHeFZYc(0T=8t?FGFqY?w0t9xHL+Kv8~9#1v`%dCQ#ANN#b?mgnPAod4+z6FE)?Ut z7h-=@JHcewgptoEY}l%fQI|LMcy{JEA7P|b0z#Tn0bX5pMa9P zVT&p9XhpZk>x<&6ydrO#fG!UZxL4srx{sZanR3V`1cYzkFl#@yIGWgyIYo{W%qK-# zmf@o)?b}1&sf9c|> zXgrqp`?x^H-;N8A{^w6X*1$!@*~0ptZgJ|5uGng*ubDLNdn0b4XPh-@Ch7f4i8d6Z zK|4!J?6R|^4X8#`w6s!gF@x76v*F2@C6Fc*@del>n@LEGC=hcJJ+g*yFTVJWvX9^o z@a*Xy)BE_kw>xLdL%DSoecUf5(mcC7yPnRyUGtmXJnyGI;Pqr-F?=wBC!#me+$Zr8 z^dlvP-~tgFcgHk9G3NHCBGiW6jw9rz`oTv^{8bLRU<%^1E5Y5=sQHkS;ggf$ojc~# zE;Lf-CnG*<1}_QgFB&j(!)}Ol5^f>{-Xeo#52fKrS1i7_pAF`1``IpKAU9)YptX9N zOm0ITxsW=?rg07eZz3~mjFz>(hbqBHj?*(+arW70)nRIuZD#E>l5KmED0hs>XC-@t z=h%ElqPsK?!ah3mGddD|e_^r0Y#od}Ni?n#gMgB59uy^U~N^DD0(xu&EK1L0&LDH53) zdx^A}9TTRj<606G0prt0b}@eY_EZ%dB2q9)+EIKLIT0t`EM!d3RarCF`^h>zqV z4H!sq2mN@0?-wyyY8yeo)j8+&gMl4DHe^5b&zeX9tJrbI$ZsRJi4jNq3aqOgFF5D^ zC0BbQA+{iIA?)fB#COE3lB6L$Pbrk1nfB1apUXXL4}aBr3uKnHCT$bU!n1PqD=TRk zDJI@ZJoCWIWg^9{lM+1;$?Z;|)517*iK9J+DBEM~nCOAqHKUwW-CFhx=l3$nX85TC zSg|QfhP>=mPTZk*gJih-+>N%Q-tGOx88_ZIL~!v>OmAX1-op#~wJ>6?rXsmO>*|Gu z=kLDobBDDOxv;yfIb6ujg!f^ZaO_mAIu$Jg8Iq|vSH^>Y1gKP#HU%>V$r;e5(#d0@ zh%`woL;;B&&#Pj;djsVj4f&7kiiB8W#`Vlo(i%rziCz_)quvcO>{7JHtpcF#=s83AL@c5{ z7n<)jA=!Rt(EVtpfJbOVH=b zw{Oqio!Kiid{U6)*eG8<&rPk+dPW#p$!a8fG_6{$?a>aFPmvS7qHsQYAU>v5GRpU# z9&Mh=p9S+zMXHEpdCYf`s#4!zHl;OhYG?&yb*fXsjPZBy!ad%7Lz}HQhlBxe1@e9m z(*~&Tz)!zeHh3#)_JV;H<(sc4G<}Pt$xP7;$p}+|E-gos<#Q)QlbdFk|CSf*gBN&o zug25rknR)6(4LlgY!9r1QR3kP-%%thVucxzt22`pcgrl1`kDEn1r!ah-M`gi%^r2o z+?s%JizOVeq=C}+!~oP>CgJsUgUt}Ch`ajWXfY`|6=qZPWqgY#eTiS+;$u(2XXbhd zD~Q0w>1MDLRU6O#?2w$$AQtATy#0#HEt~ocYOZOCE;wH{^LB|K+n7by;pcVAU^1Ry zzrF7I1B3>=shlG$7ta#~prmy*JHYcI4MyUJk{Zvi#3aOqM9|$_=4E}BGWDndu?KvasapFIcl6dgDUAY46hy9VsN{yk zw5QKxGD5vCj%$Giito;w60zSGAPOXH*yCcHbGEVc0A6rW`?v+X(kLE@otl|2Ex-ZSXAD*(q#xd$mNkxE9UV)9 z&DbS7T_$&qH$1BK`b?MBYkVvo+E#bEDJltlYL+_og3%UW}N8IRAKQ{95Ej!!e5u!ZwWp!EPp*5jU>j=F5H7n(6PoORv5vFR_Tn4rV8KOF-v>l!(0+mhF19OX5cOR!E(8y@LqGEBn)mNAE+O-4H z1y=_9@SS-c#WZi|!wu*ovr;JJh zNl5s$^dDvDO(}b|QfkH#5v5^6Un?5*!ZpAWku>!y;ky8=N@y%et)J(19-~jYMpW@y z=EjQW+ND0^gqt-=Sy*Fl8<@G6PCaHYy)iL8Jxw)Td?0hs8}!kZ%>`E@(N36Az}Akl z=kdj6+-as5Y!YT7Orc2 zo9R=*#CqSiTpaCfa#eAWPO(U(YR#Jh7a2oNJ-`AOUbJ9dES(~kNrNWawRT{5ZVLPG z!CYcIqwaDC22!ijds}eEm*j;0#KqiKZftf0+lpSL2#@0+v z6#QjK7RRG_DHa`|WF<+GtDiGfwNN~w-WLG)k&dlxg58%bCu$X!eR{?aom&?s1b3f- z<@QIHt}*(-=9Pv}OT`}U=jV@M%s!1G*Mk7ZkiJAQoo;efYb)cq-~AoV#Om^!T`OEh z*$U)%`3*XzkgMp6bc|Vgcdc-U0VsI1>U0<{=nIBCeV8Z6>l;+SJ&us={kK_4*K2QO z?Om|>av{_qDp}Kd1`IIG#0Y9tO`LI;r@FI29Sg{!)yiO6u54(F9I%do00INK)Bqk- zqEiXiV^VrTyaqAzJ-`Hdp6-N=-#%v-LE@_5&^6g%gViZ|YZHQ=tW=u#^KgW>z_pS% zr{tLe5fB-tf-n-;fOa1H2|EyWp1pl|C*0i9@D>R|XGSj0k#3(wxG<)4Ohk1oh~A`k zIe2v65v=A)9ayq*umnmWT|mijqu-g8UkAxj8EZAp1mqINV48k#AnjYPF7l}i_7Y~S zU^iK|Nc9sUHOr4Uq$lYepTOB}X$8WsO(X|gpWHUPW#(Hb(hHEF&PgUkv}tevUg0s#i>QP43?gln+fmuRg?kkR>Kg<>Qa$lWseuO>d$`fy_77_>oWq@|tF z!LO{h|Za$AyrH73f4u8{>6c%O~$xj{of zZ6Huo65q8n%&9@jg@p9&C09R3tKC~$KRwg&Vuix%4kkL!feehG9~jR{@p?182IoHMy_RuIS_o_TV(NrQ`Hk>E*$f4WDQK&R zn|80QUQJ#G-3b*U%BY;j>fU=F7|*Utr#JifX)m@wslBvOlAN2QW}R<}2$?^0uUcRD zKWllDuO?tm1?XNLSyLcS*-JF9E~a0)9gYONcFiR77w15IgAg+ZQ4w-VlS2qzfnA3< zV25KuQ^sBN4YkK&>->Ea(H0EFdGekSq5aDiEv3KsLsI_doJq;tz|rK7I|xa^Kk7yi z(Z4Eo@4~lyd~EsIma{1jJVvIUz%9+Knt(_IK4HC%)*4estIqkM<~HB$^5a8J|6=JO zN)P{a?3-s-`7I_v%VL6WFh9J16iAl@O z1a}P>!uib4Bq$1c<_TGR!ej3)iw2A6SMubR!)6+(WGk1Ci0Brh{x#FHQiUb-=tVqW zVv~X~?*)Mfj2~RD!sDP(!rVJ}Dqz=SaSoq6*X=^P%~7L58U{hjq$dB^A$HYkS3C|i8qgx=B*G-P4BhxQRZItO3V{JJQgCvF>7`t!GgMsR2>GW~n` z=K5c1dVf8J{$^-f*}&-!2aSI@g95lt(9qP^JL??^-U~i` zep2$-FlSCAUGCp|x}HdLzgU0w(zw`os!T6>y`NV2P_(=MVY0UZY6F)a)waSH{vsyo zp3+V4>+aM3?nlS7#t*4iJs)@jR`*wN9k=_guMk1;D@1U4(YlAOAVFQhroXo350meY z7wxSUq2gTc*7780qL}TNsh<^$%;+G(pruLb$nN3 zXc6||@cVQ7y1|&ZD3`Pn*R;(ZwQb1}ThMGOi}kE^{cNTSx-hp9RoM5>l{#`q%O$Vl z)tHN1`?Ee$`U=aoW}#``XQC_B3((Agg=_P*VH>WdNWw~sHu-lO+jF+-YcXck?|I&0 zY#TEmdUG;=am8r5D{*)K8-IW(y@S#s^{A`NM^iA)1UFyc3lmc^QiQ|VxXJd3PolOUP zy2Ka*T<*4USyt4uyk^$?dvgok2@_P2k|qO2Uq|e+h?vQv!ug&XL z^10q4ELY8J1{A9lPV2G=XTi-WzY1~~G2!DM*on55s)Kt+0h-?=RmW3`4okm50}^XF z(e(YMJdKCbeBE3nuq&0x(AUL@Vq0|zEz6}Fw?blTQgWu(pxRn&QI0Qj!-`zo;7X1F z07zXZ(1D2@bwrWtcygO;`HB)f-tvQTubJ{r>Jr$Qam!JaUY?55ub#{t?yNKbepE2{ zT%Js4%AfCk)tjB3Rd`JcXcJ4%Ojd>ae9>JJtk{fg-;)NfB}-&P*eb~eP;Dtt(`rNp zSkuX?ci3-)N>3DOK60seeUvMCAnPpHvOs+|2vO$tH&bbcd{TzlAu=7i(j72i>-u)F z;|Z0ibmjg0^<}VrnTCOK}^Cb%D* zMhSan0>_`In4<-Ed~r=*vZ+46)B?(GH@zZGcrwNUPyX$-&fnkvd*Qd}!1CH-N)raS8FDZzQWnlHb%iEaTKnD{YP|Tic!Dc)m>38L8>dRI580A@0BfhtPzaTflvtyf z;Ov>03MKrtcpc$_*MQDR%F6j$>MJbespdz8OQa~PSQx3PTAei7!zh~z(ivpC7o7tg ztdn&G6s*8AbtwEvzwVRm0Yhhf;!Q3fWdp)4cg)KU0T;^jP0OjFhl#?0%&OQI2sMw# z24p;+k2ifr2-^GJ!Ttruf!lrVa- zH=@9Ox8e7=waf*m-2Q{|E=oIMl46|xP%(%6-~)|Zk#D~Kk$uM zWVSAmrYOJf2CJY=#@7{W+d6~B#BOaFN#CJI5w%N_@3jKlka9VYa>yG@0Y9lVm+Uh` z52_P!jlnaUa)KdEI9w%?@zNgrkTaGd*a_7f?EKF6GNv<_@Uop^?>q+e`?CAdG5fRy zx4RZu8>b9T87$+&KaZGW477i4>>z#yUHt)q zwQr29p+R8A0k+qhqo&KYf)5yTgBRHx)Ivu2vPcEmE3D4`;h86z7VI6ODb6*kJWDBF8~` zPoFv7fW=UCZqYnbHf>F$nG}C zAKndO1nY?j77Q{E`NNh%8Vr#eLnf9e8n=*YP$6q3CVDblQabzLh}TKg&6YbeBkSqI zZM!2_G1<1WcUMeD-K|Tp)5NDn6-();DP?`sityQ1gUL2GP6Nok0N6R!=2Mzpc`qW7|H1=$)Ejb zCYe=Un%O%)$n!o-xEbx%vIG626OnRIUrDu$VNQq(AGRa>P=ep(!`NH<*Hq} zpCd6{%h%Zaay0Y7*erQr5uioK>^U}WT4MW052ZX=ipgnb^ot2ey&^{s` zBl!csn0ss6m+5k3wtj#53O-|6>OQarsq2aeY#s~TXDW0!S4;5He$h2IB<>*d41z5_ z(@InVVVItk9A26nV%(cIk)cUAx*r>GO51lFGW5hP`OLloy}yLSfX-l`$uuc%%F5*W zd6g+Rd|a1En{QyQoyg&=oSQRxw^gL%BL~f#U594&?zzUCQmRC7X)t*UbNoc0CH*IZ z4ZOb5lA^hvjF7=zQ&LsA3`w4E!zt{X%5#{B-%nv~P+{m8^Dj3sMUdB6bOGZLEx%7V2)TGd|YCnAf=ws91GXW z0hAs9u?K@m1VNjljqrS)ES|d$cch4-{#4TFH(&lBa=qV< zj-o`Uk#!yU*8M`THU+ad^3klBlh%mFsy@?KD#ZmraQ*KhWFkt=r7%BR-V&Cbb9ROrYMTnuKz ztde(#0lMS*3xOxL?Yo2N=!n9P53qFKYqkbucxw-PXsF#Cuw5T^E3wD$>UVU`W$uWA z*uRjZhjUnIPh(&arQ)(8d1yDtP+6BJ!#FOzUkj2{nAT|~_F&?+j&7y37FCu=YO=&i z_BT2cb9Bpd8DeQ@v7EZIWDuLN%!Jk))$He(JJa@VsY5X-!ygCP>JhQ^Omq7)8t2@*X3GLS2(HKFvgH0+%%S*172H zY2?XlQIZmmH$@5gZ4ihuFL02by?znfUyLS*jKniN46@cx_||;+x+d6xrmx6Cc6K20 zfzsWz3^V{{A|WPD29Z+e$-s?%Za}7Q0mf5#Z1CXW2h)nMnd%}YnXWDNimTbux%YMs zwT`@Lv)iF}Nx8d`VqG4Ejmm1xs(;TYuV658Ul1F}9sGDH{;~FJ19@luN?NWFgb?E@Fv|@uc+y z75Nrc@fH)ZDUgztg_0Y^>f~dxNKsfTP1885Q(PvMw&f}J6hd^$>^@SS!IXF%jYL$4 zPR4DK@o@JsXDSA%GL`{L4TD{{byUg&eq52xJ$^WBD46@!JQdO@5}Yw7JMtHgWVq}| zG8(3@;=#qm^9PL$S&I#yE59&Iw`LOPBwUuJ&OP9$*>!RBiuH@Q)+^g^`309ybE9*@ z4G;+z?X@u?n5tq<9vulkJuhjKaB~T9+BOm8EhhFcDf}!~mInPPi|i{-YkI;uw1PyP zAT`oS_8QHEs_=f1l?Q9VmVUZ6_wkfRB5Ivg%cSM zCf=1tlv6Q-pPW381F#T+xhE>%4SVf~5dnv10_3gNaOz)suf9fczWF^<;e_ePZMQEx zlHixu=bqaT}$tmrUynR-gy(rdob`fjb(%cd{-Qe=)LNW8t8s?;T_B#3Pv_oO7+`d&gSKxC$ zC6;KIZF4M;?_Q7~9p>m93Ib4k!ilwGbCVk3nkDJU#J@>A9KN2BU)wl8ofB*NClf!p zYa$k;VBfY;9O@Be+sy6UlBSLTlGtyNUILL`Km}F=Hikef$YbzG6mIe_X2at)WHRKY z+eKW5Xb;dLWssG=-0v!mJ_g*c+V8?0n@7Np0ZR~csrCa;j0T)Bx>phhs0AF_!X#X` z=(yzsB>3FWa(dI$@#Ulxu^=;)g09eNGEwhDZUuy#X6sQ!B9Cdg;p6w-{ElRbQ%VEA zzdPcN!Tk9+`D;z@Z@MD?xrp&ke$pfif1Dx-aW-~H0`H;BSRZcsT&!Uknw2;In9!KO z4t4<{p)e9swG6#XINdlfwg>E5Ovguj-h@sI=A4>$16jA&#Sm?3%h46U6ywV7qs(+LkLjqsshz^P^# z26C^|da&M$#x{GXB=p6=TqG8Z@z1Bm<2$xb3XGD@OPUEsrxvWVU5O>ASy)i&u4HuX zYPwWWv^y1H3!wx>atrYh?)$rkSk$C)ImtdE#J zA|SOdz$}^m+z_2wmr#0k=zTO$(PDXzR4^?4JC0bI1M}Jq2jnS@gJTkeURGWtYVS+v zD-fRS4>33Cr+Q@v%{GYG7m&0$a2q7doCAa>&0Dr#S1OG!NMHHhqaD(BivEgr-ed0f z=dVKu*zfQD+D2+%Yis9h@XI!j?*D{IfBoU7zyG0tv$Lb3$v;}H0_A0-`uGvNS62oX zO7C`e1m^IdbYU`iK+|&NEhNg&H z-P{0L+nMW&2{wW!2RgOYqnp)9c1hZq|-zj9-J)R&^q(9p4J4h-i?OQayhY%1Bloz_vA)9h#c){$#Ja z&Jg$LM~&<`@0J|9YS%b2EaM4f2EXgeZ-pcb^rbh%6^l2BxbiMv)-eyM>r>tM-j9pq zEFnneCd%E%SVJm%1&a0#T|NHnLDgVSmQ$}MuvDC?mScxClXekvBY51T&Rp50Az!E9 z$o`;z?+;e26a zha^(O04f|<(49|2Gx`cj8vTgk0+BdELJc)yGoq8W##O}oi=Q4o@Io<22`H)?7PvEU z-F%;Z`uX|*Vjsl>KZGaCoet$pUP!5kRhkas;j0R5A%m#o&Xz<)VUMS_QVQBpqA26^ z4`Nf;^Sl!~$>aN2mmeR!MFW8ig(pID=|R77N)MuvCRK`Bd&e96XKTlV%9b3SP~F`j z`K{be;+TPOYGk|P69d`vPU8*|wlh-c%v@i7Go1c+Vkg^S@dgu5-W5zj%={_|yclK50#QoVvDb5IWZcd!u*eNw9b4 z_!EP7{5}{36EG7|-#NYbx4iy$cJY5!9_!DJ2G&l0qV~wIi(7BlUI^MP$18npB#CzR z?bFa<1R45hpbj_7ASjDjXE{?3@dp-opRf2Pe&;Kpj6#s?Xj*bTIf5Q5I^jfv-mtH^n`Jwxp$@LU|QdXQj900K5jwU)ck)#Q} zrGkI?$|sqQT8{@z`-hAqM@==kerRIQJ*2!gzAp)t z-e1sYDL_{$9~TZ>5s{#ZsRN-6gF7^ppZR!=e)&X3gPu<#a{)40#=-DgM+PNLNu};z zb0Vb+XIa>ohi06-@S)OGT}c?TVoY{SSny2uen>4yIRY|!_Xv-n>qP}{F3H2)i+YUM z_f`_-0Bgl1dhufgBWIfod`U=$w#K<5>55eLswM3ldUF2|Y-{iT5p2_6f(_nA>n)No z-IM9`^iRQlSrT?bd53xCZ=wEox9NW(wvw}>g{|4Y#k%4**8IQEM%3SDWA6Le==_(n zaY5*WaI4qrw4LoH=XQh3u;4*7<9wD5tvm_;`PmiEProV?SryAgX^Ck)+N%S$6)R z7NLVyzF*;sK6iKeFRTIo_gD*?nEnw}2P%w7eiT3`pX{42cK?JYEQ|>0F|xA^$VCi@ z4mc$ehhvmjp*LV`vo`(s_*+CsT~K0ifbj~8QcI5wJvux2c!AcomxShpCi)O_*wx64 zdPgQ~x>yvP_OVTdd=3?DhVBD2^=(e$0X5!xt_hf8p8_2c;T|Qh84};}1z8hadM)Vc zWaS=3rf$10SNMpvZH0?bx9^u~bci6!--wR}Md8tjsCn!C=tyX#yT{vLj&|DYpz<2c-n)_vyeo->U8y|a z9}!6_LNgTZzaw|@zmQw(4|4AwviSTaHyLAAoXO5-mR}J`S~-&=K-X_Xi5bB>;-^Cv zpTIa5Y?gnt6`SIeW1gvxf@#VxB}r3y5B)f2{{N6W9YgH5ko-Nl$^Txn6%Abf4740D z1A;fG0qApgR2X=k8^P61P#rmu6mt(hl*g5FA|!=|X6@?03h>YoGS;KC zqjt88q~9ts-Q)f>?n4pKB*?K*S`owDXF*`tk_}Q5SLg;+V01<{S-K+?GmljHMU0bp zz=APwh3{fODC3R%3zEUq;A3of-f~Cx=Ni#iBTzHyl5&;Uo2?&wvoSKx?b|`eZVyEG zM80D?k)e_Yd+KgV<6@BRU*aadJ=;yq>^UqU7e}<#jzx*r7QHGlZ)r-UXjtA+k6+J^ z7M@4doMnQfwh{In`5BVitFXH^wKsm{54FR5jjwJRfGT|t{IKlT%sNGj^L#&Mb>>LF z2Y&Bq!o=@D#6_si1nHg56MswT{}T88GIKOAv^Mz@)?<=={3twt_iRSZ+gdv$N~9;E zr=d(=5#`-p$9dN>Rd+PrxsW7I^25I!FZ9} zIMB{Rg_Mj~#qV*S0paEzNm0OPT8p-XFCc$bmF7?+;ae!bl~I9;XcBOeP|=lBiCO0} z>GQPlykC96QAW2&2N~fQ)DQJp&5`z<^OrCA#BpWT6Od;P&*^Joz-D)tCDf+I8g436 zVjL}xvv;xD4Mm^4Lxg-MKZyhT2t!UWyM+hgoJjG|gs1*AY0ysXeSJJI)o5>(DRN^h z8iz_>WZks>758ne25`Yzzvx5TG%n)YE!i?{zQ=w3ItQJ(5=(uZ{vW|Rep@yU2T-j3 z_qgxBB#wf1cGiD_nn&(e&w2DUHfZy2X?L~iiKT| zQYt8jfMV+k3#ez1`Wz6_Cww*BVb6&4v!y3fjTwb{6w>(F7`~9g}C~{4iGmcxjgC-I%LDt*s1j84a0}E z=-kr&)ua8pT;I;@Op*GM_tC6m*2?R~B=T#Ih-fVyyDbrQV$OGU+*O2-cWE4ZRqgfJ zKWx-c&t`@8^t!p!^~S_E!-D*tb^dT4XZl8NDmG^Lz$_nvW1rF@ec#=Ns@sne7wIz` zHe!)pGX8AS_U(N0`}W^9u`rhD`ERbQv1et6GkIQg4`03c6Sa6e#e43vvv;|7r#F)ripq}nYQ+w z0kj*9{lS*bLQ#aMg8u2 z&TpUD>uH5iYxcV@iH+MocVMHjYqEdocKzFd^fO=S>%81>?@8~`=Z_Zq-0|zMx(`&t zv)9eO^BzgNh$cpxbbifi-D8qIJe|oViX@tL|M;723N*|-W~^QJ3Z)p+?)2t!)34q0 z|5go{+~cZCIjd>E{P8Hy{s!J@Hm4!e4owK3P=-wVX5hA~>SZ&#o_uRe{2m^xjl^$C zh4ed*wkbPjqrKhsLcP3~FPSm?eU6NMYC22}DsrY++DfPDRIOuNtb4u~^rYS&pC+wW z1)a(E>D_-d`j1I__EbH4r&*H%AI8)vrhEUx>#D;yUF8dpb=UKRj@j8`TEmv7>wTKH zDAl!y#WS?6&m7#{)Nj^~*Hv#_%N-iNc-W&8?x`|f#UbaIe9l2QFYe! z#nmpw={t2chLin8+nK{j=opf@-^{e_`aLSwY|YfO!BwAZ%d%r)>SaANv)f%9ee#OG z-?0KO7yDg(oO)WCbD6eO|KRgmrMYH*WMZ4}*vgSDjjPBCOE0(fwR<(+6jQF&U*DkE zKgV)R*x&ZsVYlEYL-XC`kCpbidoO6t#DG4d?8hBi+o<2=k@bF#==%Bc%O+{w+-X|v z)asc-<0dSv8MROQ+p+ol9pA!hcl3Ff_H46kVOy`~&$;}+H3#Qq-nut1+pXE_b`*}h ztJ&=x+QqKV-oEZr){je5JbS;;{zkFi8u_{?rGNJn#hyg)_SQ#u$ApEMvu!-;81v-p z@bJLyS6a6y=H_sH(Sc3N<~wBGP|@SRhq_t+oSgbPqG`>czh@4|g1yX}QEhUhvirQ} z#+!3|3TT8`SW zxRFEn_FV0zWW6@w($|TrmYf@qsiD2=$pw9eT^)Ant8}=U zWAW}b6F2PCG$`)buKls-S(EZ@>3r~`?)&lcGt%|wQd|?3=jb_2(4S!)DrT;jzkk~& zJ1U*-JZoOjDj&Z%T>3W3=}=^=!n-@49udDlqtElEdgOl{+lSujA2=aohpS75jz#Kx z9cI5^cwm5chdRHE&uc2y{`uTA5bDzZi(s8meBy>`VL5f;or!;0PMzN>zoeWxrj!4I zrw-|iRX*g@g``!!;Kdco^rQvj2vbg7@zTU8Ka2~;cf$(Ly!0+A8msewo~MKMqRQ8) zMzN*Hsl!`8UfN*jrs3}z&99MZ;lPHP!}YQiKa}fQZ{M;zT8`{IdDRr->U=m>=dF%@ zD08Is{e`~0`s~@87^gpHt6ry5)RjjF92BSP(&`)*{BQO-FHIJ_*fDZp(pA1Bl4|D& zZW&~2|I3&XFZ3=)Q)1q*Q7%i$jO|oEeA0q2kDkpoI(%FgXS|xCZ92)){A=Gn86rm~ zF3p!2S1)7I){VdG=6~Dqal6d3-D&NDD#)h9D#*vi36Da2&-MG3#k?@TSTFumH*dG8 z1!q@XvSDi569<0{Se|Fnil9T;&OHBGXJ?Ldi3{_Ug>qEG!hBY~9l92ppH+%w+-&ch z7gdm6%N_*Qe%P+Xi|Vz?e;RGr;JGL8FdyF31Sg$TCn09UXX~nb ze8*;~o%0U6_ozwxb}RZhyM63YviGHnF#*jA7T8!}XWHlPW9ptfUc17})%QEUTv+`4 zxKU?wYwcX@4$Y3b`)`9oyNzY*h#n8`pFc2RaG*UA6+@4Z|f{I&Kveb zIBySIGG^7#8QUKQMAp0LIjMEeAuk#>t#`58_O4Y1+2u3|K?S6(@K1W97aDer?=%%pKi*ZJ%9?Nt_L$z7}F z+K#y#CZtW{aAoYO1$#=mo@{=}+v95uJF2SgKIGD&dXI^9zVtZT?{WWr1Nx@!wSVYv zzZ@aO&ZfIr&F^VdRG*5BnK*vbk0r>jPp!Y)dd)MheBI{yb~dhK>{8bXT=vdyg&nP9 z;zyp!r)zd%QKN0`@=VmG>*J_ft!?-I_T7dTS9rAA{b113u=j=jNtZg{O1=rF?XZq% zebk^k# zjb5`=*ZjsE7ph&)F1&2&$AwNcu3WBatXTjmFw_!tirFYUQq7`OWm>(cTFouN~>N_{7^t$&6;N&8>ej1vuOATJ ze`<;I_l}NPmgVH?{c+*%W2>b}6`%f|{cT;Be+#z{{m|#klw-FmEf`$sbBAxE>@~$3 zWXO;`yJ^d74xv_7{)+!3JaJuiJZ7`6s)&Q&{~z z-4>6vpFYudPp@_-hd4UyT6|~Xe^=^%P4&~~XxkhIX@fB^r2mKGeJNaQKQ@E5af^AU z+ZpJWub_MRvhkh#kDo5TC{OvfPpMix$+P{I`L$ZD^ze?qden3HY>{S}gcu zzdjw*53+8S=S_|EA6B&w-L&V)@?){nJ`{Da>vO&5n|d>cAaKMy_J3=~^IaJ93%k`S_BDvfh|e<^c+Nsrpae z&?L{h^&=;J4BWYLSKVLlzJ1Oq<%x4!EnPa(yf(}enyr4>%NI|(e#;B;*M`Lxrn=Vd z;%VPi!J4V-TGxyjaO~%8kNG?D74){7TswZW%bnNnZl+FMW~_;z zN0lDca4DMY)whrNv_>($_2e7;)R^;lryhp)hb@kn0tJ;?sauLS%1Lw-l5)ymOZPKv-X;^BeY1S51x9_Yy8Anl{_CspMu77#k?sBH0uj*XydET^Y>A74V`G1bg#%EGngNKi4_r;_6RkuZ$ z1WW8KasSAXP0PB}Ea#THCM11gX{J2()lR($D`A=h4;OQOq&X1Zzj;7Rz$J5*SLA+) z{hEQLee}P@UmWi?W%Z5)|6t|)Wx{@4+%eP2+Yu}8{`*eUEz|I|!_4|w`=lP* zveujTWkS%b&4INmw7NTcUFi}&npFq)VddQ~ zTa2-q7g6nQiR!4DVh;2S)tuYa@9j-vHE+9WVH&HXQ>D(U%B#wV|4Wz3bX_P~c#N-1 zDzq&#FE4k0=ZYTQjVu2?Ni9C?Kkc*f+S?}h&*`0)`DYFADe<3rIeU9FcK7qIylS9K7knA2xX*Vkvk3Jdi|>zMrBZ`PGnW7C~8|@@d{M@ z5m+P_Oi49DLMu6+aRt_aD{(#85(Q$kGGND%Vi54Pi->^YXg zcMgLtRjsVAw`IgM-Lzd}LKJj(c$2n9HAuG#q|1)dG5br=hTz>?##`z4eveT};`=;#zCM zVzg0K8{~_7$mXy+ztXEzoxn&9D-Ql@&@9prCo#FTOY6BBFkp}}GVR@(fr&)szFOC^Np zv>1PBX6$A3V#>y`^9jbMZ&z6jy_JR0)ny1SjuaDQhpINJQ8{ccU9ta-BjRd)JC&+3 zgHt71j3WuzraLgHHUF7@B^58cktc?2mt*;4+bf}d;YcTc5sQu7l&6yF1 zewDAOpFRfB0l;vx631VK6O_j37p4nBZ<&uKLTd)0Kf~y2ZF5~zv=+*gTL>YBwlC6R z3+QJ6Voe6I$2cWK_g;}Gb7@`T{Pi(`p(aD&1uMch6>{~YGbGj+!Kw~}F>1UL1}im9 ztTxCfIuXnVmz+2DfqV&>@SG+o!T9ymMWV?g*r1IHHA}ArmWig%ioz?hsMNrp2$nlB z_jxmn1x{8()M^ZYp{;e=o+js2``LVvc(jntx$+g)@ATXRUkLRIgi2RJTVJXM7Oe@?^#U=LKvdf@f>is&-2!g@nYe&MPK3+t*0?9Nx@ z+S(E2TVuu28%qN=rrchxh$u5#KK&{q`(M8c>1m-rWMq=p!~=@-jd`YPj3G*Ia1PQ$ zB5hEcKKbiSt*n?QVzmZtIzgfwc(UTluTbI;%x;Y&uqxH_gG#{8G0}RzKm*o3=CI-# zrU{RP^Bcbt1+u$k$A`tjY}x^l%|lz9P(q{d=%bH}i44$11nJEV$(Ll-zH4sJhZKV` zqq;H-l{>8jVI)t-HOyYnOc&iuV*;gb`K~|<$IC~`=TER#sm?aHO?>N|RRW~QVl_WL zGiPFg5B~x)Jt42A3^S!aDbeOcOFD>8`_#shL(0?5H}{O=9S||EXH=;?8Pus?6j5dF zC7JTVOi`QUXt}D))~yRUe_Q4cwM_>uYaO+ID&eArrH?TLY8f}+3$*)zbvaH#ptZps zn?1kzqXfYbXf-~#hU&hwBv`U~Hwm2cz7iaL(K3bh_Is|tXD2*7l(jzP3EQ8H# z*#>-%KvYSv5VOp!d?5x;uTk+5>QfovS5+9ateKS1Y@-sNdhM%oE|v!$FW^<~4D|-6 zV56mZP6YX^Ib4iiGjRXL=n;)PDSen3gOP%7M*Pgw;78Wkg$5#bu9k4jA5m6S9t`|+$a z^Etrg*ue80D`ExaWC?^Qvp(o~$gw84DghL>fO)a15&~leq^u?eO)MggTuEeX6%q{d z+#t6WFg8a#xYG-E>jZ{s1tvbH_wPSbHI)=?~dN|9Mee2EqzA0GFNgO7&5~w6O`!1WsD{bEoIk zd7xek2Ei^5RsEExGvUDkWwAR^EXtT;qr+u@k@c~mJS(au;%G-jEp0?}bG^n`DKY3n zu-=by(HKNPx|QYTuY!(xpz6=geID2gN&yhx98y=SvB?9jx)sxR6UN%w4^x7XS)2MV zlDesNa*b{STbP6i*M|Wun4k!1=Msf6__|p8Ae}~aPJfFu_Z^Py)p)ztzrgt=hIKNl*J)?zf)<)ri z_5Ao{N_gxf-2??$t)Z(ys|n(JzUE{@HSA7R2?BLygz2?f3C6aZK>0}GcVcS{-+ez| zC!jh)lI$GNVyzOK)!3DM>!5w`^$ajUPsk0`3dt)3`LH-z%EOrC8_B}ndfA_JZX@g1 z+n{tsEn;*Otb^t2*=9v#N`HN<^8jB-&z#DZ*#K^S8y?Afmbkf?E!R!lV|J`Xv=XYV4A1je&1CBYUY7Hha zQX~+2O{aFP|D$KBd1Eegm3XU4mHCAdq-``Xu7<2%io;LAgupv?Fp*3EWs}6O$OV!T zpJ{bxu0NYTxQ6MF&&lnPCd3x~`mT8nu7(4!EyG>kH%egQ2~n78xEN@wP(BPYnH?|l zHMP>9EL1-@U4}PYCq$@{4XKp=g0=wMN;i|N~p|qY26g!gDg6%8kGT2 z_hDY!4wDfdlMP~<^YfvK#FkjH2NHaV;L?Io=J(%9P~706HU&zX-^6D7vESZe-vN3H z17H`r)l;QbFk2e{lS`^?z7@lU^{J+&f|b0Oyd<>}ET`&PoiEh|1)92!fkay)0NB)Y z&_f~iM`D5WW}DLSQ8w)IZv>B1bwzYzJ>W3*(8VaWjwoAbC??)(8x-Gv5vX6mpEF}I z<*>6TMrA!fhXATgPWoM9XP?kPaJB@@v24Dm%ccZp%Vc8u&6nkx-v^?oinRt@vCAgk zJc=;#Xkuh7xh(>DvdoRH2h43q9NGF}3^oSD(&X4;O+IIvmkrO1@~A31q86J2<;Jo` zjKJutxjwMFmp(`kipwwhUH%i22(yrQ`j7TdvQf9W~YIZrX;yk8jyO^x|W$#s+{P)WV7(QYDHwDp)*|a z#n?LU82|MU;}5*qP5AMZlmKj_plIRzG;saadr(m<{HQ)Ktgrc%mFP1=SG$I4u%=Wt zh=F~tds1oV5ERAslRdAhgp_F5rm2cArXyW@>^g+fXk-`L68+rjO2nm(%BS4Y^ZAsq zh@l;G+OFbu1u9XtjiGYtWV!#%-KO0Q@l)t@w#puVEk!9-!Ap{|Tsa)&eZ`yfcsL4B zcTm@3o$^9=ML1ccR^B@!tS<>C^Q{FV@9}2!eN3-}#afnN%~PWKnn@JOd|*s$Vd-L| zB5`4{RZv1s_;57ie*jvIY=rd>_b4SGNnUJO*Clo{$#p}f;P$KfLmfnf~?xjSXx75Z1?4&(*-k94^u82iTA7?J0RZshm`Yxwn z22H?TEBvqZ)x3`qxUCN*<{|uDNTn5zV|dB=_TZno;@DhaW?#i2$oC>^$n{5i#l7SA z-NXu2^$`BWP6ed~C?eRDuDSbX1aNcTleXBJQ>6hLRjne#U*YkJ08Ff4$Xi-b$^N7i zQAv=z(hcU457!3iG_r##1A6bRj^P;Fci0K5z7iRKgC-)14&ZnZRh%W^ZDhBC6l$E# zQHr)%oV!`S~Wvq+u+iCRC4HBWF_TAZk)u)hYvITFF<1N;Qp-b3o@7zsoHSciRnMa>L|z^qQBg8Pl`89I(I902R{kv~34Ad21*IP^q@tEDoWYeL zy?>jCG+L+vwZ{}_{3W18vz%rt zVy&uSO^I<(32n2o>)#fL@SF^bRGTzwa<)iCjrA|;V1b~j3$VJ39Gl^MRPw;oj5zr? zb7mcBMU8!w*f=sc`41!)$4c4a36#J~iD810yk;KaQVujF*Ni)mD3*L_Y_P{7Il#{L zOxPu5Levw%n{p@{n)l>(f)7l_N9JSRXB0%z+?}+RJfRIyhN9?s?_4##8b)i z30Zc4pXMl(uq~PYUQ)y`HmUS7M z(V=MNNAGV4rzej%aV_Fq;ya|38fMR;t$(P9X0j=DV?9C^{cYqdLZO&NXm0cVb)y#Q zYCvnu@K)eS5@>;%Fc$-G0yR<5UYZCEw%*l=U-)VR^#;x^JlR8){5`xz0y7WdJiA-c z`d>v%MpsD~#MsuE)LIzaXE4TsxII?{p@OC%!Z`}3E9pVd#ZCbTQ%yr+GI-134YHcfc{MiC8lT8^$H(ZLcN2}NoI z#iSopKJOG!BEx0z@k5Qc5&1D3{y zJ_0Z8;2G?C;1If56(X~((Qz?R~kjIM7K5vOZp%3az0PlIu6=7 z7(CSh6yq1FYZ(+_q~elIluA>xw(`02(LmdQGA3&Pr!y%c+8O|7;p7ZwagREDw>LPV z(97oU^Rp?^XPl+6K_91=i;Of=Uq4}CZCIBlHmO+)>ytwnz?44!hoB;cDD`W-V*3!(X9s*04lB^t*dIf6!k8|95Amjn$5d z^tswmTjHcBk08jdE8Yye4X7XpkzI&HRaS&#@-m+!GqSjA>I~@7RSlFWYFiaW97UsO z>dH5ZxQvU!)yOpKl(O}pO-_nvQm*8r$vGbt{+h8ULgt7(w#6(bS4Gl^7AEpfsttS! zy;6P##1siJv4!Y0%@wILu|=sk@M+R#`xj6+EvQ-VT<)!iV2<`JBIplFCc?DQi3fe; zvLfTU`P?t+JbKSo6t|Vt?-q(!Hr~mno-LibT>!i#0^Y)Uqhp7^p)M`_S&}&06>b5> z8y|Jz(vQVPG?Ts&l`=El94!S||G}G`EeduPQ=x4eQ^KZhflSBLF~=9fnBEObP=Red zrn?HvFlToW1DKCAiIPGZ`L321D_4SiuR^|TfpABVn6PQ&5y83;JP(U5V-!JD+k4IJ zlBP1M*pM~%Y5kq+CiLQHW;zHd$FifN4>@E=(hf8)jUKSAb!HF{?9)nH{L0mL3 z=won~Rd}Q(T5a>0-$g-%hf*J!oV^2>2tYPv;4*d>!x_(0M{C1PXh?3ebNUq?3a#9BdwSAZvm6!ri+?5g?XX>-?*_=lfMjQ#>v77EKE{fLwd+zE zOQofyZ)=+tyamJ5Ky9v!fiCVVMqu3n+qzPB5N|z@6u0Sb{Md>*)(dP_vQhEBequl) zJN}xkbWsH7o;D(!ceszsbZ~GEZ#FsqgWd^|Nf@2UC@VKA>d38)NIyE-xhf8Js+^z- z7e+|$M=O&y+ck^|d!(9(XwD~2#i3d9j35fh9yEH@XJ0F6}>7i=~2Pr{kn783s>z9zdy z{kVorJLADPHX@FnEFP33B6@a3rQ7Wm=k09j12sY8?p#bW=uUcrD#kfF01)4-By-#iEzJlN>z%y3FmE9kqoXq?nM+$GOUL(W^3MC zGMNpnFPLAAtsHFp5_QXw5#3mKbPhFH&6F9@tlcRZ9V-=fY?j}ouEtjw4Xs`9pWHae zqe=yr3uFYBd0&c#mXr~h3eo%lQ9*6Z7;{Ef{eo0EUz4y0&67I~OdsZ{B?=(s%X zHNfV=ehVv$v4>Iw%@`4@CG$^W52w_r+R`=}I@&fFcH_?|=hM*?4Txl6*&8;=Bhja= zGoQt;$UN39ANV-~s_Lsz>TJS{;P>$qkHG5Ba!_J>?fwdl8h~w@_*rM}jeVpPII{xV ztX3?xqU6j=i+T-NiI7_xR-$1<(&}D{M`slYe?WYHp~$C8le6y*2x~8f)QlNU!lM)o zN9w>PXO(M-fIoj&ZFdd8vtgX9Y3xTcX$rI?DFF!Me6zG68EPP#-kZ<1T9ox!ia>3+ zm9t7-F#T%z=adlnVk4+4BjasvQiPgxV1NYpI;Zh`oYl~WPMK_lB=pR9*1k{Cc;pTu zH$zL@Fhx-D;O^yHb(1a%*I*xC%M7mkrxXt^DgO}1QJPaOWN*;<5rRn;)Y#ahb|;dj z6qu@9UM8vVsyP(V_7tACM}lyAh30YdAA;2i&XxA}brpoHPE9Mkx1hQcuYA!53!(_zAYDd>GM0 zWlb@#)npTym!p$nYNqd<{SQ!Ez(CmOGB#(5&?SMC)rj#2#d<19!?S;uu%3u6)Z60C zNTW#Z6pcq3W0R1DQ%s3lQ=kby7-?e$I|mw&Qs5{|q?OB`CbGuux*kan%!x2NwvJMu z!2cbSJC%{z-Dmb6eH$YF4>6%5!|Ig6DT0-Xnbo`L7jek0IX1_QmC*PPECPa=q5LeC zqMcp{#9X)vChZ#1Lw zd?2hmh#Ac1@+lgOf}G4hnNuf`=2kuK(_uOS{Zj0)yE7x&SusT;lKLHABi=vPq^EvY zS7;=Jfjm+xMUZ5x*7-$l$Ml=khWUe9UgA+8t^6Cn={qrlQrAt0THFO6h zv}chw8kn&xuAic@NS)c_D{>)O657NmNgN|R)R5|pP$FmhwYSGr+}#wU@LTst%= z9UecS^an^}1+>HFLgn02gvj^}lp%7Dw7Nyk>EWywP{Cszc5>qs0Vl^+6^+B8)3Szi z&eqosn+}Zd9lcXDBB{f&W?`NvmDXN8)Gz*^8l>8WY@r)7no_M&G@9hd$7rH-x{`0A zw8nYV+<(sy7))Q7X-j5Y{o1B@ToxBmuALG5+?5Gy=sfZy#Cmo<9N7MU3ePIcB0N#S zhBmD3$NS8=3W-p^wl5=@t{ufgGwtok+H#DIE$AJyw(BTnGK60#*RlB9z+MW?v|{kL zcM{|Kp?hB2L>mW`pDSjKnxBUn!a<%`*OOnTC~uK=UG)vC3;2DMnT1*C4D>AMxR8bML1l!G3cC?tZSn^2EaMb#AZYJ;ORV8wwzTT;?hG^45Y<&GQOH7rp@+6D4fP3WxU*1Y{PLt zyn=xorC>32s-w!D7@a*zS%_sEdmrOkG))$yF{NqFlc8 z_=rW2eRbHhGZPHPE)Y{@`Z?s?9sJ{xsR1`--vBSVh;AQL&6Y^#Ef&LZjny28xW9=* zYMVBV?7Cq9Pr-8|hToT%I7O1QL^?C3<)dQY^N01l@;%zb8%Q}JT^9!J&~h=FsC^_c zJ!3jZEcZdieL~_ZKvFxLe>Z0uU{1pKSsQ(DQ4D4D2BVE~l3I$kt%e@8 zqaL_)P+1j*gL9Xa$x0gRu`-Ykny61~lJm(IP~Q&6@?xmZ$3Ys=+{Gq>wCvzFy9XFF zkq7~DXp+~=@b9@d0`eo?Y#`2kLkw-RB;gr4@wL0r_8!|oI5T)-198clV&c5eByJKX z`?8z(Ue^Q)SIZJb=UZYNR(@&-kiL<4505R9+2l?y?py&|R83)b2fy4A!}0ys=35Cm zUthz5?|G$ynGD4OnH6c%dtz)`ebHxSks2zOebECV&4D^7JK=12B*qbChl*Xt{EVCg zU9`iKuInJ=FYr2c5X+v52Vo;pbD&IIDB1j+q{OENJ@CE&ZR3ejHmi_Z&%}e^N!;L{ zxQOS+O5(Zl*k*HDLGbi2ZgwGG_=Omb=QC!;lRlOun3wI>=hPI!?wJ!@xGV%a6kE_?JFPmv~AL2L?rFV~$M!ei|LhsxC+} zQE`#H{8}huNrEqV_lQtTLT6%0vjzF<9Y}vhWtzmLFRik3t z{(W1E5fsuAnjpA8D;172UY)(V5=KD}qHvQ45XF!-;!%*2%+^Ow6%E7Vtdn(oj_kkE zjOTHGfOG|@jTywVZN-SPm4RCFO_DC|*P&wYOyF&VtXVFd(T6XZe~C+pp~%jAindUQ z%ahMn-g5$CDaf@E!=+b8Wkj=F8BI3F%_hFu5F8Pd+Y-%>okz`SoE8pu8!2vi2JLkx zF`Cg*%?DNFbSZVt)~n(QbZ@KXV=`jtdIX8-TCIck2wSrK(H9TIi~!v-7%zJ?sbi>^ zE+Z0IO;*kUNv8PobN$ak4`^GpX?$rBCPuQj%lC=~N2R7wslCTgyLoU<+j~?d zM~Gn+7?g8IavXmw!#6s!-x%DnS?kXzF^tXB#m!o&eA#(UiL>;i?X9q3R!ezf#nf%4 zUy}$ECucby6p%nKhQEYEwGEd*68EY7=pwZkr7O=f%5x^l%Pn;T%5B|r26i|N~WQ{?husq*nIc- zC}l*m>dono=@Vb~6bLd`bldL_&Tw|w#+Y-A6XUSDSKF)~N`wHj&n3H;EBaJIGXFfL6K*@GB`TU0R(K&i`yf?cXnLDOh!G(Bbs zHluZ$DjteLu;ZsOiDehQk||<+sC{o;O9-V3M#DPz#u?&~*v#|1`(wz#cVua`47FWV zZ^ZO^j0bj^rA(aff!qQunQHA50UfG<3*4`7Q!VH{Ukt>pXI3a3(AW(&`;B6>6x9^u+9Y<*Jn%lGTw>oXv3 zs%;C}&2d$#NVR|IpyH~@ojbW3Qwj7StczVi9XKh5C&r|ihP=G%>c?l9B)j~2>gct= zY!6H}Lpyz18PgmQnk1Q_5zNql@8`T=Jy-$T9JqRLRvAW-HDxEcGvZgZ1n@Q#ys@6T z=z=mR(kptYbsr=SU%7ZW!96WGS5Vge09m5!m z<_`OQ7wk>SW_viN-4!wQzYa!7j`=1;T~r5_4-XFmhtt3z+i9`;hIkBCe>9~beEtHT zmg|NdJe5AXZKLmyTVmRO%^#D3h^og|+;beKJM^#P%`Td|-xrT101vCun~N%P9?3!$ zWUFf0_#4J#^yf+E}YR`7**7*;ElGe?D# zZa=f}g9Z@i19~ zllXVO5m8Fs_~(wCJ>Z#d5qVf|X#GohB&K*O%EogwDmMX7YE#w5Ou=qB7EhgCI0q|m z;OZxG=u+)Hs-Hm?R~YudCjKkZDnps41o8o^h45uXtMA_3D_#qS);57Jc9Ok^CLU2< zL6jvPZZ&aV!IHa1fg3k71AccdgEJ1_gl3ho5QcF!mC+;!?*!b7(Ce4 z5|d}k0Vs$3c>Kb0+N|o1P{?jr^>7peD9mB}GLJcjTa(&ZKHtPaQF6%{ZJwgY;qlXwhlM=U~2l&-5T3=Kcxje1HQR&DQb zpAOd1v$$9nJXK8$CboXwo(u_A*ZiepU*J%5abu#(FWg2UvY%u`7jDIz#CRlY!xN`~ zM32j55nt94Bie}3FQ#iNU2&8k>hW6Dl=>mR(qM zK5glv&DEx(@&TUZBN83c{$!nDq!+YFyXbaIdfm_)zFiIC^<_MZZ@5Gf8w*548uCmRxGm?Rk4 zNS5vEhTVpIePJi;u2*LdF__gxl6UnbU37eLV){H(?7;SrO}?~x5 zz*vm07ngptwpX<562Iz(*4Jr+CJQ!b*z9+N0u_IdO1w+AsW!%dXTs?MqeSxyX?bs* zo@skP=y+6(P4>I4rFgg=uC2`JXyRWqzw=WXf|>JaWj)G%3u7t9`q6N-T63J$C>2G1 z)cjxRmU;u8J;V2-()ZJqD~fSkN6=uW(}H-8{r_?t#s>j#ZZ0Kh{QF_XtHRK(4mQYo z^X@h&9ffm591b$+BSQF$H)uF&=`k?9ADIvq*^-xM2W7}s(Yi226Uyaccc>o7mdIgS zA_>!oNxSHRbhsQhToVm_sS~YMLvyn`%oacJI*>?wIiGARe;pi9jh*fEEznI2$Tt@= zXRI+fhlS`3y6DhwzAUrl?^-bi47bLlP(dO|^e%Q6<1-BN33n{)@_8Ew76W7K^7@}> zF=1uKIJrN`@UmUmg{uMe25;6Ly2OZ~7#?`ZB9SgxYvFi2t5_8ab8EtgCnQ!(TA7Q+ zIzv>nl6|XxZ8%sOE=33Z*$mY`E~TLSA@LV7#Q58q-_B7_V=btUU1*{Xi|yI^?PAW~0;WWF)VgG(goW~Ob<94J|^d?hyc2R~*^I0jXA zf?u=Mxuyvz!j&tRWdEbl@IuszOwasrVc3hEkRq7GTw9Ju+cFp0e81fbc<+J7Cf6k= zr3g>7PebPLZo`Vvl;bsp^|oM`OFtz=VCqEfl2+j5OsX=Q0saEuY~AzY)RcjnSWOza z&7Yh(KZ0F(&ugg}DMRLCT0D=+HB#x8lYUT*n#sk~NbmBF(i&oQfvodUiTbg_w9)i5 zWpV^IYd!xLRGFfXp9#u(*H7Z!2DTjJHcPx6tk7N`i&2z72)o3S6i)#k~m{on!vemHvamJki*}Mu(?M{#T>pTXX4GQ!{0Q zxr6~doRN#qn*SBDP%iS;2ul#Loxa&b;Ojc>(1JA4ny#2mWwUm<#&Q49&OeS^+GM0o z>y;_FnMrb2f1<$_WaQ|$i3<^F&_%EX594hwp_PkvMzJF`Xt3Kjfr?~^SW|5nPU2x& z=#J+@ae^1gbguo3e_B8aC$LS#rkbVpDh<<0Uv5T3ci8Ko&nv+83%nf}_D>v49$nrt ziX<{N%i8w~vf?lUOn~i8IejR3luZ)hkK4`rdVmYULStBmGc)c}=aWZW?PHaVqMs8p z?dq1PmI{e;r98Ik?RznKaAx%ima`L5WxJCD5v~(f5{(#k?%YfseJML8EPg;IZstAh zxAF&sM@P16GHC7YCJ)VGtm>*F_4l6fA>s|{LmsHARA(@D-2Yn)z6q3fup4|F>dy+9 zv4_So{hK@lsm#P7jf(YB2H23+-~|^eRAUJ0iF474Ya6wctg2@cl!A0okztxRS&+=) zwXhI9qWA}90#*(?l1b6cM47n>k1zAq4sn$zAmcca&B>SnZ^8+;#RToF9jS*~Sr$eABSNINQg^V*;A zblrH+zl%3pftr9OC{B4hk#Xa2&GfOmCx&QtbmF+IP;7cAmW_Qwz9k=m=u`8gl?{>p z?7)ik^$||WVIxAzW)x(BM>Ku*(p?O5;M(k24TqmXBG|5uphpqXquLpFRwph%g#QU^ zkCt6T8cL=u(SyfgD0DYV7b`RPtKshy3?*^(?d9u~g3&a)7P7rEMpFh_Y|MF)<)bxx@3cFsrJ+3%9&_?O$g@# z6B@!*yEfU^0s05t?BT`^dH-ezf}A;MmQ$)rARPlP{TU9sSAtQ0azm zF{=XlAG%WIUTJgE>7uq?YZs*DjUaEpZJh@K!#9w8dV{BvVju6Kd z>4p^%5*G^1=pMpf`64BoY4BxqqZoMSH3UKrhQ%Cc~0vJ+f3xJ+0i-ZLkF&gvwe{imF>; zB8?52kZ{~Ti3ig*)kj6Em6}IM8?AbT7o@w)@OTTGXHb7BD;z2HT2cPdB9gA-u*|kL z-;yeLO=YBvvUXcyvp^AXKxOxKJ<2DEYD%&0zjdUbY(SH>4fQaVpVZEnsOHWu{htHggDLh8f+ArQ&6?( zldC2ytSi{%Oczv3t+Qm%`P{(i1ROR8np|CoV;Z=N#z1=;c-pQ$1SdJo?AUzE_kPOm zT-H|YZx7@%c(d9oQB#O4l_ZmDdjx7-4O&gOAEH%HU0^6*s@;b@o~i=gbG+FOshzd{ z9#hR{j!d)k%rX9hQ5qMno8MbKql4kk5RKW0)xiI6 z;5{O`>U;S`$At;{;CiZxXU_{9Jbcln>(eVhh+{Dd%N*<$6CNIi4~;c+lYN}rm&l2` z-}heo2pSv&ZMra;Jk?f+WILU3=NCC1^%JO_Zr!>HEV_)6b*N(Pg?QvpH1V61mzd7V z5s0JH2L^4Nh$C~)OWJNcv_?A(C()xM$VPU2$~kYjT`7UF)9qAj5y>S?NZCVb_I|n` z<74O}w1PHFhmOBryJkZlO)=XJx+CF22sfQ3Odn!lK|Xd$ef}8cjr)Lc|AP#lmAj^o zkg!GWOv1&>fGBHt9MiBtc1VXFs>aTBTl)(Ue6`{FXgpXaGA7!jTw~H8`BtrHy_sKC zo!bn_Tvu!dvCI0}1BIAUg_y&EDPqedNA^@G*K50+;OPT!Sd4#%2r;A_P;^c#&&n}K zcGK2>j?4zxZNr;wp=v)|h#}{LpVAQ%)j|uG8IucFewMRc{hSz)EC4oOOYGSEgPbpd z{ja`Vw@-+b9{|l}UZ-a!3sx~)n0ki5oJ~=Iv#1~qEO(tA{|*{xm)SNINPR>IB$^D=v`Hvu zh$QvT!lei7ppzev7MmZoKPkkq%mvc?u)3T+@iE+e^VtKS9}Z2h6T`*RLi*mCSY!W# z+VY1n#<}njHe6Pnnt}k`No`wD_;gN;AX`p~6Ln(a;=(!6RI_UYy|Z7C=7Q zv$YLMqyY=*U}>%lT9%i8iDtQK5my7m0@ol1Bzu_tBSYQHkXIVyJM4@);FS>2TTgW> zJLzZ}mn)-x2w+{Tygv{AAk(nu=MI3qTIfAAWqm{4D(9d^wa9 z1^Kt!Ie3F7=@^1qwp@y6a=w&psk&y%zaY;ad;KGQzIKMy6ob{Uj$NlyvWT;3lB*^n zP#Z>3Mh-{ip)daqE0P9q-{FVsd6^7dg>Zi80MVKPi15y~OEso$r#}BZFd=rO5~vjt z=IB5$gH!zY(re|O4~TzXP-tf7rL>_!2yXn4%ZBva=S<6jW5B{V%u6j8{|Yb&>2p|+ ze?_L7#2QlNK6?B-fNsaT5(C}^=bHpJY?*XXy^q%5$~R7ABJbO{@TKceguj@@f% zA1kD76FAf@BKcBlUu;9Z=NRxL0I(JA2EByDC3&Gjxo^_1t#|44(rg$jn_0!j2}w(g zRW13ORKPV}D(`K}*6-*X7;YHW!L0Fi?JES5t68)K?ouiXyQJPv4=(*WPBDa-+>qVcD=C{DtwPO z8+l?U36X6rhEr-LuD0f=^qhKIcEe2A;4pBqkal9^5vCMG+C8#E)+ZP;9o=DrgKC

c>_nw|CAVu5b>*Vv%Hcv}IZtEa7Hr1OyQ;2ENiBN1b z7kC$Ozx)6l{#TIJv)V2)M@X630Tbt8Z;w1ZNj0U#c(V(;wrhp7u>jHe#x_KM#|ICQps{tdS6JY~LG2*H>hO5ZRL{mmUkj zEJh{qMZStAG|7LMrtj%sr418qn?Dm$mxf!G6-o8U{DazZ^^>SNL6P9({w5iYfX91? zlWc1A<%Mt*+;v}KI8gg(qdg*msCh(h;NKcchEsh~xdK!P#bY6D%3w9$3Xv_|#oM(a zl`R`|wActNW8Y#K%Puw+z7rCa+Pw!l#Ie#E^1gP6XY1L4RD`5lXreHJ{Lq zhze1?8RK(BiU?6`=RNNBc%t>ojfQ|Hj{k7p9@-oRH)C5|&lM8_TSAH1pN**}pZvQp zz5gLdERAzW{A8QnJi_<*@wM@!3y00#x9tC zx(RVCqc=xBvO{$ZIUnS|zJ`|Ld&4NAFypi9;)6|u^yQW)=uad`6(8BE+-ywv4;*YG z-`o~|fuIi1Y7DdqsXRqZsaVH@Ivr?=Vi&HQ>^QtDyGcva9zMvT!u;fb#gr%G zv~_}o*m65yvLjPYLDnaB*QSS?Hah{HaW=>1Laq(n{_nWSYoqk5`>A<3=(aw_!TQ+m zaPb&;oBndWl57Xuxl(^Q>?Rj-QAb9$!}LP>Ha^U!d~;st+XYxXeuww6RyiUnNy^5A z$aqJr@HvAt`YiiK40fSq`~{7eyj?(oTUVpt2|UPGRR#MU z>`awyP_p36nXJh#lv9co?o;#|!PY#4CpK8L9xlYPC`OP^(&eFcC06e`0E7Aov9U=} zmjoeQp~#drA7-5>lC2v;-3YjCIYv37#|x1p(~#V(DA*l;uUFGmjkojK?kY5!Dx@l@ z{+U#u5bbE`ync!y_jwGtBLwBj4E^O?A&68+lHMfqpQcNLME5~%j4HS+3tSwJP_}V% zM?6DQP->BIW!w!^nCPzMIA6Fg=u8~{+GX~CEtiB(eNvsmY)v} z*iv205+Q=L+9hg>S8thmiyD&VVX0h((M9^@Lc*4z%Wa6p+G9uk3>%QWu2x;HQmhz@rzzquCN&;j4dvlJv|++2!Ubcl@Y` zR$b;2du|EAEP9t_m(}=2(5>Oz5#vF7DCQ{E2wd(8>B~iCS+gZC1&n>2aOyUgq9=*6 zb;l-;grF9tl02)|^w^HKg}~15Jhna<|5Qjlu?{boG{|9!j#{pvXVR5I;AOLrkN*h? zTO5VyNtf$_bUpdD>-!!EO$#yEu{D%wABFTSVo^>LD0zKXT=%mKTrWK&&+b=*{1B4o zC$E;CHaQ<8r&Z;h#zsH^LkigDJfD6FfuuIY`FW>Jip8Fh<@L>qGr)kK8gq2Jh| z(Q^B4NQ#b!Nk3qb22?ts_U%1rA>kC)tj@4V8tYCG)IF zc7lnPc3OXY%X5s*R_wu>^_2MP$p_%CNr?V|`NQ|nV`|dE8`%`}ZH>R9ua>YS7Z$nA ztHzTH^@F9&fu*%!rpdImg;;#I;eJc2uf#-*Te2Z*sPMdK_(QC~_T{#n|1;DRj>58; zAZLt7e#ky3hX({W8D!ZgT;EklQX($p%zfAJ%ynNvT=^k7)J*X`HrO`}5R&KGr$RPFBHH>Z_rQkW zqYS`USD)5V2xN(klDS-37q#3GK(R`y&BD?Ts+wKo^feI?dfaS-Je0X7fC!~2e912l z?Bg8X>_SYhOvpJp+ThE}XNl^i)xSRQsb1G84v#KQ5>j=UQgA9gH0m1OY(2s`P?4&0 zgz3J0A>}NyT3#i}beAr>Kv^H8NZBV034U0VknpeTZoTq?D0M@ijwpE*3sxlTY%plz zgk-za-#?qkUc#I8?2&lpx$TlrqJG>lkB1ox=}xV0cvBEGFUshC3QY!GH*K)ot;rr{ z7ieM>gM7Tf-4yAXdiRCnjdk6mr8@!6;9XWCTyW4`k*=?%r;zCJAr80rW>zO$k*MYH zgpl$j@1t31xN>J(j?7R&Z7zC!n2>4_O^lXryg4HksnVu*h*s>yzk1~vL4^8nhb>0i?x3~m(Hmy#hVRk+Gs`U zShjn|gmI2UkP$C-j9kbMYI|c8sZ#h6jUn zy%dQ;yh11T8FaWgb#|S?+n#Z@ti6?q1_`;}H~3&NlBqmCM)pyp>8cM?Fo2D-s)djN z(9Q=Nc5n7oBum=opBN(x$d`TA77Dxa38;F1MY5RLjqQ0tIow#V(v{>O4`d4mC{k_{ zE)EPmm+K?>!p$77NYqV-TY!XP-L6`ghEFznkRn-QSdfrShqZ%Bm5@3i^`#uCRCfm> HrBwYtY{WkL diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmUtils.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmUtils.java index 0327a9a060..b9b5cd24fd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmUtils.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmUtils.java @@ -20,7 +20,7 @@ public final class RealmUtils { */ public static List toXVals(RealmResults result, String xValuesField) { - List xVals = new ArrayList<>(); + List xVals = new ArrayList(); for (RealmObject object : result) { From aa6bb7418d20db0c1fbab617559bb2f58b19572c Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 1 Apr 2016 13:01:52 +0200 Subject: [PATCH 055/606] Fix #1637 --- .../src/com/github/mikephil/charting/components/Legend.java | 2 +- .../com/github/mikephil/charting/renderer/LegendRenderer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/com/github/mikephil/charting/components/Legend.java index 3a64b3a622..0d1cd4d23d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/Legend.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/Legend.java @@ -108,7 +108,7 @@ public Legend() { mTextSize = Utils.convertDpToPixel(10f); mStackSpace = Utils.convertDpToPixel(3f); this.mXOffset = Utils.convertDpToPixel(5f); - this.mYOffset = Utils.convertDpToPixel(4f); // 2 + this.mYOffset = Utils.convertDpToPixel(3f); // 2 } /** diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java index 45ae8b1939..b6b3a3f3ac 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -243,7 +243,7 @@ public void renderLegend(Canvas c) { if (legendPosition == Legend.LegendPosition.ABOVE_CHART_LEFT || legendPosition == Legend.LegendPosition.ABOVE_CHART_RIGHT || legendPosition == Legend.LegendPosition.ABOVE_CHART_CENTER) { - posY = 0.f; + posY = yoffset; } else { posY = mViewPortHandler.getChartHeight() - yoffset - mLegend.mNeededHeight; } From 56e59d87768dd2cfe5baf8fa95eefd9f562f2750 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Fri, 1 Apr 2016 16:57:16 +0300 Subject: [PATCH 056/606] Support for lines drawn to pie values --- MPChartExample/AndroidManifest.xml | 1 + .../PiePolylineChartActivity.java | 285 ++++++++++++++++++ .../notimportant/MainActivity.java | 48 +-- .../mikephil/charting/data/PieDataSet.java | 108 +++++++ .../realm/implementation/RealmPieDataSet.java | 103 +++++++ .../interfaces/datasets/IPieDataSet.java | 35 +++ .../charting/renderer/PieChartRenderer.java | 170 +++++++++-- 7 files changed, 697 insertions(+), 53 deletions(-) create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index 250757b885..9efb9639b9 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -29,6 +29,7 @@ + diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java new file mode 100644 index 0000000000..ce1e0ed060 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -0,0 +1,285 @@ + +package com.xxmassdeveloper.mpchartexample; + +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.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.Legend.LegendPosition; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.formatter.PercentFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class PiePolylineChartActivity extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + private PieChart mChart; + private SeekBar mSeekBarX, mSeekBarY; + private TextView tvX, tvY; + + private Typeface tf; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_piechart); + + tvX = (TextView) findViewById(R.id.tvXMax); + tvY = (TextView) findViewById(R.id.tvYMax); + + mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + + mSeekBarY.setProgress(10); + + mSeekBarX.setOnSeekBarChangeListener(this); + mSeekBarY.setOnSeekBarChangeListener(this); + + mChart = (PieChart) findViewById(R.id.chart1); + mChart.setUsePercentValues(true); + mChart.setDescription(""); + mChart.setExtraOffsets(5, 10, 5, 5); + + mChart.setDragDecelerationFrictionCoef(0.95f); + + tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + + mChart.setCenterTextTypeface(Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf")); + mChart.setCenterText(generateCenterSpannableText()); + + mChart.setExtraOffsets(0.f, 50.f, 0.f, 50.f); + + mChart.setDrawHoleEnabled(true); + mChart.setHoleColor(Color.WHITE); + + mChart.setTransparentCircleColor(Color.WHITE); + mChart.setTransparentCircleAlpha(110); + + mChart.setHoleRadius(58f); + mChart.setTransparentCircleRadius(61f); + + mChart.setDrawCenterText(true); + + mChart.setRotationAngle(0); + // enable rotation of the chart by touch + mChart.setRotationEnabled(true); + mChart.setHighlightPerTapEnabled(true); + + // mChart.setUnit(" €"); + // mChart.setDrawUnitsInChart(true); + + // add a selection listener + mChart.setOnChartValueSelectedListener(this); + + setData(3, 100); + + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); + // mChart.spin(2000, 0, 360); + + Legend l = mChart.getLegend(); + l.setPosition(LegendPosition.RIGHT_OF_CHART); + 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.setDrawSliceText(!mChart.isDrawSliceTextEnabled()); + 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() + 1)); + tvY.setText("" + (mSeekBarY.getProgress())); + + setData(mSeekBarX.getProgress(), mSeekBarY.getProgress()); + } + + private void setData(int count, float range) { + + float mult = range; + + ArrayList yVals1 = new ArrayList(); + + // IMPORTANT: In a PieChart, no values (Entry) should have the same + // xIndex (even if from different DataSets), since no values can be + // drawn above each other. + for (int i = 0; i < count + 1; i++) { + yVals1.add(new Entry((float) (Math.random() * mult) + mult / 5, i)); + } + + ArrayList xVals = new ArrayList(); + + for (int i = 0; i < count + 1; i++) + xVals.add(mParties[i % mParties.length]); + + PieDataSet dataSet = new PieDataSet(yVals1, "Election Results"); + dataSet.setSliceSpace(3f); + 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); + + + dataSet.setValueLinePart1OffsetPercentage(80.f); + dataSet.setValueLinePart1Length(0.4f); + dataSet.setValueLinePart2Length(0.5f); + // dataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); + dataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); + + PieData data = new PieData(xVals, dataSet); + data.setValueFormatter(new PercentFormatter()); + data.setValueTextSize(11f); + data.setValueTextColor(Color.BLACK); + data.setValueTypeface(tf); + mChart.setData(data); + + // undo all highlights + mChart.highlightValues(null); + + mChart.invalidate(); + } + + private SpannableString generateCenterSpannableText() { + + SpannableString s = new SpannableString("MPAndroidChart\ndeveloped by Philipp Jahoda"); + s.setSpan(new RelativeSizeSpan(1.7f), 0, 14, 0); + s.setSpan(new StyleSpan(Typeface.NORMAL), 14, s.length() - 15, 0); + s.setSpan(new ForegroundColorSpan(Color.GRAY), 14, s.length() - 15, 0); + s.setSpan(new RelativeSizeSpan(.8f), 14, s.length() - 15, 0); + s.setSpan(new StyleSpan(Typeface.ITALIC), s.length() - 14, s.length(), 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), s.length() - 14, s.length(), 0); + return s; + } + + @Override + public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + + if (e == null) + return; + Log.i("VAL SELECTED", + "Value: " + e.getVal() + ", xIndex: " + e.getXIndex() + + ", DataSet index: " + dataSetIndex); + } + + @Override + public void onNothingSelected() { + Log.i("PieChart", "nothing selected"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index bd6139c63a..a3569dcaaf 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -34,6 +34,7 @@ import com.xxmassdeveloper.mpchartexample.MultiLineChartActivity; import com.xxmassdeveloper.mpchartexample.PerformanceLineChart; import com.xxmassdeveloper.mpchartexample.PieChartActivity; +import com.xxmassdeveloper.mpchartexample.PiePolylineChartActivity; import com.xxmassdeveloper.mpchartexample.R; import com.xxmassdeveloper.mpchartexample.RadarChartActivitry; import com.xxmassdeveloper.mpchartexample.RealtimeLineChartActivity; @@ -73,6 +74,7 @@ protected void onCreate(Bundle savedInstanceState) { 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", @@ -173,90 +175,94 @@ public void onItemClick(AdapterView av, View v, int pos, long arg3) { startActivity(i); break; case 6: - i = new Intent(this, ScatterChartActivity.class); + i = new Intent(this, PiePolylineChartActivity.class); startActivity(i); break; case 7: - i = new Intent(this, BubbleChartActivity.class); + i = new Intent(this, ScatterChartActivity.class); startActivity(i); break; case 8: - i = new Intent(this, StackedBarActivity.class); + i = new Intent(this, BubbleChartActivity.class); startActivity(i); break; case 9: - i = new Intent(this, StackedBarActivityNegative.class); + i = new Intent(this, StackedBarActivity.class); startActivity(i); break; case 10: - i = new Intent(this, AnotherBarActivity.class); + i = new Intent(this, StackedBarActivityNegative.class); startActivity(i); break; case 11: - i = new Intent(this, MultiLineChartActivity.class); + i = new Intent(this, AnotherBarActivity.class); startActivity(i); break; case 12: - i = new Intent(this, BarChartActivityMultiDataset.class); + i = new Intent(this, MultiLineChartActivity.class); startActivity(i); break; case 13: - i = new Intent(this, SimpleChartDemo.class); + i = new Intent(this, BarChartActivityMultiDataset.class); startActivity(i); break; case 14: - i = new Intent(this, ListViewBarChartActivity.class); + i = new Intent(this, SimpleChartDemo.class); startActivity(i); break; case 15: - i = new Intent(this, ListViewMultiChartActivity.class); + i = new Intent(this, ListViewBarChartActivity.class); startActivity(i); break; case 16: - i = new Intent(this, InvertedLineChartActivity.class); + i = new Intent(this, ListViewMultiChartActivity.class); startActivity(i); break; case 17: - i = new Intent(this, CandleStickChartActivity.class); + i = new Intent(this, InvertedLineChartActivity.class); startActivity(i); break; case 18: - i = new Intent(this, CubicLineChartActivity.class); + i = new Intent(this, CandleStickChartActivity.class); startActivity(i); break; case 19: - i = new Intent(this, RadarChartActivitry.class); + i = new Intent(this, CubicLineChartActivity.class); startActivity(i); break; case 20: - i = new Intent(this, LineChartActivityColored.class); + i = new Intent(this, RadarChartActivitry.class); startActivity(i); break; case 21: - i = new Intent(this, RealtimeLineChartActivity.class); + i = new Intent(this, LineChartActivityColored.class); startActivity(i); break; case 22: - i = new Intent(this, DynamicalAddingActivity.class); + i = new Intent(this, RealtimeLineChartActivity.class); startActivity(i); break; case 23: - i = new Intent(this, PerformanceLineChart.class); + i = new Intent(this, DynamicalAddingActivity.class); startActivity(i); break; case 24: - i = new Intent(this, BarChartActivitySinus.class); + i = new Intent(this, PerformanceLineChart.class); startActivity(i); break; case 25: - i = new Intent(this, ScrollViewActivity.class); + i = new Intent(this, BarChartActivitySinus.class); startActivity(i); break; case 26: - i = new Intent(this, BarChartPositiveNegative.class); + i = new Intent(this, ScrollViewActivity.class); startActivity(i); break; case 27: + i = new Intent(this, BarChartPositiveNegative.class); + startActivity(i); + break; + case 28: i = new Intent(this, RealmMainActivity.class); startActivity(i); break; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java index 3f4b555da4..d5c311b6af 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java @@ -15,6 +15,15 @@ public class PieDataSet extends DataSet implements IPieDataSet { /** indicates the selection distance of a pie slice */ private float mShift = 18f; + private ValuePosition mXValuePosition = ValuePosition.INSIDE_SLICE; + private ValuePosition mYValuePosition = ValuePosition.INSIDE_SLICE; + private int mValueLineColor = 0xff000000; + private float mValueLineWidth = 1.0f; + private float mValueLinePart1OffsetPercentage = 50.f; + private float mValueLinePart1Length = 0.1f; + private float mValueLinePart2Length = 0.2f; + private boolean mValueLineVariableLength = true; + public PieDataSet(List yVals, String label) { super(yVals, label); // mShift = Utils.convertDpToPixel(12f); @@ -71,4 +80,103 @@ public void setSelectionShift(float shift) { public float getSelectionShift() { return mShift; } + + @Override + public ValuePosition getXValuePosition() + { + return mXValuePosition; + } + + public void setXValuePosition(ValuePosition xValuePosition) + { + this.mXValuePosition = xValuePosition; + } + + @Override + public ValuePosition getYValuePosition() + { + return mYValuePosition; + } + + public void setYValuePosition(ValuePosition yValuePosition) + { + this.mYValuePosition = yValuePosition; + } + + /** When valuePosition is OutsideSlice, indicates line color */ + @Override + public int getValueLineColor() + { + return mValueLineColor; + } + + public void setValueLineColor(int valueLineColor) + { + this.mValueLineColor = valueLineColor; + } + + /** When valuePosition is OutsideSlice, indicates line width */ + @Override + public float getValueLineWidth() + { + return mValueLineWidth; + } + + public void setValueLineWidth(float valueLineWidth) + { + this.mValueLineWidth = valueLineWidth; + } + + /** When valuePosition is OutsideSlice, indicates offset as percentage out of the slice size */ + @Override + public float getValueLinePart1OffsetPercentage() + { + return mValueLinePart1OffsetPercentage; + } + + public void setValueLinePart1OffsetPercentage(float valueLinePart1OffsetPercentage) + { + this.mValueLinePart1OffsetPercentage = valueLinePart1OffsetPercentage; + } + + /** When valuePosition is OutsideSlice, indicates length of first half of the line */ + @Override + public float getValueLinePart1Length() + { + return mValueLinePart1Length; + } + + public void setValueLinePart1Length(float valueLinePart1Length) + { + this.mValueLinePart1Length = valueLinePart1Length; + } + + /** When valuePosition is OutsideSlice, indicates length of second half of the line */ + @Override + public float getValueLinePart2Length() + { + return mValueLinePart2Length; + } + + public void setValueLinePart2Length(float valueLinePart2Length) + { + this.mValueLinePart2Length = valueLinePart2Length; + } + + /** When valuePosition is OutsideSlice, this allows variable line length */ + @Override + public boolean isValueLineVariableLength() + { + return mValueLineVariableLength; + } + + public void setValueLineVariableLength(boolean valueLineVariableLength) + { + this.mValueLineVariableLength = valueLineVariableLength; + } + + public enum ValuePosition { + INSIDE_SLICE, + OUTSIDE_SLICE + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java index c11518bc54..a80d50d2ca 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.data.realm.implementation; import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.data.realm.base.RealmBaseDataSet; import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; import com.github.mikephil.charting.utils.Utils; @@ -24,6 +25,14 @@ public class RealmPieDataSet extends RealmBaseDataSet { * @return */ float getSelectionShift(); + + PieDataSet.ValuePosition getXValuePosition(); + PieDataSet.ValuePosition getYValuePosition(); + + /** + * When valuePosition is OutsideSlice, indicates line color + * */ + int getValueLineColor(); + + /** + * When valuePosition is OutsideSlice, indicates line width + * */ + float getValueLineWidth(); + + /** + * When valuePosition is OutsideSlice, indicates offset as percentage out of the slice size + * */ + float getValueLinePart1OffsetPercentage(); + + /** + * When valuePosition is OutsideSlice, indicates length of first half of the line + * */ + float getValueLinePart1Length(); + + /** + * When valuePosition is OutsideSlice, indicates length of second half of the line + * */ + float getValueLinePart2Length(); + + /** + * When valuePosition is OutsideSlice, this allows variable line length + * */ + boolean isValueLineVariableLength(); + } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java index 227337e517..269b767c87 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -20,9 +20,11 @@ import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -39,6 +41,7 @@ public class PieChartRenderer extends DataRenderer { */ protected Paint mHolePaint; protected Paint mTransparentCirclePaint; + protected Paint mValueLinePaint; /** * paint object for the text that can be displayed in the center of the @@ -79,6 +82,9 @@ public PieChartRenderer(PieChart chart, ChartAnimator animator, mValuePaint.setTextSize(Utils.convertDpToPixel(13f)); mValuePaint.setColor(Color.WHITE); mValuePaint.setTextAlign(Align.CENTER); + + mValueLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mValueLinePaint.setStyle(Style.STROKE); } public Paint getPaintHole() { @@ -351,7 +357,7 @@ public void drawValues(Canvas c) { PointF center = mChart.getCenterCircleBox(); // get whole the radius - float r = mChart.getRadius(); + float radius = mChart.getRadius(); float rotationAngle = mChart.getRotationAngle(); float[] drawAngles = mChart.getDrawAngles(); float[] absoluteAngles = mChart.getAbsoluteAngles(); @@ -359,13 +365,14 @@ public void drawValues(Canvas c) { float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); - float off = r / 10f * 3.6f; + final float holeRadiusPercent = mChart.getHoleRadius() / 100.f; + float labelRadiusOffset = radius / 10f * 3.6f; if (mChart.isDrawHoleEnabled()) { - off = (r - (r / 100f * mChart.getHoleRadius())) / 2f; + labelRadiusOffset = (radius - (radius * holeRadiusPercent)) / 2f; } - r -= off; // offset to keep things inside the chart + final float labelRadius = radius - labelRadiusOffset; PieData data = mChart.getData(); List dataSets = data.getDataSets(); @@ -377,23 +384,34 @@ public void drawValues(Canvas c) { float angle; int xIndex = 0; + c.save(); + for (int i = 0; i < dataSets.size(); i++) { IPieDataSet dataSet = dataSets.get(i); - if (!dataSet.isDrawValuesEnabled() && !drawXVals) + final boolean drawYVals = dataSet.isDrawValuesEnabled(); + + if (!drawYVals && !drawXVals) continue; + final PieDataSet.ValuePosition xValuePosition = dataSet.getXValuePosition(); + final PieDataSet.ValuePosition yValuePosition = dataSet.getYValuePosition(); + // apply the text-styling defined by the DataSet applyValueTextStyle(dataSet); float lineHeight = Utils.calcTextHeight(mValuePaint, "Q") + Utils.convertDpToPixel(4f); + ValueFormatter formatter = dataSet.getValueFormatter(); + int entryCount = dataSet.getEntryCount(); - for (int j = 0, maxEntry = Math.min( - (int) Math.ceil(entryCount * phaseX), entryCount); j < maxEntry; j++) { + mValueLinePaint.setColor(dataSet.getValueLineColor()); + mValueLinePaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getValueLineWidth())); + + for (int j = 0; j < entryCount; j++) { Entry entry = dataSet.getEntryForIndex(j); @@ -404,51 +422,139 @@ public void drawValues(Canvas c) { final float sliceAngle = drawAngles[xIndex]; final float sliceSpace = dataSet.getSliceSpace(); - final float sliceSpaceMiddleAngle = sliceSpace / (Utils.FDEG2RAD * r); + final float sliceSpaceMiddleAngle = sliceSpace / (Utils.FDEG2RAD * labelRadius); // offset needed to center the drawn text in the slice - final float offset = (sliceAngle - sliceSpaceMiddleAngle / 2.f) / 2.f; + final float angleOffset = (sliceAngle - sliceSpaceMiddleAngle / 2.f) / 2.f; - angle = angle + offset; + angle = angle + angleOffset; - // calculate the text position - float x = (float) (r - * Math.cos(Math.toRadians(rotationAngle + angle)) - + center.x); - float y = (float) (r - * Math.sin(Math.toRadians(rotationAngle + angle)) - + center.y); + final float transformedAngle = rotationAngle + angle * phaseY; float value = mChart.isUsePercentValuesEnabled() ? entry.getVal() / yValueSum * 100f : entry.getVal(); - ValueFormatter formatter = dataSet.getValueFormatter(); + final float sliceXBase = (float)Math.cos(transformedAngle * Utils.FDEG2RAD); + final float sliceYBase = (float)Math.sin(transformedAngle * Utils.FDEG2RAD); + + final boolean drawXOutside = drawXVals && + xValuePosition == PieDataSet.ValuePosition.OUTSIDE_SLICE; + final boolean drawYOutside = drawYVals && + yValuePosition == PieDataSet.ValuePosition.OUTSIDE_SLICE; + final boolean drawXInside = drawXVals && + xValuePosition == PieDataSet.ValuePosition.INSIDE_SLICE; + final boolean drawYInside = drawYVals && + yValuePosition == PieDataSet.ValuePosition.INSIDE_SLICE; + + if (drawXOutside || drawYOutside) { + + final float valueLineLength1 = dataSet.getValueLinePart1Length(); + final float valueLineLength2 = dataSet.getValueLinePart2Length(); + final float valueLinePart1OffsetPercentage = dataSet.getValueLinePart1OffsetPercentage() / 100.f; + + float pt2x, pt2y; + float labelPtx, labelPty; + + float line1Radius; + + if (mChart.isDrawHoleEnabled()) + line1Radius = (radius - (radius * holeRadiusPercent)) + * valueLinePart1OffsetPercentage + + (radius * holeRadiusPercent); + else + line1Radius = radius * valueLinePart1OffsetPercentage; + + final float polyline2Width = dataSet.isValueLineVariableLength() + ? labelRadius * valueLineLength2 * (float)Math.abs(Math.sin( + transformedAngle * Utils.FDEG2RAD)) + : labelRadius * valueLineLength2; + + final float pt0x = line1Radius * sliceXBase + center.x; + final float pt0y = line1Radius * sliceYBase + center.y; + + final float pt1x = labelRadius * (1 + valueLineLength1) * sliceXBase + center.x; + final float pt1y = labelRadius * (1 + valueLineLength1) * sliceYBase + center.y; + + if (transformedAngle % 360.0 >= 90.0 && transformedAngle % 360.0 <= 270.0) { + pt2x = pt1x - polyline2Width; + pt2y = pt1y; + mValuePaint.setTextAlign(Align.RIGHT); + labelPtx = pt2x - Utils.convertDpToPixel(5.f); + labelPty = pt2y; + } else { + pt2x = pt1x + polyline2Width; + pt2y = pt1y; + mValuePaint.setTextAlign(Align.LEFT); + labelPtx = pt2x + Utils.convertDpToPixel(5.f); + labelPty = pt2y; + } - boolean drawYVals = dataSet.isDrawValuesEnabled(); + if (dataSet.getValueLineColor() != ColorTemplate.COLOR_NONE) { + c.drawLine(pt0x, pt0y, pt1x, pt1y, mValueLinePaint); + c.drawLine(pt1x, pt1y, pt2x, pt2y, mValueLinePaint); + } - // draw everything, depending on settings - if (drawXVals && drawYVals) { + // draw everything, depending on settings + if (drawXOutside && drawYOutside) { + + drawValue(c, + formatter, + value, + entry, + 0, + labelPtx, + labelPty, + dataSet.getValueTextColor(j)); + + if (j < data.getXValCount()) { + c.drawText(data.getXVals().get(j), labelPtx, labelPty + lineHeight, + mValuePaint); + } - drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j)); + } else if (drawXOutside) { + if (j < data.getXValCount()) { + mValuePaint.setColor(dataSet.getValueTextColor(j)); + c.drawText(data.getXVals().get(j), labelPtx, labelPty + lineHeight / 2.f, mValuePaint); + } + } else if (drawYOutside) { - if (j < data.getXValCount()) { - c.drawText(data.getXVals().get(j), x, y + lineHeight, - mValuePaint); + drawValue(c, formatter, value, entry, 0, labelPtx, labelPty + lineHeight / 2.f, dataSet.getValueTextColor(j)); } + } - } else if (drawXVals) { - if (j < data.getXValCount()) { - mValuePaint.setColor(dataSet.getValueTextColor(j)); - c.drawText(data.getXVals().get(j), x, y + lineHeight / 2f, mValuePaint); - } - } else if (drawYVals) { + if (drawXInside || drawYInside) { + // calculate the text position + float x = labelRadius * sliceXBase + center.x; + float y = labelRadius * sliceYBase + center.y; + + mValuePaint.setTextAlign(Align.CENTER); + + // draw everything, depending on settings + if (drawXInside && drawYInside) { + + drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j)); - drawValue(c, formatter, value, entry, 0, x, y + lineHeight / 2f, dataSet.getValueTextColor(j)); + if (j < data.getXValCount()) { + c.drawText(data.getXVals().get(j), x, y + lineHeight, + mValuePaint); + } + + } else if (drawXInside) { + if (j < data.getXValCount()) { + mValuePaint.setColor(dataSet.getValueTextColor(j)); + c.drawText(data.getXVals().get(j), x, y + lineHeight / 2f, mValuePaint); + } + } else if (drawYInside) { + + drawValue(c, formatter, value, entry, 0, x, y + lineHeight / 2f, dataSet.getValueTextColor(j)); + } } xIndex++; } } + + c.restore(); } @Override From 122ba413ef646055aaf70a627f736d409ffa1d7e Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Fri, 1 Apr 2016 16:57:39 +0300 Subject: [PATCH 057/606] Take into account extra offsets for pie/radar charts --- .../github/mikephil/charting/charts/PieRadarChartBase.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java index a5c3bf130a..a7cb8f73ef 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java @@ -400,6 +400,10 @@ public void setMinOffset(float minOffset) { */ public float getDiameter() { RectF content = mViewPortHandler.getContentRect(); + content.left += getExtraLeftOffset(); + content.top += getExtraTopOffset(); + content.right -= getExtraRightOffset(); + content.bottom -= getExtraBottomOffset(); return Math.min(content.width(), content.height()); } From ae7df476372044e20211712b2f228a50da22227b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 1 Apr 2016 16:28:18 +0200 Subject: [PATCH 058/606] Modify gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 70e3dabbf8..31581642ab 100644 --- a/.gitignore +++ b/.gitignore @@ -11,8 +11,9 @@ # generated files bin/ gen/ -generatedJar/ +generated/ docs/ +finalOutput/ build.xml From d976b79ed0df0a3c6e2e286331a6e8f6b6d8406f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 1 Apr 2016 18:11:37 +0200 Subject: [PATCH 059/606] Change pie value line default values --- .../mpchartexample/PiePolylineChartActivity.java | 4 ++-- .../src/com/github/mikephil/charting/data/PieDataSet.java | 6 +++--- .../charting/data/realm/implementation/RealmPieDataSet.java | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index ce1e0ed060..b475cd4abb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -226,8 +226,8 @@ private void setData(int count, float range) { dataSet.setValueLinePart1OffsetPercentage(80.f); - dataSet.setValueLinePart1Length(0.4f); - dataSet.setValueLinePart2Length(0.5f); + dataSet.setValueLinePart1Length(0.3f); + dataSet.setValueLinePart2Length(0.4f); // dataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); dataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); diff --git a/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java index d5c311b6af..3c2b9ba589 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java @@ -19,9 +19,9 @@ public class PieDataSet extends DataSet implements IPieDataSet { private ValuePosition mYValuePosition = ValuePosition.INSIDE_SLICE; private int mValueLineColor = 0xff000000; private float mValueLineWidth = 1.0f; - private float mValueLinePart1OffsetPercentage = 50.f; - private float mValueLinePart1Length = 0.1f; - private float mValueLinePart2Length = 0.2f; + private float mValueLinePart1OffsetPercentage = 75.f; + private float mValueLinePart1Length = 0.3f; + private float mValueLinePart2Length = 0.4f; private boolean mValueLineVariableLength = true; public PieDataSet(List yVals, String label) { diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java index a80d50d2ca..7be50e7c64 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java @@ -29,9 +29,9 @@ public class RealmPieDataSet extends RealmBaseDataSet Date: Sun, 3 Apr 2016 17:51:21 +0200 Subject: [PATCH 060/606] Brush up example --- .../mpchartexample/LineChartActivityColored.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index 5d1e212d78..b4d5a5ce81 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -35,12 +35,14 @@ protected void onCreate(Bundle savedInstanceState) { mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Bold.ttf"); - LineData data = getData(36, 100); - data.setValueTypeface(mTf); + for (int i = 0; i < mCharts.length; i++) { + + LineData data = getData(36, 100); + data.setValueTypeface(mTf); - for (int i = 0; i < mCharts.length; i++) // add some transparency to the color with "& 0x90FFFFFF" setupChart(mCharts[i], data, mColors[i % mColors.length]); + } } private int[] mColors = new int[] { @@ -52,6 +54,8 @@ protected void onCreate(Bundle savedInstanceState) { private void setupChart(LineChart chart, LineData data, int color) { + ((LineDataSet) data.getDataSetByIndex(0)).setCircleColor(color); + // no description text chart.setDescription(""); chart.setNoDataTextDescription("You need to provide data for the chart."); @@ -85,6 +89,8 @@ private void setupChart(LineChart chart, LineData data, int color) { l.setEnabled(false); chart.getAxisLeft().setEnabled(false); + chart.getAxisLeft().setSpaceTop(40); + chart.getAxisLeft().setSpaceBottom(40); chart.getAxisRight().setEnabled(false); chart.getXAxis().setEnabled(false); @@ -113,9 +119,9 @@ private LineData getData(int count, float range) { // set1.setFillColor(Color.RED); set1.setLineWidth(1.75f); - set1.setCircleRadius(3f); + set1.setCircleRadius(5f); set1.setColor(Color.WHITE); - set1.setCircleColor(Color.WHITE); + set1.setCircleColorHole(Color.WHITE); set1.setHighLightColor(Color.WHITE); set1.setDrawValues(false); From 727b860f11cce4c2fc7ef4ae6b836041db640a6e Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Apr 2016 18:42:44 +0200 Subject: [PATCH 061/606] Add auto calculation of granularity --- .../charting/charts/BarLineChartBase.java | 6 +- .../mikephil/charting/charts/RadarChart.java | 3 +- .../mikephil/charting/components/YAxis.java | 89 ++++++++++++++----- .../mikephil/charting/data/BubbleDataSet.java | 2 +- .../mikephil/charting/data/CandleDataSet.java | 2 +- .../github/mikephil/charting/utils/Utils.java | 82 ++++++++++++----- 6 files changed, 131 insertions(+), 53 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index 3313bd10bf..1f66c92e5f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -15,7 +15,6 @@ import android.view.MotionEvent; 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; import com.github.mikephil.charting.components.YAxis.AxisDependency; @@ -351,8 +350,9 @@ protected void calcMinMax() { mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); // calculate axis range (min / max) according to provided data - mAxisLeft.calcMinMax(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); - mAxisRight.calcMinMax(mData.getYMin(AxisDependency.RIGHT), mData.getYMax(AxisDependency.RIGHT)); + mAxisLeft.calculate(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); + mAxisRight.calculate(mData.getYMin(AxisDependency.RIGHT), mData.getYMax(AxisDependency + .RIGHT)); } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java index 8f6c1cb982..f188a4c10a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java @@ -8,7 +8,6 @@ import android.graphics.RectF; import android.util.AttributeSet; -import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.data.Entry; @@ -105,7 +104,7 @@ protected void calcMinMax() { mXAxis.mAxisMaximum = mData.getXVals().size() - 1; mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); - mYAxis.calcMinMax(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); + mYAxis.calculate(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java index 6e8c0486f4..bbbedda8c4 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java @@ -9,9 +9,12 @@ import com.github.mikephil.charting.utils.Utils; /** - * Class representing the y-axis labels settings and its entries. Only use the setter methods to modify it. Do not - * access public variables directly. Be aware that not all features the YLabels class provides are suitable for the - * RadarChart. Customizations that affect the value range of the axis need to be applied before setting data for the + * Class representing the y-axis labels settings and its entries. Only use the setter methods to + * modify it. Do not + * access public variables directly. Be aware that not all features the YLabels class provides + * are suitable for the + * RadarChart. Customizations that affect the value range of the axis need to be applied before + * setting data for the * chart. * * @author Philipp Jahoda @@ -107,7 +110,7 @@ public enum YAxisLabelPosition { /** * the minimum width that the axis should take (in dp). - * + *

* default: 0.0 */ protected float mMinWidth = 0.f; @@ -132,6 +135,11 @@ public enum YAxisLabelPosition { */ protected float mGranularity = 1.0f; + /** + * flag indicating if a custom-granularity was set - if false, granularity is auto calculated + */ + protected boolean mCustomGranularity = false; + /** * Enum that specifies the axis a DataSet should be plotted against, either LEFT or RIGHT. * @@ -166,6 +174,7 @@ public float getMinWidth() { /** * Sets the minimum width that the axis should take (in dp). + * * @param minWidth */ public void setMinWidth(float minWidth) { @@ -181,6 +190,7 @@ public float getMaxWidth() { /** * Sets the maximum width that the axis can take (in dp). + * * @param maxWidth */ public void setMaxWidth(float maxWidth) { @@ -195,7 +205,10 @@ public boolean isGranularityEnabled() { } /** - * Enabled/disable granularity control on axis value intervals + * Enabled/disable granularity control on axis value intervals. If enabled, the axis + * interval is not allowed to go below a certain granularity (which is either + * auto-calculated, or custom set) + * * @param enabled */ public void setGranularityEnabled(boolean enabled) { @@ -210,11 +223,24 @@ public float getGranularity() { } /** - * Set the minimum interval between axis values. This can be used to avoid label duplicating when zooming in. + * Set a custom minimum interval between axis values (instead of auto-calculation). The axis + * interval can then not go below + * the specified limit. Use resetGranularity() to re-enable auto calculation. + * This can be used to avoid label duplicating when zooming in. + * * @param granularity */ public void setGranularity(float granularity) { mGranularity = granularity; + mCustomGranularity = true; + } + + /** + * Reset the custom-set granularity. If this method is called, granularity + * is again auto calculated. + */ + public void resetGranularity() { + mCustomGranularity = false; } /** @@ -243,7 +269,8 @@ public boolean isDrawTopYLabelEntryEnabled() { } /** - * set this to true to enable drawing the top y-label entry. Disabling this can be helpful when the top y-label and + * set this to true to enable drawing the top y-label entry. Disabling this can be helpful + * when the top y-label and * left x-label interfere with each other. default: true * * @param enabled @@ -253,12 +280,15 @@ public void setDrawTopYLabelEntry(boolean enabled) { } /** - * sets the number of label entries for the y-axis max = 25, min = 2, default: 6, be aware that this number is not + * sets the number of label entries for the y-axis max = 25, min = 2, default: 6, be aware + * 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 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 to have uneven values + * @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 + * to have uneven values */ public void setLabelCount(int count, boolean force) { @@ -290,7 +320,8 @@ public boolean isForceLabelsEnabled() { } /** - * If enabled, the YLabels will only show the minimum and maximum value of the chart. This will ignore/override the + * If enabled, the YLabels will only show the minimum and maximum value of the chart. This + * will ignore/override the * set label count. * * @param enabled @@ -309,7 +340,8 @@ public boolean isShowOnlyMinMaxEnabled() { } /** - * If this is set to true, the y-axis is inverted which means that low values are on top of the chart, high values + * If this is set to true, the y-axis is inverted which means that low values are on top of + * the chart, high values * on bottom. * * @param enabled @@ -440,7 +472,7 @@ public float getRequiredWidthSpace(Paint p) { maxWidth = Utils.convertDpToPixel(maxWidth); width = Math.max(minWidth, Math.min(width, maxWidth > 0.0 ? maxWidth : width)); - + return width; } @@ -474,7 +506,8 @@ public String getLongestLabel() { } /** - * Returns the formatted y-label at the specified index. This will either use the auto-formatter or the custom + * Returns the formatted y-label at the specified index. This will either use the + * auto-formatter or the custom * formatter (if one is set). * * @param index @@ -489,8 +522,10 @@ public String getFormattedLabel(int index) { } /** - * Sets the formatter to be used for formatting the axis labels. If no formatter is set, the chart will - * automatically determine a reasonable formatting (concerning decimals) for all the values that are drawn inside + * Sets the formatter to be used for formatting the axis labels. If no formatter is set, the + * chart will + * automatically determine a reasonable formatting (concerning decimals) for all the values + * that are drawn inside * the chart. Use chart.getDefaultValueFormatter() to use the formatter calculated by the chart. * * @param f @@ -517,7 +552,8 @@ public YAxisValueFormatter getValueFormatter() { } /** - * If this component has no YAxisValueFormatter or is only equipped with the default one (no custom set), return true. + * If this component has no YAxisValueFormatter or is only equipped with the default one (no + * custom set), return true. * * @return */ @@ -536,19 +572,21 @@ public boolean needsDefaultFormatter() { * @return */ public boolean needsOffset() { - if (isEnabled() && isDrawLabelsEnabled() && getLabelPosition() == YAxisLabelPosition.OUTSIDE_CHART) + if (isEnabled() && isDrawLabelsEnabled() && getLabelPosition() == YAxisLabelPosition + .OUTSIDE_CHART) return true; else return false; } /** - * Calculates the minimum, maximum and range values of the YAxis with the given + * Calculates the minimum, maximum, granularity and range values of the YAxis with the given * minimum and maximum values from the chart data. + * * @param dataMin the y-min value according to chart data * @param dataMax the y-max value according to chart data */ - public void calcMinMax(float dataMin, float dataMax) { + public void calculate(float dataMin, float dataMax) { // if custom, use value as is, else use data value float min = mCustomAxisMin ? mAxisMinimum : dataMin; @@ -564,14 +602,14 @@ public void calcMinMax(float dataMin, float dataMax) { } // bottom-space only effects non-custom min - if(!mCustomAxisMin) { + if (!mCustomAxisMin) { float bottomSpace = range / 100f * getSpaceBottom(); this.mAxisMinimum = (min - bottomSpace); } // top-space only effects non-custom max - if(!mCustomAxisMax) { + if (!mCustomAxisMax) { float topSpace = range / 100f * getSpaceTop(); this.mAxisMaximum = (max + topSpace); @@ -579,5 +617,12 @@ public void calcMinMax(float dataMin, float dataMax) { // calc actual range this.mAxisRange = Math.abs(this.mAxisMaximum - this.mAxisMinimum); + + // in case granularity is not customized, auto-calculate it + if (!mCustomGranularity && mGranularityEnabled) { + + double granularity = Utils.granularity(mAxisRange, mLabelCount); + this.mGranularity = (float) granularity; + } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java index 96396d3241..5672ecb0bb 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java @@ -11,7 +11,7 @@ public class BubbleDataSet extends BarLineScatterCandleBubbleDataSet implements IBubbleDataSet { - // NOTE: Do not initialize these, as the calcMinMax is called by the super, + // NOTE: Do not initialize these, as the calculate is called by the super, // and the initializers are called after that and can reset the values protected float mXMax; protected float mXMin; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java index c63ccee1df..939014105c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java @@ -101,7 +101,7 @@ public DataSet copy() { @Override public void calcMinMax(int start, int end) { - // super.calcMinMax(); + // super.calculate(); if (mYVals == null) return; diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java index ca19784347..012477c704 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java @@ -39,7 +39,7 @@ public abstract class Utils { private static int mMinimumFlingVelocity = 50; private static int mMaximumFlingVelocity = 8000; public final static double DEG2RAD = (Math.PI / 180.0); - public final static float FDEG2RAD = ((float)Math.PI / 180.f); + public final static float FDEG2RAD = ((float) Math.PI / 180.f); /** * initialize method, called inside the Chart.init() method. @@ -99,10 +99,13 @@ public static float convertDpToPixel(float dp) { if (mMetrics == null) { Log.e("MPChartLib-Utils", - "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before calling Utils.convertDpToPixel(...). Otherwise conversion does not take place."); + "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before" + + " 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(...)."); + // "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before + // calling Utils.convertDpToPixel(...)."); } DisplayMetrics metrics = mMetrics; @@ -122,10 +125,13 @@ public static float convertPixelsToDp(float px) { if (mMetrics == null) { Log.e("MPChartLib-Utils", - "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before calling Utils.convertPixelsToDp(...). Otherwise conversion does not take place."); + "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before" + + " 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(...)."); + // "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before + // calling Utils.convertPixelsToDp(...)."); } DisplayMetrics metrics = mMetrics; @@ -195,7 +201,8 @@ public static FSize calcTextSize(Paint paint, String demoText) { /** * Formats the given number to the given number of decimals, and returns the - * number as a string, maximum 35 characters. If thousands are separated, the separating character is a dot ("."). + * number as a string, maximum 35 characters. If thousands are separated, the separating + * character is a dot ("."). * * @param number * @param digitCount @@ -216,7 +223,8 @@ public static String formatNumber(float number, int digitCount, boolean separate * @param separateChar a caracter to be paced between the "thousands" * @return */ - public static String formatNumber(float number, int digitCount, boolean separateThousands, char separateChar) { + public static String formatNumber(float number, int digitCount, boolean separateThousands, + char separateChar) { char[] out = new char[35]; @@ -536,7 +544,7 @@ public static void drawText(Canvas c, String text, float x, float y, final float lineHeight = mDrawTextRectBuffer.height(); - // Android sometimes has pre-padding + // Android sometimes has pre-padding drawOffsetX -= mDrawTextRectBuffer.left; // Android does not snap the bounds to line boundaries, @@ -575,8 +583,7 @@ public static void drawText(Canvas c, String text, float x, float y, c.drawText(text, drawOffsetX, drawOffsetY, paint); c.restore(); - } - else { + } else { if (anchor.x != 0.f || anchor.y != 0.f) { drawOffsetX -= mDrawTextRectBuffer.width() * anchor.x; @@ -647,8 +654,7 @@ public static void drawMultilineText(Canvas c, StaticLayout textLayout, textLayout.draw(c); c.restore(); - } - else { + } else { if (anchor.x != 0.f || anchor.y != 0.f) { drawOffsetX -= drawWidth * anchor.x; @@ -685,32 +691,60 @@ public static void drawMultilineText(Canvas c, String text, drawMultilineText(c, textLayout, x, y, paint, anchor, angleDegrees); } - public static FSize getSizeOfRotatedRectangleByDegrees(FSize rectangleSize, float degrees) - { + public static FSize getSizeOfRotatedRectangleByDegrees(FSize rectangleSize, float degrees) { final float radians = degrees * FDEG2RAD; - return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height, radians); + return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height, + radians); } - public static FSize getSizeOfRotatedRectangleByRadians(FSize rectangleSize, float radians) - { - return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height, radians); + public static FSize getSizeOfRotatedRectangleByRadians(FSize rectangleSize, float radians) { + return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height, + radians); } - public static FSize getSizeOfRotatedRectangleByDegrees(float rectangleWidth, float rectangleHeight, float degrees) - { + public static FSize getSizeOfRotatedRectangleByDegrees(float rectangleWidth, float + rectangleHeight, float degrees) { final float radians = degrees * FDEG2RAD; return getSizeOfRotatedRectangleByRadians(rectangleWidth, rectangleHeight, radians); } - public static FSize getSizeOfRotatedRectangleByRadians(float rectangleWidth, float rectangleHeight, float radians) - { + public static FSize getSizeOfRotatedRectangleByRadians(float rectangleWidth, float + rectangleHeight, float radians) { return new FSize( - Math.abs(rectangleWidth * (float)Math.cos(radians)) + Math.abs(rectangleHeight * (float)Math.sin(radians)), - Math.abs(rectangleWidth * (float)Math.sin(radians)) + Math.abs(rectangleHeight * (float)Math.cos(radians)) + Math.abs(rectangleWidth * (float) Math.cos(radians)) + Math.abs(rectangleHeight * + (float) Math.sin(radians)), + Math.abs(rectangleWidth * (float) Math.sin(radians)) + Math.abs(rectangleHeight * + (float) Math.cos(radians)) ); } public static int getSDKInt() { return android.os.Build.VERSION.SDK_INT; } + + /** + * Calculates the granularity (minimum axis interval) based on axis range and labelcount. + * Default granularity is 1/10th of interval. + * + * @param range + * @param labelCount + * @return + */ + public static double granularity(float range, int labelCount) { + + // Find out how much spacing (in y value space) between axis values + double rawInterval = range / labelCount; + double interval = Utils.roundToNextSignificant(rawInterval); + + // Normalize interval + double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10 + (interval))); + int intervalSigDigit = (int) (interval / intervalMagnitude); + + if (intervalSigDigit > 5) { + interval = Math.floor(10 * intervalMagnitude); + } + + return interval * 0.1; // granularity is 1/10th of interval + } } From 937ea040625f0d00fb34aacfca33785982084116 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Apr 2016 18:50:31 +0200 Subject: [PATCH 062/606] Disable granularity calculation per default --- .../github/mikephil/charting/components/YAxis.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java index bbbedda8c4..bbb38516f2 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java @@ -128,7 +128,7 @@ public enum YAxisLabelPosition { * This could happen if two adjacent axis values are rounded to same value. * If using granularity this could be avoided by having fewer axis values visible. */ - protected boolean mGranularityEnabled = true; + protected boolean mGranularityEnabled = false; /** * the minimum interval between axis values @@ -207,7 +207,7 @@ public boolean isGranularityEnabled() { /** * Enabled/disable granularity control on axis value intervals. If enabled, the axis * interval is not allowed to go below a certain granularity (which is either - * auto-calculated, or custom set) + * auto-calculated, or custom set). Default: false * * @param enabled */ @@ -224,20 +224,21 @@ public float getGranularity() { /** * Set a custom minimum interval between axis values (instead of auto-calculation). The axis - * interval can then not go below - * the specified limit. Use resetGranularity() to re-enable auto calculation. + * interval can then not go below the specified limit. Use resetGranularity() to re-enable + * auto calculation. This will automatically enable granularity. * This can be used to avoid label duplicating when zooming in. * * @param granularity */ public void setGranularity(float granularity) { mGranularity = granularity; + mGranularityEnabled = true; mCustomGranularity = true; } /** * Reset the custom-set granularity. If this method is called, granularity - * is again auto calculated. + * is again auto calculated (if enabled). */ public void resetGranularity() { mCustomGranularity = false; From 995538cf0b58af99c28d5878e0583d7c44e67069 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Apr 2016 22:36:23 +0200 Subject: [PATCH 063/606] Remove granularity auto-calc feature --- .../mikephil/charting/components/YAxis.java | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java index bbb38516f2..0846a670a7 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java @@ -135,11 +135,6 @@ public enum YAxisLabelPosition { */ protected float mGranularity = 1.0f; - /** - * flag indicating if a custom-granularity was set - if false, granularity is auto calculated - */ - protected boolean mCustomGranularity = false; - /** * Enum that specifies the axis a DataSet should be plotted against, either LEFT or RIGHT. * @@ -151,6 +146,8 @@ public enum AxisDependency { public YAxis() { super(); + + // default left this.mAxisDependency = AxisDependency.LEFT; this.mYOffset = 0f; } @@ -206,8 +203,7 @@ public boolean isGranularityEnabled() { /** * Enabled/disable granularity control on axis value intervals. If enabled, the axis - * interval is not allowed to go below a certain granularity (which is either - * auto-calculated, or custom set). Default: false + * interval is not allowed to go below a certain granularity. Default: false * * @param enabled */ @@ -223,25 +219,15 @@ public float getGranularity() { } /** - * Set a custom minimum interval between axis values (instead of auto-calculation). The axis - * interval can then not go below the specified limit. Use resetGranularity() to re-enable - * auto calculation. This will automatically enable granularity. - * This can be used to avoid label duplicating when zooming in. + * Set a minimum interval for the axis when zooming in. The axis is not allowed to go below + * that limit. This can be used to avoid label duplicating when zooming in. * * @param granularity */ public void setGranularity(float granularity) { mGranularity = granularity; + // set this to true if it was disabled, as it makes no sense to call this method with granularity disabled mGranularityEnabled = true; - mCustomGranularity = true; - } - - /** - * Reset the custom-set granularity. If this method is called, granularity - * is again auto calculated (if enabled). - */ - public void resetGranularity() { - mCustomGranularity = false; } /** @@ -619,11 +605,11 @@ public void calculate(float dataMin, float dataMax) { // calc actual range this.mAxisRange = Math.abs(this.mAxisMaximum - this.mAxisMinimum); - // in case granularity is not customized, auto-calculate it - if (!mCustomGranularity && mGranularityEnabled) { - - double granularity = Utils.granularity(mAxisRange, mLabelCount); - this.mGranularity = (float) granularity; - } +// // in case granularity is not customized, auto-calculate it +// if (!mCustomGranularity && mGranularityEnabled) { +// +// double granularity = Utils.granularity(mAxisRange, mLabelCount); +// this.mGranularity = (float) granularity; +// } } } From 00f3e422789006bcd457b1ed30c5927c85eef4cd Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Apr 2016 22:50:33 +0200 Subject: [PATCH 064/606] Create XAxisValue class --- .../mikephil/charting/data/XAxisValue.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 MPChartLib/src/com/github/mikephil/charting/data/XAxisValue.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/XAxisValue.java b/MPChartLib/src/com/github/mikephil/charting/data/XAxisValue.java new file mode 100644 index 0000000000..a14f7ab41f --- /dev/null +++ b/MPChartLib/src/com/github/mikephil/charting/data/XAxisValue.java @@ -0,0 +1,57 @@ +package com.github.mikephil.charting.data; + +/** + * Created by Philipp Jahoda on 03/04/16. + */ +public class XAxisValue { + + /** + * the label that describes this value + */ + private String mLabel = ""; + + /** + * the position of this value on the x-axis + */ + private double mPosition; + + /** + * Constructor. + * + * @param xPosition the position of this value on the x-axis + * @param label the x-axis label of this value + */ + public XAxisValue(double xPosition, String label) { + this.mLabel = label; + this.mPosition = xPosition; + } + + /** + * Sets both x-position and label. + * + * @param xPosition + * @param label + */ + public void set(double xPosition, String label) { + this.mLabel = label; + this.mPosition = xPosition; + } + + /** + * Returns the position (x) of the value on the x-axis. + * + * @return + */ + public double getPosition() { + return mPosition; + } + + /** + * Returns the x-axis label of this value. + * + * @return + */ + public String getLabel() { + return mLabel; + } +} From 31f15a941dafef1dae9c132081a36faed606f671 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 4 Apr 2016 14:52:43 +0300 Subject: [PATCH 065/606] Turn on granularity for this example For the value to take effect --- .../xxmassdeveloper/mpchartexample/BarChartActivitySinus.java | 1 + 1 file changed, 1 insertion(+) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index 6ed4009640..e200f2e9b1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -89,6 +89,7 @@ protected void onCreate(Bundle savedInstanceState) { leftAxis.setLabelCount(6, false); leftAxis.setAxisMinValue(-2.5f); leftAxis.setAxisMaxValue(2.5f); + leftAxis.setGranularityEnabled(true); leftAxis.setGranularity(0.1f); YAxis rightAxis = mChart.getAxisRight(); From 4c5a2b94a4de3064afe1b692461d43c984ab489e Mon Sep 17 00:00:00 2001 From: = Date: Mon, 4 Apr 2016 21:46:53 +0200 Subject: [PATCH 066/606] Introduce XAxisValue to ChartData object --- .../mikephil/charting/charts/Chart.java | 3 ++- .../mikephil/charting/components/XAxis.java | 9 +++---- .../mikephil/charting/data/BarData.java | 12 +++++----- .../data/BarLineScatterCandleBubbleData.java | 8 +++---- .../mikephil/charting/data/BubbleData.java | 12 +++++----- .../mikephil/charting/data/CandleData.java | 12 +++++----- .../mikephil/charting/data/ChartData.java | 24 +++++++++---------- .../mikephil/charting/data/CombinedData.java | 4 ++-- .../mikephil/charting/data/LineData.java | 12 +++++----- .../mikephil/charting/data/PieData.java | 8 +++---- .../mikephil/charting/data/RadarData.java | 12 +++++----- .../mikephil/charting/data/ScatterData.java | 12 +++++----- .../charting/data/realm/base/RealmUtils.java | 13 ++++++---- .../realm/implementation/RealmBarData.java | 4 ++-- .../realm/implementation/RealmBubbleData.java | 4 ++-- .../realm/implementation/RealmCandleData.java | 4 ++-- .../realm/implementation/RealmLineData.java | 4 ++-- .../realm/implementation/RealmPieData.java | 4 ++-- .../realm/implementation/RealmRadarData.java | 4 ++-- .../implementation/RealmScatterData.java | 4 ++-- .../charting/renderer/LegendRenderer.java | 5 ++-- .../charting/renderer/PieChartRenderer.java | 8 +++---- .../charting/renderer/XAxisRenderer.java | 5 ++-- .../renderer/XAxisRendererBarChart.java | 2 +- .../XAxisRendererHorizontalBarChart.java | 5 ++-- .../renderer/XAxisRendererRadarChart.java | 2 +- 26 files changed, 103 insertions(+), 93 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java index 055bdf7312..096b67c121 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java @@ -33,6 +33,7 @@ import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.formatter.DefaultValueFormatter; import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.ChartHighlighter; @@ -1375,7 +1376,7 @@ public void setDrawMarkerViews(boolean enabled) { * @param index * @return */ - public String getXValue(int index) { + public XAxisValue getXValue(int index) { if (mData == null || mData.getXValCount() <= index) return null; else diff --git a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java index 7343baaef8..18b6e55141 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.components; +import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.formatter.DefaultXAxisValueFormatter; import com.github.mikephil.charting.formatter.XAxisValueFormatter; import com.github.mikephil.charting.utils.Utils; @@ -18,7 +19,7 @@ public class XAxis extends AxisBase { /** the arraylist containing all the x-axis labels */ - protected List mValues = new ArrayList(); + protected List mValues = new ArrayList(); /** * width of the x-axis labels in pixels - this is automatically @@ -204,7 +205,7 @@ public boolean isAvoidFirstLastClippingEnabled() { * * @param values */ - public void setValues(List values) { + public void setValues(List values) { mValues = values; } @@ -213,7 +214,7 @@ public void setValues(List values) { * * @return */ - public List getValues() { + public List getValues() { return mValues; } @@ -246,7 +247,7 @@ public String getLongestLabel() { String longest = ""; for (int i = 0; i < mValues.size(); i++) { - String text = mValues.get(i); + String text = mValues.get(i).getLabel(); if (longest.length() < text.length()) longest = text; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarData.java b/MPChartLib/src/com/github/mikephil/charting/data/BarData.java index 43b6e88e86..a0528153be 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/BarData.java @@ -25,27 +25,27 @@ public BarData() { super(); } - public BarData(List xVals) { + public BarData(List xVals) { super(xVals); } - public BarData(String[] xVals) { + public BarData(XAxisValue[] xVals) { super(xVals); } - public BarData(List xVals, List dataSets) { + public BarData(List xVals, List dataSets) { super(xVals, dataSets); } - public BarData(String[] xVals, List dataSets) { + public BarData(XAxisValue[] xVals, List dataSets) { super(xVals, dataSets); } - public BarData(List xVals, IBarDataSet dataSet) { + public BarData(List xVals, IBarDataSet dataSet) { super(xVals, toList(dataSet)); } - public BarData(String[] xVals, IBarDataSet dataSet) { + public BarData(XAxisValue[] xVals, IBarDataSet dataSet) { super(xVals, toList(dataSet)); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java b/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java index 84dc3df67e..801f2fc87d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java @@ -17,19 +17,19 @@ public BarLineScatterCandleBubbleData() { super(); } - public BarLineScatterCandleBubbleData(List xVals) { + public BarLineScatterCandleBubbleData(List xVals) { super(xVals); } - public BarLineScatterCandleBubbleData(String[] xVals) { + public BarLineScatterCandleBubbleData(XAxisValue[] xVals) { super(xVals); } - public BarLineScatterCandleBubbleData(List xVals, List sets) { + public BarLineScatterCandleBubbleData(List xVals, List sets) { super(xVals, sets); } - public BarLineScatterCandleBubbleData(String[] xVals, List sets) { + public BarLineScatterCandleBubbleData(XAxisValue[] xVals, List sets) { super(xVals, sets); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BubbleData.java b/MPChartLib/src/com/github/mikephil/charting/data/BubbleData.java index 53945ac0e5..28e317b306 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BubbleData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/BubbleData.java @@ -12,27 +12,27 @@ public BubbleData() { super(); } - public BubbleData(List xVals) { + public BubbleData(List xVals) { super(xVals); } - public BubbleData(String[] xVals) { + public BubbleData(XAxisValue[] xVals) { super(xVals); } - public BubbleData(List xVals, List dataSets) { + public BubbleData(List xVals, List dataSets) { super(xVals, dataSets); } - public BubbleData(String[] xVals, List dataSets) { + public BubbleData(XAxisValue[] xVals, List dataSets) { super(xVals, dataSets); } - public BubbleData(List xVals, IBubbleDataSet dataSet) { + public BubbleData(List xVals, IBubbleDataSet dataSet) { super(xVals, toList(dataSet)); } - public BubbleData(String[] xVals, IBubbleDataSet dataSet) { + public BubbleData(XAxisValue[] xVals, IBubbleDataSet dataSet) { super(xVals, toList(dataSet)); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleData.java b/MPChartLib/src/com/github/mikephil/charting/data/CandleData.java index a1c797b4fa..17e79c0748 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/CandleData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/CandleData.java @@ -11,27 +11,27 @@ public CandleData() { super(); } - public CandleData(List xVals) { + public CandleData(List xVals) { super(xVals); } - public CandleData(String[] xVals) { + public CandleData(XAxisValue[] xVals) { super(xVals); } - public CandleData(List xVals, List dataSets) { + public CandleData(List xVals, List dataSets) { super(xVals, dataSets); } - public CandleData(String[] xVals, List dataSets) { + public CandleData(XAxisValue[] xVals, List dataSets) { super(xVals, dataSets); } - public CandleData(List xVals, ICandleDataSet dataSet) { + public CandleData(List xVals, ICandleDataSet dataSet) { super(xVals, toList(dataSet)); } - public CandleData(String[] xVals, ICandleDataSet dataSet) { + public CandleData(XAxisValue[] xVals, ICandleDataSet dataSet) { super(xVals, toList(dataSet)); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java index 71663eb2cd..eb39dc794b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java @@ -53,7 +53,7 @@ public abstract class ChartData> { /** * holds all x-values the chart represents */ - protected List mXVals; + protected List mXVals; /** * array that holds all DataSets the ChartData object represents @@ -61,7 +61,7 @@ public abstract class ChartData> { protected List mDataSets; public ChartData() { - mXVals = new ArrayList(); + mXVals = new ArrayList(); mDataSets = new ArrayList(); } @@ -71,7 +71,7 @@ public ChartData() { * * @param xVals */ - public ChartData(List xVals) { + public ChartData(List xVals) { this.mXVals = xVals; this.mDataSets = new ArrayList(); init(); @@ -83,7 +83,7 @@ public ChartData(List xVals) { * * @param xVals */ - public ChartData(String[] xVals) { + public ChartData(XAxisValue[] xVals) { this.mXVals = arrayToList(xVals); this.mDataSets = new ArrayList(); init(); @@ -97,7 +97,7 @@ public ChartData(String[] xVals) { * DataSets. * @param sets the dataset array */ - public ChartData(List xVals, List sets) { + public ChartData(List xVals, List sets) { this.mXVals = xVals; this.mDataSets = sets; @@ -112,7 +112,7 @@ public ChartData(List xVals, List sets) { * DataSets. * @param sets the dataset array */ - public ChartData(String[] xVals, List sets) { + public ChartData(XAxisValue[] xVals, List sets) { this.mXVals = arrayToList(xVals); this.mDataSets = sets; @@ -125,7 +125,7 @@ public ChartData(String[] xVals, List sets) { * @param array * @return */ - private List arrayToList(String[] array) { + private List arrayToList(XAxisValue[] array) { return Arrays.asList(array); } @@ -156,7 +156,7 @@ private void calcXValMaximumLength() { for (int i = 0; i < mXVals.size(); i++) { - int length = mXVals.get(i).length(); + int length = mXVals.get(i).getLabel().length(); if (length > max) max = length; @@ -372,7 +372,7 @@ public int getYValCount() { * * @return */ - public List getXVals() { + public List getXVals() { return mXVals; } @@ -381,10 +381,10 @@ public List getXVals() { * * @param xVal */ - public void addXValue(String xVal) { + public void addXValue(XAxisValue xVal) { - if (xVal != null && xVal.length() > mXValMaximumLength) - mXValMaximumLength = xVal.length(); + if (xVal != null && xVal.getLabel().length() > mXValMaximumLength) + mXValMaximumLength = xVal.getLabel().length(); mXVals.add(xVal); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java b/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java index 2d7982265d..c03db11a60 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java @@ -24,11 +24,11 @@ public CombinedData() { super(); } - public CombinedData(List xVals) { + public CombinedData(List xVals) { super(xVals); } - public CombinedData(String[] xVals) { + public CombinedData(XAxisValue[] xVals) { super(xVals); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineData.java b/MPChartLib/src/com/github/mikephil/charting/data/LineData.java index 9adb07b60d..53ba9a28d5 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/LineData.java @@ -17,27 +17,27 @@ public LineData() { super(); } - public LineData(List xVals) { + public LineData(List xVals) { super(xVals); } - public LineData(String[] xVals) { + public LineData(XAxisValue[] xVals) { super(xVals); } - public LineData(List xVals, List dataSets) { + public LineData(List xVals, List dataSets) { super(xVals, dataSets); } - public LineData(String[] xVals, List dataSets) { + public LineData(XAxisValue[] xVals, List dataSets) { super(xVals, dataSets); } - public LineData(List xVals, ILineDataSet dataSet) { + public LineData(List xVals, ILineDataSet dataSet) { super(xVals, toList(dataSet)); } - public LineData(String[] xVals, ILineDataSet dataSet) { + public LineData(XAxisValue[] xVals, ILineDataSet dataSet) { super(xVals, toList(dataSet)); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/PieData.java b/MPChartLib/src/com/github/mikephil/charting/data/PieData.java index 9a3151e6c4..c2fb239b42 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/PieData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/PieData.java @@ -20,19 +20,19 @@ public PieData() { super(); } - public PieData(List xVals) { + public PieData(List xVals) { super(xVals); } - public PieData(String[] xVals) { + public PieData(XAxisValue[] xVals) { super(xVals); } - public PieData(List xVals, IPieDataSet dataSet) { + public PieData(List xVals, IPieDataSet dataSet) { super(xVals, toList(dataSet)); } - public PieData(String[] xVals, IPieDataSet dataSet) { + public PieData(XAxisValue[] xVals, IPieDataSet dataSet) { super(xVals, toList(dataSet)); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/RadarData.java b/MPChartLib/src/com/github/mikephil/charting/data/RadarData.java index 48494d3d65..ff18c0d8e1 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/RadarData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/RadarData.java @@ -17,27 +17,27 @@ public RadarData() { super(); } - public RadarData(List xVals) { + public RadarData(List xVals) { super(xVals); } - public RadarData(String[] xVals) { + public RadarData(XAxisValue[] xVals) { super(xVals); } - public RadarData(List xVals, List dataSets) { + public RadarData(List xVals, List dataSets) { super(xVals, dataSets); } - public RadarData(String[] xVals, List dataSets) { + public RadarData(XAxisValue[] xVals, List dataSets) { super(xVals, dataSets); } - public RadarData(List xVals, IRadarDataSet dataSet) { + public RadarData(List xVals, IRadarDataSet dataSet) { super(xVals, toList(dataSet)); } - public RadarData(String[] xVals, IRadarDataSet dataSet) { + public RadarData(XAxisValue[] xVals, IRadarDataSet dataSet) { super(xVals, toList(dataSet)); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ScatterData.java b/MPChartLib/src/com/github/mikephil/charting/data/ScatterData.java index 99422dd916..713fa7f2e0 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ScatterData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/ScatterData.java @@ -12,27 +12,27 @@ public ScatterData() { super(); } - public ScatterData(List xVals) { + public ScatterData(List xVals) { super(xVals); } - public ScatterData(String[] xVals) { + public ScatterData(XAxisValue[] xVals) { super(xVals); } - public ScatterData(List xVals, List dataSets) { + public ScatterData(List xVals, List dataSets) { super(xVals, dataSets); } - public ScatterData(String[] xVals, List dataSets) { + public ScatterData(XAxisValue[] xVals, List dataSets) { super(xVals, dataSets); } - public ScatterData(List xVals, IScatterDataSet dataSet) { + public ScatterData(List xVals, IScatterDataSet dataSet) { super(xVals, toList(dataSet)); } - public ScatterData(String[] xVals, IScatterDataSet dataSet) { + public ScatterData(XAxisValue[] xVals, IScatterDataSet dataSet) { super(xVals, toList(dataSet)); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmUtils.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmUtils.java index b9b5cd24fd..8f9b8d5310 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmUtils.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmUtils.java @@ -1,5 +1,7 @@ package com.github.mikephil.charting.data.realm.base; +import com.github.mikephil.charting.data.XAxisValue; + import java.util.ArrayList; import java.util.List; @@ -15,17 +17,20 @@ public final class RealmUtils { * Transforms the given Realm-ResultSet into a String array by using the provided xValuesField. * * @param result - * @param xValuesField + * @param xPositionField + * @param xLabelField * @return */ - public static List toXVals(RealmResults result, String xValuesField) { + public static List toXVals(RealmResults result, String xPositionField, String xLabelField) { - List xVals = new ArrayList(); + List xVals = new ArrayList(); for (RealmObject object : result) { DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - xVals.add(dynamicObject.getString(xValuesField)); + + XAxisValue val = new XAxisValue(dynamicObject.getDouble(xPositionField), dynamicObject.getString(xLabelField)); + xVals.add(val); } return xVals; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java index ec124e6dee..8a46ff618a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java @@ -14,7 +14,7 @@ */ public class RealmBarData extends BarData { - public RealmBarData(RealmResults result, String xValuesField, List dataSets) { - super(RealmUtils.toXVals(result, xValuesField), dataSets); + public RealmBarData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { + super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSets); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java index 617f098dd2..e21017f6f9 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java @@ -14,7 +14,7 @@ */ public class RealmBubbleData extends BubbleData { - public RealmBubbleData(RealmResults result, String xValuesField, List dataSets) { - super(RealmUtils.toXVals(result, xValuesField), dataSets); + public RealmBubbleData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { + super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSets); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java index 769c1801c2..5cc2757498 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java @@ -14,7 +14,7 @@ */ public class RealmCandleData extends CandleData { - public RealmCandleData(RealmResults result, String xValuesField, List dataSets) { - super(RealmUtils.toXVals(result, xValuesField), dataSets); + public RealmCandleData(RealmResults result,String xPositionField, String xLabelField, List dataSets) { + super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSets); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java index bcacb98952..97357e034d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java @@ -14,7 +14,7 @@ */ public class RealmLineData extends LineData { - public RealmLineData(RealmResults result, String xValuesField, List dataSets) { - super(RealmUtils.toXVals(result, xValuesField), dataSets); + public RealmLineData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { + super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSets); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java index 665725118c..d407e67864 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java @@ -12,7 +12,7 @@ */ public class RealmPieData extends PieData { - public RealmPieData(RealmResults result, String xValuesField, IPieDataSet dataSet) { - super(RealmUtils.toXVals(result, xValuesField), dataSet); + public RealmPieData(RealmResults result,String xPositionField, String xLabelField, IPieDataSet dataSet) { + super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSet); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java index a098a6d2a5..61dbf2b86d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java @@ -14,7 +14,7 @@ */ public class RealmRadarData extends RadarData{ - public RealmRadarData(RealmResults result, String xValuesField, List dataSets) { - super(RealmUtils.toXVals(result, xValuesField), dataSets); + public RealmRadarData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { + super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSets); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java index 2838a86f11..8ae94aaac3 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java @@ -14,7 +14,7 @@ */ public class RealmScatterData extends ScatterData { - public RealmScatterData(RealmResults result, String xValuesField, List dataSets) { - super(RealmUtils.toXVals(result, xValuesField), dataSets); + public RealmScatterData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { + super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSets); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java index b6b3a3f3ac..bf16d5b695 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -8,6 +8,7 @@ import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; import com.github.mikephil.charting.interfaces.datasets.IDataSet; @@ -110,12 +111,12 @@ public void computeLegend(ChartData data) { } else if (dataSet instanceof IPieDataSet) { - List xVals = data.getXVals(); + List xVals = data.getXVals(); IPieDataSet pds = (IPieDataSet) dataSet; for (int j = 0; j < clrs.size() && j < entryCount && j < xVals.size(); j++) { - labels.add(xVals.get(j)); + labels.add(xVals.get(j).getLabel()); colors.add(clrs.get(j)); } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java index 269b767c87..fd7931f714 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -507,14 +507,14 @@ public void drawValues(Canvas c) { dataSet.getValueTextColor(j)); if (j < data.getXValCount()) { - c.drawText(data.getXVals().get(j), labelPtx, labelPty + lineHeight, + c.drawText(data.getXVals().get(j).getLabel(), labelPtx, labelPty + lineHeight, mValuePaint); } } else if (drawXOutside) { if (j < data.getXValCount()) { mValuePaint.setColor(dataSet.getValueTextColor(j)); - c.drawText(data.getXVals().get(j), labelPtx, labelPty + lineHeight / 2.f, mValuePaint); + c.drawText(data.getXVals().get(j).getLabel(), labelPtx, labelPty + lineHeight / 2.f, mValuePaint); } } else if (drawYOutside) { @@ -535,14 +535,14 @@ public void drawValues(Canvas c) { drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j)); if (j < data.getXValCount()) { - c.drawText(data.getXVals().get(j), x, y + lineHeight, + c.drawText(data.getXVals().get(j).getLabel(), x, y + lineHeight, mValuePaint); } } else if (drawXInside) { if (j < data.getXValCount()) { mValuePaint.setColor(dataSet.getValueTextColor(j)); - c.drawText(data.getXVals().get(j), x, y + lineHeight / 2f, mValuePaint); + c.drawText(data.getXVals().get(j).getLabel(), x, y + lineHeight / 2f, mValuePaint); } } else if (drawYInside) { diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java index a0a2bff642..6de6112508 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -12,6 +12,7 @@ import com.github.mikephil.charting.components.LimitLine; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.utils.FSize; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; @@ -33,7 +34,7 @@ public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer t mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); } - public void computeAxis(float xValMaximumLength, List xValues) { + public void computeAxis(float xValMaximumLength, List xValues) { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); @@ -162,7 +163,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { if (mViewPortHandler.isInBoundsX(position[0])) { - String label = mXAxis.getValues().get(i); + String label = mXAxis.getValues().get(i).getLabel(); if (mXAxis.isAvoidFirstLastClippingEnabled()) { diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java index c869bec352..d58c3842b5 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java @@ -55,7 +55,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { if (mViewPortHandler.isInBoundsX(position[0]) && i >= 0 && i < mXAxis.getValues().size()) { - String label = mXAxis.getValues().get(i); + String label = mXAxis.getValues().get(i).getLabel(); if (mXAxis.isAvoidFirstLastClippingEnabled()) { diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java index e862263520..01b17f3904 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java @@ -12,6 +12,7 @@ import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.utils.FSize; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; @@ -27,7 +28,7 @@ public XAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, XAxis xA } @Override - public void computeAxis(float xValAverageLength, List xValues) { + public void computeAxis(float xValAverageLength, List xValues) { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); @@ -124,7 +125,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { if (mViewPortHandler.isInBoundsY(position[1])) { - String label = mXAxis.getValues().get(i); + String label = mXAxis.getValues().get(i).getLabel(); drawLabel(c, label, i, pos, position[1], anchor, labelRotationAngleDegrees); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java index 50a2fc7dd4..8d926ddfcb 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java @@ -42,7 +42,7 @@ public void renderAxisLabels(Canvas c) { int mod = mXAxis.mAxisLabelModulus; for (int i = 0; i < mXAxis.getValues().size(); i += mod) { - String label = mXAxis.getValues().get(i); + String label = mXAxis.getValues().get(i).getLabel(); float angle = (sliceangle * i + mChart.getRotationAngle()) % 360f; From d771bf4467761467b6113987d7bce2a5b19de89e Mon Sep 17 00:00:00 2001 From: = Date: Mon, 4 Apr 2016 21:51:06 +0200 Subject: [PATCH 067/606] Start fixing example --- .../mpchartexample/AnotherBarActivity.java | 7 +++++-- .../mpchartexample/BarChartActivitySinus.java | 9 ++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index cabd72e9dd..0990f4144d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -16,6 +16,7 @@ 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.XAxisValue; import com.github.mikephil.charting.data.filter.Approximator; import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; @@ -174,9 +175,11 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { yVals1.add(new BarEntry((int) val1, i)); } - ArrayList xVals = new ArrayList(); + ArrayList xVals = new ArrayList(); for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - xVals.add((int) yVals1.get(i).getVal() + ""); + + XAxisValue xValue = new XAxisValue(i, (int) yVals1.get(i).getVal() + ""); + xVals.add(xValue); } BarDataSet set1 = new BarDataSet(yVals1, "Data Set"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index e200f2e9b1..08ce038e6e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -22,6 +22,7 @@ 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.XAxisValue; import com.github.mikephil.charting.data.filter.Approximator; import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; @@ -79,9 +80,6 @@ protected void onCreate(Bundle savedInstanceState) { mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); XAxis xAxis = mChart.getXAxis(); - xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setTypeface(mTf); - xAxis.setDrawGridLines(false); xAxis.setEnabled(false); YAxis leftAxis = mChart.getAxisLeft(); @@ -208,12 +206,9 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count) { - ArrayList xVals = new ArrayList(); - ArrayList entries = new ArrayList(); for (int i = 0; i < count; i++) { - xVals.add(i+""); entries.add(mSinusData.get(i)); } @@ -221,7 +216,7 @@ private void setData(int count) { set.setBarSpacePercent(40f); set.setColor(Color.rgb(240, 120, 124)); - BarData data = new BarData(xVals, set); + BarData data = new BarData(null, set); data.setValueTextSize(10f); data.setValueTypeface(mTf); data.setDrawValues(false); From a9bd0c619128eaa7bef61b12a9fb60ce9069f87d Mon Sep 17 00:00:00 2001 From: = Date: Mon, 4 Apr 2016 22:32:47 +0200 Subject: [PATCH 068/606] Adjust example according to XAxisValue --- .../mpchartexample/BarChartActivity.java | 10 ++-- .../BarChartActivityMultiDataset.java | 7 ++- .../mpchartexample/BarChartActivitySinus.java | 2 +- .../BarChartPositiveNegative.java | 5 +- .../mpchartexample/BubbleChartActivity.java | 6 +- .../CandleStickChartActivity.java | 8 +-- .../mpchartexample/CombinedChartActivity.java | 11 +++- .../CubicLineChartActivity.java | 8 +-- .../mpchartexample/DrawChartActivity.java | 9 +-- .../DynamicalAddingActivity.java | 5 +- .../HorizontalBarChartActivity.java | 5 +- .../InvertedLineChartActivity.java | 8 +-- .../mpchartexample/LineChartActivity1.java | 8 +-- .../mpchartexample/LineChartActivity2.java | 5 +- .../LineChartActivityColored.java | 10 +--- .../ListViewBarChartActivity.java | 20 +------ .../ListViewMultiChartActivity.java | 30 ---------- .../MultiLineChartActivity.java | 5 +- .../mpchartexample/PerformanceLineChart.java | 5 +- .../mpchartexample/PieChartActivity.java | 5 +- .../PiePolylineChartActivity.java | 5 +- .../mpchartexample/RadarChartActivitry.java | 5 +- .../RealtimeLineChartActivity.java | 5 +- .../mpchartexample/ScatterChartActivity.java | 5 +- .../mpchartexample/ScrollViewActivity.java | 5 +- .../mpchartexample/StackedBarActivity.java | 8 +-- .../StackedBarActivityNegative.java | 8 ++- .../mpchartexample/custom/RealmDemoData.java | 55 ++++++++++++------- .../fragments/SimpleFragment.java | 13 ++--- .../mpchartexample/notimportant/DemoBase.java | 50 +++++++++++++++++ .../realm/RealmBaseActivity.java | 8 +-- .../realm/RealmDatabaseActivityBar.java | 2 +- .../realm/RealmDatabaseActivityBubble.java | 2 +- .../realm/RealmDatabaseActivityCandle.java | 2 +- .../RealmDatabaseActivityHorizontalBar.java | 2 +- .../realm/RealmDatabaseActivityLine.java | 2 +- .../realm/RealmDatabaseActivityPie.java | 2 +- .../realm/RealmDatabaseActivityRadar.java | 2 +- .../realm/RealmDatabaseActivityScatter.java | 2 +- .../realm/RealmWikiExample.java | 4 +- .../mikephil/charting/data/BarData.java | 5 ++ .../data/BarLineScatterCandleBubbleData.java | 4 ++ .../mikephil/charting/data/ChartData.java | 11 +++- .../mikephil/charting/data/LineData.java | 4 ++ .../mikephil/charting/data/XAxisValue.java | 9 +++ 45 files changed, 221 insertions(+), 171 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 99d3d027fd..be76bebdf7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -28,6 +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.data.XAxisValue; import com.github.mikephil.charting.data.filter.Approximator; import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.formatter.YAxisValueFormatter; @@ -225,17 +226,16 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add(mMonths[i % 12]); - } - + ArrayList xVals = new ArrayList(); ArrayList yVals1 = new ArrayList(); for (int i = 0; i < count; i++) { float mult = (range + 1); float val = (float) (Math.random() * mult); yVals1.add(new BarEntry(val, i)); + + XAxisValue value = new XAxisValue(i, mMonths[i % 12]); + xVals.add(value); } BarDataSet set1 = new BarDataSet(yVals1, "DataSet"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 4f869ecc3a..f00c4d3631 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -21,6 +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.data.XAxisValue; import com.github.mikephil.charting.formatter.LargeValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; @@ -178,9 +179,11 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvX.setText("" + (mSeekBarX.getProgress() * 3)); tvY.setText("" + (mSeekBarY.getProgress())); - ArrayList xVals = new ArrayList(); + ArrayList xVals = new ArrayList(); for (int i = 0; i < mSeekBarX.getProgress(); i++) { - xVals.add((i+1990) + ""); + + XAxisValue value = new XAxisValue(i, (i+1990) + ""); + xVals.add(value); } ArrayList yVals1 = new ArrayList(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index 08ce038e6e..a82399de8b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -216,7 +216,7 @@ private void setData(int count) { set.setBarSpacePercent(40f); set.setColor(Color.rgb(240, 120, 124)); - BarData data = new BarData(null, set); + BarData data = new BarData(set); data.setValueTextSize(10f); data.setValueTypeface(mTf); data.setDrawValues(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 26f8e6fceb..7e852c0b58 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -14,6 +14,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.data.XAxisValue; import com.github.mikephil.charting.utils.ViewPortHandler; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -87,7 +88,7 @@ protected void onCreate(Bundle savedInstanceState) { private void setData(List dataList) { ArrayList values = new ArrayList(); - String[] dates = new String[dataList.size()]; + XAxisValue[] dates = new XAxisValue[dataList.size()]; List colors = new ArrayList(); int green = Color.rgb(110, 190, 102); @@ -99,7 +100,7 @@ private void setData(List dataList) { BarEntry entry = new BarEntry(d.yValue, d.xIndex); values.add(entry); - dates[i] = dataList.get(i).xAxisValue; + dates[i] = new XAxisValue(i, dataList.get(i).xAxisValue); // specific colors if (d.yValue >= 0) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java index 03d97ed812..0002668c09 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -21,6 +21,7 @@ import com.github.mikephil.charting.data.BubbleDataSet; import com.github.mikephil.charting.data.BubbleEntry; import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.data.filter.Approximator; import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.highlight.Highlight; @@ -164,9 +165,10 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvX.setText("" + count); tvY.setText("" + range); - ArrayList xVals = new ArrayList(); + ArrayList xVals = new ArrayList(); for (int i = 0; i < count; i++) { - xVals.add((i) + ""); + XAxisValue xVal = new XAxisValue(i,(i) + ""); + xVals.add(xVal); } ArrayList yVals1 = new ArrayList(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java index 36211e175c..9cbcfd9247 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -20,6 +20,7 @@ import com.github.mikephil.charting.data.CandleData; import com.github.mikephil.charting.data.CandleDataSet; import com.github.mikephil.charting.data.CandleEntry; +import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -166,6 +167,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mChart.resetTracking(); + ArrayList xVals = new ArrayList(); ArrayList yVals1 = new ArrayList(); for (int i = 0; i < prog; i++) { @@ -182,11 +184,9 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { yVals1.add(new CandleEntry(i, val + high, val - low, even ? val + open : val - open, even ? val - close : val + close)); - } - ArrayList xVals = new ArrayList(); - for (int i = 0; i < prog; i++) { - xVals.add("" + (1990 + i)); + XAxisValue xVal = new XAxisValue(i,(i) + ""); + xVals.add(xVal); } CandleDataSet set1 = new CandleDataSet(yVals1, "Data Set"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 1cc5a5d9ab..67a4de9529 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -27,6 +27,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.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -52,7 +53,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawBarShadow(false); // draw bars behind lines - mChart.setDrawOrder(new DrawOrder[] { + mChart.setDrawOrder(new DrawOrder[]{ DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.CANDLE, DrawOrder.LINE, DrawOrder.SCATTER }); @@ -67,7 +68,13 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTH_SIDED); - CombinedData data = new CombinedData(mMonths); + XAxisValue[] xVals = new XAxisValue[mMonths.length]; + + for(int i = 0; i < mMonths.length; i++) { + xVals[i] = new XAxisValue(i, mMonths[i]); + } + + CombinedData data = new CombinedData(xVals); data.setData(generateLineData()); data.setData(generateBarData()); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 26220e5309..015055138c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -20,6 +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.data.XAxisValue; import com.github.mikephil.charting.data.filter.Approximator; import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.formatter.FillFormatter; @@ -241,11 +242,6 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add((1990 +i) + ""); - } - ArrayList vals1 = new ArrayList(); for (int i = 0; i < count; i++) { @@ -278,7 +274,7 @@ public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProv }); // create a data object with the datasets - LineData data = new LineData(xVals, set1); + LineData data = new LineData(set1); data.setValueTypeface(tf); data.setValueTextSize(9f); data.setDrawValues(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java index d12b3ecbd7..2e944e13c8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java @@ -74,10 +74,6 @@ protected void onCreate(Bundle savedInstanceState) { } private void initWithDummyData() { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < 24; i++) { - xVals.add((i) + ":00"); - } ArrayList yVals = new ArrayList(); @@ -86,11 +82,8 @@ private void initWithDummyData() { set1.setLineWidth(3f); set1.setCircleRadius(5f); - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets - // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); + LineData data = new LineData(set1); mChart.setData(data); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index 0c19b2032d..af3e744b91 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -13,6 +13,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.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; @@ -62,7 +63,7 @@ private void addEntry() { } // add a new x-value first - data.addXValue(set.getEntryCount() + ""); + data.addXValue(new XAxisValue(set.getEntryCount(), set.getEntryCount() + "")); // choose a random dataSet int randomDataSetIndex = (int) (Math.random() * data.getDataSetCount()); @@ -116,7 +117,7 @@ private void addDataSet() { if(data.getXValCount() == 0) { // add 10 x-entries for (int i = 0; i < 10; i++) { - data.addXValue("" + (i+1)); + data.addXValue(new XAxisValue(i, i + "")); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 3027188cf5..5e026ee07b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -25,6 +25,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.data.XAxisValue; import com.github.mikephil.charting.data.filter.Approximator; import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; @@ -228,10 +229,10 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { ArrayList yVals1 = new ArrayList(); - ArrayList xVals = new ArrayList(); + ArrayList xVals = new ArrayList(); for (int i = 0; i < count; i++) { - xVals.add(mMonths[i % 12]); + xVals.add(new XAxisValue(i, mMonths[i % 12])); yVals1.add(new BarEntry((float) (Math.random() * range), i)); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index 2f92912e60..ee4273b838 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -19,6 +19,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.data.XAxisValue; import com.github.mikephil.charting.data.filter.Approximator; import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; @@ -255,11 +256,7 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add((i % 30) + "/" + (i % 12) + "/14"); - } - + ArrayList xVals = new ArrayList(); ArrayList yVals = new ArrayList(); for (int i = 0; i < count; i++) { @@ -268,6 +265,7 @@ private void setData(int count, float range) { // ((mult * // 0.1) / 10); yVals.add(new Entry(val, i)); + xVals.add(new XAxisValue(i, (i % 30) + "/" + (i % 12) + "/14")); } // create a dataset and give it a type diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 836afcd7f2..9c5d522f68 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -27,6 +27,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.data.XAxisValue; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.ChartTouchListener; @@ -325,11 +326,7 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add((i) + ""); - } - + ArrayList xVals = new ArrayList(); ArrayList yVals = new ArrayList(); for (int i = 0; i < count; i++) { @@ -339,6 +336,7 @@ private void setData(int count, float range) { // ((mult * // 0.1) / 10); yVals.add(new Entry(val, i)); + xVals.add(new XAxisValue(i, i + "")); } // create a dataset and give it a type diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index d7df678923..cf01559137 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -23,6 +23,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.data.XAxisValue; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; @@ -273,9 +274,9 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); + ArrayList xVals = new ArrayList(); for (int i = 0; i < count; i++) { - xVals.add((i) + ""); + xVals.add(new XAxisValue(i, i + "")); } ArrayList yVals1 = new ArrayList(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index b4d5a5ce81..77c25bec29 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -101,11 +101,6 @@ private void setupChart(LineChart chart, LineData data, int color) { private LineData getData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add(mMonths[i % 12]); - } - ArrayList yVals = new ArrayList(); for (int i = 0; i < count; i++) { @@ -125,11 +120,8 @@ private LineData getData(int count, float range) { set1.setHighLightColor(Color.WHITE); set1.setDrawValues(false); - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets - // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); + LineData data = new LineData(set1); return data; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index e1c69086bc..7ede89d56d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java @@ -20,6 +20,7 @@ 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.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -147,23 +148,4 @@ private BarData generateData(int cnt) { BarData cd = new BarData(getMonths(), sets); return cd; } - - private ArrayList getMonths() { - - ArrayList m = new ArrayList(); - m.add("Jan"); - m.add("Feb"); - m.add("Mar"); - m.add("Apr"); - m.add("May"); - m.add("Jun"); - m.add("Jul"); - m.add("Aug"); - m.add("Sep"); - m.add("Okt"); - m.add("Nov"); - m.add("Dec"); - - return m; - } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java index ad242e99ec..7c90a9142f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java @@ -173,34 +173,4 @@ private PieData generateDataPie(int cnt) { PieData cd = new PieData(getQuarters(), d); return cd; } - - private ArrayList getQuarters() { - - ArrayList q = new ArrayList(); - q.add("1st Quarter"); - q.add("2nd Quarter"); - q.add("3rd Quarter"); - q.add("4th Quarter"); - - return q; - } - - private ArrayList getMonths() { - - ArrayList m = new ArrayList(); - m.add("Jan"); - m.add("Feb"); - m.add("Mar"); - m.add("Apr"); - m.add("May"); - m.add("Jun"); - m.add("Jul"); - m.add("Aug"); - m.add("Sep"); - m.add("Okt"); - m.add("Nov"); - m.add("Dec"); - - return m; - } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index 05e25fae64..cc385d6216 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -16,6 +16,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.data.XAxisValue; import com.github.mikephil.charting.data.filter.Approximator; import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.highlight.Highlight; @@ -191,9 +192,9 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvX.setText("" + (mSeekBarX.getProgress())); tvY.setText("" + (mSeekBarY.getProgress())); - ArrayList xVals = new ArrayList(); + ArrayList xVals = new ArrayList(); for (int i = 0; i < mSeekBarX.getProgress(); i++) { - xVals.add((i) + ""); + xVals.add(new XAxisValue(i, i+"")); } ArrayList dataSets = new ArrayList(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java index 9a195cb4b4..cec5d5669c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -13,6 +13,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.data.XAxisValue; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -92,9 +93,9 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); + ArrayList xVals = new ArrayList(); for (int i = 0; i < count; i++) { - xVals.add((i) + ""); + xVals.add(new XAxisValue(i, i + "")); } ArrayList yVals = new ArrayList(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index bab7dcb525..5d3e306c4d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -23,6 +23,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; @@ -191,10 +192,10 @@ private void setData(int count, float range) { yVals1.add(new Entry((float) (Math.random() * mult) + mult / 5, i)); } - ArrayList xVals = new ArrayList(); + ArrayList xVals = new ArrayList(); for (int i = 0; i < count + 1; i++) - xVals.add(mParties[i % mParties.length]); + xVals.add(new XAxisValue(mParties[i % mParties.length])); PieDataSet dataSet = new PieDataSet(yVals1, "Election Results"); dataSet.setSliceSpace(3f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index b475cd4abb..7ce05f371a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -23,6 +23,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.formatter.PercentFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IDataSet; @@ -191,10 +192,10 @@ private void setData(int count, float range) { yVals1.add(new Entry((float) (Math.random() * mult) + mult / 5, i)); } - ArrayList xVals = new ArrayList(); + ArrayList xVals = new ArrayList(); for (int i = 0; i < count + 1; i++) - xVals.add(mParties[i % mParties.length]); + xVals.add(new XAxisValue(mParties[i % mParties.length])); PieDataSet dataSet = new PieDataSet(yVals1, "Election Results"); dataSet.setSliceSpace(3f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index e637a37efe..6402e0e60d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -17,6 +17,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.RadarData; import com.github.mikephil.charting.data.RadarDataSet; +import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -200,10 +201,10 @@ public void setData() { yVals2.add(new Entry((float) (Math.random() * mult) + mult / 2, i)); } - ArrayList xVals = new ArrayList(); + ArrayList xVals = new ArrayList(); for (int i = 0; i < cnt; i++) - xVals.add(mParties[i % mParties.length]); + xVals.add(new XAxisValue(mParties[i % mParties.length])); RadarDataSet set1 = new RadarDataSet(yVals1, "Set 1"); set1.setColor(ColorTemplate.VORDIPLOM_COLORS[0]); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index a1f31d1043..6dda2d1d38 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -19,6 +19,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.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; @@ -139,8 +140,8 @@ private void addEntry() { } // add a new x-value first - data.addXValue(mMonths[data.getXValCount() % 12] + " " - + (year + data.getXValCount() / 12)); + data.addXValue(new XAxisValue(data.getXValCount() ,mMonths[data.getXValCount() % 12] + " " + + (year + data.getXValCount() / 12))); data.addEntry(new Entry((float) (Math.random() * 40) + 30f, set.getEntryCount()), 0); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index e716f7ff47..6e7d1c7443 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -21,6 +21,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; +import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.data.filter.Approximator; import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.highlight.Highlight; @@ -165,9 +166,9 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvX.setText("" + (mSeekBarX.getProgress() + 1)); tvY.setText("" + (mSeekBarY.getProgress())); - ArrayList xVals = new ArrayList(); + ArrayList xVals = new ArrayList(); for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - xVals.add((i) + ""); + xVals.add(new XAxisValue(i, i + "")); } ArrayList yVals1 = new ArrayList(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java index 61244bad2e..34f2140a04 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java @@ -10,6 +10,7 @@ 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.XAxisValue; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -51,12 +52,12 @@ protected void onCreate(Bundle savedInstanceState) { private void setData(int count) { ArrayList yVals = new ArrayList(); - ArrayList xVals = new ArrayList(); + ArrayList xVals = new ArrayList(); for (int i = 0; i < count; i++) { float val = (float) (Math.random() * count) + 15; yVals.add(new BarEntry((int) val, i)); - xVals.add((int) val + ""); + xVals.add(new XAxisValue(i, (int) val + "")); } BarDataSet set = new BarDataSet(yVals, "Data Set"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 77c6d1f23f..8d672bf853 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -20,6 +20,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.data.XAxisValue; import com.github.mikephil.charting.data.filter.Approximator; import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.highlight.Highlight; @@ -178,11 +179,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvX.setText("" + (mSeekBarX.getProgress() + 1)); tvY.setText("" + (mSeekBarY.getProgress())); - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - xVals.add(mMonths[i % mMonths.length]); - } - + ArrayList xVals = new ArrayList(); ArrayList yVals1 = new ArrayList(); for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { @@ -192,6 +189,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { float val3 = (float) (Math.random() * mult) + mult / 3; yVals1.add(new BarEntry(new float[] { val1, val2, val3 }, i)); + xVals.add(new XAxisValue(i, mMonths[i % mMonths.length])); } BarDataSet set1 = new BarDataSet(yVals1, "Statistics Vienna 2014"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index d25ee39f2b..4f9c908754 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -19,6 +19,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.data.XAxisValue; import com.github.mikephil.charting.data.filter.Approximator; import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; @@ -103,7 +104,12 @@ protected void onCreate(Bundle savedInstanceState) { "Men", "Women" }); - String []xVals = new String[]{"0-10", "10-20", "20-30", "30-40", "40-50", "50-60", "60-70", "70-80", "80-90", "90-100", "100+"}; + 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+"}; + List xVals = new ArrayList(); + + for(int i = 0; i < xLabels.length; i++) { + xVals.add(new XAxisValue(i, xLabels[i])); + } BarData data = new BarData(xVals, set); mChart.setData(data); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java index 58767447fe..a84c65d003 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java @@ -20,7 +20,8 @@ public class RealmDemoData extends RealmObject { private int xIndex; - private String xValue; + private String xAxisLabel; + private double xAxisPosition; private String someStringField; @@ -30,10 +31,11 @@ public RealmDemoData() { } - public RealmDemoData(float value, int xIndex, String xValue) { + public RealmDemoData(float value, int xIndex, double xAxisPosition, String xAxisLabel) { this.value = value; this.xIndex = xIndex; - this.xValue = xValue; + this.xAxisPosition = xAxisPosition; + this.xAxisLabel = xAxisLabel; } /** @@ -41,11 +43,13 @@ public RealmDemoData(float value, int xIndex, String xValue) { * * @param stackValues * @param xIndex - * @param xValue + * @param xAxisPosition + * @param xAxisLabel */ - public RealmDemoData(float[] stackValues, int xIndex, String xValue) { + public RealmDemoData(float[] stackValues, int xIndex, double xAxisPosition, String xAxisLabel) { this.xIndex = xIndex; - this.xValue = xValue; + this.xAxisPosition = xAxisPosition; + this.xAxisLabel = xAxisLabel; this.stackValues = new RealmList(); for (float val : stackValues) { @@ -61,15 +65,18 @@ public RealmDemoData(float[] stackValues, int xIndex, String xValue) { * @param open * @param close * @param xIndex + * @param xAxisPosition + * @param xAxisLabel */ - public RealmDemoData(float high, float low, float open, float close, int xIndex, String xValue) { + public RealmDemoData(float high, float low, float open, float close, int xIndex, double xAxisPosition, String xAxisLabel) { this.value = (high + low) / 2f; this.high = high; this.low = low; this.open = open; this.close = close; this.xIndex = xIndex; - this.xValue = xValue; + this.xAxisPosition = xAxisPosition; + this.xAxisLabel = xAxisLabel; } /** @@ -78,13 +85,15 @@ public RealmDemoData(float high, float low, float open, float close, int xIndex, * @param value * @param xIndex * @param bubbleSize - * @param xValue + * @param xAxisPosition + * @param xAxisLabel */ - public RealmDemoData(float value, int xIndex, float bubbleSize, String xValue) { + public RealmDemoData(float value, int xIndex, float bubbleSize, double xAxisPosition, String xAxisLabel) { this.value = value; this.xIndex = xIndex; this.bubbleSize = bubbleSize; - this.xValue = xValue; + this.xAxisPosition = xAxisPosition; + this.xAxisLabel = xAxisLabel; } public float getValue() { @@ -111,14 +120,6 @@ public void setxIndex(int xIndex) { this.xIndex = xIndex; } - public String getxValue() { - return xValue; - } - - public void setxValue(String xValue) { - this.xValue = xValue; - } - public float getOpen() { return open; } @@ -166,4 +167,20 @@ public String getSomeStringField() { public void setSomeStringField(String someStringField) { this.someStringField = someStringField; } + + public double getxAxisPosition() { + return xAxisPosition; + } + + public void setxAxisPosition(double xAxisPosition) { + this.xAxisPosition = xAxisPosition; + } + + public String getxAxisLabel() { + return xAxisLabel; + } + + public void setxAxisLabel(String xAxisLabel) { + this.xAxisLabel = xAxisLabel; + } } \ No newline at end of file diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java index 25d103e8ce..29bd091c6e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java @@ -21,6 +21,7 @@ import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; +import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; @@ -103,16 +104,14 @@ protected PieData generatePieData() { int count = 4; ArrayList entries1 = new ArrayList(); - ArrayList xVals = new ArrayList(); + ArrayList xVals = new ArrayList(); - xVals.add("Quarter 1"); - xVals.add("Quarter 2"); - xVals.add("Quarter 3"); - xVals.add("Quarter 4"); + xVals.add(new XAxisValue("Quarter 1")); + xVals.add(new XAxisValue("Quarter 2")); + xVals.add(new XAxisValue("Quarter 3")); + xVals.add(new XAxisValue("Quarter 4")); for(int i = 0; i < count; i++) { - xVals.add("entry" + (i+1)); - entries1.add(new Entry((float) (Math.random() * 60) + 40, i)); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java index 774aff1f75..25d9dd450f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java @@ -3,8 +3,12 @@ import android.support.v4.app.FragmentActivity; +import com.github.mikephil.charting.data.XAxisValue; import com.xxmassdeveloper.mpchartexample.R; +import java.util.ArrayList; +import java.util.List; + /** * Baseclass of all Activities of the Demo Application. * @@ -23,6 +27,52 @@ public abstract class DemoBase extends FragmentActivity { "Party Y", "Party Z" }; + + protected ArrayList getMonths() { + + ArrayList m = new ArrayList(); + m.add(new XAxisValue(0, "Jan")); + m.add(new XAxisValue(1, "Feb")); + m.add(new XAxisValue(2, "Mar")); + m.add(new XAxisValue(3, "Apr")); + m.add(new XAxisValue(4, "May")); + m.add(new XAxisValue(5, "Jun")); + m.add(new XAxisValue(6, "Jul")); + m.add(new XAxisValue(7, "Aug")); + m.add(new XAxisValue(8, "Sep")); + m.add(new XAxisValue(9, "Okt")); + m.add(new XAxisValue(10, "Nov")); + m.add(new XAxisValue(11, "Dec")); + + return m; + } + + + protected ArrayList getQuarters() { + + ArrayList q = new ArrayList(); + q.add(new XAxisValue(0, "Quarter 1")); + q.add(new XAxisValue(1, "Quarter 2")); + q.add(new XAxisValue(2, "Quarter 3")); + q.add(new XAxisValue(3, "Quarter 4")); + + return q; + } + + protected List getYears() { + ArrayList years = new ArrayList(); + + years.add(new XAxisValue(0, "2013")); + years.add(new XAxisValue(1, "2014")); + years.add(new XAxisValue(2, "2015")); + years.add(new XAxisValue(3, "2016")); + years.add(new XAxisValue(4, "2017")); + years.add(new XAxisValue(5, "2018")); + years.add(new XAxisValue(6, "2019")); + + return years; + } + @Override public void onBackPressed() { super.onBackPressed(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java index 09bcc59e1e..1f51281e35 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -113,7 +113,7 @@ protected void writeToDB(int objectCount) { float value = 40f + (float) (Math.random() * 60f); - RealmDemoData d = new RealmDemoData(value, i, "" + i); + RealmDemoData d = new RealmDemoData(value, i, i, "" + i); mRealm.copyToRealm(d); } @@ -132,7 +132,7 @@ protected void writeToDBStack(int objectCount) { float val2 = 34f + (float) (Math.random() * 12.0f); float[] stack = new float[]{val1, val2, 100 - val1 - val2}; - RealmDemoData d = new RealmDemoData(stack, i, "" + i); + RealmDemoData d = new RealmDemoData(stack, i, i, "" + i); mRealm.copyToRealm(d); } @@ -159,7 +159,7 @@ protected void writeToDBCandle(int objectCount) { boolean even = i % 2 == 0; RealmDemoData d = new RealmDemoData(val + high, val - low, even ? val + open : val - open, - even ? val - close : val + close, i, i + ""); + even ? val - close : val + close, i, i, i + ""); mRealm.copyToRealm(d); } @@ -201,7 +201,7 @@ protected void writeToDBPie() { String[] xValues = new String[]{ "iOS", "Android", "WP 10", "BlackBerry", "Other"}; for (int i = 0; i < values.length; i++) { - RealmDemoData d = new RealmDemoData(values[i], i, xValues[i]); + RealmDemoData d = new RealmDemoData(values[i], i, i, xValues[i]); mRealm.copyToRealm(d); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java index 4387035191..ecde642f4f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java @@ -58,7 +58,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RealmBarData data = new RealmBarData(result, "xValue", dataSets); + RealmBarData data = new RealmBarData(result, "xAxisPosition", "xAxisLabel", dataSets); styleData(data); // set data diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java index b98ca0b746..2f23561622 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java @@ -61,7 +61,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RealmBubbleData data = new RealmBubbleData(result, "xValue", dataSets); + RealmBubbleData data = new RealmBubbleData(result, "xAxisPosition", "xAxisLabel", dataSets); styleData(data); // set data diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java index 70e324d4dd..97a0036567 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java @@ -67,7 +67,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RealmCandleData data = new RealmCandleData(result, "xValue", dataSets); + RealmCandleData data = new RealmCandleData(result, "xAxisPosition", "xAxisLabel", dataSets); styleData(data); // set data diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java index 7a2ef39c18..40bbf10910 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java @@ -63,7 +63,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RealmBarData data = new RealmBarData(result, "xValue", dataSets); + RealmBarData data = new RealmBarData(result, "xAxisPosition", "xAxisLabel", dataSets); styleData(data); data.setValueTextColor(Color.WHITE); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java index 057662d057..c2f8803b85 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java @@ -67,7 +67,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RealmLineData data = new RealmLineData(result, "xValue", dataSets); + RealmLineData data = new RealmLineData(result, "xAxisPosition", "xAxisLabel", dataSets); styleData(data); // set data diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java index de6767d145..489399bc9e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java @@ -60,7 +60,7 @@ private void setData() { set.setSliceSpace(2); // create a data object with the dataset list - RealmPieData data = new RealmPieData(result, "xValue", set); + RealmPieData data = new RealmPieData(result, "xAxisPosition", "xAxisLabel", set); styleData(data); data.setValueTextColor(Color.WHITE); data.setValueTextSize(12f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java index 7aae4ae377..3e1c688f6c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java @@ -67,7 +67,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RadarData data = new RadarData(new String[] {"2013", "2014", "2015", "2016", "2017", "2018", "2019"}, dataSets); + RadarData data = new RadarData(getYears(), dataSets); styleData(data); // set data diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java index b28bf137a8..135667a4bf 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java @@ -63,7 +63,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RealmScatterData data = new RealmScatterData(result, "xValue", dataSets); + RealmScatterData data = new RealmScatterData(result, "xAxisPosition", "xAxisLabel", dataSets); styleData(data); // set data diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java index 7066d8167b..2d5e3a90a5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java @@ -89,7 +89,7 @@ private void setData() { ArrayList dataSets = new ArrayList(); dataSets.add(lineDataSet); - RealmLineData lineData = new RealmLineData(results, "playerName", dataSets); + RealmLineData lineData = new RealmLineData(results, "scoreNr", "playerName", dataSets); styleData(lineData); // set data @@ -105,7 +105,7 @@ private void setData() { ArrayList barDataSets = new ArrayList(); barDataSets.add(barDataSet); - RealmBarData barData = new RealmBarData(results, "playerName", barDataSets); + RealmBarData barData = new RealmBarData(results, "scoreNr", "playerName", barDataSets); styleData(barData); barChart.setData(barData); diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarData.java b/MPChartLib/src/com/github/mikephil/charting/data/BarData.java index a0528153be..bc87fcc24d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/BarData.java @@ -4,6 +4,7 @@ import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -25,6 +26,10 @@ public BarData() { super(); } + public BarData(IBarDataSet... dataSets) { + super(dataSets); + } + public BarData(List xVals) { super(xVals); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java b/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java index 801f2fc87d..c16c2c72a1 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java @@ -16,6 +16,10 @@ public abstract class BarLineScatterCandleBubbleData xVals) { super(xVals); diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java index eb39dc794b..d5ea74e835 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java @@ -65,6 +65,11 @@ public ChartData() { mDataSets = new ArrayList(); } + public ChartData(T... dataSets) { + mDataSets = Arrays.asList(dataSets); + init(); + } + /** * Constructor for only x-values. This constructor can be used for setting * up an empty chart without data. @@ -821,12 +826,12 @@ public T getFirstRight() { * * @return */ - public static List generateXVals(int from, int to) { + public static List generateXVals(int from, int to) { - List xvals = new ArrayList(); + List xvals = new ArrayList(); for (int i = from; i < to; i++) { - xvals.add("" + i); + xvals.add(new XAxisValue(i, i + "")); } return xvals; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineData.java b/MPChartLib/src/com/github/mikephil/charting/data/LineData.java index 53ba9a28d5..b4ef3b1d53 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/LineData.java @@ -17,6 +17,10 @@ public LineData() { super(); } + public LineData(ILineDataSet... dataSets) { + super(dataSets); + } + public LineData(List xVals) { super(xVals); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/XAxisValue.java b/MPChartLib/src/com/github/mikephil/charting/data/XAxisValue.java index a14f7ab41f..eef5f638e5 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/XAxisValue.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/XAxisValue.java @@ -15,6 +15,15 @@ public class XAxisValue { */ private double mPosition; + /** + * Constructor only with label. This is relevant for pie and radarchart. + * + * @param label the x-axis label of this value + */ + public XAxisValue(String label) { + this.mLabel = label; + } + /** * Constructor. * From a599fb79f965b8b3cf06467cc2d832126bcda76f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 5 Apr 2016 11:04:57 +0200 Subject: [PATCH 069/606] Implement min/max calculation for x values --- .../mikephil/charting/data/BarDataSet.java | 19 +++- .../mikephil/charting/data/BubbleDataSet.java | 34 ++---- .../mikephil/charting/data/CandleDataSet.java | 23 ++-- .../mikephil/charting/data/ChartData.java | 47 +++++++- .../mikephil/charting/data/DataSet.java | 107 +++++++++++------- .../mikephil/charting/data/LineDataSet.java | 5 +- .../mikephil/charting/data/PieDataSet.java | 4 +- .../mikephil/charting/data/RadarDataSet.java | 4 +- .../charting/data/ScatterDataSet.java | 4 +- .../data/realm/base/RealmBaseDataSet.java | 29 +++++ .../realm/implementation/RealmBarDataSet.java | 9 ++ .../implementation/RealmBubbleDataSet.java | 12 -- .../implementation/RealmCandleDataSet.java | 9 ++ .../interfaces/datasets/IBubbleDataSet.java | 4 - .../interfaces/datasets/IDataSet.java | 14 +++ 15 files changed, 220 insertions(+), 104 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java index fb1dccccf8..d71cb7d8cd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java @@ -57,8 +57,8 @@ public DataSet copy() { List yVals = new ArrayList(); - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(((BarEntry) mYVals.get(i)).copy()); + for (int i = 0; i < mValues.size(); i++) { + yVals.add(((BarEntry) mValues.get(i)).copy()); } BarDataSet copied = new BarDataSet(yVals, getLabel()); @@ -110,10 +110,10 @@ private void calcStackSize(List yVals) { @Override public void calcMinMax(int start, int end) { - if (mYVals == null) + if (mValues == null) return; - final int yValCount = mYVals.size(); + final int yValCount = mValues.size(); if (yValCount == 0) return; @@ -128,9 +128,12 @@ public void calcMinMax(int start, int end) { mYMin = Float.MAX_VALUE; mYMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + for (int i = start; i <= endValue; i++) { - BarEntry e = mYVals.get(i); + BarEntry e = mValues.get(i); if (e != null && !Float.isNaN(e.getVal())) { @@ -149,6 +152,12 @@ public void calcMinMax(int start, int end) { if (e.getPositiveSum() > mYMax) mYMax = e.getPositiveSum(); } + + if (e.getXIndex() < mXMin) + mXMin = e.getXIndex(); + + if (e.getXIndex() > mXMax) + mXMax = e.getXIndex(); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java index 5672ecb0bb..5ed9b28810 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java @@ -1,8 +1,6 @@ package com.github.mikephil.charting.data; -import android.graphics.Color; - import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; import com.github.mikephil.charting.utils.Utils; @@ -11,10 +9,6 @@ public class BubbleDataSet extends BarLineScatterCandleBubbleDataSet implements IBubbleDataSet { - // NOTE: Do not initialize these, as the calculate is called by the super, - // and the initializers are called after that and can reset the values - protected float mXMax; - protected float mXMin; protected float mMaxSize; private float mHighlightCircleWidth = 2.5f; @@ -36,27 +30,27 @@ public float getHighlightCircleWidth() { @Override public void calcMinMax(int start, int end) { - if (mYVals == null) + if (mValues == null) return; - if (mYVals.size() == 0) + if (mValues.size() == 0) return; int endValue; - if (end == 0 || end >= mYVals.size()) - endValue = mYVals.size() - 1; + if (end == 0 || end >= mValues.size()) + endValue = mValues.size() - 1; else endValue = end; - mYMin = yMin(mYVals.get(start)); - mYMax = yMax(mYVals.get(start)); + mYMin = yMin(mValues.get(start)); + mYMax = yMax(mValues.get(start)); // need chart width to guess this properly for (int i = start; i <= endValue; i++) { - final BubbleEntry entry = mYVals.get(i); + final BubbleEntry entry = mValues.get(i); float ymin = yMin(entry); float ymax = yMax(entry); @@ -93,8 +87,8 @@ public DataSet copy() { List yVals = new ArrayList(); - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); + for (int i = 0; i < mValues.size(); i++) { + yVals.add(mValues.get(i).copy()); } BubbleDataSet copied = new BubbleDataSet(yVals, getLabel()); @@ -104,16 +98,6 @@ public DataSet copy() { return copied; } - @Override - public float getXMax() { - return mXMax; - } - - @Override - public float getXMin() { - return mXMin; - } - @Override public float getMaxSize() { return mMaxSize; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java index 939014105c..b434d76fa2 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java @@ -82,8 +82,8 @@ public DataSet copy() { List yVals = new ArrayList(); - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(((CandleEntry) mYVals.get(i)).copy()); + for (int i = 0; i < mValues.size(); i++) { + yVals.add(((CandleEntry) mValues.get(i)).copy()); } CandleDataSet copied = new CandleDataSet(yVals, getLabel()); @@ -103,31 +103,40 @@ public DataSet copy() { public void calcMinMax(int start, int end) { // super.calculate(); - if (mYVals == null) + if (mValues == null) return; - if (mYVals.size() == 0) + if (mValues.size() == 0) return; int endValue; - if (end == 0 || end >= mYVals.size()) - endValue = mYVals.size() - 1; + if (end == 0 || end >= mValues.size()) + endValue = mValues.size() - 1; else endValue = end; mYMin = Float.MAX_VALUE; mYMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + for (int i = start; i <= endValue; i++) { - CandleEntry e = mYVals.get(i); + CandleEntry e = mValues.get(i); if (e.getLow() < mYMin) mYMin = e.getLow(); if (e.getHigh() > mYMax) mYMax = e.getHigh(); + + if (e.getXIndex() < mXMin) + mXMin = e.getXIndex(); + + if (e.getXIndex() > mXMax) + mXMax = e.getXIndex(); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java index d5ea74e835..232ac837c1 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java @@ -22,15 +22,25 @@ public abstract class ChartData> { /** - * maximum y-value in the y-value array across all axes + * maximum y-value in the value array across all axes */ protected float mYMax = 0.0f; /** - * the minimum y-value in the y-value array across all axes + * the minimum y-value in the value array across all axes */ protected float mYMin = 0.0f; + /** + * maximum x-value in the value array + */ + protected float mXMax = 0f; + + /** + * minimum x-value in the value array + */ + protected float mXMin = 0f; + protected float mLeftAxisMax = 0.0f; protected float mLeftAxisMin = 0.0f; @@ -200,7 +210,7 @@ public void notifyDataChanged() { } /** - * calc minimum and maximum y value over all datasets + * calc minimum and maximum values (both x and y) over all DataSets */ public void calcMinMax(int start, int end) { @@ -208,11 +218,18 @@ public void calcMinMax(int start, int end) { mYMax = 0f; mYMin = 0f; + + mXMax = 0f; + mXMin = 0f; + } else { mYMin = Float.MAX_VALUE; mYMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + for (int i = 0; i < mDataSets.size(); i++) { IDataSet set = mDataSets.get(i); @@ -223,6 +240,12 @@ public void calcMinMax(int start, int end) { if (set.getYMax() > mYMax) mYMax = set.getYMax(); + + if (set.getXMin() < mXMin) + mXMin = set.getXMin(); + + if (set.getXMax() > mXMax) + mXMax = set.getXMax(); } if (mYMin == Float.MAX_VALUE) { @@ -352,6 +375,24 @@ public float getYMax(AxisDependency axis) { return mRightAxisMax; } + /** + * Returns the minimum x-value this data object contains. + * + * @return + */ + public float getXMin() { + return mXMin; + } + + /** + * Returns the maximum x-value this data object contains. + * + * @return + */ + public float getXMax() { + return mXMax; + } + /** * returns the maximum length (in characters) across all values in the * x-vals array diff --git a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java index 29f5f69c28..b2fab18a7b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java @@ -17,18 +17,28 @@ public abstract class DataSet extends BaseDataSet { /** * the entries that this dataset represents / holds together */ - protected List mYVals = null; + protected List mValues = null; /** - * maximum y-value in the y-value array + * maximum y-value in the value array */ protected float mYMax = 0.0f; /** - * the minimum y-value in the y-value array + * minimum y-value in the value array */ protected float mYMin = 0.0f; + /** + * maximum x-value in the value array + */ + protected float mXMax = 0.0f; + + /** + * minimum x-value in the value array + */ + protected float mXMin = 0.0f; + /** * Creates a new DataSet object with the given values it represents. Also, a @@ -40,21 +50,21 @@ public abstract class DataSet extends BaseDataSet { */ public DataSet(List yVals, String label) { super(label); - this.mYVals = yVals; + this.mValues = yVals; - if (mYVals == null) - mYVals = new ArrayList(); + if (mValues == null) + mValues = new ArrayList(); - calcMinMax(0, mYVals.size()); + calcMinMax(0, mValues.size()); } @Override public void calcMinMax(int start, int end) { - if (mYVals == null) + if (mValues == null) return; - final int yValCount = mYVals.size(); + final int yValCount = mValues.size(); if (yValCount == 0) return; @@ -69,9 +79,12 @@ public void calcMinMax(int start, int end) { mYMin = Float.MAX_VALUE; mYMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + for (int i = start; i <= endValue; i++) { - T e = mYVals.get(i); + T e = mValues.get(i); if (e != null && !Float.isNaN(e.getVal())) { @@ -80,6 +93,12 @@ public void calcMinMax(int start, int end) { if (e.getVal() > mYMax) mYMax = e.getVal(); + + if (e.getXIndex() < mXMin) + mXMin = e.getXIndex(); + + if (e.getXIndex() > mXMax) + mXMax = e.getXIndex(); } } @@ -91,7 +110,7 @@ public void calcMinMax(int start, int end) { @Override public int getEntryCount() { - return mYVals.size(); + return mValues.size(); } /** @@ -100,7 +119,7 @@ public int getEntryCount() { * @return */ public List getYVals() { - return mYVals; + return mValues; } /** @@ -114,8 +133,8 @@ public List getYVals() { public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append(toSimpleString()); - for (int i = 0; i < mYVals.size(); i++) { - buffer.append(mYVals.get(i).toString() + " "); + for (int i = 0; i < mValues.size(); i++) { + buffer.append(mValues.get(i).toString() + " "); } return buffer.toString(); } @@ -128,7 +147,7 @@ public String toString() { */ public String toSimpleString() { StringBuffer buffer = new StringBuffer(); - buffer.append("DataSet, label: " + (getLabel() == null ? "" : getLabel()) + ", entries: " + mYVals.size() + "\n"); + buffer.append("DataSet, label: " + (getLabel() == null ? "" : getLabel()) + ", entries: " + mValues.size() + "\n"); return buffer.toString(); } @@ -142,6 +161,16 @@ public float getYMax() { return mYMax; } + @Override + public float getXMin() { + return mXMin; + } + + @Override + public float getXMax() { + return mXMax; + } + @Override public void addEntryOrdered(T e) { @@ -150,11 +179,11 @@ public void addEntryOrdered(T e) { float val = e.getVal(); - if (mYVals == null) { - mYVals = new ArrayList(); + if (mValues == null) { + mValues = new ArrayList(); } - if (mYVals.size() == 0) { + if (mValues.size() == 0) { mYMax = val; mYMin = val; } else { @@ -164,18 +193,18 @@ public void addEntryOrdered(T e) { mYMin = val; } - if (mYVals.size() > 0 && mYVals.get(mYVals.size() - 1).getXIndex() > e.getXIndex()) { + if (mValues.size() > 0 && mValues.get(mValues.size() - 1).getXIndex() > e.getXIndex()) { int closestIndex = getEntryIndex(e.getXIndex(), Rounding.UP); - mYVals.add(closestIndex, e); + mValues.add(closestIndex, e); return; } - mYVals.add(e); + mValues.add(e); } @Override public void clear() { - mYVals.clear(); + mValues.clear(); notifyDataSetChanged(); } @@ -213,14 +242,14 @@ public boolean removeEntry(T e) { if (e == null) return false; - if (mYVals == null) + if (mValues == null) return false; // remove the entry - boolean removed = mYVals.remove(e); + boolean removed = mValues.remove(e); if (removed) { - calcMinMax(0, mYVals.size()); + calcMinMax(0, mValues.size()); } return removed; @@ -228,7 +257,7 @@ public boolean removeEntry(T e) { @Override public int getEntryIndex(Entry e) { - return mYVals.indexOf(e); + return mValues.indexOf(e); } @Override @@ -236,7 +265,7 @@ public T getEntryForXIndex(int xIndex, Rounding rounding) { int index = getEntryIndex(xIndex, rounding); if (index > -1) - return mYVals.get(index); + return mValues.get(index); return null; } @@ -247,27 +276,27 @@ public T getEntryForXIndex(int xIndex) { @Override public T getEntryForIndex(int index) { - return mYVals.get(index); + return mValues.get(index); } @Override public int getEntryIndex(int xIndex, Rounding rounding) { int low = 0; - int high = mYVals.size() - 1; + int high = mValues.size() - 1; int closest = -1; while (low <= high) { int m = (high + low) / 2; - if (xIndex == mYVals.get(m).getXIndex()) { - while (m > 0 && mYVals.get(m - 1).getXIndex() == xIndex) + if (xIndex == mValues.get(m).getXIndex()) { + while (m > 0 && mValues.get(m - 1).getXIndex() == xIndex) m--; return m; } - if (xIndex > mYVals.get(m).getXIndex()) + if (xIndex > mValues.get(m).getXIndex()) low = m + 1; else high = m - 1; @@ -276,9 +305,9 @@ public int getEntryIndex(int xIndex, Rounding rounding) { } if (closest != -1) { - int closestXIndex = mYVals.get(closest).getXIndex(); + int closestXIndex = mValues.get(closest).getXIndex(); if (rounding == Rounding.UP) { - if (closestXIndex < xIndex && closest < mYVals.size() - 1) { + if (closestXIndex < xIndex && closest < mValues.size() - 1) { ++closest; } } else if (rounding == Rounding.DOWN) { @@ -315,19 +344,19 @@ public List getEntriesForXIndex(int xIndex) { List entries = new ArrayList(); int low = 0; - int high = mYVals.size() - 1; + int high = mValues.size() - 1; while (low <= high) { int m = (high + low) / 2; - T entry = mYVals.get(m); + T entry = mValues.get(m); if (xIndex == entry.getXIndex()) { - while (m > 0 && mYVals.get(m - 1).getXIndex() == xIndex) + while (m > 0 && mValues.get(m - 1).getXIndex() == xIndex) m--; - high = mYVals.size(); + high = mValues.size(); for (; m < high; m++) { - entry = mYVals.get(m); + entry = mValues.get(m); if (entry.getXIndex() == xIndex) { entries.add(entry); } else { diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java index 7d9da1ac55..e07c8cbd5a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java @@ -4,7 +4,6 @@ import android.content.Context; import android.graphics.Color; import android.graphics.DashPathEffect; -import android.graphics.drawable.Drawable; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -66,8 +65,8 @@ public DataSet copy() { List yVals = new ArrayList(); - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); + for (int i = 0; i < mValues.size(); i++) { + yVals.add(mValues.get(i).copy()); } LineDataSet copied = new LineDataSet(yVals, getLabel()); diff --git a/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java index 3c2b9ba589..5e1f0b8732 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java @@ -34,8 +34,8 @@ public DataSet copy() { List yVals = new ArrayList(); - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); + for (int i = 0; i < mValues.size(); i++) { + yVals.add(mValues.get(i).copy()); } PieDataSet copied = new PieDataSet(yVals, getLabel()); diff --git a/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java index 61ea26d5a7..baa81340e1 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java @@ -118,8 +118,8 @@ public DataSet copy() { List yVals = new ArrayList(); - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); + for (int i = 0; i < mValues.size(); i++) { + yVals.add(mValues.get(i).copy()); } RadarDataSet copied = new RadarDataSet(yVals, getLabel()); diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java index 199a20638b..4c9cbe8c6f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java @@ -48,8 +48,8 @@ public DataSet copy() { List yVals = new ArrayList(); - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); + for (int i = 0; i < mValues.size(); i++) { + yVals.add(mValues.get(i).copy()); } ScatterDataSet copied = new ScatterDataSet(yVals, getLabel()); diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java index 02b769e8ac..5df853bf5c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java @@ -36,6 +36,16 @@ public abstract class RealmBaseDataSet e */ protected float mYMin = 0.0f; + /** + * maximum x-value in the value array + */ + protected float mXMax = 0.0f; + + /** + * minimum x-value in the value array + */ + protected float mXMin = 0.0f; + /** * fieldname of the column that contains the y-values of this dataset */ @@ -89,6 +99,16 @@ public float getYMax() { return mYMax; } + @Override + public float getXMin() { + return mXMin; + } + + @Override + public float getXMax() { + return mXMax; + } + @Override public int getEntryCount() { return mValues.size(); @@ -115,6 +135,9 @@ public void calcMinMax(int start, int end) { mYMin = Float.MAX_VALUE; mYMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + for (int i = start; i <= endValue; i++) { S e = mValues.get(i); @@ -126,6 +149,12 @@ public void calcMinMax(int start, int end) { if (e.getVal() > mYMax) mYMax = e.getVal(); + + if (e.getXIndex() < mXMin) + mXMin = e.getXIndex(); + + if (e.getXIndex() > mXMax) + mXMax = e.getXIndex(); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java index 1b78c278cb..afbe57ca2b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java @@ -122,6 +122,9 @@ public void calcMinMax(int start, int end) { mYMin = Float.MAX_VALUE; mYMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + for (int i = start; i <= endValue; i++) { BarEntry e = mValues.get(i); @@ -143,6 +146,12 @@ public void calcMinMax(int start, int end) { if (e.getPositiveSum() > mYMax) mYMax = e.getPositiveSum(); } + + if (e.getXIndex() < mXMin) + mXMin = e.getXIndex(); + + if (e.getXIndex() > mXMax) + mXMax = e.getXIndex(); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java index 7a8ae13f08..ceed5aaf47 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java @@ -16,8 +16,6 @@ public class RealmBubbleDataSet extends RealmBarLineScatt private String mSizeField; - protected float mXMax; - protected float mXMin; protected float mMaxSize; private float mHighlightCircleWidth = 2.5f; @@ -131,16 +129,6 @@ public void calcMinMax(int start, int end) { } } - @Override - public float getXMax() { - return mXMax; - } - - @Override - public float getXMin() { - return mXMin; - } - @Override public float getMaxSize() { return mMaxSize; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java index def4e4e935..6921904f95 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java @@ -163,6 +163,9 @@ public void calcMinMax(int start, int end) { mYMin = Float.MAX_VALUE; mYMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + for (int i = start; i <= endValue; i++) { CandleEntry e = mValues.get(i); @@ -172,6 +175,12 @@ public void calcMinMax(int start, int end) { if (e.getHigh() > mYMax) mYMax = e.getHigh(); + + if (e.getXIndex() < mXMin) + mXMin = e.getXIndex(); + + if (e.getXIndex() > mXMax) + mXMax = e.getXIndex(); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java index 2764b56d1d..6c0afc1114 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java @@ -15,10 +15,6 @@ public interface IBubbleDataSet extends IBarLineScatterCandleBubbleDataSet { */ float getYMax(); + /** + * returns the minimum x-value this DataSet holds + * + * @return + */ + float getXMin(); + + /** + * returns the maximum x-value this DataSet holds + * + * @return + */ + float getXMax(); + /** * Returns the number of y-values this DataSet represents -> the size of the y-values array * -> yvals.size() From 34a6e3b5eb08d1e5f1c347404251d9b5a6b8504c Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 5 Apr 2016 11:14:25 +0200 Subject: [PATCH 070/606] Implement axis calculate method --- .../charting/components/AxisBase.java | 9 +++++++++ .../mikephil/charting/components/XAxis.java | 20 +++++++++++++++++++ .../mikephil/charting/components/YAxis.java | 8 +------- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java index caac60b999..9b43423128 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java @@ -396,4 +396,13 @@ public void setAxisMaxValue(float max) { mCustomAxisMax = true; mAxisMaximum = max; } + + /** + * Calculates the minimum / maximum and range values of the axis with the given + * minimum and maximum values from the chart data. + * + * @param dataMin the min value according to chart data + * @param dataMax the max value according to chart data + */ + public abstract void calculate(float dataMin, float dataMax); } diff --git a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java index 18b6e55141..ba7bed8955 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java @@ -255,4 +255,24 @@ public String getLongestLabel() { return longest; } + + @Override + 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; + + // temporary range (before calculations) + float range = Math.abs(max - min); + + // in case all values are equal + if (range == 0f) { + max = max + 1f; + min = min - 1f; + } + + // actual range + this.mAxisRange = Math.abs(max - min); + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java index 0846a670a7..44427c28b1 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java @@ -566,13 +566,7 @@ public boolean needsOffset() { return false; } - /** - * Calculates the minimum, maximum, granularity and range values of the YAxis with the given - * minimum and maximum values from the chart data. - * - * @param dataMin the y-min value according to chart data - * @param dataMax the y-max value according to chart data - */ + @Override public void calculate(float dataMin, float dataMax) { // if custom, use value as is, else use data value From d0ce8e7d5a58b9241bf908660ff4240b31af19d7 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 5 Apr 2016 11:16:07 +0200 Subject: [PATCH 071/606] Improve axis calculate method --- .../charting/components/AxisBase.java | 19 +++++++++++++++++- .../mikephil/charting/components/XAxis.java | 20 ------------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java index 9b43423128..7ebf9b5cec 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java @@ -404,5 +404,22 @@ public void setAxisMaxValue(float max) { * @param dataMin the min value according to chart data * @param dataMax the max value according to chart data */ - public abstract void calculate(float dataMin, float dataMax); + 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; + + // temporary range (before calculations) + float range = Math.abs(max - min); + + // in case all values are equal + if (range == 0f) { + max = max + 1f; + min = min - 1f; + } + + // actual range + this.mAxisRange = Math.abs(max - min); + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java index ba7bed8955..18b6e55141 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java @@ -255,24 +255,4 @@ public String getLongestLabel() { return longest; } - - @Override - 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; - - // temporary range (before calculations) - float range = Math.abs(max - min); - - // in case all values are equal - if (range == 0f) { - max = max + 1f; - min = min - 1f; - } - - // actual range - this.mAxisRange = Math.abs(max - min); - } } From 0c8cc700b3b8002fd1dc73d192a96f66ba29bfe1 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 5 Apr 2016 22:27:25 +0200 Subject: [PATCH 072/606] Further improvements and timechart example --- MPChartExample/AndroidManifest.xml | 1 + .../mpchartexample/LineChartTime.java | 316 ++++++++++++++++++ .../notimportant/MainActivity.java | 12 + .../charting/charts/BarLineChartBase.java | 8 +- .../mikephil/charting/charts/LineChart.java | 8 - .../charting/components/AxisBase.java | 3 + 6 files changed, 337 insertions(+), 11 deletions(-) create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index 9efb9639b9..f7ee61b540 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -26,6 +26,7 @@ + diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java new file mode 100644 index 0000000000..8d0a7a49e4 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -0,0 +1,316 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.graphics.Color; +import android.graphics.Typeface; +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.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; +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.data.XAxisValue; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class LineChartTime extends DemoBase implements OnSeekBarChangeListener { + + private LineChart 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_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); + + mSeekBarX.setProgress(45); + mSeekBarY.setProgress(100); + + mSeekBarY.setOnSeekBarChangeListener(this); + mSeekBarX.setOnSeekBarChangeListener(this); + + mChart = (LineChart) findViewById(R.id.chart1); + + // no description text + mChart.setDescription(""); + mChart.setNoDataTextDescription("You need to provide data for the chart."); + + // enable touch gestures + mChart.setTouchEnabled(true); + + mChart.setDragDecelerationFrictionCoef(0.9f); + + // enable scaling and dragging + mChart.setDragEnabled(true); + mChart.setScaleEnabled(true); + mChart.setDrawGridBackground(false); + mChart.setHighlightPerDragEnabled(true); + + // if disabled, scaling can be done on x- and y-axis separately + mChart.setPinchZoom(true); + + // set an alternative background color + mChart.setBackgroundColor(Color.LTGRAY); + + // add data + setData(20, 30); + + mChart.animateX(2500); + + Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + + // get the legend (only possible after setting data) + Legend l = mChart.getLegend(); + + // modify the legend ... + // l.setPosition(LegendPosition.LEFT_OF_CHART); + l.setForm(LegendForm.LINE); + l.setTypeface(tf); + l.setTextSize(11f); + l.setTextColor(Color.WHITE); + l.setPosition(LegendPosition.BELOW_CHART_LEFT); +// l.setYOffset(11f); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setTypeface(tf); + xAxis.setTextSize(12f); + xAxis.setTextColor(Color.WHITE); + xAxis.setDrawAxisLine(false); + xAxis.setSpaceBetweenLabels(1); + + YAxis leftAxis = mChart.getAxisLeft(); + leftAxis.setTypeface(tf); + leftAxis.setTextColor(ColorTemplate.getHoloBlue()); + leftAxis.setAxisMaxValue(200f); + leftAxis.setAxisMinValue(0f); + leftAxis.setDrawGridLines(true); + leftAxis.setGranularityEnabled(true); + + YAxis rightAxis = mChart.getAxisRight(); + rightAxis.setEnabled(false); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.line, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.actionToggleValues: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + mChart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if (mChart.getData() != null) { + mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); + mChart.invalidate(); + } + break; + } + case R.id.actionToggleFilled: { + + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawFilledEnabled()) + set.setDrawFilled(false); + else + set.setDrawFilled(true); + } + mChart.invalidate(); + break; + } + case R.id.actionToggleCircles: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawCirclesEnabled()) + set.setDrawCircles(false); + else + set.setDrawCircles(true); + } + mChart.invalidate(); + break; + } + case R.id.actionToggleCubic: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawCubicEnabled()) + set.setDrawCubic(false); + else + set.setDrawCubic(true); + } + mChart.invalidate(); + break; + } + case R.id.actionToggleStepped: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawSteppedEnabled()) + set.setDrawStepped(false); + else + set.setDrawStepped(true); + } + 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.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.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()) + 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()); + + // redraw + mChart.invalidate(); + } + + private void setData(int count, float range) { + + ArrayList xVals = new ArrayList(); + for (int i = 0; i < count; i++) { + xVals.add(new XAxisValue(i, i + "")); + } + + ArrayList yVals1 = new ArrayList(); + + for (int i = 0; i < count; i++) { + float mult = range / 2f; + float val = (float) (Math.random() * mult) + 50;// + (float) + // ((mult * + // 0.1) / 10); + yVals1.add(new Entry(val, i)); + } + + // create a dataset and give it a type + LineDataSet 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); + + // create a data object with the datasets + LineData data = new LineData(xVals, set1); + data.setValueTextColor(Color.WHITE); + data.setValueTextSize(9f); + + // set data + mChart.setData(data); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index a3569dcaaf..34362f1e4e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -29,6 +29,7 @@ import com.xxmassdeveloper.mpchartexample.LineChartActivity1; import com.xxmassdeveloper.mpchartexample.LineChartActivity2; import com.xxmassdeveloper.mpchartexample.LineChartActivityColored; +import com.xxmassdeveloper.mpchartexample.LineChartTime; import com.xxmassdeveloper.mpchartexample.ListViewBarChartActivity; import com.xxmassdeveloper.mpchartexample.ListViewMultiChartActivity; import com.xxmassdeveloper.mpchartexample.MultiLineChartActivity; @@ -136,6 +137,12 @@ protected void onCreate(Bundle savedInstanceState) { realm.isNew = true; objects.add(realm); + ContentItem time = new ContentItem( + "Time Chart", + "Simple demonstration of a time-chart."); + time.isNew = true; + objects.add(time); + MyAdapter adapter = new MyAdapter(this, objects); ListView lv = (ListView) findViewById(R.id.listView1); @@ -266,6 +273,11 @@ public void onItemClick(AdapterView av, View v, int pos, long arg3) { i = new Intent(this, RealmMainActivity.class); startActivity(i); break; + case 29: + i = new Intent(this, LineChartTime.class); + startActivity(i); + break; + } overridePendingTransition(R.anim.move_right_in_activity, R.anim.move_left_out_activity); diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index 1f66c92e5f..e5f2c2edc1 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -345,9 +345,11 @@ protected void calcMinMax() { if (mAutoScaleMinMaxEnabled) mData.calcMinMax(getLowestVisibleXIndex(), getHighestVisibleXIndex()); - // calculate / set x-axis range - mXAxis.mAxisMaximum = mData.getXVals().size() - 1; - mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); +// // calculate / set x-axis range +// mXAxis.mAxisMaximum = mData.getXVals().size() - 1; +// mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); + + 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)); diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java index 9acfaafe17..c96926017f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java @@ -33,14 +33,6 @@ protected void init() { mRenderer = new LineChartRenderer(this, mAnimator, mViewPortHandler); } - - @Override - protected void calcMinMax() { - super.calcMinMax(); - - if (mXAxis.mAxisRange == 0 && mData.getYValCount() > 0) - mXAxis.mAxisRange = 1; - } @Override public LineData getLineData() { diff --git a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java index 7ebf9b5cec..87e701d5dd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java @@ -419,6 +419,9 @@ public void calculate(float dataMin, float dataMax) { min = min - 1f; } + this.mAxisMinimum = min; + this.mAxisMaximum = max; + // actual range this.mAxisRange = Math.abs(max - min); } From 992dd5fc85bce6dad2fe27fbfbf4cbf1d06fcad0 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 5 Apr 2016 22:30:38 +0200 Subject: [PATCH 073/606] Work on example --- .../xxmassdeveloper/mpchartexample/LineChartTime.java | 9 ++++++++- .../com/github/mikephil/charting/charts/LineChart.java | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 8d0a7a49e4..c94a2d3fea 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -39,6 +39,7 @@ public class LineChartTime extends DemoBase implements OnSeekBarChangeListener { private LineChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; + private long curTime = System.currentTimeMillis(); @Override protected void onCreate(Bundle savedInstanceState) { @@ -107,6 +108,10 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setDrawAxisLine(false); xAxis.setSpaceBetweenLabels(1); + // custom x-axis min / max + xAxis.setAxisMinValue(curTime); + xAxis.setAxisMaxValue(curTime + 100000); + YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(tf); leftAxis.setTextColor(ColorTemplate.getHoloBlue()); @@ -268,7 +273,9 @@ private void setData(int count, float range) { ArrayList xVals = new ArrayList(); for (int i = 0; i < count; i++) { - xVals.add(new XAxisValue(i, i + "")); + + long timeLong = curTime + i * 1000; + xVals.add(new XAxisValue(timeLong, new Date(timeLong).toString())); } ArrayList yVals1 = new ArrayList(); diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java index c96926017f..71acbb8d01 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java @@ -33,6 +33,14 @@ protected void init() { mRenderer = new LineChartRenderer(this, mAnimator, mViewPortHandler); } + +// @Override +// protected void calcMinMax() { +// super.calcMinMax(); +// +// if (mXAxis.mAxisRange == 0 && mData.getYValCount() > 0) +// mXAxis.mAxisRange = 1; +// } @Override public LineData getLineData() { From 545c6339180fd92bb9d9afc3eed18b229bef840f Mon Sep 17 00:00:00 2001 From: = Date: Tue, 5 Apr 2016 22:47:09 +0200 Subject: [PATCH 074/606] Work on timechart --- .../mpchartexample/LineChartTime.java | 14 +++++++------- .../github/mikephil/charting/data/ChartData.java | 4 ++-- .../mikephil/charting/renderer/XAxisRenderer.java | 8 +++++--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index c94a2d3fea..bd22874f99 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -60,6 +60,7 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarX.setOnSeekBarChangeListener(this); mChart = (LineChart) findViewById(R.id.chart1); + mChart.setLogEnabled(true); // no description text mChart.setDescription(""); @@ -84,8 +85,7 @@ protected void onCreate(Bundle savedInstanceState) { // add data setData(20, 30); - - mChart.animateX(2500); + mChart.invalidate(); Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); @@ -109,8 +109,8 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setSpaceBetweenLabels(1); // custom x-axis min / max - xAxis.setAxisMinValue(curTime); - xAxis.setAxisMaxValue(curTime + 100000); + xAxis.setAxisMinValue(5000); + xAxis.setAxisMaxValue(30000); YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(tf); @@ -272,10 +272,10 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { private void setData(int count, float range) { ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { + for (int i = 0; i < 10; i++) { - long timeLong = curTime + i * 1000; - xVals.add(new XAxisValue(timeLong, new Date(timeLong).toString())); + long timeLong = 10000 + i * 1000; + xVals.add(new XAxisValue(timeLong, i + "")); } ArrayList yVals1 = new ArrayList(); diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java index 232ac837c1..e5995d36b1 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java @@ -194,8 +194,8 @@ private void checkLegal() { for (int i = 0; i < mDataSets.size(); i++) { if (mDataSets.get(i).getEntryCount() > mXVals.size()) { - throw new IllegalArgumentException( - "One or more of the DataSet Entry arrays are longer than the x-values array of this ChartData object."); +// throw new IllegalArgumentException( +// "One or more of the DataSet Entry arrays are longer than the x-values array of this ChartData object."); } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java index 6de6112508..a12d10204f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -155,15 +155,17 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { 0f, 0f }; - for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { + for (int i = 0; i < mXAxis.getValues().size(); i++) { - position[0] = i; + XAxisValue xVal = mXAxis.getValues().get(i); + + position[0] = (float) xVal.getPosition(); mTrans.pointValuesToPixel(position); if (mViewPortHandler.isInBoundsX(position[0])) { - String label = mXAxis.getValues().get(i).getLabel(); + String label = xVal.getLabel(); if (mXAxis.isAvoidFirstLastClippingEnabled()) { From a0b49fc8c4758940b2e6e1c8d191a0c21f722e1a Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 9 Apr 2016 11:19:33 +0200 Subject: [PATCH 075/606] Include offset in position calculation --- .../com/github/mikephil/charting/renderer/LegendRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java index b6b3a3f3ac..9a24030dcd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -232,7 +232,7 @@ public void renderLegend(Canvas c) { if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) originPosX -= mLegend.mNeededWidth; } else // BELOW_CHART_CENTER || ABOVE_CHART_CENTER - originPosX = mViewPortHandler.contentLeft() + contentWidth / 2.f; + originPosX = mViewPortHandler.contentLeft() + contentWidth / 2.f + xoffset; FSize[] calculatedLineSizes = mLegend.getCalculatedLineSizes(); FSize[] calculatedLabelSizes = mLegend.getCalculatedLabelSizes(); From 986874866dee500c17bb232ed76332a4a834809d Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sat, 9 Apr 2016 21:36:55 +0300 Subject: [PATCH 076/606] Added horizontal cubic (not exaggerating vertical control points) --- .../CubicLineChartActivity.java | 7 +- .../mpchartexample/LineChartActivity1.java | 14 ++- .../mpchartexample/LineChartActivity2.java | 14 ++- .../mikephil/charting/data/LineDataSet.java | 64 +++++++------ .../implementation/RealmLineDataSet.java | 59 ++++++------ .../interfaces/datasets/ILineDataSet.java | 18 ++-- .../charting/renderer/LineChartRenderer.java | 90 +++++++++++++++++-- 7 files changed, 175 insertions(+), 91 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 26220e5309..3d904854e0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -166,10 +166,9 @@ public boolean onOptionsItemSelected(MenuItem item) { for (ILineDataSet iSet : sets) { LineDataSet set = (LineDataSet) iSet; - if (set.isDrawCubicEnabled()) - set.setDrawCubic(false); - else - set.setDrawCubic(true); + set.setMode(set.getMode() == LineDataSet.Mode.CUBIC_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.CUBIC_BEZIER); } mChart.invalidate(); break; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 836afcd7f2..00e32ade6c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -235,10 +235,9 @@ public boolean onOptionsItemSelected(MenuItem item) { for (ILineDataSet iSet : sets) { LineDataSet set = (LineDataSet) iSet; - if (set.isDrawCubicEnabled()) - set.setDrawCubic(false); - else - set.setDrawCubic(true); + set.setMode(set.getMode() == LineDataSet.Mode.CUBIC_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.CUBIC_BEZIER); } mChart.invalidate(); break; @@ -250,10 +249,9 @@ public boolean onOptionsItemSelected(MenuItem item) { for (ILineDataSet iSet : sets) { LineDataSet set = (LineDataSet) iSet; - if (set.isDrawSteppedEnabled()) - set.setDrawStepped(false); - else - set.setDrawStepped(true); + set.setMode(set.getMode() == LineDataSet.Mode.STEPPED + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.STEPPED); } mChart.invalidate(); break; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index d7df678923..479a0154fb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -194,10 +194,9 @@ public boolean onOptionsItemSelected(MenuItem item) { for (ILineDataSet iSet : sets) { LineDataSet set = (LineDataSet) iSet; - if (set.isDrawCubicEnabled()) - set.setDrawCubic(false); - else - set.setDrawCubic(true); + set.setMode(set.getMode() == LineDataSet.Mode.CUBIC_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.CUBIC_BEZIER); } mChart.invalidate(); break; @@ -209,10 +208,9 @@ public boolean onOptionsItemSelected(MenuItem item) { for (ILineDataSet iSet : sets) { LineDataSet set = (LineDataSet) iSet; - if (set.isDrawSteppedEnabled()) - set.setDrawStepped(false); - else - set.setDrawStepped(true); + set.setMode(set.getMode() == LineDataSet.Mode.STEPPED + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.STEPPED); } mChart.invalidate(); break; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java index 7d9da1ac55..1fb380eeaa 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java @@ -17,6 +17,9 @@ public class LineDataSet extends LineRadarDataSet implements ILineDataSet { + /** Drawing mode for this line dataset **/ + private LineDataSet.Mode mMode = Mode.LINEAR; + /** List representing all colors that are used for the circles */ private List mCircleColors = null; @@ -38,12 +41,6 @@ public class LineDataSet extends LineRadarDataSet implements ILineDataSet /** if true, drawing circles is enabled */ private boolean mDrawCircles = true; - /** if true, cubic lines are drawn instead of linear */ - private boolean mDrawCubic = false; - - /** if true, stepped lines are drawn instead of linear */ - private boolean mDrawStepped = false; - private boolean mDrawCircleHole = true; @@ -71,17 +68,37 @@ public DataSet copy() { } LineDataSet copied = new LineDataSet(yVals, getLabel()); + copied.mMode = mMode; copied.mColors = mColors; copied.mCircleRadius = mCircleRadius; copied.mCircleColors = mCircleColors; copied.mDashPathEffect = mDashPathEffect; copied.mDrawCircles = mDrawCircles; - copied.mDrawCubic = mDrawCubic; + copied.mDrawCircleHole = mDrawCircleHole; copied.mHighLightColor = mHighLightColor; return copied; } + /** + * Returns the drawing mode for this line dataset + * + * @return + */ + @Override + public LineDataSet.Mode getMode() { + return mMode; + } + + /** + * Returns the drawing mode for this line dataset + * + * @return + */ + public void setMode(LineDataSet.Mode mode) { + mMode = mode; + } + /** * Sets the intensity for cubic lines (if enabled). Max = 1f = very cubic, * Min = 0.05f = low cubic effect, Default: 0.2f @@ -189,36 +206,26 @@ public boolean isDrawCirclesEnabled() { return mDrawCircles; } - /** - * If set to true, the linechart lines are drawn in cubic-style instead of - * linear. This affects performance! Default: false - * - * @param enabled - */ + @Deprecated public void setDrawCubic(boolean enabled) { - mDrawCubic = enabled; + mMode = enabled ? Mode.CUBIC_BEZIER : Mode.LINEAR; } + @Deprecated @Override public boolean isDrawCubicEnabled() { - return mDrawCubic; + return mMode == Mode.CUBIC_BEZIER; } - /** - * If set to true, the linechart lines are drawn in stepped-style instead of - * linear. - * This does not work with cubic lines, of course. - * Default: false - * - * @param enabled - */ + @Deprecated public void setDrawStepped(boolean enabled) { - mDrawStepped = enabled; + mMode = enabled ? Mode.STEPPED : Mode.LINEAR; } + @Deprecated @Override public boolean isDrawSteppedEnabled() { - return mDrawStepped; + return mMode == Mode.STEPPED; } /** ALL CODE BELOW RELATED TO CIRCLE-COLORS */ @@ -348,4 +355,11 @@ public void setFillFormatter(FillFormatter formatter) { public FillFormatter getFillFormatter() { return mFillFormatter; } + + public enum Mode { + LINEAR, + STEPPED, + CUBIC_BEZIER, + HORIZONTAL_BEZIER + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java index ccea3823d4..c1ab855b0e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java @@ -5,6 +5,7 @@ import android.graphics.DashPathEffect; import android.graphics.drawable.Drawable; +import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.realm.base.RealmLineRadarDataSet; import com.github.mikephil.charting.formatter.DefaultFillFormatter; import com.github.mikephil.charting.formatter.FillFormatter; @@ -23,6 +24,9 @@ */ public class RealmLineDataSet extends RealmLineRadarDataSet implements ILineDataSet { + /** Drawing mode for this line dataset **/ + private LineDataSet.Mode mMode = LineDataSet.Mode.LINEAR; + /** * List representing all colors that are used for the circles */ @@ -58,16 +62,6 @@ public class RealmLineDataSet extends RealmLineRadarDataS */ private boolean mDrawCircles = true; - /** - * if true, cubic lines are drawn instead of linear - */ - private boolean mDrawCubic = false; - - /** - * if true, stepped lines are drawn instead of linear - */ - private boolean mDrawStepped = false; - private boolean mDrawCircleHole = true; /** @@ -110,6 +104,25 @@ public void build(RealmResults results) { super.build(results); } + /** + * Returns the drawing mode for this line dataset + * + * @return + */ + @Override + public LineDataSet.Mode getMode() { + return mMode; + } + + /** + * Returns the drawing mode for this line dataset + * + * @return + */ + public void setMode(LineDataSet.Mode mode) { + mMode = mode; + } + /** * Sets the intensity for cubic lines (if enabled). Max = 1f = very cubic, * Min = 0.05f = low cubic effect, Default: 0.2f @@ -193,36 +206,26 @@ public boolean isDrawCirclesEnabled() { return mDrawCircles; } - /** - * If set to true, the linechart lines are drawn in cubic-style instead of - * linear. This affects performance! Default: false - * - * @param enabled - */ + @Deprecated public void setDrawCubic(boolean enabled) { - mDrawCubic = enabled; + mMode = enabled ? LineDataSet.Mode.CUBIC_BEZIER : LineDataSet.Mode.LINEAR; } + @Deprecated @Override public boolean isDrawCubicEnabled() { - return mDrawCubic; + return mMode == LineDataSet.Mode.CUBIC_BEZIER; } - /** - * If set to true, the linechart lines are drawn in stepped-style instead of - * linear. - * This does not work with cubic lines, of course. - * Default: false - * - * @param enabled - */ + @Deprecated public void setDrawStepped(boolean enabled) { - mDrawStepped = enabled; + mMode = enabled ? LineDataSet.Mode.STEPPED : LineDataSet.Mode.LINEAR; } + @Deprecated @Override public boolean isDrawSteppedEnabled() { - return mDrawStepped; + return mMode == LineDataSet.Mode.STEPPED; } /** ALL CODE BELOW RELATED TO CIRCLE-COLORS */ diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java index 16a8f061cb..fb7ba45536 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java @@ -4,6 +4,7 @@ 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; /** @@ -12,25 +13,24 @@ public interface ILineDataSet extends ILineRadarDataSet { /** - * Returns the intensity of the cubic lines (the effect intensity). - * Max = 1f = very cubic, Min = 0.05f = low cubic effect, Default: 0.2f + * Returns the drawing mode for this line dataset * * @return */ - float getCubicIntensity(); + LineDataSet.Mode getMode(); /** - * Returns true if drawing cubic lines is enabled, false if not. + * Returns the intensity of the cubic lines (the effect intensity). + * Max = 1f = very cubic, Min = 0.05f = low cubic effect, Default: 0.2f * * @return */ + float getCubicIntensity(); + + @Deprecated boolean isDrawCubicEnabled(); - /** - * Returns true if drawing stepped lines is enabled, false if not. - * - * @return - */ + @Deprecated boolean isDrawSteppedEnabled(); /** diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index edf3b99b73..2af2a1f7e6 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -104,14 +104,20 @@ protected void drawDataSet(Canvas c, ILineDataSet dataSet) { mRenderPaint.setStrokeWidth(dataSet.getLineWidth()); mRenderPaint.setPathEffect(dataSet.getDashPathEffect()); - // if drawing cubic lines is enabled - if (dataSet.isDrawCubicEnabled()) { - - drawCubic(c, dataSet); - - // draw normal (straight) lines - } else { - drawLinear(c, dataSet); + switch (dataSet.getMode()) { + default: + case LINEAR: + case STEPPED: + drawLinear(c, dataSet); + break; + + case CUBIC_BEZIER: + drawCubicBezier(c, dataSet); + break; + + case HORIZONTAL_BEZIER: + drawHorizontalBezier(c, dataSet); + break; } mRenderPaint.setPathEffect(null); @@ -123,7 +129,73 @@ protected void drawDataSet(Canvas c, ILineDataSet dataSet) { * @param c * @param dataSet */ - protected void drawCubic(Canvas c, ILineDataSet dataSet) { + protected void drawHorizontalBezier(Canvas c, ILineDataSet dataSet) { + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + int entryCount = dataSet.getEntryCount(); + + Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, DataSet.Rounding.DOWN); + Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); + + int diff = (entryFrom == entryTo) ? 1 : 0; + int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); + int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + float intensity = dataSet.getCubicIntensity(); + + cubicPath.reset(); + + int size = (int) Math.ceil((maxx - minx) * phaseX + minx); + + if (size - minx >= 2) { + + Entry prev = dataSet.getEntryForIndex(minx); + Entry cur = prev; + + // let the spline start + cubicPath.moveTo(cur.getXIndex(), cur.getVal() * phaseY); + + for (int j = minx + 1, count = Math.min(size, entryCount); j < count; j++) { + + prev = dataSet.getEntryForIndex(j - 1); + cur = dataSet.getEntryForIndex(j); + + final float cpx = (float)(prev.getXIndex()) + + (float)(cur.getXIndex() - prev.getXIndex()) / 2.0f; + + cubicPath.cubicTo( + cpx, prev.getVal() * phaseY, + cpx, cur.getVal() * phaseY, + cur.getXIndex(), cur.getVal() * phaseY); + } + } + + // if filled is enabled, close the path + if (dataSet.isDrawFilledEnabled()) { + + cubicFillPath.reset(); + cubicFillPath.addPath(cubicPath); + // create a new path, this is bad for performance + drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, + minx, size); + } + + mRenderPaint.setColor(dataSet.getColor()); + + mRenderPaint.setStyle(Paint.Style.STROKE); + + trans.pathValueToPixel(cubicPath); + + mBitmapCanvas.drawPath(cubicPath, mRenderPaint); + + mRenderPaint.setPathEffect(null); + } + + protected void drawCubicBezier(Canvas c, ILineDataSet dataSet) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); From e0250bda49e849f72146836cef12f78da903bf80 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sat, 9 Apr 2016 21:45:59 +0300 Subject: [PATCH 077/606] Add horizontal bezier to the demo --- MPChartExample/res/menu/line.xml | 4 +++ .../CubicLineChartActivity.java | 28 +++++++++++++++++++ .../mpchartexample/LineChartActivity1.java | 14 ++++++++++ .../mpchartexample/LineChartActivity2.java | 14 ++++++++++ 4 files changed, 60 insertions(+) diff --git a/MPChartExample/res/menu/line.xml b/MPChartExample/res/menu/line.xml index 8d1437029b..aea6845249 100644 --- a/MPChartExample/res/menu/line.xml +++ b/MPChartExample/res/menu/line.xml @@ -21,6 +21,10 @@ android:id="@+id/actionToggleStepped" android:title="Toggle Stepped"> + + diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 3d904854e0..799838a83f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -173,6 +173,34 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleStepped: { + List sets = mChart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.STEPPED + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.STEPPED); + } + mChart.invalidate(); + break; + } + case R.id.actionToggleHorizontalCubic: { + List sets = mChart.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); + } + mChart.invalidate(); + break; + } case R.id.actionTogglePinch: { if (mChart.isPinchZoomEnabled()) mChart.setPinchZoom(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 00e32ade6c..1d4a8bae08 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -256,6 +256,20 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleHorizontalCubic: { + List sets = mChart.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); + } + mChart.invalidate(); + break; + } case R.id.actionTogglePinch: { if (mChart.isPinchZoomEnabled()) mChart.setPinchZoom(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 479a0154fb..619d019109 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -215,6 +215,20 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } + case R.id.actionToggleHorizontalCubic: { + List sets = mChart.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); + } + mChart.invalidate(); + break; + } case R.id.actionTogglePinch: { if (mChart.isPinchZoomEnabled()) mChart.setPinchZoom(false); From f74cead4e76abef1f89df3e5ff41459717af757a Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sat, 9 Apr 2016 21:48:53 +0300 Subject: [PATCH 078/606] Make yVals settable --- .../src/com/github/mikephil/charting/data/DataSet.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java index 29f5f69c28..3e2926cf9e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java @@ -103,6 +103,16 @@ public List getYVals() { return mYVals; } + /** + * Sets the array of y-values that this DataSet represents, and calls notifyDataSetChanged() + * + * @return + */ + public void setYVals(List yVals) { + mYVals = yVals; + notifyDataSetChanged(); + } + /** * Provides an exact copy of the DataSet this method is used on. * From e762b59a2820b3d542df5907494bb4b688d57a25 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sat, 9 Apr 2016 21:57:13 +0300 Subject: [PATCH 079/606] Made these line demos reuse the dataset and keep styling --- .../CubicLineChartActivity.java | 70 ++++++++------ .../mpchartexample/LineChartActivity1.java | 67 ++++++++------ .../mpchartexample/LineChartActivity2.java | 92 +++++++++++-------- 3 files changed, 131 insertions(+), 98 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 799838a83f..86ce89677c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -273,44 +273,54 @@ private void setData(int count, float range) { xVals.add((1990 +i) + ""); } - ArrayList vals1 = new ArrayList(); + 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); - vals1.add(new Entry(val, i)); + yVals.add(new Entry(val, i)); } - - // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(vals1, "DataSet 1"); - set1.setDrawCubic(true); - 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 FillFormatter() { - @Override - public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { - return -10; - } - }); - // create a data object with the datasets - LineData data = new LineData(xVals, set1); - data.setValueTypeface(tf); - data.setValueTextSize(9f); - data.setDrawValues(false); + LineDataSet set1; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); + set1.setYVals(yVals); + mChart.notifyDataSetChanged(); + } else { + // create a dataset and give it a type + set1 = new LineDataSet(yVals, "DataSet 1"); + + set1.setDrawCubic(true); + 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 FillFormatter() { + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + return -10; + } + }); - // set data - mChart.setData(data); + // create a data object with the datasets + LineData data = new LineData(xVals, set1); + data.setValueTypeface(tf); + data.setValueTextSize(9f); + data.setDrawValues(false); + + // set data + mChart.setData(data); + } } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 1d4a8bae08..82e1961304 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -353,38 +353,49 @@ private void setData(int count, float range) { yVals.add(new Entry(val, i)); } - // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(yVals, "DataSet 1"); - // set1.setFillAlpha(110); - // set1.setFillColor(Color.RED); - - // 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); - - 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); + LineDataSet set1; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); + set1.setYVals(yVals); + mChart.notifyDataSetChanged(); } else { - set1.setFillColor(Color.BLACK); - } + // create a dataset and give it a type + set1 = new LineDataSet(yVals, "DataSet 1"); + + // set1.setFillAlpha(110); + // set1.setFillColor(Color.RED); + + // 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); + + 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 + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); // add the datasets - // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); + // create a data object with the datasets + LineData data = new LineData(xVals, dataSets); - // set data - mChart.setData(data); + // set data + mChart.setData(data); + } } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 619d019109..42773b649a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -300,22 +300,6 @@ private void setData(int count, float range) { yVals1.add(new Entry(val, i)); } - // create a dataset and give it a type - LineDataSet 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); - ArrayList yVals2 = new ArrayList(); for (int i = 0; i < count; i++) { @@ -326,30 +310,58 @@ private void setData(int count, float range) { yVals2.add(new Entry(val, i)); } - // create a dataset and give it a type - LineDataSet 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)); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set2); - dataSets.add(set1); // add the datasets - - // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); - data.setValueTextColor(Color.WHITE); - data.setValueTextSize(9f); - - // set data - mChart.setData(data); + LineDataSet set1, set2; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); + set2 = (LineDataSet)mChart.getData().getDataSetByIndex(1); + set1.setYVals(yVals1); + set2.setYVals(yVals2); + 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)); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set2); + dataSets.add(set1); // add the datasets + + // create a data object with the datasets + LineData data = new LineData(xVals, dataSets); + data.setValueTextColor(Color.WHITE); + data.setValueTextSize(9f); + + // set data + mChart.setData(data); + } } @Override From 215be8ac7553188aa4acf6610660fd9e517ae9f8 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sat, 9 Apr 2016 22:07:06 +0300 Subject: [PATCH 080/606] DRYed some code here... --- .../charting/renderer/LineChartRenderer.java | 23 ++----------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index 2af2a1f7e6..430aa3edfe 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -232,37 +232,18 @@ protected void drawCubicBezier(Canvas c, ILineDataSet dataSet) { // let the spline start cubicPath.moveTo(cur.getXIndex(), cur.getVal() * phaseY); - for (int j = minx + 1, count = Math.min(size, entryCount - 1); j < count; j++) { + for (int j = minx + 1, count = Math.min(size, entryCount); j < count; j++) { prevPrev = dataSet.getEntryForIndex(j == 1 ? 0 : j - 2); prev = dataSet.getEntryForIndex(j - 1); cur = dataSet.getEntryForIndex(j); - next = dataSet.getEntryForIndex(j + 1); - - prevDx = (cur.getXIndex() - prevPrev.getXIndex()) * intensity; - prevDy = (cur.getVal() - prevPrev.getVal()) * intensity; - curDx = (next.getXIndex() - prev.getXIndex()) * intensity; - curDy = (next.getVal() - prev.getVal()) * intensity; - - cubicPath.cubicTo(prev.getXIndex() + prevDx, (prev.getVal() + prevDy) * phaseY, - cur.getXIndex() - curDx, - (cur.getVal() - curDy) * phaseY, cur.getXIndex(), cur.getVal() * phaseY); - } - - if (size > entryCount - 1) { - - prevPrev = dataSet.getEntryForIndex((entryCount >= 3) ? entryCount - 3 - : entryCount - 2); - prev = dataSet.getEntryForIndex(entryCount - 2); - cur = dataSet.getEntryForIndex(entryCount - 1); - next = cur; + next = entryCount > j + 1 ? dataSet.getEntryForIndex(j + 1) : cur; prevDx = (cur.getXIndex() - prevPrev.getXIndex()) * intensity; prevDy = (cur.getVal() - prevPrev.getVal()) * intensity; curDx = (next.getXIndex() - prev.getXIndex()) * intensity; curDy = (next.getVal() - prev.getVal()) * intensity; - // the last cubic cubicPath.cubicTo(prev.getXIndex() + prevDx, (prev.getVal() + prevDy) * phaseY, cur.getXIndex() - curDx, (cur.getVal() - curDy) * phaseY, cur.getXIndex(), cur.getVal() * phaseY); From 0ab3dbfae10e0fc2c5b21a1a0752512c326b230a Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 10 Apr 2016 10:23:26 +0300 Subject: [PATCH 081/606] Added Xamarin links --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 197c1b4881..1e58a84f48 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ As an additional feature, this library allows cross-platform development between 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). +## 3rd party bindings + +Xamarin (by @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/). + Spread the word ----- From 18e39237de56ad597e41ebcc8dc8e822d5d28cce Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 10 Apr 2016 13:14:38 +0300 Subject: [PATCH 082/606] Added feature for drawing bar borders --- .../mikephil/charting/data/BarDataSet.java | 44 +++++++++++++++++++ .../realm/implementation/RealmBarDataSet.java | 44 +++++++++++++++++++ .../interfaces/datasets/IBarDataSet.java | 15 +++++++ .../charting/renderer/BarChartRenderer.java | 21 ++++++++- .../renderer/HorizontalBarChartRenderer.java | 12 ++++- 5 files changed, 132 insertions(+), 4 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java index fb1dccccf8..eed5785bb6 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java @@ -26,6 +26,10 @@ public class BarDataSet extends BarLineScatterCandleBubbleDataSet impl */ private int mBarShadowColor = Color.rgb(215, 215, 215); + private float mBarBorderWidth = 0.0f; + + private int mBarBorderColor = Color.BLACK; + /** * the alpha value used to draw the highlight indicator bar */ @@ -217,6 +221,46 @@ public int getBarShadowColor() { return mBarShadowColor; } + /** + * Sets the width used for drawing borders around the bars. + * If borderWidth == 0, no border will be drawn. + * + * @return + */ + public void setBarBorderWidth(float width) { + mBarBorderWidth = width; + } + + /** + * Returns the width used for drawing borders around the bars. + * If borderWidth == 0, no border will be drawn. + * + * @return + */ + @Override + public float getBarBorderWidth() { + return mBarBorderWidth; + } + + /** + * Sets the color drawing borders around the bars. + * + * @return + */ + public void setBarBorderColor(int color) { + mBarBorderColor = color; + } + + /** + * Returns the color drawing borders around the bars. + * + * @return + */ + @Override + public int getBarBorderColor() { + return mBarBorderColor; + } + /** * Set the alpha value (transparency) that is used for drawing the highlight * indicator bar. min = 0 (fully transparent), max = 255 (fully opaque) diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java index 1b78c278cb..5fde69a4bd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java @@ -34,6 +34,10 @@ public class RealmBarDataSet extends RealmBarLineScatterC */ private int mBarShadowColor = Color.rgb(215, 215, 215); + private float mBarBorderWidth = 0.0f; + + private int mBarBorderColor = Color.BLACK; + /** * the alpha value used to draw the highlight indicator bar */ @@ -212,6 +216,46 @@ public int getBarShadowColor() { return mBarShadowColor; } + /** + * Sets the width used for drawing borders around the bars. + * If borderWidth == 0, no border will be drawn. + * + * @return + */ + public void setBarBorderWidth(float width) { + mBarBorderWidth = width; + } + + /** + * Returns the width used for drawing borders around the bars. + * If borderWidth == 0, no border will be drawn. + * + * @return + */ + @Override + public float getBarBorderWidth() { + return mBarBorderWidth; + } + + /** + * Sets the color drawing borders around the bars. + * + * @return + */ + public void setBarBorderColor(int color) { + mBarBorderColor = color; + } + + /** + * Returns the color drawing borders around the bars. + * + * @return + */ + @Override + public int getBarBorderColor() { + return mBarBorderColor; + } + /** * Set the alpha value (transparency) that is used for drawing the highlight * indicator bar. min = 0 (fully transparent), max = 255 (fully opaque) diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java index bc3bb9b3ea..e7c2f64355 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java @@ -37,6 +37,21 @@ public interface IBarDataSet extends IBarLineScatterCandleBubbleDataSet 0.f; float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); @@ -125,11 +133,15 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { break; // Set the color for the currently drawn value. If the index - // is - // out of bounds, reuse colors. + // 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 { @@ -145,6 +157,11 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { 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/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index facccc945c..41e1514d5d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -52,6 +52,10 @@ 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(dataSet.getBarBorderWidth()); + + final boolean drawBorder = dataSet.getBarBorderWidth() > 0.f; float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); @@ -82,11 +86,15 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { } // Set the color for the currently drawn value. If the index - // is - // out of bounds, reuse colors. + // 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); + } } } From 58b640dc0b968525b24c47d2e01ca63543e8d652 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 10 Apr 2016 13:15:46 +0300 Subject: [PATCH 083/606] Added bar borders to the demo --- MPChartExample/res/menu/bar.xml | 4 ++++ .../xxmassdeveloper/mpchartexample/AnotherBarActivity.java | 7 +++++++ .../xxmassdeveloper/mpchartexample/BarChartActivity.java | 7 +++++++ .../mpchartexample/BarChartActivityMultiDataset.java | 7 +++++++ .../mpchartexample/BarChartActivitySinus.java | 7 +++++++ .../mpchartexample/HorizontalBarChartActivity.java | 7 +++++++ .../xxmassdeveloper/mpchartexample/StackedBarActivity.java | 7 +++++++ .../mpchartexample/StackedBarActivityNegative.java | 7 +++++++ 8 files changed, 53 insertions(+) diff --git a/MPChartExample/res/menu/bar.xml b/MPChartExample/res/menu/bar.xml index 2cce21aacf..4b35e2ff5d 100644 --- a/MPChartExample/res/menu/bar.xml +++ b/MPChartExample/res/menu/bar.xml @@ -41,5 +41,9 @@ android:id="@+id/actionToggleAutoScaleMinMax" android:title="Toggle auto scale min/max"> + + \ No newline at end of file diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index cabd72e9dd..fe4f7a8381 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -126,6 +126,13 @@ public boolean onOptionsItemSelected(MenuItem item) { 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.actionToggleHighlightArrow: { if (mChart.isDrawHighlightArrowEnabled()) mChart.setDrawHighlightArrow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 99d3d027fd..d097d5da9c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -167,6 +167,13 @@ public boolean onOptionsItemSelected(MenuItem item) { 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.actionToggleHighlightArrow: { if (mChart.isDrawHighlightArrowEnabled()) mChart.setDrawHighlightArrow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 4f869ecc3a..aa734146d8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -135,6 +135,13 @@ public boolean onOptionsItemSelected(MenuItem item) { 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.actionToggleHighlight: { if(mChart.getData() != null) { mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index e200f2e9b1..d335fe7095 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -151,6 +151,13 @@ public boolean onOptionsItemSelected(MenuItem item) { 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.actionToggleHighlightArrow: { if (mChart.isDrawHighlightArrowEnabled()) mChart.setDrawHighlightArrow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 3027188cf5..70a64eb7fc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -169,6 +169,13 @@ public boolean onOptionsItemSelected(MenuItem item) { 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.actionToggleHighlightArrow: { if (mChart.isDrawHighlightArrowEnabled()) mChart.setDrawHighlightArrow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 77c6d1f23f..6934711bb8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -140,6 +140,13 @@ public boolean onOptionsItemSelected(MenuItem item) { 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.actionToggleHighlightArrow: { if (mChart.isDrawHighlightArrowEnabled()) mChart.setDrawHighlightArrow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index d25ee39f2b..ea33423354 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -154,6 +154,13 @@ public boolean onOptionsItemSelected(MenuItem item) { 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.actionToggleHighlightArrow: { if (mChart.isDrawHighlightArrowEnabled()) mChart.setDrawHighlightArrow(false); From e6915e25e2a78cc4e2f4d523be7e987736f9d2bd Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 10 Apr 2016 13:16:16 +0300 Subject: [PATCH 084/606] Made these bar demos reuse the dataset and keep styling --- .../mpchartexample/AnotherBarActivity.java | 25 +++++--- .../mpchartexample/BarChartActivity.java | 25 +++++--- .../BarChartActivityMultiDataset.java | 57 ++++++++++++------- .../mpchartexample/BarChartActivitySinus.java | 27 ++++++--- .../BarChartPositiveNegative.java | 33 +++++++---- .../HorizontalBarChartActivity.java | 23 +++++--- .../mpchartexample/StackedBarActivity.java | 28 ++++++--- 7 files changed, 143 insertions(+), 75 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index fe4f7a8381..5d5b933f57 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -186,17 +186,26 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { xVals.add((int) yVals1.get(i).getVal() + ""); } - BarDataSet set1 = new BarDataSet(yVals1, "Data Set"); - set1.setColors(ColorTemplate.VORDIPLOM_COLORS); - set1.setDrawValues(false); + BarDataSet set1; - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); + set1.setYVals(yVals1); + mChart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(yVals1, "Data Set"); + set1.setColors(ColorTemplate.VORDIPLOM_COLORS); + set1.setDrawValues(false); - BarData data = new BarData(xVals, dataSets); + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); - mChart.setData(data); - mChart.invalidate(); + BarData data = new BarData(xVals, dataSets); + + mChart.setData(data); + mChart.invalidate(); + } } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index d097d5da9c..626890aa03 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -245,17 +245,26 @@ private void setData(int count, float range) { yVals1.add(new BarEntry(val, i)); } - BarDataSet set1 = new BarDataSet(yVals1, "DataSet"); - set1.setBarSpacePercent(35f); + BarDataSet set1; - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); + set1.setYVals(yVals1); + mChart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(yVals1, "DataSet"); + set1.setBarSpacePercent(35f); - BarData data = new BarData(xVals, dataSets); - data.setValueTextSize(10f); - data.setValueTypeface(mTf); + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); - mChart.setData(data); + BarData data = new BarData(xVals, dataSets); + data.setValueTextSize(10f); + data.setValueTypeface(mTf); + + mChart.setData(data); + } } @SuppressLint("NewApi") diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index aa734146d8..1229828c4d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -211,30 +211,43 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { yVals3.add(new BarEntry(val, i)); } - // create 3 datasets with different types - BarDataSet set1 = new BarDataSet(yVals1, "Company A"); - // set1.setColors(ColorTemplate.createColors(getApplicationContext(), - // ColorTemplate.FRESH_COLORS)); - set1.setColor(Color.rgb(104, 241, 175)); - BarDataSet set2 = new BarDataSet(yVals2, "Company B"); - set2.setColor(Color.rgb(164, 228, 251)); - BarDataSet set3 = new BarDataSet(yVals3, "Company C"); - set3.setColor(Color.rgb(242, 247, 158)); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - dataSets.add(set2); - dataSets.add(set3); - - BarData data = new BarData(xVals, dataSets); + BarDataSet set1, set2, set3; + + 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.setYVals(yVals1); + set2.setYVals(yVals2); + set3.setYVals(yVals3); + mChart.notifyDataSetChanged(); + } else { + // create 3 datasets with different types + 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)); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); + dataSets.add(set2); + dataSets.add(set3); + + BarData data = new BarData(xVals, dataSets); // data.setValueFormatter(new LargeValueFormatter()); - - // add space between the dataset groups in percent of bar-width - data.setGroupSpace(80f); - data.setValueTypeface(tf); - mChart.setData(data); - mChart.invalidate(); + // add space between the dataset groups in percent of bar-width + data.setGroupSpace(80f); + data.setValueTypeface(tf); + + mChart.setData(data); + mChart.invalidate(); + } } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index d335fe7095..b7d6dfd5aa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -223,16 +223,25 @@ private void setData(int count) { xVals.add(i+""); entries.add(mSinusData.get(i)); } - - BarDataSet set = new BarDataSet(entries, "Sinus Function"); - set.setBarSpacePercent(40f); - set.setColor(Color.rgb(240, 120, 124)); - BarData data = new BarData(xVals, set); - data.setValueTextSize(10f); - data.setValueTypeface(mTf); - data.setDrawValues(false); + BarDataSet set; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set = (BarDataSet)mChart.getData().getDataSetByIndex(0); + set.setYVals(entries); + mChart.notifyDataSetChanged(); + } else { + set = new BarDataSet(entries, "Sinus Function"); + set.setBarSpacePercent(40f); + set.setColor(Color.rgb(240, 120, 124)); - mChart.setData(data); + BarData data = new BarData(xVals, set); + data.setValueTextSize(10f); + data.setValueTypeface(mTf); + data.setDrawValues(false); + + mChart.setData(data); + } } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 26f8e6fceb..e028d5d2d7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -108,18 +108,27 @@ private void setData(List dataList) { colors.add(green); } - BarDataSet set = new BarDataSet(values, "Values"); - set.setBarSpacePercent(40f); - set.setColors(colors); - set.setValueTextColors(colors); - - BarData data = new BarData(dates, set); - data.setValueTextSize(13f); - data.setValueTypeface(mTf); - data.setValueFormatter(new ValueFormatter()); - - mChart.setData(data); - mChart.invalidate(); + BarDataSet set; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set = (BarDataSet)mChart.getData().getDataSetByIndex(0); + set.setYVals(values); + mChart.notifyDataSetChanged(); + } else { + set = new BarDataSet(values, "Values"); + set.setBarSpacePercent(40f); + set.setColors(colors); + set.setValueTextColors(colors); + + BarData data = new BarData(dates, set); + data.setValueTextSize(13f); + data.setValueTypeface(mTf); + data.setValueFormatter(new ValueFormatter()); + + mChart.setData(data); + mChart.invalidate(); + } } /** diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 70a64eb7fc..1dc016ac60 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -242,16 +242,25 @@ private void setData(int count, float range) { yVals1.add(new BarEntry((float) (Math.random() * range), i)); } - BarDataSet set1 = new BarDataSet(yVals1, "DataSet 1"); + BarDataSet set1; - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); + set1.setYVals(yVals1); + mChart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(yVals1, "DataSet 1"); - BarData data = new BarData(xVals, dataSets); - data.setValueTextSize(10f); - data.setValueTypeface(tf); + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); - mChart.setData(data); + BarData data = new BarData(xVals, dataSets); + data.setValueTextSize(10f); + data.setValueTypeface(tf); + + mChart.setData(data); + } } @SuppressLint("NewApi") diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 6934711bb8..1f8ef92129 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -20,6 +20,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.data.LineDataSet; import com.github.mikephil.charting.data.filter.Approximator; import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.highlight.Highlight; @@ -201,18 +202,27 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { yVals1.add(new BarEntry(new float[] { val1, val2, val3 }, i)); } - BarDataSet set1 = new BarDataSet(yVals1, "Statistics Vienna 2014"); - set1.setColors(getColors()); - set1.setStackLabels(new String[] { "Births", "Divorces", "Marriages" }); + BarDataSet set1; - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); + set1.setYVals(yVals1); + mChart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(yVals1, "Statistics Vienna 2014"); + set1.setColors(getColors()); + set1.setStackLabels(new String[]{"Births", "Divorces", "Marriages"}); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); - BarData data = new BarData(xVals, dataSets); - data.setValueFormatter(new MyValueFormatter()); + BarData data = new BarData(xVals, dataSets); + data.setValueFormatter(new MyValueFormatter()); - mChart.setData(data); - mChart.invalidate(); + mChart.setData(data); + mChart.invalidate(); + } } @Override From 9a9ae5141c6773f2522d545c9e9a47ad126b8769 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 10 Apr 2016 14:10:03 +0300 Subject: [PATCH 085/606] Avoid division by zero when calculating huge zooms --- .../com/github/mikephil/charting/renderer/YAxisRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java index f1d8f8288e..cbc883e8aa 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -134,8 +134,8 @@ protected void computeAxisValues(float min, float max) { } else { - double first = Math.ceil(yMin / interval) * interval; - double last = Utils.nextUp(Math.floor(yMax / interval) * interval); + double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; + double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); double f; int i; From a1fa7ad5b250dee719897ca7384184a19c5b1e40 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 10 Apr 2016 20:02:55 +0300 Subject: [PATCH 086/606] Exploded the Legend-Position enum to support more combinations --- .../charting/charts/BarLineChartBase.java | 141 ++++-- .../charting/charts/HorizontalBarChart.java | 43 +- .../charting/charts/PieRadarChartBase.java | 200 ++++---- .../mikephil/charting/components/Legend.java | 447 +++++++++++++----- .../charting/renderer/LegendRenderer.java | 192 ++++---- 5 files changed, 637 insertions(+), 386 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index 1f66c92e5f..9bba9d5886 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -10,10 +10,12 @@ import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.PointF; +import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; +import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.components.YAxis; @@ -299,9 +301,13 @@ protected void prepareValuePxMatrix() { Log.i(LOG_TAG, "Preparing Value-Px Matrix, xmin: " + mXAxis.mAxisMinimum + ", xmax: " + mXAxis.mAxisMaximum + ", xdelta: " + mXAxis.mAxisRange); - mRightAxisTransformer.prepareMatrixValuePx(mXAxis.mAxisMinimum, mXAxis.mAxisRange, mAxisRight.mAxisRange, + mRightAxisTransformer.prepareMatrixValuePx(mXAxis.mAxisMinimum, + mXAxis.mAxisRange, + mAxisRight.mAxisRange, mAxisRight.mAxisMinimum); - mLeftAxisTransformer.prepareMatrixValuePx(mXAxis.mAxisMinimum, mXAxis.mAxisRange, mAxisLeft.mAxisRange, + mLeftAxisTransformer.prepareMatrixValuePx(mXAxis.mAxisMinimum, + mXAxis.mAxisRange, + mAxisLeft.mAxisRange, mAxisLeft.mAxisMinimum); } @@ -355,6 +361,90 @@ protected void calcMinMax() { .RIGHT)); } + protected void calculateLegendOffsets(RectF offsets) { + + offsets.left = 0.f; + offsets.right = 0.f; + 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(); + + 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: + break; + } + } + + break; + + case HORIZONTAL: + + switch (mLegend.getVerticalAlignment()) { + case TOP: + 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: + break; + } + break; + } + } + } + + private RectF mOffsetsBuffer = new RectF(); + @Override public void calculateOffsets() { @@ -362,49 +452,12 @@ public void calculateOffsets() { float offsetLeft = 0f, offsetRight = 0f, offsetTop = 0f, offsetBottom = 0f; - // setup offsets for legend - if (mLegend != null && mLegend.isEnabled()) { - - if (mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART - || mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART_CENTER) { - - offsetRight += Math.min(mLegend.mNeededWidth, mViewPortHandler.getChartWidth() - * mLegend.getMaxSizePercent()) - + mLegend.getXOffset() * 2f; - - } else if (mLegend.getPosition() == LegendPosition.LEFT_OF_CHART - || mLegend.getPosition() == LegendPosition.LEFT_OF_CHART_CENTER) { - - offsetLeft += Math.min(mLegend.mNeededWidth, mViewPortHandler.getChartWidth() - * mLegend.getMaxSizePercent()) - + mLegend.getXOffset() * 2f; + calculateLegendOffsets(mOffsetsBuffer); - } else if (mLegend.getPosition() == LegendPosition.BELOW_CHART_LEFT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_CENTER) { - - // It's possible that we do not need this offset anymore as it - // is available through the extraOffsets, but changing it can mean - // changing default visibility for existing apps. - float yOffset = mLegend.mTextHeightMax; - - offsetBottom += Math.min(mLegend.mNeededHeight + yOffset, - mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); - - } else if (mLegend.getPosition() == LegendPosition.ABOVE_CHART_LEFT - || mLegend.getPosition() == LegendPosition.ABOVE_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.ABOVE_CHART_CENTER) { - - // It's possible that we do not need this offset anymore as it - // is available through the extraOffsets, but changing it can mean - // changing default visibility for existing apps. - float yOffset = mLegend.mTextHeightMax; - - offsetTop += Math.min(mLegend.mNeededHeight + yOffset, - mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); - - } - } + offsetLeft += mOffsetsBuffer.left; + offsetTop += mOffsetsBuffer.top; + offsetRight += mOffsetsBuffer.right; + offsetBottom += mOffsetsBuffer.bottom; // offsets for y-labels if (mAxisLeft.needsOffset()) { diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java index 2ff098b1ad..593ce6c7fb 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java @@ -56,48 +56,19 @@ protected void init() { mXAxisRenderer = new XAxisRendererHorizontalBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer, this); } + private RectF mOffsetsBuffer = new RectF(); + @Override public void calculateOffsets() { float offsetLeft = 0f, offsetRight = 0f, offsetTop = 0f, offsetBottom = 0f; - // setup offsets for legend - if (mLegend != null && mLegend.isEnabled()) { - - if (mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART || mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART_CENTER) { - - offsetRight += Math.min(mLegend.mNeededWidth, mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) - + mLegend.getXOffset() * 2f; - - } else if (mLegend.getPosition() == LegendPosition.LEFT_OF_CHART - || mLegend.getPosition() == LegendPosition.LEFT_OF_CHART_CENTER) { - - offsetLeft += Math.min(mLegend.mNeededWidth, mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) - + mLegend.getXOffset() * 2f; - - } else if (mLegend.getPosition() == LegendPosition.BELOW_CHART_LEFT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_CENTER) { + calculateLegendOffsets(mOffsetsBuffer); - // It's possible that we do not need this offset anymore as it - // is available through the extraOffsets, but changing it can mean - // changing default visibility for existing apps. - float yOffset = mLegend.mTextHeightMax; - - offsetBottom += Math.min(mLegend.mNeededHeight + yOffset, mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); - - } else if (mLegend.getPosition() == LegendPosition.ABOVE_CHART_LEFT - || mLegend.getPosition() == LegendPosition.ABOVE_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.ABOVE_CHART_CENTER) { - - // It's possible that we do not need this offset anymore as it - // is available through the extraOffsets, but changing it can mean - // changing default visibility for existing apps. - float yOffset = mLegend.mTextHeightMax * 2.f; - - offsetTop += Math.min(mLegend.mNeededHeight + yOffset, mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); - } - } + offsetLeft += mOffsetsBuffer.left; + offsetTop += mOffsetsBuffer.top; + offsetRight += mOffsetsBuffer.right; + offsetBottom += mOffsetsBuffer.bottom; // offsets for y-labels if (mAxisLeft.needsOffset()) { diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java index a7cb8f73ef..5a938872cf 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java @@ -13,6 +13,7 @@ import android.view.MotionEvent; import com.github.mikephil.charting.animation.Easing; +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.data.ChartData; @@ -103,119 +104,118 @@ public void calculateOffsets() { float legendLeft = 0f, legendRight = 0f, legendBottom = 0f, legendTop = 0f; - if (mLegend != null && mLegend.isEnabled()) { - - float fullLegendWidth = Math.min(mLegend.mNeededWidth, - mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) + - mLegend.getFormSize() + mLegend.getFormToTextSpace(); - - if (mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART_CENTER) { - - // this is the space between the legend and the chart - float spacing = Utils.convertDpToPixel(13f); - - legendRight = fullLegendWidth + spacing; - - } else if (mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART) { - - // this is the space between the legend and the chart - float spacing = Utils.convertDpToPixel(8f); - - float legendWidth = fullLegendWidth + spacing; - - float legendHeight = mLegend.mNeededHeight + mLegend.mTextHeightMax; - - PointF c = getCenter(); - - PointF bottomRight = new PointF(getWidth() - legendWidth + 15, legendHeight + 15); - float distLegend = distanceToCenter(bottomRight.x, bottomRight.y); - - PointF reference = getPosition(c, getRadius(), - getAngleForPoint(bottomRight.x, bottomRight.y)); - - float distReference = distanceToCenter(reference.x, reference.y); - float min = Utils.convertDpToPixel(5f); - - if (distLegend < distReference) { - - float diff = distReference - distLegend; - legendRight = min + diff; - } - - if (bottomRight.y >= c.y && getHeight() - legendWidth > getWidth()) { - legendRight = legendWidth; - } - - } else if (mLegend.getPosition() == LegendPosition.LEFT_OF_CHART_CENTER) { - - // this is the space between the legend and the chart - float spacing = Utils.convertDpToPixel(13f); + if (mLegend != null && mLegend.isEnabled() && !mLegend.isDrawInsideEnabled()) { - legendLeft = fullLegendWidth + spacing; - - } else if (mLegend.getPosition() == LegendPosition.LEFT_OF_CHART) { - - // this is the space between the legend and the chart - float spacing = Utils.convertDpToPixel(8f); - - float legendWidth = fullLegendWidth + spacing; - - float legendHeight = mLegend.mNeededHeight + mLegend.mTextHeightMax; - - PointF c = getCenter(); - - PointF bottomLeft = new PointF(legendWidth - 15, legendHeight + 15); - float distLegend = distanceToCenter(bottomLeft.x, bottomLeft.y); - - PointF reference = getPosition(c, getRadius(), - getAngleForPoint(bottomLeft.x, bottomLeft.y)); - - float distReference = distanceToCenter(reference.x, reference.y); - float min = Utils.convertDpToPixel(5f); - - if (distLegend < distReference) { - - float diff = distReference - distLegend; - legendLeft = min + diff; - } + float fullLegendWidth = Math.min(mLegend.mNeededWidth, + mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) + + mLegend.getFormSize() + mLegend.getFormToTextSpace(); - if (bottomLeft.y >= c.y && getHeight() - legendWidth > getWidth()) { - legendLeft = legendWidth; + switch (mLegend.getOrientation()) { + case VERTICAL: + { + float xLegendOffset = 0.f; + + if (mLegend.getHorizontalAlignment() == Legend.LegendHorizontalAlignment.LEFT + || mLegend.getHorizontalAlignment() == Legend.LegendHorizontalAlignment.RIGHT) { + if (mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.CENTER) { + // this is the space between the legend and the chart + final float spacing = Utils.convertDpToPixel(13f); + + xLegendOffset = fullLegendWidth + spacing; + + } else { + // this is the space between the legend and the chart + float spacing = Utils.convertDpToPixel(8f); + + float legendWidth = fullLegendWidth + spacing; + float legendHeight = mLegend.mNeededHeight + mLegend.mTextHeightMax; + + PointF c = getCenter(); + + float bottomX = mLegend.getHorizontalAlignment() == + Legend.LegendHorizontalAlignment.RIGHT + ? getWidth() - legendWidth + 15.f + : legendWidth - 15.f; + float bottomY = legendHeight + 15.f; + float distLegend = distanceToCenter(bottomX, bottomY); + + PointF reference = getPosition(c, getRadius(), + getAngleForPoint(bottomX, bottomY)); + + float distReference = distanceToCenter(reference.x, reference.y); + float minOffset = Utils.convertDpToPixel(5f); + + if (bottomY >= c.y && getHeight() - legendWidth > getWidth()) { + xLegendOffset = legendWidth; + } else if (distLegend < distReference) { + + float diff = distReference - distLegend; + xLegendOffset = minOffset + diff; + } + } + } + + switch (mLegend.getHorizontalAlignment()) { + case LEFT: + legendLeft = xLegendOffset; + break; + + case RIGHT: + legendRight = xLegendOffset; + break; + + case CENTER: + switch (mLegend.getVerticalAlignment()) { + case TOP: + legendTop = Math.min(mLegend.mNeededHeight, + mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); + break; + case BOTTOM: + legendBottom = Math.min(mLegend.mNeededHeight, + mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); + break; + } + break; + } } - - } else if (mLegend.getPosition() == LegendPosition.BELOW_CHART_LEFT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_CENTER) { - - // It's possible that we do not need this offset anymore as it - // is available through the extraOffsets, but changing it can mean - // changing default visibility for existing apps. - float yOffset = getRequiredLegendOffset(); - - legendBottom = Math.min(mLegend.mNeededHeight + yOffset, mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); - - } else if (mLegend.getPosition() == LegendPosition.ABOVE_CHART_LEFT - || mLegend.getPosition() == LegendPosition.ABOVE_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.ABOVE_CHART_CENTER) { - - // It's possible that we do not need this offset anymore as it - // is available through the extraOffsets, but changing it can mean - // changing default visibility for existing apps. - float yOffset = getRequiredLegendOffset(); - - legendTop = Math.min(mLegend.mNeededHeight + yOffset, mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); - + break; + + case HORIZONTAL: + float yLegendOffset = 0.f; + + if (mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.TOP || + mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.BOTTOM) { + + // It's possible that we do not need this offset anymore as it + // is available through the extraOffsets, but changing it can mean + // changing default visibility for existing apps. + float yOffset = getRequiredLegendOffset(); + + yLegendOffset = Math.min(mLegend.mNeededHeight + yOffset, + mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); + + switch (mLegend.getVerticalAlignment()) { + case TOP: + legendTop = yLegendOffset; + break; + case BOTTOM: + legendBottom = yLegendOffset; + break; + } + } + break; } legendLeft += getRequiredBaseOffset(); legendRight += getRequiredBaseOffset(); legendTop += getRequiredBaseOffset(); + legendBottom += getRequiredBaseOffset(); } float minOffset = Utils.convertDpToPixel(mMinOffset); if (this instanceof RadarChart) { - XAxis x = ((RadarChart) this).getXAxis(); + XAxis x = this.getXAxis(); if (x.isEnabled() && x.isDrawLabelsEnabled()) { minOffset = Math.max(minOffset, x.mLabelRotatedWidth); diff --git a/MPChartLib/src/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/com/github/mikephil/charting/components/Legend.java index 0d1cd4d23d..77f1710f27 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/Legend.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/Legend.java @@ -20,6 +20,9 @@ */ public class Legend extends ComponentBase { + /** + * This property is deprecated - Use `position`, `horizontalAlignment`, `verticalAlignment`, `orientation`, `drawInside`, `direction`. + */ 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, @@ -32,6 +35,21 @@ public enum LegendForm { SQUARE, CIRCLE, LINE } + public enum LegendHorizontalAlignment + { + LEFT, CENTER, RIGHT + } + + public enum LegendVerticalAlignment + { + TOP, CENTER, BOTTOM + } + + public enum LegendOrientation + { + HORIZONTAL, VERTICAL + } + public enum LegendDirection { LEFT_TO_RIGHT, RIGHT_TO_LEFT } @@ -63,8 +81,10 @@ public enum LegendDirection { */ private boolean mIsLegendCustom = false; - /** the position relative to the chart the legend is drawn on */ - private LegendPosition mPosition = LegendPosition.BELOW_CHART_LEFT; + private LegendHorizontalAlignment mHorizontalAlignment = LegendHorizontalAlignment.LEFT; + private LegendVerticalAlignment mVerticalAlignment = LegendVerticalAlignment.BOTTOM; + private LegendOrientation mOrientation = LegendOrientation.HORIZONTAL; + private boolean mDrawInside = false; /** the text direction for the legend */ private LegendDirection mDirection = LegendDirection.LEFT_TO_RIGHT; @@ -351,16 +371,175 @@ public boolean isLegendCustom() { * @return */ public LegendPosition getPosition() { - return mPosition; + + 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); + } } /** * sets the position of the legend relative to the whole chart * - * @param pos + * @param newValue + */ + 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 + * + * @return + */ + public LegendHorizontalAlignment getHorizontalAlignment() { + return mHorizontalAlignment; + } + + /** + * sets the horizontal alignment of the legend + * + * @param value + */ + public void setHorizontalAlignment(LegendHorizontalAlignment value) { + mHorizontalAlignment = value; + } + + /** + * returns the vertical alignment of the legend + * + * @return + */ + public LegendVerticalAlignment getVerticalAlignment() { + return mVerticalAlignment; + } + + /** + * sets the vertical alignment of the legend + * + * @param value + */ + public void setVerticalAlignment(LegendVerticalAlignment value) { + mVerticalAlignment = value; + } + + /** + * returns the orientation of the legend + * + * @return */ - public void setPosition(LegendPosition pos) { - mPosition = pos; + public LegendOrientation getOrientation() { + return mOrientation; + } + + /** + * sets the orientation of the legend + * + * @param value + */ + public void setOrientation(LegendOrientation value) { + mOrientation = value; + } + + /** + * returns whether the legend will draw inside the chart or outside + * + * @return + */ + public boolean isDrawInsideEnabled() { + return mDrawInside; + } + + /** + * sets whether the legend will draw inside the chart or outside + * + * @param value + */ + public void setDrawInside(boolean value) { + mDrawInside = value; } /** @@ -605,9 +784,7 @@ public float getMaxSizePercent() { * The maximum relative size out of the whole chart view. / If * the legend is to the right/left of the chart, then this affects the width * of the legend. / If the legend is to the top/bottom of the chart, then - * this affects the height of the legend. / If the legend is the center of - * the PieChart, then this defines the size of the rectangular bounds out of - * the size of the "hole". / default: 0.95f (95%) + * this affects the height of the legend. / default: 0.95f (95%) * * @param maxSize */ @@ -640,132 +817,168 @@ public FSize[] getCalculatedLineSizes() { */ public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandler) { - if (mPosition == LegendPosition.RIGHT_OF_CHART - || mPosition == LegendPosition.RIGHT_OF_CHART_CENTER - || mPosition == LegendPosition.LEFT_OF_CHART - || mPosition == LegendPosition.LEFT_OF_CHART_CENTER - || mPosition == LegendPosition.PIECHART_CENTER) { - mNeededWidth = getMaximumEntryWidth(labelpaint); - mNeededHeight = getFullHeight(labelpaint); - mTextWidthMax = mNeededWidth; - mTextHeightMax = getMaximumEntryHeight(labelpaint); - - } else if (mPosition == LegendPosition.BELOW_CHART_LEFT - || mPosition == LegendPosition.BELOW_CHART_RIGHT - || mPosition == LegendPosition.BELOW_CHART_CENTER - || mPosition == LegendPosition.ABOVE_CHART_LEFT - || mPosition == LegendPosition.ABOVE_CHART_RIGHT - || mPosition == LegendPosition.ABOVE_CHART_CENTER) { - - int labelCount = mLabels.length; - float labelLineHeight = Utils.getLineHeight(labelpaint); - float labelLineSpacing = Utils.getLineSpacing(labelpaint) + mYEntrySpace; - float contentWidth = viewPortHandler.contentWidth(); - - // Prepare arrays for calculated layout - ArrayList calculatedLabelSizes = new ArrayList(labelCount); - ArrayList calculatedLabelBreakPoints = new ArrayList(labelCount); - ArrayList calculatedLineSizes = new ArrayList(); - - // Start calculating layout - float maxLineWidth = 0.f; - float currentLineWidth = 0.f; - float requiredWidth = 0.f; - int stackedStartIndex = -1; - - for (int i = 0; i < labelCount; i++) { - - boolean drawingForm = mColors[i] != ColorTemplate.COLOR_SKIP; - - calculatedLabelBreakPoints.add(false); - - if (stackedStartIndex == -1) - { - // we are not stacking, so required width is for this label - // only - requiredWidth = 0.f; - } else { - // add the spacing appropriate for stacked labels/forms - requiredWidth += mStackSpace; - } + mTextWidthMax = getMaximumEntryWidth(labelpaint); + mTextHeightMax = getMaximumEntryHeight(labelpaint); - // grouped forms have null labels - if (mLabels[i] != null) { + switch (mOrientation) { + case VERTICAL: { - calculatedLabelSizes.add(Utils.calcTextSize(labelpaint, mLabels[i])); - requiredWidth += drawingForm ? mFormToTextSpace + mFormSize : 0.f; - requiredWidth += calculatedLabelSizes.get(i).width; - } else { + float maxWidth = 0f, maxHeight = 0f, width = 0f; + float labelLineHeight = Utils.getLineHeight(labelpaint); + final int count = mLabels.length; + boolean wasStacked = false; - calculatedLabelSizes.add(new FSize(0.f, 0.f)); - requiredWidth += drawingForm ? mFormSize : 0.f; + for (int i = 0; i < count; i++) { - if (stackedStartIndex == -1) { - // mark this index as we might want to break here later - stackedStartIndex = i; + boolean drawingForm = mColors[i] != ColorTemplate.COLOR_SKIP; + + if (!wasStacked) + width = 0.f; + + if (drawingForm) { + if (wasStacked) + width += mStackSpace; + width += mFormSize; } + + // grouped forms have null labels + if (mLabels[i] != null) { + + // make a step to the left + if (drawingForm && !wasStacked) + width += mFormToTextSpace; + else if (wasStacked) { + maxWidth = Math.max(maxWidth, width); + maxHeight += labelLineHeight + mYEntrySpace; + width = 0.f; + wasStacked = false; + } + + width += Utils.calcTextWidth(labelpaint, mLabels[i]); + + if (i < count - 1) + maxHeight += labelLineHeight + mYEntrySpace; + } + else { + wasStacked = true; + width += mFormSize; + if (i < count - 1) + width += mStackSpace; + } + + maxWidth = Math.max(maxWidth, width); } - if (mLabels[i] != null || i == labelCount - 1) { + mNeededWidth = maxWidth; + mNeededHeight = maxHeight; + + break; + } + case HORIZONTAL: { + + int labelCount = mLabels.length; + float labelLineHeight = Utils.getLineHeight(labelpaint); + float labelLineSpacing = Utils.getLineSpacing(labelpaint) + mYEntrySpace; + float contentWidth = viewPortHandler.contentWidth() * mMaxSizePercent; + + // Prepare arrays for calculated layout + ArrayList calculatedLabelSizes = new ArrayList(labelCount); + ArrayList calculatedLabelBreakPoints = new ArrayList(labelCount); + ArrayList calculatedLineSizes = new ArrayList(); - float requiredSpacing = currentLineWidth == 0.f ? 0.f : mXEntrySpace; + // Start calculating layout + float maxLineWidth = 0.f; + float currentLineWidth = 0.f; + float requiredWidth = 0.f; + int stackedStartIndex = -1; - if (!mWordWrapEnabled || // No word wrapping, it must fit. - currentLineWidth == 0.f || // The line is empty, it - // must fit. - (contentWidth - currentLineWidth >= requiredSpacing + requiredWidth)) // It - // simply - // fits - { - // Expand current line - currentLineWidth += requiredSpacing + requiredWidth; + for (int i = 0; i < labelCount; i++) { - } else { // It doesn't fit, we need to wrap a line + boolean drawingForm = mColors[i] != ColorTemplate.COLOR_SKIP; - // Add current line size to array - calculatedLineSizes.add(new FSize(currentLineWidth, labelLineHeight)); - maxLineWidth = Math.max(maxLineWidth, currentLineWidth); + calculatedLabelBreakPoints.add(false); - // Start a new line - calculatedLabelBreakPoints.set(stackedStartIndex > -1 ? stackedStartIndex - : i, true); - currentLineWidth = requiredWidth; + if (stackedStartIndex == -1) { + // we are not stacking, so required width is for this label + // only + requiredWidth = 0.f; + } + else { + // add the spacing appropriate for stacked labels/forms + requiredWidth += mStackSpace; + } + + // grouped forms have null labels + if (mLabels[i] != null) { + + calculatedLabelSizes.add(Utils.calcTextSize(labelpaint, mLabels[i])); + requiredWidth += drawingForm ? mFormToTextSpace + mFormSize : 0.f; + requiredWidth += calculatedLabelSizes.get(i).width; } + else { - if (i == labelCount - 1) { - // Add last line size to array - calculatedLineSizes.add(new FSize(currentLineWidth, labelLineHeight)); - maxLineWidth = Math.max(maxLineWidth, currentLineWidth); + calculatedLabelSizes.add(new FSize(0.f, 0.f)); + requiredWidth += drawingForm ? mFormSize : 0.f; + + if (stackedStartIndex == -1) { + // mark this index as we might want to break here later + stackedStartIndex = i; + } + } + + if (mLabels[i] != null || i == labelCount - 1) { + + float requiredSpacing = currentLineWidth == 0.f ? 0.f : mXEntrySpace; + + if (!mWordWrapEnabled // No word wrapping, it must fit. + // The line is empty, it must fit + || currentLineWidth == 0.f + // It simply fits + || (contentWidth - currentLineWidth >= + requiredSpacing + requiredWidth)) { + // Expand current line + currentLineWidth += requiredSpacing + requiredWidth; + } + else { // It doesn't fit, we need to wrap a line + + // Add current line size to array + calculatedLineSizes.add(new FSize(currentLineWidth, labelLineHeight)); + maxLineWidth = Math.max(maxLineWidth, currentLineWidth); + + // Start a new line + calculatedLabelBreakPoints.set( + stackedStartIndex > -1 ? stackedStartIndex + : i, true); + currentLineWidth = requiredWidth; + } + + if (i == labelCount - 1) { + // Add last line size to array + calculatedLineSizes.add(new FSize(currentLineWidth, labelLineHeight)); + maxLineWidth = Math.max(maxLineWidth, currentLineWidth); + } } + + stackedStartIndex = mLabels[i] != null ? -1 : stackedStartIndex; } - stackedStartIndex = mLabels[i] != null ? -1 : stackedStartIndex; + mCalculatedLabelSizes = calculatedLabelSizes.toArray( + new FSize[calculatedLabelSizes.size()]); + mCalculatedLabelBreakPoints = calculatedLabelBreakPoints + .toArray(new Boolean[calculatedLabelBreakPoints.size()]); + mCalculatedLineSizes = calculatedLineSizes + .toArray(new FSize[calculatedLineSizes.size()]); + + mNeededWidth = maxLineWidth; + mNeededHeight = labelLineHeight + * (float) (mCalculatedLineSizes.length) + + labelLineSpacing * + (float) (mCalculatedLineSizes.length == 0 + ? 0 + : (mCalculatedLineSizes.length - 1)); + + break; } - - mCalculatedLabelSizes = calculatedLabelSizes.toArray( - new FSize[calculatedLabelSizes.size()]); - mCalculatedLabelBreakPoints = calculatedLabelBreakPoints - .toArray(new Boolean[calculatedLabelBreakPoints.size()]); - mCalculatedLineSizes = calculatedLineSizes - .toArray(new FSize[calculatedLineSizes.size()]); - - mTextWidthMax = getMaximumEntryWidth(labelpaint); - mTextHeightMax = getMaximumEntryHeight(labelpaint); - mNeededWidth = maxLineWidth; - mNeededHeight = labelLineHeight - * (float) (mCalculatedLineSizes.length) - + labelLineSpacing * - (float) (mCalculatedLineSizes.length == 0 - ? 0 - : (mCalculatedLineSizes.length - 1)); - - } else { - /* RIGHT_OF_CHART_INSIDE, LEFT_OF_CHART_INSIDE */ - - mNeededWidth = getFullWidth(labelpaint); - mNeededHeight = getMaximumEntryHeight(labelpaint); - mTextWidthMax = getMaximumEntryWidth(labelpaint); - mTextHeightMax = mNeededHeight; } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java index 9a24030dcd..f005a1f2ac 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -195,57 +195,89 @@ public void renderLegend(Canvas c) { float formToTextSpace = mLegend.getFormToTextSpace(); float xEntrySpace = mLegend.getXEntrySpace(); + Legend.LegendOrientation orientation = mLegend.getOrientation(); + Legend.LegendHorizontalAlignment horizontalAlignment = mLegend.getHorizontalAlignment(); + Legend.LegendVerticalAlignment verticalAlignment = mLegend.getVerticalAlignment(); Legend.LegendDirection direction = mLegend.getDirection(); float formSize = mLegend.getFormSize(); // space between the entries float stackSpace = mLegend.getStackSpace(); - float posX, posY; - float yoffset = mLegend.getYOffset(); float xoffset = mLegend.getXOffset(); + float originPosX = 0.f; - Legend.LegendPosition legendPosition = mLegend.getPosition(); + switch (horizontalAlignment) { + case LEFT: - switch (legendPosition) { - case BELOW_CHART_LEFT: - case BELOW_CHART_RIGHT: - case BELOW_CHART_CENTER: - case ABOVE_CHART_LEFT: - case ABOVE_CHART_RIGHT: - case ABOVE_CHART_CENTER: { - float contentWidth = mViewPortHandler.contentWidth(); + if (orientation == Legend.LegendOrientation.VERTICAL) + originPosX = xoffset; + else + originPosX = mViewPortHandler.contentLeft() + xoffset; - float originPosX; + if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) + originPosX += mLegend.mNeededWidth; - if (legendPosition == Legend.LegendPosition.BELOW_CHART_LEFT - || legendPosition == Legend.LegendPosition.ABOVE_CHART_LEFT) { - originPosX = mViewPortHandler.contentLeft() + xoffset; + break; + + case RIGHT: - if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - originPosX += mLegend.mNeededWidth; - } else if (legendPosition == Legend.LegendPosition.BELOW_CHART_RIGHT - || legendPosition == Legend.LegendPosition.ABOVE_CHART_RIGHT) { + if (orientation == Legend.LegendOrientation.VERTICAL) + originPosX = mViewPortHandler.getChartWidth() - xoffset; + else originPosX = mViewPortHandler.contentRight() - xoffset; - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - originPosX -= mLegend.mNeededWidth; - } else // BELOW_CHART_CENTER || ABOVE_CHART_CENTER - originPosX = mViewPortHandler.contentLeft() + contentWidth / 2.f + xoffset; + if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) + originPosX -= mLegend.mNeededWidth; + + break; + + case CENTER: + + if (orientation == Legend.LegendOrientation.VERTICAL) + originPosX = mViewPortHandler.getChartWidth() / 2.f; + else + originPosX = mViewPortHandler.contentLeft() + + mViewPortHandler.contentWidth() / 2.f; + + originPosX += (direction == Legend.LegendDirection.LEFT_TO_RIGHT + ? +xoffset + : -xoffset); + + // Horizontally layed out legends do the center offset on a line basis, + // So here we offset the vertical ones only. + if (orientation == Legend.LegendOrientation.VERTICAL) { + originPosX += (direction == Legend.LegendDirection.LEFT_TO_RIGHT + ? -mLegend.mNeededWidth / 2.0 + xoffset + : mLegend.mNeededWidth / 2.0 - xoffset); + } + + break; + } + + switch (orientation) { + case HORIZONTAL: { FSize[] calculatedLineSizes = mLegend.getCalculatedLineSizes(); FSize[] calculatedLabelSizes = mLegend.getCalculatedLabelSizes(); Boolean[] calculatedLabelBreakPoints = mLegend.getCalculatedLabelBreakPoints(); - posX = originPosX; + float posX = originPosX; + float posY = 0.f; + + switch (verticalAlignment) { + case TOP: + posY = yoffset; + break; - if (legendPosition == Legend.LegendPosition.ABOVE_CHART_LEFT || - legendPosition == Legend.LegendPosition.ABOVE_CHART_RIGHT || - legendPosition == Legend.LegendPosition.ABOVE_CHART_CENTER) { - posY = yoffset; - } else { - posY = mViewPortHandler.getChartHeight() - yoffset - mLegend.mNeededHeight; + case BOTTOM: + posY = mViewPortHandler.getChartHeight() - yoffset - mLegend.mNeededHeight; + break; + + case CENTER: + posY = (mViewPortHandler.getChartHeight() - mLegend.mNeededHeight) / 2.f + yoffset; + break; } int lineIndex = 0; @@ -257,10 +289,11 @@ public void renderLegend(Canvas c) { } if (posX == originPosX && - (legendPosition == Legend.LegendPosition.BELOW_CHART_CENTER || - legendPosition == Legend.LegendPosition.ABOVE_CHART_CENTER) && + horizontalAlignment == Legend.LegendHorizontalAlignment.CENTER && lineIndex < calculatedLineSizes.length) { - posX += (direction == Legend.LegendDirection.RIGHT_TO_LEFT ? calculatedLineSizes[lineIndex].width : -calculatedLineSizes[lineIndex].width) / 2.f; + posX += (direction == Legend.LegendDirection.RIGHT_TO_LEFT + ? calculatedLineSizes[lineIndex].width + : -calculatedLineSizes[lineIndex].width) / 2.f; lineIndex++; } @@ -294,91 +327,70 @@ public void renderLegend(Canvas c) { posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -stackSpace : stackSpace; } + break; } - break; - case PIECHART_CENTER: - case RIGHT_OF_CHART: - case RIGHT_OF_CHART_CENTER: - case RIGHT_OF_CHART_INSIDE: - case LEFT_OF_CHART: - case LEFT_OF_CHART_CENTER: - case LEFT_OF_CHART_INSIDE: { + case VERTICAL: { // contains the stacked legend size in pixels float stack = 0f; boolean wasStacked = false; - - if (legendPosition == Legend.LegendPosition.PIECHART_CENTER) { - posX = mViewPortHandler.getChartWidth() / 2f - + (direction == Legend.LegendDirection.LEFT_TO_RIGHT ? -mLegend.mTextWidthMax / 2f - : mLegend.mTextWidthMax / 2f); - posY = mViewPortHandler.getChartHeight() / 2f - mLegend.mNeededHeight / 2f - + mLegend.getYOffset(); - } else { - boolean isRightAligned = legendPosition == Legend.LegendPosition.RIGHT_OF_CHART - || - legendPosition == Legend.LegendPosition.RIGHT_OF_CHART_CENTER || - legendPosition == Legend.LegendPosition.RIGHT_OF_CHART_INSIDE; - - if (isRightAligned) { - posX = mViewPortHandler.getChartWidth() - xoffset; - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - posX -= mLegend.mTextWidthMax; - } else { - posX = xoffset; - if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - posX += mLegend.mTextWidthMax; - } - - if (legendPosition == Legend.LegendPosition.RIGHT_OF_CHART || - legendPosition == Legend.LegendPosition.LEFT_OF_CHART) { - posY = mViewPortHandler.contentTop() + yoffset; - } else if (legendPosition == Legend.LegendPosition.RIGHT_OF_CHART_CENTER || - legendPosition == Legend.LegendPosition.LEFT_OF_CHART_CENTER) { - posY = mViewPortHandler.getChartHeight() / 2f - mLegend.mNeededHeight / 2f; - } else /* - * if (legendPosition == - * Legend.LegendPosition.RIGHT_OF_CHART_INSIDE || - * legendPosition == - * Legend.LegendPosition.LEFT_OF_CHART_INSIDE) - */ { - posY = mViewPortHandler.contentTop() + yoffset; - } + float posY = 0.f; + + switch (verticalAlignment) { + case TOP: + posY = (horizontalAlignment == Legend.LegendHorizontalAlignment.CENTER + ? 0.f + : mViewPortHandler.contentTop()); + posY += yoffset; + break; + + case BOTTOM: + posY = (horizontalAlignment == Legend.LegendHorizontalAlignment.CENTER + ? mViewPortHandler.getChartHeight() + : mViewPortHandler.contentBottom()); + posY -= mLegend.mNeededHeight + yoffset; + break; + + case CENTER: + posY = mViewPortHandler.getChartHeight() / 2.f + - mLegend.mNeededHeight / 2.f + + mLegend.getYOffset(); + break; } for (int i = 0; i < labels.length; i++) { Boolean drawingForm = colors[i] != ColorTemplate.COLOR_SKIP; - float x = posX; + float posX = originPosX; if (drawingForm) { if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - x += stack; + posX += stack; else - x -= formSize - stack; + posX -= formSize - stack; - drawForm(c, x, posY + formYOffset, i, mLegend); + drawForm(c, posX, posY + formYOffset, i, mLegend); if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - x += formSize; + posX += formSize; } if (labels[i] != null) { if (drawingForm && !wasStacked) - x += direction == Legend.LegendDirection.LEFT_TO_RIGHT ? formToTextSpace + posX += direction == Legend.LegendDirection.LEFT_TO_RIGHT ? formToTextSpace : -formToTextSpace; else if (wasStacked) - x = posX; + posX = originPosX; if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - x -= Utils.calcTextWidth(mLegendLabelPaint, labels[i]); + posX -= Utils.calcTextWidth(mLegendLabelPaint, labels[i]); if (!wasStacked) { - drawLabel(c, x, posY + labelLineHeight, labels[i]); + drawLabel(c, posX, posY + labelLineHeight, labels[i]); } else { posY += labelLineHeight + labelLineSpacing; - drawLabel(c, x, posY + labelLineHeight, labels[i]); + drawLabel(c, posX, posY + labelLineHeight, labels[i]); } // make a step down @@ -389,8 +401,10 @@ else if (wasStacked) wasStacked = true; } } + + break; + } - break; } } From 42981cc98d0c950d9c56695457629aa4d66d8a11 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 11 Apr 2016 10:23:11 +0300 Subject: [PATCH 087/606] Protect loop from infinite loop in the highest of zooms --- .../github/mikephil/charting/renderer/YAxisRenderer.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java index cbc883e8aa..fdae353e4b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -140,8 +140,10 @@ protected void computeAxisValues(float min, float max) { double f; int i; int n = 0; - for (f = first; f <= last; f += interval) { - ++n; + if (interval != 0.0) { + for (f = first; f <= last; f += interval) { + ++n; + } } mYAxis.mEntryCount = n; From f7eabd0718422f73e34c597cd4b4d12ca8a82582 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 12 Apr 2016 10:42:51 +0200 Subject: [PATCH 088/606] Update gradle --- MPChartExample/build.gradle | 2 +- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 623e709748..5cef000d7c 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -37,7 +37,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'com.android.tools.build:gradle:2.0.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 1dbeb8610c..66dbb5af3c 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'com.android.tools.build:gradle:2.0.0' 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 1af18b88f2..f39a959361 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Nov 19 21:53:09 CST 2015 +#Tue Apr 12 10:33:07 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip From 055b5a6c4c850077017c4027de60ed3cd34acd20 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 12 Apr 2016 11:34:58 +0200 Subject: [PATCH 089/606] Add material colors --- .../mpchartexample/BarChartActivity.java | 17 ++++++++++------- .../mikephil/charting/utils/ColorTemplate.java | 3 +++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 626890aa03..1a25a35cf6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -35,6 +35,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.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.custom.MyYAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -255,16 +256,18 @@ private void setData(int count, float range) { } else { set1 = new BarDataSet(yVals1, "DataSet"); set1.setBarSpacePercent(35f); + set1.setColors(ColorTemplate.MATERIAL_COLORS); + } - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - BarData data = new BarData(xVals, dataSets); - data.setValueTextSize(10f); - data.setValueTypeface(mTf); + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); - mChart.setData(data); - } + BarData data = new BarData(xVals, dataSets); + data.setValueTextSize(10f); + data.setValueTypeface(mTf); + + mChart.setData(data); } @SuppressLint("NewApi") diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java b/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java index 0818d05a9f..106671ad13 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java @@ -51,6 +51,9 @@ public class ColorTemplate { Color.rgb(192, 255, 140), Color.rgb(255, 247, 140), Color.rgb(255, 208, 140), Color.rgb(140, 234, 255), Color.rgb(255, 140, 157) }; + public static final int[] MATERIAL_COLORS = { + rgb("#2ecc71"), rgb("#f1c40f"), rgb("#e74c3c"), rgb("#3498db") + }; /** * Converts the given hex-color-string to rgb. From bef8db1af1f2c888ddeff9bf31301233c5c5e10e Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 12 Apr 2016 14:06:13 +0300 Subject: [PATCH 090/606] Fixed a null exception when using markers with pie chart --- MPChartLib/src/com/github/mikephil/charting/charts/Chart.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java index 055bdf7312..0c837bb1ff 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java @@ -670,7 +670,9 @@ protected void drawMarkers(Canvas canvas) { int xIndex = highlight.getXIndex(); int dataSetIndex = highlight.getDataSetIndex(); - float deltaX = mXAxis.mAxisRange; + float deltaX = mXAxis != null + ? mXAxis.mAxisRange + : ((mData == null ? 0.f : mData.getXValCount()) - 1.f); if (xIndex <= deltaX && xIndex <= deltaX * mAnimator.getPhaseX()) { From cfb3fdf0a946f313124cb98e96e5683966f10960 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 13 Apr 2016 16:56:31 +0300 Subject: [PATCH 091/606] Fixed dataset update in demo --- .../mpchartexample/BarChartActivity.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 1a25a35cf6..083d016e64 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -257,17 +257,16 @@ private void setData(int count, float range) { set1 = new BarDataSet(yVals1, "DataSet"); set1.setBarSpacePercent(35f); set1.setColors(ColorTemplate.MATERIAL_COLORS); - } - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); - BarData data = new BarData(xVals, dataSets); - data.setValueTextSize(10f); - data.setValueTypeface(mTf); + BarData data = new BarData(xVals, dataSets); + data.setValueTextSize(10f); + data.setValueTypeface(mTf); - mChart.setData(data); + mChart.setData(data); + } } @SuppressLint("NewApi") From 4e50305dd3618ad12ef9c5a2e271df90bc7f2b12 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 13 Apr 2016 16:57:52 +0300 Subject: [PATCH 092/606] Avoid division by zeros --- .../github/mikephil/charting/utils/ViewPortHandler.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java b/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java index 44797c3f40..6cd677f772 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java @@ -442,6 +442,9 @@ public void setMinimumScaleX(float xScale) { */ public void setMaximumScaleX(float xScale) { + if (xScale == 0.f) + xScale = Float.MAX_VALUE; + mMaxScaleX = xScale; limitTransAndScale(mMatrixTouch, mContentRect); @@ -458,6 +461,9 @@ public void setMinMaxScaleX(float minScaleX, float maxScaleX) { if (minScaleX < 1f) minScaleX = 1f; + if (maxScaleX == 0.f) + maxScaleX = Float.MAX_VALUE; + mMinScaleX = minScaleX; mMaxScaleX = maxScaleX; @@ -486,6 +492,9 @@ public void setMinimumScaleY(float yScale) { */ public void setMaximumScaleY(float yScale) { + if (yScale == 0.f) + yScale = Float.MAX_VALUE; + mMaxScaleY = yScale; limitTransAndScale(mMatrixTouch, mContentRect); From 0b43eeaf2d9b60d3d3a033e3b6a7f39a4241daba Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 13 Apr 2016 16:57:57 +0300 Subject: [PATCH 093/606] Removed unnecessary ternary expression --- .../src/com/github/mikephil/charting/utils/ViewPortHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java b/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java index 6cd677f772..955e39cf2d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java @@ -667,7 +667,7 @@ public void setDragOffsetY(float offset) { * @return */ public boolean hasNoDragOffset() { - return mTransOffsetX <= 0 && mTransOffsetY <= 0 ? true : false; + return mTransOffsetX <= 0 && mTransOffsetY <= 0; } /** From ff0b8c375f59fbe8010a0df9a1809db65f940ab6 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 13 Apr 2016 17:20:33 +0300 Subject: [PATCH 094/606] Make these a little clearer --- .../com/github/mikephil/charting/charts/BarLineChartBase.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index 9bba9d5886..2c70824d12 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -1345,7 +1345,7 @@ public int getLowestVisibleXIndex() { mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom() }; getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return (pts[0] <= 0) ? 0 : (int) (pts[0] + 1.0f); + return (pts[0] <= 0) ? 0 : (int)Math.ceil(pts[0]); } /** @@ -1360,7 +1360,7 @@ public int getHighestVisibleXIndex() { mViewPortHandler.contentRight(), mViewPortHandler.contentBottom() }; getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return (pts[0] >= mData.getXValCount()) ? mData.getXValCount() - 1 : (int) pts[0]; + return Math.min(mData.getXValCount() - 1, (int)Math.floor(pts[0])); } /** From 1c312e85a9c6f868f76e886386621ebd3555a7d7 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 13 Apr 2016 18:05:00 +0300 Subject: [PATCH 095/606] Allow bubble sizes to not be normalized against the dataset max --- .../mikephil/charting/data/BubbleDataSet.java | 10 ++++++++++ .../implementation/RealmBubbleDataSet.java | 10 ++++++++++ .../interfaces/datasets/IBubbleDataSet.java | 2 ++ .../charting/renderer/BubbleChartRenderer.java | 17 +++++++++++++---- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java index 5672ecb0bb..42be25b61b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java @@ -16,6 +16,7 @@ public class BubbleDataSet extends BarLineScatterCandleBubbleDataSet extends RealmBarLineScatt protected float mXMax; protected float mXMin; protected float mMaxSize; + protected boolean mNormalizeSize = true; private float mHighlightCircleWidth = 2.5f; @@ -146,6 +147,15 @@ public float getMaxSize() { return mMaxSize; } + @Override + public boolean isNormalizeSizeEnabled() { + return mNormalizeSize; + } + + public void setNormalizeSizeEnabled(boolean normalizeSize) { + mNormalizeSize = normalizeSize; + } + private float yMin(BubbleEntry entry) { return entry.getVal(); } diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java index 2764b56d1d..89991f718d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java @@ -21,6 +21,8 @@ public interface IBubbleDataSet extends IBarLineScatterCandleBubbleDataSet Date: Wed, 13 Apr 2016 18:50:46 +0300 Subject: [PATCH 096/606] Fixed crash when phaseX causes entryForIndex to go out of bounds --- .../charting/renderer/BubbleChartRenderer.java | 6 +++--- .../charting/renderer/CandleStickChartRenderer.java | 2 +- .../mikephil/charting/renderer/LineChartRenderer.java | 10 +++++----- .../charting/renderer/ScatterChartRenderer.java | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index 0cdb145a46..1988aa7692 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -71,7 +71,7 @@ protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - float phaseX = mAnimator.getPhaseX(); + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); BubbleEntry entryFrom = dataSet.getEntryForXIndex(mMinX); @@ -145,7 +145,7 @@ public void drawValues(Canvas c) { // apply the text-styling defined by the DataSet applyValueTextStyle(dataSet); - final float phaseX = mAnimator.getPhaseX(); + final float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); final float phaseY = mAnimator.getPhaseY(); BubbleEntry entryFrom = dataSet.getEntryForXIndex(mMinX); @@ -194,7 +194,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { BubbleData bubbleData = mChart.getBubbleData(); - float phaseX = mAnimator.getPhaseX(); + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); for (Highlight indice : indices) { diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index 2ef6c0962c..790d83ae92 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -54,7 +54,7 @@ protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - float phaseX = mAnimator.getPhaseX(); + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); float barSpace = dataSet.getBarSpace(); boolean showCandleBar = dataSet.getShowCandleBar(); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index 430aa3edfe..83ca529815 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -142,7 +142,7 @@ protected void drawHorizontalBezier(Canvas c, ILineDataSet dataSet) { int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); - float phaseX = mAnimator.getPhaseX(); + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); float intensity = dataSet.getCubicIntensity(); @@ -208,7 +208,7 @@ protected void drawCubicBezier(Canvas c, ILineDataSet dataSet) { int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); - float phaseX = mAnimator.getPhaseX(); + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); float intensity = dataSet.getCubicIntensity(); @@ -322,7 +322,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - float phaseX = mAnimator.getPhaseX(); + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); mRenderPaint.setStyle(Paint.Style.STROKE); @@ -488,7 +488,7 @@ protected void drawLinearFill(Canvas c, ILineDataSet dataSet, int minx, private Path generateFilledPath(ILineDataSet dataSet, int from, int to) { float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart); - float phaseX = mAnimator.getPhaseX(); + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); final boolean isDrawSteppedEnabled = dataSet.isDrawSteppedEnabled(); @@ -593,7 +593,7 @@ protected void drawCircles(Canvas c) { mRenderPaint.setStyle(Paint.Style.FILL); - float phaseX = mAnimator.getPhaseX(); + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); float[] circlesBuffer = new float[2]; diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index 37a1672ac6..05f15c7e8b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -61,7 +61,7 @@ protected void drawDataSet(Canvas c, IScatterDataSet dataSet) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - float phaseX = mAnimator.getPhaseX(); + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); final float shapeSize = Utils.convertDpToPixel(dataSet.getScatterShapeSize()); From fcd696a979f7358d1d2d1bcb1520e3863c2fa92a Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 13 Apr 2016 19:36:43 +0300 Subject: [PATCH 097/606] Allow setting xVals on Data --- .../src/com/github/mikephil/charting/data/ChartData.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java index 71663eb2cd..7a01996bc6 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java @@ -376,6 +376,14 @@ public List getXVals() { return mXVals; } + /** + * sets the x-values the chart represents + * + */ + public void setXVals(List xVals) { + mXVals = xVals; + } + /** * Adds a new x-value to the chart data. * From b8ce47a78db5df0df17ece732fab00e7c460db7e Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 13 Apr 2016 19:41:13 +0300 Subject: [PATCH 098/606] Fixed dataset updates in demos --- .../com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java | 1 + .../src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java | 1 + .../mpchartexample/BarChartActivityMultiDataset.java | 1 + .../xxmassdeveloper/mpchartexample/BarChartActivitySinus.java | 1 + .../xxmassdeveloper/mpchartexample/CubicLineChartActivity.java | 1 + .../mpchartexample/HorizontalBarChartActivity.java | 1 + .../com/xxmassdeveloper/mpchartexample/LineChartActivity1.java | 1 + .../com/xxmassdeveloper/mpchartexample/LineChartActivity2.java | 1 + .../com/xxmassdeveloper/mpchartexample/StackedBarActivity.java | 1 + 9 files changed, 9 insertions(+) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 5d5b933f57..7e9d4b7fe3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -192,6 +192,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mChart.getData().getDataSetCount() > 0) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals1); + mChart.getData().setXVals(xVals); mChart.notifyDataSetChanged(); } else { set1 = new BarDataSet(yVals1, "Data Set"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 083d016e64..e3e0c5c9c6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -252,6 +252,7 @@ private void setData(int count, float range) { mChart.getData().getDataSetCount() > 0) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals1); + mChart.getData().setXVals(xVals); mChart.notifyDataSetChanged(); } else { set1 = new BarDataSet(yVals1, "DataSet"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 1229828c4d..b47103b2b2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -221,6 +221,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { set1.setYVals(yVals1); set2.setYVals(yVals2); set3.setYVals(yVals3); + mChart.getData().setXVals(xVals); mChart.notifyDataSetChanged(); } else { // create 3 datasets with different types diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index b7d6dfd5aa..dac4ccd2b5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -230,6 +230,7 @@ private void setData(int count) { mChart.getData().getDataSetCount() > 0) { set = (BarDataSet)mChart.getData().getDataSetByIndex(0); set.setYVals(entries); + mChart.getData().setXVals(xVals); mChart.notifyDataSetChanged(); } else { set = new BarDataSet(entries, "Sinus Function"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 86ce89677c..a50171e710 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -289,6 +289,7 @@ private void setData(int count, float range) { mChart.getData().getDataSetCount() > 0) { set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals); + mChart.getData().setXVals(xVals); mChart.notifyDataSetChanged(); } else { // create a dataset and give it a type diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 1dc016ac60..f186896d11 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -248,6 +248,7 @@ private void setData(int count, float range) { mChart.getData().getDataSetCount() > 0) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals1); + mChart.getData().setXVals(xVals); mChart.notifyDataSetChanged(); } else { set1 = new BarDataSet(yVals1, "DataSet 1"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 82e1961304..5a32612b5f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -359,6 +359,7 @@ private void setData(int count, float range) { mChart.getData().getDataSetCount() > 0) { set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals); + mChart.getData().setXVals(xVals); mChart.notifyDataSetChanged(); } else { // create a dataset and give it a type diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 42773b649a..0d76e71813 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -318,6 +318,7 @@ private void setData(int count, float range) { set2 = (LineDataSet)mChart.getData().getDataSetByIndex(1); set1.setYVals(yVals1); set2.setYVals(yVals2); + mChart.getData().setXVals(xVals); mChart.notifyDataSetChanged(); } else { // create a dataset and give it a type diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 1f8ef92129..da46393202 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -208,6 +208,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mChart.getData().getDataSetCount() > 0) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals1); + mChart.getData().setXVals(xVals); mChart.notifyDataSetChanged(); } else { set1 = new BarDataSet(yVals1, "Statistics Vienna 2014"); From 215ce2d82e6530d59b046305698d852eea0e585f Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Wed, 13 Apr 2016 21:50:12 +0300 Subject: [PATCH 099/606] Minor improvements for line rendering --- .../charting/renderer/LineChartRenderer.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index 83ca529815..51fe4887d1 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -145,8 +145,6 @@ protected void drawHorizontalBezier(Canvas c, ILineDataSet dataSet) { float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); - float intensity = dataSet.getCubicIntensity(); - cubicPath.reset(); int size = (int) Math.ceil((maxx - minx) * phaseX + minx); @@ -420,7 +418,8 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { if (e1 != null) { - for (int x = count > 1 ? minx + 1 : minx, j = 0; x < count; x++) { + int j = 0; + for (int x = count > 1 ? minx + 1 : minx; x < count; x++) { e1 = dataSet.getEntryForIndex(x == 0 ? 0 : (x - 1)); e2 = dataSet.getEntryForIndex(x); @@ -441,14 +440,18 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { mLineBuffer[j++] = e2.getVal() * phaseY; } - trans.pointValuesToPixel(mLineBuffer); + if (j > 0) { + trans.pointValuesToPixel(mLineBuffer); - final int size = Math.max((count - minx - 1) * pointsPerEntryPair, pointsPerEntryPair) * 2; + final int size = + Math.max((count - minx - 1) * pointsPerEntryPair, pointsPerEntryPair) * + 2; - mRenderPaint.setColor(dataSet.getColor()); + mRenderPaint.setColor(dataSet.getColor()); - canvas.drawLines(mLineBuffer, 0, size, - mRenderPaint); + canvas.drawLines(mLineBuffer, 0, size, + mRenderPaint); + } } } From ab424e69d853a53cc39dfe48dcb7cf90d8936316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Br=C4=8Di=C4=87?= Date: Wed, 13 Apr 2016 23:54:37 +0200 Subject: [PATCH 100/606] potential solution to rare out of memory problems --- .../github/mikephil/charting/renderer/LineChartRenderer.java | 5 +++++ .../github/mikephil/charting/renderer/PieChartRenderer.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index 51fe4887d1..c5a7d19380 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -727,10 +727,15 @@ public Bitmap.Config getBitmapConfig() { * Releases the drawing bitmap. This should be called when {@link LineChart#onDetachedFromWindow()}. */ public void releaseBitmap() { + if (mBitmapCanvas != null) { + mBitmapCanvas.setBitmap(null); + mBitmapCanvas = null; + } if (mDrawBitmap != null) { mDrawBitmap.get().recycle(); mDrawBitmap.clear(); mDrawBitmap = null; } + System.gc(); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java index 269b767c87..324da1441e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -919,10 +919,15 @@ protected void drawRoundedSlices(Canvas c) { * Releases the drawing bitmap. This should be called when {@link LineChart#onDetachedFromWindow()}. */ public void releaseBitmap() { + if (mBitmapCanvas != null) { + mBitmapCanvas.setBitmap(null); + mBitmapCanvas = null; + } if (mDrawBitmap != null) { mDrawBitmap.get().recycle(); mDrawBitmap.clear(); mDrawBitmap = null; } + System.gc(); } } From 6358f27f79b0b61e76370c24b8e4e5651de482cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Br=C4=8Di=C4=87?= Date: Thu, 14 Apr 2016 10:56:02 +0200 Subject: [PATCH 101/606] Seems that System.gc() call is not necessary --- .../com/github/mikephil/charting/renderer/LineChartRenderer.java | 1 - .../com/github/mikephil/charting/renderer/PieChartRenderer.java | 1 - 2 files changed, 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index c5a7d19380..0691efdfb7 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -736,6 +736,5 @@ public void releaseBitmap() { mDrawBitmap.clear(); mDrawBitmap = null; } - System.gc(); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java index 324da1441e..226b925371 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -928,6 +928,5 @@ public void releaseBitmap() { mDrawBitmap.clear(); mDrawBitmap = null; } - System.gc(); } } From 6d3faca19c6a4a396404adb7f0b093d5b781a9ba Mon Sep 17 00:00:00 2001 From: "thadeu.batista" Date: Thu, 14 Apr 2016 21:28:32 -0300 Subject: [PATCH 102/606] teste primeiro commit --- .../com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 7e9d4b7fe3..4c25b91e7f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -30,6 +30,7 @@ public class AnotherBarActivity extends DemoBase implements OnSeekBarChangeListe private BarChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; + private TextView teste; @Override protected void onCreate(Bundle savedInstanceState) { From 66068f5242ffe8b03c7ef28fe920d8ac39c40230 Mon Sep 17 00:00:00 2001 From: "thadeu.batista" Date: Thu, 14 Apr 2016 21:29:47 -0300 Subject: [PATCH 103/606] projeto pronto para ajustes --- .../com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java | 1 - 1 file changed, 1 deletion(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 4c25b91e7f..7e9d4b7fe3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -30,7 +30,6 @@ public class AnotherBarActivity extends DemoBase implements OnSeekBarChangeListe private BarChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - private TextView teste; @Override protected void onCreate(Bundle savedInstanceState) { From 3f483b90dbc2f98084f530b39faef1501247bf6d Mon Sep 17 00:00:00 2001 From: "thadeu.batista" Date: Fri, 15 Apr 2016 23:10:20 -0300 Subject: [PATCH 104/606] =?UTF-8?q?ajuste=20alinhamento=20de=20labels=20da?= =?UTF-8?q?=20parte=20inferior=20do=20gr=C3=A1fico=20LineChart?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/com/github/mikephil/charting/utils/Utils.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java index 012477c704..7a562a1546 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java @@ -532,6 +532,7 @@ public static float getNormalizedAngle(float angle) { private static Rect mDrawTextRectBuffer = new Rect(); private static Paint.FontMetrics mFontMetricsBuffer = new Paint.FontMetrics(); + private static float mLineHeight = 9999.0f; public static void drawText(Canvas c, String text, float x, float y, Paint paint, @@ -542,7 +543,10 @@ public static void drawText(Canvas c, String text, float x, float y, paint.getTextBounds(text, 0, text.length(), mDrawTextRectBuffer); - final float lineHeight = mDrawTextRectBuffer.height(); + if(mDrawTextRectBuffer.height() < mLineHeight){ + mLineHeight = mDrawTextRectBuffer.height(); + } + final float lineHeight = mLineHeight; // Android sometimes has pre-padding drawOffsetX -= mDrawTextRectBuffer.left; From 10676ea964449804723f37184e89506da1f30496 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 17 Apr 2016 23:15:42 +0200 Subject: [PATCH 105/606] Fixes related to example --- .../xxmassdeveloper/mpchartexample/AnotherBarActivity.java | 1 + .../com/xxmassdeveloper/mpchartexample/BarChartActivity.java | 1 + .../mpchartexample/BarChartActivityMultiDataset.java | 4 +++- .../xxmassdeveloper/mpchartexample/BarChartActivitySinus.java | 1 + .../mpchartexample/BarChartPositiveNegative.java | 1 + .../mpchartexample/HorizontalBarChartActivity.java | 1 + .../mpchartexample/PiePolylineChartActivity.java | 2 +- .../xxmassdeveloper/mpchartexample/StackedBarActivity.java | 1 + MPChartLib/src/com/github/mikephil/charting/utils/Utils.java | 1 + 9 files changed, 11 insertions(+), 2 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 7e9d4b7fe3..3918072671 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -193,6 +193,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals1); mChart.getData().setXVals(xVals); + mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { set1 = new BarDataSet(yVals1, "Data Set"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index e3e0c5c9c6..e1f266d218 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -253,6 +253,7 @@ private void setData(int count, float range) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals1); mChart.getData().setXVals(xVals); + mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { set1 = new BarDataSet(yVals1, "DataSet"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index b47103b2b2..21990a7661 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -222,6 +222,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { set2.setYVals(yVals2); set3.setYVals(yVals3); mChart.getData().setXVals(xVals); + mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { // create 3 datasets with different types @@ -247,8 +248,9 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { data.setValueTypeface(tf); mChart.setData(data); - mChart.invalidate(); } + + mChart.invalidate(); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index dac4ccd2b5..ec96bc5dfe 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -231,6 +231,7 @@ private void setData(int count) { set = (BarDataSet)mChart.getData().getDataSetByIndex(0); set.setYVals(entries); mChart.getData().setXVals(xVals); + mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { set = new BarDataSet(entries, "Sinus Function"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index e028d5d2d7..00036099cc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -114,6 +114,7 @@ private void setData(List dataList) { mChart.getData().getDataSetCount() > 0) { set = (BarDataSet)mChart.getData().getDataSetByIndex(0); set.setYVals(values); + mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { set = new BarDataSet(values, "Values"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index f186896d11..21e5a4ef3c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -249,6 +249,7 @@ private void setData(int count, float range) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals1); mChart.getData().setXVals(xVals); + mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { set1 = new BarDataSet(yVals1, "DataSet 1"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index b475cd4abb..58f1e085ea 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -71,7 +71,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setCenterTextTypeface(Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf")); mChart.setCenterText(generateCenterSpannableText()); - mChart.setExtraOffsets(0.f, 50.f, 0.f, 50.f); + mChart.setExtraOffsets(5.f, 5.f, 5.f, 5.f); mChart.setDrawHoleEnabled(true); mChart.setHoleColor(Color.WHITE); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index da46393202..f0ed75bac0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -209,6 +209,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals1); mChart.getData().setXVals(xVals); + mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { set1 = new BarDataSet(yVals1, "Statistics Vienna 2014"); diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java index 7a562a1546..294ab5ca2e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java @@ -546,6 +546,7 @@ public static void drawText(Canvas c, String text, float x, float y, if(mDrawTextRectBuffer.height() < mLineHeight){ mLineHeight = mDrawTextRectBuffer.height(); } + final float lineHeight = mLineHeight; // Android sometimes has pre-padding From 4b3825c68d2c21e61a1f852f054b935d3dbe39a9 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 17 Apr 2016 23:21:03 +0200 Subject: [PATCH 106/606] Revert #1684 --- .../mikephil/charting/renderer/XAxisRenderer.java | 3 +-- .../com/github/mikephil/charting/utils/Utils.java | 13 ++++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java index a0a2bff642..f3e0ac40b3 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -7,7 +7,6 @@ import android.graphics.Paint.Align; import android.graphics.Path; import android.graphics.PointF; -import android.util.Size; import com.github.mikephil.charting.components.LimitLine; import com.github.mikephil.charting.components.XAxis; @@ -189,7 +188,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { protected void drawLabel(Canvas c, String label, int xIndex, float x, float y, PointF anchor, float angleDegrees) { String formattedLabel = mXAxis.getValueFormatter().getXValue(label, xIndex, mViewPortHandler); - Utils.drawText(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees); + Utils.drawXAxisValue(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees); } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java index 294ab5ca2e..fa2532a30e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java @@ -532,22 +532,17 @@ public static float getNormalizedAngle(float angle) { private static Rect mDrawTextRectBuffer = new Rect(); private static Paint.FontMetrics mFontMetricsBuffer = new Paint.FontMetrics(); - private static float mLineHeight = 9999.0f; - public static void drawText(Canvas c, String text, float x, float y, - Paint paint, - PointF anchor, float angleDegrees) { + public static void drawXAxisValue(Canvas c, String text, float x, float y, + Paint paint, + PointF anchor, float angleDegrees) { float drawOffsetX = 0.f; float drawOffsetY = 0.f; paint.getTextBounds(text, 0, text.length(), mDrawTextRectBuffer); - if(mDrawTextRectBuffer.height() < mLineHeight){ - mLineHeight = mDrawTextRectBuffer.height(); - } - - final float lineHeight = mLineHeight; + final float lineHeight = mDrawTextRectBuffer.height(); // Android sometimes has pre-padding drawOffsetX -= mDrawTextRectBuffer.left; From 69d1b4d81108f3b8fca83625badaa05b90205eb8 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 17 Apr 2016 23:26:01 +0200 Subject: [PATCH 107/606] Minor fix --- .../com/xxmassdeveloper/mpchartexample/StackedBarActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index f0ed75bac0..5e9da82af3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -223,8 +223,9 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { data.setValueFormatter(new MyValueFormatter()); mChart.setData(data); - mChart.invalidate(); } + + mChart.invalidate(); } @Override From a3e4450c74329306cc8c86a73215944e485dad5e Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 1 May 2016 21:45:53 +0300 Subject: [PATCH 108/606] More reusable Realm data builder --- .../data/realm/base/RealmBaseDataSet.java | 23 +++++++++-- .../realm/base/RealmLineRadarDataSet.java | 24 ----------- .../realm/implementation/RealmBarDataSet.java | 40 ++++++++++--------- .../implementation/RealmBubbleDataSet.java | 26 ++++-------- .../implementation/RealmCandleDataSet.java | 31 +++++--------- .../implementation/RealmLineDataSet.java | 5 --- .../realm/implementation/RealmPieDataSet.java | 24 ----------- .../implementation/RealmRadarDataSet.java | 5 --- .../implementation/RealmScatterDataSet.java | 24 ----------- 9 files changed, 58 insertions(+), 144 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java index 02b769e8ac..20673e62e5 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java @@ -1,5 +1,6 @@ package com.github.mikephil.charting.data.realm.base; +import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.BaseDataSet; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; @@ -7,6 +8,7 @@ import java.util.ArrayList; import java.util.List; +import io.realm.DynamicRealmObject; import io.realm.RealmObject; import io.realm.RealmResults; import io.realm.Sort; @@ -75,7 +77,20 @@ public RealmBaseDataSet(RealmResults results, String yValuesField, String xIn /** * Rebuilds the DataSet based on the given RealmResults. */ - public abstract void build(RealmResults results); + public void build(RealmResults results) { + + int xIndex = 0; + for (T object : results) { + mValues.add(buildEntryFromResultObject(object, xIndex++)); + } + } + + public S buildEntryFromResultObject(T realmObject, int xIndex) { + DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); + + return (S)new Entry(dynamicObject.getFloat(mValuesField), + mIndexField == null ? xIndex : dynamicObject.getInt(mIndexField)); + } @Override public float getYMin() { @@ -167,14 +182,16 @@ public int getEntryIndex(int x, DataSet.Rounding rounding) { while (low <= high) { int m = (high + low) / 2; - if (x == mValues.get(m).getXIndex()) { + S entry = mValues.get(m); + + if (x == entry.getXIndex()) { while (m > 0 && mValues.get(m - 1).getXIndex() == x) m--; return m; } - if (x > mValues.get(m).getXIndex()) + if (x > entry.getXIndex()) low = m + 1; else high = m - 1; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java index f8ea4df17d..31010f2489 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java @@ -47,30 +47,6 @@ public RealmLineRadarDataSet(RealmResults results, String yValuesField, Strin super(results, yValuesField, xIndexField); } - @Override - public void build(RealmResults results) { - - if (mIndexField == null) { // x-index not available - - int xIndex = 0; - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new Entry(dynamicObject.getFloat(mValuesField), xIndex)); - xIndex++; - } - - } else { - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new Entry(dynamicObject.getFloat(mValuesField), dynamicObject.getInt(mIndexField))); - } - } - } - @Override public int getFillColor() { return mFillColor; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java index 5fde69a4bd..f062db7be3 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java @@ -3,10 +3,12 @@ import android.graphics.Color; import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.realm.base.RealmBarLineScatterCandleBubbleDataSet; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import io.realm.DynamicRealmObject; +import io.realm.RealmFieldType; import io.realm.RealmList; import io.realm.RealmObject; import io.realm.RealmResults; @@ -78,31 +80,33 @@ public RealmBarDataSet(RealmResults results, String yValuesField, String xInd @Override public void build(RealmResults results) { - for (T realmObject : results) { + super.build(results); - DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - - try { // normal entry + calcStackSize(); + } - float value = dynamicObject.getFloat(mValuesField); - mValues.add(new BarEntry(value, dynamicObject.getInt(mIndexField))); + @Override + public BarEntry buildEntryFromResultObject(T realmObject, int xIndex) { + DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - } catch (IllegalArgumentException e) { // stacked entry + if (dynamicObject.getFieldType(mValuesField) == RealmFieldType.LIST) { - RealmList list = dynamicObject.getList(mValuesField); - float[] values = new float[list.size()]; + RealmList list = dynamicObject.getList(mValuesField); + float[] values = new float[list.size()]; - int i = 0; - for (DynamicRealmObject o : list) { - values[i] = o.getFloat(mStackValueFieldName); - i++; - } - - mValues.add(new BarEntry(values, dynamicObject.getInt(mIndexField))); + int i = 0; + for (DynamicRealmObject o : list) { + values[i] = o.getFloat(mStackValueFieldName); + i++; } - } - calcStackSize(); + return new BarEntry(values, + mIndexField == null ? xIndex : dynamicObject.getInt(mIndexField)); + } else { + float value = dynamicObject.getFloat(mValuesField); + return new BarEntry(value, + mIndexField == null ? xIndex : dynamicObject.getInt(mIndexField)); + } } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java index d6e50ab85b..a69276ade4 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.data.realm.implementation; import com.github.mikephil.charting.data.BubbleEntry; +import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.realm.base.RealmBarLineScatterCandleBubbleDataSet; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; import com.github.mikephil.charting.utils.Utils; @@ -55,26 +56,13 @@ public RealmBubbleDataSet(RealmResults result, String yValuesField, String xI } @Override - public void build(RealmResults results) { + public BubbleEntry buildEntryFromResultObject(T realmObject, int xIndex) { + DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - if(mIndexField == null) { - - int xIndex = 0; - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new BubbleEntry(xIndex, dynamicObject.getFloat(mValuesField), dynamicObject.getFloat(mSizeField))); - xIndex++; - } - } else { - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new BubbleEntry(dynamicObject.getInt(mIndexField), dynamicObject.getFloat(mValuesField), dynamicObject.getFloat(mSizeField))); - } - } + return new BubbleEntry( + mIndexField == null ? xIndex : dynamicObject.getInt(mIndexField), + dynamicObject.getFloat(mValuesField), + dynamicObject.getFloat(mSizeField)); } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java index def4e4e935..9cc1d3c4c2 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java @@ -3,6 +3,7 @@ import android.graphics.Paint; import com.github.mikephil.charting.data.CandleEntry; +import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.realm.base.RealmLineScatterCandleRadarDataSet; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -119,29 +120,15 @@ public RealmCandleDataSet(RealmResults result, String highField, String lowFi calcMinMax(0, this.results.size()); } - @Override - public void build(RealmResults results) { - - if (mIndexField == null) { - - int xIndex = 0; - - for (T object : results) { + public CandleEntry buildEntryFromResultObject(T realmObject, int xIndex) { + DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new CandleEntry(xIndex, dynamicObject.getFloat(mHighField), dynamicObject.getFloat(mLowField), - dynamicObject.getFloat(mOpenField), dynamicObject.getFloat(mCloseField))); - xIndex++; - } - } else { - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new CandleEntry(dynamicObject.getInt(mIndexField), dynamicObject.getFloat(mHighField), dynamicObject.getFloat(mLowField), - dynamicObject.getFloat(mOpenField), dynamicObject.getFloat(mCloseField))); - } - } + return new CandleEntry( + mIndexField == null ? xIndex : dynamicObject.getInt(mIndexField), + dynamicObject.getFloat(mHighField), + dynamicObject.getFloat(mLowField), + dynamicObject.getFloat(mOpenField), + dynamicObject.getFloat(mCloseField)); } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java index c1ab855b0e..b8832271af 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java @@ -99,11 +99,6 @@ public RealmLineDataSet(RealmResults result, String yValuesField, String xInd calcMinMax(0, results.size()); } - @Override - public void build(RealmResults results) { - super.build(results); - } - /** * Returns the drawing mode for this line dataset * diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java index 7be50e7c64..35479f3637 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java @@ -61,30 +61,6 @@ public RealmPieDataSet(RealmResults result, String yValuesField, String xInde calcMinMax(0, results.size()); } - @Override - public void build(RealmResults results) { - - if (mIndexField == null) { // x-index not available - - int xIndex = 0; - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new Entry(dynamicObject.getFloat(mValuesField), xIndex)); - xIndex++; - } - - } else { - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new Entry(dynamicObject.getFloat(mValuesField), dynamicObject.getInt(mIndexField))); - } - } - } - /** * Sets the space that is left out between the piechart-slices in dp. * Default: 0 --> no space, maximum 20f diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java index aa2dc2fdfc..31957f93e5 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java @@ -138,9 +138,4 @@ public void setHighlightCircleStrokeWidth(float strokeWidth) { mHighlightCircleStrokeWidth = strokeWidth; } - - @Override - public void build(RealmResults results) { - super.build(results); - } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java index c895bc09ec..88bbc01dc6 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java @@ -67,30 +67,6 @@ public RealmScatterDataSet(RealmResults result, String yValuesField, String x calcMinMax(0, results.size()); } - @Override - public void build(RealmResults results) { - - if (mIndexField == null) { // x-index not available - - int xIndex = 0; - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new Entry(dynamicObject.getFloat(mValuesField), xIndex)); - xIndex++; - } - - } else { - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new Entry(dynamicObject.getFloat(mValuesField), dynamicObject.getInt(mIndexField))); - } - } - } - /** * Sets the size in density pixels the drawn scattershape will have. This * only applies for non custom shapes. From a002634d490e15e94984735b27a3afe11c4435c2 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 1 May 2016 21:46:04 +0300 Subject: [PATCH 109/606] Highlight enhancements There are certain cases (ie. bubbles) which make sense to have multiple values per xIndex. This fixes that - and adds more granular highlight for Combined Chart. --- .../mikephil/charting/charts/Chart.java | 22 +++- .../charting/charts/PieRadarChartBase.java | 2 +- .../mikephil/charting/data/ChartData.java | 16 ++- .../mikephil/charting/data/CombinedData.java | 34 ++++++ .../mikephil/charting/data/DataSet.java | 27 ++++- .../data/realm/base/RealmBaseDataSet.java | 33 +++++ .../charting/highlight/BarHighlighter.java | 113 ++++++++++++------ .../charting/highlight/ChartHighlighter.java | 54 +++++---- .../highlight/CombinedHighlighter.java | 24 ++-- .../charting/highlight/Highlight.java | 75 +++++++++--- .../highlight/HorizontalBarHighlighter.java | 47 +++++--- .../interfaces/datasets/IDataSet.java | 28 ++++- .../charting/listener/ChartTouchListener.java | 4 +- .../listener/PieRadarChartTouchListener.java | 6 +- .../renderer/BubbleChartRenderer.java | 8 +- .../renderer/CombinedChartRenderer.java | 41 ++++++- .../charting/renderer/LineChartRenderer.java | 2 +- .../renderer/ScatterChartRenderer.java | 2 +- .../charting/utils/SelectionDetail.java | 18 ++- .../github/mikephil/charting/utils/Utils.java | 87 ++++++++++++-- 20 files changed, 499 insertions(+), 144 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java index 0c837bb1ff..82a277f8ca 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java @@ -556,15 +556,24 @@ public void highlightValues(Highlight[] highs) { * @param dataSetIndex */ public void highlightValue(int xIndex, int dataSetIndex) { + highlightValue(xIndex, dataSetIndex, true); + } + + /** + * Highlights the value at the given x-index in the given DataSet. Provide + * -1 as the x-index or dataSetIndex to undo all highlighting. + * + * @param xIndex + * @param dataSetIndex + */ + public void highlightValue(int xIndex, int dataSetIndex, boolean callListener) { if (xIndex < 0 || dataSetIndex < 0 || xIndex >= mData.getXValCount() || dataSetIndex >= mData.getDataSetCount()) { - highlightValues(null); + highlightValue(null, callListener); } else { - highlightValues(new Highlight[]{ - new Highlight(xIndex, dataSetIndex) - }); + highlightValue(new Highlight(xIndex, dataSetIndex), callListener); } } @@ -598,7 +607,10 @@ public void highlightValue(Highlight high, boolean callListener) { Log.i(LOG_TAG, "Highlighted: " + high.toString()); e = mData.getEntryForHighlight(high); - if (e == null || e.getXIndex() != high.getXIndex()) { + if (e == null || + e.getXIndex() != high.getXIndex() || + (!Float.isNaN(high.getValue()) && + e.getVal() != high.getValue())) { mIndicesToHighlight = null; high = null; } else { diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java index 5a938872cf..9c31414ced 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java @@ -459,7 +459,7 @@ public List getSelectionDetailsAtIndex(int xIndex) { // extract all y-values from all DataSets at the given x-index final float yVal = dataSet.getYValForXIndex(xIndex); - if (yVal == Float.NaN) + if (Float.isNaN(yVal)) continue; vals.add(new SelectionDetail(yVal, i, dataSet)); diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java index 7a01996bc6..770afd5808 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java @@ -472,9 +472,19 @@ protected String[] getDataSetLabels() { public Entry getEntryForHighlight(Highlight highlight) { if (highlight.getDataSetIndex() >= mDataSets.size()) return null; - else - return mDataSets.get(highlight.getDataSetIndex()).getEntryForXIndex( - highlight.getXIndex()); + else { + // The value of the highlighted entry could be NaN - + // if we are not interested in highlighting a specific value. + + List entries = mDataSets.get(highlight.getDataSetIndex()) + .getEntriesForXIndex(highlight.getXIndex()); + for (Object entry : entries) + if (((Entry)entry).getVal() == highlight.getValue() || + Float.isNaN(highlight.getValue())) + return (Entry)entry; + + return null; + } } /** diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java b/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java index 2d7982265d..507e14434b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.data; +import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; import java.util.ArrayList; @@ -118,4 +119,37 @@ public void notifyDataChanged() { init(); // recalculate everything } + + /** + * Get the Entry for a corresponding highlight object + * + * @param highlight + * @return the entry that is highlighted + */ + @Override + public Entry getEntryForHighlight(Highlight highlight) { + + List dataObjects = getAllData(); + + if (highlight.getDataIndex() >= dataObjects.size()) + return null; + + ChartData data = dataObjects.get(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()) + .getEntriesForXIndex(highlight.getXIndex()); + for (Object entry : entries) + if (((Entry)entry).getVal() == highlight.getValue() || + Float.isNaN(highlight.getValue())) + return (Entry)entry; + + return null; + } + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java index 3e2926cf9e..5b37a296ce 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java @@ -312,6 +312,20 @@ public float getYValForXIndex(int xIndex) { return Float.NaN; } + @Override + public float[] getYValsForXIndex(int xIndex) { + + List entries = getEntriesForXIndex(xIndex); + + float[] yVals = new float[entries.size()]; + int i = 0; + + for (T e : entries) + yVals[i++] = e.getVal(); + + return yVals; + } + /** * Returns all Entry objects at the given xIndex. INFORMATION: This method * does calculations at runtime. Do not over-use in performance critical @@ -320,6 +334,7 @@ public float getYValForXIndex(int xIndex) { * @param xIndex * @return */ + @Override public List getEntriesForXIndex(int xIndex) { List entries = new ArrayList(); @@ -344,12 +359,14 @@ public List getEntriesForXIndex(int xIndex) { break; } } - } - if (xIndex > entry.getXIndex()) - low = m + 1; - else - high = m - 1; + break; + } else { + if (xIndex > entry.getXIndex()) + low = m + 1; + else + high = m - 1; + } } return entries; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java index 20673e62e5..3fa603778d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java @@ -165,6 +165,25 @@ public S getEntryForXIndex(int xIndex, DataSet.Rounding rounding) { return null; } + @Override + public List getEntriesForXIndex(int xIndex) { + + List entries = new ArrayList<>(); + + if (mIndexField == null) { + T object = results.get(xIndex); + if (object != null) + entries.add(buildEntryFromResultObject(object, xIndex)); + } else { + RealmResults foundObjects = results.where().equalTo(mIndexField, xIndex).findAll(); + + for (T e : foundObjects) + entries.add(buildEntryFromResultObject(e, xIndex)); + } + + return entries; + } + @Override public S getEntryForIndex(int index) { //DynamicRealmObject o = new DynamicRealmObject(results.get(index)); @@ -231,6 +250,20 @@ public float getYValForXIndex(int xIndex) { return Float.NaN; } + @Override + public float[] getYValsForXIndex(int xIndex) { + + List entries = getEntriesForXIndex(xIndex); + + float[] yVals = new float[entries.size()]; + int i = 0; + + for (S e : entries) + yVals[i++] = e.getVal(); + + return yVals; + } + @Override public boolean addEntry(S e) { diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java index a852aa041c..4017e3eb1d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java @@ -1,9 +1,12 @@ package com.github.mikephil.charting.highlight; import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.SelectionDetail; /** * Created by Philipp Jahoda on 22/07/15. @@ -17,27 +20,44 @@ public BarHighlighter(BarDataProvider chart) { @Override public Highlight getHighlight(float x, float y) { - Highlight h = super.getHighlight(x, y); + BarData barData = mChart.getBarData(); - if (h == null) - return h; - else { + final int xIndex = getXIndex(x); + final float baseNoSpace = getBase(x); + final int setCount = barData.getDataSetCount(); + int dataSetIndex = ((int)baseNoSpace) % setCount; - IBarDataSet set = mChart.getBarData().getDataSetByIndex(h.getDataSetIndex()); + if (dataSetIndex < 0) { + dataSetIndex = 0; + } else if (dataSetIndex >= setCount) { + dataSetIndex = setCount - 1; + } + + SelectionDetail selectionDetail = getSelectionDetail(xIndex, y, dataSetIndex); + if (selectionDetail == null) + return null; - if (set.isStacked()) { + IBarDataSet set = barData.getDataSetByIndex(dataSetIndex); + if (set.isStacked()) { - // create an array of the touch-point - float[] pts = new float[2]; - pts[1] = y; + float[] pts = new float[2]; + pts[1] = y; - // take any transformer to determine the x-axis value - mChart.getTransformer(set.getAxisDependency()).pixelsToValue(pts); + // take any transformer to determine the x-axis value + mChart.getTransformer(set.getAxisDependency()).pixelsToValue(pts); - return getStackedHighlight(h, set, h.getXIndex(), h.getDataSetIndex(), pts[1]); - } else - return h; + return getStackedHighlight(selectionDetail, + set, + xIndex, + pts[1]); } + + return new Highlight( + xIndex, + selectionDetail.value, + selectionDetail.dataIndex, + selectionDetail.dataSetIndex, + -1); } @Override @@ -64,51 +84,68 @@ else if (xIndex >= valCount) } @Override - protected int getDataSetIndex(int xIndex, float x, float y) { + protected SelectionDetail getSelectionDetail(int xIndex, float y, int dataSetIndex) { - if (!mChart.getBarData().isGrouped()) { - return 0; - } else { + dataSetIndex = Math.max(dataSetIndex, 0); - float baseNoSpace = getBase(x); + BarData barData = mChart.getBarData(); + IDataSet dataSet = barData.getDataSetCount() > dataSetIndex + ? barData.getDataSetByIndex(dataSetIndex) + : null; + if (dataSet == null) + return null; - int setCount = mChart.getBarData().getDataSetCount(); - int dataSetIndex = (int) baseNoSpace % setCount; + final float yValue = dataSet.getYValForXIndex(xIndex); - if (dataSetIndex < 0) - dataSetIndex = 0; - else if (dataSetIndex >= setCount) - dataSetIndex = setCount - 1; + if (yValue == Double.NaN) return null; - return dataSetIndex; - } + return new SelectionDetail( + yValue, + dataSetIndex, + dataSet); } /** * This method creates the Highlight object that also indicates which value of a stacked BarEntry has been selected. * - * @param old - * the old highlight object before looking for stacked values + * @param selectionDetail the selection detail to work with looking for stacked values * @param set * @param xIndex - * @param dataSetIndex * @param yValue * @return */ - protected Highlight getStackedHighlight(Highlight old, IBarDataSet set, int xIndex, int dataSetIndex, double yValue) { + protected Highlight getStackedHighlight( + SelectionDetail selectionDetail, + IBarDataSet set, + int xIndex, + double yValue) { BarEntry entry = set.getEntryForXIndex(xIndex); - if (entry == null || entry.getVals() == null) - return old; + if (entry == null) + return null; + + if (entry.getVals() == null) { + return new Highlight(xIndex, + entry.getVal(), + selectionDetail.dataIndex, + selectionDetail.dataSetIndex); + } Range[] ranges = getRanges(entry); - int stackIndex = getClosestStackIndex(ranges, (float) yValue); + if (ranges.length > 0) { + int stackIndex = getClosestStackIndex(ranges, (float)yValue); + return new Highlight( + xIndex, + entry.getPositiveSum() - entry.getNegativeSum(), + selectionDetail.dataIndex, + selectionDetail.dataSetIndex, + stackIndex, + ranges[stackIndex] + ); + } - if(ranges.length > 0) - return new Highlight(xIndex, dataSetIndex, stackIndex, ranges[stackIndex]); - else - return null; + return null; } /** diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java index 04ee9bec11..6195abe810 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java @@ -31,14 +31,15 @@ public ChartHighlighter(T chart) { public Highlight getHighlight(float x, float y) { int xIndex = getXIndex(x); - if (xIndex == -Integer.MAX_VALUE) - return null; - int dataSetIndex = getDataSetIndex(xIndex, x, y); - if (dataSetIndex == -Integer.MAX_VALUE) + SelectionDetail selectionDetail = getSelectionDetail(xIndex, y, -1); + if (selectionDetail == null) return null; - return new Highlight(xIndex, dataSetIndex); + return new Highlight(xIndex, + selectionDetail.value, + selectionDetail.dataIndex, + selectionDetail.dataSetIndex); } /** @@ -60,40 +61,48 @@ protected int getXIndex(float x) { } /** - * Returns the corresponding dataset-index for a given xIndex and xy-touch position in pixels. + * Returns the corresponding SelectionDetail for a given xIndex and y-touch position in pixels. * * @param xIndex - * @param x * @param y + * @param dataSetIndex * @return */ - protected int getDataSetIndex(int xIndex, float x, float y) { + protected SelectionDetail getSelectionDetail(int xIndex, float y, int dataSetIndex) { - List valsAtIndex = getSelectionDetailsAtIndex(xIndex); + List valsAtIndex = getSelectionDetailsAtIndex(xIndex, dataSetIndex); float leftdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.LEFT); float rightdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.RIGHT); YAxis.AxisDependency axis = leftdist < rightdist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT; - int dataSetIndex = Utils.getClosestDataSetIndex(valsAtIndex, y, axis); + SelectionDetail detail = Utils.getClosestSelectionDetailByPixelY(valsAtIndex, y, axis); - return dataSetIndex; + return detail; } /** * Returns a list of SelectionDetail object corresponding to the given xIndex. - * + * * @param xIndex + * @param dataSetIndex dataSet index to look at. -1 if unspecified. * @return */ - protected List getSelectionDetailsAtIndex(int xIndex) { + protected List getSelectionDetailsAtIndex(int xIndex, int dataSetIndex) { List vals = new ArrayList(); + if (mChart.getData() == null) return vals; + float[] pts = new float[2]; - for (int i = 0; i < mChart.getData().getDataSetCount(); i++) { + for (int i = 0, dataSetCount = mChart.getData().getDataSetCount(); + i < dataSetCount; + i++) { + + if (dataSetIndex > -1 && dataSetIndex != i) + continue; IDataSet dataSet = mChart.getData().getDataSetByIndex(i); @@ -102,16 +111,19 @@ protected List getSelectionDetailsAtIndex(int xIndex) { continue; // extract all y-values from all DataSets at the given x-index - final float yVal = dataSet.getYValForXIndex(xIndex); - if (yVal == Float.NaN) - continue; + final float[] yVals = dataSet.getYValsForXIndex(xIndex); + for (float yVal : yVals) { + if (Float.isNaN(yVal)) + continue; - pts[1] = yVal; + pts[1] = yVal; - mChart.getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); + mChart.getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); - if (!Float.isNaN(pts[1])) { - vals.add(new SelectionDetail(pts[1], i, dataSet)); + if (!Float.isNaN(pts[1])) + { + vals.add(new SelectionDetail(pts[1], yVal, i, dataSet)); + } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/CombinedHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/CombinedHighlighter.java index 25ffb1f334..4957028efe 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/CombinedHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/CombinedHighlighter.java @@ -25,17 +25,16 @@ public CombinedHighlighter(BarLineScatterCandleBubbleDataProvider chart) { * @return */ @Override - protected List getSelectionDetailsAtIndex(int xIndex) { + protected List getSelectionDetailsAtIndex(int xIndex, int dataSetIndex) { + + List vals = new ArrayList(); + float[] pts = new float[2]; CombinedData data = (CombinedData) mChart.getData(); // get all chartdata objects List dataObjects = data.getAllData(); - List vals = new ArrayList(); - - float[] pts = new float[2]; - for (int i = 0; i < dataObjects.size(); i++) { for(int j = 0; j < dataObjects.get(i).getDataSetCount(); j++) { @@ -47,16 +46,15 @@ protected List getSelectionDetailsAtIndex(int xIndex) { continue; // extract all y-values from all DataSets at the given x-index - final float yVal = dataSet.getYValForXIndex(xIndex); - if (yVal == Float.NaN) - continue; - - pts[1] = yVal; + final float yVals[] = dataSet.getYValsForXIndex(xIndex); + for (float yVal : yVals) { + pts[1] = yVal; - mChart.getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); + mChart.getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); - if (!Float.isNaN(pts[1])) { - vals.add(new SelectionDetail(pts[1], j, dataSet)); + if (!Float.isNaN(pts[1])) { + vals.add(new SelectionDetail(pts[1], yVal, i, j, dataSet)); + } } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/Highlight.java b/MPChartLib/src/com/github/mikephil/charting/highlight/Highlight.java index 07ae315eb1..7ebd5d0193 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/Highlight.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/Highlight.java @@ -11,6 +11,12 @@ public class Highlight { /** the x-index of the highlighted value */ private int mXIndex; + /** the y-value of the highlighted value */ + private float mValue = Float.NaN; + + /** the index of the data object - in case it refers to more than one */ + private int mDataIndex; + /** the index of the dataset the highlighted value is in */ private int mDataSetIndex; @@ -22,25 +28,30 @@ public class Highlight { /** * constructor - * + * * @param x the index of the highlighted value on the x-axis - * @param dataSet the index of the DataSet the highlighted value belongs to + * @param value the y-value of the highlighted value + * @param dataIndex the index of the Data the highlighted value belongs to + * @param dataSetIndex the index of the DataSet the highlighted value belongs to */ - public Highlight(int x, int dataSet) { + public Highlight(int x, float value, int dataIndex, int dataSetIndex) { this.mXIndex = x; - this.mDataSetIndex = dataSet; + this.mValue = value; + this.mDataIndex = dataIndex; + this.mDataSetIndex = dataSetIndex; } - /** * Constructor, only used for stacked-barchart. * * @param x the index of the highlighted value on the x-axis - * @param dataSet the index of the DataSet the highlighted value belongs to + * @param value the y-value of the highlighted value + * @param dataIndex the index of the Data the highlighted value belongs to + * @param dataSetIndex the index of the DataSet the highlighted value belongs to * @param stackIndex references which value of a stacked-bar entry has been * selected */ - public Highlight(int x, int dataSet, int stackIndex) { - this(x, dataSet); + public Highlight(int x, float value, int dataIndex, int dataSetIndex, int stackIndex) { + this(x, value, dataIndex, dataSetIndex); mStackIndex = stackIndex; } @@ -48,34 +59,64 @@ public Highlight(int x, int dataSet, int stackIndex) { * Constructor, only used for stacked-barchart. * * @param x the index of the highlighted value on the x-axis - * @param dataSet the index of the DataSet the highlighted value belongs to + * @param value the y-value of the highlighted value + * @param dataIndex the index of the Data the highlighted value belongs to + * @param dataSetIndex the index of the DataSet the highlighted value belongs to * @param stackIndex references which value of a stacked-bar entry has been * selected * @param range the range the selected stack-value is in */ - public Highlight(int x, int dataSet, int stackIndex, Range range) { - this(x, dataSet, stackIndex); + public Highlight(int x, float value, int dataIndex, int dataSetIndex, int stackIndex, Range range) { + this(x, value, dataIndex, dataSetIndex, stackIndex); this.mRange = range; } /** - * returns the index of the DataSet the highlighted value is in - * - * @return + * Constructor, only used for stacked-barchart. + * + * @param x the index of the highlighted value on the x-axis + * @param dataSetIndex the index of the DataSet the highlighted value belongs to */ - public int getDataSetIndex() { - return mDataSetIndex; + public Highlight(int x, int dataSetIndex) { + this(x, Float.NaN, 0, dataSetIndex, -1); } /** * returns the index of the highlighted value on the x-axis - * + * * @return */ public int getXIndex() { return mXIndex; } + /** + * returns the y-value of the highlighted value + * + * @return + */ + public float getValue() { + return mValue; + } + + /** + * the index of the data object - in case it refers to more than one + * + * @return + */ + public int getDataIndex() { + return mDataIndex; + } + + /** + * returns the index of the DataSet the highlighted value is in + * + * @return + */ + public int getDataSetIndex() { + return mDataSetIndex; + } + /** * Only needed if a stacked-barchart entry was highlighted. References the * selected value within the stacked-entry. diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java index 1930409a1b..cdd6387cd8 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java @@ -1,8 +1,10 @@ package com.github.mikephil.charting.highlight; import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.utils.SelectionDetail; /** * Created by Philipp Jahoda on 22/07/15. @@ -16,27 +18,44 @@ public HorizontalBarHighlighter(BarDataProvider chart) { @Override public Highlight getHighlight(float x, float y) { - Highlight h = super.getHighlight(x, y); + BarData barData = mChart.getBarData(); - if (h == null) - return h; - else { + final int xIndex = getXIndex(x); + final float baseNoSpace = getBase(x); + final int setCount = barData.getDataSetCount(); + int dataSetIndex = ((int)baseNoSpace) % setCount; - IBarDataSet set = mChart.getBarData().getDataSetByIndex(h.getDataSetIndex()); + if (dataSetIndex < 0) { + dataSetIndex = 0; + } else if (dataSetIndex >= setCount) { + dataSetIndex = setCount - 1; + } - if (set.isStacked()) { + SelectionDetail selectionDetail = getSelectionDetail(xIndex, y, dataSetIndex); + if (selectionDetail == null) + return null; - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = y; + IBarDataSet set = barData.getDataSetByIndex(dataSetIndex); + if (set.isStacked()) { - // take any transformer to determine the x-axis value - mChart.getTransformer(set.getAxisDependency()).pixelsToValue(pts); + float[] pts = new float[2]; + pts[0] = y; - return getStackedHighlight(h, set, h.getXIndex(), h.getDataSetIndex(), pts[0]); - } else - return h; + // take any transformer to determine the x-axis value + mChart.getTransformer(set.getAxisDependency()).pixelsToValue(pts); + + return getStackedHighlight(selectionDetail, + set, + xIndex, + pts[0]); } + + return new Highlight( + xIndex, + selectionDetail.value, + selectionDetail.dataIndex, + selectionDetail.dataSetIndex, + -1); } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index 38ea9484c2..ab237f647f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -54,9 +54,10 @@ public interface IDataSet { * not over-use in performance critical situations. * * @param xIndex + * @param rounding determine to round up/down/closest if there is no Entry matching the provided x-index * @return */ - T getEntryForXIndex(int xIndex); + T getEntryForXIndex(int xIndex, DataSet.Rounding rounding); /** * Returns the first Entry object found at the given xIndex with binary @@ -66,10 +67,20 @@ public interface IDataSet { * not over-use in performance critical situations. * * @param xIndex - * @param rounding determine to round up/down/closest if there is no Entry matching the provided x-index * @return */ - T getEntryForXIndex(int xIndex, DataSet.Rounding rounding); + T getEntryForXIndex(int xIndex); + + /** + * Returns all Entry objects found at the given xIndex with binary + * search. An empty array if no Entry object at that index. + * INFORMATION: This method does calculations at runtime. Do + * not over-use in performance critical situations. + * + * @param xIndex + * @return + */ + List getEntriesForXIndex(int xIndex); /** * Returns the Entry object found at the given index (NOT xIndex) in the values array. @@ -112,6 +123,17 @@ public interface IDataSet { */ float getYValForXIndex(int xIndex); + /** + * Returns all of the y values of the Entry objects at the given xIndex. Returns + * Float.NaN if no value is at the given x-index. INFORMATION: This method + * does calculations at runtime. Do not over-use in performance critical + * situations. + * + * @param xIndex + * @return + */ + float[] getYValsForXIndex(int xIndex); + /** * This method returns the actual * index in the Entry array of the DataSet for a given xIndex. IMPORTANT: This method does diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/ChartTouchListener.java b/MPChartLib/src/com/github/mikephil/charting/listener/ChartTouchListener.java index eef16c8460..75c8e864b4 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/ChartTouchListener.java +++ b/MPChartLib/src/com/github/mikephil/charting/listener/ChartTouchListener.java @@ -118,11 +118,11 @@ public ChartGesture getLastGesture() { protected void performHighlight(Highlight h, MotionEvent e) { if (h == null || h.equalTo(mLastHighlighted)) { - mChart.highlightTouch(null); + mChart.highlightValue(null, true); mLastHighlighted = null; } else { + mChart.highlightValue(h, true); mLastHighlighted = h; - mChart.highlightTouch(h); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java b/MPChartLib/src/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java index 9163c823d8..d40e2fdb4e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java +++ b/MPChartLib/src/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java @@ -192,8 +192,10 @@ public boolean onSingleTapUp(MotionEvent e) { // has one DataSet) if (mChart instanceof RadarChart) { - dataSetIndex = Utils.getClosestDataSetIndex(valsAtIndex, distance - / ((RadarChart) mChart).getFactor(), null); + dataSetIndex = Utils.getClosestDataSetIndexByValue( + valsAtIndex, + distance / ((RadarChart) mChart).getFactor(), + null); } if (dataSetIndex < 0) { diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index 1988aa7692..cb978c6fbc 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -204,16 +204,16 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (dataSet == null || !dataSet.isHighlightEnabled()) continue; + final BubbleEntry entry = (BubbleEntry) bubbleData.getEntryForHighlight(indice); + if (entry == null || entry.getXIndex() != indice.getXIndex()) + continue; + BubbleEntry entryFrom = dataSet.getEntryForXIndex(mMinX); BubbleEntry entryTo = dataSet.getEntryForXIndex(mMaxX); int minx = dataSet.getEntryIndex(entryFrom); int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount()); - final BubbleEntry entry = (BubbleEntry) bubbleData.getEntryForHighlight(indice); - if (entry == null || entry.getXIndex() != indice.getXIndex()) - continue; - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); sizeBuffer[0] = 0f; diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java index 6a1b2e518a..4b01d48753 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java @@ -3,12 +3,16 @@ import android.graphics.Canvas; import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.charts.Chart; import com.github.mikephil.charting.charts.CombinedChart; import com.github.mikephil.charting.charts.CombinedChart.DrawOrder; +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; import java.util.ArrayList; import java.util.List; @@ -22,9 +26,11 @@ public class CombinedChartRenderer extends DataRenderer { */ protected List mRenderers; + protected WeakReference mChart; + public CombinedChartRenderer(CombinedChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { super(animator, viewPortHandler); - + mChart = new WeakReference(chart); createRenderers(chart, animator, viewPortHandler); } @@ -99,8 +105,37 @@ public void drawExtras(Canvas c) { @Override public void drawHighlighted(Canvas c, Highlight[] indices) { - for (DataRenderer renderer : mRenderers) - renderer.drawHighlighted(c, indices); + + Chart chart = mChart.get(); + if (chart == null) return; + + for (DataRenderer renderer : mRenderers) { + ChartData data = null; + + if (renderer instanceof BarChartRenderer) + data = ((BarChartRenderer)renderer).mChart.getBarData(); + else if (renderer instanceof LineChartRenderer) + data = ((LineChartRenderer)renderer).mChart.getLineData(); + else if (renderer instanceof CandleStickChartRenderer) + data = ((CandleStickChartRenderer)renderer).mChart.getCandleData(); + else if (renderer instanceof ScatterChartRenderer) + data = ((ScatterChartRenderer)renderer).mChart.getScatterData(); + else if (renderer instanceof BubbleChartRenderer) + data = ((BubbleChartRenderer)renderer).mChart.getBubbleData(); + + int dataIndex = data == null + ? -1 + : ((CombinedData)chart.getData()).getAllData().indexOf(data); + + ArrayList dataIndices = new ArrayList<>(); + for (Highlight h : indices) { + if (h.getDataIndex() == dataIndex) + dataIndices.add(h); + } + + renderer.drawHighlighted(c, dataIndices.toArray(new Highlight[dataIndices.size()])); + + } } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index 0691efdfb7..442fe541dd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -684,7 +684,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { continue; final float yVal = set.getYValForXIndex(xIndex); - if (yVal == Float.NaN) + if (Float.isNaN(yVal)) continue; float y = yVal * mAnimator.getPhaseY(); // get diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index 05f15c7e8b..684c6701f7 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -392,7 +392,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { continue; final float yVal = set.getYValForXIndex(xIndex); - if (yVal == Float.NaN) + if (Float.isNaN(yVal)) continue; float y = yVal * mAnimator.getPhaseY(); diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/SelectionDetail.java b/MPChartLib/src/com/github/mikephil/charting/utils/SelectionDetail.java index 1704eefeb8..5f78528f4c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/SelectionDetail.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/SelectionDetail.java @@ -12,13 +12,25 @@ */ public class SelectionDetail { - public float val; + public float y; + public float value; + public int dataIndex; public int dataSetIndex; public IDataSet dataSet; - public SelectionDetail(float val, int dataSetIndex, IDataSet set) { - this.val = val; + public SelectionDetail(float y, float value, int dataIndex, int dataSetIndex, IDataSet set) { + this.y = y; + this.value = value; + this.dataIndex = dataIndex; this.dataSetIndex = dataSetIndex; this.dataSet = set; } + + public SelectionDetail(float y, float value, int dataSetIndex, IDataSet set) { + this(y, value, 0, dataSetIndex, set); + } + + public SelectionDetail(float value, int dataSetIndex, IDataSet set) { + this(Float.NaN, value, 0, dataSetIndex, set); + } } \ No newline at end of file diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java index fa2532a30e..9f342c7dce 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java @@ -388,10 +388,80 @@ public static double nextUp(double d) { * @param valsAtIndex all the values at a specific index * @return */ - public static int getClosestDataSetIndex(List valsAtIndex, float val, + public static int getClosestDataSetIndexByValue(List valsAtIndex, float value, AxisDependency axis) { - int index = -Integer.MAX_VALUE; + SelectionDetail sel = getClosestSelectionDetailByValue(valsAtIndex, value, axis); + + if (sel == null) + return -Integer.MAX_VALUE; + + return sel.dataSetIndex; + } + + /** + * Returns the index of the DataSet that contains the closest value on the + * y-axis. This is needed for highlighting. This will return -Integer.MAX_VALUE if failure. + * + * @param valsAtIndex all the values at a specific index + * @return + */ + public static int getClosestDataSetIndexByPixelY(List valsAtIndex, float y, + AxisDependency axis) { + + SelectionDetail sel = getClosestSelectionDetailByPixelY(valsAtIndex, y, axis); + + if (sel == null) + return -Integer.MAX_VALUE; + + return sel.dataSetIndex; + } + + /** + * Returns the SelectionDetail of the DataSet that contains the closest value on the + * y-axis. + * + * @param valsAtIndex all the values at a specific index + * @return + */ + public static SelectionDetail getClosestSelectionDetailByValue( + List valsAtIndex, + float value, + AxisDependency axis) { + + SelectionDetail closest = null; + float distance = Float.MAX_VALUE; + + for (int i = 0; i < valsAtIndex.size(); i++) { + + SelectionDetail sel = valsAtIndex.get(i); + + if (axis == null || sel.dataSet.getAxisDependency() == axis) { + + float cdistance = Math.abs(sel.value - value); + if (cdistance < distance) { + closest = sel; + distance = cdistance; + } + } + } + + return closest; + } + + /** + * Returns the SelectionDetail of the DataSet that contains the closest value on the + * y-axis. + * + * @param valsAtIndex all the values at a specific index + * @return + */ + public static SelectionDetail getClosestSelectionDetailByPixelY( + List valsAtIndex, + float y, + AxisDependency axis) { + + SelectionDetail closest = null; float distance = Float.MAX_VALUE; for (int i = 0; i < valsAtIndex.size(); i++) { @@ -400,15 +470,15 @@ public static int getClosestDataSetIndex(List valsAtIndex, floa if (axis == null || sel.dataSet.getAxisDependency() == axis) { - float cdistance = Math.abs((float) sel.val - val); + float cdistance = Math.abs(sel.y - y); if (cdistance < distance) { - index = valsAtIndex.get(i).dataSetIndex; + closest = sel; distance = cdistance; } } } - return index; + return closest; } /** @@ -416,11 +486,12 @@ public static int getClosestDataSetIndex(List valsAtIndex, floa * closest y-value (in pixels) that is displayed in the chart. * * @param valsAtIndex - * @param val + * @param y * @param axis * @return */ - public static float getMinimumDistance(List valsAtIndex, float val, + public static float getMinimumDistance(List valsAtIndex, + float y, AxisDependency axis) { float distance = Float.MAX_VALUE; @@ -431,7 +502,7 @@ public static float getMinimumDistance(List valsAtIndex, float if (sel.dataSet.getAxisDependency() == axis) { - float cdistance = Math.abs(sel.val - val); + float cdistance = Math.abs(sel.y - y); if (cdistance < distance) { distance = cdistance; } From 6a72ab8c189608536045568fff85d22e2149be04 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 8 May 2016 20:43:15 +0300 Subject: [PATCH 110/606] Added feature `highlightFullBar` for highlighting all values at xIndex This also provides backwards-compatibility for old behaviour of Combined chart NOTE: The drawHighlight code was just indented and wrapped in a loop over datasets. --- .../charting/charts/BarLineChartBase.java | 22 ++++ .../mikephil/charting/charts/Chart.java | 9 +- .../charting/charts/CombinedChart.java | 3 + .../charting/renderer/BarChartRenderer.java | 115 ++++++++++-------- .../renderer/BubbleChartRenderer.java | 100 ++++++++------- .../renderer/CandleStickChartRenderer.java | 52 ++++---- .../renderer/CombinedChartRenderer.java | 2 +- .../charting/renderer/LineChartRenderer.java | 57 +++++---- .../renderer/ScatterChartRenderer.java | 52 +++++--- 9 files changed, 254 insertions(+), 158 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index 2c70824d12..0a7030f901 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -80,6 +80,11 @@ public abstract class BarLineChartBase= 0 - && index < (mChart.getXChartMax() * mAnimator.getPhaseX()) / setCount) { + Transformer trans = mChart.getTransformer(set.getAxisDependency()); - BarEntry e = set.getEntryForXIndex(index); + mHighlightPaint.setColor(set.getHighLightColor()); + mHighlightPaint.setAlpha(set.getHighLightAlpha()); - if (e == null || e.getXIndex() != index) - continue; + int index = high.getXIndex(); - float groupspace = mChart.getBarData().getGroupSpace(); - boolean isStack = h.getStackIndex() < 0 ? false : true; + // check outofbounds + if (index >= 0 + && index < (mChart.getXChartMax() * mAnimator.getPhaseX()) / setCount) { - // calculate the correct x-position - float x = index * setCount + dataSetIndex + groupspace / 2f - + groupspace * index; + BarEntry e = set.getEntryForXIndex(index); - final float y1; - final float y2; + if (e == null || e.getXIndex() != index) + continue; - if (isStack) { - y1 = h.getRange().from; - y2 = h.getRange().to; - } else { - y1 = e.getVal(); - y2 = 0.f; - } + float groupspace = barData.getGroupSpace(); + boolean isStack = high.getStackIndex() < 0 ? false : true; - prepareBarHighlight(x, y1, y2, barspaceHalf, trans); + // calculate the correct x-position + float x = index * setCount + dataSetIndex + groupspace / 2f + + groupspace * index; - c.drawRect(mBarRect, mHighlightPaint); + final float y1; + final float y2; - if (mChart.isDrawHighlightArrowEnabled()) { + if (isStack) { + y1 = high.getRange().from; + y2 = high.getRange().to; + } else { + y1 = e.getVal(); + y2 = 0.f; + } + + prepareBarHighlight(x, y1, y2, barspaceHalf, trans); + + c.drawRect(mBarRect, mHighlightPaint); - mHighlightPaint.setAlpha(255); + if (mChart.isDrawHighlightArrowEnabled()) { - // distance between highlight arrow and bar - float offsetY = mAnimator.getPhaseY() * 0.07f; + mHighlightPaint.setAlpha(255); - float[] values = new float[9]; - trans.getPixelToValueMatrix().getValues(values); - final float xToYRel = Math.abs(values[Matrix.MSCALE_Y] / values[Matrix.MSCALE_X]); + // distance between highlight arrow and bar + float offsetY = mAnimator.getPhaseY() * 0.07f; - final float arrowWidth = set.getBarSpace() / 2.f; - final float arrowHeight = arrowWidth * xToYRel; + float[] values = new float[9]; + trans.getPixelToValueMatrix().getValues(values); + final float xToYRel = Math.abs( + values[Matrix.MSCALE_Y] / values[Matrix.MSCALE_X]); - final float yArrow = (y1 > -y2 ? y1 : y1) * mAnimator.getPhaseY(); + final float arrowWidth = set.getBarSpace() / 2.f; + final float arrowHeight = arrowWidth * xToYRel; - Path arrow = new Path(); - arrow.moveTo(x + 0.4f, yArrow + offsetY); - arrow.lineTo(x + 0.4f + arrowWidth, yArrow + offsetY - arrowHeight); - arrow.lineTo(x + 0.4f + arrowWidth, yArrow + offsetY + arrowHeight); + final float yArrow = (y1 > -y2 ? y1 : y1) * mAnimator.getPhaseY(); - trans.pathValueToPixel(arrow); - c.drawPath(arrow, mHighlightPaint); + Path arrow = new Path(); + arrow.moveTo(x + 0.4f, yArrow + offsetY); + arrow.lineTo(x + 0.4f + arrowWidth, yArrow + offsetY - arrowHeight); + arrow.lineTo(x + 0.4f + arrowWidth, yArrow + offsetY + arrowHeight); + + trans.pathValueToPixel(arrow); + c.drawPath(arrow, mHighlightPaint); + } } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index cb978c6fbc..3289a6bc6a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -11,6 +11,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.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -197,66 +198,83 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); - for (Highlight indice : indices) { + for (Highlight high : indices) { - IBubbleDataSet dataSet = bubbleData.getDataSetByIndex(indice.getDataSetIndex()); + final int minDataSetIndex = high.getDataSetIndex() == -1 + ? 0 + : high.getDataSetIndex(); + final int maxDataSetIndex = high.getDataSetIndex() == -1 + ? bubbleData.getDataSetCount() + : (high.getDataSetIndex() + 1); + if (maxDataSetIndex - minDataSetIndex < 1) continue; - if (dataSet == null || !dataSet.isHighlightEnabled()) - continue; + for (int dataSetIndex = minDataSetIndex; + dataSetIndex < maxDataSetIndex; + dataSetIndex++) { - final BubbleEntry entry = (BubbleEntry) bubbleData.getEntryForHighlight(indice); - if (entry == null || entry.getXIndex() != indice.getXIndex()) - continue; + IBubbleDataSet dataSet = bubbleData.getDataSetByIndex(dataSetIndex); - BubbleEntry entryFrom = dataSet.getEntryForXIndex(mMinX); - BubbleEntry entryTo = dataSet.getEntryForXIndex(mMaxX); + if (dataSet == null || !dataSet.isHighlightEnabled()) + continue; - int minx = dataSet.getEntryIndex(entryFrom); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount()); + final BubbleEntry entry = (BubbleEntry) bubbleData.getEntryForHighlight(high); + if (entry == null || entry.getXIndex() != high.getXIndex()) + continue; - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + BubbleEntry entryFrom = dataSet.getEntryForXIndex(mMinX); + BubbleEntry entryTo = dataSet.getEntryForXIndex(mMaxX); - sizeBuffer[0] = 0f; - sizeBuffer[2] = 1f; + int minx = dataSet.getEntryIndex(entryFrom); + int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount()); - trans.pointValuesToPixel(sizeBuffer); + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - boolean normalizeSize = dataSet.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] = (float) (entry.getXIndex() - minx) * phaseX + (float) minx; - pointBuffer[1] = (float) (entry.getVal()) * phaseY; - trans.pointValuesToPixel(pointBuffer); + boolean normalizeSize = dataSet.isNormalizeSizeEnabled(); - float shapeHalf = getShapeSize(entry.getSize(), dataSet.getMaxSize(), referenceSize, normalizeSize) / 2f; + // 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); - if (!mViewPortHandler.isInBoundsTop(pointBuffer[1] + shapeHalf) - || !mViewPortHandler.isInBoundsBottom(pointBuffer[1] - shapeHalf)) - continue; + pointBuffer[0] = (float) (entry.getXIndex() - minx) * phaseX + (float) minx; + pointBuffer[1] = (float) (entry.getVal()) * phaseY; + trans.pointValuesToPixel(pointBuffer); - if (!mViewPortHandler.isInBoundsLeft(pointBuffer[0] + shapeHalf)) - continue; + float shapeHalf = getShapeSize(entry.getSize(), + dataSet.getMaxSize(), + referenceSize, + normalizeSize) / 2f; - if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) - break; + if (!mViewPortHandler.isInBoundsTop(pointBuffer[1] + shapeHalf) + || !mViewPortHandler.isInBoundsBottom(pointBuffer[1] - shapeHalf)) + continue; - if (indice.getXIndex() < minx || indice.getXIndex() >= maxx) - continue; + if (!mViewPortHandler.isInBoundsLeft(pointBuffer[0] + shapeHalf)) + continue; + + if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) + break; - final int originalColor = dataSet.getColor(entry.getXIndex()); + if (high.getXIndex() < minx || high.getXIndex() >= maxx) + continue; + + final int originalColor = dataSet.getColor(entry.getXIndex()); - 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(dataSet.getHighlightCircleWidth()); - c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mHighlightPaint); + mHighlightPaint.setColor(color); + mHighlightPaint.setStrokeWidth(dataSet.getHighlightCircleWidth()); + c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mHighlightPaint); + } } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index 790d83ae92..ff4deed981 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -309,38 +309,48 @@ public void drawExtras(Canvas c) { @Override public void drawHighlighted(Canvas c, Highlight[] indices) { - for (int i = 0; i < indices.length; i++) { + CandleData candleData = mChart.getCandleData(); - int xIndex = indices[i].getXIndex(); // get the - // x-position + for (Highlight high : indices) { - ICandleDataSet set = mChart.getCandleData().getDataSetByIndex( - indices[i].getDataSetIndex()); + final int minDataSetIndex = high.getDataSetIndex() == -1 + ? 0 + : high.getDataSetIndex(); + final int maxDataSetIndex = high.getDataSetIndex() == -1 + ? candleData.getDataSetCount() + : (high.getDataSetIndex() + 1); + if (maxDataSetIndex - minDataSetIndex < 1) continue; - if (set == null || !set.isHighlightEnabled()) - continue; + for (int dataSetIndex = minDataSetIndex; + dataSetIndex < maxDataSetIndex; + dataSetIndex++) { - CandleEntry e = set.getEntryForXIndex(xIndex); + int xIndex = high.getXIndex(); // get the + // x-position - if (e == null || e.getXIndex() != xIndex) - continue; + ICandleDataSet set = mChart.getCandleData().getDataSetByIndex(dataSetIndex); - float low = e.getLow() * mAnimator.getPhaseY(); - float high = e.getHigh() * mAnimator.getPhaseY(); - float y = (low + high) / 2f; + if (set == null || !set.isHighlightEnabled()) + continue; - float min = mChart.getYChartMin(); - float max = mChart.getYChartMax(); + CandleEntry e = set.getEntryForXIndex(xIndex); + if (e == null || e.getXIndex() != xIndex) + continue; + + float lowValue = e.getLow() * mAnimator.getPhaseY(); + float highValue = e.getHigh() * mAnimator.getPhaseY(); + float y = (lowValue + highValue) / 2f; - float[] pts = new float[]{ - xIndex, y - }; + float[] pts = new float[]{ + xIndex, y + }; - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); + mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); - // draw the lines - drawHighlightLines(c, pts, set); + // draw the lines + drawHighlightLines(c, pts, set); + } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java index 4b01d48753..982e22e36a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java @@ -129,7 +129,7 @@ else if (renderer instanceof BubbleChartRenderer) ArrayList dataIndices = new ArrayList<>(); for (Highlight h : indices) { - if (h.getDataIndex() == dataIndex) + if (h.getDataIndex() == dataIndex || h.getDataIndex() == -1) dataIndices.add(h); } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index 442fe541dd..1334461831 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -9,6 +9,7 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; @@ -669,36 +670,50 @@ protected void drawCircles(Canvas c) { @Override public void drawHighlighted(Canvas c, Highlight[] indices) { - for (int i = 0; i < indices.length; i++) { + LineData lineData = mChart.getLineData(); - ILineDataSet set = mChart.getLineData().getDataSetByIndex(indices[i] - .getDataSetIndex()); + for (Highlight high : indices) { - if (set == null || !set.isHighlightEnabled()) - continue; + final int minDataSetIndex = high.getDataSetIndex() == -1 + ? 0 + : high.getDataSetIndex(); + final int maxDataSetIndex = high.getDataSetIndex() == -1 + ? lineData.getDataSetCount() + : (high.getDataSetIndex() + 1); + if (maxDataSetIndex - minDataSetIndex < 1) continue; - int xIndex = indices[i].getXIndex(); // get the - // x-position + for (int dataSetIndex = minDataSetIndex; + dataSetIndex < maxDataSetIndex; + dataSetIndex++) { - if (xIndex > mChart.getXChartMax() * mAnimator.getPhaseX()) - continue; + ILineDataSet set = lineData.getDataSetByIndex(dataSetIndex); - final float yVal = set.getYValForXIndex(xIndex); - if (Float.isNaN(yVal)) - continue; + if (set == null || !set.isHighlightEnabled()) + continue; - float y = yVal * mAnimator.getPhaseY(); // get - // the - // y-position + int xIndex = high.getXIndex(); // get the + // x-position - float[] pts = new float[]{ - xIndex, y - }; + if (xIndex > mChart.getXChartMax() * mAnimator.getPhaseX()) + continue; - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); + final float yVal = set.getYValForXIndex(xIndex); + if (Float.isNaN(yVal)) + continue; + + float y = yVal * mAnimator.getPhaseY(); // get + // the + // y-position + + float[] pts = new float[]{ + xIndex, y + }; - // draw the lines - drawHighlightLines(c, pts, set); + mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); + + // draw the lines + drawHighlightLines(c, pts, set); + } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index 684c6701f7..754b260a20 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -376,35 +376,49 @@ public void drawExtras(Canvas c) { @Override public void drawHighlighted(Canvas c, Highlight[] indices) { - for (int i = 0; i < indices.length; i++) { + ScatterData scatterData = mChart.getScatterData(); + + for (Highlight high : indices) { + + final int minDataSetIndex = high.getDataSetIndex() == -1 + ? 0 + : high.getDataSetIndex(); + final int maxDataSetIndex = high.getDataSetIndex() == -1 + ? scatterData.getDataSetCount() + : (high.getDataSetIndex() + 1); + if (maxDataSetIndex - minDataSetIndex < 1) continue; + + for (int dataSetIndex = minDataSetIndex; + dataSetIndex < maxDataSetIndex; + dataSetIndex++) { - IScatterDataSet set = mChart.getScatterData().getDataSetByIndex(indices[i] - .getDataSetIndex()); + IScatterDataSet set = scatterData.getDataSetByIndex(dataSetIndex); - if (set == null || !set.isHighlightEnabled()) - continue; + if (set == null || !set.isHighlightEnabled()) + continue; - int xIndex = indices[i].getXIndex(); // get the - // x-position + int xIndex = high.getXIndex(); // get the + // x-position - if (xIndex > mChart.getXChartMax() * mAnimator.getPhaseX()) - continue; + if (xIndex > mChart.getXChartMax() * mAnimator.getPhaseX()) + continue; - final float yVal = set.getYValForXIndex(xIndex); - if (Float.isNaN(yVal)) - continue; + final float yVal = set.getYValForXIndex(xIndex); + if (Float.isNaN(yVal)) + continue; - float y = yVal * mAnimator.getPhaseY(); + float y = yVal * mAnimator.getPhaseY(); - float[] pts = new float[]{ - xIndex, y - }; + float[] pts = new float[]{ + xIndex, y + }; - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); + mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); - // draw the lines - drawHighlightLines(c, pts, set); + // draw the lines + drawHighlightLines(c, pts, set); + } } } } From ea89dfef0443b44a153b600626d3b50e5cc5b6b7 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 8 May 2016 21:04:33 +0300 Subject: [PATCH 111/606] Supress false-positive inspection warning --- .../mikephil/charting/renderer/CandleStickChartRenderer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index ff4deed981..dd7c49481c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -50,6 +50,7 @@ public void drawData(Canvas c) { } } + @SuppressWarnings("ResourceAsColor") protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); From db19fdcb94cd74bdb1d654bd6aaee456bb7f1420 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 10 May 2016 19:27:08 +0300 Subject: [PATCH 112/606] These DPs must be converted to pixels --- .../mikephil/charting/renderer/HorizontalBarChartRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index 41e1514d5d..46ab01b6e9 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -53,7 +53,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { mShadowPaint.setColor(dataSet.getBarShadowColor()); mBarBorderPaint.setColor(dataSet.getBarBorderColor()); - mBarBorderPaint.setStrokeWidth(dataSet.getBarBorderWidth()); + mBarBorderPaint.setStrokeWidth(Util.convertDpToPixel(dataSet.getBarBorderWidth())); final boolean drawBorder = dataSet.getBarBorderWidth() > 0.f; From 4cf9f5965434ccf72c3cccde1b661272cfa5405b Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 10 May 2016 19:27:37 +0300 Subject: [PATCH 113/606] These DPs must be converted to pixels --- .../com/github/mikephil/charting/renderer/BarChartRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java index dac3a5ba7d..473a721f89 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -86,7 +86,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { mShadowPaint.setColor(dataSet.getBarShadowColor()); mBarBorderPaint.setColor(dataSet.getBarBorderColor()); - mBarBorderPaint.setStrokeWidth(dataSet.getBarBorderWidth()); + mBarBorderPaint.setStrokeWidth(Util.convertDpToPixel(dataSet.getBarBorderWidth())); final boolean drawBorder = dataSet.getBarBorderWidth() > 0.f; From 92810c8d9ab55903271626864b92c3524e38388d Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 15 May 2016 09:59:06 +0300 Subject: [PATCH 114/606] Fixed typo in Util/Utils --- .../com/github/mikephil/charting/renderer/BarChartRenderer.java | 2 +- .../mikephil/charting/renderer/HorizontalBarChartRenderer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java index 473a721f89..8179ebd2d4 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -86,7 +86,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { mShadowPaint.setColor(dataSet.getBarShadowColor()); mBarBorderPaint.setColor(dataSet.getBarBorderColor()); - mBarBorderPaint.setStrokeWidth(Util.convertDpToPixel(dataSet.getBarBorderWidth())); + mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth())); final boolean drawBorder = dataSet.getBarBorderWidth() > 0.f; diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index 46ab01b6e9..cadc706ae4 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -53,7 +53,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { mShadowPaint.setColor(dataSet.getBarShadowColor()); mBarBorderPaint.setColor(dataSet.getBarBorderColor()); - mBarBorderPaint.setStrokeWidth(Util.convertDpToPixel(dataSet.getBarBorderWidth())); + mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth())); final boolean drawBorder = dataSet.getBarBorderWidth() > 0.f; From 4393b2710ac30f858dea6369da249945bbbed591 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 15 May 2016 10:36:45 +0300 Subject: [PATCH 115/606] I hope this will position x-labels correctly vertically And without any weird hack. But by just finally figuring out how to correctly offset the drawing from the font glyph size --- MPChartLib/src/com/github/mikephil/charting/utils/Utils.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java index 9f342c7dce..caaef8153c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java @@ -611,17 +611,16 @@ public static void drawXAxisValue(Canvas c, String text, float x, float y, float drawOffsetX = 0.f; float drawOffsetY = 0.f; + final float lineHeight = paint.getFontMetrics(mFontMetricsBuffer); paint.getTextBounds(text, 0, text.length(), mDrawTextRectBuffer); - final float lineHeight = mDrawTextRectBuffer.height(); - // Android sometimes has pre-padding drawOffsetX -= mDrawTextRectBuffer.left; // Android does not snap the bounds to line boundaries, // and draws from bottom to top. // And we want to normalize it. - drawOffsetY += lineHeight; + drawOffsetY += -mFontMetricsBuffer.ascent; // To have a consistent point of reference, we always draw left-aligned Paint.Align originalTextAlign = paint.getTextAlign(); From 2cb255a9f425c44e0dd74858cad7d099a3217ac6 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 22 May 2016 15:23:18 +0300 Subject: [PATCH 116/606] Clip drawing area of limit lines when !isDrawLimitLinesBehindDataEnabled --- .../github/mikephil/charting/charts/BarLineChartBase.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index 0a7030f901..304ae6cc1f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -261,6 +261,9 @@ protected void onDraw(Canvas canvas) { mRenderer.drawExtras(canvas); + clipRestoreCount = canvas.save(); + canvas.clipRect(mViewPortHandler.getContentRect()); + if (!mXAxis.isDrawLimitLinesBehindDataEnabled()) mXAxisRenderer.renderLimitLines(canvas); @@ -270,6 +273,8 @@ protected void onDraw(Canvas canvas) { if (!mAxisRight.isDrawLimitLinesBehindDataEnabled()) mAxisRendererRight.renderLimitLines(canvas); + canvas.restoreToCount(clipRestoreCount); + mXAxisRenderer.renderAxisLabels(canvas); mAxisRendererLeft.renderAxisLabels(canvas); mAxisRendererRight.renderAxisLabels(canvas); From 17f919aa437042ebd2594c6b789613019f0ccf3f Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 22 May 2016 17:15:22 +0300 Subject: [PATCH 117/606] Allow more control over circle hole radius --- .../LineChartActivityColored.java | 1 + .../mikephil/charting/data/LineDataSet.java | 19 ++++++++++++++ .../implementation/RealmLineDataSet.java | 25 ++++++++++++++++--- .../interfaces/datasets/ILineDataSet.java | 5 ++++ .../charting/renderer/LineChartRenderer.java | 11 +++++--- 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index b4d5a5ce81..e12d8907f8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -120,6 +120,7 @@ private LineData getData(int count, float range) { set1.setLineWidth(1.75f); set1.setCircleRadius(5f); + set1.setCircleHoleRadius(2.5f); set1.setColor(Color.WHITE); set1.setCircleColorHole(Color.WHITE); set1.setHighLightColor(Color.WHITE); diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java index 1fb380eeaa..10e098712e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java @@ -29,6 +29,9 @@ public class LineDataSet extends LineRadarDataSet implements ILineDataSet /** the radius of the circle-shaped value indicators */ private float mCircleRadius = 8f; + /** the hole radius of the circle-shaped value indicators */ + private float mCircleHoleRadius = 4f; + /** sets the intensity of the cubic lines */ private float mCubicIntensity = 0.2f; @@ -71,6 +74,7 @@ public DataSet copy() { copied.mMode = mMode; copied.mColors = mColors; copied.mCircleRadius = mCircleRadius; + copied.mCircleHoleRadius = mCircleHoleRadius; copied.mCircleColors = mCircleColors; copied.mDashPathEffect = mDashPathEffect; copied.mDrawCircles = mDrawCircles; @@ -136,6 +140,21 @@ public float getCircleRadius() { return mCircleRadius; } + /** + * sets the hole radius of the drawn circles. + * Default radius = 2f + * + * @param holeRadius + */ + public void setCircleHoleRadius(float holeRadius) { + mCircleHoleRadius = Utils.convertDpToPixel(holeRadius); + } + + @Override + public float getCircleHoleRadius() { + return mCircleHoleRadius; + } + /** * sets the size (radius) of the circle shpaed value indicators, * default size = 4f diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java index b8832271af..4734a9e39c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java @@ -3,7 +3,6 @@ import android.content.Context; import android.graphics.Color; import android.graphics.DashPathEffect; -import android.graphics.drawable.Drawable; import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.realm.base.RealmLineRadarDataSet; @@ -40,7 +39,10 @@ public class RealmLineDataSet extends RealmLineRadarDataS /** * the radius of the circle-shaped value indicators */ - private float mCircleSize = 8f; + private float mCircleRadius = 8f; + + /** the hole radius of the circle-shaped value indicators */ + private float mCircleHoleRadius = 4f; /** * sets the intensity of the cubic lines @@ -146,12 +148,27 @@ public float getCubicIntensity() { * @param size */ public void setCircleSize(float size) { - mCircleSize = Utils.convertDpToPixel(size); + mCircleRadius = Utils.convertDpToPixel(size); } @Override public float getCircleRadius() { - return mCircleSize; + return mCircleRadius; + } + + /** + * sets the hole radius of the drawn circles. + * Default radius = 2f + * + * @param holeRadius + */ + public void setCircleHoleRadius(float holeRadius) { + mCircleHoleRadius = Utils.convertDpToPixel(holeRadius); + } + + @Override + public float getCircleHoleRadius() { + return mCircleHoleRadius; } /** diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java index fb7ba45536..6f62274b8e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java @@ -38,6 +38,11 @@ public interface ILineDataSet extends ILineRadarDataSet { */ float getCircleRadius(); + /** + * Returns the hole radius of the drawn circles. + */ + float getCircleHoleRadius(); + /** * Returns the color at the given index of the DataSet's circle-color array. * Performs a IndexOutOfBounds check by modulus. diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index 1334461831..ebc1215c3e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -626,7 +626,9 @@ protected void drawCircles(Canvas c) { int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); - float halfsize = dataSet.getCircleRadius() / 2f; + float circleRadius = dataSet.getCircleRadius(); + float circleHoleRadius = dataSet.getCircleHoleRadius(); + boolean isDrawCircleHoleEnabled = dataSet.isDrawCircleHoleEnabled(); for (int j = minx, count = (int) Math.ceil((maxx - minx) * phaseX + minx); @@ -658,10 +660,11 @@ protected void drawCircles(Canvas c) { c.drawCircle(circlesBuffer[0], circlesBuffer[1], dataSet.getCircleRadius(), mRenderPaint); - if (dataSet.isDrawCircleHoleEnabled() - && circleColor != mCirclePaintInner.getColor()) + if (isDrawCircleHoleEnabled && + circleHoleRadius < circleRadius && + circleHoleRadius > 0.f) c.drawCircle(circlesBuffer[0], circlesBuffer[1], - halfsize, + circleHoleRadius, mCirclePaintInner); } } From 881581fa2d04b53cdae3ff0bc8a3f9d560db68f2 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 22 May 2016 17:55:07 +0300 Subject: [PATCH 118/606] Allow transparent circle hole color --- .../charting/renderer/LineChartRenderer.java | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index ebc1215c3e..64e136bfdd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -16,6 +16,7 @@ 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.utils.ColorTemplate; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -593,6 +594,8 @@ public void drawExtras(Canvas c) { drawCircles(c); } + private Path mCirclePathBuffer = new Path(); + protected void drawCircles(Canvas c) { mRenderPaint.setStyle(Paint.Style.FILL); @@ -628,7 +631,11 @@ protected void drawCircles(Canvas c) { float circleRadius = dataSet.getCircleRadius(); float circleHoleRadius = dataSet.getCircleHoleRadius(); - boolean isDrawCircleHoleEnabled = dataSet.isDrawCircleHoleEnabled(); + boolean drawCircleHole = dataSet.isDrawCircleHoleEnabled() && + circleHoleRadius < circleRadius && + circleHoleRadius > 0.f; + boolean drawTransparentCircleHole = drawCircleHole && + dataSet.getCircleHoleColor() == ColorTemplate.COLOR_NONE; for (int j = minx, count = (int) Math.ceil((maxx - minx) * phaseX + minx); @@ -653,19 +660,37 @@ protected void drawCircles(Canvas c) { !mViewPortHandler.isInBoundsY(circlesBuffer[1])) continue; - int circleColor = dataSet.getCircleColor(j); + mRenderPaint.setColor(dataSet.getCircleColor(j)); - mRenderPaint.setColor(circleColor); + if (drawTransparentCircleHole) { - c.drawCircle(circlesBuffer[0], circlesBuffer[1], dataSet.getCircleRadius(), - mRenderPaint); + // Begin path for circle with hole + mCirclePathBuffer.reset(); - if (isDrawCircleHoleEnabled && - circleHoleRadius < circleRadius && - circleHoleRadius > 0.f) - c.drawCircle(circlesBuffer[0], circlesBuffer[1], + mCirclePathBuffer.addCircle(circlesBuffer[0], circlesBuffer[1], + circleRadius, + Path.Direction.CW); + + // Cut hole in path + mCirclePathBuffer.addCircle(circlesBuffer[0], circlesBuffer[1], circleHoleRadius, - mCirclePaintInner); + Path.Direction.CCW); + + // Fill in-between + c.drawPath(mCirclePathBuffer, mRenderPaint); + + } else { + + c.drawCircle(circlesBuffer[0], circlesBuffer[1], + circleRadius, + mRenderPaint); + + if (drawCircleHole) { + c.drawCircle(circlesBuffer[0], circlesBuffer[1], + circleHoleRadius, + mCirclePaintInner); + } + } } } } From b59cf564a085a937120e8eba509396261156a6b4 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Sun, 22 May 2016 17:55:54 +0300 Subject: [PATCH 119/606] Updated coloured line circle colors for correct transparent hole --- .../mpchartexample/LineChartActivityColored.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index e12d8907f8..49491eaa8b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -12,6 +12,7 @@ import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -54,7 +55,7 @@ protected void onCreate(Bundle savedInstanceState) { private void setupChart(LineChart chart, LineData data, int color) { - ((LineDataSet) data.getDataSetByIndex(0)).setCircleColor(color); + ((LineDataSet) data.getDataSetByIndex(0)).setCircleColorHole(color); // no description text chart.setDescription(""); @@ -122,7 +123,7 @@ private LineData getData(int count, float range) { set1.setCircleRadius(5f); set1.setCircleHoleRadius(2.5f); set1.setColor(Color.WHITE); - set1.setCircleColorHole(Color.WHITE); + set1.setCircleColor(Color.WHITE); set1.setHighLightColor(Color.WHITE); set1.setDrawValues(false); From 87193a3d2e179af7dd8d606eecd844130d69ebb9 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 26 May 2016 15:26:14 +0300 Subject: [PATCH 120/606] Add extra control point when rendering cubic bezier out of range --- .../mikephil/charting/renderer/LineChartRenderer.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index 64e136bfdd..3d4c359776 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -13,6 +13,7 @@ import com.github.mikephil.charting.data.DataSet; 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.dataprovider.LineDataProvider; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; @@ -205,7 +206,7 @@ protected void drawCubicBezier(Canvas c, ILineDataSet dataSet) { Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); int diff = (entryFrom == entryTo) ? 1 : 0; - int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); + int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff - 1, 0); int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); @@ -563,6 +564,9 @@ public void drawValues(Canvas c) { Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); int diff = (entryFrom == entryTo) ? 1 : 0; + if (dataSet.getMode() == LineDataSet.Mode.CUBIC_BEZIER) + diff += 1; + int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); @@ -626,6 +630,9 @@ protected void drawCircles(Canvas c) { Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); int diff = (entryFrom == entryTo) ? 1 : 0; + if (dataSet.getMode() == LineDataSet.Mode.CUBIC_BEZIER) + diff += 1; + int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); From 43bc45b7185de6e55ee4f79ef314818360434098 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 27 May 2016 13:27:55 +0200 Subject: [PATCH 121/606] Minor fixes in example --- MPChartExample/build.gradle | 2 +- .../xxmassdeveloper/mpchartexample/AnotherBarActivity.java | 3 ++- .../mpchartexample/CubicLineChartActivity.java | 1 + .../xxmassdeveloper/mpchartexample/LineChartActivity1.java | 3 ++- .../xxmassdeveloper/mpchartexample/LineChartActivity2.java | 3 ++- .../mpchartexample/ListViewBarChartActivity.java | 2 +- .../mpchartexample/MultiLineChartActivity.java | 3 +-- .../xxmassdeveloper/mpchartexample/ScatterChartActivity.java | 4 ++-- build.gradle | 2 +- 9 files changed, 13 insertions(+), 10 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 5cef000d7c..ad4fc88ba2 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -37,7 +37,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0' + classpath 'com.android.tools.build:gradle:2.1.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/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 3918072671..a516854faf 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -206,8 +206,9 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { BarData data = new BarData(xVals, dataSets); mChart.setData(data); - mChart.invalidate(); } + + mChart.invalidate(); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index a50171e710..0c25904f48 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -290,6 +290,7 @@ private void setData(int count, float range) { set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals); mChart.getData().setXVals(xVals); + mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { // create a dataset and give it a type diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 5a32612b5f..13efdb1503 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -349,7 +349,7 @@ private void setData(int count, float range) { float mult = (range + 1); float val = (float) (Math.random() * mult) + 3;// + (float) // ((mult * - // 0.1) / 10); + // 0.1) / 10);x yVals.add(new Entry(val, i)); } @@ -360,6 +360,7 @@ private void setData(int count, float range) { set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals); mChart.getData().setXVals(xVals); + mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { // create a dataset and give it a type diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 0d76e71813..c50edff816 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -319,6 +319,7 @@ private void setData(int count, float range) { set1.setYVals(yVals1); set2.setYVals(yVals2); mChart.getData().setXVals(xVals); + mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { // create a dataset and give it a type @@ -352,8 +353,8 @@ private void setData(int count, float range) { //set2.setFillFormatter(new MyFillFormatter(900f)); ArrayList dataSets = new ArrayList(); - dataSets.add(set2); dataSets.add(set1); // add the datasets + dataSets.add(set2); // create a data object with the datasets LineData data = new LineData(xVals, dataSets); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index e1c69086bc..f430675338 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java @@ -112,7 +112,7 @@ public View getView(int position, View convertView, ViewGroup parent) { // do not forget to refresh the chart // holder.chart.invalidate(); - holder.chart.animateY(700, Easing.EasingOption.EaseInCubic); + holder.chart.animateY(700); return convertView; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index 05e25fae64..09f5a7ac68 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -57,8 +57,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDescription(""); mChart.setDrawBorders(false); - mChart.getAxisLeft().setDrawAxisLine(false); - mChart.getAxisLeft().setDrawGridLines(false); + mChart.getAxisLeft().setEnabled(false); mChart.getAxisRight().setDrawAxisLine(false); mChart.getAxisRight().setDrawGridLines(false); mChart.getXAxis().setDrawAxisLine(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index e716f7ff47..95b3954fb8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -195,8 +195,8 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { set1.setColor(ColorTemplate.COLORFUL_COLORS[0]); ScatterDataSet set2 = new ScatterDataSet(yVals2, "DS 2"); set2.setScatterShape(ScatterShape.CIRCLE); - set2.setScatterShapeHoleColor(Color.WHITE); - set2.setScatterShapeHoleRadius(5f); + set2.setScatterShapeHoleColor(ColorTemplate.COLORFUL_COLORS[3]); + set2.setScatterShapeHoleRadius(4f); set2.setColor(ColorTemplate.COLORFUL_COLORS[1]); ScatterDataSet set3 = new ScatterDataSet(yVals3, "DS 3"); set3.setScatterShape(ScatterShape.CROSS); diff --git a/build.gradle b/build.gradle index 66dbb5af3c..564d957cc5 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0' + classpath 'com.android.tools.build:gradle:2.1.0' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } } From 98fe348b216413b728ebf1d85606eaf8916e3a0f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 27 May 2016 14:15:38 +0200 Subject: [PATCH 122/606] Update README.md --- README.md | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 1e58a84f48..177b50abe3 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,22 @@ As an additional feature, this library allows cross-platform development between 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). +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! + +**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! + - 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! + + ## 3rd party bindings Xamarin (by @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/). @@ -31,21 +47,6 @@ If you like this library, please tell others about it :two_hearts: :two_hearts: - 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) -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! - -**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! - - 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! - Demo ----- From 77f83cddc5ae9e4a7094bc4f692aeb2179c9ca6c Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 27 May 2016 17:46:48 +0200 Subject: [PATCH 123/606] Update gradle --- MPChartExample/AndroidManifest.xml | 4 ++-- MPChartExample/build.gradle | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index 9efb9639b9..be66731b50 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="51" + android:versionName="2.2.5" > Date: Fri, 27 May 2016 22:36:23 +0200 Subject: [PATCH 124/606] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 177b50abe3..6390228dae 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,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/v2.2.4/javadoc/) + - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v2.2.5/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) @@ -104,7 +104,7 @@ repositories { } dependencies { - compile 'com.github.PhilJay:MPAndroidChart:v2.2.4' + compile 'com.github.PhilJay:MPAndroidChart:v2.2.5' } ``` @@ -119,7 +119,7 @@ dependencies { com.github.PhilJay MPAndroidChart - v2.2.4 + v2.2.5 ``` @@ -139,7 +139,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/v2.2.4/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/v2.2.5/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 02704708979a2634b817dcc54615a48e6035552d Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 28 May 2016 15:47:02 +0200 Subject: [PATCH 125/606] Minor fixes related to polyline example --- .../PiePolylineChartActivity.java | 8 ++-- .../charting/renderer/PieChartRenderer.java | 45 +++++++++---------- 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index 58f1e085ea..0e9a816869 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -71,7 +71,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setCenterTextTypeface(Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf")); mChart.setCenterText(generateCenterSpannableText()); - mChart.setExtraOffsets(5.f, 5.f, 5.f, 5.f); + mChart.setExtraOffsets(20.f, 0.f, 20.f, 0.f); mChart.setDrawHoleEnabled(true); mChart.setHoleColor(Color.WHITE); @@ -226,7 +226,7 @@ private void setData(int count, float range) { dataSet.setValueLinePart1OffsetPercentage(80.f); - dataSet.setValueLinePart1Length(0.3f); + dataSet.setValueLinePart1Length(0.2f); dataSet.setValueLinePart2Length(0.4f); // dataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); dataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); @@ -247,10 +247,10 @@ private void setData(int count, float range) { private SpannableString generateCenterSpannableText() { SpannableString s = new SpannableString("MPAndroidChart\ndeveloped by Philipp Jahoda"); - s.setSpan(new RelativeSizeSpan(1.7f), 0, 14, 0); + s.setSpan(new RelativeSizeSpan(1.5f), 0, 14, 0); s.setSpan(new StyleSpan(Typeface.NORMAL), 14, s.length() - 15, 0); s.setSpan(new ForegroundColorSpan(Color.GRAY), 14, s.length() - 15, 0); - s.setSpan(new RelativeSizeSpan(.8f), 14, s.length() - 15, 0); + s.setSpan(new RelativeSizeSpan(.65f), 14, s.length() - 15, 0); s.setSpan(new StyleSpan(Typeface.ITALIC), s.length() - 14, s.length(), 0); s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), s.length() - 14, s.length(), 0); return s; diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java index 226b925371..6f570d3d2b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -143,8 +143,7 @@ protected float calculateMinimumRadiusForSpacedSlice( float arcStartPointX, float arcStartPointY, float startAngle, - float sweepAngle) - { + float sweepAngle) { final float angleMiddle = startAngle + sweepAngle / 2.f; // Other point of the arc @@ -163,7 +162,7 @@ protected float calculateMinimumRadiusForSpacedSlice( // After reducing space from both sides of the "slice", // the angle of the contained triangle should stay the same. // So let's find out the height of that triangle. - float containedTriangleHeight = (float)(basePointsDistance / 2.0 * + float containedTriangleHeight = (float) (basePointsDistance / 2.0 * Math.tan((180.0 - angle) / 2.0 * Utils.DEG2RAD)); // Now we subtract that from the radius @@ -228,8 +227,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { sliceSpace / (Utils.FDEG2RAD * radius); final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY; float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY; - if (sweepAngleOuter < 0.f) - { + if (sweepAngleOuter < 0.f) { sweepAngleOuter = 0.f; } @@ -284,8 +282,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { sliceSpace / (Utils.FDEG2RAD * innerRadius); final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY; float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY; - if (sweepAngleInner < 0.f) - { + if (sweepAngleInner < 0.f) { sweepAngleInner = 0.f; } final float endAngleInner = startAngleInner + sweepAngleInner; @@ -305,8 +302,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { -sweepAngleInner ); } - } - else { + } else { if (sweepAngleOuter % 360f != 0.f) { if (accountForSliceSpacing) { @@ -411,6 +407,8 @@ public void drawValues(Canvas c) { mValueLinePaint.setColor(dataSet.getValueLineColor()); mValueLinePaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getValueLineWidth())); + float offset = Utils.convertDpToPixel(5.f); + for (int j = 0; j < entryCount; j++) { Entry entry = dataSet.getEntryForIndex(j); @@ -434,8 +432,8 @@ public void drawValues(Canvas c) { float value = mChart.isUsePercentValuesEnabled() ? entry.getVal() / yValueSum * 100f : entry.getVal(); - final float sliceXBase = (float)Math.cos(transformedAngle * Utils.FDEG2RAD); - final float sliceYBase = (float)Math.sin(transformedAngle * Utils.FDEG2RAD); + final float sliceXBase = (float) Math.cos(transformedAngle * Utils.FDEG2RAD); + final float sliceYBase = (float) Math.sin(transformedAngle * Utils.FDEG2RAD); final boolean drawXOutside = drawXVals && xValuePosition == PieDataSet.ValuePosition.OUTSIDE_SLICE; @@ -465,9 +463,9 @@ public void drawValues(Canvas c) { line1Radius = radius * valueLinePart1OffsetPercentage; final float polyline2Width = dataSet.isValueLineVariableLength() - ? labelRadius * valueLineLength2 * (float)Math.abs(Math.sin( + ? labelRadius * valueLineLength2 * (float) Math.abs(Math.sin( transformedAngle * Utils.FDEG2RAD)) - : labelRadius * valueLineLength2; + : labelRadius * valueLineLength2; final float pt0x = line1Radius * sliceXBase + center.x; final float pt0y = line1Radius * sliceYBase + center.y; @@ -479,13 +477,13 @@ public void drawValues(Canvas c) { pt2x = pt1x - polyline2Width; pt2y = pt1y; mValuePaint.setTextAlign(Align.RIGHT); - labelPtx = pt2x - Utils.convertDpToPixel(5.f); + labelPtx = pt2x - offset; labelPty = pt2y; } else { pt2x = pt1x + polyline2Width; pt2y = pt1y; mValuePaint.setTextAlign(Align.LEFT); - labelPtx = pt2x + Utils.convertDpToPixel(5.f); + labelPtx = pt2x + offset; labelPty = pt2y; } @@ -518,7 +516,8 @@ public void drawValues(Canvas c) { } } else if (drawYOutside) { - drawValue(c, formatter, value, entry, 0, labelPtx, labelPty + lineHeight / 2.f, dataSet.getValueTextColor(j)); + drawValue(c, formatter, value, entry, 0, labelPtx, labelPty + lineHeight / 2.f, dataSet + .getValueTextColor(j)); } } @@ -743,20 +742,18 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY; float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY; - if (sweepAngleOuter < 0.f) - { + if (sweepAngleOuter < 0.f) { sweepAngleOuter = 0.f; } final float startAngleShifted = rotationAngle + (angle + sliceSpaceAngleShifted / 2.f) * phaseY; float sweepAngleShifted = (sliceAngle - sliceSpaceAngleShifted) * phaseY; - if (sweepAngleShifted < 0.f) - { + if (sweepAngleShifted < 0.f) { sweepAngleShifted = 0.f; } mPathBuffer.reset(); - + if (sweepAngleOuter % 360f == 0.f) { // Android is doing "mod 360" mPathBuffer.addCircle(center.x, center.y, highlightedRadius, Path.Direction.CW); @@ -809,8 +806,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { sliceSpace / (Utils.FDEG2RAD * innerRadius); final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY; float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY; - if (sweepAngleInner < 0.f) - { + if (sweepAngleInner < 0.f) { sweepAngleInner = 0.f; } final float endAngleInner = startAngleInner + sweepAngleInner; @@ -830,8 +826,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { -sweepAngleInner ); } - } - else { + } else { if (sweepAngleOuter % 360f != 0.f) { From 1b9b3da3deaea37114e1fe9423445dac4a97c2a5 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 28 May 2016 15:52:56 +0200 Subject: [PATCH 126/606] Add spin animation to pie example --- .../com/xxmassdeveloper/mpchartexample/PieChartActivity.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index bab7dcb525..a7350e9cac 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -165,6 +165,10 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.animateXY(1400, 1400); break; } + case R.id.actionToggleSpin: { + mChart.spin(1000, mChart.getRotationAngle(), mChart.getRotationAngle() + 360, Easing.EasingOption.EaseInCubic); + break; + } } return true; } From d2cc49b525eb69c506e7b46210f548b03828f76d Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 30 May 2016 10:54:11 +0200 Subject: [PATCH 127/606] Work on changing xaxis, WIP --- .../mpchartexample/AnotherBarActivity.java | 4 +- .../mpchartexample/BarChartActivity.java | 2 +- .../BarChartActivityMultiDataset.java | 6 +- .../BarChartPositiveNegative.java | 8 +- .../DynamicalAddingActivity.java | 2 +- .../HorizontalBarChartActivity.java | 3 +- .../mpchartexample/LineChartActivity1.java | 2 +- .../mpchartexample/LineChartActivity2.java | 4 +- .../mpchartexample/PerformanceLineChart.java | 2 +- .../charting/charts/BarLineChartBase.java | 14 +- .../mikephil/charting/charts/Chart.java | 2 +- .../mikephil/charting/charts/PieChart.java | 4 +- .../mikephil/charting/charts/RadarChart.java | 5 +- .../charting/components/AxisBase.java | 45 ++++++ .../mikephil/charting/components/YAxis.java | 36 +---- .../mikephil/charting/data/BaseDataSet.java | 2 +- .../mikephil/charting/data/ChartData.java | 4 +- .../mikephil/charting/data/DataSet.java | 26 ++-- .../data/realm/base/RealmBaseDataSet.java | 26 ++-- .../charting/highlight/BarHighlighter.java | 2 +- .../interfaces/datasets/IDataSet.java | 36 ++--- .../charting/renderer/AxisRenderer.java | 37 +++++ .../charting/renderer/BarChartRenderer.java | 2 +- .../renderer/BubbleChartRenderer.java | 27 +++- .../renderer/CandleStickChartRenderer.java | 38 ++--- .../charting/renderer/LineChartRenderer.java | 41 ++++-- .../charting/renderer/PieChartRenderer.java | 2 +- .../charting/renderer/RadarChartRenderer.java | 2 +- .../charting/renderer/XAxisRenderer.java | 137 +++++++++++------- .../renderer/XAxisRendererBarChart.java | 102 ++++++------- .../XAxisRendererHorizontalBarChart.java | 63 ++++---- .../charting/renderer/YAxisRenderer.java | 35 +---- .../YAxisRendererHorizontalBarChart.java | 5 +- .../renderer/YAxisRendererRadarChart.java | 2 +- .../charting/utils/EntryXIndexComparator.java | 8 +- .../mikephil/charting/utils/FileUtils.java | 4 +- .../mikephil/charting/utils/Transformer.java | 4 +- 37 files changed, 408 insertions(+), 336 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 3a15b09d4c..4a06e62c3c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -176,8 +176,8 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { float mult = (mSeekBarY.getProgress() + 1); - float val1 = (float) (Math.random() * mult) + mult / 3; - yVals1.add(new BarEntry((int) val1, i)); + float val = (float) (Math.random() * mult) + mult / 3; + yVals1.add(new BarEntry(i, val)); } ArrayList xVals = new ArrayList(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 8b3e59a549..48d8fe02a3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -238,7 +238,7 @@ private void setData(int count, float range) { for (int i = 0; i < count; i++) { float mult = (range + 1); float val = (float) (Math.random() * mult); - yVals1.add(new BarEntry(val, i)); + yVals1.add(new BarEntry(i, val)); XAxisValue value = new XAxisValue(i, mMonths[i % 12]); xVals.add(value); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 9fce121636..ef53cd5e86 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -201,17 +201,17 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { for (int i = 0; i < mSeekBarX.getProgress(); i++) { float val = (float) (Math.random() * mult) + 3; - yVals1.add(new BarEntry(val, i)); + yVals1.add(new BarEntry(i, val)); } for (int i = 0; i < mSeekBarX.getProgress(); i++) { float val = (float) (Math.random() * mult) + 3; - yVals2.add(new BarEntry(val, i)); + yVals2.add(new BarEntry(i, val)); } for (int i = 0; i < mSeekBarX.getProgress(); i++) { float val = (float) (Math.random() * mult) + 3; - yVals3.add(new BarEntry(val, i)); + yVals3.add(new BarEntry(i, val)); } BarDataSet set1, set2, set3; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index c41e30fccd..b795e80a9d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -97,7 +97,7 @@ private void setData(List dataList) { for (int i = 0; i < dataList.size(); i++) { Data d = dataList.get(i); - BarEntry entry = new BarEntry(d.yValue, d.xIndex); + BarEntry entry = new BarEntry(d.xValue, d.yValue); values.add(entry); dates[i] = new XAxisValue(i, dataList.get(i).xAxisValue); @@ -140,12 +140,12 @@ private class Data { public String xAxisValue; public float yValue; - public int xIndex; + public float xValue; - public Data(int xIndex, float yValue, String xAxisValue) { + public Data(float xValue, float yValue, String xAxisValue) { this.xAxisValue = xAxisValue; this.yValue = yValue; - this.xIndex = xIndex; + this.xValue = xValue; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index af3e744b91..3f5d4d152a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -91,7 +91,7 @@ private void removeLastEntry() { if (set != null) { - Entry e = set.getEntryForXIndex(set.getEntryCount() - 1); + Entry e = set.getEntryForXPos(set.getEntryCount() - 1); data.removeEntry(e, 0); // or remove by index diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 2d52ffbc45..0acda456f8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -240,7 +240,8 @@ private void setData(int count, float range) { for (int i = 0; i < count; i++) { xVals.add(new XAxisValue(i, mMonths[i % 12])); - yVals1.add(new BarEntry((float) (Math.random() * range), i)); + float val = (float) (Math.random() * range); + yVals1.add(new BarEntry(i, val)); } BarDataSet set1; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index d788ebed15..946e23b14d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -347,7 +347,7 @@ private void setData(int count, float range) { float val = (float) (Math.random() * mult) + 3;// + (float) // ((mult * // 0.1) / 10);x - yVals.add(new Entry(val, i)); + yVals.add(new Entry(i, val)); xVals.add(new XAxisValue(i, i + "")); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 2aec85977c..84ad55ac9c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -298,7 +298,7 @@ private void setData(int count, float range) { float val = (float) (Math.random() * mult) + 50;// + (float) // ((mult * // 0.1) / 10); - yVals1.add(new Entry(val, i)); + yVals1.add(new Entry(i, val)); } ArrayList yVals2 = new ArrayList(); @@ -308,7 +308,7 @@ private void setData(int count, float range) { float val = (float) (Math.random() * mult) + 450;// + (float) // ((mult * // 0.1) / 10); - yVals2.add(new Entry(val, i)); + yVals2.add(new Entry(i, val)); } LineDataSet set1, set2; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java index cec5d5669c..935f91721a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -105,7 +105,7 @@ private void setData(int count, float range) { float val = (float) (Math.random() * mult) + 3;// + (float) // ((mult * // 0.1) / 10); - yVals.add(new Entry(val, i)); + yVals.add(new Entry(i, val)); } // create a dataset and give it a type diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index 501629581b..c5df174efe 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -205,9 +205,11 @@ protected void onDraw(Canvas canvas) { drawGridBackground(canvas); if (mAxisLeft.isEnabled()) - mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum); + mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum, mAxisLeft.isInverted()); if (mAxisRight.isEnabled()) - mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum); + mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum, mAxisRight.isInverted()); + if (mXAxis.isEnabled()) + mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); mXAxisRenderer.renderAxisLine(canvas); mAxisRendererLeft.renderAxisLine(canvas); @@ -342,10 +344,10 @@ public void notifyDataSetChanged() { calcMinMax(); - mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum); - mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum); + mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum, mAxisLeft.isInverted()); + mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum, mAxisRight.isInverted()); - mXAxisRenderer.computeAxis(mData.getXValMaximumLength(), mData.getXVals()); + //mXAxisRenderer.computeAxis(mData.getXValMaximumLength(), mData.getXVals()); if (mLegend != null) mLegendRenderer.computeLegend(mData); @@ -565,7 +567,7 @@ protected float[] getMarkerPosition(Entry e, Highlight highlight) { BarData bd = (BarData) mData; float space = bd.getGroupSpace(); int setCount = mData.getDataSetCount(); - int i = e.getX(); + float i = e.getX(); if (this instanceof HorizontalBarChart) { diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java index f97af317dd..37a24ad1bd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java @@ -1414,7 +1414,7 @@ public List getEntriesAtIndex(int xIndex) { IDataSet set = mData.getDataSetByIndex(i); - Entry e = set.getEntryForXIndex(xIndex); + Entry e = set.getEntryForXPos(xIndex); if (e != null) { vals.add(e); diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java index d98efd2a30..74fbd2549d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java @@ -180,7 +180,7 @@ protected float[] getMarkerPosition(Entry e, Highlight highlight) { float rotationAngle = getRotationAngle(); - int i = e.getX(); + int i = highlight.getXIndex(); // offset needed to center the drawn text in the slice float offset = mDrawAngles[i] / 2; @@ -311,7 +311,7 @@ public int getDataSetIndexForIndex(int xIndex) { List dataSets = mData.getDataSets(); for (int i = 0; i < dataSets.size(); i++) { - if (dataSets.get(i).getEntryForXIndex(xIndex) != null) + if (dataSets.get(i).getEntryForXPos(xIndex) != null) return i; } diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java index 1a037ff2b6..13d0ac9c1d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java @@ -133,8 +133,9 @@ public void notifyDataSetChanged() { // mYAxis.setValueFormatter(mDefaultFormatter); // } - mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum); - mXAxisRenderer.computeAxis(mData.getXValMaximumLength(), mData.getXVals()); + mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted()); + mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); + //mXAxisRenderer.computeAxis(mData.getXValMaximumLength(), mData.getXVals()); if (mLegend != null && !mLegend.isLegendCustom()) mLegendRenderer.computeLegend(mData); diff --git a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java index 87e701d5dd..f078ed00c7 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java @@ -25,6 +25,26 @@ public abstract class AxisBase extends ComponentBase { private float mAxisLineWidth = 1f; + /** + * the actual array of entries + */ + public float[] mEntries = new float[]{}; + + /** + * the number of entries the legend contains + */ + public int mEntryCount; + + /** + * the number of decimal digits to use + */ + public int mDecimals; + + /** + * the number of label entries the axis should have, default 6 + */ + private int mLabelCount = 6; + /** * flag indicating if the grid lines for this axis should be drawn */ @@ -221,6 +241,31 @@ public boolean isDrawLabelsEnabled() { return mDrawLabels; } + /** + * 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 + */ + public void setLabelCount(int count) { + + if (count > 25) + count = 25; + if (count < 2) + count = 2; + + mLabelCount = count; + } + + /** + * Returns the number of label entries the y-axis should have + * + * @return + */ + public int getLabelCount() { + return mLabelCount; + } + /** * Adds a new LimitLine to this axis. * diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java index 44427c28b1..82c4c10e6d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java @@ -26,26 +26,6 @@ public class YAxis extends AxisBase { */ protected YAxisValueFormatter mYAxisValueFormatter; - /** - * the actual array of entries - */ - public float[] mEntries = new float[]{}; - - /** - * the number of entries the legend contains - */ - public int mEntryCount; - - /** - * the number of decimal digits to use - */ - public int mDecimals; - - /** - * the number of y-label entries the y-labels should have, default 6 - */ - private int mLabelCount = 6; - /** * indicates if the top y-label entry is drawn or not */ @@ -279,24 +259,10 @@ public void setDrawTopYLabelEntry(boolean enabled) { */ public void setLabelCount(int count, boolean force) { - if (count > 25) - count = 25; - if (count < 2) - count = 2; - - mLabelCount = count; + setLabelCount(count); mForceLabels = force; } - /** - * Returns the number of label entries the y-axis should have - * - * @return - */ - public int getLabelCount() { - return mLabelCount; - } - /** * Returns true if focing the y-label count is enabled. Default: false * diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/BaseDataSet.java index e26a4d2edd..e218dec225 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BaseDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/BaseDataSet.java @@ -365,7 +365,7 @@ public boolean removeLast() { @Override public boolean removeEntry(int xIndex) { - T e = getEntryForXIndex(xIndex); + T e = getEntryForXPos(xIndex); return removeEntry(e); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java index 042a7f6425..0fff111552 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java @@ -774,7 +774,7 @@ public boolean removeEntry(int xIndex, int dataSetIndex) { return false; IDataSet dataSet = mDataSets.get(dataSetIndex); - Entry e = dataSet.getEntryForXIndex(xIndex); + Entry e = dataSet.getEntryForXPos(xIndex); if (e == null || e.getX() != xIndex) return false; @@ -799,7 +799,7 @@ public T getDataSetForEntry(Entry e) { T set = mDataSets.get(i); for (int j = 0; j < set.getEntryCount(); j++) { - if (e.equalTo(set.getEntryForXIndex(e.getX()))) + if (e.equalTo(set.getEntryForXPos(e.getX()))) return set; } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java index 46e71d5865..e85c93e0ef 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java @@ -266,17 +266,17 @@ public int getEntryIndex(Entry e) { } @Override - public T getEntryForXIndex(int xIndex, Rounding rounding) { + public T getEntryForXPos(float xPos, Rounding rounding) { - int index = getEntryIndex(xIndex, rounding); + int index = getEntryIndex(xPos, rounding); if (index > -1) return mValues.get(index); return null; } @Override - public T getEntryForXIndex(int xIndex) { - return getEntryForXIndex(xIndex, Rounding.CLOSEST); + public T getEntryForXPos(float xPos) { + return getEntryForXPos(xPos, Rounding.CLOSEST); } @Override @@ -285,7 +285,7 @@ public T getEntryForIndex(int index) { } @Override - public int getEntryIndex(int xIndex, Rounding rounding) { + public int getEntryIndex(float xPos, Rounding rounding) { int low = 0; int high = mValues.size() - 1; @@ -294,14 +294,14 @@ public int getEntryIndex(int xIndex, Rounding rounding) { while (low <= high) { int m = (high + low) / 2; - if (xIndex == mValues.get(m).getX()) { - while (m > 0 && mValues.get(m - 1).getX() == xIndex) + if (xPos == mValues.get(m).getX()) { + while (m > 0 && mValues.get(m - 1).getX() == xPos) m--; return m; } - if (xIndex > mValues.get(m).getX()) + if (xPos > mValues.get(m).getX()) low = m + 1; else high = m - 1; @@ -310,13 +310,13 @@ public int getEntryIndex(int xIndex, Rounding rounding) { } if (closest != -1) { - int closestXIndex = mValues.get(closest).getX(); + float closestXPos = mValues.get(closest).getX(); if (rounding == Rounding.UP) { - if (closestXIndex < xIndex && closest < mValues.size() - 1) { + if (closestXPos < xPos && closest < mValues.size() - 1) { ++closest; } } else if (rounding == Rounding.DOWN) { - if (closestXIndex > xIndex && closest > 0) { + if (closestXPos > xPos && closest > 0) { --closest; } } @@ -328,7 +328,7 @@ public int getEntryIndex(int xIndex, Rounding rounding) { @Override public float getYValForXIndex(int xIndex) { - Entry e = getEntryForXIndex(xIndex); + Entry e = getEntryForXPos(xIndex); if (e != null && e.getX() == xIndex) return e.getY(); @@ -398,7 +398,7 @@ public List getEntriesForXIndex(int xIndex) { /** * Determines how to round DataSet index values for - * {@link DataSet#getEntryIndex(int, Rounding)} DataSet.getEntryIndex()} + * {@link DataSet#getEntryIndex(float, Rounding)} DataSet.getEntryIndex()} * when an exact x-index is not found. */ public enum Rounding { diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java index a90ce4b8a9..49399fa3c6 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java @@ -173,15 +173,15 @@ public void calcMinMax() { } @Override - public S getEntryForXIndex(int xIndex) { + public S getEntryForXPos(float xPos) { //DynamicRealmObject o = new DynamicRealmObject(results.where().equalTo(mIndexField, xIndex).findFirst()); //return new Entry(o.getFloat(mValuesField), o.getInt(mIndexField)); - return getEntryForXIndex(xIndex, DataSet.Rounding.CLOSEST); + return getEntryForXPos(xPos, DataSet.Rounding.CLOSEST); } @Override - public S getEntryForXIndex(int xIndex, DataSet.Rounding rounding) { - int index = getEntryIndex(xIndex, rounding); + public S getEntryForXPos(float xPos, DataSet.Rounding rounding) { + int index = getEntryIndex(xPos, rounding); if (index > -1) return mValues.get(index); return null; @@ -214,7 +214,7 @@ public S getEntryForIndex(int index) { } @Override - public int getEntryIndex(int x, DataSet.Rounding rounding) { + public int getEntryIndex(float xPos, DataSet.Rounding rounding) { int low = 0; int high = mValues.size() - 1; @@ -223,16 +223,14 @@ public int getEntryIndex(int x, DataSet.Rounding rounding) { while (low <= high) { int m = (high + low) / 2; - S entry = mValues.get(m); - - if (x == entry.getX()) { - while (m > 0 && mValues.get(m - 1).getX() == x) + if (xPos == mValues.get(m).getX()) { + while (m > 0 && mValues.get(m - 1).getX() == xPos) m--; return m; } - if (x > entry.getX()) + if (xPos > mValues.get(m).getX()) low = m + 1; else high = m - 1; @@ -241,13 +239,13 @@ public int getEntryIndex(int x, DataSet.Rounding rounding) { } if (closest != -1) { - int closestXIndex = mValues.get(closest).getX(); + float closestXPos = mValues.get(closest).getX(); if (rounding == DataSet.Rounding.UP) { - if (closestXIndex < x && closest < mValues.size() - 1) { + if (closestXPos < xPos && closest < mValues.size() - 1) { ++closest; } } else if (rounding == DataSet.Rounding.DOWN) { - if (closestXIndex > x && closest > 0) { + if (closestXPos > xPos && closest > 0) { --closest; } } @@ -265,7 +263,7 @@ public int getEntryIndex(S e) { public float getYValForXIndex(int xIndex) { //return new DynamicRealmObject(results.where().greaterThanOrEqualTo(mIndexField, xIndex).findFirst()) // .getFloat(mValuesField); - Entry e = getEntryForXIndex(xIndex); + Entry e = getEntryForXPos(xIndex); if (e != null && e.getX() == xIndex) return e.getY(); diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java index 155db6e88e..07e8256c42 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java @@ -120,7 +120,7 @@ protected Highlight getStackedHighlight( int xIndex, double yValue) { - BarEntry entry = set.getEntryForXIndex(xIndex); + BarEntry entry = set.getEntryForXPos(xIndex); if (entry == null) return null; diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index 7d52f3a30f..3c8cf5b119 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -58,29 +58,29 @@ public interface IDataSet { void calcMinMax(); /** - * Returns the first Entry object found at the given xIndex with binary - * search. If the no Entry at the specified x-index is found, this method - * returns the index at the closest x-index. Returns null if no Entry object - * at that index. INFORMATION: This method does calculations at runtime. Do + * 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. + * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xIndex + * @param xPos * @param rounding determine to round up/down/closest if there is no Entry matching the provided x-index * @return */ - T getEntryForXIndex(int xIndex, DataSet.Rounding rounding); + T getEntryForXPos(float xPos, DataSet.Rounding rounding); /** - * Returns the first Entry object found at the given xIndex with binary - * search. If the no Entry at the specified x-index is found, this method - * returns the index at the closest x-index. Returns null if no Entry object - * at that index. INFORMATION: This method does calculations at runtime. Do + * 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. + * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xIndex + * @param xPos * @return */ - T getEntryForXIndex(int xIndex); + T getEntryForXPos(float xPos); /** * Returns all Entry objects found at the given xIndex with binary @@ -102,17 +102,17 @@ public interface IDataSet { T getEntryForIndex(int index); /** - * Returns the first Entry index found at the given xIndex with binary - * search. If the no Entry at the specified x-index is found, this method - * returns the index at the closest x-index. Returns -1 if no Entry object - * at that index. INFORMATION: This method does calculations at runtime. Do + * 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. + * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xIndex + * @param xPos * @param rounding determine to round up/down/closest if there is no Entry matching the provided x-index * @return */ - int getEntryIndex(int xIndex, DataSet.Rounding rounding); + int getEntryIndex(float xPos, DataSet.Rounding rounding); /** * Returns the position of the provided entry in the DataSets Entry array. diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/AxisRenderer.java index 6dac9de21c..48da4be2c3 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/AxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/AxisRenderer.java @@ -6,6 +6,7 @@ import android.graphics.Paint; import android.graphics.Paint.Style; +import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -90,6 +91,42 @@ public Transformer getTransformer() { return mTrans; } + /** + * Computes the axis values. + * + * @param min - the minimum value in the data object for this axis + * @param max - the maximum value in the data object for this axis + */ + public void computeAxis(float min, float max, boolean inverted) { + + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { + + PointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); + PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); + + if (!inverted) { + + min = (float) p2.y; + max = (float) p1.y; + } else { + + min = (float) p1.y; + max = (float) p2.y; + } + } + + computeAxisValues(min, max); + } + + /** + * Sets up the axis values. Computes the desired number of labels between the two given extremes. + * + * @return + */ + protected abstract void computeAxisValues(float min, float max); + /** * Draws the axis labels to the screen. * diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java index 0e66797572..9e522272fd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -360,7 +360,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (index >= 0 && index < (mChart.getXChartMax() * mAnimator.getPhaseX()) / setCount) { - BarEntry e = set.getEntryForXIndex(index); + BarEntry e = set.getEntryForXPos(index); if (e == null || e.getX() != index) continue; diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index 412d8f3e05..384e1e5825 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -8,6 +8,8 @@ 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.data.DataSet; +import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; @@ -74,8 +76,11 @@ protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); - BubbleEntry entryFrom = dataSet.getEntryForXIndex(mMinX); - BubbleEntry entryTo = dataSet.getEntryForXIndex(mMaxX); + float low = mChart.getLowestVisibleX(); + float high = mChart.getHighestVisibleX(); + + BubbleEntry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); + BubbleEntry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); int minx = Math.max(dataSet.getEntryIndex(entryFrom), 0); int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount()); @@ -112,7 +117,7 @@ protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) break; - final int color = dataSet.getColor(entry.getX()); + final int color = dataSet.getColor((int) entry.getX()); mRenderPaint.setColor(color); c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mRenderPaint); @@ -148,8 +153,11 @@ public void drawValues(Canvas c) { final float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); final float phaseY = mAnimator.getPhaseY(); - BubbleEntry entryFrom = dataSet.getEntryForXIndex(mMinX); - BubbleEntry entryTo = dataSet.getEntryForXIndex(mMaxX); + float low = mChart.getLowestVisibleX(); + float high = mChart.getHighestVisibleX(); + + BubbleEntry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); + BubbleEntry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); int minx = dataSet.getEntryIndex(entryFrom); int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount()); @@ -220,8 +228,11 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (entry == null || entry.getX() != high.getXIndex()) continue; - BubbleEntry entryFrom = dataSet.getEntryForXIndex(mMinX); - BubbleEntry entryTo = dataSet.getEntryForXIndex(mMaxX); + float low = mChart.getLowestVisibleX(); + float highX = mChart.getHighestVisibleX(); + + BubbleEntry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); + BubbleEntry entryTo = dataSet.getEntryForXPos(highX, DataSet.Rounding.UP); int minx = dataSet.getEntryIndex(entryFrom); int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount()); @@ -263,7 +274,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (high.getXIndex() < minx || high.getXIndex() >= maxx) continue; - final int originalColor = dataSet.getColor(entry.getX()); + final int originalColor = dataSet.getColor((int) entry.getX()); Color.RGBToHSV(Color.red(originalColor), Color.green(originalColor), Color.blue(originalColor), _hsvBuffer); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index 7fc01fd518..ccef5a608d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -60,8 +60,8 @@ protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { float barSpace = dataSet.getBarSpace(); boolean showCandleBar = dataSet.getShowCandleBar(); - int minx = Math.max(mMinX, 0); - int maxx = Math.min(mMaxX + 1, dataSet.getEntryCount()); + int minx = Math.max((int) mChart.getLowestVisibleX(), 0); + int maxx = Math.min((int) mChart.getHighestVisibleX(), dataSet.getEntryCount()); mRenderPaint.setStrokeWidth(dataSet.getShadowWidth()); @@ -74,9 +74,9 @@ protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { // get the entry CandleEntry e = dataSet.getEntryForIndex(j); - final int xIndex = e.getX(); + final float xPos = e.getX(); - if (xIndex < minx || xIndex >= maxx) + if (xPos < minx || xPos >= maxx) continue; final float open = e.getOpen(); @@ -87,10 +87,10 @@ protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { if (showCandleBar) { // calculate the shadow - mShadowBuffers[0] = xIndex; - mShadowBuffers[2] = xIndex; - mShadowBuffers[4] = xIndex; - mShadowBuffers[6] = xIndex; + mShadowBuffers[0] = xPos; + mShadowBuffers[2] = xPos; + mShadowBuffers[4] = xPos; + mShadowBuffers[6] = xPos; if (open > close) { mShadowBuffers[1] = high * phaseY; @@ -150,9 +150,9 @@ else if (open < close) // calculate the body - mBodyBuffers[0] = xIndex - 0.5f + barSpace; + mBodyBuffers[0] = xPos - 0.5f + barSpace; mBodyBuffers[1] = close * phaseY; - mBodyBuffers[2] = (xIndex + 0.5f - barSpace); + mBodyBuffers[2] = (xPos + 0.5f - barSpace); mBodyBuffers[3] = open * phaseY; trans.pointValuesToPixel(mBodyBuffers); @@ -202,19 +202,19 @@ else if (open < close) } } else { - mRangeBuffers[0] = xIndex; + mRangeBuffers[0] = xPos; mRangeBuffers[1] = high * phaseY; - mRangeBuffers[2] = xIndex; + mRangeBuffers[2] = xPos; mRangeBuffers[3] = low * phaseY; - mOpenBuffers[0] = xIndex - 0.5f + barSpace; + mOpenBuffers[0] = xPos - 0.5f + barSpace; mOpenBuffers[1] = open * phaseY; - mOpenBuffers[2] = xIndex; + mOpenBuffers[2] = xPos; mOpenBuffers[3] = open * phaseY; - mCloseBuffers[0] = xIndex + 0.5f - barSpace; + mCloseBuffers[0] = xPos + 0.5f - barSpace; mCloseBuffers[1] = close * phaseY; - mCloseBuffers[2] = xIndex; + mCloseBuffers[2] = xPos; mCloseBuffers[3] = close * phaseY; trans.pointValuesToPixel(mRangeBuffers); @@ -276,8 +276,8 @@ public void drawValues(Canvas c) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - int minx = Math.max(mMinX, 0); - int maxx = Math.min(mMaxX + 1, dataSet.getEntryCount()); + int minx = Math.max((int) mChart.getLowestVisibleX(), 0); + int maxx = Math.min((int) mChart.getHighestVisibleX() + 1, dataSet.getEntryCount()); float[] positions = trans.generateTransformedValuesCandle( dataSet, mAnimator.getPhaseX(), mAnimator.getPhaseY(), minx, maxx); @@ -334,7 +334,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - CandleEntry e = set.getEntryForXIndex(xIndex); + CandleEntry e = set.getEntryForXPos(xIndex); if (e == null || e.getX() != xIndex) continue; diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index 9dccd96d9b..da1dfba60a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -137,8 +137,11 @@ protected void drawHorizontalBezier(Canvas c, ILineDataSet dataSet) { int entryCount = dataSet.getEntryCount(); - Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); + float low = mChart.getLowestVisibleX(); + float high = mChart.getHighestVisibleX(); + + Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); + Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); int diff = (entryFrom == entryTo) ? 1 : 0; int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); @@ -164,8 +167,8 @@ protected void drawHorizontalBezier(Canvas c, ILineDataSet dataSet) { prev = dataSet.getEntryForIndex(j - 1); cur = dataSet.getEntryForIndex(j); - final float cpx = (float)(prev.getX()) - + (float)(cur.getX() - prev.getX()) / 2.0f; + final float cpx = (prev.getX()) + + (cur.getX() - prev.getX()) / 2.0f; cubicPath.cubicTo( cpx, prev.getY() * phaseY, @@ -201,8 +204,11 @@ protected void drawCubicBezier(Canvas c, ILineDataSet dataSet) { int entryCount = dataSet.getEntryCount(); - Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); + float low = mChart.getLowestVisibleX(); + float high = mChart.getHighestVisibleX(); + + Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); + Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); int diff = (entryFrom == entryTo) ? 1 : 0; int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff - 1, 0); @@ -336,8 +342,11 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { canvas = c; } - Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); + float low = mChart.getLowestVisibleX(); + float high = mChart.getHighestVisibleX(); + + Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); + Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); int diff = (entryFrom == entryTo) ? 1 : 0; int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); @@ -558,9 +567,11 @@ public void drawValues(Canvas c) { int entryCount = dataSet.getEntryCount(); - Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, - DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); + float low = mChart.getLowestVisibleX(); + float high = mChart.getHighestVisibleX(); + + Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); + Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); int diff = (entryFrom == entryTo) ? 1 : 0; if (dataSet.getMode() == LineDataSet.Mode.CUBIC_BEZIER) @@ -624,9 +635,11 @@ protected void drawCircles(Canvas c) { int entryCount = dataSet.getEntryCount(); - Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, - DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); + float low = mChart.getLowestVisibleX(); + float high = mChart.getHighestVisibleX(); + + Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); + Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); int diff = (entryFrom == entryTo) ? 1 : 0; if (dataSet.getMode() == LineDataSet.Mode.CUBIC_BEZIER) diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java index 850401978c..840e0a7007 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -215,7 +215,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { // draw only if the value is greater than zero if ((Math.abs(e.getY()) > 0.000001)) { - if (!mChart.needsHighlight(e.getX(), + if (!mChart.needsHighlight((int) e.getX(), mChart.getData().getIndexOfDataSet(dataSet))) { final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java index a9f7337db9..b0717a2322 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java @@ -272,7 +272,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { // get the index to highlight int xIndex = indices[i].getXIndex(); - Entry e = set.getEntryForXIndex(xIndex); + Entry e = set.getEntryForXPos(xIndex); if (e == null || e.getX() != xIndex) continue; diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java index e297c4d5ab..bd2c64f7ba 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -33,20 +33,44 @@ public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer t mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); } - public void computeAxis(float xValMaximumLength, List xValues) { + @Override + protected void computeAxisValues(float min, float max) { - mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + int labelCount = mXAxis.getLabelCount(); + float range = Math.abs(max - min); + + float interval = range / (labelCount - 1); - StringBuilder widthText = new StringBuilder(); + if (mXAxis.mEntries == null || mXAxis.mEntries.length != labelCount) { + mXAxis.mEntries = new float[labelCount]; + } + + mXAxis.mEntries[0] = 0f; - int xValChars = Math.round(xValMaximumLength); + for (int i = 1; i < labelCount; i++) { + mXAxis.mEntries[i] = interval * (float) i; + } - for (int i = 0; i < xValChars; i++) { - widthText.append('h'); + // set decimals + if (interval < 1) { + mXAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); + } else { + mXAxis.mDecimals = 0; } - final FSize labelSize = Utils.calcTextSize(mAxisLabelPaint, widthText.toString()); + if (mXAxis.getValues() != null && !mXAxis.getValues().isEmpty()) { + computeAxis(); + } + } + + protected void computeAxis() { + + String longest = mXAxis.getLongestLabel(); + + mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + + final FSize labelSize = Utils.calcTextSize(mAxisLabelPaint, longest); final float labelWidth = labelSize.width; final float labelHeight = Utils.calcTextHeight(mAxisLabelPaint, "Q"); @@ -69,8 +93,6 @@ public void computeAxis(float xValMaximumLength, List xValues) { mXAxis.mLabelHeight = Math.round(labelHeight); mXAxis.mLabelRotatedWidth = Math.round(labelRotatedSize.width + spaceSize.width); mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height); - - mXAxis.setValues(xValues); } @Override @@ -149,22 +171,22 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); - // pre allocate to save performance (dont allocate in loop) - float[] position = new float[] { - 0f, 0f - }; + float[] positions = new float[mXAxis.mEntryCount * 2]; - for (int i = 0; i < mXAxis.getValues().size(); i++) { + for (int i = 0; i < positions.length; i += 2) { + // only fill x values + positions[i] = mXAxis.mEntries[i / 2]; + } - XAxisValue xVal = mXAxis.getValues().get(i); + mTrans.pointValuesToPixel(positions); - position[0] = (float) xVal.getPosition(); + for (int i = 0; i < positions.length; i += 2) { - mTrans.pointValuesToPixel(position); + float x = positions[i]; - if (mViewPortHandler.isInBoundsX(position[0])) { + if (mViewPortHandler.isInBoundsX(x)) { - String label = xVal.getLabel(); + String label = String.valueOf(mXAxis.mEntries[i / 2]); if (mXAxis.isAvoidFirstLastClippingEnabled()) { @@ -173,18 +195,18 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { float width = Utils.calcTextWidth(mAxisLabelPaint, label); if (width > mViewPortHandler.offsetRight() * 2 - && position[0] + width > mViewPortHandler.getChartWidth()) - position[0] -= width / 2; + && x + width > mViewPortHandler.getChartWidth()) + x -= width / 2; // avoid clipping of the first } else if (i == 0) { float width = Utils.calcTextWidth(mAxisLabelPaint, label); - position[0] += width / 2; + x += width / 2; } } - drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees); + drawLabel(c, label, i, x, pos, anchor, labelRotationAngleDegrees); } } } @@ -200,10 +222,14 @@ public void renderGridLines(Canvas c) { if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) return; - // pre alloc - float[] position = new float[] { - 0f, 0f - }; + float[] positions = new float[mXAxis.mEntryCount * 2]; + + for (int i = 0; i < positions.length; i += 2) { + // only fill x values + positions[i] = mXAxis.mEntries[i / 2]; + } + + mTrans.pointValuesToPixel(positions); mGridPaint.setColor(mXAxis.getGridColor()); mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); @@ -211,16 +237,15 @@ public void renderGridLines(Canvas c) { Path gridLinePath = new Path(); - for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { + for (int i = 0; i < positions.length; i += 2) { - position[0] = i; - mTrans.pointValuesToPixel(position); + float x = positions[i]; - if (position[0] >= mViewPortHandler.offsetLeft() - && position[0] <= mViewPortHandler.getChartWidth()) { + if (x >= mViewPortHandler.offsetLeft() + && x <= mViewPortHandler.getChartWidth()) { - gridLinePath.moveTo(position[0], mViewPortHandler.contentBottom()); - gridLinePath.lineTo(position[0], mViewPortHandler.contentTop()); + gridLinePath.moveTo(x, mViewPortHandler.contentBottom()); + gridLinePath.lineTo(x, mViewPortHandler.contentTop()); // draw a path because lines don't support dashing on lower android versions c.drawPath(gridLinePath, mGridPaint); @@ -230,26 +255,26 @@ public void renderGridLines(Canvas c) { } } - /** - * Draws the LimitLines associated with this axis to the screen. - * - * @param c - */ - @Override - public void renderLimitLines(Canvas c) { + /** + * Draws the LimitLines associated with this axis to the screen. + * + * @param c + */ + @Override + public void renderLimitLines(Canvas c) { - List limitLines = mXAxis.getLimitLines(); + List limitLines = mXAxis.getLimitLines(); - if (limitLines == null || limitLines.size() <= 0) - return; + if (limitLines == null || limitLines.size() <= 0) + return; float[] position = new float[2]; - for (int i = 0; i < limitLines.size(); i++) { + for (int i = 0; i < limitLines.size(); i++) { - LimitLine l = limitLines.get(i); + LimitLine l = limitLines.get(i); - if(!l.isEnabled()) + if (!l.isEnabled()) continue; position[0] = l.getLimit(); @@ -259,14 +284,13 @@ public void renderLimitLines(Canvas c) { renderLimitLineLine(c, l, position); renderLimitLineLabel(c, l, position, 2.f + l.getYOffset()); - } - } + } + } float[] mLimitLineSegmentsBuffer = new float[4]; private Path mLimitLinePath = new Path(); - public void renderLimitLineLine(Canvas c, LimitLine limitLine, float[] position) - { + public void renderLimitLineLine(Canvas c, LimitLine limitLine, float[] position) { mLimitLineSegmentsBuffer[0] = position[0]; mLimitLineSegmentsBuffer[1] = mViewPortHandler.contentTop(); mLimitLineSegmentsBuffer[2] = position[0]; @@ -284,8 +308,7 @@ public void renderLimitLineLine(Canvas c, LimitLine limitLine, float[] position) c.drawPath(mLimitLinePath, mLimitLinePaint); } - public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position, float yOffset) - { + public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position, float yOffset) { String label = limitLine.getLabel(); // if drawing the limit-value label is enabled @@ -305,7 +328,8 @@ public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); mLimitLinePaint.setTextAlign(Align.LEFT); - c.drawText(label, position[0] + xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, mLimitLinePaint); + c.drawText(label, position[0] + xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, + mLimitLinePaint); } else if (labelPosition == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { mLimitLinePaint.setTextAlign(Align.LEFT); @@ -314,7 +338,8 @@ public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position mLimitLinePaint.setTextAlign(Align.RIGHT); final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); - c.drawText(label, position[0] - xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, mLimitLinePaint); + c.drawText(label, position[0] - xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, + mLimitLinePaint); } else { mLimitLinePaint.setTextAlign(Align.RIGHT); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java index d58c3842b5..8398301463 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java @@ -40,45 +40,45 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { BarData bd = mChart.getData(); int step = bd.getDataSetCount(); - for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { - - position[0] = i * step + i * bd.getGroupSpace() - + bd.getGroupSpace() / 2f; - - // consider groups (center label for each group) - if (step > 1) { - position[0] += ((float) step - 1f) / 2f; - } - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsX(position[0]) && i >= 0 - && i < mXAxis.getValues().size()) { - - String label = mXAxis.getValues().get(i).getLabel(); - - if (mXAxis.isAvoidFirstLastClippingEnabled()) { - - // avoid clipping of the last - if (i == mXAxis.getValues().size() - 1) { - float width = Utils.calcTextWidth(mAxisLabelPaint, label); - - if (position[0] + width / 2.f > mViewPortHandler.contentRight()) - position[0] = mViewPortHandler.contentRight() - (width / 2.f); - - // avoid clipping of the first - } else if (i == 0) { - - float width = Utils.calcTextWidth(mAxisLabelPaint, label); - - if (position[0] - width / 2.f < mViewPortHandler.contentLeft()) - position[0] = mViewPortHandler.contentLeft() + (width / 2.f); - } - } - - drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees); - } - } +// for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { +// +// position[0] = i * step + i * bd.getGroupSpace() +// + bd.getGroupSpace() / 2f; +// +// // consider groups (center label for each group) +// if (step > 1) { +// position[0] += ((float) step - 1f) / 2f; +// } +// +// mTrans.pointValuesToPixel(position); +// +// if (mViewPortHandler.isInBoundsX(position[0]) && i >= 0 +// && i < mXAxis.getValues().size()) { +// +// String label = mXAxis.getValues().get(i).getLabel(); +// +// if (mXAxis.isAvoidFirstLastClippingEnabled()) { +// +// // avoid clipping of the last +// if (i == mXAxis.getValues().size() - 1) { +// float width = Utils.calcTextWidth(mAxisLabelPaint, label); +// +// if (position[0] + width / 2.f > mViewPortHandler.contentRight()) +// position[0] = mViewPortHandler.contentRight() - (width / 2.f); +// +// // avoid clipping of the first +// } else if (i == 0) { +// +// float width = Utils.calcTextWidth(mAxisLabelPaint, label); +// +// if (position[0] - width / 2.f < mViewPortHandler.contentLeft()) +// position[0] = mViewPortHandler.contentLeft() + (width / 2.f); +// } +// } +// +// drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees); +// } +// } } @Override @@ -97,17 +97,17 @@ public void renderGridLines(Canvas c) { BarData bd = mChart.getData(); int step = bd.getDataSetCount(); - for (int i = mMinX; i < mMaxX; i += mXAxis.mAxisLabelModulus) { - - position[0] = i * step + i * bd.getGroupSpace() - 0.5f; - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsX(position[0])) { - - c.drawLine(position[0], mViewPortHandler.offsetTop(), position[0], - mViewPortHandler.contentBottom(), mGridPaint); - } - } +// for (int i = mMinX; i < mMaxX; i += mXAxis.mAxisLabelModulus) { +// +// position[0] = i * step + i * bd.getGroupSpace() - 0.5f; +// +// mTrans.pointValuesToPixel(position); +// +// if (mViewPortHandler.isInBoundsX(position[0])) { +// +// c.drawLine(position[0], mViewPortHandler.offsetTop(), position[0], +// mViewPortHandler.contentBottom(), mGridPaint); +// } +// } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java index 01b17f3904..ed45549050 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java @@ -28,11 +28,10 @@ public XAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, XAxis xA } @Override - public void computeAxis(float xValAverageLength, List xValues) { + protected void computeAxis() { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - mXAxis.setValues(xValues); String longest = mXAxis.getLongestLabel(); @@ -111,24 +110,24 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { BarData bd = mChart.getData(); int step = bd.getDataSetCount(); - for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { - - position[1] = i * step + i * bd.getGroupSpace() - + bd.getGroupSpace() / 2f; - - // consider groups (center label for each group) - if (step > 1) { - position[1] += ((float) step - 1f) / 2f; - } - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsY(position[1])) { - - String label = mXAxis.getValues().get(i).getLabel(); - drawLabel(c, label, i, pos, position[1], anchor, labelRotationAngleDegrees); - } - } +// for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { +// +// position[1] = i * step + i * bd.getGroupSpace() +// + bd.getGroupSpace() / 2f; +// +// // consider groups (center label for each group) +// if (step > 1) { +// position[1] += ((float) step - 1f) / 2f; +// } +// +// mTrans.pointValuesToPixel(position); +// +// if (mViewPortHandler.isInBoundsY(position[1])) { +// +// String label = mXAxis.getValues().get(i).getLabel(); +// drawLabel(c, label, i, pos, position[1], anchor, labelRotationAngleDegrees); +// } +// } } @Override @@ -148,18 +147,18 @@ public void renderGridLines(Canvas c) { // take into consideration that multiple DataSets increase mDeltaX int step = bd.getDataSetCount(); - for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { - - position[1] = i * step + i * bd.getGroupSpace() - 0.5f; - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsY(position[1])) { - - c.drawLine(mViewPortHandler.contentLeft(), position[1], - mViewPortHandler.contentRight(), position[1], mGridPaint); - } - } +// for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { +// +// position[1] = i * step + i * bd.getGroupSpace() - 0.5f; +// +// mTrans.pointValuesToPixel(position); +// +// if (mViewPortHandler.isInBoundsY(position[1])) { +// +// c.drawLine(mViewPortHandler.contentLeft(), position[1], +// mViewPortHandler.contentRight(), position[1], mGridPaint); +// } +// } } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java index fdae353e4b..94b7673205 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -37,40 +37,7 @@ public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer t mZeroLinePaint.setStyle(Paint.Style.STROKE); } - /** - * Computes the axis values. - * - * @param yMin - the minimum y-value in the data object for this axis - * @param yMax - the maximum y-value in the data object for this axis - */ - public void computeAxis(float yMin, float yMax) { - - // calculate the starting and entry point of the y-labels (depending on - // zoom / contentrect bounds) - if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { - - PointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); - PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); - - if (!mYAxis.isInverted()) { - yMin = (float) p2.y; - yMax = (float) p1.y; - } else { - - yMin = (float) p1.y; - yMax = (float) p2.y; - } - } - - computeAxisValues(yMin, yMax); - } - - /** - * Sets up the y-axis labels. Computes the desired number of labels between the two given extremes. Unlike the - * papareXLabels() method, this method needs to be called upon every refresh of the view. - * - * @return - */ + @Override protected void computeAxisValues(float min, float max) { float yMin = min; diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java index bcd63778a0..4ce4879e3b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java @@ -33,7 +33,8 @@ public YAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, YAxis yA * @param yMin - the minimum y-value in the data object for this axis * @param yMax - the maximum y-value in the data object for this axis */ - public void computeAxis(float yMin, float yMax) { + @Override + public void computeAxis(float yMin, float yMax, boolean inverted) { // calculate the starting and entry point of the y-labels (depending on // zoom / contentrect bounds) @@ -44,7 +45,7 @@ public void computeAxis(float yMin, float yMax) { PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), mViewPortHandler.contentTop()); - if (!mYAxis.isInverted()) { + if (!inverted) { yMin = (float) p1.x; yMax = (float) p2.x; } else { diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java index 66ddd3ceea..68415ba67f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -23,7 +23,7 @@ public YAxisRendererRadarChart(ViewPortHandler viewPortHandler, YAxis yAxis, Rad } @Override - public void computeAxis(float yMin, float yMax) { + public void computeAxis(float yMin, float yMax, boolean inverted) { computeAxisValues(yMin, yMax); } diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/EntryXIndexComparator.java b/MPChartLib/src/com/github/mikephil/charting/utils/EntryXIndexComparator.java index e8d815e717..cf62492390 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/EntryXIndexComparator.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/EntryXIndexComparator.java @@ -11,6 +11,12 @@ public class EntryXIndexComparator implements Comparator { @Override public int compare(Entry entry1, Entry entry2) { - return entry1.getX() - entry2.getX(); + float diff = entry1.getX() - entry2.getX(); + + if (diff == 0f) return 0; + else { + if (diff > 0f) return 1; + else return -1; + } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java b/MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java index 56f54d3175..6632c23d60 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java @@ -191,7 +191,7 @@ public static List loadEntriesFromAssets(AssetManager am, String path) { /** * Saves an Array of Entries to the specified location on the sdcard * - * @param ds + * @param entries * @param path */ public static void saveToSdCard(List entries, String path) { @@ -242,7 +242,7 @@ public static List loadBarEntriesFromAssets(AssetManager am, String pa // process line String[] split = line.split("#"); - entries.add(new BarEntry(Float.parseFloat(split[0]), Integer.parseInt(split[1]))); + entries.add(new BarEntry(Float.parseFloat(split[1]), Integer.parseInt(split[0]))); line = reader.readLine(); } diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java b/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java index cb0e083880..15f71cc3a8 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java @@ -232,7 +232,7 @@ public float[] generateTransformedValuesBarChart(IBarDataSet data, for (int j = 0; j < valuePoints.length; j += 2) { Entry e = data.getEntryForIndex(j / 2); - int i = e.getX(); + float i = e.getX(); // calculate the x-position, depending on datasetcount float x = e.getX() + i * (setCount - 1) + dataSetIndex + space * i @@ -267,7 +267,7 @@ public float[] generateTransformedValuesHorizontalBarChart(IBarDataSet data, for (int j = 0; j < valuePoints.length; j += 2) { Entry e = data.getEntryForIndex(j / 2); - int i = e.getX(); + float i = e.getX(); // calculate the x-position, depending on datasetcount float x = i + i * (setCount - 1) + dataSet + space * i From ec6752567b52bd558cdf31b1a051481868a25260 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 30 May 2016 14:52:17 +0200 Subject: [PATCH 128/606] Work on barcharts with new x-value --- .../mpchartexample/RadarChartActivitry.java | 10 +++-- .../mikephil/charting/buffer/BarBuffer.java | 41 ++++++++++++------ .../mikephil/charting/charts/BarChart.java | 22 +++++----- .../charting/charts/BarLineChartBase.java | 2 +- .../mikephil/charting/charts/RadarChart.java | 2 +- .../mikephil/charting/components/XAxis.java | 8 ++-- .../charting/renderer/BarChartRenderer.java | 2 + .../charting/renderer/RadarChartRenderer.java | 2 +- .../charting/renderer/XAxisRenderer.java | 42 ++++++++++++++----- .../XAxisRendererHorizontalBarChart.java | 2 +- .../renderer/YAxisRendererRadarChart.java | 5 --- 11 files changed, 87 insertions(+), 51 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index 6402e0e60d..e843e09f08 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -170,7 +170,8 @@ 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.EasingOption + .EaseInCubic); break; } } @@ -194,11 +195,13 @@ public void setData() { // xIndex (even if from different DataSets), since no values can be // drawn above each other. for (int i = 0; i < cnt; i++) { - yVals1.add(new Entry((float) (Math.random() * mult) + mult / 2, i)); + float val = (float) (Math.random() * mult) + mult / 2; + yVals1.add(new Entry(i, val)); } for (int i = 0; i < cnt; i++) { - yVals2.add(new Entry((float) (Math.random() * mult) + mult / 2, i)); + float val = (float) (Math.random() * mult) + mult / 2; + yVals2.add(new Entry(i, val)); } ArrayList xVals = new ArrayList(); @@ -228,7 +231,6 @@ public void setData() { data.setDrawValues(false); mChart.setData(data); - mChart.invalidate(); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java b/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java index 95e1336d9a..3b001c466d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java +++ b/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java @@ -13,6 +13,9 @@ public class BarBuffer extends AbstractBuffer { protected boolean mContainsStacks = false; protected boolean mInverted = false; + /** interval on the x-axis per group */ + protected float mInterval = 0f; + public BarBuffer(int size, float groupspace, int dataSetCount, boolean containsStacks) { super(size); this.mGroupSpace = groupspace; @@ -20,6 +23,10 @@ public BarBuffer(int size, float groupspace, int dataSetCount, boolean containsS this.mContainsStacks = containsStacks; } + public void setInterval(float interval) { + this.mInterval = interval; + } + public void setBarSpace(float barspace) { this.mBarSpace = barspace; } @@ -27,7 +34,7 @@ public void setBarSpace(float barspace) { public void setDataSet(int index) { this.mDataSetIndex = index; } - + public void setInverted(boolean inverted) { this.mInverted = inverted; } @@ -45,25 +52,33 @@ public void feed(IBarDataSet data) { float size = data.getEntryCount() * phaseX; - int dataSetOffset = (mDataSetCount - 1); - float barSpaceHalf = mBarSpace / 2f; - float groupSpaceHalf = mGroupSpace / 2f; - float barWidth = 0.5f; + float barWidth = mInterval / mDataSetCount; + + float groupSpaceWidth = mDataSetCount <= 1 ? 0 : barWidth * mGroupSpace; + float newInterval = (mInterval - groupSpaceWidth); + float newBarWidth = newInterval / mDataSetCount; + + float barSpaceWidth = newBarWidth * mBarSpace; + float barSpaceWidthHalf = barSpaceWidth / 2f; + + float groupSpaceWidthHalf = groupSpaceWidth / 2f; + float dataSetSpace = mDataSetCount <= 1 ? 0 : (newInterval / mDataSetCount) * mDataSetIndex; + for (int i = 0; i < size; i++) { BarEntry e = data.getEntryForIndex(i); - // calculate the x-position, depending on datasetcount - float x = e.getX() + e.getX() * dataSetOffset + mDataSetIndex - + mGroupSpace * e.getX() + groupSpaceHalf; + // calculate the x-position, depending on interval + float x = mInterval * i + dataSetSpace; + float y = e.getY(); float [] vals = e.getYVals(); - + if (!mContainsStacks || vals == null) { - float left = x - barWidth + barSpaceHalf; - float right = x + barWidth - barSpaceHalf; + float left = x + groupSpaceWidthHalf + barSpaceWidthHalf; + float right = left + newBarWidth - barSpaceWidth; float bottom, top; if (mInverted) { bottom = y >= 0 ? y : 0; @@ -102,8 +117,8 @@ public void feed(IBarDataSet data) { negY += Math.abs(value); } - float left = x - barWidth + barSpaceHalf; - float right = x + barWidth - barSpaceHalf; + float left = x + groupSpaceWidthHalf + barSpaceWidthHalf; + float right = left + newBarWidth - barSpaceWidth; float bottom, top; if (mInverted) { bottom = y >= yStart ? y : yStart; diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java index 4cef155a59..9eed5a3f8d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java @@ -56,22 +56,22 @@ protected void init() { setHighlighter(new BarHighlighter(this)); - mXAxis.mAxisMinimum = -0.5f; + //mXAxis.mAxisMinimum = -0.5f; } @Override protected void calcMinMax() { super.calcMinMax(); - - // increase deltax by 1 because the bars have a width of 1 - mXAxis.mAxisRange += 0.5f; - - // extend xDelta to make space for multiple datasets (if ther are one) - mXAxis.mAxisRange *= mData.getDataSetCount(); - - float groupSpace = mData.getGroupSpace(); - mXAxis.mAxisRange += mData.getXValCount() * groupSpace; - mXAxis.mAxisMaximum = mXAxis.mAxisRange - mXAxis.mAxisMinimum; +// +// // increase deltax by 1 because the bars have a width of 1 +// mXAxis.mAxisRange += 0.5f; +// +// // extend xDelta to make space for multiple datasets (if ther are one) +// mXAxis.mAxisRange *= mData.getDataSetCount(); +// +// float groupSpace = mData.getGroupSpace(); +// mXAxis.mAxisRange += mData.getXValCount() * groupSpace; +// mXAxis.mAxisMaximum = mXAxis.mAxisRange - mXAxis.mAxisMinimum; } /** diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java index c5df174efe..64a22aa107 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -347,7 +347,7 @@ public void notifyDataSetChanged() { mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum, mAxisLeft.isInverted()); mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum, mAxisRight.isInverted()); - //mXAxisRenderer.computeAxis(mData.getXValMaximumLength(), mData.getXVals()); + //mXAxisRenderer.computeSize(mData.getXValMaximumLength(), mData.getXVals()); if (mLegend != null) mLegendRenderer.computeLegend(mData); diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java index 13d0ac9c1d..e80066d6b6 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java @@ -135,7 +135,7 @@ public void notifyDataSetChanged() { mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted()); mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); - //mXAxisRenderer.computeAxis(mData.getXValMaximumLength(), mData.getXVals()); + //mXAxisRenderer.computeSize(mData.getXValMaximumLength(), mData.getXVals()); if (mLegend != null && !mLegend.isLegendCustom()) mLegendRenderer.computeLegend(mData); diff --git a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java index 5089f1cbad..d49605c3b8 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java @@ -23,25 +23,25 @@ public class XAxis extends AxisBase { /** * width of the x-axis labels in pixels - this is automatically - * calculated by the computeAxis() methods in the renderers + * calculated by the computeSize() methods in the renderers */ public int mLabelWidth = 1; /** * height of the x-axis labels in pixels - this is automatically - * calculated by the computeAxis() methods in the renderers + * calculated by the computeSize() methods in the renderers */ public int mLabelHeight = 1; /** * width of the (rotated) x-axis labels in pixels - this is automatically - * calculated by the computeAxis() methods in the renderers + * calculated by the computeSize() methods in the renderers */ public int mLabelRotatedWidth = 1; /** * height of the (rotated) x-axis labels in pixels - this is automatically - * calculated by the computeAxis() methods in the renderers + * calculated by the computeSize() methods in the renderers */ public int mLabelRotatedHeight = 1; diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java index 9e522272fd..51bb35a7b7 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -99,6 +99,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { buffer.setBarSpace(dataSet.getBarSpace()); buffer.setDataSet(index); buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); + buffer.setInterval(mChart.getXChartMax() / dataSet.getEntryCount()); buffer.feed(dataSet); @@ -192,6 +193,7 @@ protected void prepareBarHighlight(float x, float y1, float y2, float barspaceHa @Override public void drawValues(Canvas c) { + // if values are drawn if (passesCheck()) { diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java index b0717a2322..206a85add2 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java @@ -233,7 +233,7 @@ protected void drawWeb(Canvas c) { mWebPaint.setColor(mChart.getWebColorInner()); mWebPaint.setAlpha(mChart.getWebAlpha()); - int labelCount = mChart.getYAxis().mEntryCount; + int labelCount = mChart.getYAxis().mEntries.length; for (int j = 0; j < labelCount; j++) { diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java index bd2c64f7ba..3f28571f10 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -11,8 +11,8 @@ import com.github.mikephil.charting.components.LimitLine; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -33,6 +33,30 @@ public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer t mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); } + @Override + public void computeAxis(float min, float max, boolean inverted) { + + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { + + PointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); + PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), mViewPortHandler.contentTop()); + + if (inverted) { + + min = (float) p2.x; + max = (float) p1.x; + } else { + + min = (float) p1.x; + max = (float) p2.x; + } + } + + computeAxisValues(min, max); + } + @Override protected void computeAxisValues(float min, float max) { @@ -43,12 +67,13 @@ protected void computeAxisValues(float min, float max) { if (mXAxis.mEntries == null || mXAxis.mEntries.length != labelCount) { mXAxis.mEntries = new float[labelCount]; + mXAxis.mEntryCount = labelCount; } - mXAxis.mEntries[0] = 0f; + mXAxis.mEntries[0] = min; for (int i = 1; i < labelCount; i++) { - mXAxis.mEntries[i] = interval * (float) i; + mXAxis.mEntries[i] = min + interval * (float) i; } // set decimals @@ -58,12 +83,10 @@ protected void computeAxisValues(float min, float max) { mXAxis.mDecimals = 0; } - if (mXAxis.getValues() != null && !mXAxis.getValues().isEmpty()) { - computeAxis(); - } + computeSize(); } - protected void computeAxis() { + protected void computeSize() { String longest = mXAxis.getLongestLabel(); @@ -171,7 +194,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); - float[] positions = new float[mXAxis.mEntryCount * 2]; + float[] positions = new float[mXAxis.mEntries.length * 2]; for (int i = 0; i < positions.length; i += 2) { // only fill x values @@ -222,7 +245,7 @@ public void renderGridLines(Canvas c) { if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) return; - float[] positions = new float[mXAxis.mEntryCount * 2]; + float[] positions = new float[mXAxis.mEntries.length * 2]; for (int i = 0; i < positions.length; i += 2) { // only fill x values @@ -347,5 +370,4 @@ public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position } } } - } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java index ed45549050..44107750ea 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java @@ -28,7 +28,7 @@ public XAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, XAxis xA } @Override - protected void computeAxis() { + protected void computeSize() { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java index 68415ba67f..e06d2fcbf8 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -22,11 +22,6 @@ public YAxisRendererRadarChart(ViewPortHandler viewPortHandler, YAxis yAxis, Rad this.mChart = chart; } - @Override - public void computeAxis(float yMin, float yMax, boolean inverted) { - computeAxisValues(yMin, yMax); - } - @Override protected void computeAxisValues(float min, float max) { float yMin = min; From 7db6b7ea4af0cd914bf5671e204c6411619350d7 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 30 May 2016 15:44:03 +0200 Subject: [PATCH 129/606] Work on barchart rendering --- .../mpchartexample/StackedBarActivity.java | 7 +- .../mikephil/charting/buffer/BarBuffer.java | 10 ++- .../mikephil/charting/charts/Chart.java | 5 ++ .../dataprovider/ChartInterface.java | 2 + .../charting/renderer/BarChartRenderer.java | 84 +++++++++++-------- .../mikephil/charting/utils/Transformer.java | 37 -------- 6 files changed, 69 insertions(+), 76 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 76267ccac5..ce9419e5dc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -24,6 +24,7 @@ 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.MyValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.MyYAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -59,7 +60,7 @@ protected void onCreate(Bundle savedInstanceState) { // if more than 60 entries are displayed in the chart, no values will be // drawn - mChart.setMaxVisibleValueCount(60); + mChart.setMaxVisibleValueCount(40); // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); @@ -261,6 +262,10 @@ private int[] getColors() { // have as many colors as stack-values per entry int[] colors = new int[stacksize]; + for(int i = 0; i < colors.length; i++) { + colors[i] = ColorTemplate.MATERIAL_COLORS[i]; + } + return colors; } } diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java b/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java index 3b001c466d..91fc8c769a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java +++ b/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java @@ -13,7 +13,9 @@ public class BarBuffer extends AbstractBuffer { protected boolean mContainsStacks = false; protected boolean mInverted = false; - /** interval on the x-axis per group */ + /** + * interval on the x-axis per group + */ protected float mInterval = 0f; public BarBuffer(int size, float groupspace, int dataSetCount, boolean containsStacks) { @@ -73,13 +75,14 @@ public void feed(IBarDataSet data) { float x = mInterval * i + dataSetSpace; float y = e.getY(); - float [] vals = e.getYVals(); + float[] vals = e.getYVals(); if (!mContainsStacks || vals == null) { float left = x + groupSpaceWidthHalf + barSpaceWidthHalf; float right = left + newBarWidth - barSpaceWidth; float bottom, top; + if (mInverted) { bottom = y >= 0 ? y : 0; top = y <= 0 ? y : 0; @@ -107,7 +110,7 @@ public void feed(IBarDataSet data) { float value = vals[k]; - if(value >= 0f) { + if (value >= 0f) { y = posY; yStart = posY + value; posY = yStart; @@ -120,6 +123,7 @@ public void feed(IBarDataSet data) { float left = x + groupSpaceWidthHalf + barSpaceWidthHalf; float right = left + newBarWidth - barSpaceWidth; float bottom, top; + if (mInverted) { bottom = y >= yStart ? y : yStart; top = y <= yStart ? y : yStart; diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java index 37a24ad1bd..2b3ffde109 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java @@ -1018,6 +1018,11 @@ public float getXChartMin() { return mXAxis.mAxisMinimum; } + @Override + public float getXRange() { + return mXAxis.mAxisRange; + } + @Override public int getXValCount() { return mData.getXValCount(); diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java index 350d35e644..a7f9544ed8 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java +++ b/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java @@ -28,6 +28,8 @@ public interface ChartInterface { */ float getXChartMax(); + float getXRange(); + /** * Returns the minimum y-value of the chart, regardless of zoom or translation. * diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java index 51bb35a7b7..ecc30e9f84 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -25,7 +25,9 @@ public class BarChartRenderer extends DataRenderer { protected BarDataProvider mChart; - /** the rect object that is used for drawing the bars */ + /** + * the rect object that is used for drawing the bars + */ protected RectF mBarRect = new RectF(); protected BarBuffer[] mBarBuffers; @@ -34,7 +36,7 @@ public class BarChartRenderer extends DataRenderer { protected Paint mBarBorderPaint; public BarChartRenderer(BarDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { + ViewPortHandler viewPortHandler) { super(animator, viewPortHandler); this.mChart = chart; @@ -99,7 +101,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { buffer.setBarSpace(dataSet.getBarSpace()); buffer.setDataSet(index); buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); - buffer.setInterval(mChart.getXChartMax() / dataSet.getEntryCount()); + buffer.setInterval(mChart.getXRange() / dataSet.getEntryCount()); buffer.feed(dataSet); @@ -169,15 +171,15 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { /** * Prepares a bar for being highlighted. - * - * @param x the x-position - * @param y1 the y1-position - * @param y2 the y2-position + * + * @param x the x-position + * @param y1 the y1-position + * @param y2 the y2-position * @param barspaceHalf the space between bars * @param trans */ protected void prepareBarHighlight(float x, float y1, float y2, float barspaceHalf, - Transformer trans) { + Transformer trans) { float barWidth = 0.5f; @@ -227,58 +229,66 @@ public void drawValues(Canvas c) { negOffset = -negOffset - valueTextHeight; } - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - float[] valuePoints = getTransformedValues(trans, dataSet, i); + // get the buffer + BarBuffer buffer = mBarBuffers[i]; // if only single values are drawn (sum) if (!dataSet.isStacked()) { - for (int j = 0; j < valuePoints.length * mAnimator.getPhaseX(); j += 2) { + for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) { - if (!mViewPortHandler.isInBoundsRight(valuePoints[j])) + float x = (buffer.buffer[j] + buffer.buffer[j + 2]) / 2f; + + if (!mViewPortHandler.isInBoundsRight(x)) break; - if (!mViewPortHandler.isInBoundsY(valuePoints[j + 1]) - || !mViewPortHandler.isInBoundsLeft(valuePoints[j])) + if (!mViewPortHandler.isInBoundsY(buffer.buffer[j + 1]) + || !mViewPortHandler.isInBoundsLeft(x)) continue; - BarEntry entry = dataSet.getEntryForIndex(j / 2); + BarEntry entry = dataSet.getEntryForIndex(j / 4); float val = entry.getY(); - drawValue(c, dataSet.getValueFormatter(), val, entry, i, valuePoints[j], - valuePoints[j + 1] + (val >= 0 ? posOffset : negOffset), dataSet.getValueTextColor(j / 2)); + drawValue(c, dataSet.getValueFormatter(), val, entry, i, x, + buffer.buffer[j + 1] + (val >= 0 ? posOffset : negOffset), dataSet.getValueTextColor + (j / 4)); } // if we have stacks } else { - for (int j = 0; j < (valuePoints.length - 1) * mAnimator.getPhaseX(); j += 2) { + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + int bufferIndex = 0; + int index = 0; - BarEntry entry = dataSet.getEntryForIndex(j / 2); + while (index < dataSet.getEntryCount() * mAnimator.getPhaseX()) { + + BarEntry entry = dataSet.getEntryForIndex(index); float[] vals = entry.getYVals(); + float x = (buffer.buffer[bufferIndex] + buffer.buffer[bufferIndex + 2]) / 2f; + + int color = dataSet.getValueTextColor(index); // we still draw stacked bars, but there is one // non-stacked // in between if (vals == null) { - if (!mViewPortHandler.isInBoundsRight(valuePoints[j])) + if (!mViewPortHandler.isInBoundsRight(x)) break; - if (!mViewPortHandler.isInBoundsY(valuePoints[j + 1]) - || !mViewPortHandler.isInBoundsLeft(valuePoints[j])) + if (!mViewPortHandler.isInBoundsY(buffer.buffer[bufferIndex + 1]) + || !mViewPortHandler.isInBoundsLeft(x)) continue; - drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, valuePoints[j], - valuePoints[j + 1] + (entry.getY() >= 0 ? posOffset : negOffset), dataSet.getValueTextColor(j / 2)); + drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, + buffer.buffer[bufferIndex + 1] + (entry.getY() >= 0 ? posOffset : negOffset), color); // draw stack values } else { - int color = dataSet.getValueTextColor(j / 2); - float[] transformed = new float[vals.length * 2]; float posY = 0f; @@ -304,7 +314,6 @@ public void drawValues(Canvas c) { for (int k = 0; k < transformed.length; k += 2) { - float x = valuePoints[j]; float y = transformed[k + 1] + (vals[k / 2] >= 0 ? posOffset : negOffset); @@ -318,6 +327,9 @@ public void drawValues(Canvas c) { drawValue(c, dataSet.getValueFormatter(), vals[k / 2], entry, i, x, y, color); } } + + bufferIndex = vals == null ? bufferIndex + 4 : bufferIndex + 4 * vals.length; + index++; } } } @@ -341,8 +353,8 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (maxDataSetIndex - minDataSetIndex < 1) continue; for (int dataSetIndex = minDataSetIndex; - dataSetIndex < maxDataSetIndex; - dataSetIndex++) { + dataSetIndex < maxDataSetIndex; + dataSetIndex++) { IBarDataSet set = barData.getDataSetByIndex(dataSetIndex); @@ -420,10 +432,11 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } public float[] getTransformedValues(Transformer trans, IBarDataSet data, - int dataSetIndex) { - return trans.generateTransformedValuesBarChart(data, dataSetIndex, - mChart.getBarData(), - mAnimator.getPhaseY()); + int dataSetIndex) { +// return trans.generateTransformedValuesBarChart(data, dataSetIndex, +// mChart.getBarData(), +// mAnimator.getPhaseY()); + return null; } protected boolean passesCheck() { @@ -432,5 +445,6 @@ protected boolean passesCheck() { } @Override - public void drawExtras(Canvas c) { } + public void drawExtras(Canvas c) { + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java b/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java index 15f71cc3a8..d7f19d3992 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java @@ -211,43 +211,6 @@ public float[] generateTransformedValuesCandle(ICandleDataSet data, return valuePoints; } - /** - * Transforms an List of Entry into a float array containing the x and - * y values transformed with all matrices for the BARCHART. - * - * @param data - * @param dataSetIndex the dataset index - * @param bd - * @param phaseY - * @return - */ - public float[] generateTransformedValuesBarChart(IBarDataSet data, - int dataSetIndex, BarData bd, float phaseY) { - - float[] valuePoints = new float[data.getEntryCount() * 2]; - - int setCount = bd.getDataSetCount(); - float space = bd.getGroupSpace(); - - for (int j = 0; j < valuePoints.length; j += 2) { - - Entry e = data.getEntryForIndex(j / 2); - float i = e.getX(); - - // calculate the x-position, depending on datasetcount - float x = e.getX() + i * (setCount - 1) + dataSetIndex + space * i - + space / 2f; - float y = e.getY(); - - valuePoints[j] = x; - valuePoints[j + 1] = y * phaseY; - } - - getValueToPixelMatrix().mapPoints(valuePoints); - - return valuePoints; - } - /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the BARCHART. From 690dd87a062d1d7484d6788fa7c045f884d2b614 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 30 May 2016 17:21:18 +0200 Subject: [PATCH 130/606] Work on highlighting --- .../mpchartexample/StackedBarActivity.java | 460 +++++++++--------- .../StackedBarActivityNegative.java | 22 +- .../mikephil/charting/charts/Chart.java | 8 +- .../mikephil/charting/charts/PieChart.java | 11 +- .../charting/charts/PieRadarChartBase.java | 3 +- .../mikephil/charting/data/BarEntry.java | 374 +++++++------- .../mikephil/charting/data/ChartData.java | 6 +- .../mikephil/charting/data/CombinedData.java | 6 +- .../mikephil/charting/data/DataSet.java | 22 +- .../data/realm/base/RealmBaseDataSet.java | 90 ++-- .../realm/implementation/RealmBarDataSet.java | 23 +- .../implementation/RealmBubbleDataSet.java | 6 +- .../implementation/RealmCandleDataSet.java | 2 +- .../charting/highlight/BarHighlighter.java | 26 +- .../charting/highlight/ChartHighlighter.java | 27 +- .../highlight/CombinedHighlighter.java | 6 +- .../charting/highlight/Highlight.java | 46 +- .../highlight/HorizontalBarHighlighter.java | 10 +- .../interfaces/datasets/IDataSet.java | 24 +- .../charting/renderer/BarChartRenderer.java | 109 ++--- .../renderer/BubbleChartRenderer.java | 5 +- .../renderer/CandleStickChartRenderer.java | 8 +- .../renderer/HorizontalBarChartRenderer.java | 1 - .../charting/renderer/LineChartRenderer.java | 8 +- .../charting/renderer/PieChartRenderer.java | 16 +- .../charting/renderer/RadarChartRenderer.java | 6 +- .../renderer/ScatterChartRenderer.java | 8 +- .../mikephil/charting/utils/FileUtils.java | 4 +- 28 files changed, 672 insertions(+), 665 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index ce9419e5dc..36fd6f1d33 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -1,5 +1,6 @@ package com.xxmassdeveloper.mpchartexample; +import android.graphics.Color; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -34,237 +35,238 @@ public class StackedBarActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - private BarChart 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_barchart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); - - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); - - mChart = (BarChart) findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - - mChart.setDescription(""); - - // if more than 60 entries are displayed in the chart, no values will be - // drawn - mChart.setMaxVisibleValueCount(40); - - // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.setDrawGridBackground(false); - mChart.setDrawBarShadow(false); - - mChart.setDrawValueAboveBar(false); - - // change the position of the y-labels - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setValueFormatter(new MyYAxisValueFormatter()); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) - mChart.getAxisRight().setEnabled(false); - - XAxis xLabels = mChart.getXAxis(); - xLabels.setPosition(XAxisPosition.TOP); - - // mChart.setDrawXLabels(false); - // mChart.setDrawYLabels(false); - - // setting data - mSeekBarX.setProgress(12); - mSeekBarY.setProgress(100); - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_RIGHT); - l.setFormSize(8f); - l.setFormToTextSpace(4f); - l.setXEntrySpace(6f); - - // mChart.setDrawLegend(false); - } - - @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) { - - BarDataSet set = (BarDataSet) iSet; - set.setDrawValues(!set.isDrawValuesEnabled()); - } - - 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.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - 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())); - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - xVals.add(new XAxisValue(i, mMonths[i % mMonths.length])); - } - - 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(new float[] { val1, val2, val3 }, i)); - } - - BarDataSet set1; - - if (mChart.getData() != null && - mChart.getData().getDataSetCount() > 0) { - set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); - set1.setYVals(yVals1); - mChart.getData().setXVals(xVals); - mChart.getData().notifyDataChanged(); - mChart.notifyDataSetChanged(); - } else { - set1 = new BarDataSet(yVals1, "Statistics Vienna 2014"); - set1.setColors(getColors()); - set1.setStackLabels(new String[]{"Births", "Divorces", "Marriages"}); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - - BarData data = new BarData(xVals, dataSets); - data.setValueFormatter(new MyValueFormatter()); - - mChart.setData(data); - } - - mChart.invalidate(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - - BarEntry entry = (BarEntry) e; - - if (entry.getYVals() != null) - Log.i("VAL SELECTED", "Value: " + entry.getYVals()[h.getStackIndex()]); - else - Log.i("VAL SELECTED", "Value: " + entry.getY()); - } - - @Override - public void onNothingSelected() { - // TODO Auto-generated method stub - - } - - private int[] getColors() { - - int stacksize = 3; + private BarChart mChart; + private SeekBar mSeekBarX, mSeekBarY; + private TextView tvX, tvY; - // have as many colors as stack-values per entry - int[] colors = new int[stacksize]; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_barchart); - for(int i = 0; i < colors.length; i++) { - colors[i] = ColorTemplate.MATERIAL_COLORS[i]; - } + tvX = (TextView) findViewById(R.id.tvXMax); + tvY = (TextView) findViewById(R.id.tvYMax); + + mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); + mSeekBarX.setOnSeekBarChangeListener(this); + + mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarY.setOnSeekBarChangeListener(this); + + mChart = (BarChart) findViewById(R.id.chart1); + mChart.setOnChartValueSelectedListener(this); + + mChart.setDescription(""); + + // if more than 60 entries are displayed in the chart, no values will be + // drawn + mChart.setMaxVisibleValueCount(40); + + // scaling can now only be done on x- and y-axis separately + mChart.setPinchZoom(false); + + mChart.setDrawGridBackground(false); + mChart.setDrawBarShadow(false); + + mChart.setDrawValueAboveBar(false); + + // change the position of the y-labels + YAxis leftAxis = mChart.getAxisLeft(); + leftAxis.setValueFormatter(new MyYAxisValueFormatter()); + leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + mChart.getAxisRight().setEnabled(false); + + XAxis xLabels = mChart.getXAxis(); + xLabels.setPosition(XAxisPosition.TOP); + + // mChart.setDrawXLabels(false); + // mChart.setDrawYLabels(false); + + // setting data + mSeekBarX.setProgress(12); + mSeekBarY.setProgress(100); + + Legend l = mChart.getLegend(); + l.setPosition(LegendPosition.BELOW_CHART_RIGHT); + l.setFormSize(8f); + l.setFormToTextSpace(4f); + l.setXEntrySpace(6f); + + // mChart.setDrawLegend(false); + } + + @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) { + + BarDataSet set = (BarDataSet) iSet; + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + 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.actionToggleHighlightArrow: { + if (mChart.isDrawHighlightArrowEnabled()) + mChart.setDrawHighlightArrow(false); + else + mChart.setDrawHighlightArrow(true); + 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())); + + ArrayList xVals = new ArrayList(); + for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { + xVals.add(new XAxisValue(i, mMonths[i % mMonths.length])); + } + + 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})); + } + + BarDataSet set1; + + if (mChart.getData() != null && + mChart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet) mChart.getData().getDataSetByIndex(0); + set1.setYVals(yVals1); + mChart.getData().setXVals(xVals); + mChart.getData().notifyDataChanged(); + mChart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(yVals1, "Statistics Vienna 2014"); + set1.setColors(getColors()); + set1.setStackLabels(new String[]{"Births", "Divorces", "Marriages"}); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); + + BarData data = new BarData(xVals, dataSets); + data.setValueFormatter(new MyValueFormatter()); + data.setValueTextColor(Color.WHITE); + + mChart.setData(data); + } + + mChart.invalidate(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + // TODO Auto-generated method stub + + } + + @Override + public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + + BarEntry entry = (BarEntry) e; + + if (entry.getYVals() != null) + Log.i("VAL SELECTED", "Value: " + entry.getYVals()[h.getStackIndex()]); + else + Log.i("VAL SELECTED", "Value: " + entry.getY()); + } + + @Override + public void onNothingSelected() { + // TODO Auto-generated method stub + + } + + private int[] getColors() { + + int stacksize = 3; + + // have as many colors as stack-values per entry + int[] colors = new int[stacksize]; + + for (int i = 0; i < colors.length; i++) { + colors[i] = ColorTemplate.MATERIAL_COLORS[i]; + } return colors; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 012065683e..ddd8ac3fcf 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -80,17 +80,17 @@ protected void onCreate(Bundle savedInstanceState) { // 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(new float[]{ -10, 10 }, 0)); - yValues.add(new BarEntry(new float[]{ -12, 13 }, 1)); - yValues.add(new BarEntry(new float[]{ -15, 15 }, 2)); - yValues.add(new BarEntry(new float[]{ -17, 17 }, 3)); - yValues.add(new BarEntry(new float[]{ -19, 20 }, 4)); - yValues.add(new BarEntry(new float[]{ -19, 19 }, 5)); - yValues.add(new BarEntry(new float[]{ -16, 16 }, 6)); - yValues.add(new BarEntry(new float[]{ -13, 14 }, 7)); - yValues.add(new BarEntry(new float[]{ -10, 11 }, 8)); - yValues.add(new BarEntry(new float[]{ -5, 6 }, 9)); - yValues.add(new BarEntry(new float[]{ -1, 2 }, 10)); + yValues.add(new BarEntry(0, new float[]{ -10, 10 })); + yValues.add(new BarEntry(1, new float[]{ -12, 13 })); + yValues.add(new BarEntry(2, new float[]{ -15, 15 })); + yValues.add(new BarEntry(3, new float[]{ -17, 17 })); + yValues.add(new BarEntry(4, new float[]{ -19, 20 })); + yValues.add(new BarEntry(5, new float[]{ -19, 19 })); + yValues.add(new BarEntry(6, new float[]{ -16, 16 })); + yValues.add(new BarEntry(7, new float[]{ -13, 14 })); + yValues.add(new BarEntry(8, new float[]{ -10, 11 })); + yValues.add(new BarEntry(9, new float[]{ -5, 6 })); + yValues.add(new BarEntry(10, new float[]{ -1, 2 })); BarDataSet set = new BarDataSet(yValues, "Age Distribution"); set.setValueFormatter(new CustomFormatter()); diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java index 2b3ffde109..e492d74e5b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java @@ -614,7 +614,7 @@ public void highlightValue(Highlight high, boolean callListener) { } else { if (this instanceof BarLineChartBase && ((BarLineChartBase)this).isHighlightFullBarEnabled()) - high = new Highlight(high.getXIndex(), Float.NaN, -1, -1, -1); + high = new Highlight(high.getX(), Float.NaN, -1, -1, -1); // set the indices to highlight mIndicesToHighlight = new Highlight[]{ @@ -681,19 +681,19 @@ protected void drawMarkers(Canvas canvas) { for (int i = 0; i < mIndicesToHighlight.length; i++) { Highlight highlight = mIndicesToHighlight[i]; - int xIndex = highlight.getXIndex(); + float xVal = highlight.getX(); int dataSetIndex = highlight.getDataSetIndex(); float deltaX = mXAxis != null ? mXAxis.mAxisRange : ((mData == null ? 0.f : mData.getXValCount()) - 1.f); - if (xIndex <= deltaX && xIndex <= deltaX * mAnimator.getPhaseX()) { + if (xVal <= deltaX && xVal <= deltaX * mAnimator.getPhaseX()) { Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]); // make sure entry not null - if (e == null || e.getX() != mIndicesToHighlight[i].getXIndex()) + if (e == null || e.getX() != mIndicesToHighlight[i].getX()) continue; float[] pos = getMarkerPosition(e, highlight); diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java index 74fbd2549d..ba8e37c8ea 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java @@ -10,6 +10,7 @@ import android.util.AttributeSet; import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.highlight.Highlight; @@ -180,17 +181,17 @@ protected float[] getMarkerPosition(Entry e, Highlight highlight) { float rotationAngle = getRotationAngle(); - int i = highlight.getXIndex(); + int entryIndex = getData().getDataSet().getEntryIndex(highlight.getX(), DataSet.Rounding.CLOSEST); // offset needed to center the drawn text in the slice - float offset = mDrawAngles[i] / 2; + float offset = mDrawAngles[entryIndex] / 2; // calculate the text position float x = (float) (r - * Math.cos(Math.toRadians((rotationAngle + mAbsoluteAngles[i] - offset) + * Math.cos(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset) * mAnimator.getPhaseY())) + center.x); float y = (float) (r - * Math.sin(Math.toRadians((rotationAngle + mAbsoluteAngles[i] - offset) + * Math.sin(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset) * mAnimator.getPhaseY())) + center.y); return new float[]{x, y}; @@ -247,7 +248,7 @@ public boolean needsHighlight(int xIndex, int dataSetIndex) { for (int i = 0; i < mIndicesToHighlight.length; i++) // check if the xvalue for the given dataset needs highlight - if (mIndicesToHighlight[i].getXIndex() == xIndex + if (mIndicesToHighlight[i].getX() == xIndex && mIndicesToHighlight[i].getDataSetIndex() == dataSetIndex) return true; diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java index 9c31414ced..2c55c24125 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java @@ -14,7 +14,6 @@ import com.github.mikephil.charting.animation.Easing; 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.data.ChartData; import com.github.mikephil.charting.data.Entry; @@ -458,7 +457,7 @@ public List getSelectionDetailsAtIndex(int xIndex) { IDataSet dataSet = mData.getDataSetByIndex(i); // extract all y-values from all DataSets at the given x-index - final float yVal = dataSet.getYValForXIndex(xIndex); + final float yVal = dataSet.getYValueForXValue(xIndex); if (Float.isNaN(yVal)) continue; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarEntry.java b/MPChartLib/src/com/github/mikephil/charting/data/BarEntry.java index 6f5a8c5c47..97358ae378 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarEntry.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/BarEntry.java @@ -4,195 +4,197 @@ /** * Entry class for the BarChart. (especially stacked bars) - * + * * @author Philipp Jahoda */ @SuppressLint("ParcelCreator") public class BarEntry extends Entry { - /** the values the stacked barchart holds */ - private float[] mYVals; - - /** the sum of all negative values this entry (if stacked) contains */ - private float mNegativeSum; - - /** the sum of all positive values this entry (if stacked) contains */ - private float mPositiveSum; - - /** - * Constructor for stacked bar entries. - * - * @param vals - * - the stack values, use at lest 2 - * @param x - */ - public BarEntry(float[] vals, float x) { - super(x, calcSum(vals)); - - this.mYVals = vals; - calcPosNegSum(); - } - - /** - * Constructor for normal bars (not stacked). - * - * @param x - * @param y - */ - public BarEntry(float x, float y) { - super(x, y); - } - - /** - * Constructor for stacked bar entries. - * - * @param x - * @param vals - * - the stack values, use at least 2 - * @param label - * Additional description label. - */ - public BarEntry(float x, float[] vals, String label) { - super(x, calcSum(vals), label); - - this.mYVals = vals; - calcPosNegSum(); - } - - /** - * 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); - } - - /** - * Returns an exact copy of the BarEntry. - */ - public BarEntry copy() { - - BarEntry copied = new BarEntry(getX(), getY(), getData()); - copied.setVals(mYVals); - return copied; - } - - /** - * Returns the stacked values this BarEntry represents, or null, if only a single value is represented (then, use - * getY()). - * - * @return - */ - public float[] getYVals() { - return mYVals; - } - - /** - * Set the array of values this BarEntry should represent. - * - * @param vals - */ - public void setVals(float[] vals) { - setY(calcSum(vals)); - mYVals = vals; - calcPosNegSum(); - } - - /** - * Returns the value of this BarEntry. If the entry is stacked, it returns the positive sum of all values. - * - * @return - */ - @Override - public float getY() { - return super.getY(); - } - - /** - * Returns true if this BarEntry is stacked (has a values array), false if not. - * - * @return - */ - public boolean isStacked() { - return mYVals != null; - } - - public float getBelowSum(int stackIndex) { - - if (mYVals == null) - return 0; - - float remainder = 0f; - int index = mYVals.length - 1; - - while (index > stackIndex && index >= 0) { - remainder += mYVals[index]; - index--; - } - - return remainder; - } - - /** - * Reuturns the sum of all positive values this entry (if stacked) contains. - * - * @return - */ - public float getPositiveSum() { - return mPositiveSum; - } - - /** - * Returns the sum of all negative values this entry (if stacked) contains. (this is a positive number) - * - * @return - */ - public float getNegativeSum() { - return mNegativeSum; - } - - private void calcPosNegSum() { - - if (mYVals == null) { - mNegativeSum = 0; - mPositiveSum = 0; - return; - } - - float sumNeg = 0f; - float sumPos = 0f; - - for (float f : mYVals) { - if (f <= 0f) - sumNeg += Math.abs(f); - else - sumPos += f; - } - - mNegativeSum = sumNeg; - mPositiveSum = sumPos; - } - - /** - * Calculates the sum across all values of the given stack. - * - * @param vals - * @return - */ - private static float calcSum(float[] vals) { - - if (vals == null) - return 0f; - - float sum = 0f; - - for (float f : vals) - sum += f; - - return sum; - } + /** + * the values the stacked barchart holds + */ + private float[] mYVals; + + /** + * the sum of all negative values this entry (if stacked) contains + */ + private float mNegativeSum; + + /** + * the sum of all positive values this entry (if stacked) contains + */ + private float mPositiveSum; + + /** + * Constructor for stacked bar entries. + * + * @param x + * @param vals - the stack values, use at lest 2 + */ + public BarEntry(float x, float[] vals) { + super(x, calcSum(vals)); + + this.mYVals = vals; + calcPosNegSum(); + } + + /** + * Constructor for normal bars (not stacked). + * + * @param x + * @param y + */ + public BarEntry(float x, float y) { + super(x, y); + } + + /** + * Constructor for stacked bar entries. + * + * @param x + * @param vals - the stack values, use at least 2 + * @param label Additional description label. + */ + public BarEntry(float x, float[] vals, String label) { + super(x, calcSum(vals), label); + + this.mYVals = vals; + calcPosNegSum(); + } + + /** + * 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); + } + + /** + * Returns an exact copy of the BarEntry. + */ + public BarEntry copy() { + + BarEntry copied = new BarEntry(getX(), getY(), getData()); + copied.setVals(mYVals); + return copied; + } + + /** + * Returns the stacked values this BarEntry represents, or null, if only a single value is represented (then, use + * getY()). + * + * @return + */ + public float[] getYVals() { + return mYVals; + } + + /** + * Set the array of values this BarEntry should represent. + * + * @param vals + */ + public void setVals(float[] vals) { + setY(calcSum(vals)); + mYVals = vals; + calcPosNegSum(); + } + + /** + * Returns the value of this BarEntry. If the entry is stacked, it returns the positive sum of all values. + * + * @return + */ + @Override + public float getY() { + return super.getY(); + } + + /** + * Returns true if this BarEntry is stacked (has a values array), false if not. + * + * @return + */ + public boolean isStacked() { + return mYVals != null; + } + + public float getBelowSum(int stackIndex) { + + if (mYVals == null) + return 0; + + float remainder = 0f; + int index = mYVals.length - 1; + + while (index > stackIndex && index >= 0) { + remainder += mYVals[index]; + index--; + } + + return remainder; + } + + /** + * Reuturns the sum of all positive values this entry (if stacked) contains. + * + * @return + */ + public float getPositiveSum() { + return mPositiveSum; + } + + /** + * Returns the sum of all negative values this entry (if stacked) contains. (this is a positive number) + * + * @return + */ + public float getNegativeSum() { + return mNegativeSum; + } + + private void calcPosNegSum() { + + if (mYVals == null) { + mNegativeSum = 0; + mPositiveSum = 0; + return; + } + + float sumNeg = 0f; + float sumPos = 0f; + + for (float f : mYVals) { + if (f <= 0f) + sumNeg += Math.abs(f); + else + sumPos += f; + } + + mNegativeSum = sumNeg; + mPositiveSum = sumPos; + } + + /** + * Calculates the sum across all values of the given stack. + * + * @param vals + * @return + */ + private static float calcSum(float[] vals) { + + if (vals == null) + return 0f; + + float sum = 0f; + + for (float f : vals) + sum += f; + + return sum; + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java index 0fff111552..70605b3a8a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java @@ -523,10 +523,10 @@ public Entry getEntryForHighlight(Highlight highlight) { // if we are not interested in highlighting a specific value. List entries = mDataSets.get(highlight.getDataSetIndex()) - .getEntriesForXIndex(highlight.getXIndex()); + .getEntriesForXPos(highlight.getX()); for (Object entry : entries) - if (((Entry)entry).getY() == highlight.getValue() || - Float.isNaN(highlight.getValue())) + if (((Entry)entry).getY() == highlight.getY() || + Float.isNaN(highlight.getY())) return (Entry)entry; return null; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java b/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java index f02374a7a9..845499b7db 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java @@ -143,10 +143,10 @@ public Entry getEntryForHighlight(Highlight highlight) { // if we are not interested in highlighting a specific value. List entries = data.getDataSetByIndex(highlight.getDataSetIndex()) - .getEntriesForXIndex(highlight.getXIndex()); + .getEntriesForXPos(highlight.getX()); for (Object entry : entries) - if (((Entry)entry).getY() == highlight.getValue() || - Float.isNaN(highlight.getValue())) + if (((Entry)entry).getY() == highlight.getY() || + Float.isNaN(highlight.getY())) return (Entry)entry; return null; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java index e85c93e0ef..fed5f780ea 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java @@ -326,20 +326,20 @@ public int getEntryIndex(float xPos, Rounding rounding) { } @Override - public float getYValForXIndex(int xIndex) { + public float getYValueForXValue(float xVal) { - Entry e = getEntryForXPos(xIndex); + Entry e = getEntryForXPos(xVal); - if (e != null && e.getX() == xIndex) + if (e != null && e.getX() == xVal) return e.getY(); else return Float.NaN; } @Override - public float[] getYValsForXIndex(int xIndex) { + public float[] getYValuesForXPos(float xVal) { - List entries = getEntriesForXIndex(xIndex); + List entries = getEntriesForXPos(xVal); float[] yVals = new float[entries.size()]; int i = 0; @@ -355,11 +355,11 @@ public float[] getYValsForXIndex(int xIndex) { * does calculations at runtime. Do not over-use in performance critical * situations. * - * @param xIndex + * @param xVal * @return */ @Override - public List getEntriesForXIndex(int xIndex) { + public List getEntriesForXPos(float xVal) { List entries = new ArrayList(); @@ -370,14 +370,14 @@ public List getEntriesForXIndex(int xIndex) { int m = (high + low) / 2; T entry = mValues.get(m); - if (xIndex == entry.getX()) { - while (m > 0 && mValues.get(m - 1).getX() == xIndex) + if (xVal == entry.getX()) { + while (m > 0 && mValues.get(m - 1).getX() == xVal) m--; high = mValues.size(); for (; m < high; m++) { entry = mValues.get(m); - if (entry.getX() == xIndex) { + if (entry.getX() == xVal) { entries.add(entry); } else { break; @@ -386,7 +386,7 @@ public List getEntriesForXIndex(int xIndex) { break; } else { - if (xIndex > entry.getX()) + if (xVal > entry.getX()) low = m + 1; else high = m - 1; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java index 49399fa3c6..238546f83c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java @@ -50,20 +50,20 @@ public abstract class RealmBaseDataSet e /** * fieldname of the column that contains the y-values of this dataset */ - protected String mValuesField; + protected String mYValuesField; /** - * fieldname of the column that contains the x-indices of this dataset + * fieldname of the column that contains the x-values of this dataset */ - protected String mIndexField; + protected String mXValuesField; public RealmBaseDataSet(RealmResults results, String yValuesField) { this.results = results; - this.mValuesField = yValuesField; + this.mYValuesField = yValuesField; this.mValues = new ArrayList(); - if (mIndexField != null) - this.results.sort(mIndexField, Sort.ASCENDING); + if (mXValuesField != null) + this.results.sort(mXValuesField, Sort.ASCENDING); } /** @@ -75,12 +75,12 @@ public RealmBaseDataSet(RealmResults results, String yValuesField) { */ public RealmBaseDataSet(RealmResults results, String yValuesField, String xIndexField) { this.results = results; - this.mValuesField = yValuesField; - this.mIndexField = xIndexField; + this.mYValuesField = yValuesField; + this.mXValuesField = xIndexField; this.mValues = new ArrayList(); - if (mIndexField != null) - this.results.sort(mIndexField, Sort.ASCENDING); + if (mXValuesField != null) + this.results.sort(mXValuesField, Sort.ASCENDING); } /** @@ -94,22 +94,22 @@ public void build(RealmResults results) { } } - public S buildEntryFromResultObject(T realmObject, int xIndex) { + public S buildEntryFromResultObject(T realmObject, float x) { DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - return (S) new Entry(dynamicObject.getFloat(mValuesField), - mIndexField == null ? xIndex : dynamicObject.getInt(mIndexField)); + return (S) new Entry(dynamicObject.getFloat(mYValuesField), + mXValuesField == null ? x : dynamicObject.getInt(mXValuesField)); } @Override public float getYMin() { - //return results.min(mValuesField).floatValue(); + //return results.min(mYValuesField).floatValue(); return mYMin; } @Override public float getYMax() { - //return results.max(mValuesField).floatValue(); + //return results.max(mYValuesField).floatValue(); return mYMax; } @@ -174,8 +174,8 @@ public void calcMinMax() { @Override public S getEntryForXPos(float xPos) { - //DynamicRealmObject o = new DynamicRealmObject(results.where().equalTo(mIndexField, xIndex).findFirst()); - //return new Entry(o.getFloat(mValuesField), o.getInt(mIndexField)); + //DynamicRealmObject o = new DynamicRealmObject(results.where().equalTo(mXValuesField, xIndex).findFirst()); + //return new Entry(o.getFloat(mYValuesField), o.getInt(mXValuesField)); return getEntryForXPos(xPos, DataSet.Rounding.CLOSEST); } @@ -188,19 +188,21 @@ public S getEntryForXPos(float xPos, DataSet.Rounding rounding) { } @Override - public List getEntriesForXIndex(int xIndex) { + public List getEntriesForXPos(float xVal) { List entries = new ArrayList<>(); - if (mIndexField == null) { - T object = results.get(xIndex); - if (object != null) - entries.add(buildEntryFromResultObject(object, xIndex)); - } else { - RealmResults foundObjects = results.where().equalTo(mIndexField, xIndex).findAll(); +// { +// T object = results.get(xVal); +// if (object != null) +// entries.add(buildEntryFromResultObject(object, xVal)); +// } else + + if (mXValuesField != null) { + RealmResults foundObjects = results.where().equalTo(mXValuesField, xVal).findAll(); for (T e : foundObjects) - entries.add(buildEntryFromResultObject(e, xIndex)); + entries.add(buildEntryFromResultObject(e, xVal)); } return entries; @@ -209,7 +211,7 @@ public List getEntriesForXIndex(int xIndex) { @Override public S getEntryForIndex(int index) { //DynamicRealmObject o = new DynamicRealmObject(results.get(index)); - //return new Entry(o.getFloat(mValuesField), o.getInt(mIndexField)); + //return new Entry(o.getFloat(mYValuesField), o.getInt(mXValuesField)); return mValues.get(index); } @@ -260,21 +262,21 @@ public int getEntryIndex(S e) { } @Override - public float getYValForXIndex(int xIndex) { - //return new DynamicRealmObject(results.where().greaterThanOrEqualTo(mIndexField, xIndex).findFirst()) - // .getFloat(mValuesField); - Entry e = getEntryForXPos(xIndex); + public float getYValueForXValue(float xVal) { + //return new DynamicRealmObject(results.where().greaterThanOrEqualTo(mXValuesField, xIndex).findFirst()) + // .getFloat(mYValuesField); + Entry e = getEntryForXPos(xVal); - if (e != null && e.getX() == xIndex) + if (e != null && e.getX() == xVal) return e.getY(); else return Float.NaN; } @Override - public float[] getYValsForXIndex(int xIndex) { + public float[] getYValuesForXPos(float xVal) { - List entries = getEntriesForXIndex(xIndex); + List entries = getEntriesForXPos(xVal); float[] yVals = new float[entries.size()]; int i = 0; @@ -387,8 +389,8 @@ public RealmResults getResults() { * * @return */ - public String getValuesField() { - return mValuesField; + public String getYValuesField() { + return mYValuesField; } /** @@ -396,25 +398,25 @@ public String getValuesField() { * * @param yValuesField */ - public void setValuesField(String yValuesField) { - this.mValuesField = yValuesField; + public void setYValuesField(String yValuesField) { + this.mYValuesField = yValuesField; } /** - * Returns the fieldname that represents the "x-index" in the realm-data. + * Returns the fieldname that represents the "x-values" in the realm-data. * * @return */ - public String getIndexField() { - return mIndexField; + public String getXValuesField() { + return mXValuesField; } /** - * Sets the field name that is used for getting the x-indices out of the RealmResultSet. + * Sets the field name that is used for getting the x-values out of the RealmResultSet. * - * @param xIndexField + * @param xValuesField */ - public void setIndexField(String xIndexField) { - this.mIndexField = xIndexField; + public void setXValuesField(String xValuesField) { + this.mXValuesField = xValuesField; } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java index ce111d9cdd..a11ebcdc99 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java @@ -15,7 +15,8 @@ /** * Created by Philipp Jahoda on 07/11/15. */ -public class RealmBarDataSet extends RealmBarLineScatterCandleBubbleDataSet implements IBarDataSet { +public class RealmBarDataSet extends RealmBarLineScatterCandleBubbleDataSet + implements IBarDataSet { private String mStackValueFieldName; @@ -67,7 +68,8 @@ public RealmBarDataSet(RealmResults results, String yValuesField, String xInd * @param xIndexField * @param stackValueFieldName */ - public RealmBarDataSet(RealmResults results, String yValuesField, String xIndexField, String stackValueFieldName) { + public RealmBarDataSet(RealmResults results, String yValuesField, String xIndexField, String + stackValueFieldName) { super(results, yValuesField, xIndexField); this.mStackValueFieldName = stackValueFieldName; mHighLightColor = Color.rgb(0, 0, 0); @@ -85,12 +87,12 @@ public void build(RealmResults results) { } @Override - public BarEntry buildEntryFromResultObject(T realmObject, int xIndex) { + public BarEntry buildEntryFromResultObject(T realmObject, float x) { DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - if (dynamicObject.getFieldType(mValuesField) == RealmFieldType.LIST) { + if (dynamicObject.getFieldType(mYValuesField) == RealmFieldType.LIST) { - RealmList list = dynamicObject.getList(mValuesField); + RealmList list = dynamicObject.getList(mYValuesField); float[] values = new float[list.size()]; int i = 0; @@ -99,12 +101,11 @@ public BarEntry buildEntryFromResultObject(T realmObject, int xIndex) { i++; } - return new BarEntry(values, - mIndexField == null ? xIndex : dynamicObject.getInt(mIndexField)); + return new BarEntry( + mXValuesField == null ? x : dynamicObject.getInt(mXValuesField), values); } else { - float value = dynamicObject.getFloat(mValuesField); - return new BarEntry(value, - mIndexField == null ? xIndex : dynamicObject.getInt(mIndexField)); + float value = dynamicObject.getFloat(mYValuesField); + return new BarEntry(mXValuesField == null ? x : dynamicObject.getInt(mXValuesField), value); } } @@ -156,7 +157,7 @@ public void calcMinMax() { mYMax = 0.f; } - if(mXMin == Float.MAX_VALUE) { + if (mXMin == Float.MAX_VALUE) { mXMin = 0.f; mXMax = 0.f; } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java index a7f54e17be..196be07224 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java @@ -53,12 +53,12 @@ public RealmBubbleDataSet(RealmResults result, String yValuesField, String xI } @Override - public BubbleEntry buildEntryFromResultObject(T realmObject, int xIndex) { + public BubbleEntry buildEntryFromResultObject(T realmObject, float x) { DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); return new BubbleEntry( - mIndexField == null ? xIndex : dynamicObject.getInt(mIndexField), - dynamicObject.getFloat(mValuesField), + mXValuesField == null ? x : dynamicObject.getInt(mXValuesField), + dynamicObject.getFloat(mYValuesField), dynamicObject.getFloat(mSizeField)); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java index 24fd6b4704..9f0d43bfaf 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java @@ -123,7 +123,7 @@ public CandleEntry buildEntryFromResultObject(T realmObject, int xIndex) { DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); return new CandleEntry( - mIndexField == null ? xIndex : dynamicObject.getInt(mIndexField), + mXValuesField == null ? xIndex : dynamicObject.getInt(mXValuesField), dynamicObject.getFloat(mHighField), dynamicObject.getFloat(mLowField), dynamicObject.getFloat(mOpenField), diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java index 07e8256c42..0af151d3d6 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java @@ -22,7 +22,7 @@ public Highlight getHighlight(float x, float y) { BarData barData = mChart.getBarData(); - final int xIndex = getXIndex(x); + final float xVal = getXForTouch(x); final float baseNoSpace = getBase(x); final int setCount = barData.getDataSetCount(); int dataSetIndex = ((int)baseNoSpace) % setCount; @@ -33,7 +33,7 @@ public Highlight getHighlight(float x, float y) { dataSetIndex = setCount - 1; } - SelectionDetail selectionDetail = getSelectionDetail(xIndex, y, dataSetIndex); + SelectionDetail selectionDetail = getSelectionDetail(xVal, y, dataSetIndex); if (selectionDetail == null) return null; @@ -48,12 +48,12 @@ public Highlight getHighlight(float x, float y) { return getStackedHighlight(selectionDetail, set, - xIndex, + xVal, pts[1]); } return new Highlight( - xIndex, + xVal, selectionDetail.value, selectionDetail.dataIndex, selectionDetail.dataSetIndex, @@ -61,10 +61,10 @@ public Highlight getHighlight(float x, float y) { } @Override - protected int getXIndex(float x) { + protected float getXForTouch(float x) { if (!mChart.getBarData().isGrouped()) { - return super.getXIndex(x); + return super.getXForTouch(x); } else { float baseNoSpace = getBase(x); @@ -84,7 +84,7 @@ else if (xIndex >= valCount) } @Override - protected SelectionDetail getSelectionDetail(int xIndex, float y, int dataSetIndex) { + protected SelectionDetail getSelectionDetail(float xVal, float y, int dataSetIndex) { dataSetIndex = Math.max(dataSetIndex, 0); @@ -95,7 +95,7 @@ protected SelectionDetail getSelectionDetail(int xIndex, float y, int dataSetInd if (dataSet == null) return null; - final float yValue = dataSet.getYValForXIndex(xIndex); + final float yValue = dataSet.getYValueForXValue(xVal); if (yValue == Double.NaN) return null; @@ -110,23 +110,23 @@ protected SelectionDetail getSelectionDetail(int xIndex, float y, int dataSetInd * * @param selectionDetail the selection detail to work with looking for stacked values * @param set - * @param xIndex + * @param xVal * @param yValue * @return */ protected Highlight getStackedHighlight( SelectionDetail selectionDetail, IBarDataSet set, - int xIndex, + float xVal, double yValue) { - BarEntry entry = set.getEntryForXPos(xIndex); + BarEntry entry = set.getEntryForXPos(xVal); if (entry == null) return null; if (entry.getYVals() == null) { - return new Highlight(xIndex, + return new Highlight(xVal, entry.getY(), selectionDetail.dataIndex, selectionDetail.dataSetIndex); @@ -136,7 +136,7 @@ protected Highlight getStackedHighlight( if (ranges.length > 0) { int stackIndex = getClosestStackIndex(ranges, (float)yValue); return new Highlight( - xIndex, + xVal, entry.getPositiveSum() - entry.getNegativeSum(), selectionDetail.dataIndex, selectionDetail.dataSetIndex, diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java index 6195abe810..1cd1466d25 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java @@ -30,13 +30,13 @@ public ChartHighlighter(T chart) { */ public Highlight getHighlight(float x, float y) { - int xIndex = getXIndex(x); + float xVal = getXForTouch(x); - SelectionDetail selectionDetail = getSelectionDetail(xIndex, y, -1); + SelectionDetail selectionDetail = getSelectionDetail(xVal, y, -1); if (selectionDetail == null) return null; - return new Highlight(xIndex, + return new Highlight(xVal, selectionDetail.value, selectionDetail.dataIndex, selectionDetail.dataSetIndex); @@ -48,7 +48,7 @@ public Highlight getHighlight(float x, float y) { * @param x * @return */ - protected int getXIndex(float x) { + protected float getXForTouch(float x) { // create an array of the touch-point float[] pts = new float[2]; @@ -57,20 +57,20 @@ protected int getXIndex(float x) { // take any transformer to determine the x-axis value mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); - return (int) Math.round(pts[0]); + return Math.round(pts[0]); } /** - * Returns the corresponding SelectionDetail for a given xIndex and y-touch position in pixels. + * Returns the corresponding SelectionDetail for a given xVal and y-touch position in pixels. * - * @param xIndex + * @param xVal * @param y * @param dataSetIndex * @return */ - protected SelectionDetail getSelectionDetail(int xIndex, float y, int dataSetIndex) { + protected SelectionDetail getSelectionDetail(float xVal, float y, int dataSetIndex) { - List valsAtIndex = getSelectionDetailsAtIndex(xIndex, dataSetIndex); + List valsAtIndex = getSelectionDetailsAtIndex(xVal, dataSetIndex); float leftdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.LEFT); float rightdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.RIGHT); @@ -85,11 +85,11 @@ protected SelectionDetail getSelectionDetail(int xIndex, float y, int dataSetInd /** * Returns a list of SelectionDetail object corresponding to the given xIndex. * - * @param xIndex + * @param xVal * @param dataSetIndex dataSet index to look at. -1 if unspecified. * @return */ - protected List getSelectionDetailsAtIndex(int xIndex, int dataSetIndex) { + protected List getSelectionDetailsAtIndex(float xVal, int dataSetIndex) { List vals = new ArrayList(); @@ -111,7 +111,7 @@ protected List getSelectionDetailsAtIndex(int xIndex, int dataS continue; // extract all y-values from all DataSets at the given x-index - final float[] yVals = dataSet.getYValsForXIndex(xIndex); + final float[] yVals = dataSet.getYValuesForXPos(xVal); for (float yVal : yVals) { if (Float.isNaN(yVal)) continue; @@ -120,8 +120,7 @@ protected List getSelectionDetailsAtIndex(int xIndex, int dataS mChart.getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); - if (!Float.isNaN(pts[1])) - { + if (!Float.isNaN(pts[1])) { vals.add(new SelectionDetail(pts[1], yVal, i, dataSet)); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/CombinedHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/CombinedHighlighter.java index 4957028efe..f752be4cfa 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/CombinedHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/CombinedHighlighter.java @@ -21,11 +21,11 @@ public CombinedHighlighter(BarLineScatterCandleBubbleDataProvider chart) { /** * Returns a list of SelectionDetail object corresponding to the given xIndex. * - * @param xIndex + * @param xVal * @return */ @Override - protected List getSelectionDetailsAtIndex(int xIndex, int dataSetIndex) { + protected List getSelectionDetailsAtIndex(float xVal, int dataSetIndex) { List vals = new ArrayList(); float[] pts = new float[2]; @@ -46,7 +46,7 @@ protected List getSelectionDetailsAtIndex(int xIndex, int dataS continue; // extract all y-values from all DataSets at the given x-index - final float yVals[] = dataSet.getYValsForXIndex(xIndex); + final float yVals[] = dataSet.getYValuesForXPos(xVal); for (float yVal : yVals) { pts[1] = yVal; diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/Highlight.java b/MPChartLib/src/com/github/mikephil/charting/highlight/Highlight.java index 7ebd5d0193..4bdd840d7c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/Highlight.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/Highlight.java @@ -8,11 +8,11 @@ */ public class Highlight { - /** the x-index of the highlighted value */ - private int mXIndex; + /** the x-value of the highlighted value */ + private float mX = Float.NaN; /** the y-value of the highlighted value */ - private float mValue = Float.NaN; + private float mY = Float.NaN; /** the index of the data object - in case it refers to more than one */ private int mDataIndex; @@ -29,29 +29,29 @@ public class Highlight { /** * constructor * - * @param x the index of the highlighted value on the x-axis - * @param value the y-value of the highlighted value + * @param x the x-value of the highlighted value + * @param y the y-value of the highlighted value * @param dataIndex the index of the Data the highlighted value belongs to * @param dataSetIndex the index of the DataSet the highlighted value belongs to */ - public Highlight(int x, float value, int dataIndex, int dataSetIndex) { - this.mXIndex = x; - this.mValue = value; + public Highlight(float x, float y, int dataIndex, int dataSetIndex) { + this.mX = x; + this.mY = y; this.mDataIndex = dataIndex; this.mDataSetIndex = dataSetIndex; } /** * Constructor, only used for stacked-barchart. * - * @param x the index of the highlighted value on the x-axis - * @param value the y-value of the highlighted value + * @param x the x-value of the highlighted value on the x-axis + * @param y the y-value of the highlighted value * @param dataIndex the index of the Data the highlighted value belongs to * @param dataSetIndex the index of the DataSet the highlighted value belongs to * @param stackIndex references which value of a stacked-bar entry has been * selected */ - public Highlight(int x, float value, int dataIndex, int dataSetIndex, int stackIndex) { - this(x, value, dataIndex, dataSetIndex); + public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackIndex) { + this(x, y, dataIndex, dataSetIndex); mStackIndex = stackIndex; } @@ -59,15 +59,15 @@ public Highlight(int x, float value, int dataIndex, int dataSetIndex, int stackI * Constructor, only used for stacked-barchart. * * @param x the index of the highlighted value on the x-axis - * @param value the y-value of the highlighted value + * @param y the y-value of the highlighted value * @param dataIndex the index of the Data the highlighted value belongs to * @param dataSetIndex the index of the DataSet the highlighted value belongs to * @param stackIndex references which value of a stacked-bar entry has been * selected * @param range the range the selected stack-value is in */ - public Highlight(int x, float value, int dataIndex, int dataSetIndex, int stackIndex, Range range) { - this(x, value, dataIndex, dataSetIndex, stackIndex); + public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackIndex, Range range) { + this(x, y, dataIndex, dataSetIndex, stackIndex); this.mRange = range; } @@ -82,12 +82,12 @@ public Highlight(int x, int dataSetIndex) { } /** - * returns the index of the highlighted value on the x-axis + * returns the x-value of the highlighted value * * @return */ - public int getXIndex() { - return mXIndex; + public float getX() { + return mX; } /** @@ -95,8 +95,8 @@ public int getXIndex() { * * @return */ - public float getValue() { - return mValue; + public float getY() { + return mY; } /** @@ -136,7 +136,7 @@ public Range getRange() { } /** - * returns true if this highlight object is equal to the other (compares + * Returns true if this highlight object is equal to the other (compares * xIndex and dataSetIndex) * * @param h @@ -147,7 +147,7 @@ public boolean equalTo(Highlight h) { if (h == null) return false; else { - if (this.mDataSetIndex == h.mDataSetIndex && this.mXIndex == h.mXIndex + if (this.mDataSetIndex == h.mDataSetIndex && this.mX == h.mX && this.mStackIndex == h.mStackIndex) return true; else @@ -157,7 +157,7 @@ public boolean equalTo(Highlight h) { @Override public String toString() { - return "Highlight, xIndex: " + mXIndex + ", dataSetIndex: " + mDataSetIndex + return "Highlight, x: " + mX + "y: " + mY + ", dataSetIndex: " + mDataSetIndex + ", stackIndex (only stacked barentry): " + mStackIndex; } } diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java index cdd6387cd8..ac857f3b51 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java @@ -20,7 +20,7 @@ public Highlight getHighlight(float x, float y) { BarData barData = mChart.getBarData(); - final int xIndex = getXIndex(x); + final float xVal = getXForTouch(x); final float baseNoSpace = getBase(x); final int setCount = barData.getDataSetCount(); int dataSetIndex = ((int)baseNoSpace) % setCount; @@ -31,7 +31,7 @@ public Highlight getHighlight(float x, float y) { dataSetIndex = setCount - 1; } - SelectionDetail selectionDetail = getSelectionDetail(xIndex, y, dataSetIndex); + SelectionDetail selectionDetail = getSelectionDetail(xVal, y, dataSetIndex); if (selectionDetail == null) return null; @@ -46,12 +46,12 @@ public Highlight getHighlight(float x, float y) { return getStackedHighlight(selectionDetail, set, - xIndex, + xVal, pts[0]); } return new Highlight( - xIndex, + xVal, selectionDetail.value, selectionDetail.dataIndex, selectionDetail.dataSetIndex, @@ -59,7 +59,7 @@ public Highlight getHighlight(float x, float y) { } @Override - protected int getXIndex(float x) { + protected float getXForTouch(float x) { if (!mChart.getBarData().isGrouped()) { diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java index 3c8cf5b119..5e62fc9224 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -83,15 +83,15 @@ public interface IDataSet { T getEntryForXPos(float xPos); /** - * Returns all Entry objects found at the given xIndex with binary - * search. An empty array if no Entry object at that index. + * Returns all Entry objects found at the given xPos with binary + * search. An empty array if no Entry object at that xPos. * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xIndex + * @param xPos * @return */ - List getEntriesForXIndex(int xIndex); + List getEntriesForXPos(float xPos); /** * Returns the Entry object found at the given index (NOT xIndex) in the values array. @@ -124,26 +124,26 @@ public interface IDataSet { int getEntryIndex(T e); /** - * Returns the value of the Entry object at the given xIndex. Returns - * Float.NaN if no value is at the given x-index. INFORMATION: This method + * Returns the value of the Entry object at the given xVal. Returns + * Float.NaN if no value is at the given xVal. INFORMATION: This method * does calculations at runtime. Do not over-use in performance critical * situations. * - * @param xIndex + * @param xVal * @return */ - float getYValForXIndex(int xIndex); + float getYValueForXValue(float xVal); /** - * Returns all of the y values of the Entry objects at the given xIndex. Returns - * Float.NaN if no value is at the given x-index. INFORMATION: This method + * Returns all of the y values of the Entry objects at the given xPos. Returns + * Float.NaN if no value is at the given xPos. INFORMATION: This method * does calculations at runtime. Do not over-use in performance critical * situations. * - * @param xIndex + * @param xPos * @return */ - float[] getYValsForXIndex(int xIndex); + float[] getYValuesForXPos(float xPos); /** * This method returns the actual diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java index ecc30e9f84..cfdba02418 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -3,9 +3,7 @@ import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Matrix; import android.graphics.Paint; -import android.graphics.Path; import android.graphics.RectF; import com.github.mikephil.charting.animation.ChartAnimator; @@ -169,22 +167,25 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { } } - /** - * Prepares a bar for being highlighted. - * - * @param x the x-position - * @param y1 the y1-position - * @param y2 the y2-position - * @param barspaceHalf the space between bars - * @param trans - */ - protected void prepareBarHighlight(float x, float y1, float y2, float barspaceHalf, - Transformer trans) { + protected void prepareBarHighlight(float y1, float y2, float interval, int entryIndex, int dataSetIndex, int + dataSetCount, float barSpace, float groupSpace, Transformer trans) { + + float barWidth = interval / dataSetCount; + + float groupSpaceWidth = dataSetCount <= 1 ? 0 : barWidth * groupSpace; + float newInterval = (interval - groupSpaceWidth); + float newBarWidth = newInterval / dataSetCount; - float barWidth = 0.5f; + float barSpaceWidth = newBarWidth * barSpace; + float barSpaceWidthHalf = barSpaceWidth / 2f; - float left = x - barWidth + barspaceHalf; - float right = x + barWidth - barspaceHalf; + float groupSpaceWidthHalf = groupSpaceWidth / 2f; + float dataSetSpace = dataSetCount <= 1 ? 0 : (newInterval / dataSetCount) * dataSetIndex; + + float x = interval * entryIndex + dataSetSpace; + + float left = x + groupSpaceWidthHalf + barSpaceWidthHalf; + float right = left + newBarWidth - barSpaceWidth; float top = y1; float bottom = y2; @@ -284,7 +285,8 @@ public void drawValues(Canvas c) { continue; drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, - buffer.buffer[bufferIndex + 1] + (entry.getY() >= 0 ? posOffset : negOffset), color); + buffer.buffer[bufferIndex + 1] + (entry.getY() >= 0 ? posOffset : negOffset), + color); // draw stack values } else { @@ -361,30 +363,27 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - float barspaceHalf = set.getBarSpace() / 2f; - Transformer trans = mChart.getTransformer(set.getAxisDependency()); mHighlightPaint.setColor(set.getHighLightColor()); mHighlightPaint.setAlpha(set.getHighLightAlpha()); - int index = high.getXIndex(); + float x = high.getX(); // check outofbounds - if (index >= 0 - && index < (mChart.getXChartMax() * mAnimator.getPhaseX()) / setCount) { + if (x >= 0 + && x < (mChart.getXChartMax() * mAnimator.getPhaseX()) / setCount) { - BarEntry e = set.getEntryForXPos(index); + BarEntry e = set.getEntryForXPos(x); + int entryIndex = set.getEntryIndex(e); - if (e == null || e.getX() != index) + if (e == null || e.getX() != x) continue; - float groupspace = barData.getGroupSpace(); + float interval = mChart.getXRange() / set.getEntryCount(); + float groupSpace = barData.getGroupSpace(); boolean isStack = high.getStackIndex() < 0 ? false : true; - - // calculate the correct x-position - float x = index * setCount + dataSetIndex + groupspace / 2f - + groupspace * index; + float barSpace = set.getBarSpace(); final float y1; final float y2; @@ -397,35 +396,35 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { y2 = 0.f; } - prepareBarHighlight(x, y1, y2, barspaceHalf, trans); + prepareBarHighlight(y1, y2, interval, entryIndex, dataSetIndex, setCount, barSpace, groupSpace, trans); c.drawRect(mBarRect, mHighlightPaint); - if (mChart.isDrawHighlightArrowEnabled()) { - - mHighlightPaint.setAlpha(255); - - // distance between highlight arrow and bar - float offsetY = mAnimator.getPhaseY() * 0.07f; - - float[] values = new float[9]; - trans.getPixelToValueMatrix().getValues(values); - final float xToYRel = Math.abs( - values[Matrix.MSCALE_Y] / values[Matrix.MSCALE_X]); - - final float arrowWidth = set.getBarSpace() / 2.f; - final float arrowHeight = arrowWidth * xToYRel; - - final float yArrow = (y1 > -y2 ? y1 : y1) * mAnimator.getPhaseY(); - - Path arrow = new Path(); - arrow.moveTo(x + 0.4f, yArrow + offsetY); - arrow.lineTo(x + 0.4f + arrowWidth, yArrow + offsetY - arrowHeight); - arrow.lineTo(x + 0.4f + arrowWidth, yArrow + offsetY + arrowHeight); - - trans.pathValueToPixel(arrow); - c.drawPath(arrow, mHighlightPaint); - } +// if (mChart.isDrawHighlightArrowEnabled()) { +// +// mHighlightPaint.setAlpha(255); +// +// // distance between highlight arrow and bar +// float offsetY = mAnimator.getPhaseY() * 0.07f; +// +// float[] values = new float[9]; +// trans.getPixelToValueMatrix().getValues(values); +// final float xToYRel = Math.abs( +// values[Matrix.MSCALE_Y] / values[Matrix.MSCALE_X]); +// +// final float arrowWidth = set.getBarSpace() / 2.f; +// final float arrowHeight = arrowWidth * xToYRel; +// +// final float yArrow = (y1 > -y2 ? y1 : y1) * mAnimator.getPhaseY(); +// +// Path arrow = new Path(); +// arrow.moveTo(x + 0.4f, yArrow + offsetY); +// arrow.lineTo(x + 0.4f + arrowWidth, yArrow + offsetY - arrowHeight); +// arrow.lineTo(x + 0.4f + arrowWidth, yArrow + offsetY + arrowHeight); +// +// trans.pathValueToPixel(arrow); +// c.drawPath(arrow, mHighlightPaint); +// } } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index 384e1e5825..a1d9521a55 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -9,7 +9,6 @@ import com.github.mikephil.charting.data.BubbleData; import com.github.mikephil.charting.data.BubbleEntry; import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; @@ -225,7 +224,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { continue; final BubbleEntry entry = (BubbleEntry) bubbleData.getEntryForHighlight(high); - if (entry == null || entry.getX() != high.getXIndex()) + if (entry == null || entry.getX() != high.getX()) continue; float low = mChart.getLowestVisibleX(); @@ -271,7 +270,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) break; - if (high.getXIndex() < minx || high.getXIndex() >= maxx) + if (high.getX() < minx || high.getX() >= maxx) continue; final int originalColor = dataSet.getColor((int) entry.getX()); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index ccef5a608d..b6d567797b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -326,7 +326,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { dataSetIndex < maxDataSetIndex; dataSetIndex++) { - int xIndex = high.getXIndex(); // get the + float x = high.getX(); // get the // x-position ICandleDataSet set = mChart.getCandleData().getDataSetByIndex(dataSetIndex); @@ -334,9 +334,9 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - CandleEntry e = set.getEntryForXPos(xIndex); + CandleEntry e = set.getEntryForXPos(x); - if (e == null || e.getX() != xIndex) + if (e == null || e.getX() != x) continue; float lowValue = e.getLow() * mAnimator.getPhaseY(); @@ -344,7 +344,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { float y = (lowValue + highValue) / 2f; float[] pts = new float[]{ - xIndex, y + x, y }; mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index 524b78a236..b27c5ebdab 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -268,7 +268,6 @@ protected void drawValue(Canvas c, String valueText, float x, float y, int color c.drawText(valueText, x, y, mValuePaint); } - @Override protected void prepareBarHighlight(float x, float y1, float y2, float barspaceHalf, Transformer trans) { diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java index da1dfba60a..1b8ab83c10 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -738,13 +738,13 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - int xIndex = high.getXIndex(); // get the + float x = high.getX(); // get the // x-position - if (xIndex > mChart.getXChartMax() * mAnimator.getPhaseX()) + if (x > mChart.getXChartMax() * mAnimator.getPhaseX()) continue; - final float yVal = set.getYValForXIndex(xIndex); + final float yVal = set.getYValueForXValue(x); if (Float.isNaN(yVal)) continue; @@ -753,7 +753,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { // y-position float[] pts = new float[]{ - xIndex, y + x, y }; mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java index 840e0a7007..962ac88eaa 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -18,6 +18,7 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; @@ -693,14 +694,17 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { for (int i = 0; i < indices.length; i++) { // get the index to highlight - int xIndex = indices[i].getXIndex(); - if (xIndex >= drawAngles.length) + float x = indices[i].getX(); + + if (x >= drawAngles.length) continue; IPieDataSet set = mChart.getData() .getDataSetByIndex(indices[i] .getDataSetIndex()); + int entryIndex = set.getEntryIndex(x, DataSet.Rounding.CLOSEST); + if (set == null || !set.isHighlightEnabled()) continue; @@ -713,14 +717,14 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } } - if (xIndex == 0) + if (x == 0) angle = 0.f; else - angle = absoluteAngles[xIndex - 1] * phaseX; + angle = absoluteAngles[entryIndex - 1] * phaseX; final float sliceSpace = visibleAngleCount <= 1 ? 0.f : set.getSliceSpace(); - float sliceAngle = drawAngles[xIndex]; + float sliceAngle = drawAngles[entryIndex]; float innerRadius = userInnerRadius; float shift = set.getSelectionShift(); @@ -730,7 +734,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; - mRenderPaint.setColor(set.getColor(xIndex)); + mRenderPaint.setColor(set.getColor(entryIndex)); final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? 0.f : diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java index 206a85add2..30f3ef33ac 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java @@ -270,10 +270,10 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { continue; // get the index to highlight - int xIndex = indices[i].getXIndex(); + float x = indices[i].getX(); - Entry e = set.getEntryForXPos(xIndex); - if (e == null || e.getX() != xIndex) + Entry e = set.getEntryForXPos(x); + if (e == null || e.getX() != x) continue; int j = set.getEntryIndex(e); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index b5365575f3..caf9650f3e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -397,21 +397,21 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - int xIndex = high.getXIndex(); // get the + float x = high.getX(); // get the // x-position - if (xIndex > mChart.getXChartMax() * mAnimator.getPhaseX()) + if (x > mChart.getXChartMax() * mAnimator.getPhaseX()) continue; - final float yVal = set.getYValForXIndex(xIndex); + final float yVal = set.getYValueForXValue(x); if (Float.isNaN(yVal)) continue; float y = yVal * mAnimator.getPhaseY(); float[] pts = new float[]{ - xIndex, y + x, y }; mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java b/MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java index 6632c23d60..31fbb10805 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java @@ -61,7 +61,7 @@ public static List loadEntriesFromFile(String path) { vals[i] = Float.parseFloat(split[i]); } - entries.add(new BarEntry(vals, Integer.parseInt(split[split.length - 1]))); + entries.add(new BarEntry(Integer.parseInt(split[split.length - 1]), vals)); } } } catch (IOException e) { @@ -131,7 +131,7 @@ public static List loadEntriesFromAssets(AssetManager am, String path) { vals[i] = Float.parseFloat(split[i]); } - entries.add(new BarEntry(vals, Integer.parseInt(split[split.length - 1]))); + entries.add(new BarEntry(Integer.parseInt(split[split.length - 1]), vals)); } line = reader.readLine(); } From 530ab11157799cdca486fc1ee94ab0f17e4358ff Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 31 May 2016 08:56:40 +0200 Subject: [PATCH 131/606] Work on highlighting --- MPChartExample/build.gradle | 3 + .../mpchartexample/PieChartActivity.java | 2 +- .../mpchartexample/ScatterChartActivity.java | 6 +- .../mpchartexample/ScrollViewActivity.java | 2 +- .../mpchartexample/test/DataSetTest.java | 33 ++++ .../charting/highlight/BarHighlighter.java | 4 +- .../charting/highlight/ChartHighlighter.java | 170 +++++++++--------- .../highlight/HorizontalBarHighlighter.java | 2 +- .../charting/utils/SelectionDetail.java | 8 +- .../github/mikephil/charting/utils/Utils.java | 29 +-- 10 files changed, 139 insertions(+), 120 deletions(-) create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/test/DataSetTest.java diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 12d3a86424..4c549106e6 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -8,6 +8,7 @@ android { targetSdkVersion 23 versionCode 51 versionName '2.2.5' + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" sourceSets { main { @@ -57,4 +58,6 @@ dependencies { compile 'com.android.support:appcompat-v7:23.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.0' + + testCompile 'junit:junit:4.12' } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index dc94027e12..601a4e6140 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -193,7 +193,7 @@ private void setData(int count, float range) { // xIndex (even if from different DataSets), since no values can be // drawn above each other. for (int i = 0; i < count + 1; i++) { - yVals1.add(new Entry((float) (Math.random() * mult) + mult / 5, i)); + yVals1.add(new Entry(i, (float) (Math.random() * mult) + mult / 5)); } ArrayList xVals = new ArrayList(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index 5ff44fb5a1..2614a015fd 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -174,17 +174,17 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { for (int i = 0; i < mSeekBarX.getProgress(); i++) { float val = (float) (Math.random() * mSeekBarY.getProgress()) + 3; - yVals1.add(new Entry(val, i)); + 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(val, i)); + 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(val, i)); + yVals3.add(new Entry(i+0.66f, val)); } // create a dataset and give it a type diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java index 34f2140a04..1dcff1efe1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java @@ -56,7 +56,7 @@ private void setData(int count) { for (int i = 0; i < count; i++) { float val = (float) (Math.random() * count) + 15; - yVals.add(new BarEntry((int) val, i)); + yVals.add(new BarEntry(i, (int) val)); xVals.add(new XAxisValue(i, (int) val + "")); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/test/DataSetTest.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/test/DataSetTest.java new file mode 100644 index 0000000000..8cf89a6101 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/test/DataSetTest.java @@ -0,0 +1,33 @@ +package com.xxmassdeveloper.mpchartexample.test; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.ScatterDataSet; + +//import org.junit.Test; +// +//import java.util.ArrayList; +//import java.util.List; +//import static org.junit.Assert.*; +// +///** +// * Created by philipp on 30/05/16. +// */ +//public class DataSetTest { +// +// +// @Test +// public void testGetEntryForXPos() { +// +// List vals = new ArrayList<>(); +// vals.add(new Entry(10, 10)); +// vals.add(new Entry(15, 5)); +// vals.add(new Entry(17, 10)); +// +// ScatterDataSet set = new ScatterDataSet(vals, ""); +// +// Entry e1 = set.getEntryForXPos(13); +// assertEquals(15, e1.getX(), 0.01f); +// assertEquals(5, e1.getY(), 0.01f); +// } +//} + diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java index 0af151d3d6..9ca0e66a05 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java @@ -33,7 +33,7 @@ public Highlight getHighlight(float x, float y) { dataSetIndex = setCount - 1; } - SelectionDetail selectionDetail = getSelectionDetail(xVal, y, dataSetIndex); + SelectionDetail selectionDetail = getSelectionDetail(xVal, x, y, dataSetIndex); if (selectionDetail == null) return null; @@ -84,7 +84,7 @@ else if (xIndex >= valCount) } @Override - protected SelectionDetail getSelectionDetail(float xVal, float y, int dataSetIndex) { + protected SelectionDetail getSelectionDetail(float xVal, float x, float y, int dataSetIndex) { dataSetIndex = Math.max(dataSetIndex, 0); diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java index 1cd1466d25..a2e0f19deb 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java @@ -4,6 +4,7 @@ import java.util.List; import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; import com.github.mikephil.charting.utils.SelectionDetail; @@ -14,118 +15,115 @@ */ public class ChartHighlighter { - /** instance of the data-provider */ - protected T mChart; + /** + * instance of the data-provider + */ + protected T mChart; - public ChartHighlighter(T chart) { - this.mChart = chart; - } + public ChartHighlighter(T chart) { + this.mChart = chart; + } - /** - * Returns a Highlight object corresponding to the given x- and y- touch positions in pixels. - * - * @param x - * @param y - * @return - */ - public Highlight getHighlight(float x, float y) { + /** + * Returns a Highlight object corresponding to the given x- and y- touch positions in pixels. + * + * @param x + * @param y + * @return + */ + public Highlight getHighlight(float x, float y) { - float xVal = getXForTouch(x); + float xVal = getXForTouch(x); - SelectionDetail selectionDetail = getSelectionDetail(xVal, y, -1); - if (selectionDetail == null) - return null; + SelectionDetail selectionDetail = getSelectionDetail(xVal, x, y, -1); + if (selectionDetail == null) + return null; - return new Highlight(xVal, - selectionDetail.value, - selectionDetail.dataIndex, - selectionDetail.dataSetIndex); - } + return new Highlight(xVal, + selectionDetail.value, + selectionDetail.dataIndex, + selectionDetail.dataSetIndex); + } - /** - * Returns the corresponding x-index for a given touch-position in pixels. - * - * @param x - * @return - */ - protected float getXForTouch(float x) { + /** + * Returns the corresponding x-index for a given touch-position in pixels. + * + * @param x + * @return + */ + protected float getXForTouch(float x) { - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; + // create an array of the touch-point + float[] pts = new float[2]; + pts[0] = x; - // take any transformer to determine the x-axis value - mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); + // take any transformer to determine the x-axis value + mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); - return Math.round(pts[0]); - } + return Math.round(pts[0]); + } - /** - * Returns the corresponding SelectionDetail for a given xVal and y-touch position in pixels. - * - * @param xVal - * @param y - * @param dataSetIndex - * @return - */ - protected SelectionDetail getSelectionDetail(float xVal, float y, int dataSetIndex) { + /** + * Returns the corresponding SelectionDetail for a given xVal and y-touch position in pixels. + * + * @param xVal + * @param y + * @param dataSetIndex + * @return + */ + protected SelectionDetail getSelectionDetail(float xVal, float x, float y, int dataSetIndex) { - List valsAtIndex = getSelectionDetailsAtIndex(xVal, dataSetIndex); + List valsAtIndex = getSelectionDetailsAtIndex(xVal, dataSetIndex); - float leftdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.LEFT); - float rightdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.RIGHT); + float leftdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.LEFT); + float rightdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.RIGHT); - YAxis.AxisDependency axis = leftdist < rightdist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT; + YAxis.AxisDependency axis = leftdist < rightdist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT; - SelectionDetail detail = Utils.getClosestSelectionDetailByPixelY(valsAtIndex, y, axis); + SelectionDetail detail = Utils.getClosestSelectionDetailByPixel(valsAtIndex, x, y, axis); - return detail; - } + return detail; + } - /** - * Returns a list of SelectionDetail object corresponding to the given xIndex. - * - * @param xVal - * @param dataSetIndex dataSet index to look at. -1 if unspecified. - * @return - */ - protected List getSelectionDetailsAtIndex(float xVal, int dataSetIndex) { + /** + * Returns a list of SelectionDetail object corresponding to the given xIndex. + * + * @param xVal + * @param dataSetIndex dataSet index to look at. -1 if unspecified. + * @return + */ + protected List getSelectionDetailsAtIndex(float xVal, int dataSetIndex) { - List vals = new ArrayList(); + List vals = new ArrayList(); - if (mChart.getData() == null) return vals; + if (mChart.getData() == null) return vals; - float[] pts = new float[2]; + float[] pts = new float[2]; - for (int i = 0, dataSetCount = mChart.getData().getDataSetCount(); - i < dataSetCount; - i++) { + for (int i = 0, dataSetCount = mChart.getData().getDataSetCount(); i < dataSetCount; i++) { - if (dataSetIndex > -1 && dataSetIndex != i) - continue; + if (dataSetIndex > -1 && dataSetIndex != i) + continue; - IDataSet dataSet = mChart.getData().getDataSetByIndex(i); + IDataSet dataSet = mChart.getData().getDataSetByIndex(i); - // dont include datasets that cannot be highlighted - if (!dataSet.isHighlightEnabled()) - continue; + // dont include datasets that cannot be highlighted + if (!dataSet.isHighlightEnabled()) + continue; - // extract all y-values from all DataSets at the given x-index - final float[] yVals = dataSet.getYValuesForXPos(xVal); - for (float yVal : yVals) { - if (Float.isNaN(yVal)) - continue; + // extract all y-values from all DataSets at the given x-index + final Entry e = dataSet.getEntryForXPos(xVal); - pts[1] = yVal; + pts[0] = e.getX(); + pts[1] = e.getY(); - mChart.getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); + mChart.getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); - if (!Float.isNaN(pts[1])) { - vals.add(new SelectionDetail(pts[1], yVal, i, dataSet)); - } - } - } + if (!Float.isNaN(pts[1])) { + vals.add(new SelectionDetail(pts[0], pts[1], e.getY(), i, dataSet)); + } + } - return vals; - } + return vals; + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java index ac857f3b51..22a8d7356d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java +++ b/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java @@ -31,7 +31,7 @@ public Highlight getHighlight(float x, float y) { dataSetIndex = setCount - 1; } - SelectionDetail selectionDetail = getSelectionDetail(xVal, y, dataSetIndex); + SelectionDetail selectionDetail = getSelectionDetail(xVal, x, y, dataSetIndex); if (selectionDetail == null) return null; diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/SelectionDetail.java b/MPChartLib/src/com/github/mikephil/charting/utils/SelectionDetail.java index 5f78528f4c..2aaa786e90 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/SelectionDetail.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/SelectionDetail.java @@ -13,12 +13,14 @@ public class SelectionDetail { public float y; + public float x; public float value; public int dataIndex; public int dataSetIndex; public IDataSet dataSet; - public SelectionDetail(float y, float value, int dataIndex, int dataSetIndex, IDataSet set) { + public SelectionDetail(float x, float y, float value, int dataIndex, int dataSetIndex, IDataSet set) { + this.x = x; this.y = y; this.value = value; this.dataIndex = dataIndex; @@ -26,8 +28,8 @@ public SelectionDetail(float y, float value, int dataIndex, int dataSetIndex, ID this.dataSet = set; } - public SelectionDetail(float y, float value, int dataSetIndex, IDataSet set) { - this(y, value, 0, dataSetIndex, set); + public SelectionDetail(float x, float y, float value, int dataSetIndex, IDataSet set) { + this(x, y, value, 0, dataSetIndex, set); } public SelectionDetail(float value, int dataSetIndex, IDataSet set) { diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java index caaef8153c..1abdc46b06 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java @@ -399,24 +399,6 @@ public static int getClosestDataSetIndexByValue(List valsAtInde return sel.dataSetIndex; } - /** - * Returns the index of the DataSet that contains the closest value on the - * y-axis. This is needed for highlighting. This will return -Integer.MAX_VALUE if failure. - * - * @param valsAtIndex all the values at a specific index - * @return - */ - public static int getClosestDataSetIndexByPixelY(List valsAtIndex, float y, - AxisDependency axis) { - - SelectionDetail sel = getClosestSelectionDetailByPixelY(valsAtIndex, y, axis); - - if (sel == null) - return -Integer.MAX_VALUE; - - return sel.dataSetIndex; - } - /** * Returns the SelectionDetail of the DataSet that contains the closest value on the * y-axis. @@ -456,9 +438,9 @@ public static SelectionDetail getClosestSelectionDetailByValue( * @param valsAtIndex all the values at a specific index * @return */ - public static SelectionDetail getClosestSelectionDetailByPixelY( + public static SelectionDetail getClosestSelectionDetailByPixel( List valsAtIndex, - float y, + float x, float y, AxisDependency axis) { SelectionDetail closest = null; @@ -470,10 +452,11 @@ public static SelectionDetail getClosestSelectionDetailByPixelY( if (axis == null || sel.dataSet.getAxisDependency() == axis) { - float cdistance = Math.abs(sel.y - y); - if (cdistance < distance) { + float cDistance = (float) Math.hypot(x - sel.x, y - sel.y); + + if (cDistance < distance) { closest = sel; - distance = cdistance; + distance = cDistance; } } } From cc81a21c6415ef931a6ded7d0a76626d7c2f6b18 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 31 May 2016 09:34:11 +0200 Subject: [PATCH 132/606] Restructure project from eclipse to android studio file structure --- MPChartExample/build.gradle | 2 - .../mpchartexample/test/DataSetTest.java | 33 ----------- MPChartLib/build.gradle | 14 ++--- MPChartLib/{ => src/main}/AndroidManifest.xml | 0 .../charting/animation/ChartAnimator.java | 0 .../mikephil/charting/animation/Easing.java | 0 .../charting/animation/EasingFunction.java | 0 .../charting/buffer/AbstractBuffer.java | 0 .../mikephil/charting/buffer/BarBuffer.java | 0 .../charting/buffer/HorizontalBarBuffer.java | 0 .../charting/buffer/ScatterBuffer.java | 0 .../mikephil/charting/charts/BarChart.java | 0 .../charting/charts/BarLineChartBase.java | 0 .../mikephil/charting/charts/BubbleChart.java | 0 .../charting/charts/CandleStickChart.java | 0 .../mikephil/charting/charts/Chart.java | 0 .../charting/charts/CombinedChart.java | 0 .../charting/charts/HorizontalBarChart.java | 0 .../mikephil/charting/charts/LineChart.java | 0 .../mikephil/charting/charts/PieChart.java | 0 .../charting/charts/PieRadarChartBase.java | 0 .../mikephil/charting/charts/RadarChart.java | 0 .../charting/charts/ScatterChart.java | 0 .../charting/components/AxisBase.java | 0 .../charting/components/ComponentBase.java | 0 .../mikephil/charting/components/Legend.java | 0 .../charting/components/LimitLine.java | 0 .../charting/components/MarkerView.java | 0 .../mikephil/charting/components/XAxis.java | 0 .../mikephil/charting/components/YAxis.java | 0 .../mikephil/charting/data/BarData.java | 0 .../mikephil/charting/data/BarDataSet.java | 0 .../mikephil/charting/data/BarEntry.java | 0 .../data/BarLineScatterCandleBubbleData.java | 0 .../BarLineScatterCandleBubbleDataSet.java | 0 .../mikephil/charting/data/BaseDataSet.java | 0 .../mikephil/charting/data/BubbleData.java | 0 .../mikephil/charting/data/BubbleDataSet.java | 0 .../mikephil/charting/data/BubbleEntry.java | 0 .../mikephil/charting/data/CandleData.java | 0 .../mikephil/charting/data/CandleDataSet.java | 0 .../mikephil/charting/data/CandleEntry.java | 0 .../mikephil/charting/data/ChartData.java | 0 .../mikephil/charting/data/CombinedData.java | 0 .../mikephil/charting/data/DataSet.java | 41 ++++++------- .../github/mikephil/charting/data/Entry.java | 0 .../mikephil/charting/data/LineData.java | 0 .../mikephil/charting/data/LineDataSet.java | 0 .../charting/data/LineRadarDataSet.java | 0 .../data/LineScatterCandleRadarDataSet.java | 0 .../mikephil/charting/data/PieData.java | 0 .../mikephil/charting/data/PieDataSet.java | 0 .../mikephil/charting/data/RadarData.java | 0 .../mikephil/charting/data/RadarDataSet.java | 0 .../mikephil/charting/data/ScatterData.java | 0 .../charting/data/ScatterDataSet.java | 0 .../mikephil/charting/data/XAxisValue.java | 0 .../charting/data/filter/Approximator.java | 0 ...ealmBarLineScatterCandleBubbleDataSet.java | 0 .../data/realm/base/RealmBaseDataSet.java | 0 .../realm/base/RealmLineRadarDataSet.java | 0 .../RealmLineScatterCandleRadarDataSet.java | 0 .../charting/data/realm/base/RealmUtils.java | 0 .../realm/implementation/RealmBarData.java | 0 .../realm/implementation/RealmBarDataSet.java | 0 .../realm/implementation/RealmBubbleData.java | 0 .../implementation/RealmBubbleDataSet.java | 0 .../realm/implementation/RealmCandleData.java | 0 .../implementation/RealmCandleDataSet.java | 0 .../realm/implementation/RealmLineData.java | 0 .../implementation/RealmLineDataSet.java | 0 .../realm/implementation/RealmPieData.java | 0 .../realm/implementation/RealmPieDataSet.java | 0 .../realm/implementation/RealmRadarData.java | 0 .../implementation/RealmRadarDataSet.java | 0 .../implementation/RealmScatterData.java | 0 .../implementation/RealmScatterDataSet.java | 0 .../DrawingDataSetNotCreatedException.java | 0 .../charting/formatter/ColorFormatter.java | 0 .../formatter/DefaultFillFormatter.java | 0 .../formatter/DefaultValueFormatter.java | 0 .../formatter/DefaultXAxisValueFormatter.java | 0 .../formatter/DefaultYAxisValueFormatter.java | 0 .../charting/formatter/FillFormatter.java | 0 .../formatter/LargeValueFormatter.java | 0 .../charting/formatter/PercentFormatter.java | 0 .../formatter/StackedValueFormatter.java | 0 .../charting/formatter/ValueFormatter.java | 0 .../formatter/XAxisValueFormatter.java | 0 .../formatter/YAxisValueFormatter.java | 0 .../charting/highlight/BarHighlighter.java | 0 .../charting/highlight/ChartHighlighter.java | 0 .../highlight/CombinedHighlighter.java | 0 .../charting/highlight/Highlight.java | 0 .../highlight/HorizontalBarHighlighter.java | 0 .../mikephil/charting/highlight/Range.java | 0 .../dataprovider/BarDataProvider.java | 0 ...arLineScatterCandleBubbleDataProvider.java | 0 .../dataprovider/BubbleDataProvider.java | 0 .../dataprovider/CandleDataProvider.java | 0 .../dataprovider/ChartInterface.java | 0 .../dataprovider/LineDataProvider.java | 0 .../dataprovider/ScatterDataProvider.java | 0 .../interfaces/datasets/IBarDataSet.java | 0 .../IBarLineScatterCandleBubbleDataSet.java | 0 .../interfaces/datasets/IBubbleDataSet.java | 0 .../interfaces/datasets/ICandleDataSet.java | 0 .../interfaces/datasets/IDataSet.java | 0 .../interfaces/datasets/ILineDataSet.java | 0 .../datasets/ILineRadarDataSet.java | 0 .../ILineScatterCandleRadarDataSet.java | 0 .../interfaces/datasets/IPieDataSet.java | 0 .../interfaces/datasets/IRadarDataSet.java | 0 .../interfaces/datasets/IScatterDataSet.java | 0 .../charting/jobs/AnimatedMoveViewJob.java | 0 .../charting/jobs/AnimatedViewPortJob.java | 0 .../charting/jobs/AnimatedZoomJob.java | 0 .../mikephil/charting/jobs/MoveViewJob.java | 0 .../mikephil/charting/jobs/ViewPortJob.java | 0 .../mikephil/charting/jobs/ZoomJob.java | 0 .../listener/BarLineChartTouchListener.java | 0 .../charting/listener/ChartTouchListener.java | 0 .../listener/OnChartGestureListener.java | 0 .../OnChartValueSelectedListener.java | 0 .../OnDrawLineChartTouchListener.java | 0 .../charting/listener/OnDrawListener.java | 0 .../listener/PieRadarChartTouchListener.java | 0 .../mikephil/charting/matrix/Vector3.java | 0 .../charting/renderer/AxisRenderer.java | 0 .../charting/renderer/BarChartRenderer.java | 0 .../renderer/BubbleChartRenderer.java | 0 .../renderer/CandleStickChartRenderer.java | 0 .../renderer/CombinedChartRenderer.java | 0 .../charting/renderer/DataRenderer.java | 0 .../renderer/HorizontalBarChartRenderer.java | 0 .../charting/renderer/LegendRenderer.java | 0 .../charting/renderer/LineChartRenderer.java | 0 .../charting/renderer/LineRadarRenderer.java | 0 .../LineScatterCandleRadarRenderer.java | 0 .../charting/renderer/PieChartRenderer.java | 0 .../charting/renderer/RadarChartRenderer.java | 0 .../mikephil/charting/renderer/Renderer.java | 0 .../renderer/ScatterChartRenderer.java | 0 .../charting/renderer/XAxisRenderer.java | 0 .../renderer/XAxisRendererBarChart.java | 0 .../XAxisRendererHorizontalBarChart.java | 0 .../renderer/XAxisRendererRadarChart.java | 0 .../charting/renderer/YAxisRenderer.java | 0 .../YAxisRendererHorizontalBarChart.java | 0 .../renderer/YAxisRendererRadarChart.java | 0 .../charting/utils/ColorTemplate.java | 0 .../charting/utils/EntryXIndexComparator.java | 0 .../github/mikephil/charting/utils/FSize.java | 0 .../mikephil/charting/utils/FileUtils.java | 0 .../mikephil/charting/utils/PointD.java | 0 .../charting/utils/SelectionDetail.java | 0 .../mikephil/charting/utils/Transformer.java | 0 .../utils/TransformerHorizontalBarChart.java | 0 .../github/mikephil/charting/utils/Utils.java | 0 .../charting/utils/ViewPortHandler.java | 0 .../mikephil/charting/test/DataSetTest.java | 59 +++++++++++++++++++ 161 files changed, 83 insertions(+), 66 deletions(-) delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/test/DataSetTest.java rename MPChartLib/{ => src/main}/AndroidManifest.xml (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/animation/ChartAnimator.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/animation/Easing.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/animation/EasingFunction.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/buffer/AbstractBuffer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/buffer/BarBuffer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/buffer/ScatterBuffer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/charts/BarChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/charts/BarLineChartBase.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/charts/BubbleChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/charts/CandleStickChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/charts/Chart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/charts/CombinedChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/charts/HorizontalBarChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/charts/LineChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/charts/PieChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/charts/PieRadarChartBase.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/charts/RadarChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/charts/ScatterChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/components/AxisBase.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/components/ComponentBase.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/components/Legend.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/components/LimitLine.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/components/MarkerView.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/components/XAxis.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/components/YAxis.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/BarData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/BarDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/BarEntry.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/BaseDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/BubbleData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/BubbleDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/BubbleEntry.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/CandleData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/CandleDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/CandleEntry.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/ChartData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/CombinedData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/DataSet.java (92%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/Entry.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/LineData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/LineDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/LineRadarDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/PieData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/PieDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/RadarData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/RadarDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/ScatterData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/ScatterDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/XAxisValue.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/filter/Approximator.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/base/RealmUtils.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/formatter/ColorFormatter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/formatter/DefaultFillFormatter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/formatter/DefaultValueFormatter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/formatter/FillFormatter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/formatter/LargeValueFormatter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/formatter/PercentFormatter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/formatter/StackedValueFormatter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/formatter/ValueFormatter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/formatter/XAxisValueFormatter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/formatter/YAxisValueFormatter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/highlight/BarHighlighter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/highlight/ChartHighlighter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/highlight/CombinedHighlighter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/highlight/Highlight.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/highlight/Range.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/datasets/IBarLineScatterCandleBubbleDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/datasets/ICandleDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/datasets/IDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/jobs/AnimatedZoomJob.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/jobs/MoveViewJob.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/jobs/ViewPortJob.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/jobs/ZoomJob.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/listener/BarLineChartTouchListener.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/listener/ChartTouchListener.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/listener/OnChartGestureListener.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/listener/OnDrawLineChartTouchListener.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/listener/OnDrawListener.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/matrix/Vector3.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/AxisRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/BarChartRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/BubbleChartRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/CombinedChartRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/DataRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/LegendRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/LineChartRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/LineRadarRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/PieChartRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/RadarChartRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/Renderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/ScatterChartRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/XAxisRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/YAxisRenderer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/utils/ColorTemplate.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/utils/EntryXIndexComparator.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/utils/FSize.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/utils/FileUtils.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/utils/PointD.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/utils/SelectionDetail.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/utils/Transformer.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/utils/Utils.java (100%) rename MPChartLib/src/{ => main/java}/com/github/mikephil/charting/utils/ViewPortHandler.java (100%) create mode 100644 MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 4c549106e6..1f6fca6575 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -58,6 +58,4 @@ dependencies { compile 'com.android.support:appcompat-v7:23.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.0' - - testCompile 'junit:junit:4.12' } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/test/DataSetTest.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/test/DataSetTest.java deleted file mode 100644 index 8cf89a6101..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/test/DataSetTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.test; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.ScatterDataSet; - -//import org.junit.Test; -// -//import java.util.ArrayList; -//import java.util.List; -//import static org.junit.Assert.*; -// -///** -// * Created by philipp on 30/05/16. -// */ -//public class DataSetTest { -// -// -// @Test -// public void testGetEntryForXPos() { -// -// List vals = new ArrayList<>(); -// vals.add(new Entry(10, 10)); -// vals.add(new Entry(15, 5)); -// vals.add(new Entry(17, 10)); -// -// ScatterDataSet set = new ScatterDataSet(vals, ""); -// -// Entry e1 = set.getEntryForXPos(13); -// assertEquals(15, e1.getX(), 0.01f); -// assertEquals(5, e1.getY(), 0.01f); -// } -//} - diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index 1c7ca0e099..af8e41792f 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -11,15 +11,6 @@ android { targetSdkVersion 23 versionCode 1 versionName '1.0' - - sourceSets { - main { - java.srcDirs = ['src'] - res.srcDirs = ['res'] - assets.srcDirs = ['assets'] - manifest.srcFile 'AndroidManifest.xml' - } - } } buildTypes { release { @@ -30,6 +21,9 @@ android { lintOptions { abortOnError false } + testOptions { + unitTests.returnDefaultValues = true // this prevents "not mocked" error + } } repositories { @@ -42,6 +36,8 @@ 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.9.5" } android.libraryVariants.all { variant -> diff --git a/MPChartLib/AndroidManifest.xml b/MPChartLib/src/main/AndroidManifest.xml similarity index 100% rename from MPChartLib/AndroidManifest.xml rename to MPChartLib/src/main/AndroidManifest.xml diff --git a/MPChartLib/src/com/github/mikephil/charting/animation/ChartAnimator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/animation/ChartAnimator.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java diff --git a/MPChartLib/src/com/github/mikephil/charting/animation/Easing.java b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/animation/Easing.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java diff --git a/MPChartLib/src/com/github/mikephil/charting/animation/EasingFunction.java b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/EasingFunction.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/animation/EasingFunction.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/animation/EasingFunction.java diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/AbstractBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/buffer/AbstractBuffer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/BarBuffer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/buffer/BarBuffer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/ScatterBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/ScatterBuffer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/buffer/ScatterBuffer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/buffer/ScatterBuffer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BubbleChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/charts/BubbleChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/CandleStickChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/charts/CandleStickChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/charts/Chart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/CombinedChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/charts/CombinedChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java diff --git a/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java diff --git a/MPChartLib/src/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/components/Legend.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java diff --git a/MPChartLib/src/com/github/mikephil/charting/components/LimitLine.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/components/LimitLine.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java diff --git a/MPChartLib/src/com/github/mikephil/charting/components/MarkerView.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/components/MarkerView.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java diff --git a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/components/XAxis.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/components/YAxis.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/BarData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/BarEntry.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/BaseDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BubbleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/BubbleData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BubbleEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleEntry.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/BubbleEntry.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleEntry.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/CandleData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/CandleEntry.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/ChartData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java similarity index 92% rename from MPChartLib/src/com/github/mikephil/charting/data/DataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java index fed5f780ea..f260339ca7 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java @@ -96,7 +96,7 @@ public void calcMinMax() { mYMax = 0.f; } - if(mXMin == Float.MAX_VALUE) { + if (mXMin == Float.MAX_VALUE) { mXMin = 0.f; mXMax = 0.f; } @@ -287,42 +287,39 @@ public T getEntryForIndex(int index) { @Override public int getEntryIndex(float xPos, Rounding rounding) { + if (mValues == null || mValues.isEmpty()) + return -1; + int low = 0; int high = mValues.size() - 1; - int closest = -1; - while (low <= high) { - int m = (high + low) / 2; + while (low < high) { + int m = (low + high) / 2; - if (xPos == mValues.get(m).getX()) { - while (m > 0 && mValues.get(m - 1).getX() == xPos) - m--; + float d1 = Math.abs(mValues.get(m).getX() - xPos); + float d2 = Math.abs(mValues.get(m + 1).getX() - xPos); - return m; - } - - if (xPos > mValues.get(m).getX()) + if (d2 <= d1) { low = m + 1; - else - high = m - 1; - - closest = m; + } else { + high = m; + } } - if (closest != -1) { - float closestXPos = mValues.get(closest).getX(); + if (high != -1) { + float closestXPos = mValues.get(high).getX(); if (rounding == Rounding.UP) { - if (closestXPos < xPos && closest < mValues.size() - 1) { - ++closest; + if (closestXPos < xPos && high < mValues.size() - 1) { + ++high; } } else if (rounding == Rounding.DOWN) { - if (closestXPos > xPos && closest > 0) { - --closest; + if (closestXPos > xPos && high > 0) { + --high; } } } - return closest; + return high; } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/data/Entry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/Entry.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/LineData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/LineData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/LineRadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/PieData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/PieData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/RadarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/RadarData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ScatterData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/ScatterData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/XAxisValue.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/XAxisValue.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/XAxisValue.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/XAxisValue.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/filter/Approximator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/filter/Approximator.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmUtils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmUtils.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmUtils.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmUtils.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java b/MPChartLib/src/main/java/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/ColorFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ColorFormatter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/formatter/ColorFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ColorFormatter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultFillFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/formatter/DefaultFillFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/formatter/DefaultValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/FillFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FillFormatter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/formatter/FillFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FillFormatter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/LargeValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/formatter/LargeValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/PercentFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/formatter/PercentFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/StackedValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/formatter/StackedValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/ValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/formatter/ValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/XAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/formatter/XAxisValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/YAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/formatter/YAxisValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/CombinedHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/highlight/CombinedHighlighter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/Highlight.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/highlight/Highlight.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/Range.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/highlight/Range.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBarLineScatterCandleBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarLineScatterCandleBubbleDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBarLineScatterCandleBubbleDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarLineScatterCandleBubbleDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ICandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ICandleDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ICandleDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ICandleDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java diff --git a/MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java diff --git a/MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java diff --git a/MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedZoomJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedZoomJob.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java diff --git a/MPChartLib/src/com/github/mikephil/charting/jobs/MoveViewJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/MoveViewJob.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/jobs/MoveViewJob.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/jobs/MoveViewJob.java diff --git a/MPChartLib/src/com/github/mikephil/charting/jobs/ViewPortJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/jobs/ViewPortJob.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java diff --git a/MPChartLib/src/com/github/mikephil/charting/jobs/ZoomJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/jobs/ZoomJob.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/ChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/listener/ChartTouchListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/OnChartGestureListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/listener/OnChartGestureListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/OnDrawLineChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawLineChartTouchListener.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/listener/OnDrawLineChartTouchListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawLineChartTouchListener.java diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/OnDrawListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawListener.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/listener/OnDrawListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawListener.java diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java diff --git a/MPChartLib/src/com/github/mikephil/charting/matrix/Vector3.java b/MPChartLib/src/main/java/com/github/mikephil/charting/matrix/Vector3.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/matrix/Vector3.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/matrix/Vector3.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/AxisRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/DataRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/DataRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineRadarRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineRadarRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/LineRadarRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineRadarRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/Renderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/Renderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ColorTemplate.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/ColorTemplate.java diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/EntryXIndexComparator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXIndexComparator.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/utils/EntryXIndexComparator.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXIndexComparator.java diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/FSize.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FSize.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/utils/FSize.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/FSize.java diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/PointD.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/utils/PointD.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/SelectionDetail.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/utils/SelectionDetail.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/utils/Utils.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java 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 new file mode 100644 index 0000000000..5e86a89e66 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java @@ -0,0 +1,59 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.ScatterDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.*; + +/** + * Created by philipp on 31/05/16. + */ +public class DataSetTest { + + + @Test + public void testGetEntryForXPos() { + + List entries = new ArrayList(); + entries.add(new Entry(10, 10)); + entries.add(new Entry(15, 5)); + entries.add(new Entry(21, 5)); + + ScatterDataSet set = new ScatterDataSet(entries, ""); + + Entry closest = set.getEntryForXPos(17, DataSet.Rounding.CLOSEST); + assertEquals(15, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXPos(17, DataSet.Rounding.DOWN); + assertEquals(15, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXPos(15, DataSet.Rounding.DOWN); + assertEquals(15, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXPos(14, DataSet.Rounding.DOWN); + assertEquals(10, closest.getX(), 0.01f); + assertEquals(10, closest.getY(), 0.01f); + + closest = set.getEntryForXPos(17, DataSet.Rounding.UP); + assertEquals(21, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXPos(21, DataSet.Rounding.UP); + assertEquals(21, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXPos(21, DataSet.Rounding.CLOSEST); + assertEquals(21, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + } +} From 0221ddfd24bac9a09fd6a56134334a44d73d6999 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 31 May 2016 09:49:31 +0200 Subject: [PATCH 133/606] Work on getting highest and lowest visible x --- .../CubicLineChartActivity.java | 2 +- .../mikephil/charting/charts/BarChart.java | 66 ++-- .../charting/charts/BarLineChartBase.java | 30 +- .../charting/charts/HorizontalBarChart.java | 370 +++++++++--------- .../mikephil/charting/utils/Transformer.java | 15 +- 5 files changed, 251 insertions(+), 232 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index ad8d10714d..ccd15f2067 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -282,7 +282,7 @@ private void setData(int count, float range) { float val = (float) (Math.random() * mult) + 20;// + (float) // ((mult * // 0.1) / 10); - yVals.add(new Entry(val, i)); + yVals.add(new Entry(i, val)); } LineDataSet set1; 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 9eed5a3f8d..0a69c03748 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 @@ -185,37 +185,37 @@ public BarData getBarData() { return mData; } - /** - * Returns the lowest x-index (value on the x-axis) that is still visible on the chart. - * - * @return - */ - @Override - public float getLowestVisibleX() { - - float step = mData.getDataSetCount(); - float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); - - float[] pts = new float[] { mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom() }; - - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return ((pts[0] <= getXChartMin()) ? 0 : (pts[0] / div) + 1); - } - - /** - * Returns the highest x-index (value on the x-axis) that is still visible on the chart. - * - * @return - */ - @Override - public float getHighestVisibleX() { - - float step = mData.getDataSetCount(); - float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); - - float[] pts = new float[] { mViewPortHandler.contentRight(), mViewPortHandler.contentBottom() }; - - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return ((pts[0] >= getXChartMax()) ? getXChartMax() / div : (pts[0] / div)); - } +// /** +// * Returns the lowest x-index (value on the x-axis) that is still visible on the chart. +// * +// * @return +// */ +// @Override +// public float getLowestVisibleX() { +// +// float step = mData.getDataSetCount(); +// float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); +// +// float[] pts = new float[] { mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom() }; +// +// getTransformer(AxisDependency.LEFT).pixelsToValue(pts); +// return ((pts[0] <= getXChartMin()) ? 0 : (pts[0] / div) + 1); +// } +// +// /** +// * Returns the highest x-index (value on the x-axis) that is still visible on the chart. +// * +// * @return +// */ +// @Override +// public float getHighestVisibleX() { +// +// float step = mData.getDataSetCount(); +// float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); +// +// float[] pts = new float[] { mViewPortHandler.contentRight(), mViewPortHandler.contentBottom() }; +// +// getTransformer(AxisDependency.LEFT).pixelsToValue(pts); +// return ((pts[0] >= getXChartMax()) ? getXChartMax() / div : (pts[0] / div)); +// } } 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 64a22aa107..714ff17a61 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 @@ -44,7 +44,8 @@ * @author Philipp Jahoda */ @SuppressLint("RtlHardcoded") -public abstract class BarLineChartBase>> +public abstract class BarLineChartBase>> extends Chart implements BarLineScatterCandleBubbleDataProvider { /** @@ -263,7 +264,7 @@ protected void onDraw(Canvas canvas) { clipRestoreCount = canvas.save(); canvas.clipRect(mViewPortHandler.getContentRect()); - + if (!mXAxis.isDrawLimitLinesBehindDataEnabled()) mXAxisRenderer.renderLimitLines(canvas); @@ -754,13 +755,16 @@ public void zoom(float scaleX, float scaleY, float xValue, float yValue, AxisDep * @param duration */ @TargetApi(11) - public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis, long duration) { + public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis, + long duration) { if (android.os.Build.VERSION.SDK_INT >= 11) { PointD origin = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - Runnable job = new AnimatedZoomJob(mViewPortHandler, this, getTransformer(axis), getAxis(axis), mXAxis.getValues().size(), scaleX, scaleY, mViewPortHandler.getScaleX(), mViewPortHandler.getScaleY(), xValue, yValue, (float) origin.x, (float) origin.y, duration); + Runnable job = new AnimatedZoomJob(mViewPortHandler, this, getTransformer(axis), getAxis(axis), mXAxis + .getValues().size(), scaleX, scaleY, mViewPortHandler.getScaleX(), mViewPortHandler.getScaleY(), + xValue, yValue, (float) origin.x, (float) origin.y, duration); addViewportJob(job); } else { @@ -1370,11 +1374,9 @@ public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float */ @Override public float getLowestVisibleX() { - float[] pts = new float[]{ - mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom() - }; - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return (pts[0] <= 0) ? 0 : (float) Math.ceil(pts[0]); + PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom()); + return (float) Math.min(mXAxis.mAxisMinimum, pos.x); } /** @@ -1385,11 +1387,9 @@ public float getLowestVisibleX() { */ @Override public float getHighestVisibleX() { - float[] pts = new float[]{ - mViewPortHandler.contentRight(), mViewPortHandler.contentBottom() - }; - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return (float) Math.min(mXAxis.mAxisMaximum, Math.floor(pts[0])); + PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentRight(), + mViewPortHandler.contentBottom()); + return (float) Math.min(mXAxis.mAxisMaximum, pos.x); } /** @@ -1617,7 +1617,7 @@ public Paint getPaint(int which) { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { - + // Saving current position of chart. float[] pts = new float[2]; if (mKeepPositionOnRotation) { 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 be59090cac..f78419ce50 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 @@ -17,217 +17,235 @@ import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer; import com.github.mikephil.charting.renderer.XAxisRendererHorizontalBarChart; import com.github.mikephil.charting.renderer.YAxisRendererHorizontalBarChart; +import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.TransformerHorizontalBarChart; import com.github.mikephil.charting.utils.Utils; /** * BarChart with horizontal bar orientation. In this implementation, x- and y-axis are switched, meaning the YAxis class * represents the horizontal values and the XAxis class represents the vertical values. - * + * * @author Philipp Jahoda */ public class HorizontalBarChart extends BarChart { - public HorizontalBarChart(Context context) { - super(context); - } + public HorizontalBarChart(Context context) { + super(context); + } - public HorizontalBarChart(Context context, AttributeSet attrs) { - super(context, attrs); - } + public HorizontalBarChart(Context context, AttributeSet attrs) { + super(context, attrs); + } - public HorizontalBarChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } + public HorizontalBarChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } - @Override - protected void init() { - super.init(); + @Override + protected void init() { + super.init(); - mLeftAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler); - mRightAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler); + mLeftAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler); + mRightAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler); - mRenderer = new HorizontalBarChartRenderer(this, mAnimator, mViewPortHandler); - setHighlighter(new HorizontalBarHighlighter(this)); + mRenderer = new HorizontalBarChartRenderer(this, mAnimator, mViewPortHandler); + setHighlighter(new HorizontalBarHighlighter(this)); - mAxisRendererLeft = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisLeft, mLeftAxisTransformer); - mAxisRendererRight = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisRight, mRightAxisTransformer); - mXAxisRenderer = new XAxisRendererHorizontalBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer, this); - } + mAxisRendererLeft = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisLeft, mLeftAxisTransformer); + mAxisRendererRight = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisRight, mRightAxisTransformer); + mXAxisRenderer = new XAxisRendererHorizontalBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer, this); + } - private RectF mOffsetsBuffer = new RectF(); + private RectF mOffsetsBuffer = new RectF(); - @Override - public void calculateOffsets() { + @Override + public void calculateOffsets() { - float offsetLeft = 0f, offsetRight = 0f, offsetTop = 0f, offsetBottom = 0f; + float offsetLeft = 0f, offsetRight = 0f, offsetTop = 0f, offsetBottom = 0f; - calculateLegendOffsets(mOffsetsBuffer); + calculateLegendOffsets(mOffsetsBuffer); - offsetLeft += mOffsetsBuffer.left; - offsetTop += mOffsetsBuffer.top; - offsetRight += mOffsetsBuffer.right; - offsetBottom += mOffsetsBuffer.bottom; + offsetLeft += mOffsetsBuffer.left; + offsetTop += mOffsetsBuffer.top; + offsetRight += mOffsetsBuffer.right; + offsetBottom += mOffsetsBuffer.bottom; - // offsets for y-labels - if (mAxisLeft.needsOffset()) { - offsetTop += mAxisLeft.getRequiredHeightSpace(mAxisRendererLeft.getPaintAxisLabels()); - } + // offsets for y-labels + if (mAxisLeft.needsOffset()) { + offsetTop += mAxisLeft.getRequiredHeightSpace(mAxisRendererLeft.getPaintAxisLabels()); + } - if (mAxisRight.needsOffset()) { - offsetBottom += mAxisRight.getRequiredHeightSpace(mAxisRendererRight.getPaintAxisLabels()); - } + if (mAxisRight.needsOffset()) { + offsetBottom += mAxisRight.getRequiredHeightSpace(mAxisRendererRight.getPaintAxisLabels()); + } - float xlabelwidth = mXAxis.mLabelRotatedWidth; + float xlabelwidth = mXAxis.mLabelRotatedWidth; - if (mXAxis.isEnabled()) { + if (mXAxis.isEnabled()) { - // offsets for x-labels - if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { + // offsets for x-labels + if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - offsetLeft += xlabelwidth; + offsetLeft += xlabelwidth; - } else if (mXAxis.getPosition() == XAxisPosition.TOP) { + } else if (mXAxis.getPosition() == XAxisPosition.TOP) { - offsetRight += xlabelwidth; + offsetRight += xlabelwidth; - } else if (mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { + } else if (mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - offsetLeft += xlabelwidth; - offsetRight += xlabelwidth; - } - } + offsetLeft += xlabelwidth; + offsetRight += xlabelwidth; + } + } - offsetTop += getExtraTopOffset(); - offsetRight += getExtraRightOffset(); - offsetBottom += getExtraBottomOffset(); - offsetLeft += getExtraLeftOffset(); + offsetTop += getExtraTopOffset(); + offsetRight += getExtraRightOffset(); + offsetBottom += getExtraBottomOffset(); + offsetLeft += getExtraLeftOffset(); - float minOffset = Utils.convertDpToPixel(mMinOffset); + float minOffset = Utils.convertDpToPixel(mMinOffset); - mViewPortHandler.restrainViewPort( - Math.max(minOffset, offsetLeft), - Math.max(minOffset, offsetTop), - Math.max(minOffset, offsetRight), - Math.max(minOffset, offsetBottom)); + mViewPortHandler.restrainViewPort( + Math.max(minOffset, offsetLeft), + Math.max(minOffset, offsetTop), + Math.max(minOffset, offsetRight), + Math.max(minOffset, offsetBottom)); - if (mLogEnabled) { - Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop + ", offsetRight: " + offsetRight + ", offsetBottom: " - + offsetBottom); - Log.i(LOG_TAG, "Content: " + mViewPortHandler.getContentRect().toString()); - } + if (mLogEnabled) { + Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop + ", offsetRight: " + + offsetRight + ", offsetBottom: " + + offsetBottom); + Log.i(LOG_TAG, "Content: " + mViewPortHandler.getContentRect().toString()); + } - prepareOffsetMatrix(); - prepareValuePxMatrix(); - } + prepareOffsetMatrix(); + prepareValuePxMatrix(); + } - @Override - protected void prepareValuePxMatrix() { - mRightAxisTransformer.prepareMatrixValuePx(mAxisRight.mAxisMinimum, mAxisRight.mAxisRange, mXAxis.mAxisRange, mXAxis.mAxisMinimum); - mLeftAxisTransformer.prepareMatrixValuePx(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisRange, mXAxis.mAxisRange, mXAxis.mAxisMinimum); - } - - @Override - protected void calcModulus() { - float[] values = new float[9]; - mViewPortHandler.getMatrixTouch().getValues(values); - - mXAxis.mAxisLabelModulus = - (int) Math.ceil((mData.getXValCount() * mXAxis.mLabelRotatedHeight) - / (mViewPortHandler.contentHeight() * values[Matrix.MSCALE_Y])); - - if (mXAxis.mAxisLabelModulus < 1) - mXAxis.mAxisLabelModulus = 1; - } - - @Override - public RectF getBarBounds(BarEntry e) { - - IBarDataSet set = mData.getDataSetForEntry(e); - - if (set == null) - return null; - - float barspace = set.getBarSpace(); - float y = e.getY(); - float x = e.getX(); - - float spaceHalf = barspace / 2f; - - float top = x - 0.5f + spaceHalf; - float bottom = x + 0.5f - spaceHalf; - float left = y >= 0 ? y : 0; - float right = y <= 0 ? y : 0; - - RectF bounds = new RectF(left, top, right, bottom); - - getTransformer(set.getAxisDependency()).rectValueToPixel(bounds); - - return bounds; - } - - @Override - public PointF getPosition(Entry e, AxisDependency axis) { - - if (e == null) - return null; - - float[] vals = new float[] { e.getY(), e.getX() }; - - getTransformer(axis).pointValuesToPixel(vals); - - return new PointF(vals[0], vals[1]); - } - - /** - * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point - * inside the BarChart. - * - * @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 - return getHighlighter().getHighlight(y, x); // switch x and y - } - - /** - * Returns the lowest x-index (value on the x-axis) that is still visible on the chart. - * - * @return - */ - @Override - public float getLowestVisibleX() { - - float step = mData.getDataSetCount(); - float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); - - float[] pts = new float[] { mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom() }; - - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return (((pts[1] <= 0) ? 0 : ((pts[1])) / div) + 1); - } - - /** - * Returns the highest x-index (value on the x-axis) that is still visible on the chart. - * - * @return - */ - @Override - public float getHighestVisibleX() { - - float step = mData.getDataSetCount(); - float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); - - float[] pts = new float[] { mViewPortHandler.contentLeft(), mViewPortHandler.contentTop() }; - - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return ((pts[1] >= getXChartMax()) ? getXChartMax() / div : (pts[1] / div)); - } + @Override + protected void prepareValuePxMatrix() { + mRightAxisTransformer.prepareMatrixValuePx(mAxisRight.mAxisMinimum, mAxisRight.mAxisRange, mXAxis.mAxisRange, + mXAxis.mAxisMinimum); + mLeftAxisTransformer.prepareMatrixValuePx(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisRange, mXAxis.mAxisRange, + mXAxis.mAxisMinimum); + } + + @Override + protected void calcModulus() { + float[] values = new float[9]; + mViewPortHandler.getMatrixTouch().getValues(values); + + mXAxis.mAxisLabelModulus = + (int) Math.ceil((mData.getXValCount() * mXAxis.mLabelRotatedHeight) + / (mViewPortHandler.contentHeight() * values[Matrix.MSCALE_Y])); + + if (mXAxis.mAxisLabelModulus < 1) + mXAxis.mAxisLabelModulus = 1; + } + + @Override + public RectF getBarBounds(BarEntry e) { + + IBarDataSet set = mData.getDataSetForEntry(e); + + if (set == null) + return null; + + float barspace = set.getBarSpace(); + float y = e.getY(); + float x = e.getX(); + + float spaceHalf = barspace / 2f; + + float top = x - 0.5f + spaceHalf; + float bottom = x + 0.5f - spaceHalf; + float left = y >= 0 ? y : 0; + float right = y <= 0 ? y : 0; + + RectF bounds = new RectF(left, top, right, bottom); + + getTransformer(set.getAxisDependency()).rectValueToPixel(bounds); + + return bounds; + } + + @Override + public PointF getPosition(Entry e, AxisDependency axis) { + + if (e == null) + return null; + + float[] vals = new float[]{e.getY(), e.getX()}; + + getTransformer(axis).pointValuesToPixel(vals); + + return new PointF(vals[0], vals[1]); + } + + /** + * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point + * inside the BarChart. + * + * @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 + return getHighlighter().getHighlight(y, x); // switch x and y + } + + @Override + public float getLowestVisibleX() { + PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom()); + return (float) Math.min(mXAxis.mAxisMinimum, pos.y); + } + + @Override + public float getHighestVisibleX() { + PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), + mViewPortHandler.contentTop()); + return (float) Math.min(mXAxis.mAxisMaximum, pos.y); + } + +// /** +// * Returns the lowest x-index (value on the x-axis) that is still visible on the chart. +// * +// * @return +// */ +// @Override +// public float getLowestVisibleX() { +// +// float step = mData.getDataSetCount(); +// float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); +// +// float[] pts = new float[] { mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom() }; +// +// getTransformer(AxisDependency.LEFT).pixelsToValue(pts); +// return (((pts[1] <= 0) ? 0 : ((pts[1])) / div) + 1); +// } +// +// /** +// * Returns the highest x-index (value on the x-axis) that is still visible on the chart. +// * +// * @return +// */ +// @Override +// public float getHighestVisibleX() { +// +// float step = mData.getDataSetCount(); +// float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); +// +// float[] pts = new float[] { mViewPortHandler.contentLeft(), mViewPortHandler.contentTop() }; +// +// getTransformer(AxisDependency.LEFT).pixelsToValue(pts); +// return ((pts[1] >= getXChartMax()) ? getXChartMax() / div : (pts[1] / div)); +// } } 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 d7f19d3992..af7074559a 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 @@ -376,6 +376,9 @@ public void pixelsToValue(float[] pixels) { tmp.mapPoints(pixels); } + /** buffer for performance */ + float[] ptsBuffer = new float[2]; + /** * Returns the x and y values in the chart at the given touch point * (encapsulated in a PointD). This method transforms pixel coordinates to @@ -388,15 +391,13 @@ public void pixelsToValue(float[] pixels) { */ public PointD getValuesByTouchPoint(float x, float y) { - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - pts[1] = y; + ptsBuffer[0] = x; + ptsBuffer[1] = y; - pixelsToValue(pts); + pixelsToValue(ptsBuffer); - double xTouchVal = pts[0]; - double yTouchVal = pts[1]; + double xTouchVal = ptsBuffer[0]; + double yTouchVal = ptsBuffer[1]; return new PointD(xTouchVal, yTouchVal); } From a0d39b918be3b5798280bcdee38de9eb7b0ef2f6 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 31 May 2016 11:08:29 +0200 Subject: [PATCH 134/606] Work on highlighting --- .../mpchartexample/AnotherBarActivity.java | 2 +- .../mpchartexample/BarChartActivity.java | 4 +- .../BarChartActivityMultiDataset.java | 2 +- .../mpchartexample/BarChartActivitySinus.java | 4 +- .../BarChartPositiveNegative.java | 2 +- .../CandleStickChartActivity.java | 2 +- .../CubicLineChartActivity.java | 2 +- .../mpchartexample/DrawChartActivity.java | 2 +- .../DynamicalAddingActivity.java | 6 +- .../HorizontalBarChartActivity.java | 4 +- .../InvertedLineChartActivity.java | 2 +- .../mpchartexample/LineChartActivity1.java | 10 +- .../mpchartexample/LineChartActivity2.java | 2 +- .../LineChartActivityColored.java | 2 +- .../mpchartexample/LineChartTime.java | 4 +- .../MultiLineChartActivity.java | 2 +- .../mpchartexample/PerformanceLineChart.java | 2 +- .../RealtimeLineChartActivity.java | 4 +- .../mpchartexample/ScrollViewActivity.java | 2 +- .../mpchartexample/StackedBarActivity.java | 4 +- .../StackedBarActivityNegative.java | 2 +- .../custom/MyCustomXAxisValueFormatter.java | 4 +- .../mpchartexample/custom/MyMarkerView.java | 2 +- .../custom/StackedBarsMarkerView.java | 4 +- .../notimportant/MainActivity.java | 8 +- .../realm/RealmBaseActivity.java | 2 +- .../realm/RealmDatabaseActivityBar.java | 2 +- .../realm/RealmDatabaseActivityBubble.java | 2 +- .../realm/RealmDatabaseActivityLine.java | 2 +- .../realm/RealmDatabaseActivityPie.java | 2 +- .../realm/RealmDatabaseActivityRadar.java | 2 +- .../realm/RealmDatabaseActivityScatter.java | 2 +- .../charting/animation/ChartAnimator.java | 32 ++-- .../charting/buffer/AbstractBuffer.java | 14 +- .../mikephil/charting/buffer/BarBuffer.java | 4 +- .../charting/buffer/HorizontalBarBuffer.java | 2 +- .../mikephil/charting/charts/BarChart.java | 10 +- .../charting/charts/BarLineChartBase.java | 146 ++++++++---------- .../mikephil/charting/charts/Chart.java | 90 +++++------ .../charting/charts/CombinedChart.java | 4 +- .../charting/charts/HorizontalBarChart.java | 17 +- .../mikephil/charting/charts/PieChart.java | 16 +- .../charting/charts/PieRadarChartBase.java | 10 +- .../mikephil/charting/charts/RadarChart.java | 16 +- .../charting/components/AxisBase.java | 30 ++-- .../charting/components/ComponentBase.java | 10 +- .../mikephil/charting/components/Legend.java | 2 +- .../charting/components/LimitLine.java | 16 +- .../charting/components/MarkerView.java | 12 +- .../mikephil/charting/components/XAxis.java | 42 ++--- .../mikephil/charting/components/YAxis.java | 52 +++---- .../mikephil/charting/data/BarDataSet.java | 14 +- .../mikephil/charting/data/BarEntry.java | 4 +- .../mikephil/charting/data/BaseDataSet.java | 12 +- .../mikephil/charting/data/BubbleEntry.java | 12 +- .../mikephil/charting/data/CandleEntry.java | 34 ++-- .../mikephil/charting/data/ChartData.java | 105 +++++++------ .../mikephil/charting/data/CombinedData.java | 4 +- .../mikephil/charting/data/DataSet.java | 14 +- .../github/mikephil/charting/data/Entry.java | 26 ++-- .../mikephil/charting/data/LineDataSet.java | 6 +- .../charting/data/LineRadarDataSet.java | 2 +- .../mikephil/charting/data/PieData.java | 2 +- .../mikephil/charting/data/XAxisValue.java | 16 +- .../charting/data/filter/Approximator.java | 4 +- .../data/realm/base/RealmBaseDataSet.java | 20 +-- .../realm/base/RealmLineRadarDataSet.java | 2 +- .../realm/implementation/RealmBarDataSet.java | 10 +- .../implementation/RealmBubbleDataSet.java | 6 +- .../implementation/RealmCandleDataSet.java | 18 +-- .../implementation/RealmLineDataSet.java | 12 +- .../realm/implementation/RealmPieDataSet.java | 6 +- .../implementation/RealmRadarDataSet.java | 6 +- .../implementation/RealmScatterDataSet.java | 6 +- .../formatter/DefaultValueFormatter.java | 4 +- .../formatter/DefaultXAxisValueFormatter.java | 4 +- .../formatter/DefaultYAxisValueFormatter.java | 4 +- .../charting/formatter/FillFormatter.java | 2 +- .../formatter/LargeValueFormatter.java | 4 +- .../charting/formatter/PercentFormatter.java | 2 +- .../formatter/StackedValueFormatter.java | 8 +- .../charting/formatter/ValueFormatter.java | 6 +- .../formatter/XAxisValueFormatter.java | 8 +- .../formatter/YAxisValueFormatter.java | 6 +- .../charting/highlight/BarHighlighter.java | 16 +- .../charting/highlight/ChartHighlighter.java | 45 +++--- .../highlight/CombinedHighlighter.java | 4 +- .../charting/highlight/Highlight.java | 56 +++---- .../highlight/HorizontalBarHighlighter.java | 10 +- .../mikephil/charting/highlight/Range.java | 4 +- .../dataprovider/ChartInterface.java | 8 +- .../interfaces/datasets/IBarDataSet.java | 8 +- .../interfaces/datasets/IDataSet.java | 46 +++--- .../datasets/ILineRadarDataSet.java | 2 +- .../listener/BarLineChartTouchListener.java | 16 +- .../charting/listener/ChartTouchListener.java | 2 +- .../listener/OnChartGestureListener.java | 8 +- .../OnChartValueSelectedListener.java | 2 +- .../listener/PieRadarChartTouchListener.java | 2 +- .../charting/renderer/AxisRenderer.java | 8 +- .../charting/renderer/BarChartRenderer.java | 10 +- .../renderer/BubbleChartRenderer.java | 4 +- .../renderer/CandleStickChartRenderer.java | 11 +- .../charting/renderer/DataRenderer.java | 12 +- .../renderer/HorizontalBarChartRenderer.java | 10 +- .../charting/renderer/LineChartRenderer.java | 22 +-- .../LineScatterCandleRadarRenderer.java | 13 +- .../charting/renderer/PieChartRenderer.java | 8 +- .../charting/renderer/RadarChartRenderer.java | 13 +- .../mikephil/charting/renderer/Renderer.java | 8 +- .../renderer/ScatterChartRenderer.java | 21 +-- .../charting/renderer/XAxisRenderer.java | 10 +- .../renderer/XAxisRendererBarChart.java | 2 +- .../XAxisRendererHorizontalBarChart.java | 4 +- .../renderer/XAxisRendererRadarChart.java | 2 +- .../charting/renderer/YAxisRenderer.java | 14 +- .../YAxisRendererHorizontalBarChart.java | 16 +- .../renderer/YAxisRendererRadarChart.java | 4 +- .../charting/utils/EntryXIndexComparator.java | 2 +- .../github/mikephil/charting/utils/FSize.java | 2 +- .../mikephil/charting/utils/PointD.java | 2 +- .../charting/utils/SelectionDetail.java | 28 ++-- .../mikephil/charting/utils/Transformer.java | 62 +++++--- .../github/mikephil/charting/utils/Utils.java | 32 ++-- .../charting/utils/ViewPortHandler.java | 70 ++++----- README.md | 6 +- gradlew | 6 +- gradlew.bat | 2 +- 128 files changed, 813 insertions(+), 832 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 4a06e62c3c..20f015e640 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -54,7 +54,7 @@ protected void onCreate(Bundle savedInstanceState) { // drawn mChart.setMaxVisibleValueCount(60); - // scaling can now only be done on x- and y-axis separately + // scaling can now only be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); mChart.setDrawBarShadow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 48d8fe02a3..a6c662a4c3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -74,7 +74,7 @@ protected void onCreate(Bundle savedInstanceState) { // drawn mChart.setMaxVisibleValueCount(60); - // scaling can now only be done on x- and y-axis separately + // scaling can now only be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); mChart.setDrawGridBackground(false); @@ -282,7 +282,7 @@ public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { Log.i("bounds", bounds.toString()); Log.i("position", position.toString()); - Log.i("x-index", + Log.i("xPx-index", "low: " + mChart.getLowestVisibleX() + ", high: " + mChart.getHighestVisibleX()); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index ef53cd5e86..5da09fc6d7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -62,7 +62,7 @@ protected void onCreate(Bundle savedInstanceState) { // mChart.setDrawBorders(true); - // scaling can now only be done on x- and y-axis separately + // scaling can now only be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); mChart.setDrawBarShadow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index 7d9be92996..d7d5b2f5cb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -63,10 +63,10 @@ protected void onCreate(Bundle savedInstanceState) { // drawn mChart.setMaxVisibleValueCount(60); - // scaling can now only be done on x- and y-axis separately + // scaling can now only be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); - // draw shadows for each bar that show the maximum value + // draw shadows for each bar that show the maximum yValue // mChart.setDrawBarShadow(true); // mChart.setDrawXLabels(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index b795e80a9d..a2f653a7b9 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.setDescription(""); - // scaling can now only be done on x- and y-axis separately + // scaling can now only be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java index 9cbcfd9247..1eb8fa6b8c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -57,7 +57,7 @@ protected void onCreate(Bundle savedInstanceState) { // drawn mChart.setMaxVisibleValueCount(60); - // scaling can now only be done on x- and y-axis separately + // scaling can now only be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index ccd15f2067..4c42066d7f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -73,7 +73,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDragEnabled(true); mChart.setScaleEnabled(true); - // if disabled, scaling can be done on x- and y-axis separately + // if disabled, scaling can be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java index cdd34adafc..9e2ba71981 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java @@ -69,7 +69,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.getLegend().setEnabled(false); // mChart.setYRange(-40f, 40f, true); - // call this to reset the changed y-range + // call this to reset the changed yPx-range // mChart.resetYRange(true); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index 3f5d4d152a..c67086f667 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -62,7 +62,7 @@ private void addEntry() { data.addDataSet(set); } - // add a new x-value first + // add a new xPx-yValue first data.addXValue(new XAxisValue(set.getEntryCount(), set.getEntryCount() + "")); // choose a random dataSet @@ -111,11 +111,11 @@ private void addDataSet() { int count = (data.getDataSetCount() + 1); - // create 10 y-vals + // create 10 yPx-vals ArrayList yVals = new ArrayList(); if(data.getXValCount() == 0) { - // add 10 x-entries + // add 10 xPx-entries for (int i = 0; i < 10; i++) { data.addXValue(new XAxisValue(i, i + "")); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 0acda456f8..ac3c6035ff 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -72,10 +72,10 @@ protected void onCreate(Bundle savedInstanceState) { // drawn mChart.setMaxVisibleValueCount(60); - // scaling can now only be done on x- and y-axis separately + // scaling can now only be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); - // draw shadows for each bar that show the maximum value + // draw shadows for each bar that show the maximum yValue // mChart.setDrawBarShadow(true); // mChart.setDrawXLabels(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index 086de305af..ab43c195ac 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -69,7 +69,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDragEnabled(true); mChart.setScaleEnabled(true); - // if disabled, scaling can be done on x- and y-axis separately + // if disabled, scaling can be done on xPx- and yPx-axis separately mChart.setPinchZoom(true); // set an alternative background color diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 946e23b14d..ed3f86b921 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -84,7 +84,7 @@ protected void onCreate(Bundle savedInstanceState) { // mChart.setScaleXEnabled(true); // mChart.setScaleYEnabled(true); - // if disabled, scaling can be done on x- and y-axis separately + // if disabled, scaling can be done on xPx- and yPx-axis separately mChart.setPinchZoom(true); // set an alternative background color @@ -97,7 +97,7 @@ protected void onCreate(Bundle savedInstanceState) { // set the marker to the chart mChart.setMarkerView(mv); - // x-axis limit line + // xPx-axis limit line LimitLine llXAxis = new LimitLine(10f, "Index 10"); llXAxis.setLineWidth(4f); llXAxis.enableDashedLine(10f, 10f, 0f); @@ -106,7 +106,7 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xAxis = mChart.getXAxis(); //xAxis.setValueFormatter(new MyCustomXAxisValueFormatter()); - //xAxis.addLimitLine(llXAxis); // add x-axis limit line + //xAxis.addLimitLine(llXAxis); // add xPx-axis limit line Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); @@ -346,7 +346,7 @@ private void setData(int count, float range) { float mult = (range + 1); float val = (float) (Math.random() * mult) + 3;// + (float) // ((mult * - // 0.1) / 10);x + // 0.1) / 10);xPx yVals.add(new Entry(i, val)); xVals.add(new XAxisValue(i, i + "")); } @@ -400,7 +400,7 @@ private void setData(int count, float range) { @Override public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { - Log.i("Gesture", "START, x: " + me.getX() + ", y: " + me.getY()); + Log.i("Gesture", "START, xPx: " + me.getX() + ", yPx: " + me.getY()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 84ad55ac9c..9644dbd23a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -76,7 +76,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); mChart.setHighlightPerDragEnabled(true); - // if disabled, scaling can be done on x- and y-axis separately + // if disabled, scaling can be done on xPx- and yPx-axis separately mChart.setPinchZoom(true); // set an alternative background color diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index 9b65e1c74b..0474ea6508 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -74,7 +74,7 @@ private void setupChart(LineChart chart, LineData data, int color) { chart.setDragEnabled(true); chart.setScaleEnabled(true); - // if disabled, scaling can be done on x- and y-axis separately + // if disabled, scaling can be done on xPx- and yPx-axis separately chart.setPinchZoom(false); chart.setBackgroundColor(color); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 2e72d60dfa..a45baab65d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -73,7 +73,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); mChart.setHighlightPerDragEnabled(true); - // if disabled, scaling can be done on x- and y-axis separately + // if disabled, scaling can be done on xPx- and yPx-axis separately mChart.setPinchZoom(true); // set an alternative background color @@ -104,7 +104,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setDrawAxisLine(false); xAxis.setSpaceBetweenLabels(1); - // custom x-axis min / max + // custom xPx-axis min / max xAxis.setAxisMinValue(5000); xAxis.setAxisMaxValue(30000); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index d98e83954a..e890773750 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -69,7 +69,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDragEnabled(true); mChart.setScaleEnabled(true); - // if disabled, scaling can be done on x- and y-axis separately + // if disabled, scaling can be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); mSeekBarX.setProgress(20); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java index 935f91721a..eedec87e5c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -53,7 +53,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDragEnabled(true); mChart.setScaleEnabled(true); - // if disabled, scaling can be done on x- and y-axis separately + // if disabled, scaling can be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); mChart.getAxisLeft().setDrawGridLines(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 6dda2d1d38..4edf885c94 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -53,7 +53,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setScaleEnabled(true); mChart.setDrawGridBackground(false); - // if disabled, scaling can be done on x- and y-axis separately + // if disabled, scaling can be done on xPx- and yPx-axis separately mChart.setPinchZoom(true); // set an alternative background color @@ -139,7 +139,7 @@ private void addEntry() { data.addDataSet(set); } - // add a new x-value first + // add a new xPx-yValue first data.addXValue(new XAxisValue(data.getXValCount() ,mMonths[data.getXValCount() % 12] + " " + (year + data.getXValCount() / 12))); data.addEntry(new Entry((float) (Math.random() * 40) + 30f, set.getEntryCount()), 0); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java index 1dcff1efe1..8c8ae3bb0a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java @@ -31,7 +31,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDescription(""); - // scaling can now only be done on x- and y-axis separately + // scaling can now only be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); mChart.setDrawBarShadow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 36fd6f1d33..e5d7ce1076 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -63,7 +63,7 @@ protected void onCreate(Bundle savedInstanceState) { // drawn mChart.setMaxVisibleValueCount(40); - // scaling can now only be done on x- and y-axis separately + // scaling can now only be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); mChart.setDrawGridBackground(false); @@ -71,7 +71,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawValueAboveBar(false); - // change the position of the y-labels + // change the position of the yPx-labels YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setValueFormatter(new MyYAxisValueFormatter()); leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index ddd8ac3fcf..5f74bbed62 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -51,7 +51,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); mChart.setDescription(""); - // scaling can now only be done on x- and y-axis separately + // scaling can now only be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); mChart.setDrawBarShadow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index bdf918b1b7..ed25ba090a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -15,9 +15,9 @@ public MyCustomXAxisValueFormatter() { @Override public String getXValue(String original, int index, ViewPortHandler viewPortHandler) { - //Log.i("TRANS", "x: " + viewPortHandler.getTransX() + ", y: " + viewPortHandler.getTransY()); + //Log.i("TRANS", "xPx: " + viewPortHandler.getTransX() + ", yPx: " + viewPortHandler.getTransY()); - // e.g. adjust the x-axis values depending on scale / zoom level + // e.g. adjust the xPx-axis values depending on scale / zoom level if (viewPortHandler.getScaleX() > 5) return "4"; else if (viewPortHandler.getScaleX() > 3) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java index 53a3bc47f4..4389f11c04 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java @@ -50,7 +50,7 @@ public int getXOffset(float xpos) { @Override public int getYOffset(float ypos) { - // this will cause the marker-view to be above the selected value + // this will cause the marker-view to be above the selected yValue return -getHeight(); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java index 0b8938778d..4a28f772e8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java @@ -37,7 +37,7 @@ public void refreshContent(Entry e, Highlight highlight) { if(be.getYVals() != null) { - // draw the stack value + // draw the stack yValue tvContent.setText("" + Utils.formatNumber(be.getYVals()[highlight.getStackIndex()], 0, true)); } else { tvContent.setText("" + Utils.formatNumber(be.getY(), 0, true)); @@ -56,7 +56,7 @@ public int getXOffset(float xpos) { @Override public int getYOffset(float ypos) { - // this will cause the marker-view to be above the selected value + // this will cause the marker-view to be above the selected yValue return -getHeight(); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index 34362f1e4e..f511862353 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -68,14 +68,14 @@ protected void onCreate(Bundle savedInstanceState) { 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.")); + "Demonstration of the linechart with dual yPx-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("Pie Chart with yValue 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", @@ -99,7 +99,7 @@ protected void onCreate(Bundle savedInstanceState) { "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.")); + "Demonstrates the feature of inverting the yPx-axis.")); objects.add(new ContentItem( "Candle Stick Chart", "Demonstrates usage of the CandleStickChart.")); @@ -114,7 +114,7 @@ protected void onCreate(Bundle savedInstanceState) { "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.")); + "This chart is fed with new data in realtime. It also restrains the view on the xPx-axis.")); objects.add(new ContentItem( "Dynamical data adding", "This Activity demonstrates dynamical adding of Entries and DataSets (real time graph).")); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java index 1f51281e35..f84031c2db 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -55,7 +55,7 @@ protected void setup(Chart chart) { mChart.setDragEnabled(true); mChart.setScaleEnabled(true); - // if disabled, scaling can be done on x- and y-axis separately + // if disabled, scaling can be done on xPx- and yPx-axis separately mChart.setPinchZoom(false); YAxis leftAxis = mChart.getAxisLeft(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java index ecde642f4f..9527f3e6be 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java @@ -50,7 +50,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet(result, "value", "xIndex"); // stacked entries + RealmBarDataSet set = new RealmBarDataSet(result, "yValue", "xIndex"); // stacked entries set.setColors(new int[] {ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); set.setLabel("Realm BarDataSet"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java index 2f23561622..cd188a4266 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java @@ -53,7 +53,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); - RealmBubbleDataSet set = new RealmBubbleDataSet(result, "value", "xIndex", "bubbleSize"); + RealmBubbleDataSet set = new RealmBubbleDataSet(result, "yValue", "xIndex", "bubbleSize"); set.setLabel("Realm BubbleDataSet"); set.setColors(ColorTemplate.COLORFUL_COLORS, 110); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java index c2f8803b85..36a8dc5abd 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java @@ -54,7 +54,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); - RealmLineDataSet set = new RealmLineDataSet(result, "value", "xIndex"); + RealmLineDataSet set = new RealmLineDataSet(result, "yValue", "xIndex"); set.setDrawCubic(false); set.setLabel("Realm LineDataSet"); set.setDrawCircleHole(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java index 489399bc9e..dfe37e61f6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java @@ -54,7 +54,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmPieDataSet set = new RealmPieDataSet(result, "value", "xIndex"); // stacked entries + RealmPieDataSet set = new RealmPieDataSet(result, "yValue", "xIndex"); // stacked entries 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 3e1c688f6c..599c7f9a29 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java @@ -55,7 +55,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmRadarDataSet set = new RealmRadarDataSet(result, "value", "xIndex"); // stacked entries + RealmRadarDataSet set = new RealmRadarDataSet(result, "yValue", "xIndex"); // stacked entries set.setLabel("Realm RadarDataSet"); set.setDrawFilled(true); set.setColor(ColorTemplate.rgb("#009688")); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java index 135667a4bf..a465433d00 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java @@ -53,7 +53,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); - RealmScatterDataSet set = new RealmScatterDataSet(result, "value", "xIndex"); + RealmScatterDataSet set = new RealmScatterDataSet(result, "yValue", "xIndex"); set.setLabel("Realm ScatterDataSet"); set.setScatterShapeSize(9f); set.setColor(ColorTemplate.rgb("#CDDC39")); 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..455f7be3e2 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 @@ -7,7 +7,7 @@ /** * Object responsible for all animations in the Chart. ANIMATIONS ONLY WORK FOR - * API LEVEL 11 (Android 3.0.x) AND HIGHER. + * API LEVEL 11 (Android 3.0.xPx) AND HIGHER. * * @author Philipp Jahoda */ @@ -29,10 +29,10 @@ public ChartAnimator(AnimatorUpdateListener listener) { */ /** CODE BELOW THIS RELATED TO ANIMATION */ - /** the phase that is animated and influences the drawn values on the y-axis */ + /** the phase that is animated and influences the drawn values on the yPx-axis */ protected float mPhaseY = 1f; - /** the phase that is animated and influences the drawn values on the x-axis */ + /** the phase that is animated and influences the drawn values on the xPx-axis */ protected float mPhaseX = 1f; /** @@ -41,7 +41,7 @@ public ChartAnimator(AnimatorUpdateListener listener) { /** METHODS FOR CUSTOM EASING */ /** - * Animates the drawing / rendering of the chart on both x- and y-axis with + * Animates the drawing / rendering of the chart on both xPx- and yPx-axis with * the specified animation time. If animate(...) is called, no further * calling of invalidate() is necessary to refresh the chart. * @@ -78,7 +78,7 @@ public void animateXY(int durationMillisX, int durationMillisY, EasingFunction e } /** - * Animates the rendering of the chart on the x-axis with the specified + * Animates the rendering of the chart on the xPx-axis with the specified * animation time. If animate(...) is called, no further calling of * invalidate() is necessary to refresh the chart. * @@ -98,7 +98,7 @@ public void animateX(int durationMillis, EasingFunction easing) { } /** - * Animates the rendering of the chart on the y-axis with the specified + * Animates the rendering of the chart on the yPx-axis with the specified * animation time. If animate(...) is called, no further calling of * invalidate() is necessary to refresh the chart. * @@ -123,7 +123,7 @@ public void animateY(int durationMillis, EasingFunction easing) { /** METHODS FOR PREDEFINED EASING */ /** - * Animates the drawing / rendering of the chart on both x- and y-axis with + * Animates the drawing / rendering of the chart on both xPx- and yPx-axis with * the specified animation time. If animate(...) is called, no further * calling of invalidate() is necessary to refresh the chart. * @@ -160,7 +160,7 @@ public void animateXY(int durationMillisX, int durationMillisY, Easing.EasingOpt } /** - * Animates the rendering of the chart on the x-axis with the specified + * Animates the rendering of the chart on the xPx-axis with the specified * animation time. If animate(...) is called, no further calling of * invalidate() is necessary to refresh the chart. * @@ -180,7 +180,7 @@ public void animateX(int durationMillis, Easing.EasingOption easing) { } /** - * Animates the rendering of the chart on the y-axis with the specified + * Animates the rendering of the chart on the yPx-axis with the specified * animation time. If animate(...) is called, no further calling of * invalidate() is necessary to refresh the chart. * @@ -205,7 +205,7 @@ public void animateY(int durationMillis, Easing.EasingOption easing) { /** METHODS FOR ANIMATION WITHOUT EASING */ /** - * Animates the drawing / rendering of the chart on both x- and y-axis with + * Animates the drawing / rendering of the chart on both xPx- and yPx-axis with * the specified animation time. If animate(...) is called, no further * calling of invalidate() is necessary to refresh the chart. * @@ -237,7 +237,7 @@ public void animateXY(int durationMillisX, int durationMillisY) { } /** - * Animates the rendering of the chart on the x-axis with the specified + * Animates the rendering of the chart on the xPx-axis with the specified * animation time. If animate(...) is called, no further calling of * invalidate() is necessary to refresh the chart. * @@ -255,7 +255,7 @@ public void animateX(int durationMillis) { } /** - * Animates the rendering of the chart on the y-axis with the specified + * Animates the rendering of the chart on the yPx-axis with the specified * animation time. If animate(...) is called, no further calling of * invalidate() is necessary to refresh the chart. * @@ -273,7 +273,7 @@ public void animateY(int durationMillis) { } /** - * This gets the y-phase that is used to animate the values. + * This gets the yPx-phase that is used to animate the values. * * @return */ @@ -282,7 +282,7 @@ public float getPhaseY() { } /** - * This modifys the y-phase that is used to animate the values. + * This modifys the yPx-phase that is used to animate the values. * * @param phase */ @@ -291,7 +291,7 @@ public void setPhaseY(float phase) { } /** - * This gets the x-phase that is used to animate the values. + * This gets the xPx-phase that is used to animate the values. * * @return */ @@ -300,7 +300,7 @@ public float getPhaseX() { } /** - * This modifys the x-phase that is used to animate the values. + * This modifys the xPx-phase that is used to animate the values. * * @param phase */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java index 958d12afba..6df0c01ba8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java @@ -15,19 +15,19 @@ public abstract class AbstractBuffer { /** index in the buffer */ protected int index = 0; - /** float-buffer that holds the data points to draw, order: x,y,x,y,... */ + /** float-buffer that holds the data points to draw, order: xPx,yPx,xPx,yPx,... */ public final float[] buffer; - /** animation phase x-axis */ + /** animation phase xPx-axis */ protected float phaseX = 1f; - /** animation phase y-axis */ + /** animation phase yPx-axis */ protected float phaseY = 1f; - /** indicates from which x-index the visible data begins */ + /** indicates from which xPx-index the visible data begins */ protected int mFrom = 0; - /** indicates to which x-index the visible data ranges */ + /** indicates to which xPx-index the visible data ranges */ protected int mTo = 0; /** @@ -40,14 +40,14 @@ public AbstractBuffer(int size) { buffer = new float[size]; } - /** limits the drawing on the x-axis */ + /** limits the drawing on the xPx-axis */ public void limitFrom(int from) { if (from < 0) from = 0; mFrom = from; } - /** limits the drawing on the x-axis */ + /** limits the drawing on the xPx-axis */ public void limitTo(int to) { if (to < 0) to = 0; 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 91fc8c769a..d0ccaf01a6 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 @@ -14,7 +14,7 @@ public class BarBuffer extends AbstractBuffer { protected boolean mInverted = false; /** - * interval on the x-axis per group + * interval on the xPx-axis per group */ protected float mInterval = 0f; @@ -71,7 +71,7 @@ public void feed(IBarDataSet data) { BarEntry e = data.getEntryForIndex(i); - // calculate the x-position, depending on interval + // calculate the xPx-position, depending on interval float x = mInterval * i + dataSetSpace; float y = e.getY(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java index e50267bd95..5a7044522e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java @@ -24,7 +24,7 @@ public void feed(IBarDataSet data) { BarEntry e = data.getEntryForIndex(i); - // calculate the x-position, depending on datasetcount + // calculate the xPx-position, depending on datasetcount float x = e.getX() + e.getX() * dataSetOffset + mDataSetIndex + mGroupSpace * e.getX() + groupSpaceHalf; float y = e.getY(); 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 0a69c03748..f5958d9018 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 @@ -31,7 +31,7 @@ public class BarChart extends BarLineChartBase implements BarDataProvid private boolean mDrawValueAboveBar = true; /** - * if set to true, a grey area is drawn behind each bar that indicates the maximum value + * if set to true, a grey area is drawn behind each bar that indicates the maximum yValue */ private boolean mDrawBarShadow = false; @@ -75,7 +75,7 @@ protected void calcMinMax() { } /** - * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point + * Returns the Highlight object (contains xPx-index and DataSet index) of the selected yValue at the given touch point * inside the BarChart. * * @param x @@ -162,7 +162,7 @@ public boolean isDrawValueAboveBarEnabled() { } /** - * If set to true, a grey area is drawn behind each bar that indicates the maximum value. Enabling his will reduce + * If set to true, a grey area is drawn behind each bar that indicates the maximum yValue. Enabling his will reduce * performance by about 50%. * * @param enabled @@ -186,7 +186,7 @@ public BarData getBarData() { } // /** -// * Returns the lowest x-index (value on the x-axis) that is still visible on the chart. +// * Returns the lowest xPx-index (yValue on the xPx-axis) that is still visible on the chart. // * // * @return // */ @@ -203,7 +203,7 @@ public BarData getBarData() { // } // // /** -// * Returns the highest x-index (value on the x-axis) that is still visible on the chart. +// * Returns the highest xPx-index (yValue on the xPx-axis) that is still visible on the chart. // * // * @return // */ 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 714ff17a61..510c1e0a88 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 @@ -50,20 +50,20 @@ public abstract class BarLineChartBase zoom out, if > 1f --> zoom in @@ -730,7 +730,7 @@ public void zoom(float scaleX, float scaleY, float x, float y) { /** * Zooms in or out by the given scale factor. - * x and y are the values (NOT PIXELS) which to zoom to or from (the values of the zoom center). + * xPx and yPx are the values (NOT PIXELS) which to zoom to or from (the values of the zoom center). * * @param scaleX * @param scaleY @@ -785,7 +785,7 @@ public void fitScreen() { } /** - * Sets the minimum scale factor value to which can be zoomed out. 1f = + * Sets the minimum scale factor yValue to which can be zoomed out. 1f = * fitScreen * * @param scaleX @@ -797,12 +797,12 @@ public void setScaleMinima(float scaleX, float scaleY) { } /** - * Sets the size of the area (range on the x-axis) that should be maximum + * Sets the size of the area (range on the xPx-axis) that should be maximum * visible at once (no further zooming out allowed). If this is e.g. set to - * 10, no more than 10 values on the x-axis can be viewed at once without + * 10, no more than 10 values on the xPx-axis can be viewed at once without * scrolling. * - * @param maxXRange The maximum visible range of x-values. + * @param maxXRange The maximum visible range of xPx-values. */ public void setVisibleXRangeMaximum(float maxXRange) { float xScale = mXAxis.mAxisRange / (maxXRange); @@ -810,12 +810,12 @@ public void setVisibleXRangeMaximum(float maxXRange) { } /** - * Sets the size of the area (range on the x-axis) that should be minimum + * Sets the size of the area (range on the xPx-axis) that should be minimum * visible at once (no further zooming in allowed). If this is e.g. set to - * 10, no less than 10 values on the x-axis can be viewed at once without + * 10, no less than 10 values on the xPx-axis can be viewed at once without * scrolling. * - * @param minXRange The minimum visible range of x-values. + * @param minXRange The minimum visible range of xPx-values. */ public void setVisibleXRangeMinimum(float minXRange) { float xScale = mXAxis.mAxisRange / (minXRange); @@ -823,7 +823,7 @@ public void setVisibleXRangeMinimum(float minXRange) { } /** - * Limits the maximum and minimum value count that can be visible by + * Limits the maximum and minimum yValue count that can be visible by * pinching and zooming. e.g. minRange=10, maxRange=100 no less than 10 * values and no more that 100 values can be viewed at once without * scrolling @@ -838,10 +838,10 @@ public void setVisibleXRange(float minXRange, float maxXRange) { } /** - * Sets the size of the area (range on the y-axis) that should be maximum + * Sets the size of the area (range on the yPx-axis) that should be maximum * visible at once. * - * @param maxYRange the maximum visible range on the y-axis + * @param maxYRange the maximum visible range on the yPx-axis * @param axis - the axis for which this limit should apply */ public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { @@ -850,7 +850,7 @@ public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { } /** - * Moves the left side of the current viewport to the specified x-index. + * Moves the left side of the current viewport to the specified xPx-index. * This also refreshes the chart by calling invalidate(). * * @param xIndex @@ -864,11 +864,11 @@ public void moveViewToX(float xIndex) { } /** - * Centers the viewport to the specified y-value on the y-axis. + * Centers the viewport to the specified yPx-yValue on the yPx-axis. * This also refreshes the chart by calling invalidate(). * * @param yValue - * @param axis - which axis should be used as a reference for the y-axis + * @param axis - which axis should be used as a reference for the yPx-axis */ public void moveViewToY(float yValue, AxisDependency axis) { @@ -882,13 +882,13 @@ public void moveViewToY(float yValue, AxisDependency axis) { /** * This will move the left side of the current viewport to the specified - * x-value on the x-axis, and center the viewport to the specified y-value - * on the y-axis. + * xPx-yValue on the xPx-axis, and center the viewport to the specified yPx-yValue + * on the yPx-axis. * This also refreshes the chart by calling invalidate(). * * @param xIndex * @param yValue - * @param axis - which axis should be used as a reference for the y-axis + * @param axis - which axis should be used as a reference for the yPx-axis */ public void moveViewTo(float xIndex, float yValue, AxisDependency axis) { @@ -901,8 +901,8 @@ public void moveViewTo(float xIndex, float yValue, AxisDependency axis) { } /** - * This will move the left side of the current viewport to the specified x-position - * and center the viewport to the specified y-position animated. + * This will move the left side of the current viewport to the specified xPx-position + * and center the viewport to the specified yPx-position animated. * This also refreshes the chart by calling invalidate(). * * @param xIndex @@ -930,12 +930,12 @@ public void moveViewToAnimated(float xIndex, float yValue, AxisDependency axis, /** * This will move the center of the current viewport to the specified - * x-value and y-value. + * xPx-yValue and yPx-yValue. * This also refreshes the chart by calling invalidate(). * * @param xIndex * @param yValue - * @param axis - which axis should be used as a reference for the y-axis + * @param axis - which axis should be used as a reference for the yPx-axis */ public void centerViewTo(float xIndex, float yValue, AxisDependency axis) { @@ -951,7 +951,7 @@ public void centerViewTo(float xIndex, float yValue, AxisDependency axis) { /** * This will move the center of the current viewport to the specified - * x-value and y-value animated. + * xPx-yValue and yPx-yValue animated. * * @param xIndex * @param yValue @@ -1026,7 +1026,7 @@ public void resetViewPortOffsets() { /** CODE BELOW IS GETTERS AND SETTERS */ /** - * Returns the delta-y value (y-value range) of the specified axis. + * Returns the delta-yPx yValue (yPx-yValue range) of the specified axis. * * @param axis * @return @@ -1116,7 +1116,7 @@ public void setHighlightFullBarEnabled(boolean enabled) { } /** - * @return true the highlight is be full-bar oriented, false if single-value + * @return true the highlight is be full-bar oriented, false if single-yValue */ public boolean isHighlightFullBarEnabled() { return mHighlightFullBarEnabled; @@ -1208,7 +1208,7 @@ 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. + * no point drawing the axis-lines of xPx- and yPx-axis. * * @param enabled */ @@ -1263,8 +1263,8 @@ public void setKeepPositionOnRotation(boolean keepPositionOnRotation) { } /** - * Returns the Highlight object (contains x-index and DataSet index) of the - * selected value at the given touch point inside the Line-, Scatter-, or + * Returns the Highlight object (contains xPx-index and DataSet index) of the + * selected yValue at the given touch point inside the Line-, Scatter-, or * CandleStick-Chart. * * @param x @@ -1281,7 +1281,7 @@ public Highlight getHighlightByTouchPoint(float x, float y) { } /** - * Returns the x and y values in the chart at the given touch point + * Returns the xPx and yPx values in the chart at the given touch point * (encapsulated in a PointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to * getPixelsForValues(...). @@ -1291,18 +1291,7 @@ public Highlight getHighlightByTouchPoint(float x, float y) { * @return */ public PointD getValuesByTouchPoint(float x, float y, AxisDependency axis) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - pts[1] = y; - - getTransformer(axis).pixelsToValue(pts); - - double xTouchVal = pts[0]; - double yTouchVal = pts[1]; - - return new PointD(xTouchVal, yTouchVal); + return getTransformer(axis).getValuesByTouchPoint(x, y); } /** @@ -1314,19 +1303,12 @@ public PointD getValuesByTouchPoint(float x, float y, AxisDependency axis) { * @return */ public PointD getPixelsForValues(float x, float y, AxisDependency axis) { - - float[] pts = new float[]{ - x, y - }; - - getTransformer(axis).pointValuesToPixel(pts); - - return new PointD(pts[0], pts[1]); + return getTransformer(axis).getPixelsForValues(x, y); } /** - * returns the y-value at the given touch position (must not necessarily be - * a value contained in one of the datasets) + * returns the yPx-yValue at the given touch position (must not necessarily be + * a yValue contained in one of the datasets) * * @param x * @param y @@ -1367,7 +1349,7 @@ public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float } /** - * Returns the lowest x-index (value on the x-axis) that is still visible on + * Returns the lowest xPx-index (yValue on the xPx-axis) that is still visible on * the chart. * * @return @@ -1380,7 +1362,7 @@ public float getLowestVisibleX() { } /** - * Returns the highest x-index (value on the x-axis) that is still visible + * Returns the highest xPx-index (yValue on the xPx-axis) that is still visible * on the chart. * * @return @@ -1393,7 +1375,7 @@ public float getHighestVisibleX() { } /** - * returns the current x-scale factor + * returns the current xPx-scale factor */ public float getScaleX() { if (mViewPortHandler == null) @@ -1403,7 +1385,7 @@ public float getScaleX() { } /** - * returns the current y-scale factor + * returns the current yPx-scale factor */ public float getScaleY() { if (mViewPortHandler == null) @@ -1422,7 +1404,7 @@ public boolean isFullyZoomedOut() { } /** - * Returns the left y-axis object. In the horizontal bar-chart, this is the + * Returns the left yPx-axis object. In the horizontal bar-chart, this is the * top axis. * * @return @@ -1432,7 +1414,7 @@ public YAxis getAxisLeft() { } /** - * Returns the right y-axis object. In the horizontal bar-chart, this is the + * Returns the right yPx-axis object. In the horizontal bar-chart, this is the * bottom axis. * * @return @@ -1442,7 +1424,7 @@ public YAxis getAxisRight() { } /** - * Returns the y-axis object to the corresponding AxisDependency. In the + * Returns the yPx-axis object to the corresponding AxisDependency. In the * horizontal bar-chart, LEFT == top, RIGHT == BOTTOM * * @param axis @@ -1461,8 +1443,8 @@ public boolean isInverted(AxisDependency axis) { } /** - * If set to true, both x and y axis can be scaled simultaneously with 2 fingers, if false, - * x and y axis can be scaled separately. default: false + * If set to true, both xPx and yPx axis can be scaled simultaneously with 2 fingers, if false, + * xPx and yPx axis can be scaled separately. default: false * * @param enabled */ @@ -1481,7 +1463,7 @@ public boolean isPinchZoomEnabled() { /** * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the x-axis. + * bounds on the xPx-axis. * * @param offset */ @@ -1491,7 +1473,7 @@ public void setDragOffsetX(float offset) { /** * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the y-axis. + * bounds on the yPx-axis. * * @param offset */ @@ -1500,7 +1482,7 @@ public void setDragOffsetY(float offset) { } /** - * Returns true if both drag offsets (x and y) are zero or smaller. + * Returns true if both drag offsets (xPx and yPx) are zero or smaller. * * @return */ @@ -1571,11 +1553,11 @@ public boolean isAnyAxisInverted() { } /** - * Flag that indicates if auto scaling on the y axis is enabled. This is + * Flag that indicates if auto scaling on the yPx axis is enabled. This is * especially interesting for charts displaying financial data. * - * @param enabled the y axis automatically adjusts to the min and max y - * values of the current x axis range whenever the viewport + * @param enabled the yPx axis automatically adjusts to the min and max yPx + * values of the current xPx axis range whenever the viewport * changes */ public void setAutoScaleMinMaxEnabled(boolean enabled) { @@ -1583,7 +1565,7 @@ public void setAutoScaleMinMaxEnabled(boolean enabled) { } /** - * @return true if auto scaling on the y axis is enabled. + * @return true if auto scaling on the yPx axis is enabled. * @default false */ public boolean isAutoScaleMinMaxEnabled() { 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 e492d74e5b..ae7075b725 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 @@ -91,13 +91,13 @@ public abstract class Chart implements Lin /** * if set to true, a grey area is drawn behind each bar that indicates the - * maximum value + * maximum yValue */ private boolean mDrawBarShadow = false; @@ -194,7 +194,7 @@ public void setDrawValueAboveBar(boolean enabled) { /** * If set to true, a grey area is drawn behind each bar that indicates the - * maximum value. Enabling his will reduce performance by about 50%. + * maximum yValue. Enabling his will reduce performance by about 50%. * * @param enabled */ 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 f78419ce50..b183e28c69 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 @@ -22,7 +22,7 @@ import com.github.mikephil.charting.utils.Utils; /** - * BarChart with horizontal bar orientation. In this implementation, x- and y-axis are switched, meaning the YAxis class + * BarChart with horizontal bar orientation. In this implementation, xPx- and yPx-axis are switched, meaning the YAxis class * represents the horizontal values and the XAxis class represents the vertical values. * * @author Philipp Jahoda @@ -70,7 +70,7 @@ public void calculateOffsets() { offsetRight += mOffsetsBuffer.right; offsetBottom += mOffsetsBuffer.bottom; - // offsets for y-labels + // offsets for yPx-labels if (mAxisLeft.needsOffset()) { offsetTop += mAxisLeft.getRequiredHeightSpace(mAxisRendererLeft.getPaintAxisLabels()); } @@ -83,7 +83,7 @@ public void calculateOffsets() { if (mXAxis.isEnabled()) { - // offsets for x-labels + // offsets for xPx-labels if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { offsetLeft += xlabelwidth; @@ -184,7 +184,7 @@ public PointF getPosition(Entry e, AxisDependency axis) { } /** - * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point + * Returns the Highlight object (contains xPx-index and DataSet index) of the selected yValue at the given touch point * inside the BarChart. * * @param x @@ -195,10 +195,11 @@ public PointF getPosition(Entry e, AxisDependency axis) { public Highlight getHighlightByTouchPoint(float x, float y) { if (mData == null) { - Log.e(LOG_TAG, "Can't select by touch. No data set."); + if(mLogEnabled) + Log.e(LOG_TAG, "Can't select by touch. No data set."); return null; } else - return getHighlighter().getHighlight(y, x); // switch x and y + return getHighlighter().getHighlight(y, x); // switch xPx and yPx } @Override @@ -216,7 +217,7 @@ public float getHighestVisibleX() { } // /** -// * Returns the lowest x-index (value on the x-axis) that is still visible on the chart. +// * Returns the lowest xPx-index (yValue on the xPx-axis) that is still visible on the chart. // * // * @return // */ @@ -233,7 +234,7 @@ public float getHighestVisibleX() { // } // // /** -// * Returns the highest x-index (value on the x-axis) that is still visible on the chart. +// * Returns the highest xPx-index (yValue on the xPx-axis) that is still visible on the chart. // * // * @return // */ 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 ba8e37c8ea..eee3026cac 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 @@ -34,7 +34,7 @@ public class PieChart extends PieRadarChartBase { private RectF mCircleBox = new RectF(); /** - * flag indicating if the x-labels should be drawn or not + * flag indicating if the xPx-labels should be drawn or not */ private boolean mDrawXLabels = true; @@ -256,7 +256,7 @@ public boolean needsHighlight(int xIndex, int dataSetIndex) { } /** - * calculates the needed angle for a given value + * calculates the needed angle for a given yValue * * @param value * @return @@ -266,7 +266,7 @@ private float calcAngle(float value) { } /** - * calculates the needed angle for a given value + * calculates the needed angle for a given yValue * * @param value * @param yValueSum @@ -302,7 +302,7 @@ public int getIndexForAngle(float angle) { } /** - * Returns the index of the DataSet this x-index belongs to. + * Returns the index of the DataSet this xPx-index belongs to. * * @param xIndex * @return @@ -550,7 +550,7 @@ public float getTransparentCircleRadius() { /** * Sets the amount of transparency the transparent circle should have 0 = fully transparent, * 255 = fully opaque. - * Default value is 100. + * Default yValue is 100. * * @param alpha 0-255 */ @@ -559,7 +559,7 @@ public void setTransparentCircleAlpha(int alpha) { } /** - * set this to true to draw the x-value text into the pie slices + * set this to true to draw the xPx-yValue text into the pie slices * * @param enabled */ @@ -568,7 +568,7 @@ public void setDrawSliceText(boolean enabled) { } /** - * returns true if drawing x-values is enabled, false if not + * returns true if drawing xPx-values is enabled, false if not * * @return */ @@ -588,7 +588,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 yValue. Values provided for the ValueFormatter to * format are then provided in percent. * * @param enabled 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 2c55c24125..fc00d890be 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 @@ -343,7 +343,7 @@ public void setRotationAngle(float angle) { /** * gets the raw version of the current rotation angle of the pie chart the - * returned value could be any value, negative or positive, outside of the + * returned yValue could be any yValue, negative or positive, outside of the * 360 degrees. this is used when working with rotation direction, mainly by * gestures and animations. * @@ -441,8 +441,8 @@ public float getYChartMin() { } /** - * Returns an array of SelectionDetail objects for the given x-index. The SelectionDetail - * objects give information about the value at the selected index and the + * Returns an array of SelectionDetail objects for the given xPx-index. The SelectionDetail + * objects give information about the yValue at the selected index and the * DataSet it belongs to. INFORMATION: This method does calculations at * runtime. Do not over-use in performance critical situations. * @@ -456,12 +456,12 @@ public List getSelectionDetailsAtIndex(int xIndex) { IDataSet dataSet = mData.getDataSetByIndex(i); - // extract all y-values from all DataSets at the given x-index + // extract all yPx-values from all DataSets at the given xPx-index final float yVal = dataSet.getYValueForXValue(xIndex); if (Float.isNaN(yVal)) continue; - vals.add(new SelectionDetail(yVal, i, dataSet)); + vals.add(new SelectionDetail(0f, yVal, i, dataSet)); } return vals; 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 e80066d6b6..400fb85711 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 @@ -62,7 +62,7 @@ public class RadarChart extends PieRadarChartBase { private int mSkipWebLineCount = 0; /** - * the object reprsenting the y-axis labels + * the object reprsenting the yPx-axis labels */ private YAxis mYAxis; @@ -100,7 +100,7 @@ protected void init() { protected void calcMinMax() { super.calcMinMax(); - // calculate / set x-axis range + // calculate / set xPx-axis range mXAxis.mAxisMaximum = mData.getXVals().size() - 1; mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); @@ -210,7 +210,7 @@ public int getIndexForAngle(float angle) { } /** - * Returns the object that represents all y-labels of the RadarChart. + * Returns the object that represents all yPx-labels of the RadarChart. * * @return */ @@ -246,7 +246,7 @@ public float getWebLineWidthInner() { } /** - * Sets the transparency (alpha) value for all web lines, default: 150, 255 + * Sets the transparency (alpha) yValue for all web lines, default: 150, 255 * = 100% opaque, 0 = 100% transparent * * @param alpha @@ -256,7 +256,7 @@ public void setWebAlpha(int alpha) { } /** - * Returns the alpha value for all web lines. + * Returns the alpha yValue for all web lines. * * @return */ @@ -343,21 +343,21 @@ public float getRadius() { } /** - * Returns the maximum value this chart can display on it's y-axis. + * Returns the maximum yValue this chart can display on it's yPx-axis. */ public float getYChartMax() { return mYAxis.mAxisMaximum; } /** - * Returns the minimum value this chart can display on it's y-axis. + * Returns the minimum yValue this chart can display on it's yPx-axis. */ public float getYChartMin() { return mYAxis.mAxisMinimum; } /** - * Returns the range of y-values this chart can display. + * Returns the range of yPx-values this chart can display. * * @return */ 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 f078ed00c7..4e3b3cc2a1 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 @@ -76,12 +76,12 @@ public abstract class AxisBase extends ComponentBase { protected boolean mDrawLimitLineBehindData = false; /** - * flag indicating that the axis-min value has been customized + * flag indicating that the axis-min yValue has been customized */ protected boolean mCustomAxisMin = false; /** - * flag indicating that the axis-max value has been customized + * flag indicating that the axis-max yValue has been customized */ protected boolean mCustomAxisMax = false; @@ -242,10 +242,10 @@ public boolean isDrawLabelsEnabled() { } /** - * Sets the number of label entries for the y-axis max = 25, min = 2, default: 6, be aware + * Sets the number of label entries for the yPx-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 yPx-axis labels that sould be displayed */ public void setLabelCount(int count) { @@ -258,7 +258,7 @@ public void setLabelCount(int count) { } /** - * Returns the number of label entries the y-axis should have + * Returns the number of label entries the yPx-axis should have * * @return */ @@ -381,7 +381,7 @@ public float getAxisMinimum() { } /** - * By calling this method, any custom maximum value that has been previously set is reseted, + * By calling this method, any custom maximum yValue that has been previously set is reseted, * and the calculation is * done automatically. */ @@ -390,7 +390,7 @@ public void resetAxisMaxValue() { } /** - * Returns true if the axis max value has been customized (and is not calculated automatically) + * Returns true if the axis max yValue has been customized (and is not calculated automatically) * * @return */ @@ -399,7 +399,7 @@ public boolean isAxisMaxCustom() { } /** - * By calling this method, any custom minimum value that has been previously set is reseted, + * By calling this method, any custom minimum yValue that has been previously set is reseted, * and the calculation is * done automatically. */ @@ -408,7 +408,7 @@ public void resetAxisMinValue() { } /** - * Returns true if the axis min value has been customized (and is not calculated automatically) + * Returns true if the axis min yValue has been customized (and is not calculated automatically) * * @return */ @@ -417,11 +417,11 @@ public boolean isAxisMinCustom() { } /** - * Set a custom minimum value for this axis. If set, this value will not be calculated + * Set a custom minimum yValue for this axis. If set, this yValue will not be calculated * automatically depending on * the provided data. Use resetAxisMinValue() to undo this. Do not forget to call * setStartAtZero(false) if you use - * this method. Otherwise, the axis-minimum value will still be forced to 0. + * this method. Otherwise, the axis-minimum yValue will still be forced to 0. * * @param min */ @@ -431,7 +431,7 @@ public void setAxisMinValue(float min) { } /** - * Set a custom maximum value for this axis. If set, this value will not be calculated + * Set a custom maximum yValue for this axis. If set, this yValue will not be calculated * automatically depending on * the provided data. Use resetAxisMaxValue() to undo this. * @@ -446,12 +446,12 @@ public void setAxisMaxValue(float max) { * Calculates the minimum / maximum and range values of the axis with the given * minimum and maximum values from the chart data. * - * @param dataMin the min value according to chart data - * @param dataMax the max value according to chart data + * @param dataMin the min yValue according to chart data + * @param dataMax the max yValue according to chart data */ public void calculate(float dataMin, float dataMax) { - // if custom, use value as is, else use data value + // if custom, use yValue as is, else use data yValue float min = mCustomAxisMin ? mAxisMinimum : dataMin; float max = mCustomAxisMax ? mAxisMaximum : dataMax; 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 713f89dd79..77bbe97ca7 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,7 +19,7 @@ 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 axis labels have on the xPx-axis */ protected float mXOffset = 5f; @@ -48,7 +48,7 @@ public ComponentBase() { } /** - * Returns the used offset on the x-axis for drawing the axis or legend + * Returns the used offset on the xPx-axis for drawing the axis or legend * labels. This offset is applied before and after the label. * * @return @@ -58,7 +58,7 @@ public float getXOffset() { } /** - * Sets the used x-axis offset for the labels on this axis. + * Sets the used xPx-axis offset for the labels on this axis. * * @param xOffset */ @@ -67,7 +67,7 @@ public void setXOffset(float xOffset) { } /** - * Returns the used offset on the x-axis for drawing the axis labels. This + * Returns the used offset on the xPx-axis for drawing the axis labels. This * offset is applied before and after the label. * * @return @@ -77,7 +77,7 @@ public float getYOffset() { } /** - * Sets the used y-axis offset for the labels on this axis. For the legend, + * Sets the used yPx-axis offset for the labels on this axis. For the legend, * higher offset means the legend as a whole will be placed further away * from the top. * 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 77f1710f27..0514d91f32 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 @@ -76,7 +76,7 @@ public enum LegendDirection { private String[] mExtraLabels; /** - * Are the legend labels/colors a custom value or auto calculated? If false, + * Are the legend labels/colors a custom yValue or auto calculated? If false, * then it's auto, if true, then custom. default false (automatic legend) */ private boolean mIsLegendCustom = false; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java index 8fcdee8fc1..7b826fae30 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java @@ -11,13 +11,13 @@ /** * The limit line is an additional feature for all Line-, Bar- and * ScatterCharts. It allows the displaying of an additional line in the chart - * that marks a certain maximum / limit on the specified axis (x- or y-axis). + * that marks a certain maximum / limit on the specified axis (xPx- or yPx-axis). * * @author Philipp Jahoda */ public class LimitLine extends ComponentBase { - /** limit / maximum (the y-value or xIndex) */ + /** limit / maximum (the yPx-yValue or xIndex) */ private float mLimit = 0f; /** the width of the limit line */ @@ -46,7 +46,7 @@ public enum LimitLabelPosition { /** * Constructor with limit. * - * @param limit - the position (the value) on the y-axis (y-value) or x-axis + * @param limit - the position (the yValue) on the yPx-axis (yPx-yValue) or xPx-axis * (xIndex) where this line should appear */ public LimitLine(float limit) { @@ -56,7 +56,7 @@ public LimitLine(float limit) { /** * Constructor with limit and label. * - * @param limit - the position (the value) on the y-axis (y-value) or x-axis + * @param limit - the position (the yValue) on the yPx-axis (yPx-yValue) or xPx-axis * (xIndex) where this line should appear * @param label - provide "" if no label is required */ @@ -157,7 +157,7 @@ public DashPathEffect getDashPathEffect() { } /** - * Sets the color of the value-text that is drawn next to the LimitLine. + * Sets the color of the yValue-text that is drawn next to the LimitLine. * Default: Paint.Style.FILL_AND_STROKE * * @param style @@ -167,7 +167,7 @@ public void setTextStyle(Paint.Style style) { } /** - * Returns the color of the value-text that is drawn next to the LimitLine. + * Returns the color of the yValue-text that is drawn next to the LimitLine. * * @return */ @@ -176,7 +176,7 @@ public Paint.Style getTextStyle() { } /** - * Sets the position of the LimitLine value label (either on the right or on + * Sets the position of the LimitLine yValue label (either on the right or on * the left edge of the chart). Not supported for RadarChart. * * @param pos @@ -186,7 +186,7 @@ public void setLabelPosition(LimitLabelPosition pos) { } /** - * Returns the position of the LimitLine label (value). + * Returns the position of the LimitLine label (yValue). * * @return */ 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..ca99c59b09 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 @@ -68,25 +68,25 @@ public void draw(Canvas canvas, float posx, float posy) { * * @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 + * @param highlight the highlight object contains information about the highlighted yValue such as it's dataset-index, the * selected range or stack-index (only stacked bar entries). */ public abstract void refreshContent(Entry e, Highlight highlight); /** - * Use this to return the desired offset you wish the MarkerView to have on the x-axis. By returning -(getWidth() / + * Use this to return the desired offset you wish the MarkerView to have on the xPx-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 + * @param xpos the position on the xPx-axis in pixels where the marker is drawn * @return */ public abstract int getXOffset(float xpos); /** - * 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. + * Use this to return the desired position offset you wish the MarkerView to have on the yPx-axis. By returning + * -getHeight() you will cause the MarkerView to be above the selected yValue. * - * @param ypos the position on the y-axis in pixels where the marker is drawn + * @param ypos the position on the yPx-axis in pixels where the marker is drawn * @return */ public abstract int getYOffset(float ypos); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java index d49605c3b8..3617cb69aa 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java @@ -10,7 +10,7 @@ import java.util.List; /** - * Class representing the x-axis labels settings. Only use the setter methods to + * Class representing the xPx-axis labels settings. Only use the setter methods to * modify it. Do not access public variables directly. Be aware that not all * features the XLabels class provides are suitable for the RadarChart. * @@ -18,29 +18,29 @@ */ public class XAxis extends AxisBase { - /** the arraylist containing all the x-axis labels */ + /** the arraylist containing all the xPx-axis labels */ protected List mValues = new ArrayList(); /** - * width of the x-axis labels in pixels - this is automatically + * width of the xPx-axis labels in pixels - this is automatically * calculated by the computeSize() methods in the renderers */ public int mLabelWidth = 1; /** - * height of the x-axis labels in pixels - this is automatically + * height of the xPx-axis labels in pixels - this is automatically * calculated by the computeSize() methods in the renderers */ public int mLabelHeight = 1; /** - * width of the (rotated) x-axis labels in pixels - this is automatically + * width of the (rotated) xPx-axis labels in pixels - this is automatically * calculated by the computeSize() methods in the renderers */ public int mLabelRotatedWidth = 1; /** - * height of the (rotated) x-axis labels in pixels - this is automatically + * height of the (rotated) xPx-axis labels in pixels - this is automatically * calculated by the computeSize() methods in the renderers */ public int mLabelRotatedHeight = 1; @@ -51,20 +51,20 @@ public class XAxis extends AxisBase { protected float mLabelRotationAngle = 0f; /** - * the space that should be left out (in characters) between the x-axis + * the space that should be left out (in characters) between the xPx-axis * labels */ private int mSpaceBetweenLabels = 4; /** - * the modulus that indicates if a value at a specified index in an - * array(list) for the x-axis-labels is drawn or not. If index % modulus == + * the modulus that indicates if a yValue at a specified index in an + * array(list) for the xPx-axis-labels is drawn or not. If index % modulus == * 0 DRAW, else dont draw. */ public int mAxisLabelModulus = 1; /** - * Is axisLabelModulus a custom value or auto calculated? If false, then + * Is axisLabelModulus a custom yValue or auto calculated? If false, then * it's auto, if true, then custom. default: false (automatic modulus) */ private boolean mIsAxisModulusCustom = false; @@ -75,14 +75,14 @@ public class XAxis extends AxisBase { private boolean mAvoidFirstLastClipping = false; /** - * Custom formatter for adjusting x-value strings + * Custom formatter for adjusting xPx-yValue strings */ protected XAxisValueFormatter mXAxisValueFormatter = new DefaultXAxisValueFormatter(); - /** the position of the x-labels relative to the chart */ + /** the position of the xPx-labels relative to the chart */ private XAxisPosition mPosition = XAxisPosition.TOP; - /** enum for the position of the x-labels relative to the chart */ + /** enum for the position of the xPx-labels relative to the chart */ public enum XAxisPosition { TOP, BOTTOM, BOTH_SIDED, TOP_INSIDE, BOTTOM_INSIDE } @@ -94,14 +94,14 @@ public XAxis() { } /** - * returns the position of the x-labels + * returns the position of the xPx-labels */ public XAxisPosition getPosition() { return mPosition; } /** - * sets the position of the x-labels + * sets the position of the xPx-labels * * @param pos */ @@ -126,7 +126,7 @@ public void setLabelRotationAngle(float angle) { } /** - * Sets the space (in characters) that should be left out between the x-axis + * Sets the space (in characters) that should be left out between the xPx-axis * labels, default 4. This only applies if the number of labels that will be * skipped in between drawn axis labels is not custom set. * @@ -156,7 +156,7 @@ public void setLabelsToSkip(int count) { /** * Calling this will disable a custom number of labels to be skipped (set by - * setLabelsToSkip(...)) while drawing the x-axis. Instead, the number of + * setLabelsToSkip(...)) while drawing the xPx-axis. Instead, the number of * values to skip will again be calculated automatically. */ public void resetLabelsToSkip() { @@ -175,7 +175,7 @@ public boolean isAxisModulusCustom() { /** * Returns the space (in characters) that should be left out between the - * x-axis labels + * xPx-axis labels */ public int getSpaceBetweenLabels() { return mSpaceBetweenLabels; @@ -219,7 +219,7 @@ public List getValues() { } /** - * Adds a new x-value to the chart data. + * Adds a new xPx-yValue to the chart data. * * @param xVal */ @@ -228,7 +228,7 @@ public void addXValue(XAxisValue xVal) { } /** - * Removes the x-value at the specified index. + * Removes the xPx-yValue at the specified index. * * @param index */ @@ -239,7 +239,7 @@ public void removeXValue(int index) { /** * Sets a custom XAxisValueFormatter for the data object that allows custom-formatting - * of all x-values before rendering them. Provide null to reset back to the + * of all xPx-values before rendering them. Provide null to reset back to the * default formatting. * * @param formatter 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 82c4c10e6d..347d89ce96 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 @@ -9,11 +9,11 @@ import com.github.mikephil.charting.utils.Utils; /** - * Class representing the y-axis labels settings and its entries. Only use the setter methods to + * Class representing the yPx-axis labels settings and its entries. Only use the setter methods to * modify it. Do not * access public variables directly. Be aware that not all features the YLabels class provides * are suitable for the - * RadarChart. Customizations that affect the value range of the axis need to be applied before + * RadarChart. Customizations that affect the yValue range of the axis need to be applied before * setting data for the * chart. * @@ -27,12 +27,12 @@ public class YAxis extends AxisBase { protected YAxisValueFormatter mYAxisValueFormatter; /** - * indicates if the top y-label entry is drawn or not + * indicates if the top yPx-label entry is drawn or not */ private boolean mDrawTopYLabelEntry = true; /** - * if true, the y-labels show only the minimum and maximum value + * if true, the yPx-labels show only the minimum and maximum yValue */ protected boolean mShowOnlyMinMax = false; @@ -42,7 +42,7 @@ public class YAxis extends AxisBase { protected boolean mInverted = false; /** - * if true, the set number of y-labels will be forced + * if true, the set number of yPx-labels will be forced */ protected boolean mForceLabels = false; @@ -62,22 +62,22 @@ public class YAxis extends AxisBase { protected float mZeroLineWidth = 1f; /** - * axis space from the largest value to the top in percent of the total axis range + * axis space from the largest yValue to the top in percent of the total axis range */ protected float mSpacePercentTop = 10f; /** - * axis space from the smallest value to the bottom in percent of the total axis range + * axis space from the smallest yValue to the bottom in percent of the total axis range */ protected float mSpacePercentBottom = 10f; /** - * the position of the y-labels relative to the chart + * the position of the yPx-labels relative to the chart */ private YAxisLabelPosition mPosition = YAxisLabelPosition.OUTSIDE_CHART; /** - * enum for the position of the y-labels relative to the chart + * enum for the position of the yPx-labels relative to the chart */ public enum YAxisLabelPosition { OUTSIDE_CHART, INSIDE_CHART @@ -105,7 +105,7 @@ public enum YAxisLabelPosition { /** * When true, axis labels are controlled by the `granularity` property. * When false, axis values could possibly be repeated. - * This could happen if two adjacent axis values are rounded to same value. + * This could happen if two adjacent axis values are rounded to same yValue. * If using granularity this could be avoided by having fewer axis values visible. */ protected boolean mGranularityEnabled = false; @@ -182,7 +182,7 @@ public boolean isGranularityEnabled() { } /** - * Enabled/disable granularity control on axis value intervals. If enabled, the axis + * Enabled/disable granularity control on axis yValue intervals. If enabled, the axis * interval is not allowed to go below a certain granularity. Default: false * * @param enabled @@ -211,14 +211,14 @@ public void setGranularity(float granularity) { } /** - * returns the position of the y-labels + * returns the position of the yPx-labels */ public YAxisLabelPosition getLabelPosition() { return mPosition; } /** - * sets the position of the y-labels + * sets the position of the yPx-labels * * @param pos */ @@ -227,7 +227,7 @@ public void setPosition(YAxisLabelPosition pos) { } /** - * returns true if drawing the top y-axis label entry is enabled + * returns true if drawing the top yPx-axis label entry is enabled * * @return */ @@ -236,9 +236,9 @@ public boolean isDrawTopYLabelEntryEnabled() { } /** - * set this to true to enable drawing the top y-label entry. Disabling this can be helpful - * when the top y-label and - * left x-label interfere with each other. default: true + * set this to true to enable drawing the top yPx-label entry. Disabling this can be helpful + * when the top yPx-label and + * left xPx-label interfere with each other. default: true * * @param enabled */ @@ -247,11 +247,11 @@ public void setDrawTopYLabelEntry(boolean enabled) { } /** - * sets the number of label entries for the y-axis max = 25, min = 2, default: 6, be aware + * sets the number of label entries for the yPx-axis max = 25, min = 2, default: 6, be aware * 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 yPx-axis labels that sould 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 @@ -264,7 +264,7 @@ public void setLabelCount(int count, boolean force) { } /** - * Returns true if focing the y-label count is enabled. Default: false + * Returns true if focing the yPx-label count is enabled. Default: false * * @return */ @@ -273,7 +273,7 @@ public boolean isForceLabelsEnabled() { } /** - * If enabled, the YLabels will only show the minimum and maximum value of the chart. This + * If enabled, the YLabels will only show the minimum and maximum yValue of the chart. This * will ignore/override the * set label count. * @@ -284,7 +284,7 @@ public void setShowOnlyMinMax(boolean enabled) { } /** - * Returns true if showing only min and max value is enabled. + * Returns true if showing only min and max yValue is enabled. * * @return */ @@ -293,7 +293,7 @@ public boolean isShowOnlyMinMaxEnabled() { } /** - * If this is set to true, the y-axis is inverted which means that low values are on top of + * If this is set to true, the yPx-axis is inverted which means that low values are on top of * the chart, high values * on bottom. * @@ -304,7 +304,7 @@ public void setInverted(boolean enabled) { } /** - * If this returns true, the y-axis is inverted. + * If this returns true, the yPx-axis is inverted. * * @return */ @@ -459,7 +459,7 @@ public String getLongestLabel() { } /** - * Returns the formatted y-label at the specified index. This will either use the + * Returns the formatted yPx-label at the specified index. This will either use the * auto-formatter or the custom * formatter (if one is set). * @@ -535,7 +535,7 @@ public boolean needsOffset() { @Override public void calculate(float dataMin, float dataMax) { - // if custom, use value as is, else use data value + // if custom, use yValue as is, else use data yValue float min = mCustomAxisMin ? mAxisMinimum : dataMin; float max = mCustomAxisMax ? mAxisMaximum : dataMax; 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 152b596e91..cacf466a80 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 @@ -16,7 +16,7 @@ public class BarDataSet extends BarLineScatterCandleBubbleDataSet impl private float mBarSpace = 0.15f; /** - * the maximum number of bars that are stacked upon each other, this value + * the maximum number of bars that are stacked upon each other, this yValue * is calculated from the Entries that are added to the DataSet */ private int mStackSize = 1; @@ -31,12 +31,12 @@ public class BarDataSet extends BarLineScatterCandleBubbleDataSet impl private int mBarBorderColor = Color.BLACK; /** - * the alpha value used to draw the highlight indicator bar + * the alpha yValue used to draw the highlight indicator bar */ private int mHighLightAlpha = 120; /** - * the overall entry count, including counting each stack-value individually + * the overall entry count, including counting each stack-yValue individually */ private int mEntryCountStacks = 0; @@ -176,7 +176,7 @@ public boolean isStacked() { } /** - * returns the overall entry count, including counting each stack-value + * returns the overall entry count, including counting each stack-yValue * individually * * @return @@ -186,7 +186,7 @@ public int getEntryCountStacks() { } /** - * returns the space between bars in percent of the whole width of one value + * returns the space between bars in percent of the whole width of one yValue * * @return */ @@ -210,7 +210,7 @@ public void setBarSpacePercent(float percent) { /** * Sets the color used for drawing the bar-shadows. The bar shadows is a - * surface behind the bar that indicates the maximum value. Don't for get to + * surface behind the bar that indicates the maximum yValue. Don't for get to * use getResources().getColor(...) to set this. Or Color.rgb(...). * * @param color @@ -265,7 +265,7 @@ public int getBarBorderColor() { } /** - * Set the alpha value (transparency) that is used for drawing the highlight + * Set the alpha yValue (transparency) that is used for drawing the highlight * indicator bar. min = 0 (fully transparent), max = 255 (fully opaque) * * @param alpha 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 97358ae378..62b2a8a940 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 @@ -84,7 +84,7 @@ public BarEntry copy() { } /** - * Returns the stacked values this BarEntry represents, or null, if only a single value is represented (then, use + * Returns the stacked values this BarEntry represents, or null, if only a single yValue is represented (then, use * getY()). * * @return @@ -105,7 +105,7 @@ public void setVals(float[] vals) { } /** - * Returns the value of this BarEntry. If the entry is stacked, it returns the positive sum of all values. + * Returns the yValue of this BarEntry. If the entry is stacked, it returns the positive sum of all values. * * @return */ 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 e218dec225..1cdddda683 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 @@ -42,7 +42,7 @@ public abstract class BaseDataSet implements IDataSet { protected YAxis.AxisDependency mAxisDependency = YAxis.AxisDependency.LEFT; /** - * if true, value highlightning is enabled + * if true, yValue highlightning is enabled */ protected boolean mHighlightEnabled = true; @@ -52,17 +52,17 @@ public abstract class BaseDataSet implements IDataSet { protected transient ValueFormatter mValueFormatter; /** - * the typeface used for the value text + * the typeface used for the yValue text */ protected Typeface mValueTypeface; /** - * if true, y-values are drawn on the chart + * if true, yPx-values are drawn on the chart */ protected boolean mDrawValues = true; /** - * the size of the value-text labels + * the size of the yValue-text labels */ protected float mValueTextSize = 17f; @@ -196,7 +196,7 @@ public void setColor(int color) { } /** - * Sets a color with a specific alpha value. + * Sets a color with a specific alpha yValue. * * @param color * @param alpha from 0-255 @@ -206,7 +206,7 @@ public void setColor(int color, int alpha) { } /** - * Sets colors with a specific alpha value. + * Sets colors with a specific alpha yValue. * * @param colors * @param alpha 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..0778e57673 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 @@ -4,7 +4,7 @@ import android.annotation.SuppressLint; /** - * Subclass of Entry that holds a value for one entry in a BubbleChart. Bubble + * Subclass of Entry that holds a yValue for one entry in a BubbleChart. Bubble * chart implementation: Copyright 2015 Pierre-Marc Airoldi Licensed under * Apache License 2.0 * @@ -13,14 +13,14 @@ @SuppressLint("ParcelCreator") public class BubbleEntry extends Entry { - /** size value */ + /** size yValue */ private float mSize = 0f; /** * Constructor. * - * @param x The value on the x-axis. - * @param y The value on the y-axis. + * @param x The yValue on the xPx-axis. + * @param y The yValue on the yPx-axis. * @param size The size of the bubble. */ public BubbleEntry(float x, float y, float size) { @@ -31,8 +31,8 @@ public BubbleEntry(float x, float y, float size) { /** * Constructor. * - * @param x The value on the x-axis. - * @param y The value on the y-axis. + * @param x The yValue on the xPx-axis. + * @param y The yValue on the yPx-axis. * @param size The size of the bubble. * @param data Spot for additional data this Entry represents. */ 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..b499540a64 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 @@ -11,26 +11,26 @@ @SuppressLint("ParcelCreator") public class CandleEntry extends Entry { - /** shadow-high value */ + /** shadow-high yValue */ private float mShadowHigh = 0f; - /** shadow-low value */ + /** shadow-low yValue */ private float mShadowLow = 0f; - /** close value */ + /** close yValue */ private float mClose = 0f; - /** open value */ + /** open yValue */ private float mOpen = 0f; /** * 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 yValue on the xPx-axis. + * @param shadowH The (shadow) high yValue. + * @param shadowL The (shadow) low yValue. + * @param open The open yValue. + * @param close The close yValue. */ public CandleEntry(float x, float shadowH, float shadowL, float open, float close) { super(x, (shadowH + shadowL) / 2f); @@ -44,9 +44,9 @@ 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 yValue on the xPx-axis. + * @param shadowH The (shadow) high yValue. + * @param shadowL The (shadow) low yValue. * @param open * @param close * @param data Spot for additional data this Entry represents. @@ -81,7 +81,7 @@ public float getBodyRange() { } /** - * Returns the center value of the candle. (Middle value between high and + * Returns the center yValue of the candle. (Middle yValue between high and * low) */ @Override @@ -98,7 +98,7 @@ public CandleEntry copy() { } /** - * Returns the upper shadows highest value. + * Returns the upper shadows highest yValue. * * @return */ @@ -111,7 +111,7 @@ public void setHigh(float mShadowHigh) { } /** - * Returns the lower shadows lowest value. + * Returns the lower shadows lowest yValue. * * @return */ @@ -124,7 +124,7 @@ public void setLow(float mShadowLow) { } /** - * Returns the bodys close value. + * Returns the bodys close yValue. * * @return */ @@ -137,7 +137,7 @@ public void setClose(float mClose) { } /** - * Returns the bodys open value. + * Returns the bodys open yValue. * * @return */ 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 70605b3a8a..dd4fcee6bf 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 @@ -15,29 +15,29 @@ /** * Class that holds all relevant data that represents the chart. That involves - * at least one (or more) DataSets, and an array of x-values. + * at least one (or more) DataSets, and an array of xPx-values. * * @author Philipp Jahoda */ public abstract class ChartData> { /** - * maximum y-value in the value array across all axes + * maximum yPx-yValue in the yValue array across all axes */ protected float mYMax = 0.0f; /** - * the minimum y-value in the value array across all axes + * the minimum yPx-yValue in the yValue array across all axes */ protected float mYMin = 0.0f; /** - * maximum x-value in the value array + * maximum xPx-yValue in the yValue array */ protected float mXMax = 0f; /** - * minimum x-value in the value array + * minimum xPx-yValue in the yValue array */ protected float mXMin = 0f; @@ -50,18 +50,18 @@ public abstract class ChartData> { protected float mRightAxisMin = 0.0f; /** - * total number of y-values across all DataSet objects + * total number of yPx-values across all DataSet objects */ private int mYValCount = 0; /** - * contains the maximum length (in characters) an entry in the x-vals array + * contains the maximum length (in characters) an entry in the xPx-vals array * has */ private float mXValMaximumLength = 0; /** - * holds all x-values the chart represents + * holds all xPx-values the chart represents */ protected List mXVals; @@ -81,7 +81,7 @@ public ChartData(T... dataSets) { } /** - * Constructor for only x-values. This constructor can be used for setting + * Constructor for only xPx-values. This constructor can be used for setting * up an empty chart without data. * * @param xVals @@ -93,7 +93,7 @@ public ChartData(List xVals) { } /** - * Constructor for only x-values. This constructor can be used for setting + * Constructor for only xPx-values. This constructor can be used for setting * up an empty chart without data. * * @param xVals @@ -107,7 +107,7 @@ public ChartData(XAxisValue[] xVals) { /** * constructor for chart data * - * @param xVals The values describing the x-axis. Must be at least as long + * @param xVals The values describing the xPx-axis. Must be at least as long * as the highest xIndex in the Entry objects across all * DataSets. * @param sets the dataset array @@ -122,7 +122,7 @@ public ChartData(List xVals, List sets) { /** * constructor that takes string array instead of List string * - * @param xVals The values describing the x-axis. Must be at least as long + * @param xVals The values describing the xPx-axis. Must be at least as long * as the highest xIndex in the Entry objects across all * DataSets. * @param sets the dataset array @@ -146,7 +146,7 @@ private List arrayToList(XAxisValue[] array) { /** * performs all kinds of initialization calculations, such as min-max and - * value count and sum + * yValue count and sum */ protected void init() { @@ -158,7 +158,7 @@ protected void init() { } /** - * calculates the average length (in characters) across all x-value strings + * calculates the average length (in characters) across all xPx-yValue strings */ private void calcXValMaximumLength() { @@ -181,7 +181,7 @@ private void calcXValMaximumLength() { } /** - * Checks if the combination of x-values array and DataSet array is legal or + * Checks if the combination of xPx-values array and DataSet array is legal or * not. */ private void checkLegal() { @@ -195,7 +195,7 @@ private void checkLegal() { for (int i = 0; i < mDataSets.size(); i++) { if (mDataSets.get(i).getEntryCount() > mXVals.size()) { // throw new IllegalArgumentException( -// "One or more of the DataSet Entry arrays are longer than the x-values array of this ChartData object."); +// "One or more of the DataSet Entry arrays are longer than the xPx-values array of this ChartData object."); } } } @@ -210,7 +210,7 @@ public void notifyDataChanged() { } /** - * calc minimum and maximum values (both x and y) over all DataSets + * calc minimum and maximum values (both xPx and yPx) over all DataSets */ public void calcMinMax() { @@ -297,7 +297,7 @@ public void calcMinMax() { } /** - * Calculates the total number of y-values across all DataSets the ChartData + * Calculates the total number of yPx-values across all DataSets the ChartData * represents. * * @return @@ -332,7 +332,7 @@ public int getDataSetCount() { } /** - * Returns the smallest y-value the data object contains. + * Returns the smallest yPx-yValue the data object contains. * * @return */ @@ -341,7 +341,7 @@ public float getYMin() { } /** - * Returns the minimum y-value for the specified axis. + * Returns the minimum yPx-yValue for the specified axis. * * @param axis * @return @@ -354,7 +354,7 @@ public float getYMin(AxisDependency axis) { } /** - * Returns the greatest y-value the data object contains. + * Returns the greatest yPx-yValue the data object contains. * * @return */ @@ -363,7 +363,7 @@ public float getYMax() { } /** - * Returns the maximum y-value for the specified axis. + * Returns the maximum yPx-yValue for the specified axis. * * @param axis * @return @@ -376,7 +376,7 @@ public float getYMax(AxisDependency axis) { } /** - * Returns the minimum x-value this data object contains. + * Returns the minimum xPx-yValue this data object contains. * * @return */ @@ -385,7 +385,7 @@ public float getXMin() { } /** - * Returns the maximum x-value this data object contains. + * Returns the maximum xPx-yValue this data object contains. * * @return */ @@ -395,7 +395,7 @@ public float getXMax() { /** * returns the maximum length (in characters) across all values in the - * x-vals array + * xPx-vals array * * @return */ @@ -404,7 +404,7 @@ public float getXValMaximumLength() { } /** - * Returns the total number of y-values across all DataSet objects the this + * Returns the total number of yPx-values across all DataSet objects the this * object represents. * * @return @@ -414,7 +414,7 @@ public int getYValCount() { } /** - * returns the x-values the chart represents + * returns the xPx-values the chart represents * * @return */ @@ -423,7 +423,7 @@ public List getXVals() { } /** - * sets the x-values the chart represents + * sets the xPx-values the chart represents * */ public void setXVals(List xVals) { @@ -431,7 +431,7 @@ public void setXVals(List xVals) { } /** - * Adds a new x-value to the chart data. + * Adds a new xPx-yValue to the chart data. * * @param xVal */ @@ -444,7 +444,7 @@ public void addXValue(XAxisValue xVal) { } /** - * Removes the x-value at the specified index. + * Removes the xPx-yValue at the specified index. * * @param index */ @@ -484,8 +484,8 @@ protected int getDataSetIndexByLabel(List dataSets, String label, } /** - * returns the total number of x-values this ChartData object represents - * (the size of the x-values array) + * returns the total number of xPx-values this ChartData object represents + * (the size of the xPx-values array) * * @return */ @@ -519,19 +519,26 @@ public Entry getEntryForHighlight(Highlight highlight) { if (highlight.getDataSetIndex() >= mDataSets.size()) return null; else { - // The value of the highlighted entry could be NaN - - // if we are not interested in highlighting a specific value. - - List entries = mDataSets.get(highlight.getDataSetIndex()) - .getEntriesForXPos(highlight.getX()); - for (Object entry : entries) - if (((Entry)entry).getY() == highlight.getY() || - Float.isNaN(highlight.getY())) - return (Entry)entry; - - return null; + return mDataSets.get(highlight.getDataSetIndex()).getEntryForXPos(highlight.getX()); } } +// public Entry getEntryForHighlight(Highlight highlight) { +// if (highlight.getDataSetIndex() >= mDataSets.size()) +// return null; +// else { +// // The yValue of the highlighted entry could be NaN - +// // if we are not interested in highlighting a specific yValue. +// +// List entries = mDataSets.get(highlight.getDataSetIndex()) +// .getEntriesForXPos(highlight.getX()); +// for (Object entry : entries) +// if (((Entry)entry).getY() == highlight.getY() || +// Float.isNaN(highlight.getY())) +// return (Entry)entry; +// +// return null; +// } +// } /** * Returns the DataSet object with the given label. Search can be case @@ -880,7 +887,7 @@ public T getFirstRight() { } /** - * Generates an x-values array filled with numbers in range specified by the + * Generates an xPx-values array filled with numbers in range specified by the * parameters. Can be used for convenience. * * @return @@ -912,7 +919,7 @@ public void setValueFormatter(ValueFormatter f) { } /** - * Sets the color of the value-text (color in which the value-labels are + * Sets the color of the yValue-text (color in which the yValue-labels are * drawn) for all DataSets this data object contains. * * @param color @@ -924,7 +931,7 @@ public void setValueTextColor(int color) { } /** - * Sets the same list of value-colors for all DataSets this + * Sets the same list of yValue-colors for all DataSets this * data object contains. * * @param colors @@ -936,7 +943,7 @@ public void setValueTextColors(List colors) { } /** - * Sets the Typeface for all value-labels for all DataSets this data object + * Sets the Typeface for all yValue-labels for all DataSets this data object * contains. * * @param tf @@ -948,7 +955,7 @@ public void setValueTypeface(Typeface tf) { } /** - * Sets the size (in dp) of the value-text for all DataSets this data object + * Sets the size (in dp) of the yValue-text for all DataSets this data object * contains. * * @param size @@ -960,7 +967,7 @@ public void setValueTextSize(float size) { } /** - * Enables / disables drawing values (value-text) for all DataSets this data + * Enables / disables drawing values (yValue-text) for all DataSets this data * object contains. * * @param enabled 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 845499b7db..c6341f77e8 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 @@ -139,8 +139,8 @@ public Entry getEntryForHighlight(Highlight highlight) { 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. + // The yValue of the highlighted entry could be NaN - + // if we are not interested in highlighting a specific yValue. List entries = data.getDataSetByIndex(highlight.getDataSetIndex()) .getEntriesForXPos(highlight.getX()); 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 f260339ca7..8c2eeb2346 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 @@ -20,22 +20,22 @@ public abstract class DataSet extends BaseDataSet { protected List mValues = null; /** - * maximum y-value in the value array + * maximum yPx-yValue in the yValue array */ protected float mYMax = 0.0f; /** - * minimum y-value in the value array + * minimum yPx-yValue in the yValue array */ protected float mYMin = 0.0f; /** - * maximum x-value in the value array + * maximum xPx-yValue in the yValue array */ protected float mXMax = 0.0f; /** - * minimum x-value in the value array + * minimum xPx-yValue in the yValue array */ protected float mXMin = 0.0f; @@ -108,7 +108,7 @@ public int getEntryCount() { } /** - * Returns the array of y-values that this DataSet represents. + * Returns the array of yPx-values that this DataSet represents. * * @return */ @@ -117,7 +117,7 @@ public List getYVals() { } /** - * Sets the array of y-values that this DataSet represents, and calls notifyDataSetChanged() + * Sets the array of yPx-values that this DataSet represents, and calls notifyDataSetChanged() * * @return */ @@ -396,7 +396,7 @@ public List getEntriesForXPos(float xVal) { /** * Determines how to round DataSet index values for * {@link DataSet#getEntryIndex(float, Rounding)} DataSet.getEntryIndex()} - * when an exact x-index is not found. + * when an exact xPx-index is not found. */ public enum Rounding { UP, 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 12a98ac6ff..33f1c6d246 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 @@ -7,16 +7,16 @@ /** * Class representing one entry in the chart. Might contain multiple values. - * Might only contain a single value depending on the used constructor. + * Might only contain a single yValue depending on the used constructor. * * @author Philipp Jahoda */ public class Entry implements Parcelable { - /** the y value */ + /** the yPx yValue */ private float y = 0f; - /** the x value */ + /** the xPx yValue */ private float x = 0f; /** optional spot for additional data this Entry represents */ @@ -25,8 +25,8 @@ public class Entry implements Parcelable { /** * 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 x the xPx yValue + * @param y the yPx yValue (the actual yValue of the entry) */ public Entry(float x, float y) { this.y = y; @@ -36,8 +36,8 @@ public Entry(float x, float y) { /** * 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 x the xPx yValue + * @param y the yPx yValue (the actual yValue of the entry) * @param data Spot for additional data this Entry represents. */ public Entry(float x, float y, Object data) { @@ -46,7 +46,7 @@ public Entry(float x, float y, Object data) { } /** - * Returns the x-value of this Entry object. + * Returns the xPx-yValue of this Entry object. * * @return */ @@ -55,7 +55,7 @@ public float getX() { } /** - * Sets the x-value of this Entry object. + * Sets the xPx-yValue of this Entry object. * * @param x */ @@ -64,7 +64,7 @@ public void setX(float x) { } /** - * Returns the y value of this Entry. + * Returns the yPx yValue of this Entry. * * @return */ @@ -73,7 +73,7 @@ public float getY() { } /** - * Sets the y-value for the Entry. + * Sets the yPx-yValue for the Entry. * * @param y */ @@ -111,7 +111,7 @@ public Entry copy() { } /** - * Compares value, xIndex and data of the entries. Returns true if entries + * Compares yValue, xIndex and data of the entries. Returns true if entries * are equal in those points, false if not. Does not check by hash-code like * it's done by the "equals" method. * @@ -136,7 +136,7 @@ public boolean equalTo(Entry e) { } /** - * returns a string representation of the entry containing x-index and value + * returns a string representation of the entry containing xPx-index and yValue */ @Override public String toString() { 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 78b92da3a8..64937fba99 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 @@ -25,10 +25,10 @@ public class LineDataSet extends LineRadarDataSet implements ILineDataSet /** the color of the inner circles */ private int mCircleColorHole = Color.WHITE; - /** the radius of the circle-shaped value indicators */ + /** the radius of the circle-shaped yValue indicators */ private float mCircleRadius = 8f; - /** the hole radius of the circle-shaped value indicators */ + /** the hole radius of the circle-shaped yValue indicators */ private float mCircleHoleRadius = 4f; /** sets the intensity of the cubic lines */ @@ -155,7 +155,7 @@ public float getCircleHoleRadius() { } /** - * sets the size (radius) of the circle shpaed value indicators, + * sets the size (radius) of the circle shpaed yValue indicators, * default size = 4f * * This method is deprecated because of unclarity. Use setCircleRadius instead. 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..14dc008243 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 @@ -84,7 +84,7 @@ public int getFillAlpha() { } /** - * sets the alpha value (transparency) that is used for filling the line + * sets the alpha yValue (transparency) that is used for filling the line * surface (0-255), default: 85 * * @param alpha 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 2af374e5da..4df8aee73f 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 @@ -8,7 +8,7 @@ /** * A PieData object can only represent one DataSet. Unlike all other charts, the - * legend labels of the PieChart are created from the x-values array, and not + * legend labels of the PieChart are created from the xPx-values array, and not * from the DataSet labels. Each PieData object can only represent one * PieDataSet (multiple PieDataSets inside a single PieChart are not possible). * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/XAxisValue.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/XAxisValue.java index eef5f638e5..cc0fde6e6f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/XAxisValue.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/XAxisValue.java @@ -6,19 +6,19 @@ public class XAxisValue { /** - * the label that describes this value + * the label that describes this yValue */ private String mLabel = ""; /** - * the position of this value on the x-axis + * the position of this yValue on the xPx-axis */ private double mPosition; /** * Constructor only with label. This is relevant for pie and radarchart. * - * @param label the x-axis label of this value + * @param label the xPx-axis label of this yValue */ public XAxisValue(String label) { this.mLabel = label; @@ -27,8 +27,8 @@ public XAxisValue(String label) { /** * Constructor. * - * @param xPosition the position of this value on the x-axis - * @param label the x-axis label of this value + * @param xPosition the position of this yValue on the xPx-axis + * @param label the xPx-axis label of this yValue */ public XAxisValue(double xPosition, String label) { this.mLabel = label; @@ -36,7 +36,7 @@ public XAxisValue(double xPosition, String label) { } /** - * Sets both x-position and label. + * Sets both xPx-position and label. * * @param xPosition * @param label @@ -47,7 +47,7 @@ public void set(double xPosition, String label) { } /** - * Returns the position (x) of the value on the x-axis. + * Returns the position (xPx) of the yValue on the xPx-axis. * * @return */ @@ -56,7 +56,7 @@ public double getPosition() { } /** - * Returns the x-axis label of this value. + * Returns the xPx-axis label of this yValue. * * @return */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java index cbc630c562..4fac3f615d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java @@ -81,7 +81,7 @@ public void setType(ApproximatorType type) { } /** - * Sets the ratios for x- and y-axis, as well as the ratio of the scale + * Sets the ratios for xPx- and yPx-axis, as well as the ratio of the scale * levels * * @param deltaRatio @@ -163,7 +163,7 @@ private List reduceWithDouglasPeuker(List entries, double epsilon) * epsilon (tolerance) * * @param entries - * @param epsilon as y-value + * @param epsilon as yPx-yValue * @param start * @param end */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java index 238546f83c..168903dbac 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java @@ -28,32 +28,32 @@ public abstract class RealmBaseDataSet e protected List mValues; /** - * maximum y-value in the y-value array + * maximum yPx-yValue in the yPx-yValue array */ protected float mYMax = 0.0f; /** - * the minimum y-value in the y-value array + * the minimum yPx-yValue in the yPx-yValue array */ protected float mYMin = 0.0f; /** - * maximum x-value in the value array + * maximum xPx-yValue in the yValue array */ protected float mXMax = 0.0f; /** - * minimum x-value in the value array + * minimum xPx-yValue in the yValue array */ protected float mXMin = 0.0f; /** - * fieldname of the column that contains the y-values of this dataset + * fieldname of the column that contains the yPx-values of this dataset */ protected String mYValuesField; /** - * fieldname of the column that contains the x-values of this dataset + * fieldname of the column that contains the xPx-values of this dataset */ protected String mXValuesField; @@ -385,7 +385,7 @@ public RealmResults getResults() { } /** - * Returns the fieldname that represents the "y-values" in the realm-data. + * Returns the fieldname that represents the "yPx-values" in the realm-data. * * @return */ @@ -394,7 +394,7 @@ public String getYValuesField() { } /** - * Sets the field name that is used for getting the y-values out of the RealmResultSet. + * Sets the field name that is used for getting the yPx-values out of the RealmResultSet. * * @param yValuesField */ @@ -403,7 +403,7 @@ public void setYValuesField(String yValuesField) { } /** - * Returns the fieldname that represents the "x-values" in the realm-data. + * Returns the fieldname that represents the "xPx-values" in the realm-data. * * @return */ @@ -412,7 +412,7 @@ public String getXValuesField() { } /** - * Sets the field name that is used for getting the x-values out of the RealmResultSet. + * Sets the field name that is used for getting the xPx-values out of the RealmResultSet. * * @param xValuesField */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java index 31010f2489..73e95f80c2 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java @@ -82,7 +82,7 @@ public int getFillAlpha() { } /** - * sets the alpha value (transparency) that is used for filling the line + * sets the alpha yValue (transparency) that is used for filling the line * surface (0-255), default: 85 * * @param alpha diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java index a11ebcdc99..9e964b1eb6 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java @@ -26,7 +26,7 @@ public class RealmBarDataSet extends RealmBarLineScatterC private float mBarSpace = 0.15f; /** - * the maximum number of bars that are stacked upon each other, this value + * the maximum number of bars that are stacked upon each other, this yValue * is calculated from the Entries that are added to the DataSet */ private int mStackSize = 1; @@ -41,7 +41,7 @@ public class RealmBarDataSet extends RealmBarLineScatterC private int mBarBorderColor = Color.BLACK; /** - * the alpha value used to draw the highlight indicator bar + * the alpha yValue used to draw the highlight indicator bar */ private int mHighLightAlpha = 120; @@ -185,7 +185,7 @@ public boolean isStacked() { } /** - * returns the space between bars in percent of the whole width of one value + * returns the space between bars in percent of the whole width of one yValue * * @return */ @@ -209,7 +209,7 @@ public void setBarSpacePercent(float percent) { /** * Sets the color used for drawing the bar-shadows. The bar shadows is a - * surface behind the bar that indicates the maximum value. Don't for get to + * surface behind the bar that indicates the maximum yValue. Don't for get to * use getResources().getColor(...) to set this. Or Color.rgb(...). * * @param color @@ -264,7 +264,7 @@ public int getBarBorderColor() { } /** - * Set the alpha value (transparency) that is used for drawing the highlight + * Set the alpha yValue (transparency) that is used for drawing the highlight * indicator bar. min = 0 (fully transparent), max = 255 (fully opaque) * * @param alpha diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java index 196be07224..8c6e1d71c6 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java @@ -25,7 +25,7 @@ public class RealmBubbleDataSet extends RealmBarLineScatt * Constructor for creating a CandleDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value + * @param yValuesField the name of the field in your data object that represents the yPx-yValue * @param sizeField the name of the field in your data object that represents the bubble size */ public RealmBubbleDataSet(RealmResults result, String yValuesField, String sizeField) { @@ -40,8 +40,8 @@ public RealmBubbleDataSet(RealmResults result, String yValuesField, String si * Constructor for creating a CandleDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - * @param xIndexField the name of the field in your data object that represents the x-index + * @param yValuesField the name of the field in your data object that represents the yPx-yValue + * @param xIndexField the name of the field in your data object that represents the xPx-index * @param sizeField the name of the field in your data object that represents the bubble size */ public RealmBubbleDataSet(RealmResults result, String yValuesField, String xIndexField, String sizeField) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java index 9f0d43bfaf..53279e5008 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java @@ -82,10 +82,10 @@ public class RealmCandleDataSet extends RealmLineScatterC * Constructor for creating a LineDataSet with realm data. * * @param result the queried results from the realm database - * @param highField the name of the field in your data object that represents the "high" value - * @param lowField the name of the field in your data object that represents the "low" value - * @param openField the name of the field in your data object that represents the "open" value - * @param closeField the name of the field in your data object that represents the "close" value + * @param highField the name of the field in your data object that represents the "high" yValue + * @param lowField the name of the field in your data object that represents the "low" yValue + * @param openField the name of the field in your data object that represents the "open" yValue + * @param closeField the name of the field in your data object that represents the "close" yValue */ public RealmCandleDataSet(RealmResults result, String highField, String lowField, String openField, String closeField) { super(result, null); @@ -102,11 +102,11 @@ public RealmCandleDataSet(RealmResults result, String highField, String lowFi * Constructor for creating a LineDataSet with realm data. * * @param result the queried results from the realm database - * @param highField the name of the field in your data object that represents the "high" value - * @param lowField the name of the field in your data object that represents the "low" value - * @param openField the name of the field in your data object that represents the "open" value - * @param closeField the name of the field in your data object that represents the "close" value - * @param xIndexField the name of the field in your data object that represents the x-index + * @param highField the name of the field in your data object that represents the "high" yValue + * @param lowField the name of the field in your data object that represents the "low" yValue + * @param openField the name of the field in your data object that represents the "open" yValue + * @param closeField the name of the field in your data object that represents the "close" yValue + * @param xIndexField the name of the field in your data object that represents the xPx-index */ public RealmCandleDataSet(RealmResults result, String highField, String lowField, String openField, String closeField, String xIndexField) { super(result, null, xIndexField); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java index 4bca14d960..d5ac1405c7 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java @@ -37,11 +37,11 @@ public class RealmLineDataSet extends RealmLineRadarDataS private int mCircleColorHole = Color.WHITE; /** - * the radius of the circle-shaped value indicators + * the radius of the circle-shaped yValue indicators */ private float mCircleRadius = 8f; - /** the hole radius of the circle-shaped value indicators */ + /** the hole radius of the circle-shaped yValue indicators */ private float mCircleHoleRadius = 4f; /** @@ -70,7 +70,7 @@ public class RealmLineDataSet extends RealmLineRadarDataS * Constructor for creating a LineDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value + * @param yValuesField the name of the field in your data object that represents the yPx-yValue */ public RealmLineDataSet(RealmResults result, String yValuesField) { super(result, yValuesField); @@ -87,8 +87,8 @@ public RealmLineDataSet(RealmResults result, String yValuesField) { * Constructor for creating a LineDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - * @param xIndexField the name of the field in your data object that represents the x-index + * @param yValuesField the name of the field in your data object that represents the yPx-yValue + * @param xIndexField the name of the field in your data object that represents the xPx-index */ public RealmLineDataSet(RealmResults result, String yValuesField, String xIndexField) { super(result, yValuesField, xIndexField); @@ -142,7 +142,7 @@ public float getCubicIntensity() { } /** - * sets the size (radius) of the circle shpaed value indicators, default + * sets the size (radius) of the circle shpaed yValue indicators, default * size = 4f * * @param size diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java index 55f0903119..bb6d275e8a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java @@ -38,7 +38,7 @@ public class RealmPieDataSet extends RealmBaseDataSet result, String yValuesField) { super(result, yValuesField); @@ -51,8 +51,8 @@ public RealmPieDataSet(RealmResults result, String yValuesField) { * Constructor for creating a PieDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - * @param xIndexField the name of the field in your data object that represents the x-index + * @param yValuesField the name of the field in your data object that represents the yPx-yValue + * @param xIndexField the name of the field in your data object that represents the xPx-index */ public RealmPieDataSet(RealmResults result, String yValuesField, String xIndexField) { super(result, yValuesField, xIndexField); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java index 7688fec9f7..77b0b19da8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java @@ -32,7 +32,7 @@ public class RealmRadarDataSet extends RealmLineRadarData * Constructor for creating a RadarDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value + * @param yValuesField the name of the field in your data object that represents the yPx-yValue */ public RealmRadarDataSet(RealmResults result, String yValuesField) { super(result, yValuesField); @@ -45,8 +45,8 @@ public RealmRadarDataSet(RealmResults result, String yValuesField) { * Constructor for creating a RadarDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - * @param xIndexField the name of the field in your data object that represents the x-index + * @param yValuesField the name of the field in your data object that represents the yPx-yValue + * @param xIndexField the name of the field in your data object that represents the xPx-index */ public RealmRadarDataSet(RealmResults result, String yValuesField, String xIndexField) { super(result, yValuesField, xIndexField); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java index c532a3a186..18075feccc 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java @@ -44,7 +44,7 @@ public class RealmScatterDataSet extends RealmLineScatter * Constructor for creating a ScatterDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value + * @param yValuesField the name of the field in your data object that represents the yPx-yValue */ public RealmScatterDataSet(RealmResults result, String yValuesField) { super(result, yValuesField); @@ -57,8 +57,8 @@ public RealmScatterDataSet(RealmResults result, String yValuesField) { * Constructor for creating a ScatterDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - * @param xIndexField the name of the field in your data object that represents the x-index + * @param yValuesField the name of the field in your data object that represents the yPx-yValue + * @param xIndexField the name of the field in your data object that represents the xPx-index */ public RealmScatterDataSet(RealmResults result, String yValuesField, String xIndexField) { super(result, yValuesField, xIndexField); 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 bd66f51d08..fe92d5f4bb 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 @@ -8,7 +8,7 @@ /** * Default formatter used for formatting values inside the chart. Uses a DecimalFormat with - * pre-calculated number of digits (depending on max and min value). + * pre-calculated number of digits (depending on max and min yValue). * * @author Philipp Jahoda */ @@ -18,7 +18,7 @@ public class DefaultValueFormatter implements ValueFormatter { private DecimalFormat mFormat; /** - * Constructor that specifies to how many digits the value should be + * Constructor that specifies to how many digits the yValue should be * formatted. * * @param digits diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java index 383e195322..16277c72ed 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java @@ -4,8 +4,8 @@ /** * Created by Philipp Jahoda on 14/09/15. - * Default formatter class for adjusting x-values before drawing them. - * This simply returns the original value unmodified. + * Default formatter class for adjusting xPx-values before drawing them. + * This simply returns the original yValue unmodified. */ public class DefaultXAxisValueFormatter implements XAxisValueFormatter { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java index 5b8cd01452..33fff63545 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java @@ -7,7 +7,7 @@ /** * Created by Philipp Jahoda on 20/09/15. * Default formatter used for formatting labels of the YAxis. Uses a DecimalFormat with - * pre-calculated number of digits (depending on max and min value). + * pre-calculated number of digits (depending on max and min yValue). */ public class DefaultYAxisValueFormatter implements YAxisValueFormatter { @@ -15,7 +15,7 @@ public class DefaultYAxisValueFormatter implements YAxisValueFormatter { private DecimalFormat mFormat; /** - * Constructor that specifies to how many digits the value should be + * Constructor that specifies to how many digits the yValue should be * formatted. * * @param digits diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FillFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FillFormatter.java index 66895d77e7..22ba60528e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FillFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FillFormatter.java @@ -12,7 +12,7 @@ public interface FillFormatter { /** - * Returns the vertical (y-axis) position where the filled-line of the + * Returns the vertical (yPx-axis) position where the filled-line of the * LineDataSet should end. * * @param dataSet the ILineDataSet that is currently drawn 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 96b4ead74f..d5fc954fdc 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 @@ -8,7 +8,7 @@ import java.text.DecimalFormat; /** - * Predefined value-formatter that formats large numbers in a pretty way. + * Predefined yValue-formatter that formats large numbers in a pretty way. * Outputs: 856 = 856; 1000 = 1k; 5821 = 5.8k; 10500 = 10k; 101800 = 102k; * 2000000 = 2m; 7800000 = 7.8m; 92150000 = 92m; 123200000 = 123m; 9999999 = * 10m; 1000000000 = 1b; Special thanks to Roman Gromov @@ -53,7 +53,7 @@ public String getFormattedValue(float value, YAxis yAxis) { } /** - * Set an appendix text to be added at the end of the formatted value. + * Set an appendix text to be added at the end of the formatted yValue. * * @param appendix */ 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 6e3535d8b5..93fbe1fa25 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 @@ -9,7 +9,7 @@ /** * This ValueFormatter is just for convenience and simply puts a "%" sign after - * each value. (Recommeded for PieChart) + * each yValue. (Recommeded for PieChart) * * @author Philipp Jahoda */ 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..4c5dce6b50 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 @@ -10,7 +10,7 @@ * Created by Philipp Jahoda on 28/01/16. *

* A formatter specifically for stacked BarChart that allows to specify whether the all stack values - * or just the top value should be drawn. + * or just the top yValue should be drawn. */ public class StackedValueFormatter implements ValueFormatter { @@ -20,7 +20,7 @@ public class StackedValueFormatter implements ValueFormatter { private boolean mDrawWholeStack; /** - * a string that should be appended behind the value + * a string that should be appended behind the yValue */ private String mAppendix; @@ -30,7 +30,7 @@ public class StackedValueFormatter implements ValueFormatter { * 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 appendix a string that should be appended behind the yValue * @param decimals the number of decimal digits to use */ public StackedValueFormatter(boolean drawWholeStack, String appendix, int decimals) { @@ -68,7 +68,7 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View } } - // return the "proposed" value + // return the "proposed" yValue 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 index 1f056bf786..12e5ac6a72 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java @@ -14,12 +14,12 @@ public interface ValueFormatter { /** - * Called when a value (from labels inside the chart) is formatted + * Called when a yValue (from labels inside the chart) is formatted * before being drawn. For performance reasons, avoid excessive calculations * and memory allocations inside this method. * - * @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 value the yValue to be formatted + * @param entry the entry the yValue 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 the formatted label ready for being drawn diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java index 1dcb72b04b..d7fa386112 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java @@ -4,19 +4,19 @@ /** * Created by Philipp Jahoda on 14/09/15. - * An interface for providing custom x-axis Strings. + * An interface for providing custom xPx-axis Strings. * * @author Philipp Jahoda */ public interface XAxisValueFormatter { /** - * Returns the customized label that is drawn on the x-axis. + * Returns the customized label that is drawn on the xPx-axis. * For performance reasons, avoid excessive calculations * and memory allocations inside this method. * - * @param original the original x-axis label to be drawn - * @param index the x-index that is currently being drawn + * @param original the original xPx-axis label to be drawn + * @param index the xPx-index that is currently being drawn * @param viewPortHandler provides information about the current chart state (scale, translation, ...) * @return */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java index 4bc6c3e987..eacd3ab33a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java @@ -10,12 +10,12 @@ public interface YAxisValueFormatter { /** - * Called when a value from the YAxis is formatted + * Called when a yValue from the YAxis is formatted * before being drawn. For performance reasons, avoid excessive calculations * and memory allocations inside this method. * - * @param value the YAxis value to be formatted - * @param yAxis the YAxis object the value belongs to + * @param value the YAxis yValue to be formatted + * @param yAxis the YAxis object the yValue belongs to * @return */ String getFormattedValue(float value, YAxis yAxis); 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 9ca0e66a05..0a5b4e6f94 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 @@ -43,7 +43,7 @@ public Highlight getHighlight(float x, float y) { float[] pts = new float[2]; pts[1] = y; - // take any transformer to determine the x-axis value + // take any transformer to determine the xPx-axis yValue mChart.getTransformer(set.getAxisDependency()).pixelsToValue(pts); return getStackedHighlight(selectionDetail, @@ -54,7 +54,7 @@ public Highlight getHighlight(float x, float y) { return new Highlight( xVal, - selectionDetail.value, + selectionDetail.yValue, selectionDetail.dataIndex, selectionDetail.dataSetIndex, -1); @@ -99,14 +99,14 @@ protected SelectionDetail getSelectionDetail(float xVal, float x, float y, int d if (yValue == Double.NaN) return null; - return new SelectionDetail( + return new SelectionDetail(0f, yValue, dataSetIndex, dataSet); } /** - * This method creates the Highlight object that also indicates which value of a stacked BarEntry has been selected. + * This method creates the Highlight object that also indicates which yValue of a stacked BarEntry has been selected. * * @param selectionDetail the selection detail to work with looking for stacked values * @param set @@ -149,7 +149,7 @@ protected Highlight getStackedHighlight( } /** - * Returns the index of the closest value inside the values array / ranges (stacked barchart) to the value given as + * Returns the index of the closest yValue inside the values array / ranges (stacked barchart) to the yValue given as * a parameter. * * @param ranges @@ -182,7 +182,7 @@ protected int getClosestStackIndex(Range[] ranges, float value) { // int index = 0; // float remainder = e.getNegativeSum(); // - // while (index < vals.length - 1 && value > vals[index] + remainder) { + // while (index < vals.length - 1 && yValue > vals[index] + remainder) { // remainder += vals[index]; // index++; // } @@ -191,7 +191,7 @@ protected int getClosestStackIndex(Range[] ranges, float value) { } /** - * Returns the base x-value to the corresponding x-touch value in pixels. + * Returns the base xPx-yValue to the corresponding xPx-touch yValue in pixels. * * @param x * @return @@ -202,7 +202,7 @@ protected float getBase(float x) { float[] pts = new float[2]; pts[0] = x; - // take any transformer to determine the x-axis value + // take any transformer to determine the xPx-axis yValue mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); float xVal = pts[0]; 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 a2e0f19deb..4fc57febdb 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 @@ -4,9 +4,11 @@ import java.util.List; 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.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; +import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.SelectionDetail; import com.github.mikephil.charting.utils.Utils; @@ -25,7 +27,7 @@ public ChartHighlighter(T chart) { } /** - * Returns a Highlight object corresponding to the given x- and y- touch positions in pixels. + * Returns a Highlight object corresponding to the given xPx- and yPx- touch positions in pixels. * * @param x * @param y @@ -39,32 +41,27 @@ public Highlight getHighlight(float x, float y) { if (selectionDetail == null) return null; - return new Highlight(xVal, - selectionDetail.value, + return new Highlight(selectionDetail.xValue, + selectionDetail.yValue, selectionDetail.dataIndex, selectionDetail.dataSetIndex); } /** - * Returns the corresponding x-index for a given touch-position in pixels. + * Returns the corresponding xPos for a given touch-position in pixels. * * @param x * @return */ protected float getXForTouch(float x) { - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - - // take any transformer to determine the x-axis value - mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); - - return Math.round(pts[0]); + // take any transformer to determine the xPx-axis yValue + PointD pos = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(x, 0f); + return (float) pos.x; } /** - * Returns the corresponding SelectionDetail for a given xVal and y-touch position in pixels. + * Returns the corresponding SelectionDetail for a given xVal and yPx-touch position in pixels. * * @param xVal * @param y @@ -98,8 +95,6 @@ protected List getSelectionDetailsAtIndex(float xVal, int dataS if (mChart.getData() == null) return vals; - float[] pts = new float[2]; - for (int i = 0, dataSetCount = mChart.getData().getDataSetCount(); i < dataSetCount; i++) { if (dataSetIndex > -1 && dataSetIndex != i) @@ -111,19 +106,19 @@ protected List getSelectionDetailsAtIndex(float xVal, int dataS if (!dataSet.isHighlightEnabled()) continue; - // extract all y-values from all DataSets at the given x-index - final Entry e = dataSet.getEntryForXPos(xVal); + vals.add(getDetails(dataSet, i, xVal, DataSet.Rounding.UP)); + vals.add(getDetails(dataSet, i, xVal, DataSet.Rounding.DOWN)); + } - pts[0] = e.getX(); - pts[1] = e.getY(); + return vals; + } - mChart.getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); + private SelectionDetail getDetails(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { - if (!Float.isNaN(pts[1])) { - vals.add(new SelectionDetail(pts[0], pts[1], e.getY(), i, dataSet)); - } - } + final Entry e = set.getEntryForXPos(xVal, rounding); - return vals; + PointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), e.getY()); + + return new SelectionDetail((float) pixels.x, (float) pixels.y, e.getX(), e.getY(), dataSetIndex, set); } } 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 f752be4cfa..98e24859d4 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 @@ -45,7 +45,7 @@ protected List getSelectionDetailsAtIndex(float xVal, int dataS if (!dataSet.isHighlightEnabled()) continue; - // extract all y-values from all DataSets at the given x-index + // extract all yPx-values from all DataSets at the given xPx-index final float yVals[] = dataSet.getYValuesForXPos(xVal); for (float yVal : yVals) { pts[1] = yVal; @@ -53,7 +53,7 @@ protected List getSelectionDetailsAtIndex(float xVal, int dataS mChart.getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); if (!Float.isNaN(pts[1])) { - vals.add(new SelectionDetail(pts[1], yVal, i, j, dataSet)); + vals.add(new SelectionDetail(0f, pts[1], yVal, i, j, dataSet)); } } } 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 4bdd840d7c..fe52b33619 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 @@ -2,25 +2,25 @@ package com.github.mikephil.charting.highlight; /** - * Contains information needed to determine the highlighted value. + * Contains information needed to determine the highlighted yValue. * * @author Philipp Jahoda */ public class Highlight { - /** the x-value of the highlighted value */ + /** the xPx-yValue of the highlighted yValue */ private float mX = Float.NaN; - /** the y-value of the highlighted value */ + /** the yPx-yValue of the highlighted yValue */ private float mY = Float.NaN; /** the index of the data object - in case it refers to more than one */ private int mDataIndex; - /** the index of the dataset the highlighted value is in */ + /** the index of the dataset the highlighted yValue is in */ private int mDataSetIndex; - /** index which value of a stacked bar entry is highlighted, default -1 */ + /** index which yValue of a stacked bar entry is highlighted, default -1 */ private int mStackIndex = -1; /** the range of the bar that is selected (only for stacked-barchart) */ @@ -29,10 +29,10 @@ public class Highlight { /** * constructor * - * @param x the x-value of the highlighted value - * @param y the y-value of the highlighted value - * @param dataIndex the index of the Data the highlighted value belongs to - * @param dataSetIndex the index of the DataSet the highlighted value belongs to + * @param x the xPx-yValue of the highlighted yValue + * @param y the yPx-yValue of the highlighted yValue + * @param dataIndex the index of the Data the highlighted yValue belongs to + * @param dataSetIndex the index of the DataSet the highlighted yValue belongs to */ public Highlight(float x, float y, int dataIndex, int dataSetIndex) { this.mX = x; @@ -43,11 +43,11 @@ public Highlight(float x, float y, int dataIndex, int dataSetIndex) { /** * Constructor, only used for stacked-barchart. * - * @param x the x-value of the highlighted value on the x-axis - * @param y the y-value of the highlighted value - * @param dataIndex the index of the Data the highlighted value belongs to - * @param dataSetIndex the index of the DataSet the highlighted value belongs to - * @param stackIndex references which value of a stacked-bar entry has been + * @param x the xPx-yValue of the highlighted yValue on the xPx-axis + * @param y the yPx-yValue of the highlighted yValue + * @param dataIndex the index of the Data the highlighted yValue belongs to + * @param dataSetIndex the index of the DataSet the highlighted yValue belongs to + * @param stackIndex references which yValue of a stacked-bar entry has been * selected */ public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackIndex) { @@ -58,13 +58,13 @@ public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackInd /** * Constructor, only used for stacked-barchart. * - * @param x the index of the highlighted value on the x-axis - * @param y the y-value of the highlighted value - * @param dataIndex the index of the Data the highlighted value belongs to - * @param dataSetIndex the index of the DataSet the highlighted value belongs to - * @param stackIndex references which value of a stacked-bar entry has been + * @param x the index of the highlighted yValue on the xPx-axis + * @param y the yPx-yValue of the highlighted yValue + * @param dataIndex the index of the Data the highlighted yValue belongs to + * @param dataSetIndex the index of the DataSet the highlighted yValue belongs to + * @param stackIndex references which yValue of a stacked-bar entry has been * selected - * @param range the range the selected stack-value is in + * @param range the range the selected stack-yValue is in */ public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackIndex, Range range) { this(x, y, dataIndex, dataSetIndex, stackIndex); @@ -74,15 +74,15 @@ public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackInd /** * Constructor, only used for stacked-barchart. * - * @param x the index of the highlighted value on the x-axis - * @param dataSetIndex the index of the DataSet the highlighted value belongs to + * @param x the index of the highlighted yValue on the xPx-axis + * @param dataSetIndex the index of the DataSet the highlighted yValue belongs to */ public Highlight(int x, int dataSetIndex) { this(x, Float.NaN, 0, dataSetIndex, -1); } /** - * returns the x-value of the highlighted value + * returns the xPx-yValue of the highlighted yValue * * @return */ @@ -91,7 +91,7 @@ public float getX() { } /** - * returns the y-value of the highlighted value + * returns the yPx-yValue of the highlighted yValue * * @return */ @@ -109,7 +109,7 @@ public int getDataIndex() { } /** - * returns the index of the DataSet the highlighted value is in + * returns the index of the DataSet the highlighted yValue is in * * @return */ @@ -119,7 +119,7 @@ public int getDataSetIndex() { /** * Only needed if a stacked-barchart entry was highlighted. References the - * selected value within the stacked-entry. + * selected yValue within the stacked-entry. * * @return */ @@ -128,7 +128,7 @@ public int getStackIndex() { } /** - * Returns the range of values the selected value of a stacked bar is in. (this is only relevant for stacked-barchart) + * Returns the range of values the selected yValue of a stacked bar is in. (this is only relevant for stacked-barchart) * @return */ public Range getRange() { @@ -157,7 +157,7 @@ public boolean equalTo(Highlight h) { @Override public String toString() { - return "Highlight, x: " + mX + "y: " + mY + ", dataSetIndex: " + mDataSetIndex + return "Highlight, xPx: " + mX + "yPx: " + mY + ", dataSetIndex: " + mDataSetIndex + ", stackIndex (only stacked barentry): " + mStackIndex; } } 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 22a8d7356d..56865a8776 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 @@ -41,7 +41,7 @@ public Highlight getHighlight(float x, float y) { float[] pts = new float[2]; pts[0] = y; - // take any transformer to determine the x-axis value + // take any transformer to determine the xPx-axis yValue mChart.getTransformer(set.getAxisDependency()).pixelsToValue(pts); return getStackedHighlight(selectionDetail, @@ -52,7 +52,7 @@ public Highlight getHighlight(float x, float y) { return new Highlight( xVal, - selectionDetail.value, + selectionDetail.yValue, selectionDetail.dataIndex, selectionDetail.dataSetIndex, -1); @@ -67,7 +67,7 @@ protected float getXForTouch(float x) { float[] pts = new float[2]; pts[1] = x; - // take any transformer to determine the x-axis value + // take any transformer to determine the xPx-axis yValue mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); return (int) Math.round(pts[1]); @@ -90,7 +90,7 @@ else if (xIndex >= valCount) } /** - * Returns the base y-value to the corresponding x-touch value in pixels. + * Returns the base yPx-yValue to the corresponding xPx-touch yValue in pixels. * * @param y * @return @@ -102,7 +102,7 @@ protected float getBase(float y) { float[] pts = new float[2]; pts[1] = y; - // take any transformer to determine the x-axis value + // take any transformer to determine the xPx-axis yValue mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); float yVal = pts[1]; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java index 96dd592b8f..60391124ec 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java @@ -1,7 +1,7 @@ package com.github.mikephil.charting.highlight; /** - * Created by Philipp Jahoda on 24/07/15. Class that represents the range of one value in a stacked bar entry. e.g. + * Created by Philipp Jahoda on 24/07/15. Class that represents the range of one yValue in a stacked bar entry. e.g. * stack values are -10, 5, 20 -> then ranges are (-10 - 0, 0 - 5, 5 - 25). */ public final class Range { @@ -15,7 +15,7 @@ public Range(float from, float to) { } /** - * Returns true if this range contains (if the value is in between) the given value, false if not. + * Returns true if this range contains (if the yValue is in between) the given yValue, false if not. * * @param value * @return 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 a7f9544ed8..a063213380 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 @@ -15,14 +15,14 @@ public interface ChartInterface { /** - * Returns the minimum x-value of the chart, regardless of zoom or translation. + * Returns the minimum xPx-yValue of the chart, regardless of zoom or translation. * * @return */ float getXChartMin(); /** - * Returns the maximum x-value of the chart, regardless of zoom or translation. + * Returns the maximum xPx-yValue of the chart, regardless of zoom or translation. * * @return */ @@ -31,14 +31,14 @@ public interface ChartInterface { float getXRange(); /** - * Returns the minimum y-value of the chart, regardless of zoom or translation. + * Returns the minimum yPx-yValue of the chart, regardless of zoom or translation. * * @return */ float getYChartMin(); /** - * Returns the maximum y-value of the chart, regardless of zoom or translation. + * Returns the maximum yPx-yValue of the chart, regardless of zoom or translation. * * @return */ 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 e7c2f64355..3c260245bc 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 @@ -8,7 +8,7 @@ public interface IBarDataSet extends IBarLineScatterCandleBubbleDataSet { /** - * Returns the space between bars as the actual value (0 - 1.0f) + * Returns the space between bars as the actual yValue (0 - 1.0f) * * @return */ @@ -31,7 +31,7 @@ public interface IBarDataSet extends IBarLineScatterCandleBubbleDataSet { /** ###### ###### DATA RELATED METHODS ###### ###### */ /** - * returns the minimum y-value this DataSet holds + * returns the minimum yPx-yValue this DataSet holds * * @return */ float getYMin(); /** - * returns the maximum y-value this DataSet holds + * returns the maximum yPx-yValue this DataSet holds * * @return */ float getYMax(); /** - * returns the minimum x-value this DataSet holds + * returns the minimum xPx-yValue this DataSet holds * * @return */ float getXMin(); /** - * returns the maximum x-value this DataSet holds + * returns the maximum xPx-yValue this DataSet holds * * @return */ float getXMax(); /** - * Returns the number of y-values this DataSet represents -> the size of the y-values array + * Returns the number of yPx-values this DataSet represents -> the size of the yPx-values array * -> yvals.size() * * @return @@ -53,7 +53,7 @@ public interface IDataSet { int getEntryCount(); /** - * Calculates the minimum and maximum x and y values (mXMin, mXMax, mYMin, mYMax). + * Calculates the minimum and maximum xPx and yPx values (mXMin, mXMax, mYMin, mYMax). */ void calcMinMax(); @@ -65,7 +65,7 @@ public interface IDataSet { * not over-use in performance critical situations. * * @param xPos - * @param rounding determine to round up/down/closest if there is no Entry matching the provided x-index + * @param rounding determine to round up/down/closest if there is no Entry matching the provided xPx-index * @return */ T getEntryForXPos(float xPos, DataSet.Rounding rounding); @@ -109,7 +109,7 @@ public interface IDataSet { * not over-use in performance critical situations. * * @param xPos - * @param rounding determine to round up/down/closest if there is no Entry matching the provided x-index + * @param rounding determine to round up/down/closest if there is no Entry matching the provided xPx-index * @return */ int getEntryIndex(float xPos, DataSet.Rounding rounding); @@ -124,8 +124,8 @@ public interface IDataSet { int getEntryIndex(T e); /** - * Returns the value of the Entry object at the given xVal. Returns - * Float.NaN if no value is at the given xVal. INFORMATION: This method + * Returns the yValue of the Entry object at the given xVal. Returns + * Float.NaN if no yValue is at the given xVal. INFORMATION: This method * does calculations at runtime. Do not over-use in performance critical * situations. * @@ -135,8 +135,8 @@ public interface IDataSet { float getYValueForXValue(float xVal); /** - * Returns all of the y values of the Entry objects at the given xPos. Returns - * Float.NaN if no value is at the given xPos. INFORMATION: This method + * Returns all of the yPx values of the Entry objects at the given xPos. Returns + * Float.NaN if no yValue is at the given xPos. INFORMATION: This method * does calculations at runtime. Do not over-use in performance critical * situations. * @@ -160,7 +160,7 @@ public interface IDataSet { * Adds an Entry to the DataSet dynamically. * Entries are added to the end of the list. * This will also recalculate the current minimum and maximum - * values of the DataSet and the value-sum. + * values of the DataSet and the yValue-sum. * * @param e */ @@ -169,7 +169,7 @@ public interface IDataSet { /** * Removes an Entry from the DataSets entries array. This will also * recalculate the current minimum and maximum values of the DataSet and the - * value-sum. Returns true if an Entry was removed, false if no Entry could + * yValue-sum. Returns true if an Entry was removed, false if no Entry could * be removed. * * @param e @@ -178,9 +178,9 @@ public interface IDataSet { /** * Adds an Entry to the DataSet dynamically. - * Entries are added to their appropriate index respective to it's x-index. + * Entries are added to their appropriate index respective to it's xPx-index. * This will also recalculate the current minimum and maximum - * values of the DataSet and the value-sum. + * values of the DataSet and the yValue-sum. * * @param e */ @@ -250,7 +250,7 @@ public interface IDataSet { YAxis.AxisDependency getAxisDependency(); /** - * Set the y-axis this DataSet should be plotted against (either LEFT or + * Set the yPx-axis this DataSet should be plotted against (either LEFT or * RIGHT). Default: LEFT * * @param dependency @@ -289,7 +289,7 @@ public interface IDataSet { boolean isHighlightEnabled(); /** - * If set to true, value highlighting is enabled which means that values can + * If set to true, yValue highlighting is enabled which means that values can * be highlighted programmatically or by touch gesture. * * @param enabled @@ -315,7 +315,7 @@ public interface IDataSet { ValueFormatter getValueFormatter(); /** - * Sets the color the value-labels of this DataSet should have. + * Sets the color the yValue-labels of this DataSet should have. * * @param color */ @@ -329,14 +329,14 @@ public interface IDataSet { void setValueTextColors(List colors); /** - * Sets a Typeface for the value-labels of this DataSet. + * Sets a Typeface for the yValue-labels of this DataSet. * * @param tf */ void setValueTypeface(Typeface tf); /** - * Sets the text-size of the value-labels of this DataSet in dp. + * Sets the text-size of the yValue-labels of this DataSet in dp. * * @param size */ @@ -373,7 +373,7 @@ public interface IDataSet { float getValueTextSize(); /** - * set this to true to draw y-values on the chart NOTE (for bar and + * set this to true to draw yPx-values on the chart NOTE (for bar and * linechart): if "maxvisiblecount" is reached, no values will be drawn even * if this is enabled * @@ -382,7 +382,7 @@ public interface IDataSet { void setDrawValues(boolean enabled); /** - * Returns true if y-value drawing is enabled, false if not + * Returns true if yPx-yValue drawing is enabled, false if not * * @return */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java index ce89822716..ebf7e4f565 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java @@ -24,7 +24,7 @@ public interface ILineRadarDataSet extends ILineScatterCandleRa Drawable getFillDrawable(); /** - * Returns the alpha value that is used for filling the line surface, + * Returns the alpha yValue that is used for filling the line surface, * default: 85 * * @return 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 71168adebf..98e60ffb32 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 @@ -126,10 +126,10 @@ public boolean onTouch(View v, MotionEvent event) { saveTouchStart(event); - // get the distance between the pointers on the x-axis + // get the distance between the pointers on the xPx-axis mSavedXDist = getXDist(event); - // get the distance between the pointers on the y-axis + // get the distance between the pointers on the yPx-axis mSavedYDist = getYDist(event); // get the total distance between the pointers @@ -363,7 +363,7 @@ private void performZoom(MotionEvent event) { mLastGesture = ChartGesture.X_ZOOM; float xDist = getXDist(event); - float scaleX = xDist / mSavedXDist; // x-axis scale + float scaleX = xDist / mSavedXDist; // xPx-axis scale boolean isZoomingOut = (scaleX < 1); boolean canZoomMoreX = isZoomingOut ? @@ -384,7 +384,7 @@ private void performZoom(MotionEvent event) { mLastGesture = ChartGesture.Y_ZOOM; float yDist = getYDist(event); - float scaleY = yDist / mSavedYDist; // y-axis scale + float scaleY = yDist / mSavedYDist; // yPx-axis scale boolean isZoomingOut = (scaleY < 1); boolean canZoomMoreY = isZoomingOut ? @@ -450,7 +450,7 @@ private static float spacing(MotionEvent event) { } /** - * calculates the distance on the x-axis between two pointers (fingers on + * calculates the distance on the xPx-axis between two pointers (fingers on * the display) * * @param e @@ -462,7 +462,7 @@ private static float getXDist(MotionEvent e) { } /** - * calculates the distance on the y-axis between two pointers (fingers on + * calculates the distance on the yPx-axis between two pointers (fingers on * the display) * * @param e @@ -474,7 +474,7 @@ private static float getYDist(MotionEvent e) { } /** - * returns the correct translation depending on the provided x and y touch + * returns the correct translation depending on the provided xPx and yPx touch * points * * @param x @@ -532,7 +532,7 @@ public boolean onDoubleTap(MotionEvent e) { mChart.zoom(mChart.isScaleXEnabled() ? 1.4f : 1f, mChart.isScaleYEnabled() ? 1.4f : 1f, trans.x, trans.y); if (mChart.isLogEnabled()) - Log.i("BarlineChartTouch", "Double-Tap, Zooming In, x: " + trans.x + ", y: " + Log.i("BarlineChartTouch", "Double-Tap, Zooming In, xPx: " + trans.x + ", yPx: " + trans.y); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java index 75c8e864b4..aae6050916 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java @@ -83,7 +83,7 @@ public void endAction(MotionEvent me) { } /** - * Sets the last value that was highlighted via touch. + * Sets the last yValue that was highlighted via touch. * * @param high */ 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..0449e79228 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 @@ -60,8 +60,8 @@ public interface OnChartGestureListener { * Callbacks when the chart is scaled / zoomed via pinch zoom gesture. * * @param me - * @param scaleX scalefactor on the x-axis - * @param scaleY scalefactor on the y-axis + * @param scaleX scalefactor on the xPx-axis + * @param scaleY scalefactor on the yPx-axis */ void onChartScale(MotionEvent me, float scaleX, float scaleY); @@ -69,8 +69,8 @@ public interface OnChartGestureListener { * Callbacks when the chart is moved / translated via drag gesture. * * @param me - * @param dX translation distance on the x-axis - * @param dY translation distance on the y-axis + * @param dX translation distance on the xPx-axis + * @param dY translation distance on the yPx-axis */ void onChartTranslate(MotionEvent me, float dX, float dY); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java index 4c6da2e8c9..e0153dada6 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java @@ -12,7 +12,7 @@ public interface OnChartValueSelectedListener { /** - * Called when a value has been selected inside the chart. + * Called when a yValue has been selected inside the chart. * * @param e The selected Entry. * @param dataSetIndex The index in the datasets array of the data object 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 d40e2fdb4e..59ec3d1101 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 @@ -283,7 +283,7 @@ private float calculateVelocity() { /** * sets the starting angle of the rotation, this is only used by the touch - * listener, x and y is the touch position + * listener, xPx and yPx is the touch position * * @param x * @param y 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 48da4be2c3..6515f7b82b 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 @@ -22,7 +22,7 @@ public abstract class AxisRenderer extends Renderer { /** paint object for the grid lines */ protected Paint mGridPaint; - /** paint for the x-label values */ + /** paint for the xPx-label values */ protected Paint mAxisLabelPaint; /** paint for the line surrounding the chart */ @@ -94,12 +94,12 @@ public Transformer getTransformer() { /** * Computes the axis values. * - * @param min - the minimum value in the data object for this axis - * @param max - the maximum value in the data object for this axis + * @param min - the minimum yValue in the data object for this axis + * @param max - the maximum yValue in the data object for this axis */ public void computeAxis(float min, float max, boolean inverted) { - // calculate the starting and entry point of the y-labels (depending on + // calculate the starting and entry point of the yPx-labels (depending on // zoom / contentrect bounds) if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { 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 cfdba02418..740863325c 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 @@ -133,7 +133,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) break; - // Set the color for the currently drawn value. If the index + // Set the color for the currently drawn yValue. 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], @@ -220,7 +220,7 @@ public void drawValues(Canvas c) { boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); // calculate the correct offset depending on the draw position of - // the value + // the yValue float valueTextHeight = Utils.calcTextHeight(mValuePaint, "8"); posOffset = (drawValueAboveBar ? -valueOffsetPlus : valueTextHeight + valueOffsetPlus); negOffset = (drawValueAboveBar ? valueTextHeight + valueOffsetPlus : -valueOffsetPlus); @@ -418,9 +418,9 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { // final float yArrow = (y1 > -y2 ? y1 : y1) * mAnimator.getPhaseY(); // // Path arrow = new Path(); -// arrow.moveTo(x + 0.4f, yArrow + offsetY); -// arrow.lineTo(x + 0.4f + arrowWidth, yArrow + offsetY - arrowHeight); -// arrow.lineTo(x + 0.4f + arrowWidth, yArrow + offsetY + arrowHeight); +// arrow.moveTo(xPx + 0.4f, yArrow + offsetY); +// arrow.lineTo(xPx + 0.4f + arrowWidth, yArrow + offsetY - arrowHeight); +// arrow.lineTo(xPx + 0.4f + arrowWidth, yArrow + offsetY + arrowHeight); // // trans.pathValueToPixel(arrow); // c.drawPath(arrow, mHighlightPaint); 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 a1d9521a55..3fcaca0913 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 @@ -91,7 +91,7 @@ protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { boolean normalizeSize = dataSet.isNormalizeSizeEnabled(); - // calcualte the full width of 1 step on the x-axis + // calcualte the full width of 1 step on the xPx-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); @@ -245,7 +245,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { boolean normalizeSize = dataSet.isNormalizeSizeEnabled(); - // calcualte the full width of 1 step on the x-axis + // calcualte the full width of 1 step on the xPx-axis final float maxBubbleWidth = Math.abs(sizeBuffer[2] - sizeBuffer[0]); final float maxBubbleHeight = Math.abs( mViewPortHandler.contentBottom() - mViewPortHandler.contentTop()); 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 b6d567797b..04b97f31cc 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 @@ -11,6 +11,7 @@ import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -327,7 +328,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { dataSetIndex++) { float x = high.getX(); // get the - // x-position + // xPx-position ICandleDataSet set = mChart.getCandleData().getDataSetByIndex(dataSetIndex); @@ -343,14 +344,10 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { float highValue = e.getHigh() * mAnimator.getPhaseY(); float y = (lowValue + highValue) / 2f; - float[] pts = new float[]{ - x, y - }; - - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); + PointD px = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(x, y); // draw the lines - drawHighlightLines(c, pts, set); + drawHighlightLines(c, (float) px.x, (float) px.y, set); } } } 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 f4a88fe08d..20a28ea9ae 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 @@ -67,7 +67,7 @@ public DataRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { /** * Returns the Paint object this renderer uses for drawing the values - * (value-text). + * (yValue-text). * * @return */ @@ -95,7 +95,7 @@ public Paint getPaintRender() { } /** - * Applies the required styling (provided by the DataSet) to the value-paint + * Applies the required styling (provided by the DataSet) to the yValue-paint * object. * * @param set @@ -128,12 +128,12 @@ 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 yValue of the given entry by using the provided ValueFormatter. * * @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 formatter formatter for custom yValue-formatting + * @param value the yValue to be drawn + * @param entry the entry the yValue belongs to * @param dataSetIndex the index of the DataSet the drawn Entry belongs to * @param x position * @param y position 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 b27c5ebdab..9b02178179 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 @@ -85,7 +85,7 @@ 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 + // Set the color for the currently drawn yValue. 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], @@ -147,7 +147,7 @@ public void drawValues(Canvas c) { float val = e.getY(); String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); - // calculate the correct offset depending on the draw position of the value + // calculate the correct offset depending on the draw position of the yValue float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); @@ -161,7 +161,7 @@ public void drawValues(Canvas c) { valuePoints[j + 1] + halfTextHeight, dataSet.getValueTextColor(j / 2)); } - // if each value of a potential stack should be drawn + // if each yValue of a potential stack should be drawn } else { for (int j = 0; j < (valuePoints.length - 1) * mAnimator.getPhaseX(); j += 2) { @@ -187,7 +187,7 @@ public void drawValues(Canvas c) { float val = e.getY(); String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); - // calculate the correct offset depending on the draw position of the value + // calculate the correct offset depending on the draw position of the yValue float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); @@ -231,7 +231,7 @@ public void drawValues(Canvas c) { 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 + // calculate the correct offset depending on the draw position of the yValue float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); 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 1b8ab83c10..1cf0250791 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 @@ -28,7 +28,7 @@ public class LineChartRenderer extends LineRadarRenderer { protected LineDataProvider mChart; /** - * paint for the inner circle of the value indicators + * paint for the inner circle of the yValue indicators */ protected Paint mCirclePaintInner; @@ -738,28 +738,16 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - float x = high.getX(); // get the - // x-position + float x = high.getX(); + float y = high.getY() * mAnimator.getPhaseY(); if (x > mChart.getXChartMax() * mAnimator.getPhaseX()) continue; - final float yVal = set.getYValueForXValue(x); - if (Float.isNaN(yVal)) - continue; - - float y = yVal * mAnimator.getPhaseY(); // get - // the - // y-position - - float[] pts = new float[]{ - x, y - }; - - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); + mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(x, y); // draw the lines - drawHighlightLines(c, pts, set); + drawHighlightLines(c, x, y, set); } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java index c262953aa3..1f19c44492 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java @@ -25,10 +25,11 @@ public LineScatterCandleRadarRenderer(ChartAnimator animator, ViewPortHandler vi * Draws vertical & horizontal highlight-lines if enabled. * * @param c - * @param pts the transformed x- and y-position of the lines + * @param x xPx-position of the highlight line intersection + * @param y yPx-position of the highlight line intersection * @param set the currently drawn dataset */ - protected void drawHighlightLines(Canvas c, float[] pts, ILineScatterCandleRadarDataSet set) { + protected void drawHighlightLines(Canvas c, float x, float y, ILineScatterCandleRadarDataSet set) { // set color and stroke-width mHighlightPaint.setColor(set.getHighLightColor()); @@ -42,8 +43,8 @@ protected void drawHighlightLines(Canvas c, float[] pts, ILineScatterCandleRadar // create vertical path mHighlightLinePath.reset(); - mHighlightLinePath.moveTo(pts[0], mViewPortHandler.contentTop()); - mHighlightLinePath.lineTo(pts[0], mViewPortHandler.contentBottom()); + mHighlightLinePath.moveTo(x, mViewPortHandler.contentTop()); + mHighlightLinePath.lineTo(x, mViewPortHandler.contentBottom()); c.drawPath(mHighlightLinePath, mHighlightPaint); } @@ -53,8 +54,8 @@ protected void drawHighlightLines(Canvas c, float[] pts, ILineScatterCandleRadar // create horizontal path mHighlightLinePath.reset(); - mHighlightLinePath.moveTo(mViewPortHandler.contentLeft(), pts[1]); - mHighlightLinePath.lineTo(mViewPortHandler.contentRight(), pts[1]); + mHighlightLinePath.moveTo(mViewPortHandler.contentLeft(), y); + mHighlightLinePath.lineTo(mViewPortHandler.contentRight(), y); c.drawPath(mHighlightLinePath, mHighlightPaint); } 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 962ac88eaa..78f1a996b2 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 @@ -198,7 +198,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 + // draw only if the yValue is greater than zero if ((Math.abs(dataSet.getEntryForIndex(j).getY()) > 0.000001)) { visibleAngleCount++; } @@ -213,7 +213,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { Entry e = dataSet.getEntryForIndex(j); - // draw only if the value is greater than zero + // draw only if the yValue is greater than zero if ((Math.abs(e.getY()) > 0.000001)) { if (!mChart.needsHighlight((int) e.getX(), @@ -711,7 +711,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { final int entryCount = set.getEntryCount(); int visibleAngleCount = 0; for (int j = 0; j < entryCount; j++) { - // draw only if the value is greater than zero + // draw only if the yValue is greater than zero if ((Math.abs(set.getEntryForIndex(j).getY()) > 0.000001)) { visibleAngleCount++; } @@ -896,7 +896,7 @@ protected void drawRoundedSlices(Canvas c) { Entry e = dataSet.getEntryForIndex(j); - // draw only if the value is greater than zero + // draw only if the yValue is greater than zero if ((Math.abs(e.getY()) > 0.000001)) { float x = (float) ((r - circleRadius) 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 30f3ef33ac..7e23bbd403 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 @@ -89,7 +89,7 @@ protected void drawDataSet(Canvas c, IRadarDataSet dataSet, int mostEntries) { float sliceangle = mChart.getSliceAngle(); - // calculate the factor that is needed for transforming the value to + // calculate the factor that is needed for transforming the yValue to // pixels float factor = mChart.getFactor(); @@ -163,7 +163,7 @@ public void drawValues(Canvas c) { float sliceangle = mChart.getSliceAngle(); - // calculate the factor that is needed for transforming the value to + // calculate the factor that is needed for transforming the yValue to // pixels float factor = mChart.getFactor(); @@ -204,7 +204,7 @@ protected void drawWeb(Canvas c) { float sliceangle = mChart.getSliceAngle(); - // calculate the factor that is needed for transforming the value to + // calculate the factor that is needed for transforming the yValue to // pixels float factor = mChart.getFactor(); float rotationangle = mChart.getRotationAngle(); @@ -287,16 +287,13 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { y * factor * phaseY, sliceangle * j * phaseX + mChart.getRotationAngle()); - float[] pts = new float[]{ - p.x, p.y - }; // draw the lines - drawHighlightLines(c, pts, set); + drawHighlightLines(c, p.x, p.y, set); if (set.isDrawHighlightCircleEnabled()) { - if (!Float.isNaN(pts[0]) && !Float.isNaN(pts[1])) { + if (!Float.isNaN(p.x) && !Float.isNaN(p.y)) { int strokeColor = set.getHighlightCircleStrokeColor(); if (strokeColor == ColorTemplate.COLOR_NONE) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java index b646a527c2..f8d111d5b2 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java @@ -16,10 +16,10 @@ public abstract class Renderer { */ protected ViewPortHandler mViewPortHandler; - /** the minimum value on the x-axis that should be plotted */ + /** the minimum yValue on the xPx-axis that should be plotted */ protected float mMinX = 0; - /** the maximum value on the x-axis that should be plotted */ + /** the maximum yValue on the xPx-axis that should be plotted */ protected float mMaxX = 0; public Renderer(ViewPortHandler viewPortHandler) { @@ -27,7 +27,7 @@ public Renderer(ViewPortHandler viewPortHandler) { } /** - * Returns true if the specified value fits in between the provided min + * Returns true if the specified yValue fits in between the provided min * and max bounds, false if not. * * @param val @@ -44,7 +44,7 @@ protected boolean fitsBounds(float val, float min, float max) { } /** - * Calculates the minimum and maximum x-value the chart can currently + * Calculates the minimum and maximum xPx-yValue the chart can currently * display (with the given zoom level). -> mMinX, mMaxX * * @param dataProvider 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 caf9650f3e..99cf49d1f5 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 @@ -4,6 +4,7 @@ import android.graphics.Canvas; import android.graphics.Paint.Style; import android.graphics.Path; +import android.graphics.Point; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.buffer.ScatterBuffer; @@ -14,6 +15,7 @@ import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -397,27 +399,16 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - float x = high.getX(); // get the - // x-position - + float x = high.getX(); + float y = high.getY() * mAnimator.getPhaseY(); if (x > mChart.getXChartMax() * mAnimator.getPhaseX()) continue; - final float yVal = set.getYValueForXValue(x); - if (Float.isNaN(yVal)) - continue; - - float y = yVal * mAnimator.getPhaseY(); - - float[] pts = new float[]{ - x, y - }; - - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); + PointD px = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(x, y); // draw the lines - drawHighlightLines(c, pts, set); + drawHighlightLines(c, (float) px.x, (float) px.y, set); } } } 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 3f28571f10..6a6fb2a045 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 @@ -36,7 +36,7 @@ public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer t @Override public void computeAxis(float min, float max, boolean inverted) { - // calculate the starting and entry point of the y-labels (depending on + // calculate the starting and entry point of the yPx-labels (depending on // zoom / contentrect bounds) if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { @@ -186,7 +186,7 @@ public void renderAxisLine(Canvas c) { } /** - * draws the x-labels on the specified y-position + * draws the xPx-labels on the specified yPx-position * * @param pos */ @@ -197,7 +197,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { float[] positions = new float[mXAxis.mEntries.length * 2]; for (int i = 0; i < positions.length; i += 2) { - // only fill x values + // only fill xPx values positions[i] = mXAxis.mEntries[i / 2]; } @@ -248,7 +248,7 @@ public void renderGridLines(Canvas c) { float[] positions = new float[mXAxis.mEntries.length * 2]; for (int i = 0; i < positions.length; i += 2) { - // only fill x values + // only fill xPx values positions[i] = mXAxis.mEntries[i / 2]; } @@ -334,7 +334,7 @@ public void renderLimitLineLine(Canvas c, LimitLine limitLine, float[] position) public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position, float yOffset) { String label = limitLine.getLabel(); - // if drawing the limit-value label is enabled + // if drawing the limit-yValue label is enabled if (label != null && !label.equals("")) { mLimitLinePaint.setStyle(limitLine.getTextStyle()); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java index 8398301463..0c16dc2a65 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java @@ -23,7 +23,7 @@ public XAxisRendererBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, Trans } /** - * draws the x-labels on the specified y-position + * draws the xPx-labels on the specified yPx-position * * @param pos */ 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 44107750ea..7990205fac 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 @@ -93,7 +93,7 @@ public void renderAxisLabels(Canvas c) { } /** - * draws the x-labels on the specified y-position + * draws the xPx-labels on the specified yPx-position * * @param pos */ @@ -229,7 +229,7 @@ public void renderLimitLines(Canvas c) { String label = l.getLabel(); - // if drawing the limit-value label is enabled + // if drawing the limit-yValue label is enabled if (label != null && !label.equals("")) { mLimitLinePaint.setStyle(l.getTextStyle()); 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 8d926ddfcb..71d9380e50 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 @@ -34,7 +34,7 @@ public void renderAxisLabels(Canvas c) { float sliceangle = mChart.getSliceAngle(); - // calculate the factor that is needed for transforming the value to + // calculate the factor that is needed for transforming the yValue to // pixels float factor = mChart.getFactor(); 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 94b7673205..17cf3dc397 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 @@ -52,7 +52,7 @@ protected void computeAxisValues(float min, float max) { return; } - // Find out how much spacing (in y value space) between axis values + // Find out how much spacing (in yPx yValue space) between axis values double rawInterval = range / labelCount; double interval = Utils.roundToNextSignificant(rawInterval); @@ -122,7 +122,7 @@ protected void computeAxisValues(float min, float max) { for (f = first, i = 0; i < n; f += interval, ++i) { - if (f == 0.0) // Fix for negative zero case (Where value == -0.0, and 0.0 == -0.0) + if (f == 0.0) // Fix for negative zero case (Where yValue == -0.0, and 0.0 == -0.0) f = 0.0; mYAxis.mEntries[i] = (float) f; @@ -139,7 +139,7 @@ protected void computeAxisValues(float min, float max) { } /** - * draws the y-axis labels to the screen + * draws the yPx-axis labels to the screen */ @Override public void renderAxisLabels(Canvas c) { @@ -150,9 +150,9 @@ public void renderAxisLabels(Canvas c) { float[] positions = new float[mYAxis.mEntryCount * 2]; for (int i = 0; i < positions.length; i += 2) { - // only fill y values, x values are not needed since the y-labels + // only fill yPx values, xPx values are not needed since the yPx-labels // are - // static on the x-axis + // static on the xPx-axis positions[i + 1] = mYAxis.mEntries[i / 2]; } @@ -213,7 +213,7 @@ public void renderAxisLine(Canvas c) { } /** - * draws the y-labels on the specified x-position + * draws the yPx-labels on the specified xPx-position * * @param fixedPosition * @param positions @@ -339,7 +339,7 @@ public void renderLimitLines(Canvas c) { String label = l.getLabel(); - // if drawing the limit-value label is enabled + // if drawing the limit-yValue label is enabled if (label != null && !label.equals("")) { mLimitLinePaint.setStyle(l.getTextStyle()); 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 4ce4879e3b..330ac05eb6 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 @@ -30,13 +30,13 @@ public YAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, YAxis yA /** * Computes the axis values. * - * @param yMin - the minimum y-value in the data object for this axis - * @param yMax - the maximum y-value in the data object for this axis + * @param yMin - the minimum yPx-yValue in the data object for this axis + * @param yMax - the maximum yPx-yValue in the data object for this axis */ @Override public void computeAxis(float yMin, float yMax, boolean inverted) { - // calculate the starting and entry point of the y-labels (depending on + // calculate the starting and entry point of the yPx-labels (depending on // zoom / contentrect bounds) if (mViewPortHandler.contentHeight() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { @@ -58,7 +58,7 @@ public void computeAxis(float yMin, float yMax, boolean inverted) { } /** - * draws the y-axis labels to the screen + * draws the yPx-axis labels to the screen */ @Override public void renderAxisLabels(Canvas c) { @@ -69,9 +69,9 @@ public void renderAxisLabels(Canvas c) { float[] positions = new float[mYAxis.mEntryCount * 2]; for (int i = 0; i < positions.length; i += 2) { - // only fill y values, x values are not needed since the y-labels + // only fill yPx values, xPx values are not needed since the yPx-labels // are - // static on the x-axis + // static on the xPx-axis positions[i] = mYAxis.mEntries[i / 2]; } @@ -131,7 +131,7 @@ public void renderAxisLine(Canvas c) { } /** - * draws the y-labels on the specified x-position + * draws the yPx-labels on the specified xPx-position * * @param fixedPosition * @param positions @@ -235,7 +235,7 @@ public void renderLimitLines(Canvas c) { String label = l.getLabel(); - // if drawing the limit-value label is enabled + // if drawing the limit-yValue label is enabled if (label != null && !label.equals("")) { mLimitLinePaint.setStyle(l.getTextStyle()); 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 e06d2fcbf8..e31487d84d 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 @@ -80,7 +80,7 @@ protected void computeAxisValues(float min, float max) { final double rawCount = yMin / interval; double first = rawCount < 0.0 ? Math.floor(rawCount) * interval : Math.ceil(rawCount) * interval; - if (first == 0.0) // Fix for IEEE negative zero case (Where value == -0.0, and 0.0 == -0.0) + if (first == 0.0) // Fix for IEEE negative zero case (Where yValue == -0.0, and 0.0 == -0.0) first = 0.0; double last = Utils.nextUp(Math.floor(yMax / interval) * interval); @@ -164,7 +164,7 @@ public void renderLimitLines(Canvas c) { float sliceangle = mChart.getSliceAngle(); - // calculate the factor that is needed for transforming the value to + // calculate the factor that is needed for transforming the yValue to // pixels float factor = mChart.getFactor(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXIndexComparator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXIndexComparator.java index cf62492390..ed9045a25e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXIndexComparator.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXIndexComparator.java @@ -5,7 +5,7 @@ import java.util.Comparator; /** - * Comparator for comparing Entry-objects by their x-index. + * Comparator for comparing Entry-objects by their xPx-index. * Created by philipp on 17/06/15. */ public class EntryXIndexComparator implements Comparator { 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 fa7ff4489a..6cb5c837e7 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 @@ -32,7 +32,7 @@ public boolean equals(final Object obj) { @Override public String toString() { - return width + "x" + height; + return width + "xPx" + height; } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java index 7335d771ab..e5d7abce23 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java @@ -20,6 +20,6 @@ public PointD(double x, double y) { * returns a string representation of the object */ public String toString() { - return "PointD, x: " + x + ", y: " + y; + return "PointD, xPx: " + x + ", yPx: " + y; } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java index 2aaa786e90..0934883b02 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java @@ -3,36 +3,38 @@ import com.github.mikephil.charting.interfaces.datasets.IDataSet; /** - * Class that encapsulates information of a value that has been + * Class that encapsulates information of a yValue that has been * selected/highlighted and its DataSet index. The SelectionDetail objects give - * information about the value at the selected index and the DataSet it belongs + * information about the yValue at the selected index and the DataSet it belongs * to. Needed only for highlighting onTouch(). * * @author Philipp Jahoda */ public class SelectionDetail { - public float y; - public float x; - public float value; + public float yPx; + public float xPx; + public float yValue; + public float xValue; public int dataIndex; public int dataSetIndex; public IDataSet dataSet; - public SelectionDetail(float x, float y, float value, int dataIndex, int dataSetIndex, IDataSet set) { - this.x = x; - this.y = y; - this.value = value; + public SelectionDetail(float xPx, float yPx, float xValue, float yValue, int dataIndex, int dataSetIndex, IDataSet set) { + this.xPx = xPx; + this.yPx = yPx; + this.xValue = xValue; + this.yValue = yValue; this.dataIndex = dataIndex; this.dataSetIndex = dataSetIndex; this.dataSet = set; } - public SelectionDetail(float x, float y, float value, int dataSetIndex, IDataSet set) { - this(x, y, value, 0, dataSetIndex, set); + public SelectionDetail(float xPx, float yPx, float xValue, float yValue, int dataSetIndex, IDataSet set) { + this(xPx, yPx, xValue, yValue, 0, dataSetIndex, set); } - public SelectionDetail(float value, int dataSetIndex, IDataSet set) { - this(Float.NaN, value, 0, dataSetIndex, set); + public SelectionDetail(float xValue, float yValue, int dataSetIndex, IDataSet set) { + this(Float.NaN, Float.NaN, xValue, yValue, 0, dataSetIndex, set); } } \ No newline at end of file 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 af7074559a..40d738e9d6 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 @@ -54,12 +54,10 @@ public void prepareMatrixValuePx(float xChartMin, float deltaX, float deltaY, fl float scaleX = (float) ((mViewPortHandler.contentWidth()) / deltaX); float scaleY = (float) ((mViewPortHandler.contentHeight()) / deltaY); - if (Float.isInfinite(scaleX)) - { + if (Float.isInfinite(scaleX)) { scaleX = 0; } - if (Float.isInfinite(scaleY)) - { + if (Float.isInfinite(scaleY)) { scaleY = 0; } @@ -98,8 +96,8 @@ public void prepareMatrixOffset(boolean inverted) { } /** - * Transforms an List of Entry into a float array containing the x and - * y values transformed with all matrices for the SCATTERCHART. + * Transforms an List of Entry into a float array containing the xPx and + * yPx values transformed with all matrices for the SCATTERCHART. * * @param data * @return @@ -125,8 +123,8 @@ public float[] generateTransformedValuesScatter(IScatterDataSet data, } /** - * Transforms an List of Entry into a float array containing the x and - * y values transformed with all matrices for the BUBBLECHART. + * Transforms an List of Entry into a float array containing the xPx and + * yPx values transformed with all matrices for the BUBBLECHART. * * @param data * @return @@ -154,8 +152,8 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, } /** - * Transforms an List of Entry into a float array containing the x and - * y values transformed with all matrices for the LINECHART. + * Transforms an List of Entry into a float array containing the xPx and + * yPx values transformed with all matrices for the LINECHART. * * @param data * @return @@ -183,8 +181,8 @@ public float[] generateTransformedValuesLine(ILineDataSet data, } /** - * Transforms an List of Entry into a float array containing the x and - * y values transformed with all matrices for the CANDLESTICKCHART. + * Transforms an List of Entry into a float array containing the xPx and + * yPx values transformed with all matrices for the CANDLESTICKCHART. * * @param data * @return @@ -212,8 +210,8 @@ public float[] generateTransformedValuesCandle(ICandleDataSet data, } /** - * Transforms an List of Entry into a float array containing the x and - * y values transformed with all matrices for the BARCHART. + * Transforms an List of Entry into a float array containing the xPx and + * yPx values transformed with all matrices for the BARCHART. * * @param data * @param dataSet the dataset index @@ -232,7 +230,7 @@ public float[] generateTransformedValuesHorizontalBarChart(IBarDataSet data, Entry e = data.getEntryForIndex(j / 2); float i = e.getX(); - // calculate the x-position, depending on datasetcount + // calculate the xPx-position, depending on datasetcount float x = i + i * (setCount - 1) + dataSet + space * i + space / 2f; float y = e.getY(); @@ -248,7 +246,7 @@ public float[] generateTransformedValuesHorizontalBarChart(IBarDataSet data, /** * transform a path with all the given matrices VERY IMPORTANT: keep order - * to value-touch-offset + * to yValue-touch-offset * * @param path */ @@ -273,7 +271,7 @@ public void pathValuesToPixel(List paths) { /** * Transform an array of points with all matrices. VERY IMPORTANT: Keep - * matrix order "value-touch-offset" when transforming. + * matrix order "yValue-touch-offset" when transforming. * * @param pts */ @@ -356,7 +354,7 @@ public void rectValuesToPixel(List rects) { } /** - * Transforms the given array of touch positions (pixels) (x, y, x, y, ...) + * Transforms the given array of touch positions (pixels) (xPx, yPx, xPx, yPx, ...) * into values on the chart. * * @param pixels @@ -365,7 +363,7 @@ public void pixelsToValue(float[] pixels) { Matrix tmp = new Matrix(); - // invert all matrixes to convert back to the original value + // invert all matrixes to convert back to the original yValue mMatrixOffset.invert(tmp); tmp.mapPoints(pixels); @@ -376,11 +374,13 @@ public void pixelsToValue(float[] pixels) { tmp.mapPoints(pixels); } - /** buffer for performance */ + /** + * buffer for performance + */ float[] ptsBuffer = new float[2]; /** - * Returns the x and y values in the chart at the given touch point + * Returns the xPx and yPx values in the chart at the given touch point * (encapsulated in a PointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to * getPixelsForValues(...). @@ -402,6 +402,26 @@ public PointD getValuesByTouchPoint(float x, float y) { return new PointD(xTouchVal, yTouchVal); } + /** + * Returns the xPx and yPx coordinates (pixels) for a given xPx and yPx yValue in the chart. + * + * @param x + * @param y + * @return + */ + public PointD getPixelsForValues(float x, float y) { + + ptsBuffer[0] = x; + ptsBuffer[1] = y; + + pointValuesToPixel(ptsBuffer); + + double xPx = ptsBuffer[0]; + double yPx = ptsBuffer[1]; + + return new PointD(xPx, yPx); + } + public Matrix getValueMatrix() { return mMatrixValueToPx; } 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 1abdc46b06..bf9f3157d3 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 @@ -89,9 +89,9 @@ public static void init(Resources res) { * This method converts dp unit to equivalent pixels, depending on device * density. NEEDS UTILS TO BE INITIALIZED BEFORE USAGE. * - * @param dp A value in dp (density independent pixels) unit. Which we need + * @param dp A yValue in dp (density independent pixels) unit. Which we need * to convert into pixels - * @return A float value to represent px equivalent to dp depending on + * @return A float yValue to represent px equivalent to dp depending on * device density */ public static float convertDpToPixel(float dp) { @@ -117,8 +117,8 @@ public static float convertDpToPixel(float dp) { * This method converts device specific pixels to density independent * pixels. NEEDS UTILS TO BE INITIALIZED BEFORE USAGE. * - * @param px A value in px (pixels) unit. Which we need to convert into db - * @return A float value to represent dp equivalent to px value + * @param px A yValue in px (pixels) unit. Which we need to convert into db + * @return A float yValue to represent dp equivalent to px yValue */ public static float convertPixelsToDp(float px) { @@ -382,8 +382,8 @@ public static double nextUp(double d) { } /** - * Returns the index of the DataSet that contains the closest value on the - * y-axis. This is needed for highlighting. This will return -Integer.MAX_VALUE if failure. + * Returns the index of the DataSet that contains the closest yValue on the + * yPx-axis. This is needed for highlighting. This will return -Integer.MAX_VALUE if failure. * * @param valsAtIndex all the values at a specific index * @return @@ -400,8 +400,8 @@ public static int getClosestDataSetIndexByValue(List valsAtInde } /** - * Returns the SelectionDetail of the DataSet that contains the closest value on the - * y-axis. + * Returns the SelectionDetail of the DataSet that contains the closest yValue on the + * yPx-axis. * * @param valsAtIndex all the values at a specific index * @return @@ -420,7 +420,7 @@ public static SelectionDetail getClosestSelectionDetailByValue( if (axis == null || sel.dataSet.getAxisDependency() == axis) { - float cdistance = Math.abs(sel.value - value); + float cdistance = Math.abs(sel.yValue - value); if (cdistance < distance) { closest = sel; distance = cdistance; @@ -432,8 +432,8 @@ public static SelectionDetail getClosestSelectionDetailByValue( } /** - * Returns the SelectionDetail of the DataSet that contains the closest value on the - * y-axis. + * Returns the SelectionDetail of the DataSet that contains the closest yValue on the + * yPx-axis. * * @param valsAtIndex all the values at a specific index * @return @@ -452,7 +452,7 @@ public static SelectionDetail getClosestSelectionDetailByPixel( if (axis == null || sel.dataSet.getAxisDependency() == axis) { - float cDistance = (float) Math.hypot(x - sel.x, y - sel.y); + float cDistance = (float) Math.hypot(x - sel.xPx, y - sel.yPx); if (cDistance < distance) { closest = sel; @@ -465,8 +465,8 @@ public static SelectionDetail getClosestSelectionDetailByPixel( } /** - * Returns the minimum distance from a touch-y-value (in pixels) to the - * closest y-value (in pixels) that is displayed in the chart. + * Returns the minimum distance from a touch-yPx-yValue (in pixels) to the + * closest yPx-yValue (in pixels) that is displayed in the chart. * * @param valsAtIndex * @param y @@ -485,7 +485,7 @@ public static float getMinimumDistance(List valsAtIndex, if (sel.dataSet.getAxisDependency() == axis) { - float cdistance = Math.abs(sel.y - y); + float cdistance = Math.abs(sel.yPx - y); if (cdistance < distance) { distance = cdistance; } @@ -785,7 +785,7 @@ public static int getSDKInt() { */ public static double granularity(float range, int labelCount) { - // Find out how much spacing (in y value space) between axis values + // Find out how much spacing (in yPx yValue space) between axis values double rawInterval = range / labelCount; double interval = Utils.roundToNextSignificant(rawInterval); 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 955e39cf2d..4031a6323f 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 @@ -28,52 +28,52 @@ public class ViewPortHandler { protected float mChartHeight = 0f; /** - * minimum scale value on the y-axis + * minimum scale yValue on the yPx-axis */ private float mMinScaleY = 1f; /** - * maximum scale value on the y-axis + * maximum scale yValue on the yPx-axis */ private float mMaxScaleY = Float.MAX_VALUE; /** - * minimum scale value on the x-axis + * minimum scale yValue on the xPx-axis */ private float mMinScaleX = 1f; /** - * maximum scale value on the x-axis + * maximum scale yValue on the xPx-axis */ private float mMaxScaleX = Float.MAX_VALUE; /** - * contains the current scale factor of the x-axis + * contains the current scale factor of the xPx-axis */ private float mScaleX = 1f; /** - * contains the current scale factor of the y-axis + * contains the current scale factor of the yPx-axis */ private float mScaleY = 1f; /** - * current translation (drag distance) on the x-axis + * current translation (drag distance) on the xPx-axis */ private float mTransX = 0f; /** - * current translation (drag distance) on the y-axis + * current translation (drag distance) on the yPx-axis */ private float mTransY = 0f; /** - * offset that allows the chart to be dragged over its bounds on the x-axis + * offset that allows the chart to be dragged over its bounds on the xPx-axis */ private float mTransOffsetX = 0f; /** - * offset that allows the chart to be dragged over its bounds on the x-axis + * offset that allows the chart to be dragged over its bounds on the xPx-axis */ private float mTransOffsetY = 0f; @@ -180,7 +180,7 @@ public float getChartWidth() { /** CODE BELOW THIS RELATED TO SCALING AND GESTURES */ /** - * Zooms in by 1.4f, x and y are the coordinates (in pixels) of the zoom + * Zooms in by 1.4f, xPx and yPx are the coordinates (in pixels) of the zoom * center. * * @param x @@ -197,7 +197,7 @@ public Matrix zoomIn(float x, float y) { } /** - * Zooms out by 0.7f, x and y are the coordinates (in pixels) of the zoom + * Zooms out by 0.7f, xPx and yPx are the coordinates (in pixels) of the zoom * center. */ public Matrix zoomOut(float x, float y) { @@ -228,7 +228,7 @@ public Matrix zoom(float scaleX, float scaleY) { } /** - * Post-scales by the specified scale factors. x and y is pivot. + * Post-scales by the specified scale factors. xPx and yPx is pivot. * * @param scaleX * @param scaleY @@ -264,7 +264,7 @@ public Matrix setZoom(float scaleX, float scaleY) { } /** - * Sets the scale factor to the specified values. x and y is pivot. + * Sets the scale factor to the specified values. xPx and yPx is pivot. * * @param scaleX * @param scaleY @@ -329,7 +329,7 @@ public Matrix translate(final float[] transformedPts) { } /** - * Centers the viewport around the specified position (x-index and y-value) + * Centers the viewport around the specified position (xPx-index and yPx-yValue) * in the chart. Centering the viewport outside the bounds of the chart is * not possible. Makes most sense in combination with the * setScaleMinima(...) method. @@ -391,10 +391,10 @@ public void limitTransAndScale(Matrix matrix, RectF content) { float curTransY = matrixBuffer[Matrix.MTRANS_Y]; float curScaleY = matrixBuffer[Matrix.MSCALE_Y]; - // min scale-x is 1f + // min scale-xPx is 1f mScaleX = Math.min(Math.max(mMinScaleX, curScaleX), mMaxScaleX); - // min scale-y is 1f + // min scale-yPx is 1f mScaleY = Math.min(Math.max(mMinScaleY, curScaleY), mMaxScaleY); float width = 0f; @@ -421,7 +421,7 @@ public void limitTransAndScale(Matrix matrix, RectF content) { } /** - * Sets the minimum scale factor for the x-axis + * Sets the minimum scale factor for the xPx-axis * * @param xScale */ @@ -436,7 +436,7 @@ public void setMinimumScaleX(float xScale) { } /** - * Sets the maximum scale factor for the x-axis + * Sets the maximum scale factor for the xPx-axis * * @param xScale */ @@ -451,7 +451,7 @@ public void setMaximumScaleX(float xScale) { } /** - * Sets the minimum and maximum scale factors for the x-axis + * Sets the minimum and maximum scale factors for the xPx-axis * * @param minScaleX * @param maxScaleX @@ -471,7 +471,7 @@ public void setMinMaxScaleX(float minScaleX, float maxScaleX) { } /** - * Sets the minimum scale factor for the y-axis + * Sets the minimum scale factor for the yPx-axis * * @param yScale */ @@ -486,7 +486,7 @@ public void setMinimumScaleY(float yScale) { } /** - * Sets the maximum scale factor for the y-axis + * Sets the maximum scale factor for the yPx-axis * * @param yScale */ @@ -556,14 +556,14 @@ public boolean isInBoundsBottom(float y) { } /** - * returns the current x-scale factor + * returns the current xPx-scale factor */ public float getScaleX() { return mScaleX; } /** - * returns the current y-scale factor + * returns the current yPx-scale factor */ public float getScaleY() { return mScaleY; @@ -586,7 +586,7 @@ public float getMaxScaleY() { } /** - * Returns the translation (drag / pan) distance on the x-axis + * Returns the translation (drag / pan) distance on the xPx-axis * * @return */ @@ -595,7 +595,7 @@ public float getTransX() { } /** - * Returns the translation (drag / pan) distance on the y-axis + * Returns the translation (drag / pan) distance on the yPx-axis * * @return */ @@ -617,7 +617,7 @@ public boolean isFullyZoomedOut() { } /** - * Returns true if the chart is fully zoomed out on it's y-axis (vertical). + * Returns true if the chart is fully zoomed out on it's yPx-axis (vertical). * * @return */ @@ -629,7 +629,7 @@ public boolean isFullyZoomedOutY() { } /** - * Returns true if the chart is fully zoomed out on it's x-axis + * Returns true if the chart is fully zoomed out on it's xPx-axis * (horizontal). * * @return @@ -643,7 +643,7 @@ public boolean isFullyZoomedOutX() { /** * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the x-axis. + * bounds on the xPx-axis. * * @param offset */ @@ -653,7 +653,7 @@ public void setDragOffsetX(float offset) { /** * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the y-axis. + * bounds on the yPx-axis. * * @param offset */ @@ -662,7 +662,7 @@ public void setDragOffsetY(float offset) { } /** - * Returns true if both drag offsets (x and y) are zero or smaller. + * Returns true if both drag offsets (xPx and yPx) are zero or smaller. * * @return */ @@ -671,7 +671,7 @@ public boolean hasNoDragOffset() { } /** - * Returns true if the chart is not yet fully zoomed out on the x-axis + * Returns true if the chart is not yet fully zoomed out on the xPx-axis * * @return */ @@ -680,7 +680,7 @@ public boolean canZoomOutMoreX() { } /** - * Returns true if the chart is not yet fully zoomed in on the x-axis + * Returns true if the chart is not yet fully zoomed in on the xPx-axis * * @return */ @@ -689,7 +689,7 @@ public boolean canZoomInMoreX() { } /** - * Returns true if the chart is not yet fully zoomed out on the y-axis + * Returns true if the chart is not yet fully zoomed out on the yPx-axis * * @return */ @@ -698,7 +698,7 @@ public boolean canZoomOutMoreY() { } /** - * Returns true if the chart is not yet fully zoomed in on the y-axis + * Returns true if the chart is not yet fully zoomed in on the yPx-axis * * @return */ diff --git a/README.md b/README.md index 6390228dae..1d36cef3a8 100644 --- a/README.md +++ b/README.md @@ -78,12 +78,12 @@ Features - Dragging / Panning (with touch-gesture) - Combined-Charts (line-, bar-, scatter-, candle-data) - Dual (separate) Axes - - Customizable Axes (both x- and y-axis) + - Customizable Axes (both xPx- and yPx-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 x- and y-axis) + - 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 @@ -188,7 +188,7 @@ Chart types ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/candlestickchart.png) - - **BubbleChart** (area covered by bubbles indicates the value) + - **BubbleChart** (area covered by bubbles indicates the yValue) ![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/bubblechart.png) diff --git a/gradlew b/gradlew index 9d82f78915..86ee20772b 100755 --- a/gradlew +++ b/gradlew @@ -12,7 +12,7 @@ DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` -# Use the maximum available, or set MAX_FD != -1 to use that value. +# Use the maximum available, or set MAX_FD != -1 to use that yValue. MAX_FD="maximum" warn ( ) { @@ -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 [ -x "$JAVA_HOME/jre/sh/java" ] ; then + if [ -xPx "$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 [ ! -x "$JAVACMD" ] ; then + if [ ! -xPx "$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 diff --git a/gradlew.bat b/gradlew.bat index aec99730b4..b411ee1fa1 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -57,7 +57,7 @@ set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp -if "x%~1" == "x" goto execute +if "xPx%~1" == "xPx" goto execute set CMD_LINE_ARGS=%* goto execute From ddb03d4dec17c60c2c02a744c2dd885815c4596b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 31 May 2016 12:27:45 +0200 Subject: [PATCH 135/606] Work on highlighting --- .../charting/charts/BarLineChartBase.java | 4 +- .../charting/highlight/BarHighlighter.java | 462 +++++++++--------- .../charting/renderer/LineChartRenderer.java | 5 +- 3 files changed, 230 insertions(+), 241 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 510c1e0a88..90e4f9ffdf 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 @@ -940,7 +940,7 @@ public void moveViewToAnimated(float xIndex, float yValue, AxisDependency axis, public void centerViewTo(float xIndex, float yValue, AxisDependency axis) { float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - float xsInView = getXAxis().getValues().size() / mViewPortHandler.getScaleX(); + float xsInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); Runnable job = new MoveViewJob(mViewPortHandler, xIndex - xsInView / 2f, yValue + valsInView / 2f, @@ -966,7 +966,7 @@ public void centerViewToAnimated(float xIndex, float yValue, AxisDependency axis PointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - float xsInView = getXAxis().getValues().size() / mViewPortHandler.getScaleX(); + float xsInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); Runnable job = new AnimatedMoveViewJob(mViewPortHandler, xIndex - xsInView / 2f, yValue + valsInView / 2f, 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 0a5b4e6f94..ad96567eba 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 @@ -3,9 +3,11 @@ import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.SelectionDetail; /** @@ -13,241 +15,227 @@ */ public class BarHighlighter extends ChartHighlighter { - public BarHighlighter(BarDataProvider chart) { - super(chart); - } - - @Override - public Highlight getHighlight(float x, float y) { - - BarData barData = mChart.getBarData(); - - final float xVal = getXForTouch(x); - final float baseNoSpace = getBase(x); - final int setCount = barData.getDataSetCount(); - int dataSetIndex = ((int)baseNoSpace) % setCount; - - if (dataSetIndex < 0) { - dataSetIndex = 0; - } else if (dataSetIndex >= setCount) { - dataSetIndex = setCount - 1; - } - - SelectionDetail selectionDetail = getSelectionDetail(xVal, x, y, dataSetIndex); - if (selectionDetail == null) - return null; - - IBarDataSet set = barData.getDataSetByIndex(dataSetIndex); - if (set.isStacked()) { - - float[] pts = new float[2]; - pts[1] = y; - - // take any transformer to determine the xPx-axis yValue - mChart.getTransformer(set.getAxisDependency()).pixelsToValue(pts); - - return getStackedHighlight(selectionDetail, - set, - xVal, - pts[1]); - } - - return new Highlight( - xVal, - selectionDetail.yValue, - selectionDetail.dataIndex, - selectionDetail.dataSetIndex, - -1); - } - - @Override - protected float getXForTouch(float x) { - - if (!mChart.getBarData().isGrouped()) { - return super.getXForTouch(x); - } else { - - float baseNoSpace = getBase(x); - - int setCount = mChart.getBarData().getDataSetCount(); - int xIndex = (int) baseNoSpace / setCount; - - int valCount = mChart.getData().getXValCount(); - - if (xIndex < 0) - xIndex = 0; - else if (xIndex >= valCount) - xIndex = valCount - 1; - - return xIndex; - } - } - - @Override - protected SelectionDetail getSelectionDetail(float xVal, float x, float y, int dataSetIndex) { - - dataSetIndex = Math.max(dataSetIndex, 0); - - BarData barData = mChart.getBarData(); - IDataSet dataSet = barData.getDataSetCount() > dataSetIndex - ? barData.getDataSetByIndex(dataSetIndex) - : null; - if (dataSet == null) - return null; - - final float yValue = dataSet.getYValueForXValue(xVal); - - if (yValue == Double.NaN) return null; - - return new SelectionDetail(0f, - yValue, - dataSetIndex, - dataSet); - } - - /** - * This method creates the Highlight object that also indicates which yValue of a stacked BarEntry has been selected. - * - * @param selectionDetail the selection detail to work with looking for stacked values - * @param set - * @param xVal - * @param yValue - * @return - */ - protected Highlight getStackedHighlight( - SelectionDetail selectionDetail, - IBarDataSet set, - float xVal, - double yValue) { - - BarEntry entry = set.getEntryForXPos(xVal); - - if (entry == null) - return null; - - if (entry.getYVals() == null) { - return new Highlight(xVal, - entry.getY(), - selectionDetail.dataIndex, - selectionDetail.dataSetIndex); - } - - Range[] ranges = getRanges(entry); - if (ranges.length > 0) { - int stackIndex = getClosestStackIndex(ranges, (float)yValue); - return new Highlight( - xVal, - entry.getPositiveSum() - entry.getNegativeSum(), - selectionDetail.dataIndex, - selectionDetail.dataSetIndex, - stackIndex, - ranges[stackIndex] - ); - } - - return null; - } - - /** - * Returns the index of the closest yValue inside the values array / ranges (stacked barchart) to the yValue given as - * a parameter. - * - * @param ranges - * @param value - * @return - */ - protected int getClosestStackIndex(Range[] ranges, float value) { - - if (ranges == null || ranges.length == 0) - return 0; - - int stackIndex = 0; - - for (Range range : ranges) { - if (range.contains(value)) - return stackIndex; - else - stackIndex++; - } - - int length = Math.max(ranges.length - 1, 0); - - return (value > ranges[length].to) ? length : 0; - // - // float[] vals = e.getYVals(); - // - // if (vals == null) - // return -1; - // - // int index = 0; - // float remainder = e.getNegativeSum(); - // - // while (index < vals.length - 1 && yValue > vals[index] + remainder) { - // remainder += vals[index]; - // index++; - // } - // - // return index; - } - - /** - * Returns the base xPx-yValue to the corresponding xPx-touch yValue in pixels. - * - * @param x - * @return - */ - protected float getBase(float x) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - - // take any transformer to determine the xPx-axis yValue - mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); - float xVal = pts[0]; - - int setCount = mChart.getBarData().getDataSetCount(); - - // calculate how often the group-space appears - int steps = (int) ((float) xVal / ((float) setCount + mChart.getBarData().getGroupSpace())); - - float groupSpaceSum = mChart.getBarData().getGroupSpace() * (float) steps; - - float baseNoSpace = (float) xVal - groupSpaceSum; - return baseNoSpace; - } - - /** - * Splits up the stack-values of the given bar-entry into Range objects. - * - * @param entry - * @return - */ - protected Range[] getRanges(BarEntry entry) { - - float[] values = entry.getYVals(); - - if (values == null || values.length == 0) - return new Range[0]; - - Range[] ranges = new Range[values.length]; - - float negRemain = -entry.getNegativeSum(); - float posRemain = 0f; - - for (int i = 0; i < ranges.length; i++) { - - float value = values[i]; - - if (value < 0) { - ranges[i] = new Range(negRemain, negRemain + Math.abs(value)); - negRemain += Math.abs(value); - } else { - ranges[i] = new Range(posRemain, posRemain + value); - posRemain += value; - } - } - - return ranges; - } + public BarHighlighter(BarDataProvider chart) { + super(chart); + } + + @Override + public Highlight getHighlight(float x, float y) { + + BarData barData = mChart.getBarData(); + + final float xVal = getXForTouch(x); + final float baseNoSpace = getBase(x); + final int setCount = barData.getDataSetCount(); + int dataSetIndex = ((int) baseNoSpace) % setCount; + + if (dataSetIndex < 0) { + dataSetIndex = 0; + } else if (dataSetIndex >= setCount) { + dataSetIndex = setCount - 1; + } + + SelectionDetail selectionDetail = getSelectionDetail(xVal, x, y, dataSetIndex); + if (selectionDetail == null) + return null; + + IBarDataSet set = barData.getDataSetByIndex(dataSetIndex); + if (set.isStacked()) { + + float[] pts = new float[2]; + pts[1] = y; + + // take any transformer to determine the xPx-axis yValue + mChart.getTransformer(set.getAxisDependency()).pixelsToValue(pts); + + return getStackedHighlight(selectionDetail, + set, + xVal, + pts[1]); + } + + return new Highlight( + selectionDetail.xValue, + selectionDetail.yValue, + selectionDetail.dataIndex, + selectionDetail.dataSetIndex, + -1); + } + + @Override + protected float getXForTouch(float x) { + + if (!mChart.getBarData().isGrouped()) { + return super.getXForTouch(x); + } else { + return getBase(x); +// +// float baseNoSpace = getBase(x); +// +// int setCount = mChart.getBarData().getDataSetCount(); +// int xIndex = (int) baseNoSpace / setCount; +// +// int valCount = mChart.getData().getXValCount(); +// +// if (xIndex < 0) +// xIndex = 0; +// else if (xIndex >= valCount) +// xIndex = valCount - 1; +// +// return xIndex; + } + } + + @Override + protected SelectionDetail getSelectionDetail(float xVal, float x, float y, int dataSetIndex) { + + dataSetIndex = Math.max(dataSetIndex, 0); + + BarData barData = mChart.getBarData(); + IDataSet dataSet = barData.getDataSetCount() > dataSetIndex + ? barData.getDataSetByIndex(dataSetIndex) + : null; + + if (dataSet == null) + return null; + + final Entry entry = dataSet.getEntryForXPos(xVal); + + return new SelectionDetail(entry.getX(), + entry.getY(), + dataSetIndex, + dataSet); + } + + /** + * This method creates the Highlight object that also indicates which yValue of a stacked BarEntry has been + * selected. + * + * @param selectionDetail the selection detail to work with looking for stacked values + * @param set + * @param xVal + * @param yValue + * @return + */ + protected Highlight getStackedHighlight( + SelectionDetail selectionDetail, + IBarDataSet set, + float xVal, + double yValue) { + + BarEntry entry = set.getEntryForXPos(xVal); + + if (entry == null) + return null; + + // not stacked + if (entry.getYVals() == null) { + return new Highlight(entry.getX(), + entry.getY(), + selectionDetail.dataIndex, + selectionDetail.dataSetIndex); + } else { + Range[] ranges = getRanges(entry); + + if (ranges.length > 0) { + int stackIndex = getClosestStackIndex(ranges, (float) yValue); + return new Highlight( + entry.getX(), + entry.getPositiveSum() - entry.getNegativeSum(), + selectionDetail.dataIndex, + selectionDetail.dataSetIndex, + stackIndex, + ranges[stackIndex] + ); + } + } + + return null; + } + + /** + * Returns the index of the closest yValue inside the values array / ranges (stacked barchart) to the yValue + * given as + * a parameter. + * + * @param ranges + * @param value + * @return + */ + protected int getClosestStackIndex(Range[] ranges, float value) { + + if (ranges == null || ranges.length == 0) + return 0; + + int stackIndex = 0; + + for (Range range : ranges) { + if (range.contains(value)) + return stackIndex; + else + stackIndex++; + } + + int length = Math.max(ranges.length - 1, 0); + + return (value > ranges[length].to) ? length : 0; + } + + /** + * Returns the base xPx-yValue to the given xPx-touch value in pixels. + * + * @param x + * @return + */ + protected float getBase(float x) { + + // take any transformer to determine the x-axis value + PointD val = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(x, 0f); + int setCount = mChart.getBarData().getDataSetCount(); + return (float) val.x; +// +// +// +// // calculate how often the group-space appears +// int steps = (int) ((float) xVal / ((float) setCount + mChart.getBarData().getGroupSpace())); +// +// float groupSpaceSum = mChart.getBarData().getGroupSpace() * (float) steps; +// +// float baseNoSpace = (float) xVal - groupSpaceSum; +// return baseNoSpace; + } + + /** + * Splits up the stack-values of the given bar-entry into Range objects. + * + * @param entry + * @return + */ + protected Range[] getRanges(BarEntry entry) { + + float[] values = entry.getYVals(); + + if (values == null || values.length == 0) + return new Range[0]; + + Range[] ranges = new Range[values.length]; + + float negRemain = -entry.getNegativeSum(); + float posRemain = 0f; + + for (int i = 0; i < ranges.length; i++) { + + float value = values[i]; + + if (value < 0) { + ranges[i] = new Range(negRemain, negRemain + Math.abs(value)); + negRemain += Math.abs(value); + } else { + ranges[i] = new Range(posRemain, posRemain + value); + posRemain += value; + } + } + + return ranges; + } } 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 1cf0250791..d7070cb7d5 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 @@ -17,6 +17,7 @@ import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -744,10 +745,10 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (x > mChart.getXChartMax() * mAnimator.getPhaseX()) continue; - mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(x, y); + PointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(x, y); // draw the lines - drawHighlightLines(c, x, y, set); + drawHighlightLines(c, (float) pix.x, (float) pix.y, set); } } } From 1bf2948f48b31dc8979ccd3dddefe006968f050a Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 31 May 2016 15:02:31 +0200 Subject: [PATCH 136/606] Work on bar rendering --- .../mpchartexample/BarChartActivity.java | 14 +-- .../BarChartActivityMultiDataset.java | 7 +- .../mpchartexample/BarChartActivitySinus.java | 2 +- .../BarChartPositiveNegative.java | 2 +- .../mpchartexample/CombinedChartActivity.java | 7 +- .../ListViewBarChartActivity.java | 4 +- .../ListViewMultiChartActivity.java | 10 +- .../StackedBarActivityNegative.java | 2 +- .../mikephil/charting/buffer/BarBuffer.java | 37 ++---- .../charting/charts/CombinedChart.java | 60 +++++----- .../mikephil/charting/data/BarData.java | 113 ++++++++++++++---- .../mikephil/charting/data/BarDataSet.java | 9 +- .../github/mikephil/charting/data/Entry.java | 2 +- .../charting/highlight/BarHighlighter.java | 18 +-- .../highlight/HorizontalBarHighlighter.java | 60 +++++----- .../charting/renderer/BarChartRenderer.java | 24 +++- 16 files changed, 220 insertions(+), 151 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index a6c662a4c3..768e6f6ccb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -232,16 +232,15 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); + mChart.getXAxis().setAxisMinValue(0f); + mChart.getXAxis().setAxisMaxValue(count); + ArrayList yVals1 = new ArrayList(); for (int i = 0; i < count; i++) { float mult = (range + 1); float val = (float) (Math.random() * mult); - yVals1.add(new BarEntry(i, val)); - - XAxisValue value = new XAxisValue(i, mMonths[i % 12]); - xVals.add(value); + yVals1.add(new BarEntry(i + 0.5f, val)); } BarDataSet set1; @@ -250,20 +249,19 @@ private void setData(int count, float range) { mChart.getData().getDataSetCount() > 0) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals1); - mChart.getData().setXVals(xVals); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { set1 = new BarDataSet(yVals1, "DataSet"); - set1.setBarSpacePercent(35f); set1.setColors(ColorTemplate.MATERIAL_COLORS); ArrayList dataSets = new ArrayList(); dataSets.add(set1); - BarData data = new BarData(xVals, dataSets); + BarData data = new BarData(new ArrayList(), dataSets); data.setValueTextSize(10f); data.setValueTypeface(mTf); + data.setBarWidth(0.9f); mChart.setData(data); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 5da09fc6d7..853ccd763f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -183,6 +183,9 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + float groupSpace = 0.8f; + float barSpace = 0.15f; + tvX.setText("" + (mSeekBarX.getProgress() * 3)); tvY.setText("" + (mSeekBarY.getProgress())); @@ -247,12 +250,14 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // data.setValueFormatter(new LargeValueFormatter()); // add space between the dataset groups in percent of bar-width - data.setGroupSpace(80f); data.setValueTypeface(tf); mChart.setData(data); } + mChart.getBarData().groupBars(0, groupSpace, barSpace); + mChart.getXAxis().setAxisMinValue(0f); + mChart.getXAxis().setAxisMaxValue(mChart.getBarData().getIntervalWidth(groupSpace, barSpace) * mSeekBarX.getProgress()); mChart.invalidate(); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index d7d5b2f5cb..133f2ac437 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -229,7 +229,7 @@ private void setData(int count) { mChart.notifyDataSetChanged(); } else { set = new BarDataSet(entries, "Sinus Function"); - set.setBarSpacePercent(40f); + set.setBarSpace(40f); set.setColor(Color.rgb(240, 120, 124)); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index a2f653a7b9..a7730e3250 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -119,7 +119,7 @@ private void setData(List dataList) { mChart.notifyDataSetChanged(); } else { set = new BarDataSet(values, "Values"); - set.setBarSpacePercent(40f); + set.setBarSpace(40f); set.setColors(colors); set.setValueTextColors(colors); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 67a4de9529..d97ac3bf12 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -93,7 +93,7 @@ private LineData generateLineData() { ArrayList entries = new ArrayList(); for (int index = 0; index < itemcount; index++) - entries.add(new Entry(getRandom(15, 10), index)); + entries.add(new Entry(index, getRandom(15, 10))); LineDataSet set = new LineDataSet(entries, "Line DataSet"); set.setColor(Color.rgb(240, 238, 70)); @@ -116,11 +116,12 @@ private LineData generateLineData() { private BarData generateBarData() { BarData d = new BarData(); + d.setBarWidth(0.9f); ArrayList entries = new ArrayList(); for (int index = 0; index < itemcount; index++) - entries.add(new BarEntry(getRandom(15, 30), index)); + entries.add(new BarEntry(index, getRandom(15, 30))); BarDataSet set = new BarDataSet(entries, "Bar DataSet"); set.setColor(Color.rgb(60, 220, 78)); @@ -140,7 +141,7 @@ protected ScatterData generateScatterData() { ArrayList entries = new ArrayList(); for (int index = 0; index < itemcount; index++) - entries.add(new Entry(getRandom(20, 15), index)); + entries.add(new Entry(index, getRandom(20, 15))); ScatterDataSet set = new ScatterDataSet(entries, "Scatter DataSet"); set.setColor(Color.GREEN); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index 1912e07f89..a99bae5937 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java @@ -12,7 +12,6 @@ import android.widget.ArrayAdapter; import android.widget.ListView; -import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.BarChart; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; @@ -20,7 +19,6 @@ 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.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -138,7 +136,7 @@ private BarData generateData(int cnt) { } BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); - d.setBarSpacePercent(20f); + d.setBarSpace(20f); d.setColors(ColorTemplate.VORDIPLOM_COLORS); d.setBarShadowColor(Color.rgb(203, 203, 203)); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java index 3a4f5ba75c..6f50439a68 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java @@ -98,7 +98,7 @@ private LineData generateDataLine(int cnt) { ArrayList e1 = new ArrayList(); for (int i = 0; i < 12; i++) { - e1.add(new Entry((int) (Math.random() * 65) + 40, i)); + e1.add(new Entry(i, (int) (Math.random() * 65) + 40)); } LineDataSet d1 = new LineDataSet(e1, "New DataSet " + cnt + ", (1)"); @@ -110,7 +110,7 @@ private LineData generateDataLine(int cnt) { ArrayList e2 = new ArrayList(); for (int i = 0; i < 12; i++) { - e2.add(new Entry(e1.get(i).getY() - 30, i)); + e2.add(new Entry(i, e1.get(i).getY() - 30)); } LineDataSet d2 = new LineDataSet(e2, "New DataSet " + cnt + ", (2)"); @@ -139,11 +139,11 @@ private BarData generateDataBar(int cnt) { ArrayList entries = new ArrayList(); for (int i = 0; i < 12; i++) { - entries.add(new BarEntry((int) (Math.random() * 70) + 30, i)); + entries.add(new BarEntry(i, (int) (Math.random() * 70) + 30)); } BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); - d.setBarSpacePercent(20f); + d.setBarSpace(20f); d.setColors(ColorTemplate.VORDIPLOM_COLORS); d.setHighLightAlpha(255); @@ -161,7 +161,7 @@ private PieData generateDataPie(int cnt) { ArrayList entries = new ArrayList(); for (int i = 0; i < 4; i++) { - entries.add(new Entry((int) (Math.random() * 70) + 30, i)); + entries.add(new Entry(i, (int) (Math.random() * 70) + 30)); } PieDataSet d = new PieDataSet(entries, ""); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 5f74bbed62..318e9d899d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -96,7 +96,7 @@ protected void onCreate(Bundle savedInstanceState) { set.setValueFormatter(new CustomFormatter()); set.setValueTextSize(7f); set.setAxisDependency(YAxis.AxisDependency.RIGHT); - set.setBarSpacePercent(40f); + set.setBarSpace(40f); set.setColors(new int[] {Color.rgb(67,67,72), Color.rgb(124,181,236)}); set.setStackLabels(new String[]{ "Men", "Women" 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 d0ccaf01a6..615fe453dc 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 @@ -13,10 +13,8 @@ public class BarBuffer extends AbstractBuffer { protected boolean mContainsStacks = false; protected boolean mInverted = false; - /** - * interval on the xPx-axis per group - */ - protected float mInterval = 0f; + /** width of the bar on the x-axis, in values (not pixels) */ + protected float mBarWidth = 1f; public BarBuffer(int size, float groupspace, int dataSetCount, boolean containsStacks) { super(size); @@ -25,8 +23,8 @@ public BarBuffer(int size, float groupspace, int dataSetCount, boolean containsS this.mContainsStacks = containsStacks; } - public void setInterval(float interval) { - this.mInterval = interval; + public void setBarWidth(float barWidth) { + this.mBarWidth = barWidth; } public void setBarSpace(float barspace) { @@ -53,34 +51,23 @@ protected void addBar(float left, float top, float right, float bottom) { public void feed(IBarDataSet data) { float size = data.getEntryCount() * phaseX; - - float barWidth = mInterval / mDataSetCount; - - float groupSpaceWidth = mDataSetCount <= 1 ? 0 : barWidth * mGroupSpace; - float newInterval = (mInterval - groupSpaceWidth); - float newBarWidth = newInterval / mDataSetCount; - - float barSpaceWidth = newBarWidth * mBarSpace; - float barSpaceWidthHalf = barSpaceWidth / 2f; - - float groupSpaceWidthHalf = groupSpaceWidth / 2f; - float dataSetSpace = mDataSetCount <= 1 ? 0 : (newInterval / mDataSetCount) * mDataSetIndex; - + float barWidthHalf = mBarWidth / 2f; for (int i = 0; i < size; i++) { BarEntry e = data.getEntryForIndex(i); - // calculate the xPx-position, depending on interval - float x = mInterval * i + dataSetSpace; + if(e == null) + continue; + float x = e.getX(); float y = e.getY(); float[] vals = e.getYVals(); if (!mContainsStacks || vals == null) { - float left = x + groupSpaceWidthHalf + barSpaceWidthHalf; - float right = left + newBarWidth - barSpaceWidth; + float left = x - barWidthHalf; + float right = x + barWidthHalf; float bottom, top; if (mInverted) { @@ -120,8 +107,8 @@ public void feed(IBarDataSet data) { negY += Math.abs(value); } - float left = x + groupSpaceWidthHalf + barSpaceWidthHalf; - float right = left + newBarWidth - barSpaceWidth; + float left = x - barWidthHalf; + float right = x + barWidthHalf; float bottom, top; if (mInverted) { 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 3e435538d9..a5f6d77de6 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 @@ -82,36 +82,36 @@ protected void init() { // mViewPortHandler); } - @Override - protected void calcMinMax() { - super.calcMinMax(); - - if (getBarData() != null || getCandleData() != null || getBubbleData() != null) { - mXAxis.mAxisMinimum = -0.5f; - mXAxis.mAxisMaximum = mData.getXVals().size() - 0.5f; - - if (getBubbleData() != null) { - - for (IBubbleDataSet set : getBubbleData().getDataSets()) { - - final float xmin = set.getXMin(); - final float xmax = set.getXMax(); - - if (xmin < mXAxis.mAxisMinimum) - mXAxis.mAxisMinimum = xmin; - - if (xmax > mXAxis.mAxisMaximum) - mXAxis.mAxisMaximum = xmax; - } - } - } - - mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); - - if (mXAxis.mAxisRange == 0.f && getLineData() != null && getLineData().getYValCount() > 0) { - mXAxis.mAxisRange = 1.f; - } - } +// @Override +// protected void calcMinMax() { +// super.calcMinMax(); +// +// if (getBarData() != null || getCandleData() != null || getBubbleData() != null) { +// mXAxis.mAxisMinimum = -0.5f; +// mXAxis.mAxisMaximum = mData.getXVals().size() - 0.5f; +// +// if (getBubbleData() != null) { +// +// for (IBubbleDataSet set : getBubbleData().getDataSets()) { +// +// final float xmin = set.getXMin(); +// final float xmax = set.getXMax(); +// +// if (xmin < mXAxis.mAxisMinimum) +// mXAxis.mAxisMinimum = xmin; +// +// if (xmax > mXAxis.mAxisMaximum) +// mXAxis.mAxisMaximum = xmax; +// } +// } +// } +// +// mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); +// +// if (mXAxis.mAxisRange == 0.f && getLineData() != null && getLineData().getYValCount() > 0) { +// mXAxis.mAxisRange = 1.f; +// } +// } @Override public void setData(CombinedData data) { 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 bc87fcc24d..df43f4c073 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 @@ -9,13 +9,15 @@ /** * Data object that represents all data for the BarChart. - * + * * @author Philipp Jahoda */ public class BarData extends BarLineScatterCandleBubbleData { - /** the space that is left between groups of bars */ - private float mGroupSpace = 0.8f; + /** + * the width of the bars on the x-axis, in values (not pixels) + */ + private float mBarWidth = 1f; // /** // * The maximum space (in pixels on the screen) a single bar can consume. @@ -64,38 +66,103 @@ private static List toList(IBarDataSet dataSet) { * Returns the space that is left out between groups of bars. Always returns * 0 if the BarData object only contains one DataSet (because for one * DataSet, there is no group-space needed). - * + * * @return */ public float getGroupSpace() { - - if (mDataSets.size() <= 1) - return 0f; - else - return mGroupSpace; + return 0f; } /** - * Sets the space between groups of bars of different datasets in percent of - * the total width of one bar. 100 = space is exactly one bar width, - * default: 80 - * - * @param percent + * Sets the width each bar should have on the x-axis (in values, not pixels). + * Default 1f + * + * @param mBarWidth */ - public void setGroupSpace(float percent) { - mGroupSpace = percent / 100f; + public void setBarWidth(float mBarWidth) { + this.mBarWidth = mBarWidth; + } + + public float getBarWidth() { + return mBarWidth; } + // /** +// * Returns true if this BarData object contains grouped DataSets (more than +// * 1 DataSet). +// * +// * @return +// */ +// public boolean isGrouped() { +// return mDataSets.size() > 1 ? true : false; +// } + /** - * Returns true if this BarData object contains grouped DataSets (more than - * 1 DataSet). - * - * @return + * Groups all BarDataSet objects this data object holds together. Leaves space as specified by the parameters. + * + * @param fromX + * @param groupSpace the space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f + * @param barSpace the space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f */ - public boolean isGrouped() { - return mDataSets.size() > 1 ? true : false; + public void groupBars(float fromX, float groupSpace, float barSpace) { + + int setCount = mDataSets.size(); + if (setCount <= 1) { + throw new RuntimeException("BarData needs to hold at least 2 BarDataSets to allow grouping."); + } + + IBarDataSet max = getMaxEntryCountSet(); + int maxEntryCount = max.getEntryCount(); + + float groupSpaceWidthHalf = groupSpace / 2f; + float barSpaceHalf = barSpace / 2f; + float barWidthHalf = mBarWidth / 2f; + + for (int i = 0; i < maxEntryCount; i++) { + + fromX += groupSpaceWidthHalf; + + for (IBarDataSet set : mDataSets) { + + fromX += barSpaceHalf; + fromX += barWidthHalf; + + if (i < set.getEntryCount()) { + + BarEntry entry = set.getEntryForIndex(i); + + if (entry != null) { + entry.setX(fromX); + } + } + + fromX += barWidthHalf; + fromX += barSpaceHalf; + } + + fromX += groupSpaceWidthHalf; + } + + notifyDataChanged(); + } + + public float getIntervalWidth(float groupSpace, float barSpace) { + return mDataSets.size() * (mBarWidth + barSpace) + groupSpace; } - + + protected IBarDataSet getMaxEntryCountSet() { + + IBarDataSet max = mDataSets.get(0); + + for (IBarDataSet set : mDataSets) { + + if (set.getEntryCount() > max.getEntryCount()) + max = set; + } + + return max; + } + // // /** // * Sets the maximum width (in density pixels) a single bar in the barchart 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 cacf466a80..bc10e84ccb 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 @@ -200,12 +200,13 @@ public float getBarSpace() { } /** - * sets the space between the bars in percent (0-100) of the total bar width + * Sets the space between the bars in values (not pixels). + * Default 0.15f * - * @param percent + * @param barSpace */ - public void setBarSpacePercent(float percent) { - mBarSpace = percent / 100f; + public void setBarSpace(float barSpace) { + mBarSpace = barSpace; } /** 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 33f1c6d246..5eefb72b0f 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 @@ -140,7 +140,7 @@ public boolean equalTo(Entry e) { */ @Override public String toString() { - return "Entry, xIndex: " + x + " val (sum): " + getY(); + return "Entry, x: " + x + " y (sum): " + getY(); } @Override 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 ad96567eba..6116f7d43e 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 @@ -62,13 +62,13 @@ public Highlight getHighlight(float x, float y) { -1); } - @Override - protected float getXForTouch(float x) { - - if (!mChart.getBarData().isGrouped()) { - return super.getXForTouch(x); - } else { - return getBase(x); +// @Override +// protected float getXForTouch(float x) { +// +// if (!mChart.getBarData().isGrouped()) { +// return super.getXForTouch(x); +// } else { +// return getBase(x); // // float baseNoSpace = getBase(x); // @@ -83,8 +83,8 @@ protected float getXForTouch(float x) { // xIndex = valCount - 1; // // return xIndex; - } - } +// } +// } @Override protected SelectionDetail getSelectionDetail(float xVal, float x, float y, int dataSetIndex) { 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 56865a8776..fae91f0d0a 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 @@ -58,36 +58,36 @@ public Highlight getHighlight(float x, float y) { -1); } - @Override - protected float getXForTouch(float x) { - - if (!mChart.getBarData().isGrouped()) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[1] = x; - - // take any transformer to determine the xPx-axis yValue - mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); - - return (int) Math.round(pts[1]); - } else { - - float baseNoSpace = getBase(x); - - int setCount = mChart.getBarData().getDataSetCount(); - int xIndex = (int) baseNoSpace / setCount; - - int valCount = mChart.getData().getXValCount(); - - if (xIndex < 0) - xIndex = 0; - else if (xIndex >= valCount) - xIndex = valCount - 1; - - return xIndex; - } - } +// @Override +// protected float getXForTouch(float x) { +// +// if (!mChart.getBarData().isGrouped()) { +// +// // create an array of the touch-point +// float[] pts = new float[2]; +// pts[1] = x; +// +// // take any transformer to determine the xPx-axis yValue +// mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); +// +// return (int) Math.round(pts[1]); +// } else { +// +// float baseNoSpace = getBase(x); +// +// int setCount = mChart.getBarData().getDataSetCount(); +// int xIndex = (int) baseNoSpace / setCount; +// +// int valCount = mChart.getData().getXValCount(); +// +// if (xIndex < 0) +// xIndex = 0; +// else if (xIndex >= valCount) +// xIndex = valCount - 1; +// +// return xIndex; +// } +// } /** * Returns the base yPx-yValue to the corresponding xPx-touch yValue in pixels. 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 740863325c..52bd45907c 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 @@ -99,7 +99,8 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { buffer.setBarSpace(dataSet.getBarSpace()); buffer.setDataSet(index); buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); - buffer.setInterval(mChart.getXRange() / dataSet.getEntryCount()); + buffer.setBarWidth(mChart.getBarData().getBarWidth()); + //buffer.setInterval(mChart.getXRange() / dataSet.getEntryCount()); buffer.feed(dataSet); @@ -194,6 +195,18 @@ protected void prepareBarHighlight(float y1, float y2, float interval, int entry trans.rectValueToPixel(mBarRect, mAnimator.getPhaseY()); } + protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHalf, Transformer trans) { + + float left = x - barWidthHalf; + float right = x + barWidthHalf; + float top = y1; + float bottom = y2; + + mBarRect.set(left, top, right, bottom); + + trans.rectValueToPixel(mBarRect, mAnimator.getPhaseY()); + } + @Override public void drawValues(Canvas c) { @@ -377,13 +390,10 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { BarEntry e = set.getEntryForXPos(x); int entryIndex = set.getEntryIndex(e); - if (e == null || e.getX() != x) + if (e == null) continue; - float interval = mChart.getXRange() / set.getEntryCount(); - float groupSpace = barData.getGroupSpace(); boolean isStack = high.getStackIndex() < 0 ? false : true; - float barSpace = set.getBarSpace(); final float y1; final float y2; @@ -396,7 +406,9 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { y2 = 0.f; } - prepareBarHighlight(y1, y2, interval, entryIndex, dataSetIndex, setCount, barSpace, groupSpace, trans); + prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans); + + //prepareBarHighlight(y1, y2, interval, entryIndex, dataSetIndex, setCount, barSpace, groupSpace, trans); c.drawRect(mBarRect, mHighlightPaint); From e2ccaf353bc7a5197ace52e33dd537f538138a71 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 31 May 2016 15:10:39 +0200 Subject: [PATCH 137/606] Remove barspace from BarDataSet --- .../mpchartexample/BarChartActivitySinus.java | 2 +- .../BarChartPositiveNegative.java | 2 +- .../ListViewBarChartActivity.java | 4 +-- .../ListViewMultiChartActivity.java | 2 +- .../StackedBarActivityNegative.java | 2 +- .../mikephil/charting/buffer/BarBuffer.java | 5 ---- .../charting/buffer/HorizontalBarBuffer.java | 21 ++++++------- .../mikephil/charting/charts/BarChart.java | 8 ++--- .../charting/charts/HorizontalBarChart.java | 7 ++--- .../mikephil/charting/data/BarDataSet.java | 30 ------------------- .../realm/implementation/RealmBarDataSet.java | 28 ----------------- .../interfaces/datasets/IBarDataSet.java | 7 ----- .../charting/renderer/BarChartRenderer.java | 1 - .../renderer/HorizontalBarChartRenderer.java | 1 - 14 files changed, 21 insertions(+), 99 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index 133f2ac437..2710321aea 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -229,7 +229,6 @@ private void setData(int count) { mChart.notifyDataSetChanged(); } else { set = new BarDataSet(entries, "Sinus Function"); - set.setBarSpace(40f); set.setColor(Color.rgb(240, 120, 124)); } @@ -237,6 +236,7 @@ private void setData(int count) { data.setValueTextSize(10f); data.setValueTypeface(mTf); data.setDrawValues(false); + data.setBarWidth(0.8f); mChart.setData(data); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index a7730e3250..db8e954550 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -119,7 +119,6 @@ private void setData(List dataList) { mChart.notifyDataSetChanged(); } else { set = new BarDataSet(values, "Values"); - set.setBarSpace(40f); set.setColors(colors); set.setValueTextColors(colors); @@ -127,6 +126,7 @@ private void setData(List dataList) { data.setValueTextSize(13f); data.setValueTypeface(mTf); data.setValueFormatter(new ValueFormatter()); + data.setBarWidth(0.8f); mChart.setData(data); mChart.invalidate(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index a99bae5937..7cbd00a414 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java @@ -135,8 +135,7 @@ private BarData generateData(int cnt) { entries.add(new BarEntry((int) (Math.random() * 70) + 30, i)); } - BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); - d.setBarSpace(20f); + BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); d.setColors(ColorTemplate.VORDIPLOM_COLORS); d.setBarShadowColor(Color.rgb(203, 203, 203)); @@ -144,6 +143,7 @@ private BarData generateData(int cnt) { sets.add(d); BarData cd = new BarData(getMonths(), sets); + cd.setBarWidth(0.9f); return cd; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java index 6f50439a68..34389e8260 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java @@ -143,11 +143,11 @@ private BarData generateDataBar(int cnt) { } BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); - d.setBarSpace(20f); d.setColors(ColorTemplate.VORDIPLOM_COLORS); d.setHighLightAlpha(255); BarData cd = new BarData(getMonths(), d); + cd.setBarWidth(0.9f); return cd; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 318e9d899d..a8834f56ca 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -96,7 +96,6 @@ protected void onCreate(Bundle savedInstanceState) { set.setValueFormatter(new CustomFormatter()); set.setValueTextSize(7f); set.setAxisDependency(YAxis.AxisDependency.RIGHT); - set.setBarSpace(40f); set.setColors(new int[] {Color.rgb(67,67,72), Color.rgb(124,181,236)}); set.setStackLabels(new String[]{ "Men", "Women" @@ -110,6 +109,7 @@ protected void onCreate(Bundle savedInstanceState) { } BarData data = new BarData(xVals, set); + data.setBarWidth(0.8f); mChart.setData(data); mChart.invalidate(); } 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 615fe453dc..fdcae06af7 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 @@ -6,7 +6,6 @@ public class BarBuffer extends AbstractBuffer { - protected float mBarSpace = 0f; protected float mGroupSpace = 0f; protected int mDataSetIndex = 0; protected int mDataSetCount = 1; @@ -27,10 +26,6 @@ public void setBarWidth(float barWidth) { this.mBarWidth = barWidth; } - public void setBarSpace(float barspace) { - this.mBarSpace = barspace; - } - public void setDataSet(int index) { this.mDataSetIndex = index; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java index 5a7044522e..86cc1a7228 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java @@ -14,26 +14,23 @@ public HorizontalBarBuffer(int size, float groupspace, int dataSetCount, boolean public void feed(IBarDataSet data) { float size = data.getEntryCount() * phaseX; - - int dataSetOffset = (mDataSetCount - 1); - float barSpaceHalf = mBarSpace / 2f; - float groupSpaceHalf = mGroupSpace / 2f; - float barWidth = 0.5f; + float barWidthHalf = mBarWidth / 2f; for (int i = 0; i < size; i++) { BarEntry e = data.getEntryForIndex(i); - // calculate the xPx-position, depending on datasetcount - float x = e.getX() + e.getX() * dataSetOffset + mDataSetIndex - + mGroupSpace * e.getX() + groupSpaceHalf; + if(e == null) + continue; + + float x = e.getX(); float y = e.getY(); float[] vals = e.getYVals(); if (!mContainsStacks || vals == null) { - float bottom = x - barWidth + barSpaceHalf; - float top = x + barWidth - barSpaceHalf; + float top = x - barWidthHalf; + float bottom = x + barWidthHalf; float left, right; if (mInverted) { left = y >= 0 ? y : 0; @@ -72,8 +69,8 @@ public void feed(IBarDataSet data) { negY += Math.abs(value); } - float bottom = x - barWidth + barSpaceHalf; - float top = x + barWidth - barSpaceHalf; + float top = x - barWidthHalf; + float bottom = x + barWidthHalf; float left, right; if (mInverted) { left = y >= yStart ? y : yStart; 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 f5958d9018..2c387c8488 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 @@ -106,15 +106,13 @@ public RectF getBarBounds(BarEntry e) { if (set == null) return null; - float barspace = set.getBarSpace(); float y = e.getY(); float x = e.getX(); - float barWidth = 0.5f; + float barWidth = mData.getBarWidth(); - float spaceHalf = barspace / 2f; - float left = x - barWidth + spaceHalf; - float right = x + barWidth - spaceHalf; + float left = x - barWidth / 2f; + float right = x + barWidth / 2f; float top = y >= 0 ? y : 0; float bottom = y <= 0 ? y : 0; 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 b183e28c69..18597df658 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 @@ -152,14 +152,13 @@ public RectF getBarBounds(BarEntry e) { if (set == null) return null; - float barspace = set.getBarSpace(); float y = e.getY(); float x = e.getX(); - float spaceHalf = barspace / 2f; + float barWidth = mData.getBarWidth(); - float top = x - 0.5f + spaceHalf; - float bottom = x + 0.5f - spaceHalf; + float top = x - barWidth / 2f; + float bottom = x + barWidth / 2f; float left = y >= 0 ? y : 0; float right = y <= 0 ? y : 0; 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 bc10e84ccb..ad343eb110 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 @@ -10,11 +10,6 @@ public class BarDataSet extends BarLineScatterCandleBubbleDataSet implements IBarDataSet { - /** - * space indicator between the bars 0.1f == 10 % - */ - private float mBarSpace = 0.15f; - /** * the maximum number of bars that are stacked upon each other, this yValue * is calculated from the Entries that are added to the DataSet @@ -68,7 +63,6 @@ public DataSet copy() { BarDataSet copied = new BarDataSet(yVals, getLabel()); copied.mColors = mColors; copied.mStackSize = mStackSize; - copied.mBarSpace = mBarSpace; copied.mBarShadowColor = mBarShadowColor; copied.mStackLabels = mStackLabels; copied.mHighLightColor = mHighLightColor; @@ -185,30 +179,6 @@ public int getEntryCountStacks() { return mEntryCountStacks; } - /** - * returns the space between bars in percent of the whole width of one yValue - * - * @return - */ - public float getBarSpacePercent() { - return mBarSpace * 100f; - } - - @Override - public float getBarSpace() { - return mBarSpace; - } - - /** - * Sets the space between the bars in values (not pixels). - * Default 0.15f - * - * @param barSpace - */ - public void setBarSpace(float barSpace) { - mBarSpace = barSpace; - } - /** * Sets the color used for drawing the bar-shadows. The bar shadows is a * surface behind the bar that indicates the maximum yValue. Don't for get to diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java index 9e964b1eb6..fae88776fa 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java @@ -20,11 +20,6 @@ public class RealmBarDataSet extends RealmBarLineScatterC private String mStackValueFieldName; - /** - * space indicator between the bars 0.1f == 10 % - */ - private float mBarSpace = 0.15f; - /** * the maximum number of bars that are stacked upon each other, this yValue * is calculated from the Entries that are added to the DataSet @@ -184,29 +179,6 @@ public boolean isStacked() { return mStackSize > 1 ? true : false; } - /** - * returns the space between bars in percent of the whole width of one yValue - * - * @return - */ - public float getBarSpacePercent() { - return mBarSpace * 100f; - } - - @Override - public float getBarSpace() { - return mBarSpace; - } - - /** - * sets the space between the bars in percent (0-100) of the total bar width - * - * @param percent - */ - public void setBarSpacePercent(float percent) { - mBarSpace = percent / 100f; - } - /** * Sets the color used for drawing the bar-shadows. The bar shadows is a * surface behind the bar that indicates the maximum yValue. Don't for get to 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 3c260245bc..87e651b16f 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 @@ -7,13 +7,6 @@ */ public interface IBarDataSet extends IBarLineScatterCandleBubbleDataSet { - /** - * Returns the space between bars as the actual yValue (0 - 1.0f) - * - * @return - */ - float getBarSpace(); - /** * Returns true if this DataSet is stacked (stacksize > 1) or not. * 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 52bd45907c..0cc30c726b 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 @@ -96,7 +96,6 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { // initialize the buffer BarBuffer buffer = mBarBuffers[index]; buffer.setPhases(phaseX, phaseY); - buffer.setBarSpace(dataSet.getBarSpace()); buffer.setDataSet(index); buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); buffer.setBarWidth(mChart.getBarData().getBarWidth()); 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 9b02178179..347d3487fe 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 @@ -63,7 +63,6 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { // initialize the buffer BarBuffer buffer = mBarBuffers[index]; buffer.setPhases(phaseX, phaseY); - buffer.setBarSpace(dataSet.getBarSpace()); buffer.setDataSet(index); buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); From 3d6f0b982b209692568dd47daf4fc93fd9d8d24b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 31 May 2016 15:18:09 +0200 Subject: [PATCH 138/606] Fix in horizontal buffer --- .../mikephil/charting/buffer/HorizontalBarBuffer.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java index 86cc1a7228..c97c909508 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java @@ -29,8 +29,8 @@ public void feed(IBarDataSet data) { if (!mContainsStacks || vals == null) { - float top = x - barWidthHalf; - float bottom = x + barWidthHalf; + float bottom = x - barWidthHalf; + float top = x + barWidthHalf; float left, right; if (mInverted) { left = y >= 0 ? y : 0; @@ -69,8 +69,8 @@ public void feed(IBarDataSet data) { negY += Math.abs(value); } - float top = x - barWidthHalf; - float bottom = x + barWidthHalf; + float bottom = x - barWidthHalf; + float top = x + barWidthHalf; float left, right; if (mInverted) { left = y >= yStart ? y : yStart; From d33b27cde0a9f0998d005be492fa1098755a51cb Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 31 May 2016 16:31:26 +0200 Subject: [PATCH 139/606] Work on bar highlighting --- .../charting/charts/BarLineChartBase.java | 41 --------- .../charting/highlight/BarHighlighter.java | 84 ++++++------------- .../charting/highlight/ChartHighlighter.java | 21 ++--- .../highlight/CombinedHighlighter.java | 2 +- .../highlight/HorizontalBarHighlighter.java | 5 +- .../listener/BarLineChartTouchListener.java | 1 - .../charting/renderer/BarChartRenderer.java | 42 +++++----- 7 files changed, 58 insertions(+), 138 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 90e4f9ffdf..fb70364186 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 @@ -563,47 +563,6 @@ protected float[] getMarkerPosition(Entry e, Highlight highlight) { float xPos = e.getX(); float yPos = e.getY(); - if (this instanceof BarChart) { - - BarData bd = (BarData) mData; - float space = bd.getGroupSpace(); - int setCount = mData.getDataSetCount(); - float i = e.getX(); - - if (this instanceof HorizontalBarChart) { - - // calculate the xPx-position, depending on datasetcount - float y = i + i * (setCount - 1) + dataSetIndex + space * i + space / 2f; - - yPos = y; - - BarEntry entry = (BarEntry) e; - if (entry.getYVals() != null) { - xPos = highlight.getRange().to; - } else { - xPos = e.getY(); - } - - xPos *= mAnimator.getPhaseY(); - } else { - - float x = i + i * (setCount - 1) + dataSetIndex + space * i + space / 2f; - - xPos = x; - - BarEntry entry = (BarEntry) e; - if (entry.getYVals() != null) { - yPos = highlight.getRange().to; - } else { - yPos = e.getY(); - } - - yPos *= mAnimator.getPhaseY(); - } - } else { - yPos *= mAnimator.getPhaseY(); - } - // position of the marker depends on selected yValue index and yValue float[] pts = new float[]{ xPos, yPos 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 6116f7d43e..693fc63724 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 @@ -24,34 +24,19 @@ public Highlight getHighlight(float x, float y) { BarData barData = mChart.getBarData(); - final float xVal = getXForTouch(x); - final float baseNoSpace = getBase(x); - final int setCount = barData.getDataSetCount(); - int dataSetIndex = ((int) baseNoSpace) % setCount; - - if (dataSetIndex < 0) { - dataSetIndex = 0; - } else if (dataSetIndex >= setCount) { - dataSetIndex = setCount - 1; - } + PointD pos = getValsForTouch(x, y); - SelectionDetail selectionDetail = getSelectionDetail(xVal, x, y, dataSetIndex); + SelectionDetail selectionDetail = getSelectionDetail((float) pos.x, x, y); if (selectionDetail == null) return null; - IBarDataSet set = barData.getDataSetByIndex(dataSetIndex); + IBarDataSet set = barData.getDataSetByIndex(selectionDetail.dataSetIndex); if (set.isStacked()) { - float[] pts = new float[2]; - pts[1] = y; - - // take any transformer to determine the xPx-axis yValue - mChart.getTransformer(set.getAxisDependency()).pixelsToValue(pts); - return getStackedHighlight(selectionDetail, set, - xVal, - pts[1]); + (float) pos.x, + (float) pos.y); } return new Highlight( @@ -87,24 +72,33 @@ public Highlight getHighlight(float x, float y) { // } @Override - protected SelectionDetail getSelectionDetail(float xVal, float x, float y, int dataSetIndex) { - - dataSetIndex = Math.max(dataSetIndex, 0); + protected SelectionDetail getSelectionDetail(float xVal, float x, float y) { BarData barData = mChart.getBarData(); - IDataSet dataSet = barData.getDataSetCount() > dataSetIndex - ? barData.getDataSetByIndex(dataSetIndex) - : null; - if (dataSet == null) - return null; + int closestDataSetIndex = 0; + float closestDistance = Float.MAX_VALUE; + Entry closestEntry = null; + + for(int i = 0; i < barData.getDataSets().size(); i++) { - final Entry entry = dataSet.getEntryForXPos(xVal); + IBarDataSet dataSet = barData.getDataSetByIndex(i); - return new SelectionDetail(entry.getX(), - entry.getY(), - dataSetIndex, - dataSet); + final Entry entry = dataSet.getEntryForXPos(xVal); + + final float distance = Math.abs(xVal - entry.getX()); + + if(distance < closestDistance) { + closestDataSetIndex = i; + closestDistance = distance; + closestEntry = entry; + } + } + + return new SelectionDetail(x, y, closestEntry.getX(), + closestEntry.getY(), + closestDataSetIndex, + barData.getDataSetByIndex(closestDataSetIndex)); } /** @@ -181,30 +175,6 @@ protected int getClosestStackIndex(Range[] ranges, float value) { return (value > ranges[length].to) ? length : 0; } - /** - * Returns the base xPx-yValue to the given xPx-touch value in pixels. - * - * @param x - * @return - */ - protected float getBase(float x) { - - // take any transformer to determine the x-axis value - PointD val = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(x, 0f); - int setCount = mChart.getBarData().getDataSetCount(); - return (float) val.x; -// -// -// -// // calculate how often the group-space appears -// int steps = (int) ((float) xVal / ((float) setCount + mChart.getBarData().getGroupSpace())); -// -// float groupSpaceSum = mChart.getBarData().getGroupSpace() * (float) steps; -// -// float baseNoSpace = (float) xVal - groupSpaceSum; -// return baseNoSpace; - } - /** * Splits up the stack-values of the given bar-entry into Range objects. * 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 4fc57febdb..c4896842de 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 @@ -35,9 +35,9 @@ public ChartHighlighter(T chart) { */ public Highlight getHighlight(float x, float y) { - float xVal = getXForTouch(x); + float xVal = (float) getValsForTouch(x, y).x; - SelectionDetail selectionDetail = getSelectionDetail(xVal, x, y, -1); + SelectionDetail selectionDetail = getSelectionDetail(xVal, x, y); if (selectionDetail == null) return null; @@ -53,11 +53,11 @@ public Highlight getHighlight(float x, float y) { * @param x * @return */ - protected float getXForTouch(float x) { + protected PointD getValsForTouch(float x, float y) { // take any transformer to determine the xPx-axis yValue - PointD pos = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(x, 0f); - return (float) pos.x; + PointD pos = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(x, y); + return pos; } /** @@ -65,12 +65,11 @@ protected float getXForTouch(float x) { * * @param xVal * @param y - * @param dataSetIndex * @return */ - protected SelectionDetail getSelectionDetail(float xVal, float x, float y, int dataSetIndex) { + protected SelectionDetail getSelectionDetail(float xVal, float x, float y) { - List valsAtIndex = getSelectionDetailsAtIndex(xVal, dataSetIndex); + List valsAtIndex = getSelectionDetailsAtIndex(xVal); float leftdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.LEFT); float rightdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.RIGHT); @@ -86,10 +85,9 @@ protected SelectionDetail getSelectionDetail(float xVal, float x, float y, int d * Returns a list of SelectionDetail object corresponding to the given xIndex. * * @param xVal - * @param dataSetIndex dataSet index to look at. -1 if unspecified. * @return */ - protected List getSelectionDetailsAtIndex(float xVal, int dataSetIndex) { + protected List getSelectionDetailsAtIndex(float xVal) { List vals = new ArrayList(); @@ -97,9 +95,6 @@ protected List getSelectionDetailsAtIndex(float xVal, int dataS for (int i = 0, dataSetCount = mChart.getData().getDataSetCount(); i < dataSetCount; i++) { - if (dataSetIndex > -1 && dataSetIndex != i) - continue; - IDataSet dataSet = mChart.getData().getDataSetByIndex(i); // dont include datasets that cannot be highlighted 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 98e24859d4..8422b1e6a4 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 @@ -25,7 +25,7 @@ public CombinedHighlighter(BarLineScatterCandleBubbleDataProvider chart) { * @return */ @Override - protected List getSelectionDetailsAtIndex(float xVal, int dataSetIndex) { + protected List getSelectionDetailsAtIndex(float xVal) { List vals = new ArrayList(); float[] pts = new float[2]; 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 fae91f0d0a..753eb5018e 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 @@ -20,7 +20,7 @@ public Highlight getHighlight(float x, float y) { BarData barData = mChart.getBarData(); - final float xVal = getXForTouch(x); + final float xVal = (float) getValsForTouch(x, y).x; final float baseNoSpace = getBase(x); final int setCount = barData.getDataSetCount(); int dataSetIndex = ((int)baseNoSpace) % setCount; @@ -31,7 +31,7 @@ public Highlight getHighlight(float x, float y) { dataSetIndex = setCount - 1; } - SelectionDetail selectionDetail = getSelectionDetail(xVal, x, y, dataSetIndex); + SelectionDetail selectionDetail = getSelectionDetail(xVal, x, y); if (selectionDetail == null) return null; @@ -95,7 +95,6 @@ public Highlight getHighlight(float x, float y) { * @param y * @return */ - @Override protected float getBase(float y) { // create an array of the touch-point 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 98e60ffb32..7f293090bd 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 @@ -567,7 +567,6 @@ public boolean onSingleTapUp(MotionEvent e) { return false; } - Highlight h = mChart.getHighlightByTouchPoint(e.getX(), e.getY()); performHighlight(h, e); 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 0cc30c726b..f01d3d3ab3 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 @@ -382,34 +382,31 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { float x = high.getX(); - // check outofbounds - if (x >= 0 - && x < (mChart.getXChartMax() * mAnimator.getPhaseX()) / setCount) { + BarEntry e = set.getEntryForXPos(x); + int entryIndex = set.getEntryIndex(e); - BarEntry e = set.getEntryForXPos(x); - int entryIndex = set.getEntryIndex(e); - - if (e == null) - continue; + if (e == null) + continue; - boolean isStack = high.getStackIndex() < 0 ? false : true; + boolean isStack = high.getStackIndex() < 0 ? false : true; - final float y1; - final float y2; + final float y1; + final float y2; - if (isStack) { - y1 = high.getRange().from; - y2 = high.getRange().to; - } else { - y1 = e.getY(); - y2 = 0.f; - } + if (isStack) { + y1 = high.getRange().from; + y2 = high.getRange().to; + } else { + y1 = e.getY(); + y2 = 0.f; + } - prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans); + prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans); - //prepareBarHighlight(y1, y2, interval, entryIndex, dataSetIndex, setCount, barSpace, groupSpace, trans); + //prepareBarHighlight(y1, y2, interval, entryIndex, dataSetIndex, setCount, barSpace, groupSpace, + // trans); - c.drawRect(mBarRect, mHighlightPaint); + c.drawRect(mBarRect, mHighlightPaint); // if (mChart.isDrawHighlightArrowEnabled()) { // @@ -436,9 +433,10 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { // trans.pathValueToPixel(arrow); // c.drawPath(arrow, mHighlightPaint); // } - } + } } + } public float[] getTransformedValues(Transformer trans, IBarDataSet data, From 6919a97f177df4a15db69b818be9a509f35d0282 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 31 May 2016 17:49:07 +0200 Subject: [PATCH 140/606] Remove x-values from data objects --- .../charting/charts/BarLineChartBase.java | 44 ++-- .../mikephil/charting/charts/BubbleChart.java | 52 ++--- .../mikephil/charting/charts/Chart.java | 36 +--- .../charting/charts/HorizontalBarChart.java | 18 +- .../charting/charts/PieRadarChartBase.java | 2 +- .../mikephil/charting/charts/RadarChart.java | 8 +- .../mikephil/charting/data/BarData.java | 43 +--- .../mikephil/charting/data/BarDataSet.java | 2 +- .../data/BarLineScatterCandleBubbleData.java | 16 +- .../mikephil/charting/data/BubbleData.java | 29 +-- .../mikephil/charting/data/CandleData.java | 32 +-- .../mikephil/charting/data/ChartData.java | 194 ++++-------------- .../mikephil/charting/data/CombinedData.java | 8 - .../github/mikephil/charting/data/Entry.java | 4 + .../mikephil/charting/data/LineData.java | 30 +-- .../mikephil/charting/data/PieData.java | 22 +- .../mikephil/charting/data/PieDataSet.java | 8 +- .../mikephil/charting/data/PieEntry.java | 52 +++++ .../mikephil/charting/data/RadarData.java | 30 +-- .../mikephil/charting/data/ScatterData.java | 30 +-- .../realm/implementation/RealmBarData.java | 3 +- .../realm/implementation/RealmBubbleData.java | 3 +- .../realm/implementation/RealmCandleData.java | 3 +- .../realm/implementation/RealmLineData.java | 3 +- .../realm/implementation/RealmPieData.java | 3 +- .../realm/implementation/RealmPieDataSet.java | 3 +- .../realm/implementation/RealmRadarData.java | 3 +- .../implementation/RealmScatterData.java | 3 +- .../charting/highlight/Highlight.java | 4 +- .../dataprovider/ChartInterface.java | 2 - .../interfaces/datasets/IPieDataSet.java | 3 +- .../charting/renderer/LegendRenderer.java | 5 +- .../charting/renderer/PieChartRenderer.java | 19 +- .../charting/renderer/RadarChartRenderer.java | 4 +- .../renderer/YAxisRendererRadarChart.java | 2 +- 35 files changed, 228 insertions(+), 495 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java 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 fb70364186..a419a1274e 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 @@ -532,28 +532,28 @@ public void calculateOffsets() { */ protected void calcModulus() { - if (mXAxis == null || !mXAxis.isEnabled()) - return; - - if (!mXAxis.isAxisModulusCustom()) { - - float[] values = new float[9]; - mViewPortHandler.getMatrixTouch().getValues(values); - - mXAxis.mAxisLabelModulus = (int) Math - .ceil((mData.getXValCount() * mXAxis.mLabelRotatedWidth) - / (mViewPortHandler.contentWidth() * values[Matrix.MSCALE_X])); - - } - - if (mLogEnabled) - Log.i(LOG_TAG, "X-Axis modulus: " + mXAxis.mAxisLabelModulus + - ", xPx-axis label width: " + mXAxis.mLabelWidth + - ", xPx-axis label rotated width: " + mXAxis.mLabelRotatedWidth + - ", content width: " + mViewPortHandler.contentWidth()); - - if (mXAxis.mAxisLabelModulus < 1) - mXAxis.mAxisLabelModulus = 1; +// if (mXAxis == null || !mXAxis.isEnabled()) +// return; +// +// if (!mXAxis.isAxisModulusCustom()) { +// +// float[] values = new float[9]; +// mViewPortHandler.getMatrixTouch().getValues(values); +// +// mXAxis.mAxisLabelModulus = (int) Math +// .ceil((mData.getXValCount() * mXAxis.mLabelRotatedWidth) +// / (mViewPortHandler.contentWidth() * values[Matrix.MSCALE_X])); +// +// } +// +// if (mLogEnabled) +// Log.i(LOG_TAG, "X-Axis modulus: " + mXAxis.mAxisLabelModulus + +// ", xPx-axis label width: " + mXAxis.mLabelWidth + +// ", xPx-axis label rotated width: " + mXAxis.mLabelRotatedWidth + +// ", content width: " + mViewPortHandler.contentWidth()); +// +// if (mXAxis.mAxisLabelModulus < 1) +// mXAxis.mAxisLabelModulus = 1; } @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java index af93fdfb18..a0681e652c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java @@ -38,32 +38,32 @@ protected void init() { mRenderer = new BubbleChartRenderer(this, mAnimator, mViewPortHandler); } - @Override - protected void calcMinMax() { - super.calcMinMax(); - - if (mXAxis.mAxisRange == 0 && mData.getYValCount() > 0) - mXAxis.mAxisRange = 1; - - mXAxis.mAxisMinimum = -0.5f; - mXAxis.mAxisMaximum = (float) mData.getXValCount() - 0.5f; - - if (mRenderer != null) { - for (IBubbleDataSet set : mData.getDataSets()) { - - final float xmin = set.getXMin(); - final float xmax = set.getXMax(); - - if (xmin < mXAxis.mAxisMinimum) - mXAxis.mAxisMinimum = xmin; - - if (xmax > mXAxis.mAxisMaximum) - mXAxis.mAxisMaximum = xmax; - } - } - - mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); - } +// @Override +// protected void calcMinMax() { +// super.calcMinMax(); +// +// if (mXAxis.mAxisRange == 0 && mData.getYValCount() > 0) +// mXAxis.mAxisRange = 1; +// +// mXAxis.mAxisMinimum = -0.5f; +// mXAxis.mAxisMaximum = (float) mData.getXValCount() - 0.5f; +// +// if (mRenderer != null) { +// for (IBubbleDataSet set : mData.getDataSets()) { +// +// final float xmin = set.getXMin(); +// final float xmax = set.getXMax(); +// +// if (xmin < mXAxis.mAxisMinimum) +// mXAxis.mAxisMinimum = xmin; +// +// if (xmax > mXAxis.mAxisMaximum) +// mXAxis.mAxisMaximum = xmax; +// } +// } +// +// mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); +// } public BubbleData getBubbleData() { return mData; 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 ae7075b725..006cbd1c33 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 @@ -389,7 +389,7 @@ protected void calculateFormatter(float min, float max) { float reference = 0f; - if (mData == null || mData.getXValCount() < 2) { + if (mData == null || mData.getEntryCount() < 2) { reference = Math.max(Math.abs(min), Math.abs(max)); } else { @@ -561,20 +561,18 @@ public void highlightValue(int xIndex, int dataSetIndex) { } /** - * Highlights the yValue at the given xPx-index in the given DataSet. Provide - * -1 as the xPx-index or dataSetIndex to undo all highlighting. + * Highlights the yValue at the given x position in the given DataSet. Provide + * -1 as the dataSetIndex to undo all highlighting. * - * @param xIndex + * @param x * @param dataSetIndex */ - public void highlightValue(int xIndex, int dataSetIndex, boolean callListener) { - - if (xIndex < 0 || dataSetIndex < 0 || xIndex >= mData.getXValCount() - || dataSetIndex >= mData.getDataSetCount()) { + public void highlightValue(float x, int dataSetIndex, boolean callListener) { + if (dataSetIndex < 0 || dataSetIndex >= mData.getDataSetCount()) { highlightValue(null, callListener); } else { - highlightValue(new Highlight(xIndex, dataSetIndex), callListener); + highlightValue(new Highlight(x, dataSetIndex), callListener); } } @@ -686,7 +684,7 @@ protected void drawMarkers(Canvas canvas) { float deltaX = mXAxis != null ? mXAxis.mAxisRange - : ((mData == null ? 0.f : mData.getXValCount()) - 1.f); + : 1f; if (xVal <= deltaX && xVal <= deltaX * mAnimator.getPhaseX()) { @@ -1023,11 +1021,6 @@ public float getXRange() { return mXAxis.mAxisRange; } - @Override - public int getXValCount() { - return mData.getXValCount(); - } - /** * Returns the total number of (yPx) values the chart holds (across all DataSets). * @@ -1390,19 +1383,6 @@ public void setDrawMarkerViews(boolean enabled) { mDrawMarkerViews = enabled; } - /** - * returns the xPx-yValue at the given index - * - * @param index - * @return - */ - public XAxisValue getXValue(int index) { - if (mData == null || mData.getXValCount() <= index) - return null; - else - return mData.getXVals().get(index); - } - /** * Get all Entry objects at the given index across all DataSets. * INFORMATION: This method does calculations at runtime. Do not over-use in 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 18597df658..d8fd26da4e 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 @@ -133,15 +133,15 @@ protected void prepareValuePxMatrix() { @Override protected void calcModulus() { - float[] values = new float[9]; - mViewPortHandler.getMatrixTouch().getValues(values); - - mXAxis.mAxisLabelModulus = - (int) Math.ceil((mData.getXValCount() * mXAxis.mLabelRotatedHeight) - / (mViewPortHandler.contentHeight() * values[Matrix.MSCALE_Y])); - - if (mXAxis.mAxisLabelModulus < 1) - mXAxis.mAxisLabelModulus = 1; +// float[] values = new float[9]; +// mViewPortHandler.getMatrixTouch().getValues(values); +// +// mXAxis.mAxisLabelModulus = +// (int) Math.ceil((mData.getXValCount() * mXAxis.mLabelRotatedHeight) +// / (mViewPortHandler.contentHeight() * values[Matrix.MSCALE_Y])); +// +// if (mXAxis.mAxisLabelModulus < 1) +// mXAxis.mAxisLabelModulus = 1; } @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 fc00d890be..c3d14c042a 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 @@ -66,7 +66,7 @@ protected void init() { @Override protected void calcMinMax() { - mXAxis.mAxisRange = mData.getXVals().size() - 1; + //mXAxis.mAxisRange = mData.getXVals().size() - 1; } @Override 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 400fb85711..28f2529db6 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 @@ -101,8 +101,8 @@ protected void calcMinMax() { super.calcMinMax(); // calculate / set xPx-axis range - mXAxis.mAxisMaximum = mData.getXVals().size() - 1; - mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); +// mXAxis.mAxisMaximum = mData.getXVals().size() - 1; +// mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); mYAxis.calculate(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); } @@ -190,7 +190,7 @@ public float getFactor() { * @return */ public float getSliceAngle() { - return 360f / (float) mData.getXValCount(); + return 360f / (float) mData.getEntryCount(); } @Override @@ -201,7 +201,7 @@ public int getIndexForAngle(float angle) { float sliceangle = getSliceAngle(); - for (int i = 0; i < mData.getXValCount(); i++) { + for (int i = 0; i < mData.getEntryCount(); i++) { if (sliceangle * (i + 1) - sliceangle / 2f > a) 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 df43f4c073..08187c2061 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 @@ -32,34 +32,8 @@ public BarData(IBarDataSet... dataSets) { super(dataSets); } - public BarData(List xVals) { - super(xVals); - } - - public BarData(XAxisValue[] xVals) { - super(xVals); - } - - public BarData(List xVals, List dataSets) { - super(xVals, dataSets); - } - - public BarData(XAxisValue[] xVals, List dataSets) { - super(xVals, dataSets); - } - - public BarData(List xVals, IBarDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public BarData(XAxisValue[] xVals, IBarDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(IBarDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; + public BarData(List dataSets) { + super(dataSets); } /** @@ -150,19 +124,6 @@ public float getIntervalWidth(float groupSpace, float barSpace) { return mDataSets.size() * (mBarWidth + barSpace) + groupSpace; } - protected IBarDataSet getMaxEntryCountSet() { - - IBarDataSet max = mDataSets.get(0); - - for (IBarDataSet set : mDataSets) { - - if (set.getEntryCount() > max.getEntryCount()) - max = set; - } - - return max; - } - // // /** // * Sets the maximum width (in density pixels) a single bar in the barchart 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 ad343eb110..14738aa4bc 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 @@ -57,7 +57,7 @@ public DataSet copy() { List yVals = new ArrayList(); for (int i = 0; i < mValues.size(); i++) { - yVals.add(((BarEntry) mValues.get(i)).copy()); + yVals.add(mValues.get(i).copy()); } BarDataSet copied = new BarDataSet(yVals, getLabel()); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java index c16c2c72a1..c09eadec9b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java @@ -20,20 +20,8 @@ public BarLineScatterCandleBubbleData() { public BarLineScatterCandleBubbleData(T... sets) { super(sets); } - - public BarLineScatterCandleBubbleData(List xVals) { - super(xVals); - } - - public BarLineScatterCandleBubbleData(XAxisValue[] xVals) { - super(xVals); - } - public BarLineScatterCandleBubbleData(List xVals, List sets) { - super(xVals, sets); - } - - public BarLineScatterCandleBubbleData(XAxisValue[] xVals, List sets) { - super(xVals, sets); + public BarLineScatterCandleBubbleData(List sets) { + super(sets); } } 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 28e317b306..0ce345bc90 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 @@ -12,35 +12,14 @@ public BubbleData() { super(); } - public BubbleData(List xVals) { - super(xVals); + public BubbleData(IBubbleDataSet... dataSets) { + super(dataSets); } - public BubbleData(XAxisValue[] xVals) { - super(xVals); + public BubbleData(List dataSets) { + super(dataSets); } - public BubbleData(List xVals, List dataSets) { - super(xVals, dataSets); - } - - public BubbleData(XAxisValue[] xVals, List dataSets) { - super(xVals, dataSets); - } - - public BubbleData(List xVals, IBubbleDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public BubbleData(XAxisValue[] xVals, IBubbleDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(IBubbleDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; - } /** * Sets the width of the circle that surrounds the bubble when highlighted diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleData.java index 17e79c0748..1e90db2ba5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleData.java @@ -10,34 +10,12 @@ public class CandleData extends BarLineScatterCandleBubbleData { public CandleData() { super(); } - - public CandleData(List xVals) { - super(xVals); - } - - public CandleData(XAxisValue[] xVals) { - super(xVals); - } - - public CandleData(List xVals, List dataSets) { - super(xVals, dataSets); - } - public CandleData(XAxisValue[] xVals, List dataSets) { - super(xVals, dataSets); + public CandleData(List dataSets) { + super(dataSets); } - - public CandleData(List xVals, ICandleDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public CandleData(XAxisValue[] xVals, ICandleDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(ICandleDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; + + public CandleData(ICandleDataSet... dataSets) { + super(dataSets); } } 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 dd4fcee6bf..b3ea3809a3 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 @@ -7,6 +7,7 @@ import com.github.mikephil.charting.components.YAxis.AxisDependency; 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; import java.util.ArrayList; @@ -60,18 +61,12 @@ public abstract class ChartData> { */ private float mXValMaximumLength = 0; - /** - * holds all xPx-values the chart represents - */ - protected List mXVals; - /** * array that holds all DataSets the ChartData object represents */ protected List mDataSets; public ChartData() { - mXVals = new ArrayList(); mDataSets = new ArrayList(); } @@ -80,124 +75,24 @@ public ChartData(T... dataSets) { init(); } - /** - * Constructor for only xPx-values. This constructor can be used for setting - * up an empty chart without data. - * - * @param xVals - */ - public ChartData(List xVals) { - this.mXVals = xVals; - this.mDataSets = new ArrayList(); - init(); - } - - /** - * Constructor for only xPx-values. This constructor can be used for setting - * up an empty chart without data. - * - * @param xVals - */ - public ChartData(XAxisValue[] xVals) { - this.mXVals = arrayToList(xVals); - this.mDataSets = new ArrayList(); - init(); - } - /** * constructor for chart data * - * @param xVals The values describing the xPx-axis. Must be at least as long - * as the highest xIndex in the Entry objects across all - * DataSets. - * @param sets the dataset array - */ - public ChartData(List xVals, List sets) { - this.mXVals = xVals; - this.mDataSets = sets; - - init(); - } - - /** - * constructor that takes string array instead of List string - * - * @param xVals The values describing the xPx-axis. Must be at least as long - * as the highest xIndex in the Entry objects across all - * DataSets. - * @param sets the dataset array + * @param sets the dataset array */ - public ChartData(XAxisValue[] xVals, List sets) { - this.mXVals = arrayToList(xVals); + public ChartData(List sets) { this.mDataSets = sets; - init(); } - /** - * Turns an array of strings into an List of strings. - * - * @param array - * @return - */ - private List arrayToList(XAxisValue[] array) { - return Arrays.asList(array); - } - /** * performs all kinds of initialization calculations, such as min-max and * yValue count and sum */ protected void init() { - checkLegal(); calcYValueCount(); calcMinMax(); - - calcXValMaximumLength(); - } - - /** - * calculates the average length (in characters) across all xPx-yValue strings - */ - private void calcXValMaximumLength() { - - if (mXVals.size() <= 0) { - mXValMaximumLength = 1; - return; - } - - int max = 1; - - for (int i = 0; i < mXVals.size(); i++) { - - int length = mXVals.get(i).getLabel().length(); - - if (length > max) - max = length; - } - - mXValMaximumLength = max; - } - - /** - * Checks if the combination of xPx-values array and DataSet array is legal or - * not. - */ - private void checkLegal() { - - if (mDataSets == null) - return; - - if (this instanceof ScatterData || this instanceof CombinedData) - return; - - for (int i = 0; i < mDataSets.size(); i++) { - if (mDataSets.get(i).getEntryCount() > mXVals.size()) { -// throw new IllegalArgumentException( -// "One or more of the DataSet Entry arrays are longer than the xPx-values array of this ChartData object."); - } - } } /** @@ -413,45 +308,6 @@ public int getYValCount() { return mYValCount; } - /** - * returns the xPx-values the chart represents - * - * @return - */ - public List getXVals() { - return mXVals; - } - - /** - * sets the xPx-values the chart represents - * - */ - public void setXVals(List xVals) { - mXVals = xVals; - } - - /** - * Adds a new xPx-yValue to the chart data. - * - * @param xVal - */ - public void addXValue(XAxisValue xVal) { - - if (xVal != null && xVal.getLabel().length() > mXValMaximumLength) - mXValMaximumLength = xVal.getLabel().length(); - - mXVals.add(xVal); - } - - /** - * Removes the xPx-yValue at the specified index. - * - * @param index - */ - public void removeXValue(int index) { - mXVals.remove(index); - } - public List getDataSets() { return mDataSets; } @@ -483,16 +339,6 @@ protected int getDataSetIndexByLabel(List dataSets, String label, return -1; } - /** - * returns the total number of xPx-values this ChartData object represents - * (the size of the xPx-values array) - * - * @return - */ - public int getXValCount() { - return mXVals.size(); - } - /** * Returns the labels of all DataSets as a string array. * @@ -1046,4 +892,38 @@ public boolean contains(T dataSet) { return false; } + + /** + * Returns the total entry count across all DataSet objects this data object contains. + * + * @return + */ + public int getEntryCount() { + + int count = 0; + + for (T set : mDataSets) { + count += set.getEntryCount(); + } + + return count; + } + + /** + * Returns the DataSet object with the maximum number of entries. + * + * @return + */ + public T getMaxEntryCountSet() { + + T max = mDataSets.get(0); + + for (T set : mDataSets) { + + if (set.getEntryCount() > max.getEntryCount()) + max = set; + } + + return max; + } } 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 c6341f77e8..174cac65a8 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 @@ -25,14 +25,6 @@ public CombinedData() { super(); } - public CombinedData(List xVals) { - super(xVals); - } - - public CombinedData(XAxisValue[] xVals) { - super(xVals); - } - public void setData(LineData data) { mLineData = data; mDataSets.addAll(data.getDataSets()); 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 5eefb72b0f..834f46b202 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 @@ -22,6 +22,10 @@ public class Entry implements Parcelable { /** optional spot for additional data this Entry represents */ private Object mData = null; + public Entry() { + + } + /** * A Entry represents one single entry in the chart. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineData.java index b4ef3b1d53..4cf544874b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineData.java @@ -21,33 +21,7 @@ public LineData(ILineDataSet... dataSets) { super(dataSets); } - public LineData(List xVals) { - super(xVals); - } - - public LineData(XAxisValue[] xVals) { - super(xVals); - } - - public LineData(List xVals, List dataSets) { - super(xVals, dataSets); - } - - public LineData(XAxisValue[] xVals, List dataSets) { - super(xVals, dataSets); - } - - public LineData(List xVals, ILineDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public LineData(XAxisValue[] xVals, ILineDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(ILineDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; + public LineData(List dataSets) { + super(dataSets); } } 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 4df8aee73f..62b5284c14 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 @@ -20,26 +20,8 @@ public PieData() { super(); } - public PieData(List xVals) { - super(xVals); - } - - public PieData(XAxisValue[] xVals) { - super(xVals); - } - - public PieData(List xVals, IPieDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public PieData(XAxisValue[] xVals, IPieDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(IPieDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; + public PieData(IPieDataSet dataSet) { + super(dataSet); } /** 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 5e1f0b8732..4d8b33b964 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 @@ -7,7 +7,7 @@ import java.util.ArrayList; import java.util.List; -public class PieDataSet extends DataSet implements IPieDataSet { +public class PieDataSet extends DataSet implements IPieDataSet { /** the space in pixels between the chart-slices, default 0f */ private float mSliceSpace = 0f; @@ -24,15 +24,15 @@ public class PieDataSet extends DataSet implements IPieDataSet { private float mValueLinePart2Length = 0.4f; private boolean mValueLineVariableLength = true; - public PieDataSet(List yVals, String label) { + public PieDataSet(List yVals, String label) { super(yVals, label); // mShift = Utils.convertDpToPixel(12f); } @Override - public DataSet copy() { + public DataSet copy() { - List yVals = new ArrayList(); + List yVals = new ArrayList<>(); for (int i = 0; i < mValues.size(); i++) { yVals.add(mValues.get(i).copy()); 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 new file mode 100644 index 0000000000..52853629fd --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java @@ -0,0 +1,52 @@ +package com.github.mikephil.charting.data; + +import android.annotation.SuppressLint; + +/** + * Created by Philipp Jahoda on 31/05/16. + */ +@SuppressLint("ParcelCreator") +public class PieEntry extends Entry { + + private String label; + + public PieEntry(float value) { + super(0f, value); + } + + public PieEntry(float value, Object data) { + super(0f, value, data); + } + + public PieEntry(float value, String label) { + super(0f, value); + this.label = label; + } + + public PieEntry(float value, String label, Object data) { + super(0f, value, data); + this.label = label; + } + + /** + * This is the same as getY(). Returns the value of the PieEntry. + * + * @return + */ + public float getValue() { + return getY(); + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public PieEntry copy() { + PieEntry e = new PieEntry(getY(), label, getData()); + return e; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java index ff18c0d8e1..bfb38c3e13 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java @@ -17,33 +17,11 @@ public RadarData() { super(); } - public RadarData(List xVals) { - super(xVals); + public RadarData(List dataSets) { + super(dataSets); } - public RadarData(XAxisValue[] xVals) { - super(xVals); - } - - public RadarData(List xVals, List dataSets) { - super(xVals, dataSets); - } - - public RadarData(XAxisValue[] xVals, List dataSets) { - super(xVals, dataSets); - } - - public RadarData(List xVals, IRadarDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public RadarData(XAxisValue[] xVals, IRadarDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(IRadarDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; + public RadarData(IRadarDataSet... dataSets) { + super(dataSets); } } 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 713fa7f2e0..fef13c20fd 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 @@ -11,35 +11,13 @@ public class ScatterData extends BarLineScatterCandleBubbleData public ScatterData() { super(); } - - public ScatterData(List xVals) { - super(xVals); - } - - public ScatterData(XAxisValue[] xVals) { - super(xVals); - } - - public ScatterData(List xVals, List dataSets) { - super(xVals, dataSets); - } - - public ScatterData(XAxisValue[] xVals, List dataSets) { - super(xVals, dataSets); - } - - public ScatterData(List xVals, IScatterDataSet dataSet) { - super(xVals, toList(dataSet)); - } - public ScatterData(XAxisValue[] xVals, IScatterDataSet dataSet) { - super(xVals, toList(dataSet)); + public ScatterData(List dataSets) { + super(dataSets); } - private static List toList(IScatterDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; + public ScatterData(IScatterDataSet... dataSets) { + super(dataSets); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java index 8a46ff618a..a230385032 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java @@ -15,6 +15,7 @@ public class RealmBarData extends BarData { public RealmBarData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { - super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSets); + super(dataSets); + //RealmUtils.toXVals(result, xPositionField, xLabelField) } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java index e21017f6f9..ec23ec516b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java @@ -15,6 +15,7 @@ public class RealmBubbleData extends BubbleData { public RealmBubbleData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { - super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSets); + super(dataSets); + ////RealmUtils.toXVals(result, xPositionField, xLabelField) } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java index 5cc2757498..fbe2cb6a92 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java @@ -15,6 +15,7 @@ public class RealmCandleData extends CandleData { public RealmCandleData(RealmResults result,String xPositionField, String xLabelField, List dataSets) { - super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSets); + super(dataSets); + //RealmUtils.toXVals(result, xPositionField, xLabelField) } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java index 97357e034d..cee63fa6cc 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java @@ -15,6 +15,7 @@ public class RealmLineData extends LineData { public RealmLineData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { - super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSets); + super(dataSets); + //RealmUtils.toXVals(result, xPositionField, xLabelField) } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java index d407e67864..3a08452813 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java @@ -13,6 +13,7 @@ public class RealmPieData extends PieData { public RealmPieData(RealmResults result,String xPositionField, String xLabelField, IPieDataSet dataSet) { - super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSet); + super(dataSet); + //RealmUtils.toXVals(result, xPositionField, xLabelField) } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java index bb6d275e8a..8ce3fc0df1 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java @@ -2,6 +2,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; import com.github.mikephil.charting.data.realm.base.RealmBaseDataSet; import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; import com.github.mikephil.charting.utils.Utils; @@ -13,7 +14,7 @@ /** * Created by Philipp Jahoda on 07/11/15. */ -public class RealmPieDataSet extends RealmBaseDataSet implements IPieDataSet { +public class RealmPieDataSet extends RealmBaseDataSet implements IPieDataSet { /** * the space in pixels between the chart-slices, default 0f diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java index 61dbf2b86d..e2a1d95811 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java @@ -15,6 +15,7 @@ public class RealmRadarData extends RadarData{ public RealmRadarData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { - super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSets); + super(dataSets); + //RealmUtils.toXVals(result, xPositionField, xLabelField) } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java index 8ae94aaac3..480455c30c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java @@ -15,6 +15,7 @@ public class RealmScatterData extends ScatterData { public RealmScatterData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { - super(RealmUtils.toXVals(result, xPositionField, xLabelField), dataSets); + super(dataSets); + //RealmUtils.toXVals(result, xPositionField, xLabelField) } } 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 fe52b33619..4c267338b1 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 @@ -74,10 +74,10 @@ public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackInd /** * Constructor, only used for stacked-barchart. * - * @param x the index of the highlighted yValue on the xPx-axis + * @param x the x-value of the highlighted value on the x-axis * @param dataSetIndex the index of the DataSet the highlighted yValue belongs to */ - public Highlight(int x, int dataSetIndex) { + public Highlight(float x, int dataSetIndex) { this(x, Float.NaN, 0, dataSetIndex, -1); } 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 a063213380..a77ecaf393 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 @@ -44,8 +44,6 @@ public interface ChartInterface { */ float getYChartMax(); - int getXValCount(); - int getWidth(); int getHeight(); 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 21980a428c..3b10d003ba 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 @@ -2,11 +2,12 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; /** * Created by Philipp Jahoda on 03/11/15. */ -public interface IPieDataSet extends IDataSet { +public interface IPieDataSet extends IDataSet { /** * Returns the space that is set to be between the piechart-slices of this 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 f620a81b0c..f5fdacc0b9 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 @@ -111,12 +111,11 @@ public void computeLegend(ChartData data) { } else if (dataSet instanceof IPieDataSet) { - List xVals = data.getXVals(); IPieDataSet pds = (IPieDataSet) dataSet; - for (int j = 0; j < clrs.size() && j < entryCount && j < xVals.size(); j++) { + for (int j = 0; j < clrs.size() && j < entryCount; j++) { - labels.add(xVals.get(j).getLabel()); + labels.add(pds.getEntryForIndex(j).getLabel()); colors.add(clrs.get(j)); } 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 78f1a996b2..43f70b0809 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 @@ -22,6 +22,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; @@ -412,7 +413,7 @@ public void drawValues(Canvas c) { for (int j = 0; j < entryCount; j++) { - Entry entry = dataSet.getEntryForIndex(j); + PieEntry entry = dataSet.getEntryForIndex(j); if (xIndex == 0) angle = 0.f; @@ -505,15 +506,15 @@ public void drawValues(Canvas c) { labelPty, dataSet.getValueTextColor(j)); - if (j < data.getXValCount()) { - c.drawText(data.getXVals().get(j).getLabel(), labelPtx, labelPty + lineHeight, + if (j < data.getEntryCount()) { + c.drawText(entry.getLabel(), labelPtx, labelPty + lineHeight, mValuePaint); } } else if (drawXOutside) { - if (j < data.getXValCount()) { + if (j < data.getEntryCount()) { mValuePaint.setColor(dataSet.getValueTextColor(j)); - c.drawText(data.getXVals().get(j).getLabel(), labelPtx, labelPty + lineHeight / 2.f, mValuePaint); + c.drawText(entry.getLabel(), labelPtx, labelPty + lineHeight / 2.f, mValuePaint); } } else if (drawYOutside) { @@ -534,15 +535,15 @@ public void drawValues(Canvas c) { drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j)); - if (j < data.getXValCount()) { - c.drawText(data.getXVals().get(j).getLabel(), x, y + lineHeight, + if (j < data.getEntryCount()) { + c.drawText(entry.getLabel(), x, y + lineHeight, mValuePaint); } } else if (drawXInside) { - if (j < data.getXValCount()) { + if (j < data.getEntryCount()) { mValuePaint.setColor(dataSet.getValueTextColor(j)); - c.drawText(data.getXVals().get(j).getLabel(), x, y + lineHeight / 2f, mValuePaint); + c.drawText(entry.getLabel(), x, y + lineHeight / 2f, mValuePaint); } } else if (drawYInside) { 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 7e23bbd403..ffdd8685cb 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 @@ -218,7 +218,7 @@ protected void drawWeb(Canvas c) { final int xIncrements = 1 + mChart.getSkipWebLineCount(); - for (int i = 0; i < mChart.getData().getXValCount(); i += xIncrements) { + for (int i = 0; i < mChart.getData().getEntryCount(); i += xIncrements) { PointF p = Utils.getPosition( center, @@ -237,7 +237,7 @@ protected void drawWeb(Canvas c) { for (int j = 0; j < labelCount; j++) { - for (int i = 0; i < mChart.getData().getXValCount(); i++) { + for (int i = 0; i < mChart.getData().getEntryCount(); i++) { float r = (mChart.getYAxis().mEntries[j] - mChart.getYChartMin()) * factor; 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 e31487d84d..dc79bc9aad 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 @@ -185,7 +185,7 @@ public void renderLimitLines(Canvas c) { Path limitPath = new Path(); - for (int j = 0; j < mChart.getData().getXValCount(); j++) { + for (int j = 0; j < mChart.getData().getMaxEntryCountSet().getEntryCount(); j++) { PointF p = Utils.getPosition(center, r, sliceangle * j + mChart.getRotationAngle()); From 0d4b60800c1c48663247d3c83b49bd8803faebf2 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 31 May 2016 18:00:35 +0200 Subject: [PATCH 141/606] Remove XAxisValue from example --- .../mpchartexample/AnotherBarActivity.java | 11 +--- .../mpchartexample/BarChartActivity.java | 2 +- .../BarChartActivityMultiDataset.java | 10 +-- .../mpchartexample/BarChartActivitySinus.java | 3 +- .../BarChartPositiveNegative.java | 5 +- .../mpchartexample/BubbleChartActivity.java | 8 +-- .../CandleStickChartActivity.java | 6 +- .../mpchartexample/CombinedChartActivity.java | 8 +-- .../CubicLineChartActivity.java | 9 +-- .../DynamicalAddingActivity.java | 10 +-- .../HorizontalBarChartActivity.java | 3 +- .../InvertedLineChartActivity.java | 2 +- .../mpchartexample/LineChartActivity1.java | 3 +- .../mpchartexample/LineChartActivity2.java | 3 +- .../mpchartexample/LineChartTime.java | 9 +-- .../ListViewBarChartActivity.java | 2 +- .../ListViewMultiChartActivity.java | 11 ++-- .../MultiLineChartActivity.java | 7 +- .../mpchartexample/PerformanceLineChart.java | 7 +- .../mpchartexample/PieChartActivity.java | 12 ++-- .../PiePolylineChartActivity.java | 12 ++-- .../mpchartexample/RadarChartActivitry.java | 7 +- .../RealtimeLineChartActivity.java | 6 +- .../mpchartexample/ScatterChartActivity.java | 7 +- .../mpchartexample/ScrollViewActivity.java | 4 +- .../mpchartexample/StackedBarActivity.java | 8 +-- .../StackedBarActivityNegative.java | 7 +- .../fragments/SimpleFragment.java | 21 +++--- .../realm/RealmDatabaseActivityRadar.java | 2 +- .../mikephil/charting/data/XAxisValue.java | 66 ------------------- 30 files changed, 53 insertions(+), 218 deletions(-) delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/XAxisValue.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 20f015e640..2ab4868b2a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -180,20 +180,12 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { yVals1.add(new BarEntry(i, val)); } - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - - XAxisValue xValue = new XAxisValue(i, (int) yVals1.get(i).getY() + ""); - xVals.add(xValue); - } - BarDataSet set1; if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals1); - mChart.getData().setXVals(xVals); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { @@ -204,8 +196,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { ArrayList dataSets = new ArrayList(); dataSets.add(set1); - BarData data = new BarData(xVals, dataSets); - + BarData data = new BarData(dataSets); mChart.setData(data); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 768e6f6ccb..753157c7f4 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -258,7 +258,7 @@ private void setData(int count, float range) { ArrayList dataSets = new ArrayList(); dataSets.add(set1); - BarData data = new BarData(new ArrayList(), dataSets); + BarData data = new BarData(dataSets); data.setValueTextSize(10f); data.setValueTypeface(mTf); data.setBarWidth(0.9f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 853ccd763f..c9f16aec0b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -189,13 +189,6 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvX.setText("" + (mSeekBarX.getProgress() * 3)); tvY.setText("" + (mSeekBarY.getProgress())); - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - - XAxisValue value = new XAxisValue(i, (i+1990) + ""); - xVals.add(value); - } - ArrayList yVals1 = new ArrayList(); ArrayList yVals2 = new ArrayList(); ArrayList yVals3 = new ArrayList(); @@ -227,7 +220,6 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { set1.setYVals(yVals1); set2.setYVals(yVals2); set3.setYVals(yVals3); - mChart.getData().setXVals(xVals); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { @@ -246,7 +238,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { dataSets.add(set2); dataSets.add(set3); - BarData data = new BarData(xVals, dataSets); + BarData data = new BarData(dataSets); // data.setValueFormatter(new LargeValueFormatter()); // add space between the dataset groups in percent of bar-width diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index 2710321aea..fe32a1e2f7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -224,7 +224,6 @@ private void setData(int count) { mChart.getData().getDataSetCount() > 0) { set = (BarDataSet) mChart.getData().getDataSetByIndex(0); set.setYVals(entries); - mChart.getData().setXVals(xVals); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { @@ -232,7 +231,7 @@ private void setData(int count) { set.setColor(Color.rgb(240, 120, 124)); } - BarData data = new BarData(xVals, set); + BarData data = new BarData(set); data.setValueTextSize(10f); data.setValueTypeface(mTf); data.setDrawValues(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index db8e954550..ceb699561e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -88,7 +88,6 @@ protected void onCreate(Bundle savedInstanceState) { private void setData(List dataList) { ArrayList values = new ArrayList(); - XAxisValue[] dates = new XAxisValue[dataList.size()]; List colors = new ArrayList(); int green = Color.rgb(110, 190, 102); @@ -100,8 +99,6 @@ private void setData(List dataList) { BarEntry entry = new BarEntry(d.xValue, d.yValue); values.add(entry); - dates[i] = new XAxisValue(i, dataList.get(i).xAxisValue); - // specific colors if (d.yValue >= 0) colors.add(red); @@ -122,7 +119,7 @@ private void setData(List dataList) { set.setColors(colors); set.setValueTextColors(colors); - BarData data = new BarData(dates, set); + BarData data = new BarData(set); data.setValueTextSize(13f); data.setValueTypeface(mTf); data.setValueFormatter(new ValueFormatter()); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java index 3c16c8a045..27050469b7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -163,12 +163,6 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvX.setText("" + count); tvY.setText("" + range); - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - XAxisValue xVal = new XAxisValue(i,(i) + ""); - xVals.add(xVal); - } - ArrayList yVals1 = new ArrayList(); ArrayList yVals2 = new ArrayList(); ArrayList yVals3 = new ArrayList(); @@ -211,7 +205,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { dataSets.add(set3); // create a data object with the datasets - BubbleData data = new BubbleData(xVals, dataSets); + BubbleData data = new BubbleData(dataSets); data.setValueTypeface(tf); data.setValueTextSize(8f); data.setValueTextColor(Color.WHITE); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java index 1eb8fa6b8c..fe76c9e2cb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -167,7 +167,6 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mChart.resetTracking(); - ArrayList xVals = new ArrayList(); ArrayList yVals1 = new ArrayList(); for (int i = 0; i < prog; i++) { @@ -184,9 +183,6 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { yVals1.add(new CandleEntry(i, val + high, val - low, even ? val + open : val - open, even ? val - close : val + close)); - - XAxisValue xVal = new XAxisValue(i,(i) + ""); - xVals.add(xVal); } CandleDataSet set1 = new CandleDataSet(yVals1, "Data Set"); @@ -201,7 +197,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { set1.setNeutralColor(Color.BLUE); //set1.setHighlightLineWidth(1f); - CandleData data = new CandleData(xVals, set1); + CandleData data = new CandleData(set1); mChart.setData(data); mChart.invalidate(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index d97ac3bf12..12923422dc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -68,13 +68,7 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTH_SIDED); - XAxisValue[] xVals = new XAxisValue[mMonths.length]; - - for(int i = 0; i < mMonths.length; i++) { - xVals[i] = new XAxisValue(i, mMonths[i]); - } - - CombinedData data = new CombinedData(xVals); + CombinedData data = new CombinedData(); data.setData(generateLineData()); data.setData(generateBarData()); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 4c42066d7f..e82c445987 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -269,12 +269,6 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - - for (int i = 0; i < count; i++) { - xVals.add(new XAxisValue(1990 + i, (1990 +i) + "")); - } - ArrayList yVals = new ArrayList(); for (int i = 0; i < count; i++) { @@ -291,7 +285,6 @@ private void setData(int count, float range) { mChart.getData().getDataSetCount() > 0) { set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals); - mChart.getData().setXVals(xVals); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { @@ -318,7 +311,7 @@ public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProv }); // create a data object with the datasets - LineData data = new LineData(xVals, set1); + LineData data = new LineData(set1); data.setValueTypeface(tf); data.setValueTextSize(9f); data.setDrawValues(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index c67086f667..c040d12d7d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -63,7 +63,7 @@ private void addEntry() { } // add a new xPx-yValue first - data.addXValue(new XAxisValue(set.getEntryCount(), set.getEntryCount() + "")); + //data.addXValue(new XAxisValue(set.getEntryCount(), set.getEntryCount() + "")); // choose a random dataSet int randomDataSetIndex = (int) (Math.random() * data.getDataSetCount()); @@ -77,7 +77,7 @@ private void addEntry() { mChart.setVisibleYRangeMaximum(15, AxisDependency.LEFT); // // // this automatically refreshes the chart (calls invalidate()) - mChart.moveViewTo(data.getXValCount()-7, 50f, AxisDependency.LEFT); + mChart.moveViewTo(data.getEntryCount()-7, 50f, AxisDependency.LEFT); } } @@ -114,14 +114,14 @@ private void addDataSet() { // create 10 yPx-vals ArrayList yVals = new ArrayList(); - if(data.getXValCount() == 0) { + if(data.getEntryCount() == 0) { // add 10 xPx-entries for (int i = 0; i < 10; i++) { - data.addXValue(new XAxisValue(i, i + "")); + //data.addXValue(new XAxisValue(i, i + "")); } } - for (int i = 0; i < data.getXValCount(); i++) { + for (int i = 0; i < data.getEntryCount(); i++) { yVals.add(new Entry((float) (Math.random() * 50f) + 50f * count, i)); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index ac3c6035ff..4845af0128 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -250,7 +250,6 @@ private void setData(int count, float range) { mChart.getData().getDataSetCount() > 0) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals1); - mChart.getData().setXVals(xVals); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { @@ -259,7 +258,7 @@ private void setData(int count, float range) { ArrayList dataSets = new ArrayList(); dataSets.add(set1); - BarData data = new BarData(xVals, dataSets); + BarData data = new BarData(dataSets); data.setValueTextSize(10f); data.setValueTypeface(tf); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index ab43c195ac..bfe3fabbe9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -273,7 +273,7 @@ private void setData(int count, float range) { set1.setCircleRadius(4f); // create a data object with the datasets - LineData data = new LineData(xVals, set1); + LineData data = new LineData(set1); // set data mChart.setData(data); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index ed3f86b921..0bff13fe87 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -357,7 +357,6 @@ private void setData(int count, float range) { mChart.getData().getDataSetCount() > 0) { set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals); - mChart.getData().setXVals(xVals); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { @@ -391,7 +390,7 @@ private void setData(int count, float range) { dataSets.add(set1); // add the datasets // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); + LineData data = new LineData(dataSets); // set data mChart.setData(data); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 9644dbd23a..45ae9ba8bd 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -319,7 +319,6 @@ private void setData(int count, float range) { set2 = (LineDataSet)mChart.getData().getDataSetByIndex(1); set1.setYVals(yVals1); set2.setYVals(yVals2); - mChart.getData().setXVals(xVals); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { @@ -358,7 +357,7 @@ private void setData(int count, float range) { dataSets.add(set2); // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); + LineData data = new LineData(dataSets); data.setValueTextColor(Color.WHITE); data.setValueTextSize(9f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index a45baab65d..7b7fc8e0d9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -272,13 +272,6 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < 10; i++) { - - long timeLong = 10000 + i * 1000; - xVals.add(new XAxisValue(timeLong, i + "")); - } - ArrayList yVals1 = new ArrayList(); for (int i = 0; i < count; i++) { @@ -306,7 +299,7 @@ private void setData(int count, float range) { set1.setDrawCircleHole(false); // create a data object with the datasets - LineData data = new LineData(xVals, set1); + LineData data = new LineData(set1); data.setValueTextColor(Color.WHITE); data.setValueTextSize(9f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index 7cbd00a414..ce58acaf41 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java @@ -142,7 +142,7 @@ private BarData generateData(int cnt) { ArrayList sets = new ArrayList(); sets.add(d); - BarData cd = new BarData(getMonths(), sets); + BarData cd = new BarData(sets); cd.setBarWidth(0.9f); return cd; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java index 34389e8260..0e273596fe 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java @@ -18,6 +18,7 @@ import com.github.mikephil.charting.data.LineDataSet; 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.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.listviewitems.BarChartItem; @@ -125,7 +126,7 @@ private LineData generateDataLine(int cnt) { sets.add(d1); sets.add(d2); - LineData cd = new LineData(getMonths(), sets); + LineData cd = new LineData(sets); return cd; } @@ -146,7 +147,7 @@ private BarData generateDataBar(int cnt) { d.setColors(ColorTemplate.VORDIPLOM_COLORS); d.setHighLightAlpha(255); - BarData cd = new BarData(getMonths(), d); + BarData cd = new BarData(d); cd.setBarWidth(0.9f); return cd; } @@ -158,10 +159,10 @@ private BarData generateDataBar(int cnt) { */ private PieData generateDataPie(int cnt) { - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList(); for (int i = 0; i < 4; i++) { - entries.add(new Entry(i, (int) (Math.random() * 70) + 30)); + entries.add(new PieEntry((float) ((Math.random() * 70) + 30), "Quarter " + (i+1))); } PieDataSet d = new PieDataSet(entries, ""); @@ -170,7 +171,7 @@ private PieData generateDataPie(int cnt) { d.setSliceSpace(2f); d.setColors(ColorTemplate.VORDIPLOM_COLORS); - PieData cd = new PieData(getQuarters(), d); + PieData cd = new PieData(d); return cd; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index e890773750..52e0611a23 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -189,11 +189,6 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvX.setText("" + (mSeekBarX.getProgress())); tvY.setText("" + (mSeekBarY.getProgress())); - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - xVals.add(new XAxisValue(i, i+"")); - } - ArrayList dataSets = new ArrayList(); for (int z = 0; z < 3; z++) { @@ -220,7 +215,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { ((LineDataSet) dataSets.get(0)).setColors(ColorTemplate.VORDIPLOM_COLORS); ((LineDataSet) dataSets.get(0)).setCircleColors(ColorTemplate.VORDIPLOM_COLORS); - LineData data = new LineData(xVals, dataSets); + LineData data = new LineData(dataSets); mChart.setData(data); mChart.invalidate(); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java index eedec87e5c..18b5f42d3c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -93,11 +93,6 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add(new XAxisValue(i, i + "")); - } - ArrayList yVals = new ArrayList(); for (int i = 0; i < count; i++) { @@ -119,7 +114,7 @@ private void setData(int count, float range) { set1.setDrawFilled(false); // create a data object with the datasets - LineData data = new LineData(xVals, set1); + LineData data = new LineData(set1); // set data mChart.setData(data); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 601a4e6140..2f2997542b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -23,6 +23,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; @@ -187,20 +188,15 @@ private void setData(int count, float range) { float mult = range; - ArrayList yVals1 = new ArrayList(); + ArrayList yVals1 = new ArrayList(); // IMPORTANT: In a PieChart, no values (Entry) should have the same // xIndex (even if from different DataSets), since no values can be // drawn above each other. for (int i = 0; i < count + 1; i++) { - yVals1.add(new Entry(i, (float) (Math.random() * mult) + mult / 5)); + yVals1.add(new PieEntry((float) ((Math.random() * mult) + mult / 5), mParties[i % mParties.length])); } - ArrayList xVals = new ArrayList(); - - for (int i = 0; i < count + 1; i++) - xVals.add(new XAxisValue(mParties[i % mParties.length])); - PieDataSet dataSet = new PieDataSet(yVals1, "Election Results"); dataSet.setSliceSpace(3f); dataSet.setSelectionShift(5f); @@ -229,7 +225,7 @@ private void setData(int count, float range) { dataSet.setColors(colors); //dataSet.setSelectionShift(0f); - PieData data = new PieData(xVals, dataSet); + PieData data = new PieData(dataSet); data.setValueFormatter(new PercentFormatter()); data.setValueTextSize(11f); data.setValueTextColor(Color.WHITE); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index 119fb8ba9b..0472b22c50 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -23,6 +23,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.formatter.PercentFormatter; import com.github.mikephil.charting.highlight.Highlight; @@ -183,20 +184,15 @@ private void setData(int count, float range) { float mult = range; - ArrayList yVals1 = new ArrayList(); + ArrayList yVals1 = new ArrayList(); // IMPORTANT: In a PieChart, no values (Entry) should have the same // xIndex (even if from different DataSets), since no values can be // drawn above each other. for (int i = 0; i < count + 1; i++) { - yVals1.add(new Entry((float) (Math.random() * mult) + mult / 5, i)); + yVals1.add(new PieEntry((float) (Math.random() * mult) + mult / 5, mParties[i % mParties.length])); } - ArrayList xVals = new ArrayList(); - - for (int i = 0; i < count + 1; i++) - xVals.add(new XAxisValue(mParties[i % mParties.length])); - PieDataSet dataSet = new PieDataSet(yVals1, "Election Results"); dataSet.setSliceSpace(3f); dataSet.setSelectionShift(5f); @@ -232,7 +228,7 @@ private void setData(int count, float range) { // dataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); dataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); - PieData data = new PieData(xVals, dataSet); + PieData data = new PieData(dataSet); data.setValueFormatter(new PercentFormatter()); data.setValueTextSize(11f); data.setValueTextColor(Color.BLACK); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index e843e09f08..c8b478ed7e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -204,11 +204,6 @@ public void setData() { yVals2.add(new Entry(i, val)); } - ArrayList xVals = new ArrayList(); - - for (int i = 0; i < cnt; i++) - xVals.add(new XAxisValue(mParties[i % mParties.length])); - RadarDataSet set1 = new RadarDataSet(yVals1, "Set 1"); set1.setColor(ColorTemplate.VORDIPLOM_COLORS[0]); set1.setFillColor(ColorTemplate.VORDIPLOM_COLORS[0]); @@ -225,7 +220,7 @@ public void setData() { sets.add(set1); sets.add(set2); - RadarData data = new RadarData(xVals, sets); + RadarData data = new RadarData(sets); data.setValueTypeface(tf); data.setValueTextSize(8f); data.setDrawValues(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 4edf885c94..eb8701c5ad 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -140,8 +140,8 @@ private void addEntry() { } // add a new xPx-yValue first - data.addXValue(new XAxisValue(data.getXValCount() ,mMonths[data.getXValCount() % 12] + " " - + (year + data.getXValCount() / 12))); +// data.addXValue(new XAxisValue(data.getXValCount() ,mMonths[data.getXValCount() % 12] + " " +// + (year + data.getXValCount() / 12))); data.addEntry(new Entry((float) (Math.random() * 40) + 30f, set.getEntryCount()), 0); @@ -153,7 +153,7 @@ private void addEntry() { // mChart.setVisibleYRange(30, AxisDependency.LEFT); // move to the latest entry - mChart.moveViewToX(data.getXValCount() - 121); + mChart.moveViewToX(data.getEntryCount() - 121); // this automatically refreshes the chart (calls invalidate()) // mChart.moveViewTo(data.getXValCount()-7, 55f, diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index 2614a015fd..dd317384fa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -163,11 +163,6 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvX.setText("" + (mSeekBarX.getProgress() + 1)); tvY.setText("" + (mSeekBarY.getProgress())); - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - xVals.add(new XAxisValue(i, i + "")); - } - ArrayList yVals1 = new ArrayList(); ArrayList yVals2 = new ArrayList(); ArrayList yVals3 = new ArrayList(); @@ -210,7 +205,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { dataSets.add(set3); // create a data object with the datasets - ScatterData data = new ScatterData(xVals, dataSets); + ScatterData data = new ScatterData(dataSets); data.setValueTypeface(tf); mChart.setData(data); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java index 8c8ae3bb0a..e91720b66f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java @@ -52,19 +52,17 @@ protected void onCreate(Bundle savedInstanceState) { private void setData(int count) { ArrayList yVals = new ArrayList(); - ArrayList xVals = new ArrayList(); for (int i = 0; i < count; i++) { float val = (float) (Math.random() * count) + 15; yVals.add(new BarEntry(i, (int) val)); - xVals.add(new XAxisValue(i, (int) val + "")); } BarDataSet set = new BarDataSet(yVals, "Data Set"); set.setColors(ColorTemplate.VORDIPLOM_COLORS); set.setDrawValues(false); - BarData data = new BarData(xVals, set); + BarData data = new BarData(set); mChart.setData(data); mChart.invalidate(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index e5d7ce1076..8621611f80 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -185,11 +185,6 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvX.setText("" + (mSeekBarX.getProgress() + 1)); tvY.setText("" + (mSeekBarY.getProgress())); - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - xVals.add(new XAxisValue(i, mMonths[i % mMonths.length])); - } - ArrayList yVals1 = new ArrayList(); for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { @@ -207,7 +202,6 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mChart.getData().getDataSetCount() > 0) { set1 = (BarDataSet) mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals1); - mChart.getData().setXVals(xVals); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { @@ -218,7 +212,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { ArrayList dataSets = new ArrayList(); dataSets.add(set1); - BarData data = new BarData(xVals, dataSets); + BarData data = new BarData(dataSets); data.setValueFormatter(new MyValueFormatter()); data.setValueTextColor(Color.WHITE); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index a8834f56ca..7fadf7e13a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -102,13 +102,8 @@ protected void onCreate(Bundle savedInstanceState) { }); 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+"}; - List xVals = new ArrayList(); - for(int i = 0; i < xLabels.length; i++) { - xVals.add(new XAxisValue(i, xLabels[i])); - } - - BarData data = new BarData(xVals, set); + BarData data = new BarData(set); data.setBarWidth(0.8f); mChart.setData(data); mChart.invalidate(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java index 29bd091c6e..6edd7e2bea 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java @@ -19,6 +19,7 @@ import com.github.mikephil.charting.data.LineDataSet; 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.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; import com.github.mikephil.charting.data.XAxisValue; @@ -63,7 +64,7 @@ protected BarData generateBarData(int dataSets, float range, int count) { sets.add(ds); } - BarData d = new BarData(ChartData.generateXVals(0, count), sets); + BarData d = new BarData(sets); d.setValueTypeface(tf); return d; } @@ -90,7 +91,7 @@ protected ScatterData generateScatterData(int dataSets, float range, int count) sets.add(ds); } - ScatterData d = new ScatterData(ChartData.generateXVals(0, count), sets); + ScatterData d = new ScatterData(sets); d.setValueTypeface(tf); return d; } @@ -103,16 +104,10 @@ protected PieData generatePieData() { int count = 4; - ArrayList entries1 = new ArrayList(); - ArrayList xVals = new ArrayList(); - - xVals.add(new XAxisValue("Quarter 1")); - xVals.add(new XAxisValue("Quarter 2")); - xVals.add(new XAxisValue("Quarter 3")); - xVals.add(new XAxisValue("Quarter 4")); + ArrayList entries1 = new ArrayList(); for(int i = 0; i < count; i++) { - entries1.add(new Entry((float) (Math.random() * 60) + 40, i)); + entries1.add(new PieEntry((float) ((Math.random() * 60) + 40), "Quarter " + (i+1))); } PieDataSet ds1 = new PieDataSet(entries1, "Quarterly Revenues 2015"); @@ -121,7 +116,7 @@ protected PieData generatePieData() { ds1.setValueTextColor(Color.WHITE); ds1.setValueTextSize(12f); - PieData d = new PieData(xVals, ds1); + PieData d = new PieData(ds1); d.setValueTypeface(tf); return d; @@ -159,7 +154,7 @@ protected LineData generateLineData() { int max = Math.max(sets.get(0).getEntryCount(), sets.get(1).getEntryCount()); - LineData d = new LineData(ChartData.generateXVals(0, max), sets); + LineData d = new LineData(sets); d.setValueTypeface(tf); return d; } @@ -199,7 +194,7 @@ protected LineData getComplexity() { sets.add(ds3); sets.add(ds4); - LineData d = new LineData(ChartData.generateXVals(0, ds1.getEntryCount()), sets); + LineData d = new LineData(sets); d.setValueTypeface(tf); return d; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java index 599c7f9a29..3b57ca9ceb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java @@ -67,7 +67,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RadarData data = new RadarData(getYears(), dataSets); + RadarData data = new RadarData(dataSets); styleData(data); // set data diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/XAxisValue.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/XAxisValue.java deleted file mode 100644 index cc0fde6e6f..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/XAxisValue.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.github.mikephil.charting.data; - -/** - * Created by Philipp Jahoda on 03/04/16. - */ -public class XAxisValue { - - /** - * the label that describes this yValue - */ - private String mLabel = ""; - - /** - * the position of this yValue on the xPx-axis - */ - private double mPosition; - - /** - * Constructor only with label. This is relevant for pie and radarchart. - * - * @param label the xPx-axis label of this yValue - */ - public XAxisValue(String label) { - this.mLabel = label; - } - - /** - * Constructor. - * - * @param xPosition the position of this yValue on the xPx-axis - * @param label the xPx-axis label of this yValue - */ - public XAxisValue(double xPosition, String label) { - this.mLabel = label; - this.mPosition = xPosition; - } - - /** - * Sets both xPx-position and label. - * - * @param xPosition - * @param label - */ - public void set(double xPosition, String label) { - this.mLabel = label; - this.mPosition = xPosition; - } - - /** - * Returns the position (xPx) of the yValue on the xPx-axis. - * - * @return - */ - public double getPosition() { - return mPosition; - } - - /** - * Returns the xPx-axis label of this yValue. - * - * @return - */ - public String getLabel() { - return mLabel; - } -} From 8e2355c78f315679395d38283497e07109fe4839 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 31 May 2016 18:10:07 +0200 Subject: [PATCH 142/606] Remove all XAxisValue imports and dependencies --- .../mpchartexample/AnotherBarActivity.java | 1 - .../mpchartexample/BarChartActivity.java | 1 - .../BarChartActivityMultiDataset.java | 1 - .../mpchartexample/BarChartActivitySinus.java | 3 - .../BarChartPositiveNegative.java | 1 - .../mpchartexample/BubbleChartActivity.java | 1 - .../CandleStickChartActivity.java | 1 - .../mpchartexample/CombinedChartActivity.java | 1 - .../CubicLineChartActivity.java | 7 +- .../mpchartexample/DrawChartActivity.java | 2 +- .../DynamicalAddingActivity.java | 3 +- .../HorizontalBarChartActivity.java | 12 +-- .../InvertedLineChartActivity.java | 5 +- .../mpchartexample/LineChartActivity1.java | 3 - .../mpchartexample/LineChartActivity2.java | 6 -- .../LineChartActivityColored.java | 2 - .../mpchartexample/LineChartTime.java | 1 - .../MultiLineChartActivity.java | 1 - .../mpchartexample/PerformanceLineChart.java | 1 - .../mpchartexample/PieChartActivity.java | 5 +- .../PiePolylineChartActivity.java | 1 - .../mpchartexample/RadarChartActivitry.java | 1 - .../RealtimeLineChartActivity.java | 3 +- .../mpchartexample/ScatterChartActivity.java | 1 - .../mpchartexample/ScrollViewActivity.java | 1 - .../mpchartexample/StackedBarActivity.java | 1 - .../StackedBarActivityNegative.java | 7 +- .../custom/MyCustomXAxisValueFormatter.java | 2 +- .../custom/MyFillFormatter.java | 2 +- .../fragments/ScatterChartFrag.java | 3 +- .../fragments/SimpleFragment.java | 2 - .../mpchartexample/notimportant/DemoBase.java | 92 +++++++++---------- .../notimportant/MainActivity.java | 2 - .../realm/RealmBaseActivity.java | 3 - .../charting/charts/BarLineChartBase.java | 3 +- .../mikephil/charting/charts/Chart.java | 1 - .../mikephil/charting/components/XAxis.java | 89 +++++++++--------- .../mikephil/charting/data/ChartData.java | 32 +++---- .../charting/data/realm/base/RealmUtils.java | 52 +++++------ .../charting/jobs/AnimatedZoomJob.java | 8 +- .../mikephil/charting/jobs/ZoomJob.java | 2 +- .../charting/renderer/LegendRenderer.java | 1 - .../charting/renderer/XAxisRenderer.java | 2 +- .../XAxisRendererHorizontalBarChart.java | 1 - .../renderer/XAxisRendererRadarChart.java | 4 +- 45 files changed, 147 insertions(+), 227 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 2ab4868b2a..f327d4382c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -16,7 +16,6 @@ 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.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ColorTemplate; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 753157c7f4..9a7de90190 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -28,7 +28,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.data.XAxisValue; import com.github.mikephil.charting.formatter.YAxisValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index c9f16aec0b..7d0fa93790 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -21,7 +21,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.data.XAxisValue; import com.github.mikephil.charting.formatter.LargeValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index fe32a1e2f7..09c03e3cef 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -21,7 +21,6 @@ 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.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.utils.FileUtils; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -210,12 +209,10 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count) { - List xVals = new ArrayList(); ArrayList entries = new ArrayList(); for (int i = 0; i < count; i++) { entries.add(mSinusData.get(i)); - xVals.add(new XAxisValue(i, i+"")); } BarDataSet set; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index ceb699561e..6c270a1e0f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -14,7 +14,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.data.XAxisValue; import com.github.mikephil.charting.utils.ViewPortHandler; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java index 27050469b7..63107114b5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -21,7 +21,6 @@ import com.github.mikephil.charting.data.BubbleDataSet; import com.github.mikephil.charting.data.BubbleEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; import com.github.mikephil.charting.interfaces.datasets.IDataSet; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java index fe76c9e2cb..35af7306b0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -20,7 +20,6 @@ import com.github.mikephil.charting.data.CandleData; import com.github.mikephil.charting.data.CandleDataSet; import com.github.mikephil.charting.data.CandleEntry; -import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 12923422dc..9c53fa7e85 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -27,7 +27,6 @@ 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.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index e82c445987..0e337219f1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -3,9 +3,7 @@ import android.graphics.Color; import android.graphics.Typeface; -import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.support.v4.content.ContextCompat; import android.view.Menu; import android.view.MenuItem; import android.view.WindowManager; @@ -20,13 +18,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.data.XAxisValue; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.formatter.FillFormatter; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java index 9e2ba71981..ba1fc3073c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java @@ -16,10 +16,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.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.listener.OnDrawListener; -import com.github.mikephil.charting.highlight.Highlight; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index c040d12d7d..48aace6cb4 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -13,11 +13,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.data.XAxisValue; +import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.highlight.Highlight; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 4845af0128..fd741f846c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -25,12 +25,9 @@ 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.data.XAxisValue; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; +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.highlight.Highlight; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -236,13 +233,6 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { ArrayList yVals1 = new ArrayList(); - ArrayList xVals = new ArrayList(); - - for (int i = 0; i < count; i++) { - xVals.add(new XAxisValue(i, mMonths[i % 12])); - float val = (float) (Math.random() * range); - yVals1.add(new BarEntry(i, val)); - } BarDataSet set1; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index bfe3fabbe9..fe0a4cc6f1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -19,10 +19,9 @@ 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.data.XAxisValue; +import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.highlight.Highlight; import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -254,7 +253,6 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); ArrayList yVals = new ArrayList(); for (int i = 0; i < count; i++) { @@ -263,7 +261,6 @@ private void setData(int count, float range) { // ((mult * // 0.1) / 10); yVals.add(new Entry(val, i)); - xVals.add(new XAxisValue(i, (i % 30) + "/" + (i % 12) + "/14")); } // create a dataset and give it a type diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 0bff13fe87..3a06bdeeb9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -27,7 +27,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.data.XAxisValue; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.ChartTouchListener; @@ -338,7 +337,6 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); ArrayList yVals = new ArrayList(); for (int i = 0; i < count; i++) { @@ -348,7 +346,6 @@ private void setData(int count, float range) { // ((mult * // 0.1) / 10);xPx yVals.add(new Entry(i, val)); - xVals.add(new XAxisValue(i, i + "")); } LineDataSet set1; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 45ae9ba8bd..5423d8fc20 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -23,7 +23,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.data.XAxisValue; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; @@ -286,11 +285,6 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { private void setData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add(new XAxisValue(i, i + "")); - } - ArrayList yVals1 = new ArrayList(); for (int i = 0; i < count; i++) { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index 0474ea6508..c452dda6bb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -11,8 +11,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.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 7b7fc8e0d9..9a10cf3e76 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -22,7 +22,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.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index 52e0611a23..d354aedc5b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -16,7 +16,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.data.XAxisValue; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java index 18b5f42d3c..e6b6f6444e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -13,7 +13,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.data.XAxisValue; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 2f2997542b..38d4799b99 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -24,12 +24,11 @@ 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.data.XAxisValue; +import com.github.mikephil.charting.formatter.PercentFormatter; +import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.formatter.PercentFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index 0472b22c50..30a1dcd238 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -24,7 +24,6 @@ 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.data.XAxisValue; import com.github.mikephil.charting.formatter.PercentFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IDataSet; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index c8b478ed7e..672fa2b261 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -17,7 +17,6 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.RadarData; import com.github.mikephil.charting.data.RadarDataSet; -import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; import com.github.mikephil.charting.utils.ColorTemplate; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index eb8701c5ad..ac8cdf568d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -19,11 +19,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.data.XAxisValue; +import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.highlight.Highlight; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; public class RealtimeLineChartActivity extends DemoBase implements diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index dd317384fa..5e4dcb37d4 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -20,7 +20,6 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; -import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java index e91720b66f..3d9f07f85d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java @@ -10,7 +10,6 @@ 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.XAxisValue; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 8621611f80..fa6808e23f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -21,7 +21,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.data.XAxisValue; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 7fadf7e13a..dac526041a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -19,13 +19,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.data.XAxisValue; +import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.YAxisValueFormatter; +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.highlight.Highlight; -import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.utils.ViewPortHandler; -import com.github.mikephil.charting.formatter.YAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.text.DecimalFormat; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index ed25ba090a..4cfa6d4d83 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.utils.ViewPortHandler; import com.github.mikephil.charting.formatter.XAxisValueFormatter; +import com.github.mikephil.charting.utils.ViewPortHandler; /** * Created by Philipp Jahoda on 14/09/15. diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java index 2ca92f61eb..1c4d450167 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java @@ -1,8 +1,8 @@ package com.xxmassdeveloper.mpchartexample.custom; import com.github.mikephil.charting.formatter.FillFormatter; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; /** * Created by Philipp Jahoda on 12/09/15. diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java index a93038c1e2..652a796321 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java @@ -1,4 +1,5 @@ package com.xxmassdeveloper.mpchartexample.fragments; + import android.graphics.Typeface; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -9,8 +10,8 @@ import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; import com.xxmassdeveloper.mpchartexample.R; import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java index 6edd7e2bea..2e837b1a19 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java @@ -13,7 +13,6 @@ 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.ChartData; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; @@ -22,7 +21,6 @@ import com.github.mikephil.charting.data.PieEntry; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; -import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java index 25d9dd450f..fb3eb51cf9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java @@ -3,12 +3,8 @@ import android.support.v4.app.FragmentActivity; -import com.github.mikephil.charting.data.XAxisValue; import com.xxmassdeveloper.mpchartexample.R; -import java.util.ArrayList; -import java.util.List; - /** * Baseclass of all Activities of the Demo Application. * @@ -28,50 +24,50 @@ public abstract class DemoBase extends FragmentActivity { }; - protected ArrayList getMonths() { - - ArrayList m = new ArrayList(); - m.add(new XAxisValue(0, "Jan")); - m.add(new XAxisValue(1, "Feb")); - m.add(new XAxisValue(2, "Mar")); - m.add(new XAxisValue(3, "Apr")); - m.add(new XAxisValue(4, "May")); - m.add(new XAxisValue(5, "Jun")); - m.add(new XAxisValue(6, "Jul")); - m.add(new XAxisValue(7, "Aug")); - m.add(new XAxisValue(8, "Sep")); - m.add(new XAxisValue(9, "Okt")); - m.add(new XAxisValue(10, "Nov")); - m.add(new XAxisValue(11, "Dec")); - - return m; - } - - - protected ArrayList getQuarters() { - - ArrayList q = new ArrayList(); - q.add(new XAxisValue(0, "Quarter 1")); - q.add(new XAxisValue(1, "Quarter 2")); - q.add(new XAxisValue(2, "Quarter 3")); - q.add(new XAxisValue(3, "Quarter 4")); - - return q; - } - - protected List getYears() { - ArrayList years = new ArrayList(); - - years.add(new XAxisValue(0, "2013")); - years.add(new XAxisValue(1, "2014")); - years.add(new XAxisValue(2, "2015")); - years.add(new XAxisValue(3, "2016")); - years.add(new XAxisValue(4, "2017")); - years.add(new XAxisValue(5, "2018")); - years.add(new XAxisValue(6, "2019")); - - return years; - } +// protected ArrayList getMonths() { +// +// ArrayList m = new ArrayList(); +// m.add(new XAxisValue(0, "Jan")); +// m.add(new XAxisValue(1, "Feb")); +// m.add(new XAxisValue(2, "Mar")); +// m.add(new XAxisValue(3, "Apr")); +// m.add(new XAxisValue(4, "May")); +// m.add(new XAxisValue(5, "Jun")); +// m.add(new XAxisValue(6, "Jul")); +// m.add(new XAxisValue(7, "Aug")); +// m.add(new XAxisValue(8, "Sep")); +// m.add(new XAxisValue(9, "Okt")); +// m.add(new XAxisValue(10, "Nov")); +// m.add(new XAxisValue(11, "Dec")); +// +// return m; +// } +// +// +// protected ArrayList getQuarters() { +// +// ArrayList q = new ArrayList(); +// q.add(new XAxisValue(0, "Quarter 1")); +// q.add(new XAxisValue(1, "Quarter 2")); +// q.add(new XAxisValue(2, "Quarter 3")); +// q.add(new XAxisValue(3, "Quarter 4")); +// +// return q; +// } +// +// protected List getYears() { +// ArrayList years = new ArrayList(); +// +// years.add(new XAxisValue(0, "2013")); +// years.add(new XAxisValue(1, "2014")); +// years.add(new XAxisValue(2, "2015")); +// years.add(new XAxisValue(3, "2016")); +// years.add(new XAxisValue(4, "2017")); +// years.add(new XAxisValue(5, "2018")); +// years.add(new XAxisValue(6, "2019")); +// +// return years; +// } @Override public void onBackPressed() { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index f511862353..96b383848a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -44,8 +44,6 @@ import com.xxmassdeveloper.mpchartexample.StackedBarActivity; import com.xxmassdeveloper.mpchartexample.StackedBarActivityNegative; import com.xxmassdeveloper.mpchartexample.fragments.SimpleChartDemo; -import com.xxmassdeveloper.mpchartexample.realm.RealmDatabaseActivityBar; -import com.xxmassdeveloper.mpchartexample.realm.RealmDatabaseActivityLine; import com.xxmassdeveloper.mpchartexample.realm.RealmMainActivity; import java.util.ArrayList; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java index f84031c2db..9e3197b259 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -9,10 +9,7 @@ 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.DefaultYAxisValueFormatter; import com.github.mikephil.charting.formatter.PercentFormatter; -import com.xxmassdeveloper.mpchartexample.custom.MyValueFormatter; -import com.xxmassdeveloper.mpchartexample.custom.MyYAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; 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 a419a1274e..ae66e89fca 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 @@ -721,8 +721,7 @@ public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, floa PointD origin = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - Runnable job = new AnimatedZoomJob(mViewPortHandler, this, getTransformer(axis), getAxis(axis), mXAxis - .getValues().size(), scaleX, scaleY, mViewPortHandler.getScaleX(), mViewPortHandler.getScaleY(), + Runnable job = new AnimatedZoomJob(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); 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 006cbd1c33..12c29c78da 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,6 @@ import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.formatter.DefaultValueFormatter; import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.ChartHighlighter; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java index 3617cb69aa..60f7849854 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java @@ -1,14 +1,10 @@ package com.github.mikephil.charting.components; -import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.formatter.DefaultXAxisValueFormatter; import com.github.mikephil.charting.formatter.XAxisValueFormatter; import com.github.mikephil.charting.utils.Utils; -import java.util.ArrayList; -import java.util.List; - /** * Class representing the xPx-axis labels settings. Only use the setter methods to * modify it. Do not access public variables directly. Be aware that not all @@ -18,9 +14,6 @@ */ public class XAxis extends AxisBase { - /** the arraylist containing all the xPx-axis labels */ - protected List mValues = new ArrayList(); - /** * width of the xPx-axis labels in pixels - this is automatically * calculated by the computeSize() methods in the renderers @@ -200,41 +193,41 @@ public boolean isAvoidFirstLastClippingEnabled() { return mAvoidFirstLastClipping; } - /** - * Sets the labels for this axis. - * - * @param values - */ - public void setValues(List values) { - mValues = values; - } - - /** - * Returns the labels for this axis. - * - * @return - */ - public List getValues() { - return mValues; - } - - /** - * Adds a new xPx-yValue to the chart data. - * - * @param xVal - */ - public void addXValue(XAxisValue xVal) { - mValues.add(xVal); - } - - /** - * Removes the xPx-yValue at the specified index. - * - * @param index - */ - public void removeXValue(int index) { - mValues.remove(index); - } +// /** +// * Sets the labels for this axis. +// * +// * @param values +// */ +// public void setValues(List values) { +// mValues = values; +// } +// +// /** +// * Returns the labels for this axis. +// * +// * @return +// */ +// public List getValues() { +// return mValues; +// } +// +// /** +// * Adds a new xPx-yValue to the chart data. +// * +// * @param xVal +// */ +// public void addXValue(XAxisValue xVal) { +// mValues.add(xVal); +// } +// +// /** +// * Removes the xPx-yValue at the specified index. +// * +// * @param index +// */ +// public void removeXValue(int index) { +// mValues.remove(index); +// } /** @@ -264,12 +257,12 @@ public String getLongestLabel() { String longest = ""; - for (int i = 0; i < mValues.size(); i++) { - String text = mValues.get(i).getLabel(); - - if (longest.length() < text.length()) - longest = text; - } +// for (int i = 0; i < mValues.size(); i++) { +// String text = mValues.get(i).getLabel(); +// +// if (longest.length() < text.length()) +// longest = text; +// } return longest; } 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 b3ea3809a3..d29456774b 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 @@ -732,22 +732,22 @@ public T getFirstRight() { return null; } - /** - * Generates an xPx-values array filled with numbers in range specified by the - * parameters. Can be used for convenience. - * - * @return - */ - public static List generateXVals(int from, int to) { - - List xvals = new ArrayList(); - - for (int i = from; i < to; i++) { - xvals.add(new XAxisValue(i, i + "")); - } - - return xvals; - } +// /** +// * Generates an xPx-values array filled with numbers in range specified by the +// * parameters. Can be used for convenience. +// * +// * @return +// */ +// public static List generateXVals(int from, int to) { +// +// List xvals = new ArrayList(); +// +// for (int i = from; i < to; i++) { +// xvals.add(new XAxisValue(i, i + "")); +// } +// +// return xvals; +// } /** * Sets a custom ValueFormatter for all DataSets this data object contains. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmUtils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmUtils.java index 8f9b8d5310..6e7d0558bc 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmUtils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmUtils.java @@ -1,38 +1,30 @@ package com.github.mikephil.charting.data.realm.base; -import com.github.mikephil.charting.data.XAxisValue; - -import java.util.ArrayList; -import java.util.List; - -import io.realm.DynamicRealmObject; -import io.realm.RealmObject; -import io.realm.RealmResults; /** * Created by Philipp Jahoda on 19/12/15. */ public final class RealmUtils { - /** - * Transforms the given Realm-ResultSet into a String array by using the provided xValuesField. - * - * @param result - * @param xPositionField - * @param xLabelField - * @return - */ - public static List toXVals(RealmResults result, String xPositionField, String xLabelField) { - - List xVals = new ArrayList(); - - for (RealmObject object : result) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - - XAxisValue val = new XAxisValue(dynamicObject.getDouble(xPositionField), dynamicObject.getString(xLabelField)); - xVals.add(val); - } - - return xVals; - } +// /** +// * Transforms the given Realm-ResultSet into a String array by using the provided xValuesField. +// * +// * @param result +// * @param xPositionField +// * @param xLabelField +// * @return +// */ +// public static List toXVals(RealmResults result, String xPositionField, String xLabelField) { +// +// List xVals = new ArrayList(); +// +// for (RealmObject object : result) { +// +// DynamicRealmObject dynamicObject = new DynamicRealmObject(object); +// +// XAxisValue val = new XAxisValue(dynamicObject.getDouble(xPositionField), dynamicObject.getString(xLabelField)); +// xVals.add(val); +// } +// +// return xVals; +// } } 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 293032207a..da22c92507 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 @@ -25,10 +25,10 @@ public class AnimatedZoomJob extends AnimatedViewPortJob implements Animator.Ani protected YAxis yAxis; - protected float xValCount; + protected float xAxisRange; @SuppressLint("NewApi") - public AnimatedZoomJob(ViewPortHandler viewPortHandler, View v, Transformer trans, YAxis axis, float xValCount, float scaleX, float scaleY, float xOrigin, float yOrigin, float zoomCenterX, float zoomCenterY, float zoomOriginX, float zoomOriginY, long duration) { + public AnimatedZoomJob(ViewPortHandler viewPortHandler, View v, Transformer trans, YAxis axis, float xAxisRange, float scaleX, float scaleY, float xOrigin, float yOrigin, float zoomCenterX, float zoomCenterY, float zoomOriginX, float zoomOriginY, long duration) { super(viewPortHandler, scaleX, scaleY, trans, v, xOrigin, yOrigin, duration); this.zoomCenterX = zoomCenterX; @@ -37,7 +37,7 @@ public AnimatedZoomJob(ViewPortHandler viewPortHandler, View v, Transformer tran this.zoomOriginY = zoomOriginY; this.animator.addListener(this); this.yAxis = axis; - this.xValCount = xValCount; + this.xAxisRange = xAxisRange; } @Override @@ -50,7 +50,7 @@ public void onAnimationUpdate(ValueAnimator animation) { mViewPortHandler.refresh(save, view, false); float valsInView = yAxis.mAxisRange / mViewPortHandler.getScaleY(); - float xsInView = xValCount / mViewPortHandler.getScaleX(); + float xsInView = xAxisRange / mViewPortHandler.getScaleX(); pts[0] = zoomOriginX + ((zoomCenterX - xsInView / 2f) - zoomOriginX) * phase; pts[1] = zoomOriginY + ((zoomCenterY + valsInView / 2f) - zoomOriginY) * phase; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java index 108a6f3955..d9d7e99354 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java @@ -34,7 +34,7 @@ public void run() { mViewPortHandler.refresh(save, view, false); float valsInView = ((BarLineChartBase) view).getDeltaY(axisDependency) / mViewPortHandler.getScaleY(); - float xsInView = ((BarLineChartBase) view).getXAxis().getValues().size() / mViewPortHandler.getScaleX(); + float xsInView = ((BarLineChartBase) view).getXAxis().mAxisRange / mViewPortHandler.getScaleX(); pts[0] = xValue - xsInView / 2f; pts[1] = yValue + valsInView / 2f; 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 f5fdacc0b9..9092f42857 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 @@ -8,7 +8,6 @@ import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; import com.github.mikephil.charting.interfaces.datasets.IDataSet; 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 6a6fb2a045..c481b02204 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 @@ -214,7 +214,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { if (mXAxis.isAvoidFirstLastClippingEnabled()) { // avoid clipping of the last - if (i == mXAxis.getValues().size() - 1 && mXAxis.getValues().size() > 1) { + if (i == mXAxis.mEntryCount - 1 && mXAxis.mEntryCount > 1) { float width = Utils.calcTextWidth(mAxisLabelPaint, label); if (width > mViewPortHandler.offsetRight() * 2 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 7990205fac..9e37f729f0 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 @@ -12,7 +12,6 @@ import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.XAxisValue; import com.github.mikephil.charting.utils.FSize; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; 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 71d9380e50..d09a5e805c 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 @@ -41,8 +41,8 @@ public void renderAxisLabels(Canvas c) { PointF center = mChart.getCenterOffsets(); int mod = mXAxis.mAxisLabelModulus; - for (int i = 0; i < mXAxis.getValues().size(); i += mod) { - String label = mXAxis.getValues().get(i).getLabel(); + for (int i = 0; i < mXAxis.mEntryCount; i += mod) { + String label = String.valueOf(mXAxis.mEntries[i]); float angle = (sliceangle * i + mChart.getRotationAngle()) % 360f; From 2d4cf0d4b7306e72a63d849e1d2feb9af8edff01 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 31 May 2016 20:33:26 +0200 Subject: [PATCH 143/606] Work on x-axis formatting --- .../mpchartexample/ScatterChartActivity.java | 4 ++- .../custom/MonthXAxisFormatter.java | 28 +++++++++++++++++++ .../custom/MyCustomXAxisValueFormatter.java | 9 ++++-- .../charting/components/AxisBase.java | 2 ++ .../formatter/DefaultXAxisValueFormatter.java | 8 ++++-- .../formatter/XAxisValueFormatter.java | 9 +++--- .../charting/highlight/BarHighlighter.java | 18 ++++++++---- .../charting/renderer/XAxisRenderer.java | 6 ++-- .../renderer/XAxisRendererRadarChart.java | 2 +- .../charting/utils/ViewPortHandler.java | 4 +-- 10 files changed, 69 insertions(+), 21 deletions(-) create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index 5e4dcb37d4..916b211fd9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -24,6 +24,7 @@ import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.custom.MonthXAxisFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -88,6 +89,7 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xl = mChart.getXAxis(); xl.setTypeface(tf); xl.setDrawGridLines(false); + xl.setValueFormatter(new MonthXAxisFormatter()); } @Override @@ -188,7 +190,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { ScatterDataSet set2 = new ScatterDataSet(yVals2, "DS 2"); set2.setScatterShape(ScatterShape.CIRCLE); set2.setScatterShapeHoleColor(ColorTemplate.COLORFUL_COLORS[3]); - set2.setScatterShapeHoleRadius(4f); + set2.setScatterShapeHoleRadius(3f); set2.setColor(ColorTemplate.COLORFUL_COLORS[1]); ScatterDataSet set3 = new ScatterDataSet(yVals3, "DS 3"); set3.setScatterShape(ScatterShape.CROSS); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java new file mode 100644 index 0000000000..fe1021b979 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java @@ -0,0 +1,28 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.formatter.XAxisValueFormatter; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; + +/** + * Created by Philipp Jahoda on 14/09/15. + */ +public class MonthXAxisFormatter implements XAxisValueFormatter { + + protected String[] mMonths = new String[] { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" + }; + + public MonthXAxisFormatter() { + // maybe do something here or provide parameters in constructor + + } + + @Override + public String getXValue(float xValue, float xRange, float xPosition, ViewPortHandler viewPortHandler) { + + float percent = xValue / xRange; + return mMonths[(int) (mMonths.length * percent)]; + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index 4cfa6d4d83..6f3c789cf8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -3,17 +3,22 @@ import com.github.mikephil.charting.formatter.XAxisValueFormatter; import com.github.mikephil.charting.utils.ViewPortHandler; +import java.text.DecimalFormat; + /** * Created by Philipp Jahoda on 14/09/15. */ public class MyCustomXAxisValueFormatter implements XAxisValueFormatter { + private DecimalFormat mFormat; + public MyCustomXAxisValueFormatter() { // maybe do something here or provide parameters in constructor + mFormat = new DecimalFormat("###,###,###,##0.0"); } @Override - public String getXValue(String original, int index, ViewPortHandler viewPortHandler) { + public String getXValue(float xValue, float xRange, float xPosition, ViewPortHandler viewPortHandler) { //Log.i("TRANS", "xPx: " + viewPortHandler.getTransX() + ", yPx: " + viewPortHandler.getTransY()); @@ -25,6 +30,6 @@ else if (viewPortHandler.getScaleX() > 3) else if (viewPortHandler.getScaleX() > 1) return "2"; else - return original; + return mFormat.format(xValue); } } 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 4e3b3cc2a1..56e9d0bc9f 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 @@ -428,6 +428,7 @@ public boolean isAxisMinCustom() { public void setAxisMinValue(float min) { mCustomAxisMin = true; mAxisMinimum = min; + this.mAxisRange = Math.abs(mAxisMaximum - min); } /** @@ -440,6 +441,7 @@ public void setAxisMinValue(float min) { public void setAxisMaxValue(float max) { mCustomAxisMax = true; mAxisMaximum = max; + this.mAxisRange = Math.abs(max - mAxisMinimum); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java index 16277c72ed..e836f7a47e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java @@ -2,6 +2,8 @@ import com.github.mikephil.charting.utils.ViewPortHandler; +import java.text.DecimalFormat; + /** * Created by Philipp Jahoda on 14/09/15. * Default formatter class for adjusting xPx-values before drawing them. @@ -9,8 +11,10 @@ */ public class DefaultXAxisValueFormatter implements XAxisValueFormatter { + private DecimalFormat mFormat = new DecimalFormat("###,###,###,##0.0"); + @Override - public String getXValue(String original, int index, ViewPortHandler viewPortHandler) { - return original; // just return original, no adjustments + public String getXValue(float xValue, float xRange, float xPosition, ViewPortHandler viewPortHandler) { + return mFormat.format(xValue); // just return original, no adjustments } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java index d7fa386112..a58b67ebd5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java @@ -11,14 +11,15 @@ public interface XAxisValueFormatter { /** - * Returns the customized label that is drawn on the xPx-axis. + * Returns the customized label that is drawn on the x-axis. * For performance reasons, avoid excessive calculations * and memory allocations inside this method. * - * @param original the original xPx-axis label to be drawn - * @param index the xPx-index that is currently being drawn + * @param xValue the original x-value + * @param xRange the total range of the x-values + * @param xPosition the position on the x-axis where the value is drawn (in pixels) * @param viewPortHandler provides information about the current chart state (scale, translation, ...) * @return */ - String getXValue(String original, int index, ViewPortHandler viewPortHandler); + String getXValue(float xValue, float xRange, float xPosition, ViewPortHandler viewPortHandler); } 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 693fc63724..0bda142eca 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 @@ -80,21 +80,27 @@ protected SelectionDetail getSelectionDetail(float xVal, float x, float y) { float closestDistance = Float.MAX_VALUE; Entry closestEntry = null; - for(int i = 0; i < barData.getDataSets().size(); i++) { + for (int i = 0; i < barData.getDataSets().size(); i++) { IBarDataSet dataSet = barData.getDataSetByIndex(i); final Entry entry = dataSet.getEntryForXPos(xVal); - final float distance = Math.abs(xVal - entry.getX()); + if (entry != null) { - if(distance < closestDistance) { - closestDataSetIndex = i; - closestDistance = distance; - closestEntry = entry; + final float distance = Math.abs(xVal - entry.getX()); + + if (distance < closestDistance) { + closestDataSetIndex = i; + closestDistance = distance; + closestEntry = entry; + } } } + if(closestEntry == null) + return null; + return new SelectionDetail(x, y, closestEntry.getX(), closestEntry.getY(), closestDataSetIndex, 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 c481b02204..0b3c4d1178 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 @@ -229,13 +229,13 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { } } - drawLabel(c, label, i, x, pos, anchor, labelRotationAngleDegrees); + drawLabel(c, mXAxis.mEntries[i / 2], x, pos, anchor, labelRotationAngleDegrees); } } } - protected void drawLabel(Canvas c, String label, int xIndex, float x, float y, PointF anchor, float angleDegrees) { - String formattedLabel = mXAxis.getValueFormatter().getXValue(label, xIndex, mViewPortHandler); + protected void drawLabel(Canvas c, float xValue, float x, float y, PointF anchor, float angleDegrees) { + String formattedLabel = mXAxis.getValueFormatter().getXValue(xValue, mXAxis.mAxisRange, x, mViewPortHandler); Utils.drawXAxisValue(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees); } 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 d09a5e805c..0f861aaa72 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 @@ -49,7 +49,7 @@ public void renderAxisLabels(Canvas c) { PointF p = Utils.getPosition(center, mChart.getYRange() * factor + mXAxis.mLabelRotatedWidth / 2f, angle); - drawLabel(c, label, i, p.x, p.y - mXAxis.mLabelRotatedHeight / 2.f, + drawLabel(c, 0f, p.x, p.y - mXAxis.mLabelRotatedHeight / 2.f, drawLabelAnchor, labelRotationAngleDegrees); } } 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 4031a6323f..5c37f6eaea 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 @@ -538,12 +538,12 @@ public boolean isInBounds(float x, float y) { } public boolean isInBoundsLeft(float x) { - return mContentRect.left <= x ? true : false; + return mContentRect.left <= x + 1 ? true : false; } public boolean isInBoundsRight(float x) { x = (float) ((int) (x * 100.f)) / 100.f; - return mContentRect.right >= x ? true : false; + return mContentRect.right >= x - 1 ? true : false; } public boolean isInBoundsTop(float y) { From ceda923114ba3ee95ba6bce2303317c70f36abea Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 1 Jun 2016 18:05:23 +0200 Subject: [PATCH 144/606] Work on horizontalbarchart rendering --- .../HorizontalBarChartActivity.java | 5 ++ .../highlight/HorizontalBarHighlighter.java | 89 ++----------------- .../charting/renderer/BarChartRenderer.java | 35 -------- .../renderer/HorizontalBarChartRenderer.java | 66 +++++++------- 4 files changed, 48 insertions(+), 147 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index fd741f846c..afe35d1e10 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -234,6 +234,11 @@ private void setData(int count, float range) { ArrayList yVals1 = new ArrayList(); + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range); + yVals1.add(new BarEntry(i, val)); + } + BarDataSet set1; if (mChart.getData() != null && 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 753eb5018e..982c2eb6c6 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 @@ -1,9 +1,9 @@ package com.github.mikephil.charting.highlight; -import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.SelectionDetail; /** @@ -20,99 +20,26 @@ public Highlight getHighlight(float x, float y) { BarData barData = mChart.getBarData(); - final float xVal = (float) getValsForTouch(x, y).x; - final float baseNoSpace = getBase(x); - final int setCount = barData.getDataSetCount(); - int dataSetIndex = ((int)baseNoSpace) % setCount; - - if (dataSetIndex < 0) { - dataSetIndex = 0; - } else if (dataSetIndex >= setCount) { - dataSetIndex = setCount - 1; - } + PointD pos = getValsForTouch(x, y); - SelectionDetail selectionDetail = getSelectionDetail(xVal, x, y); + SelectionDetail selectionDetail = getSelectionDetail((float) pos.y, x, y); if (selectionDetail == null) return null; - IBarDataSet set = barData.getDataSetByIndex(dataSetIndex); + IBarDataSet set = barData.getDataSetByIndex(selectionDetail.dataSetIndex); if (set.isStacked()) { - float[] pts = new float[2]; - pts[0] = y; - - // take any transformer to determine the xPx-axis yValue - mChart.getTransformer(set.getAxisDependency()).pixelsToValue(pts); - return getStackedHighlight(selectionDetail, set, - xVal, - pts[0]); + (float) pos.y, + (float) pos.x); } return new Highlight( - xVal, + selectionDetail.xValue, selectionDetail.yValue, selectionDetail.dataIndex, selectionDetail.dataSetIndex, -1); } - -// @Override -// protected float getXForTouch(float x) { -// -// if (!mChart.getBarData().isGrouped()) { -// -// // create an array of the touch-point -// float[] pts = new float[2]; -// pts[1] = x; -// -// // take any transformer to determine the xPx-axis yValue -// mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); -// -// return (int) Math.round(pts[1]); -// } else { -// -// float baseNoSpace = getBase(x); -// -// int setCount = mChart.getBarData().getDataSetCount(); -// int xIndex = (int) baseNoSpace / setCount; -// -// int valCount = mChart.getData().getXValCount(); -// -// if (xIndex < 0) -// xIndex = 0; -// else if (xIndex >= valCount) -// xIndex = valCount - 1; -// -// return xIndex; -// } -// } - - /** - * Returns the base yPx-yValue to the corresponding xPx-touch yValue in pixels. - * - * @param y - * @return - */ - protected float getBase(float y) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[1] = y; - - // take any transformer to determine the xPx-axis yValue - mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); - float yVal = pts[1]; - - int setCount = mChart.getBarData().getDataSetCount(); - - // calculate how often the group-space appears - int steps = (int) ((float) yVal / ((float) setCount + mChart.getBarData().getGroupSpace())); - - float groupSpaceSum = mChart.getBarData().getGroupSpace() * (float) steps; - - float baseNoSpace = (float) yVal - groupSpaceSum; - return baseNoSpace; - } } 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 f01d3d3ab3..7aeec4ce90 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 @@ -167,33 +167,6 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { } } - protected void prepareBarHighlight(float y1, float y2, float interval, int entryIndex, int dataSetIndex, int - dataSetCount, float barSpace, float groupSpace, Transformer trans) { - - float barWidth = interval / dataSetCount; - - float groupSpaceWidth = dataSetCount <= 1 ? 0 : barWidth * groupSpace; - float newInterval = (interval - groupSpaceWidth); - float newBarWidth = newInterval / dataSetCount; - - float barSpaceWidth = newBarWidth * barSpace; - float barSpaceWidthHalf = barSpaceWidth / 2f; - - float groupSpaceWidthHalf = groupSpaceWidth / 2f; - float dataSetSpace = dataSetCount <= 1 ? 0 : (newInterval / dataSetCount) * dataSetIndex; - - float x = interval * entryIndex + dataSetSpace; - - float left = x + groupSpaceWidthHalf + barSpaceWidthHalf; - float right = left + newBarWidth - barSpaceWidth; - float top = y1; - float bottom = y2; - - mBarRect.set(left, top, right, bottom); - - trans.rectValueToPixel(mBarRect, mAnimator.getPhaseY()); - } - protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHalf, Transformer trans) { float left = x - barWidthHalf; @@ -439,14 +412,6 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } - public float[] getTransformedValues(Transformer trans, IBarDataSet data, - int dataSetIndex) { -// return trans.generateTransformedValuesBarChart(data, dataSetIndex, -// mChart.getBarData(), -// mAnimator.getPhaseY()); - return null; - } - protected boolean passesCheck() { return mChart.getBarData().getYValCount() < mChart.getMaxVisibleCount() * mViewPortHandler.getScaleX(); 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 347d3487fe..8c1d64dd73 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 @@ -65,6 +65,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { buffer.setPhases(phaseX, phaseY); buffer.setDataSet(index); buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); + buffer.setBarWidth(mChart.getBarData().getBarWidth()); buffer.feed(dataSet); @@ -124,25 +125,26 @@ public void drawValues(Canvas c) { ValueFormatter formatter = dataSet.getValueFormatter(); - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - float[] valuePoints = getTransformedValues(trans, dataSet, i); + // get the buffer + BarBuffer buffer = mBarBuffers[i]; // if only single values are drawn (sum) if (!dataSet.isStacked()) { - for (int j = 0; j < valuePoints.length * mAnimator.getPhaseX(); j += 2) { + for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) { + + float y = (buffer.buffer[j + 1] + buffer.buffer[j + 3]) / 2f; - if (!mViewPortHandler.isInBoundsTop(valuePoints[j + 1])) + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 1])) break; - if (!mViewPortHandler.isInBoundsX(valuePoints[j])) + if (!mViewPortHandler.isInBoundsX(buffer.buffer[j])) continue; - if (!mViewPortHandler.isInBoundsBottom(valuePoints[j + 1])) + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1])) continue; - BarEntry e = dataSet.getEntryForIndex(j / 2); + BarEntry e = dataSet.getEntryForIndex(j / 4); float val = e.getY(); String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); @@ -156,17 +158,23 @@ public void drawValues(Canvas c) { negOffset = -negOffset - valueTextWidth; } - drawValue(c, formattedValue, valuePoints[j] + (val >= 0 ? posOffset : negOffset), - valuePoints[j + 1] + halfTextHeight, dataSet.getValueTextColor(j / 2)); + drawValue(c, formattedValue, buffer.buffer[j + 2] + (val >= 0 ? posOffset : negOffset), + y + halfTextHeight, dataSet.getValueTextColor(j / 2)); } // if each yValue of a potential stack should be drawn } else { - for (int j = 0; j < (valuePoints.length - 1) * mAnimator.getPhaseX(); j += 2) { + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + int bufferIndex = 0; + int index = 0; - BarEntry e = dataSet.getEntryForIndex(j / 2); + while (index < dataSet.getEntryCount() * mAnimator.getPhaseX()) { + BarEntry e = dataSet.getEntryForIndex(index); + + int color = dataSet.getValueTextColor(index); float[] vals = e.getYVals(); // we still draw stacked bars, but there is one @@ -174,13 +182,13 @@ public void drawValues(Canvas c) { // in between if (vals == null) { - if (!mViewPortHandler.isInBoundsTop(valuePoints[j + 1])) + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[bufferIndex + 1])) break; - if (!mViewPortHandler.isInBoundsX(valuePoints[j])) + if (!mViewPortHandler.isInBoundsX(buffer.buffer[bufferIndex])) continue; - if (!mViewPortHandler.isInBoundsBottom(valuePoints[j + 1])) + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[bufferIndex + 1])) continue; float val = e.getY(); @@ -196,9 +204,9 @@ public void drawValues(Canvas c) { negOffset = -negOffset - valueTextWidth; } - drawValue(c, formattedValue, valuePoints[j] + drawValue(c, formattedValue, buffer.buffer[bufferIndex + 2] + (e.getY() >= 0 ? posOffset : negOffset), - valuePoints[j + 1] + halfTextHeight, dataSet.getValueTextColor(j / 2)); + buffer.buffer[bufferIndex + 1] + halfTextHeight, color); } else { @@ -242,7 +250,7 @@ public void drawValues(Canvas c) { float x = transformed[k] + (val >= 0 ? posOffset : negOffset); - float y = valuePoints[j + 1]; + float y = (buffer.buffer[bufferIndex + 1] + buffer.buffer[bufferIndex + 3]) / 2f; if (!mViewPortHandler.isInBoundsTop(y)) break; @@ -253,9 +261,12 @@ public void drawValues(Canvas c) { if (!mViewPortHandler.isInBoundsBottom(y)) continue; - drawValue(c, formattedValue, x, y + halfTextHeight, dataSet.getValueTextColor(j / 2)); + drawValue(c, formattedValue, x, y + halfTextHeight, color); } } + + bufferIndex = vals == null ? bufferIndex + 4 : bufferIndex + 4 * vals.length; + index++; } } } @@ -267,24 +278,17 @@ protected void drawValue(Canvas c, String valueText, float x, float y, int color c.drawText(valueText, x, y, mValuePaint); } - protected void prepareBarHighlight(float x, float y1, float y2, float barspaceHalf, - Transformer trans) { + @Override + protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHalf, Transformer trans) { - float top = x - 0.5f + barspaceHalf; - float bottom = x + 0.5f - barspaceHalf; + float top = x - barWidthHalf; + float bottom = x + barWidthHalf; float left = y1; float right = y2; mBarRect.set(left, top, right, bottom); - trans.rectValueToPixelHorizontal(mBarRect, mAnimator.getPhaseY()); - } - - @Override - public float[] getTransformedValues(Transformer trans, IBarDataSet data, - int dataSetIndex) { - return trans.generateTransformedValuesHorizontalBarChart(data, dataSetIndex, - mChart.getBarData(), mAnimator.getPhaseY()); + trans.rectValueToPixel(mBarRect, mAnimator.getPhaseY()); } @Override From 6d23f7a3aae3163ccd120912c323d94d5580de53 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 1 Jun 2016 21:40:37 +0200 Subject: [PATCH 145/606] Work on horizontal highlighting --- .../charting/highlight/BarHighlighter.java | 24 ------------------- .../highlight/HorizontalBarHighlighter.java | 2 +- 2 files changed, 1 insertion(+), 25 deletions(-) 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 0bda142eca..2768728dce 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 @@ -47,30 +47,6 @@ public Highlight getHighlight(float x, float y) { -1); } -// @Override -// protected float getXForTouch(float x) { -// -// if (!mChart.getBarData().isGrouped()) { -// return super.getXForTouch(x); -// } else { -// return getBase(x); -// -// float baseNoSpace = getBase(x); -// -// int setCount = mChart.getBarData().getDataSetCount(); -// int xIndex = (int) baseNoSpace / setCount; -// -// int valCount = mChart.getData().getXValCount(); -// -// if (xIndex < 0) -// xIndex = 0; -// else if (xIndex >= valCount) -// xIndex = valCount - 1; -// -// return xIndex; -// } -// } - @Override protected SelectionDetail getSelectionDetail(float xVal, float x, float y) { 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 982c2eb6c6..868f703a34 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 @@ -20,7 +20,7 @@ public Highlight getHighlight(float x, float y) { BarData barData = mChart.getBarData(); - PointD pos = getValsForTouch(x, y); + PointD pos = getValsForTouch(y, x); SelectionDetail selectionDetail = getSelectionDetail((float) pos.y, x, y); if (selectionDetail == null) From 71be944ea9516abad2f7405c5e391311b98f6bd3 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 1 Jun 2016 22:09:58 +0200 Subject: [PATCH 146/606] Fix offset related issues --- .../com/xxmassdeveloper/mpchartexample/LineChartActivity2.java | 2 +- .../com/github/mikephil/charting/charts/BarLineChartBase.java | 3 +-- .../com/github/mikephil/charting/renderer/XAxisRenderer.java | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 5423d8fc20..d92de857ce 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -102,7 +102,7 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xAxis = mChart.getXAxis(); xAxis.setTypeface(tf); - xAxis.setTextSize(12f); + xAxis.setTextSize(11f); xAxis.setTextColor(Color.WHITE); xAxis.setDrawGridLines(false); xAxis.setDrawAxisLine(false); 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 ae66e89fca..6cbb7298dd 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 @@ -347,8 +347,7 @@ public void notifyDataSetChanged() { mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum, mAxisLeft.isInverted()); mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum, mAxisRight.isInverted()); - - //mXAxisRenderer.computeSize(mData.getXValMaximumLength(), mData.getXVals()); + mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); if (mLegend != null) mLegendRenderer.computeLegend(mData); 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 0b3c4d1178..565c176399 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 @@ -133,7 +133,7 @@ public void renderAxisLabels(Canvas c) { if (mXAxis.getPosition() == XAxisPosition.TOP) { drawLabels(c, mViewPortHandler.contentTop() - yoffset, - new PointF(0.5f, 1.0f)); + new PointF(0.5f, 0.9f)); } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { From 92cfe0a482a5c4cd85416db4cdd9725cb3f6326a Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 1 Jun 2016 22:14:38 +0200 Subject: [PATCH 147/606] Remove xmin and xmax from renderer --- .../charting/charts/BarLineChartBase.java | 3 --- .../renderer/CombinedChartRenderer.java | 7 ------ .../mikephil/charting/renderer/Renderer.java | 24 ------------------- 3 files changed, 34 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 6cbb7298dd..fd8d07874a 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 @@ -199,9 +199,6 @@ protected void onDraw(Canvas canvas) { long starttime = System.currentTimeMillis(); calcModulus(); - mXAxisRenderer.calcXBounds(this, mXAxis.mAxisLabelModulus); - mRenderer.calcXBounds(this, mXAxis.mAxisLabelModulus); - // execute all drawing commands drawGridBackground(canvas); 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 982e22e36a..c96db83df6 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 @@ -134,16 +134,9 @@ else if (renderer instanceof BubbleChartRenderer) } renderer.drawHighlighted(c, dataIndices.toArray(new Highlight[dataIndices.size()])); - } } - @Override - public void calcXBounds(BarLineScatterCandleBubbleDataProvider chart, int xAxisModulus) { - for (DataRenderer renderer : mRenderers) - renderer.calcXBounds(chart, xAxisModulus); - } - /** * Returns the sub-renderer object at the specified index. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java index f8d111d5b2..dcaaeee2a8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java @@ -16,12 +16,6 @@ public abstract class Renderer { */ protected ViewPortHandler mViewPortHandler; - /** the minimum yValue on the xPx-axis that should be plotted */ - protected float mMinX = 0; - - /** the maximum yValue on the xPx-axis that should be plotted */ - protected float mMaxX = 0; - public Renderer(ViewPortHandler viewPortHandler) { this.mViewPortHandler = viewPortHandler; } @@ -42,22 +36,4 @@ protected boolean fitsBounds(float val, float min, float max) { else return true; } - - /** - * Calculates the minimum and maximum xPx-yValue the chart can currently - * display (with the given zoom level). -> mMinX, mMaxX - * - * @param dataProvider - * @param xAxisModulus - */ - public void calcXBounds(BarLineScatterCandleBubbleDataProvider dataProvider, int xAxisModulus) { - - float low = dataProvider.getLowestVisibleX(); - float high = dataProvider.getHighestVisibleX(); - - int subLow = (low % xAxisModulus == 0) ? xAxisModulus : 0; - - mMinX = Math.max((low / xAxisModulus) * (xAxisModulus) - subLow, 0); - mMaxX = Math.min((high / xAxisModulus) * (xAxisModulus) + xAxisModulus, dataProvider.getXChartMax()); - } } From 90dc54a6853e7e82262c6ffeda006994bbc23b46 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 1 Jun 2016 22:30:02 +0200 Subject: [PATCH 148/606] Fix performance issue in linechart --- .../com/github/mikephil/charting/charts/BarLineChartBase.java | 2 +- .../com/github/mikephil/charting/charts/HorizontalBarChart.java | 2 +- .../main/java/com/github/mikephil/charting/utils/FileUtils.java | 2 +- 3 files changed, 3 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 fd8d07874a..bac69f2633 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 @@ -1312,7 +1312,7 @@ public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float public float getLowestVisibleX() { PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); - return (float) Math.min(mXAxis.mAxisMinimum, pos.x); + return (float) Math.max(mXAxis.mAxisMinimum, pos.x); } /** 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 d8fd26da4e..d917b8547d 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 @@ -205,7 +205,7 @@ public Highlight getHighlightByTouchPoint(float x, float y) { public float getLowestVisibleX() { PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); - return (float) Math.min(mXAxis.mAxisMinimum, pos.y); + return (float) Math.max(mXAxis.mAxisMinimum, pos.y); } @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java index 31fbb10805..dac8573d5d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java @@ -242,7 +242,7 @@ public static List loadBarEntriesFromAssets(AssetManager am, String pa // process line String[] split = line.split("#"); - entries.add(new BarEntry(Float.parseFloat(split[1]), Integer.parseInt(split[0]))); + entries.add(new BarEntry(Float.parseFloat(split[1]), Float.parseFloat(split[0]))); line = reader.readLine(); } From d2aa701eb2597e6735bd701b2e4bd2fc9de88c39 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 09:50:06 +0200 Subject: [PATCH 149/606] Work on x-axis rendering and formatters --- .../mpchartexample/AnotherBarActivity.java | 1 - .../mpchartexample/BarChartActivity.java | 1 - .../BarChartPositiveNegative.java | 1 - .../CandleStickChartActivity.java | 1 - .../mpchartexample/LineChartActivity2.java | 1 - .../mpchartexample/LineChartTime.java | 1 - .../RealtimeLineChartActivity.java | 1 - .../mikephil/charting/charts/RadarChart.java | 1 - .../charting/components/AxisBase.java | 82 +++++++ .../mikephil/charting/components/XAxis.java | 50 ++--- .../mikephil/charting/components/YAxis.java | 105 --------- .../formatter/DefaultAxisValueFormatter.java | 30 +++ .../formatter/DefaultValueFormatter.java | 2 +- .../formatter/DefaultXAxisValueFormatter.java | 6 +- .../formatter/DefaultYAxisValueFormatter.java | 21 +- .../charting/renderer/AxisRenderer.java | 97 ++++++++- .../charting/renderer/XAxisRenderer.java | 64 +++--- .../charting/renderer/YAxisRenderer.java | 202 +++++++++--------- .../renderer/YAxisRendererRadarChart.java | 51 ++--- 19 files changed, 384 insertions(+), 334 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index f327d4382c..b7c2a50b06 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -61,7 +61,6 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setSpaceBetweenLabels(0); xAxis.setDrawGridLines(false); mChart.getAxisLeft().setDrawGridLines(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 9a7de90190..800ba6b828 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -85,7 +85,6 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setPosition(XAxisPosition.BOTTOM); xAxis.setTypeface(mTf); xAxis.setDrawGridLines(false); - xAxis.setSpaceBetweenLabels(2); YAxisValueFormatter custom = new MyYAxisValueFormatter(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 6c270a1e0f..029e7936f5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -56,7 +56,6 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTypeface(mTf); xAxis.setDrawGridLines(false); xAxis.setDrawAxisLine(false); - xAxis.setSpaceBetweenLabels(2); xAxis.setTextColor(Color.LTGRAY); xAxis.setTextSize(13f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java index 35af7306b0..8eabc4be56 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -63,7 +63,6 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setSpaceBetweenLabels(2); xAxis.setDrawGridLines(false); YAxis leftAxis = mChart.getAxisLeft(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index d92de857ce..236fceb28c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -106,7 +106,6 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTextColor(Color.WHITE); xAxis.setDrawGridLines(false); xAxis.setDrawAxisLine(false); - xAxis.setSpaceBetweenLabels(1); YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(tf); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 9a10cf3e76..8a3c0ae982 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -101,7 +101,6 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTextSize(12f); xAxis.setTextColor(Color.WHITE); xAxis.setDrawAxisLine(false); - xAxis.setSpaceBetweenLabels(1); // custom xPx-axis min / max xAxis.setAxisMinValue(5000); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index ac8cdf568d..833428e613 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -80,7 +80,6 @@ protected void onCreate(Bundle savedInstanceState) { xl.setTextColor(Color.WHITE); xl.setDrawGridLines(false); xl.setAvoidFirstLastClipping(true); - xl.setSpaceBetweenLabels(5); xl.setEnabled(true); YAxis leftAxis = mChart.getAxisLeft(); 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 28f2529db6..b0adde7f69 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 @@ -86,7 +86,6 @@ protected void init() { super.init(); mYAxis = new YAxis(AxisDependency.LEFT); - mXAxis.setSpaceBetweenLabels(0); mWebLineWidth = Utils.convertDpToPixel(1.5f); mInnerWebLineWidth = Utils.convertDpToPixel(0.75f); 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 56e9d0bc9f..754ece5257 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,6 +5,7 @@ import android.graphics.DashPathEffect; import android.util.Log; +import com.github.mikephil.charting.formatter.DefaultAxisValueFormatter; import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; @@ -45,6 +46,24 @@ public abstract class AxisBase extends ComponentBase { */ private int mLabelCount = 6; + /** + * the minimum interval between axis values + */ + protected float mGranularity = 1.0f; + + /** + * When true, axis labels are controlled by the `granularity` property. + * When false, axis values could possibly be repeated. + * This could happen if two adjacent axis values are rounded to same yValue. + * If using granularity this could be avoided by having fewer axis values visible. + */ + protected boolean mGranularityEnabled = false; + + /** + * if true, the set number of yPx-labels will be forced + */ + protected boolean mForceLabels = false; + /** * flag indicating if the grid lines for this axis should be drawn */ @@ -255,6 +274,33 @@ public void setLabelCount(int count) { count = 2; mLabelCount = count; + mForceLabels = false; + } + + /** + * sets the number of label entries for the yPx-axis max = 25, min = 2, default: 6, be aware + * that this number is not + * fixed (if force == false) and can only be approximated. + * + * @param count the number of yPx-axis labels that sould 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 + * to have uneven values + */ + public void setLabelCount(int count, boolean force) { + + setLabelCount(count); + mForceLabels = force; + } + + /** + * Returns true if focing the yPx-label count is enabled. Default: false + * + * @return + */ + public boolean isForceLabelsEnabled() { + return mForceLabels; } /** @@ -266,6 +312,42 @@ public int getLabelCount() { return mLabelCount; } + /** + * @return true if granularity is enabled + */ + public boolean isGranularityEnabled() { + return mGranularityEnabled; + } + + /** + * Enabled/disable granularity control on axis yValue intervals. If enabled, the axis + * interval is not allowed to go below a certain granularity. Default: false + * + * @param enabled + */ + public void setGranularityEnabled(boolean enabled) { + mGranularityEnabled = true; + } + + /** + * @return the minimum interval between axis values + */ + public float getGranularity() { + return mGranularity; + } + + /** + * Set a minimum interval for the axis when zooming in. The axis is not allowed to go below + * that limit. This can be used to avoid label duplicating when zooming in. + * + * @param granularity + */ + public void setGranularity(float granularity) { + mGranularity = granularity; + // set this to true if it was disabled, as it makes no sense to call this method with granularity disabled + mGranularityEnabled = true; + } + /** * Adds a new LimitLine to this axis. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java index 60f7849854..72a68a0ae7 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java @@ -9,7 +9,7 @@ * Class representing the xPx-axis labels settings. Only use the setter methods to * modify it. Do not access public variables directly. Be aware that not all * features the XLabels class provides are suitable for the RadarChart. - * + * * @author Philipp Jahoda */ public class XAxis extends AxisBase { @@ -70,12 +70,16 @@ public class XAxis extends AxisBase { /** * Custom formatter for adjusting xPx-yValue strings */ - protected XAxisValueFormatter mXAxisValueFormatter = new DefaultXAxisValueFormatter(); + protected XAxisValueFormatter mXAxisValueFormatter; - /** the position of the xPx-labels relative to the chart */ + /** + * the position of the xPx-labels relative to the chart + */ private XAxisPosition mPosition = XAxisPosition.TOP; - /** enum for the position of the xPx-labels relative to the chart */ + /** + * enum for the position of the xPx-labels relative to the chart + */ public enum XAxisPosition { TOP, BOTTOM, BOTH_SIDED, TOP_INSIDE, BOTTOM_INSIDE } @@ -95,7 +99,7 @@ public XAxisPosition getPosition() { /** * sets the position of the xPx-labels - * + * * @param pos */ public void setPosition(XAxisPosition pos) { @@ -118,24 +122,13 @@ public void setLabelRotationAngle(float angle) { mLabelRotationAngle = angle; } - /** - * Sets the space (in characters) that should be left out between the xPx-axis - * labels, default 4. This only applies if the number of labels that will be - * skipped in between drawn axis labels is not custom set. - * - * @param spaceCharacters - */ - public void setSpaceBetweenLabels(int spaceCharacters) { - mSpaceBetweenLabels = spaceCharacters; - } - /** * Sets the number of labels that should be skipped on the axis before the * next label is drawn. This will disable the feature that automatically * calculates an adequate space between the axis labels and set the number * of labels to be skipped to the fixed number provided by this method. Call * resetLabelsToSkip(...) to re-enable automatic calculation. - * + * * @param count */ public void setLabelsToSkip(int count) { @@ -159,25 +152,17 @@ public void resetLabelsToSkip() { /** * Returns true if a custom axis-modulus has been set that determines the * number of labels to skip when drawing. - * + * * @return */ public boolean isAxisModulusCustom() { return mIsAxisModulusCustom; } - /** - * Returns the space (in characters) that should be left out between the - * xPx-axis labels - */ - public int getSpaceBetweenLabels() { - return mSpaceBetweenLabels; - } - /** * if set to true, the chart will avoid that the first and last label entry * in the chart "clip" off the edge of the chart or the screen - * + * * @param enabled */ public void setAvoidFirstLastClipping(boolean enabled) { @@ -186,7 +171,7 @@ public void setAvoidFirstLastClipping(boolean enabled) { /** * returns true if avoid-first-lastclipping is enabled, false if not - * + * * @return */ public boolean isAvoidFirstLastClippingEnabled() { @@ -238,17 +223,22 @@ public boolean isAvoidFirstLastClippingEnabled() { * @param formatter */ public void setValueFormatter(XAxisValueFormatter formatter) { - if(formatter == null) - mXAxisValueFormatter = new DefaultXAxisValueFormatter(); + if (formatter == null) + mXAxisValueFormatter = new DefaultXAxisValueFormatter(mDecimals); else mXAxisValueFormatter = formatter; } /** * Returns the custom XAxisValueFormatter that is set for this data object. + * * @return */ public XAxisValueFormatter getValueFormatter() { + + if (mXAxisValueFormatter == null) + mXAxisValueFormatter = new DefaultXAxisValueFormatter(mDecimals); + return mXAxisValueFormatter; } 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 347d89ce96..d2b928f71a 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 @@ -31,21 +31,11 @@ public class YAxis extends AxisBase { */ private boolean mDrawTopYLabelEntry = true; - /** - * if true, the yPx-labels show only the minimum and maximum yValue - */ - protected boolean mShowOnlyMinMax = false; - /** * flag that indicates if the axis is inverted or not */ protected boolean mInverted = false; - /** - * if true, the set number of yPx-labels will be forced - */ - protected boolean mForceLabels = false; - /** * flag that indicates if the zero-line should be drawn regardless of other grid lines */ @@ -102,19 +92,6 @@ public enum YAxisLabelPosition { */ protected float mMaxWidth = Float.POSITIVE_INFINITY; - /** - * When true, axis labels are controlled by the `granularity` property. - * When false, axis values could possibly be repeated. - * This could happen if two adjacent axis values are rounded to same yValue. - * If using granularity this could be avoided by having fewer axis values visible. - */ - protected boolean mGranularityEnabled = false; - - /** - * the minimum interval between axis values - */ - protected float mGranularity = 1.0f; - /** * Enum that specifies the axis a DataSet should be plotted against, either LEFT or RIGHT. * @@ -174,42 +151,6 @@ public void setMaxWidth(float maxWidth) { mMaxWidth = maxWidth; } - /** - * @return true if granularity is enabled - */ - public boolean isGranularityEnabled() { - return mGranularityEnabled; - } - - /** - * Enabled/disable granularity control on axis yValue intervals. If enabled, the axis - * interval is not allowed to go below a certain granularity. Default: false - * - * @param enabled - */ - public void setGranularityEnabled(boolean enabled) { - mGranularityEnabled = true; - } - - /** - * @return the minimum interval between axis values - */ - public float getGranularity() { - return mGranularity; - } - - /** - * Set a minimum interval for the axis when zooming in. The axis is not allowed to go below - * that limit. This can be used to avoid label duplicating when zooming in. - * - * @param granularity - */ - public void setGranularity(float granularity) { - mGranularity = granularity; - // set this to true if it was disabled, as it makes no sense to call this method with granularity disabled - mGranularityEnabled = true; - } - /** * returns the position of the yPx-labels */ @@ -246,52 +187,6 @@ public void setDrawTopYLabelEntry(boolean enabled) { mDrawTopYLabelEntry = enabled; } - /** - * sets the number of label entries for the yPx-axis max = 25, min = 2, default: 6, be aware - * that this number is not - * fixed (if force == false) and can only be approximated. - * - * @param count the number of yPx-axis labels that sould 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 - * to have uneven values - */ - public void setLabelCount(int count, boolean force) { - - setLabelCount(count); - mForceLabels = force; - } - - /** - * Returns true if focing the yPx-label count is enabled. Default: false - * - * @return - */ - public boolean isForceLabelsEnabled() { - return mForceLabels; - } - - /** - * If enabled, the YLabels will only show the minimum and maximum yValue of the chart. This - * will ignore/override the - * set label count. - * - * @param enabled - */ - public void setShowOnlyMinMax(boolean enabled) { - mShowOnlyMinMax = enabled; - } - - /** - * Returns true if showing only min and max yValue is enabled. - * - * @return - */ - public boolean isShowOnlyMinMaxEnabled() { - return mShowOnlyMinMax; - } - /** * If this is set to true, the yPx-axis is inverted which means that low values are on top of * the chart, high values 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 new file mode 100644 index 0000000000..c5b859d4a1 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java @@ -0,0 +1,30 @@ +package com.github.mikephil.charting.formatter; + +import java.text.DecimalFormat; + +/** + * Created by philipp on 02/06/16. + */ +public class DefaultAxisValueFormatter { + + /** decimalformat for formatting */ + protected DecimalFormat mFormat; + + /** + * Constructor that specifies to how many digits the yValue should be + * formatted. + * + * @param digits + */ + public DefaultAxisValueFormatter(int digits) { + + StringBuffer b = new StringBuffer(); + for (int i = 0; i < digits; i++) { + if (i == 0) + b.append("."); + b.append("0"); + } + + mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); + } +} 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 fe92d5f4bb..00c514aff0 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,7 +15,7 @@ public class DefaultValueFormatter implements ValueFormatter { /** decimalformat for formatting */ - private DecimalFormat mFormat; + protected DecimalFormat mFormat; /** * Constructor that specifies to how many digits the yValue should be diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java index e836f7a47e..a62f0f88be 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java @@ -9,9 +9,11 @@ * Default formatter class for adjusting xPx-values before drawing them. * This simply returns the original yValue unmodified. */ -public class DefaultXAxisValueFormatter implements XAxisValueFormatter { +public class DefaultXAxisValueFormatter extends DefaultAxisValueFormatter implements XAxisValueFormatter { - private DecimalFormat mFormat = new DecimalFormat("###,###,###,##0.0"); + public DefaultXAxisValueFormatter(int digits) { + super(digits); + } @Override public String getXValue(float xValue, float xRange, float xPosition, ViewPortHandler viewPortHandler) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java index 33fff63545..553362d317 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java @@ -9,27 +9,10 @@ * Default formatter used for formatting labels of the YAxis. Uses a DecimalFormat with * pre-calculated number of digits (depending on max and min yValue). */ -public class DefaultYAxisValueFormatter implements YAxisValueFormatter { +public class DefaultYAxisValueFormatter extends DefaultAxisValueFormatter implements YAxisValueFormatter { - /** decimalformat for formatting */ - private DecimalFormat mFormat; - - /** - * Constructor that specifies to how many digits the yValue should be - * formatted. - * - * @param digits - */ public DefaultYAxisValueFormatter(int digits) { - - StringBuffer b = new StringBuffer(); - for (int i = 0; i < digits; i++) { - if (i == 0) - b.append("."); - b.append("0"); - } - - mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); + super(digits); } @Override 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 6515f7b82b..644267cbac 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 @@ -6,8 +6,10 @@ import android.graphics.Paint; import android.graphics.Paint.Style; +import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; /** @@ -17,6 +19,8 @@ */ public abstract class AxisRenderer extends Renderer { + private AxisBase mAxis; + protected Transformer mTrans; /** paint object for the grid lines */ @@ -31,10 +35,11 @@ public abstract class AxisRenderer extends Renderer { /** paint used for the limit lines */ protected Paint mLimitLinePaint; - public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans) { + public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans, AxisBase axis) { super(viewPortHandler); this.mTrans = trans; + this.mAxis = axis; mAxisLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -125,7 +130,95 @@ public void computeAxis(float min, float max, boolean inverted) { * * @return */ - protected abstract void computeAxisValues(float min, float max); + protected void computeAxisValues(float min, float max) { + + float yMin = min; + float yMax = max; + + int labelCount = mAxis.getLabelCount(); + double range = Math.abs(yMax - yMin); + + if (labelCount == 0 || range <= 0) { + mAxis.mEntries = new float[]{}; + mAxis.mEntryCount = 0; + return; + } + + // Find out how much spacing (in yPx yValue space) between axis values + double rawInterval = range / labelCount; + double interval = Utils.roundToNextSignificant(rawInterval); + + // If granularity is enabled, then do not allow the interval to go below specified granularity. + // This is used to avoid repeated values when rounding values for display. + if (mAxis.isGranularityEnabled()) + interval = interval < mAxis.getGranularity() ? mAxis.getGranularity() : interval; + + // Normalize interval + 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); + } + + // force label count + if (mAxis.isForceLabelsEnabled()) { + + float step = (float) range / (float) (labelCount - 1); + mAxis.mEntryCount = labelCount; + + if (mAxis.mEntries.length < labelCount) { + // Ensure stops contains at least numStops elements. + mAxis.mEntries = new float[labelCount]; + } + + float v = min; + + for (int i = 0; i < labelCount; i++) { + mAxis.mEntries[i] = v; + v += step; + } + + // no forced count + } else { + + + double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; + double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); + + double f; + int i; + int n = 0; + if (interval != 0.0) { + for (f = first; f <= last; f += interval) { + ++n; + } + } + + mAxis.mEntryCount = n; + + if (mAxis.mEntries.length < n) { + // Ensure stops contains at least numStops elements. + mAxis.mEntries = new float[n]; + } + + for (f = first, i = 0; i < n; f += interval, ++i) { + + if (f == 0.0) // Fix for negative zero case (Where yValue == -0.0, and 0.0 == -0.0) + f = 0.0; + + mAxis.mEntries[i] = (float) f; + } + } + + // set decimals + if (interval < 1) { + mAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); + } else { + mAxis.mDecimals = 0; + } + } /** * Draws the axis labels to the screen. 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 565c176399..747d4917b6 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 @@ -24,7 +24,7 @@ public class XAxisRenderer extends AxisRenderer { protected XAxis mXAxis; public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans) { - super(viewPortHandler, trans); + super(viewPortHandler, trans, xAxis); this.mXAxis = xAxis; @@ -60,28 +60,30 @@ public void computeAxis(float min, float max, boolean inverted) { @Override protected void computeAxisValues(float min, float max) { - int labelCount = mXAxis.getLabelCount(); - float range = Math.abs(max - min); - - float interval = range / (labelCount - 1); - - if (mXAxis.mEntries == null || mXAxis.mEntries.length != labelCount) { - mXAxis.mEntries = new float[labelCount]; - mXAxis.mEntryCount = labelCount; - } - - mXAxis.mEntries[0] = min; - - for (int i = 1; i < labelCount; i++) { - mXAxis.mEntries[i] = min + interval * (float) i; - } - - // set decimals - if (interval < 1) { - mXAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); - } else { - mXAxis.mDecimals = 0; - } +// int labelCount = mXAxis.getLabelCount(); +// float range = Math.abs(max - min); +// +// float interval = range / (labelCount - 1); +// +// if (mXAxis.mEntries == null || mXAxis.mEntries.length != labelCount) { +// mXAxis.mEntries = new float[labelCount]; +// mXAxis.mEntryCount = labelCount; +// } +// +// mXAxis.mEntries[0] = min; +// +// for (int i = 1; i < labelCount; i++) { +// mXAxis.mEntries[i] = min + interval * (float) i; +// } +// +// // set decimals +// if (interval < 1) { +// mXAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); +// } else { +// mXAxis.mDecimals = 0; +// } + + super.computeAxisValues(min, max); computeSize(); } @@ -103,18 +105,10 @@ protected void computeSize() { labelHeight, mXAxis.getLabelRotationAngle()); - StringBuilder space = new StringBuilder(); - int xValSpaceChars = mXAxis.getSpaceBetweenLabels(); - - for (int i = 0; i < xValSpaceChars; i++) { - space.append('h'); - } - - final FSize spaceSize = Utils.calcTextSize(mAxisLabelPaint, space.toString()); - mXAxis.mLabelWidth = Math.round(labelWidth + spaceSize.width); + mXAxis.mLabelWidth = Math.round(labelWidth); mXAxis.mLabelHeight = Math.round(labelHeight); - mXAxis.mLabelRotatedWidth = Math.round(labelRotatedSize.width + spaceSize.width); + mXAxis.mLabelRotatedWidth = Math.round(labelRotatedSize.width); mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height); } @@ -194,7 +188,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); - float[] positions = new float[mXAxis.mEntries.length * 2]; + float[] positions = new float[mXAxis.mEntryCount * 2]; for (int i = 0; i < positions.length; i += 2) { // only fill xPx values @@ -245,7 +239,7 @@ public void renderGridLines(Canvas c) { if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) return; - float[] positions = new float[mXAxis.mEntries.length * 2]; + float[] positions = new float[mXAxis.mEntryCount * 2]; for (int i = 0; i < positions.length; i += 2) { // only fill xPx values 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 17cf3dc397..cf4c30ddb7 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 @@ -24,7 +24,7 @@ public class YAxisRenderer extends AxisRenderer { protected Paint mZeroLinePaint; public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer trans) { - super(viewPortHandler, trans); + super(viewPortHandler, trans, yAxis); this.mYAxis = yAxis; @@ -37,106 +37,106 @@ public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer t mZeroLinePaint.setStyle(Paint.Style.STROKE); } - @Override - protected void computeAxisValues(float min, float max) { - - float yMin = min; - float yMax = max; - - int labelCount = mYAxis.getLabelCount(); - double range = Math.abs(yMax - yMin); - - if (labelCount == 0 || range <= 0) { - mYAxis.mEntries = new float[]{}; - mYAxis.mEntryCount = 0; - return; - } - - // Find out how much spacing (in yPx yValue space) between axis values - double rawInterval = range / labelCount; - double interval = Utils.roundToNextSignificant(rawInterval); - - // If granularity is enabled, then do not allow the interval to go below specified granularity. - // This is used to avoid repeated values when rounding values for display. - if (mYAxis.isGranularityEnabled()) - interval = interval < mYAxis.getGranularity() ? mYAxis.getGranularity() : interval; - - // Normalize interval - 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); - } - - // force label count - if (mYAxis.isForceLabelsEnabled()) { - - float step = (float) range / (float) (labelCount - 1); - mYAxis.mEntryCount = labelCount; - - if (mYAxis.mEntries.length < labelCount) { - // Ensure stops contains at least numStops elements. - mYAxis.mEntries = new float[labelCount]; - } - - float v = min; - - for (int i = 0; i < labelCount; i++) { - mYAxis.mEntries[i] = v; - v += step; - } - - // no forced count - } else { - - // if the labels should only show min and max - if (mYAxis.isShowOnlyMinMaxEnabled()) { - - mYAxis.mEntryCount = 2; - mYAxis.mEntries = new float[2]; - mYAxis.mEntries[0] = yMin; - mYAxis.mEntries[1] = yMax; - - } else { - - double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; - double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); - - double f; - int i; - int n = 0; - if (interval != 0.0) { - for (f = first; f <= last; f += interval) { - ++n; - } - } - - mYAxis.mEntryCount = n; - - if (mYAxis.mEntries.length < n) { - // Ensure stops contains at least numStops elements. - mYAxis.mEntries = new float[n]; - } - - for (f = first, i = 0; i < n; f += interval, ++i) { - - if (f == 0.0) // Fix for negative zero case (Where yValue == -0.0, and 0.0 == -0.0) - f = 0.0; - - mYAxis.mEntries[i] = (float) f; - } - } - } - - // set decimals - if (interval < 1) { - mYAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); - } else { - mYAxis.mDecimals = 0; - } - } +// @Override +// protected void computeAxisValues(float min, float max) { +// +// float yMin = min; +// float yMax = max; +// +// int labelCount = mYAxis.getLabelCount(); +// double range = Math.abs(yMax - yMin); +// +// if (labelCount == 0 || range <= 0) { +// mYAxis.mEntries = new float[]{}; +// mYAxis.mEntryCount = 0; +// return; +// } +// +// // Find out how much spacing (in yPx yValue space) between axis values +// double rawInterval = range / labelCount; +// double interval = Utils.roundToNextSignificant(rawInterval); +// +// // If granularity is enabled, then do not allow the interval to go below specified granularity. +// // This is used to avoid repeated values when rounding values for display. +// if (mYAxis.isGranularityEnabled()) +// interval = interval < mYAxis.getGranularity() ? mYAxis.getGranularity() : interval; +// +// // Normalize interval +// 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); +// } +// +// // force label count +// if (mYAxis.isForceLabelsEnabled()) { +// +// float step = (float) range / (float) (labelCount - 1); +// mYAxis.mEntryCount = labelCount; +// +// if (mYAxis.mEntries.length < labelCount) { +// // Ensure stops contains at least numStops elements. +// mYAxis.mEntries = new float[labelCount]; +// } +// +// float v = min; +// +// for (int i = 0; i < labelCount; i++) { +// mYAxis.mEntries[i] = v; +// v += step; +// } +// +// // no forced count +// } else { +// +// // if the labels should only show min and max +// if (mYAxis.isShowOnlyMinMaxEnabled()) { +// +// mYAxis.mEntryCount = 2; +// mYAxis.mEntries = new float[2]; +// mYAxis.mEntries[0] = yMin; +// mYAxis.mEntries[1] = yMax; +// +// } else { +// +// double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; +// double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); +// +// double f; +// int i; +// int n = 0; +// if (interval != 0.0) { +// for (f = first; f <= last; f += interval) { +// ++n; +// } +// } +// +// mYAxis.mEntryCount = n; +// +// if (mYAxis.mEntries.length < n) { +// // Ensure stops contains at least numStops elements. +// mYAxis.mEntries = new float[n]; +// } +// +// for (f = first, i = 0; i < n; f += interval, ++i) { +// +// if (f == 0.0) // Fix for negative zero case (Where yValue == -0.0, and 0.0 == -0.0) +// f = 0.0; +// +// mYAxis.mEntries[i] = (float) f; +// } +// } +// } +// +// // set decimals +// if (interval < 1) { +// mYAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); +// } else { +// mYAxis.mDecimals = 0; +// } +// } /** * draws the yPx-axis labels to the screen 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 dc79bc9aad..5dcaff1fcc 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 @@ -67,44 +67,33 @@ protected void computeAxisValues(float min, float max) { // no forced count } else { - // if the labels should only show min and max - if (mYAxis.isShowOnlyMinMaxEnabled()) { + final double rawCount = yMin / interval; + double first = rawCount < 0.0 ? Math.floor(rawCount) * interval : Math.ceil(rawCount) * interval; - mYAxis.mEntryCount = 2; - mYAxis.mEntries = new float[2]; - mYAxis.mEntries[0] = yMin; - mYAxis.mEntries[1] = yMax; + if (first == 0.0) // Fix for IEEE negative zero case (Where yValue == -0.0, and 0.0 == -0.0) + first = 0.0; - } else { + double last = Utils.nextUp(Math.floor(yMax / interval) * interval); - final double rawCount = yMin / interval; - double first = rawCount < 0.0 ? Math.floor(rawCount) * interval : Math.ceil(rawCount) * interval; - - if (first == 0.0) // Fix for IEEE negative zero case (Where yValue == -0.0, and 0.0 == -0.0) - first = 0.0; - - double last = Utils.nextUp(Math.floor(yMax / interval) * interval); - - double f; - int i; - int n = 0; - for (f = first; f <= last; f += interval) { - ++n; - } + double f; + int i; + int n = 0; + for (f = first; f <= last; f += interval) { + ++n; + } - if (!mYAxis.isAxisMaxCustom()) - n += 1; + if (!mYAxis.isAxisMaxCustom()) + n += 1; - mYAxis.mEntryCount = n; + mYAxis.mEntryCount = n; - if (mYAxis.mEntries.length < n) { - // Ensure stops contains at least numStops elements. - mYAxis.mEntries = new float[n]; - } + if (mYAxis.mEntries.length < n) { + // Ensure stops contains at least numStops elements. + mYAxis.mEntries = new float[n]; + } - for (f = first, i = 0; i < n; f += interval, ++i) { - mYAxis.mEntries[i] = (float) f; - } + for (f = first, i = 0; i < n; f += interval, ++i) { + mYAxis.mEntries[i] = (float) f; } } From 5110dc6349f0f06dc7502dfafe07d8e933a036b7 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 09:54:04 +0200 Subject: [PATCH 150/606] Remove deprecated methods --- .../mpchartexample/CombinedChartActivity.java | 2 +- .../mpchartexample/CubicLineChartActivity.java | 2 +- .../mpchartexample/LineChartTime.java | 12 ++++++------ .../mpchartexample/PerformanceLineChart.java | 2 +- .../github/mikephil/charting/data/LineDataSet.java | 12 +----------- 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 9c53fa7e85..e9b5a6e81b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -94,7 +94,7 @@ private LineData generateLineData() { set.setCircleColor(Color.rgb(240, 238, 70)); set.setCircleRadius(5f); set.setFillColor(Color.rgb(240, 238, 70)); - set.setDrawCubic(true); + set.setMode(LineDataSet.Mode.CUBIC_BEZIER); set.setDrawValues(true); set.setValueTextSize(10f); set.setValueTextColor(Color.rgb(240, 238, 70)); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 0e337219f1..6e8b0ec5d3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -286,7 +286,7 @@ private void setData(int count, float range) { // create a dataset and give it a type set1 = new LineDataSet(yVals, "DataSet 1"); - set1.setDrawCubic(true); + set1.setMode(LineDataSet.Mode.CUBIC_BEZIER); set1.setCubicIntensity(0.2f); //set1.setDrawFilled(true); set1.setDrawCircles(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 8a3c0ae982..f12d003555 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -186,10 +186,10 @@ public boolean onOptionsItemSelected(MenuItem item) { for (ILineDataSet iSet : sets) { LineDataSet set = (LineDataSet) iSet; - if (set.isDrawCubicEnabled()) - set.setDrawCubic(false); + if (set.getMode() == LineDataSet.Mode.CUBIC_BEZIER) + set.setMode(LineDataSet.Mode.LINEAR); else - set.setDrawCubic(true); + set.setMode(LineDataSet.Mode.CUBIC_BEZIER); } mChart.invalidate(); break; @@ -201,10 +201,10 @@ public boolean onOptionsItemSelected(MenuItem item) { for (ILineDataSet iSet : sets) { LineDataSet set = (LineDataSet) iSet; - if (set.isDrawSteppedEnabled()) - set.setDrawStepped(false); + if (set.getMode() == LineDataSet.Mode.STEPPED) + set.setMode(LineDataSet.Mode.LINEAR); else - set.setDrawStepped(true); + set.setMode(LineDataSet.Mode.STEPPED); } mChart.invalidate(); break; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java index e6b6f6444e..14b0d7eda6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -109,7 +109,7 @@ private void setData(int count, float range) { set1.setLineWidth(0.5f); set1.setDrawValues(false); set1.setDrawCircles(false); - set1.setDrawCubic(false); + set1.setMode(LineDataSet.Mode.LINEAR); set1.setDrawFilled(false); // create a data object with the datasets 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 64937fba99..b8c3f5e642 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 @@ -94,7 +94,7 @@ public LineDataSet.Mode getMode() { } /** - * Returns the drawing mode for this line dataset + * Returns the drawing mode for this LineDataSet * * @return */ @@ -224,22 +224,12 @@ public boolean isDrawCirclesEnabled() { return mDrawCircles; } - @Deprecated - public void setDrawCubic(boolean enabled) { - mMode = enabled ? Mode.CUBIC_BEZIER : Mode.LINEAR; - } - @Deprecated @Override public boolean isDrawCubicEnabled() { return mMode == Mode.CUBIC_BEZIER; } - @Deprecated - public void setDrawStepped(boolean enabled) { - mMode = enabled ? Mode.STEPPED : Mode.LINEAR; - } - @Deprecated @Override public boolean isDrawSteppedEnabled() { From 1a642899e052aec3c299e994f89665069f3d5fa9 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 10:08:04 +0200 Subject: [PATCH 151/606] Work on axis formatters --- .../mpchartexample/PerformanceLineChart.java | 2 +- .../mpchartexample/StackedBarActivityNegative.java | 5 +++++ .../mpchartexample/custom/MonthXAxisFormatter.java | 5 +++++ .../custom/MyCustomXAxisValueFormatter.java | 5 +++++ .../custom/MyYAxisValueFormatter.java | 5 +++++ .../github/mikephil/charting/components/XAxis.java | 6 +++++- .../github/mikephil/charting/components/YAxis.java | 7 ++++++- .../charting/formatter/AxisValueFormatter.java | 14 ++++++++++++++ .../formatter/DefaultAxisValueFormatter.java | 14 ++++++++++++-- .../charting/formatter/LargeValueFormatter.java | 5 +++++ .../charting/formatter/PercentFormatter.java | 5 +++++ .../charting/formatter/XAxisValueFormatter.java | 2 +- .../charting/formatter/YAxisValueFormatter.java | 2 +- 13 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/formatter/AxisValueFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java index 14b0d7eda6..036e12ace7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -99,7 +99,7 @@ private void setData(int count, float range) { float val = (float) (Math.random() * mult) + 3;// + (float) // ((mult * // 0.1) / 10); - yVals.add(new Entry(i, val)); + yVals.add(new Entry(i * 0.001f, val)); } // create a dataset and give it a type diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index dac526041a..d7b82c1ded 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -226,5 +226,10 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View public String getFormattedValue(float value, YAxis yAxis) { return mFormat.format(Math.abs(value)) + "m"; } + + @Override + public int getDecimalDigits() { + return 0; + } } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java index fe1021b979..f960f9bf37 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java @@ -25,4 +25,9 @@ public String getXValue(float xValue, float xRange, float xPosition, ViewPortHan float percent = xValue / xRange; return mMonths[(int) (mMonths.length * percent)]; } + + @Override + public int getDecimalDigits() { + return 0; + } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index 6f3c789cf8..e11ee86732 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -32,4 +32,9 @@ else if (viewPortHandler.getScaleX() > 1) else return mFormat.format(xValue); } + + @Override + public int getDecimalDigits() { + return 1; + } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyYAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyYAxisValueFormatter.java index cae82be1d9..d9aefb0cd6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyYAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyYAxisValueFormatter.java @@ -17,4 +17,9 @@ public MyYAxisValueFormatter() { public String getFormattedValue(float value, YAxis yAxis) { return mFormat.format(value) + " $"; } + + @Override + public int getDecimalDigits() { + return 1; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java index 72a68a0ae7..ae0983f5f0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java @@ -236,8 +236,12 @@ public void setValueFormatter(XAxisValueFormatter formatter) { */ public XAxisValueFormatter getValueFormatter() { - if (mXAxisValueFormatter == null) + if (mXAxisValueFormatter == null) { mXAxisValueFormatter = new DefaultXAxisValueFormatter(mDecimals); + } else if (mXAxisValueFormatter.getDecimalDigits() != mDecimals && mXAxisValueFormatter instanceof + DefaultXAxisValueFormatter) { + mXAxisValueFormatter = new DefaultXAxisValueFormatter(mDecimals); + } return mXAxisValueFormatter; } 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 d2b928f71a..3497cdb4d4 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 @@ -4,6 +4,7 @@ import android.graphics.Paint; import com.github.mikephil.charting.formatter.DefaultValueFormatter; +import com.github.mikephil.charting.formatter.DefaultXAxisValueFormatter; import com.github.mikephil.charting.formatter.DefaultYAxisValueFormatter; import com.github.mikephil.charting.formatter.YAxisValueFormatter; import com.github.mikephil.charting.utils.Utils; @@ -393,8 +394,12 @@ public void setValueFormatter(YAxisValueFormatter f) { */ public YAxisValueFormatter getValueFormatter() { - if (mYAxisValueFormatter == null) + if (mYAxisValueFormatter == null) { + mYAxisValueFormatter = new DefaultYAxisValueFormatter(mDecimals); + } else if (mYAxisValueFormatter.getDecimalDigits() != mDecimals && mYAxisValueFormatter instanceof + DefaultYAxisValueFormatter) { mYAxisValueFormatter = new DefaultYAxisValueFormatter(mDecimals); + } return mYAxisValueFormatter; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/AxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/AxisValueFormatter.java new file mode 100644 index 0000000000..4f16ece806 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/AxisValueFormatter.java @@ -0,0 +1,14 @@ +package com.github.mikephil.charting.formatter; + +/** + * Created by philipp on 02/06/16. + */ +public interface AxisValueFormatter { + + /** + * Returns the number of decimal digits this formatter uses. + * + * @return + */ + int getDecimalDigits(); +} 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 c5b859d4a1..fcb0a348a4 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 @@ -5,11 +5,15 @@ /** * Created by philipp on 02/06/16. */ -public class DefaultAxisValueFormatter { +public class DefaultAxisValueFormatter implements AxisValueFormatter { - /** decimalformat for formatting */ + /** + * decimalformat for formatting + */ protected DecimalFormat mFormat; + protected int digits = 0; + /** * Constructor that specifies to how many digits the yValue should be * formatted. @@ -17,6 +21,7 @@ public class DefaultAxisValueFormatter { * @param digits */ public DefaultAxisValueFormatter(int digits) { + this.digits = digits; StringBuffer b = new StringBuffer(); for (int i = 0; i < digits; i++) { @@ -27,4 +32,9 @@ public DefaultAxisValueFormatter(int digits) { mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); } + + @Override + public int getDecimalDigits() { + return digits; + } } 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 d5fc954fdc..690fed4a56 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 @@ -89,4 +89,9 @@ 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 93fbe1fa25..5f6e2946d4 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 @@ -41,4 +41,9 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View public String getFormattedValue(float value, YAxis yAxis) { return mFormat.format(value) + " %"; } + + @Override + public int getDecimalDigits() { + return 1; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java index a58b67ebd5..f805907eb5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java @@ -8,7 +8,7 @@ * * @author Philipp Jahoda */ -public interface XAxisValueFormatter { +public interface XAxisValueFormatter extends AxisValueFormatter { /** * Returns the customized label that is drawn on the x-axis. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java index eacd3ab33a..a8f5c1b273 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java @@ -7,7 +7,7 @@ * Custom formatter interface that allows formatting of * YAxis labels before they are being drawn. */ -public interface YAxisValueFormatter { +public interface YAxisValueFormatter extends AxisValueFormatter { /** * Called when a yValue from the YAxis is formatted From 2ae37c30883b0208a2490674495e85e948011687 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 10:24:51 +0200 Subject: [PATCH 152/606] Restructuring of axis formatters --- .../mpchartexample/BarChartActivity.java | 6 +- .../mpchartexample/StackedBarActivity.java | 4 +- .../StackedBarActivityNegative.java | 7 +- .../custom/MonthXAxisFormatter.java | 14 ++- ...rmatter.java => MyAxisValueFormatter.java} | 10 +-- .../custom/MyCustomXAxisValueFormatter.java | 19 ++-- .../charting/components/AxisBase.java | 62 ++++++++++++- .../mikephil/charting/components/XAxis.java | 54 ----------- .../mikephil/charting/components/YAxis.java | 89 ------------------- .../formatter/AxisValueFormatter.java | 17 +++- .../formatter/DefaultAxisValueFormatter.java | 11 +++ .../formatter/DefaultXAxisValueFormatter.java | 22 ----- .../formatter/DefaultYAxisValueFormatter.java | 23 ----- .../formatter/LargeValueFormatter.java | 8 +- .../charting/formatter/PercentFormatter.java | 8 +- .../formatter/XAxisValueFormatter.java | 25 ------ .../formatter/YAxisValueFormatter.java | 22 ----- .../charting/renderer/XAxisRenderer.java | 2 +- 18 files changed, 128 insertions(+), 275 deletions(-) rename MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/{MyYAxisValueFormatter.java => MyAxisValueFormatter.java} (53%) delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 800ba6b828..103f87a76c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -28,13 +28,13 @@ 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.YAxisValueFormatter; +import com.github.mikephil.charting.formatter.AxisValueFormatter; import com.github.mikephil.charting.highlight.Highlight; 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.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.custom.MyYAxisValueFormatter; +import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -86,7 +86,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTypeface(mTf); xAxis.setDrawGridLines(false); - YAxisValueFormatter custom = new MyYAxisValueFormatter(); + AxisValueFormatter custom = new MyAxisValueFormatter(); YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(mTf); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index fa6808e23f..2e281d797e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -25,8 +25,8 @@ 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.custom.MyYAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -72,7 +72,7 @@ protected void onCreate(Bundle savedInstanceState) { // change the position of the yPx-labels YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setValueFormatter(new MyYAxisValueFormatter()); + leftAxis.setValueFormatter(new MyAxisValueFormatter()); leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) mChart.getAxisRight().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index d7b82c1ded..cbf4bb4fe5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -10,6 +10,7 @@ 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; @@ -20,7 +21,7 @@ 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.YAxisValueFormatter; +import com.github.mikephil.charting.formatter.AxisValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; @@ -207,7 +208,7 @@ public void onNothingSelected() { Log.i("NOTING SELECTED", ""); } - private class CustomFormatter implements ValueFormatter, YAxisValueFormatter { + private class CustomFormatter implements ValueFormatter, AxisValueFormatter { private DecimalFormat mFormat; @@ -223,7 +224,7 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View // YAxis @Override - public String getFormattedValue(float value, YAxis yAxis) { + public String getFormattedValue(float value, AxisBase axis) { return mFormat.format(Math.abs(value)) + "m"; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java index f960f9bf37..ffa49ec24f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java @@ -1,16 +1,14 @@ package com.xxmassdeveloper.mpchartexample.custom; -import com.github.mikephil.charting.formatter.XAxisValueFormatter; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.text.DecimalFormat; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.AxisValueFormatter; /** * Created by Philipp Jahoda on 14/09/15. */ -public class MonthXAxisFormatter implements XAxisValueFormatter { +public class MonthXAxisFormatter implements AxisValueFormatter { - protected String[] mMonths = new String[] { + protected String[] mMonths = new String[]{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" }; @@ -20,9 +18,9 @@ public MonthXAxisFormatter() { } @Override - public String getXValue(float xValue, float xRange, float xPosition, ViewPortHandler viewPortHandler) { + public String getFormattedValue(float value, AxisBase axis) { - float percent = xValue / xRange; + float percent = value / axis.mAxisRange; return mMonths[(int) (mMonths.length * percent)]; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyYAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java similarity index 53% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyYAxisValueFormatter.java rename to MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java index d9aefb0cd6..ea50fd8fad 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyYAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java @@ -1,20 +1,20 @@ package com.xxmassdeveloper.mpchartexample.custom; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.formatter.YAxisValueFormatter; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.AxisValueFormatter; import java.text.DecimalFormat; -public class MyYAxisValueFormatter implements YAxisValueFormatter { +public class MyAxisValueFormatter implements AxisValueFormatter { private DecimalFormat mFormat; - public MyYAxisValueFormatter() { + public MyAxisValueFormatter() { mFormat = new DecimalFormat("###,###,###,##0.0"); } @Override - public String getFormattedValue(float value, YAxis yAxis) { + public String getFormattedValue(float value, AxisBase axis) { return mFormat.format(value) + " $"; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index e11ee86732..73c1ef4681 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -1,6 +1,7 @@ package com.xxmassdeveloper.mpchartexample.custom; -import com.github.mikephil.charting.formatter.XAxisValueFormatter; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.AxisValueFormatter; import com.github.mikephil.charting.utils.ViewPortHandler; import java.text.DecimalFormat; @@ -8,29 +9,31 @@ /** * Created by Philipp Jahoda on 14/09/15. */ -public class MyCustomXAxisValueFormatter implements XAxisValueFormatter { +public class MyCustomXAxisValueFormatter implements AxisValueFormatter { private DecimalFormat mFormat; + private ViewPortHandler mViewPortHandler; - public MyCustomXAxisValueFormatter() { + public MyCustomXAxisValueFormatter(ViewPortHandler viewPortHandler) { + mViewPortHandler = viewPortHandler; // maybe do something here or provide parameters in constructor mFormat = new DecimalFormat("###,###,###,##0.0"); } @Override - public String getXValue(float xValue, float xRange, float xPosition, ViewPortHandler viewPortHandler) { + public String getFormattedValue(float value, AxisBase axis) { //Log.i("TRANS", "xPx: " + viewPortHandler.getTransX() + ", yPx: " + viewPortHandler.getTransY()); // e.g. adjust the xPx-axis values depending on scale / zoom level - if (viewPortHandler.getScaleX() > 5) + if (mViewPortHandler.getScaleX() > 5) return "4"; - else if (viewPortHandler.getScaleX() > 3) + else if (mViewPortHandler.getScaleX() > 3) return "3"; - else if (viewPortHandler.getScaleX() > 1) + else if (mViewPortHandler.getScaleX() > 1) return "2"; else - return mFormat.format(xValue); + return mFormat.format(value); } @Override 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 754ece5257..c5c3445207 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,6 +5,7 @@ import android.graphics.DashPathEffect; import android.util.Log; +import com.github.mikephil.charting.formatter.AxisValueFormatter; import com.github.mikephil.charting.formatter.DefaultAxisValueFormatter; import com.github.mikephil.charting.utils.Utils; @@ -18,6 +19,11 @@ */ public abstract class AxisBase extends ComponentBase { + /** + * custom formatter that is used instead of the auto-formatter if set + */ + protected AxisValueFormatter mAxisValueFormatter; + private int mGridColor = Color.GRAY; private float mGridLineWidth = 1f; @@ -408,7 +414,61 @@ public boolean isDrawLimitLinesBehindDataEnabled() { * * @return */ - public abstract String getLongestLabel(); + public String getLongestLabel() { + + String longest = ""; + + for (int i = 0; i < mEntries.length; i++) { + String text = getFormattedLabel(i); + + if (longest.length() < text.length()) + longest = text; + } + + return longest; + } + + public String getFormattedLabel(int index) { + + if (index < 0 || index >= mEntries.length) + return ""; + else + return getValueFormatter().getFormattedValue(mEntries[index], this); + } + + /** + * Sets the formatter to be used for formatting the axis labels. If no formatter is set, the + * chart will + * automatically determine a reasonable formatting (concerning decimals) for all the values + * that are drawn inside + * the chart. Use chart.getDefaultValueFormatter() to use the formatter calculated by the chart. + * + * @param f + */ + public void setValueFormatter(AxisValueFormatter f) { + + if (f == null) + mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals); + else + mAxisValueFormatter = f; + } + + /** + * Returns the formatter used for formatting the axis labels. + * + * @return + */ + public AxisValueFormatter getValueFormatter() { + + if (mAxisValueFormatter == null) { + mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals); + } else if (mAxisValueFormatter.getDecimalDigits() != mDecimals && mAxisValueFormatter instanceof + DefaultAxisValueFormatter) { + mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals); + } + + return mAxisValueFormatter; + } /** * Enables the grid line to be drawn in dashed mode, e.g. like this diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java index ae0983f5f0..a19711ebef 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java @@ -1,8 +1,6 @@ package com.github.mikephil.charting.components; -import com.github.mikephil.charting.formatter.DefaultXAxisValueFormatter; -import com.github.mikephil.charting.formatter.XAxisValueFormatter; import com.github.mikephil.charting.utils.Utils; /** @@ -67,11 +65,6 @@ public class XAxis extends AxisBase { */ private boolean mAvoidFirstLastClipping = false; - /** - * Custom formatter for adjusting xPx-yValue strings - */ - protected XAxisValueFormatter mXAxisValueFormatter; - /** * the position of the xPx-labels relative to the chart */ @@ -213,51 +206,4 @@ public boolean isAvoidFirstLastClippingEnabled() { // public void removeXValue(int index) { // mValues.remove(index); // } - - - /** - * Sets a custom XAxisValueFormatter for the data object that allows custom-formatting - * of all xPx-values before rendering them. Provide null to reset back to the - * default formatting. - * - * @param formatter - */ - public void setValueFormatter(XAxisValueFormatter formatter) { - if (formatter == null) - mXAxisValueFormatter = new DefaultXAxisValueFormatter(mDecimals); - else - mXAxisValueFormatter = formatter; - } - - /** - * Returns the custom XAxisValueFormatter that is set for this data object. - * - * @return - */ - public XAxisValueFormatter getValueFormatter() { - - if (mXAxisValueFormatter == null) { - mXAxisValueFormatter = new DefaultXAxisValueFormatter(mDecimals); - } else if (mXAxisValueFormatter.getDecimalDigits() != mDecimals && mXAxisValueFormatter instanceof - DefaultXAxisValueFormatter) { - mXAxisValueFormatter = new DefaultXAxisValueFormatter(mDecimals); - } - - return mXAxisValueFormatter; - } - - @Override - public String getLongestLabel() { - - String longest = ""; - -// for (int i = 0; i < mValues.size(); i++) { -// String text = mValues.get(i).getLabel(); -// -// if (longest.length() < text.length()) -// longest = text; -// } - - return longest; - } } 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 3497cdb4d4..6b0c6adedf 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 @@ -3,10 +3,6 @@ import android.graphics.Color; import android.graphics.Paint; -import com.github.mikephil.charting.formatter.DefaultValueFormatter; -import com.github.mikephil.charting.formatter.DefaultXAxisValueFormatter; -import com.github.mikephil.charting.formatter.DefaultYAxisValueFormatter; -import com.github.mikephil.charting.formatter.YAxisValueFormatter; import com.github.mikephil.charting.utils.Utils; /** @@ -22,11 +18,6 @@ */ public class YAxis extends AxisBase { - /** - * custom formatter that is used instead of the auto-formatter if set - */ - protected YAxisValueFormatter mYAxisValueFormatter; - /** * indicates if the top yPx-label entry is drawn or not */ @@ -339,86 +330,6 @@ public float getRequiredHeightSpace(Paint p) { return (float) Utils.calcTextHeight(p, label) + getYOffset() * 2f; } - @Override - public String getLongestLabel() { - - String longest = ""; - - for (int i = 0; i < mEntries.length; i++) { - String text = getFormattedLabel(i); - - if (longest.length() < text.length()) - longest = text; - } - - return longest; - } - - /** - * Returns the formatted yPx-label at the specified index. This will either use the - * auto-formatter or the custom - * formatter (if one is set). - * - * @param index - * @return - */ - public String getFormattedLabel(int index) { - - if (index < 0 || index >= mEntries.length) - return ""; - else - return getValueFormatter().getFormattedValue(mEntries[index], this); - } - - /** - * Sets the formatter to be used for formatting the axis labels. If no formatter is set, the - * chart will - * automatically determine a reasonable formatting (concerning decimals) for all the values - * that are drawn inside - * the chart. Use chart.getDefaultValueFormatter() to use the formatter calculated by the chart. - * - * @param f - */ - public void setValueFormatter(YAxisValueFormatter f) { - - if (f == null) - mYAxisValueFormatter = new DefaultYAxisValueFormatter(mDecimals); - else - mYAxisValueFormatter = f; - } - - /** - * Returns the formatter used for formatting the axis labels. - * - * @return - */ - public YAxisValueFormatter getValueFormatter() { - - if (mYAxisValueFormatter == null) { - mYAxisValueFormatter = new DefaultYAxisValueFormatter(mDecimals); - } else if (mYAxisValueFormatter.getDecimalDigits() != mDecimals && mYAxisValueFormatter instanceof - DefaultYAxisValueFormatter) { - mYAxisValueFormatter = new DefaultYAxisValueFormatter(mDecimals); - } - - return mYAxisValueFormatter; - } - - /** - * If this component has no YAxisValueFormatter or is only equipped with the default one (no - * custom set), return true. - * - * @return - */ - public boolean needsDefaultFormatter() { - if (mYAxisValueFormatter == null) - return true; - if (mYAxisValueFormatter instanceof DefaultValueFormatter) - return true; - - return false; - } - /** * Returns true if this axis needs horizontal offset, false if no offset is needed. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/AxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/AxisValueFormatter.java index 4f16ece806..dee03a9272 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/AxisValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/AxisValueFormatter.java @@ -1,10 +1,25 @@ package com.github.mikephil.charting.formatter; +import com.github.mikephil.charting.components.AxisBase; + /** - * Created by philipp on 02/06/16. + * Created by Philipp Jahoda on 20/09/15. + * Custom formatter interface that allows formatting of + * axis labels before they are being drawn. */ public interface AxisValueFormatter { + /** + * Called when a value from an axis is to be formatted + * before being drawn. For performance reasons, avoid excessive calculations + * and memory allocations inside this method. + * + * @param value the value to be formatted + * @param axis the axis the value belongs to + * @return + */ + String getFormattedValue(float value, AxisBase axis); + /** * Returns the number of decimal digits this formatter uses. * 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 fcb0a348a4..80294399e9 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,5 +1,7 @@ package com.github.mikephil.charting.formatter; +import com.github.mikephil.charting.components.AxisBase; + import java.text.DecimalFormat; /** @@ -12,6 +14,9 @@ public class DefaultAxisValueFormatter implements AxisValueFormatter { */ protected DecimalFormat mFormat; + /** + * the number of decimal digits this formatter uses + */ protected int digits = 0; /** @@ -33,6 +38,12 @@ public DefaultAxisValueFormatter(int digits) { mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); } + @Override + public String getFormattedValue(float value, AxisBase axis) { + // avoid memory allocations here (for performance) + return mFormat.format(value); + } + @Override public int getDecimalDigits() { return digits; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java deleted file mode 100644 index a62f0f88be..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.mikephil.charting.formatter; - -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.text.DecimalFormat; - -/** - * Created by Philipp Jahoda on 14/09/15. - * Default formatter class for adjusting xPx-values before drawing them. - * This simply returns the original yValue unmodified. - */ -public class DefaultXAxisValueFormatter extends DefaultAxisValueFormatter implements XAxisValueFormatter { - - public DefaultXAxisValueFormatter(int digits) { - super(digits); - } - - @Override - public String getXValue(float xValue, float xRange, float xPosition, ViewPortHandler viewPortHandler) { - return mFormat.format(xValue); // just return original, no adjustments - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java deleted file mode 100644 index 553362d317..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.github.mikephil.charting.formatter; - -import com.github.mikephil.charting.components.YAxis; - -import java.text.DecimalFormat; - -/** - * Created by Philipp Jahoda on 20/09/15. - * Default formatter used for formatting labels of the YAxis. Uses a DecimalFormat with - * pre-calculated number of digits (depending on max and min yValue). - */ -public class DefaultYAxisValueFormatter extends DefaultAxisValueFormatter implements YAxisValueFormatter { - - public DefaultYAxisValueFormatter(int digits) { - super(digits); - } - - @Override - public String getFormattedValue(float value, YAxis yAxis) { - // avoid memory allocations here (for performance) - return mFormat.format(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 690fed4a56..ba9d355830 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,7 +1,7 @@ package com.github.mikephil.charting.formatter; -import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -17,7 +17,7 @@ * @author Philipp Jahoda * @author Oleksandr Tyshkovets */ -public class LargeValueFormatter implements ValueFormatter, YAxisValueFormatter { +public class LargeValueFormatter implements ValueFormatter, AxisValueFormatter { private static String[] SUFFIX = new String[]{ "", "k", "m", "b", "t" @@ -46,9 +46,9 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View return makePretty(value) + mText; } - // YAxisValueFormatter + // AxisValueFormatter @Override - public String getFormattedValue(float value, YAxis yAxis) { + 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 5f6e2946d4..b4a5fd9dee 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,7 +1,7 @@ package com.github.mikephil.charting.formatter; -import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -13,7 +13,7 @@ * * @author Philipp Jahoda */ -public class PercentFormatter implements ValueFormatter, YAxisValueFormatter { +public class PercentFormatter implements ValueFormatter, AxisValueFormatter { protected DecimalFormat mFormat; @@ -36,9 +36,9 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View return mFormat.format(value) + " %"; } - // YAxisValueFormatter + // AxisValueFormatter @Override - public String getFormattedValue(float value, YAxis yAxis) { + public String getFormattedValue(float value, AxisBase axis) { return mFormat.format(value) + " %"; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java deleted file mode 100644 index f805907eb5..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/XAxisValueFormatter.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.mikephil.charting.formatter; - -import com.github.mikephil.charting.utils.ViewPortHandler; - -/** - * Created by Philipp Jahoda on 14/09/15. - * An interface for providing custom xPx-axis Strings. - * - * @author Philipp Jahoda - */ -public interface XAxisValueFormatter extends AxisValueFormatter { - - /** - * Returns the customized label that is drawn on the x-axis. - * For performance reasons, avoid excessive calculations - * and memory allocations inside this method. - * - * @param xValue the original x-value - * @param xRange the total range of the x-values - * @param xPosition the position on the x-axis where the value is drawn (in pixels) - * @param viewPortHandler provides information about the current chart state (scale, translation, ...) - * @return - */ - String getXValue(float xValue, float xRange, float xPosition, ViewPortHandler viewPortHandler); -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java deleted file mode 100644 index a8f5c1b273..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/YAxisValueFormatter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.mikephil.charting.formatter; - -import com.github.mikephil.charting.components.YAxis; - -/** - * Created by Philipp Jahoda on 20/09/15. - * Custom formatter interface that allows formatting of - * YAxis labels before they are being drawn. - */ -public interface YAxisValueFormatter extends AxisValueFormatter { - - /** - * Called when a yValue from the YAxis is formatted - * before being drawn. For performance reasons, avoid excessive calculations - * and memory allocations inside this method. - * - * @param value the YAxis yValue to be formatted - * @param yAxis the YAxis object the yValue belongs to - * @return - */ - String getFormattedValue(float value, YAxis yAxis); -} 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 747d4917b6..2ba4b57b8f 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 @@ -229,7 +229,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { } protected void drawLabel(Canvas c, float xValue, float x, float y, PointF anchor, float angleDegrees) { - String formattedLabel = mXAxis.getValueFormatter().getXValue(xValue, mXAxis.mAxisRange, x, mViewPortHandler); + String formattedLabel = mXAxis.getValueFormatter().getFormattedValue(xValue, mXAxis); Utils.drawXAxisValue(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees); } From b857e58837e686b2aeb3301541079ec3daf3a70e Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 14:00:06 +0200 Subject: [PATCH 153/606] Work on x-axis rendering of barchart --- .../mpchartexample/BarChartActivity.java | 4 +- .../BarChartActivityMultiDataset.java | 24 +-- .../custom/YearXAxisFormatter.java | 31 ++++ .../charting/components/AxisBase.java | 15 ++ .../mikephil/charting/components/XAxis.java | 6 - .../mikephil/charting/data/BarData.java | 11 ++ .../charting/renderer/AxisRenderer.java | 79 +++++++--- .../charting/renderer/XAxisRenderer.java | 15 +- .../renderer/XAxisRendererBarChart.java | 146 +++++++----------- .../renderer/XAxisRendererRadarChart.java | 4 +- 10 files changed, 198 insertions(+), 137 deletions(-) create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 103f87a76c..561756882d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -231,14 +231,14 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { mChart.getXAxis().setAxisMinValue(0f); - mChart.getXAxis().setAxisMaxValue(count); + mChart.getXAxis().setAxisMaxValue(count+1f); ArrayList yVals1 = new ArrayList(); for (int i = 0; i < count; i++) { float mult = (range + 1); float val = (float) (Math.random() * mult); - yVals1.add(new BarEntry(i + 0.5f, val)); + yVals1.add(new BarEntry(i + 1f, val)); } BarDataSet set1; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 7d0fa93790..71328613d6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -93,6 +93,8 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xl = mChart.getXAxis(); xl.setTypeface(tf); + xl.setGranularity(1f); + xl.setCenterAxisLabels(true); YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(tf); @@ -182,10 +184,13 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - float groupSpace = 0.8f; - float barSpace = 0.15f; + float groupSpace = 0.04f; + float barSpace = 0.02f; + float barWidth = 0.3f; + int startYear = 1980; + int endYear = startYear + mSeekBarX.getProgress(); - tvX.setText("" + (mSeekBarX.getProgress() * 3)); + tvX.setText(startYear + "\n-" + endYear); tvY.setText("" + (mSeekBarY.getProgress())); ArrayList yVals1 = new ArrayList(); @@ -194,17 +199,17 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { float mult = mSeekBarY.getProgress() * 1000f; - for (int i = 0; i < mSeekBarX.getProgress(); i++) { + for (int i = startYear; i < endYear; i++) { float val = (float) (Math.random() * mult) + 3; yVals1.add(new BarEntry(i, val)); } - for (int i = 0; i < mSeekBarX.getProgress(); i++) { + for (int i = startYear; i < endYear; i++) { float val = (float) (Math.random() * mult) + 3; yVals2.add(new BarEntry(i, val)); } - for (int i = 0; i < mSeekBarX.getProgress(); i++) { + for (int i = startYear; i < endYear; i++) { float val = (float) (Math.random() * mult) + 3; yVals3.add(new BarEntry(i, val)); } @@ -246,9 +251,10 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mChart.setData(data); } - mChart.getBarData().groupBars(0, groupSpace, barSpace); - mChart.getXAxis().setAxisMinValue(0f); - mChart.getXAxis().setAxisMaxValue(mChart.getBarData().getIntervalWidth(groupSpace, barSpace) * mSeekBarX.getProgress()); + mChart.getBarData().setBarWidth(barWidth); + mChart.getBarData().groupBars(startYear, groupSpace, barSpace); + mChart.getXAxis().setAxisMinValue(startYear); + mChart.getXAxis().setAxisMaxValue(mChart.getBarData().getIntervalWidth(groupSpace, barSpace) * mSeekBarX.getProgress() + startYear); mChart.invalidate(); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java new file mode 100644 index 0000000000..e9b31bb6c0 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java @@ -0,0 +1,31 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.AxisValueFormatter; + +/** + * Created by Philipp Jahoda on 14/09/15. + */ +public class YearXAxisFormatter implements AxisValueFormatter { + + protected 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 + + } + + @Override + 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/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java index c5c3445207..2c717ea6de 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 @@ -37,6 +37,11 @@ public abstract class AxisBase extends ComponentBase { */ public float[] mEntries = new float[]{}; + /** + * axis label entries only used for centered labels + */ + public float[] mCenteredEntries = new float[]{}; + /** * the number of entries the legend contains */ @@ -85,6 +90,8 @@ public abstract class AxisBase extends ComponentBase { */ protected boolean mDrawLabels = true; + protected boolean mCenterAxisLabels = false; + /** * the path effect of the grid lines that makes dashed lines possible */ @@ -171,6 +178,14 @@ public boolean isDrawAxisLineEnabled() { return mDrawAxisLine; } + public void setCenterAxisLabels(boolean enabled) { + mCenterAxisLabels = enabled; + } + + public boolean isCenterAxisLabelsEnabled() { + return mCenterAxisLabels && mEntryCount > 1; + } + /** * Sets the color of the grid lines for this axis (the horizontal lines * coming from each label). diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java index a19711ebef..3e796f5365 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java @@ -41,12 +41,6 @@ public class XAxis extends AxisBase { */ protected float mLabelRotationAngle = 0f; - /** - * the space that should be left out (in characters) between the xPx-axis - * labels - */ - private int mSpaceBetweenLabels = 4; - /** * the modulus that indicates if a yValue at a specified index in an * array(list) for the xPx-axis-labels is drawn or not. If index % modulus == 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 08187c2061..0bc79d56e9 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 @@ -92,8 +92,11 @@ public void groupBars(float fromX, float groupSpace, float barSpace) { float barSpaceHalf = barSpace / 2f; float barWidthHalf = mBarWidth / 2f; + float interval = getIntervalWidth(groupSpace, barSpace); + for (int i = 0; i < maxEntryCount; i++) { + float start = fromX; fromX += groupSpaceWidthHalf; for (IBarDataSet set : mDataSets) { @@ -115,6 +118,14 @@ public void groupBars(float fromX, float groupSpace, float barSpace) { } fromX += groupSpaceWidthHalf; + float end = fromX; + float innerInterval = end - start; + float diff = interval - innerInterval; + + // correct rounding errors + if (diff > 0 || diff < 0) { + fromX += diff; + } } notifyDataChanged(); 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 644267cbac..c52b1cb0bb 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 @@ -14,7 +14,7 @@ /** * Baseclass of all axis renderers. - * + * * @author Philipp Jahoda */ public abstract class AxisRenderer extends Renderer { @@ -23,19 +23,27 @@ public abstract class AxisRenderer extends Renderer { protected Transformer mTrans; - /** paint object for the grid lines */ + /** + * paint object for the grid lines + */ protected Paint mGridPaint; - /** paint for the xPx-label values */ + /** + * paint for the xPx-label values + */ protected Paint mAxisLabelPaint; - /** paint for the line surrounding the chart */ + /** + * paint for the line surrounding the chart + */ protected Paint mAxisLinePaint; - /** paint used for the limit lines */ - protected Paint mLimitLinePaint; + /** + * paint used for the limit lines + */ + protected Paint mLimitLinePaint; - public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans, AxisBase axis) { + public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans, AxisBase axis) { super(viewPortHandler); this.mTrans = trans; @@ -54,13 +62,13 @@ public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans, AxisBase mAxisLinePaint.setStrokeWidth(1f); mAxisLinePaint.setStyle(Style.STROKE); - mLimitLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mLimitLinePaint.setStyle(Paint.Style.STROKE); - } + mLimitLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mLimitLinePaint.setStyle(Paint.Style.STROKE); + } /** * Returns the Paint object used for drawing the axis (labels). - * + * * @return */ public Paint getPaintAxisLabels() { @@ -70,7 +78,7 @@ public Paint getPaintAxisLabels() { /** * Returns the Paint object that is used for drawing the grid-lines of the * axis. - * + * * @return */ public Paint getPaintGrid() { @@ -80,7 +88,7 @@ public Paint getPaintGrid() { /** * Returns the Paint object that is used for drawing the axis-line that goes * alongside the axis. - * + * * @return */ public Paint getPaintAxisLine() { @@ -89,7 +97,7 @@ public Paint getPaintAxisLine() { /** * Returns the Transformer object used for transforming the axis values. - * + * * @return */ public Transformer getTransformer() { @@ -162,6 +170,9 @@ 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()) { @@ -180,16 +191,21 @@ protected void computeAxisValues(float min, float max) { v += step; } + n = labelCount; + // no forced count } else { - double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; + if(centeringEnabled) { + first -= interval; + } + double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); double f; int i; - int n = 0; + if (interval != 0.0) { for (f = first; f <= last; f += interval) { ++n; @@ -218,33 +234,46 @@ protected void computeAxisValues(float min, float max) { } else { mAxis.mDecimals = 0; } + + if (centeringEnabled) { + + if (mAxis.mCenteredEntries.length < n) { + mAxis.mCenteredEntries = new float[n]; + } + + float offset = (mAxis.mEntries[1] - mAxis.mEntries[0]) / 2f; + + for (int i = 0; i < n; i++) { + mAxis.mCenteredEntries[i] = mAxis.mEntries[i] + offset; + } + } } /** * Draws the axis labels to the screen. - * + * * @param c */ public abstract void renderAxisLabels(Canvas c); /** * Draws the grid lines belonging to the axis. - * + * * @param c */ public abstract void renderGridLines(Canvas c); /** * Draws the line that goes alongside the axis. - * + * * @param c */ public abstract void renderAxisLine(Canvas c); - /** - * Draws the LimitLines associated with this axis to the screen. - * - * @param c - */ - public abstract void renderLimitLines(Canvas c); + /** + * Draws the LimitLines associated with this axis to the screen. + * + * @param c + */ + public abstract void renderLimitLines(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 2ba4b57b8f..0f13346701 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 @@ -187,12 +187,18 @@ public void renderAxisLine(Canvas c) { protected void drawLabels(Canvas c, float pos, PointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); + boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); float[] positions = new float[mXAxis.mEntryCount * 2]; for (int i = 0; i < positions.length; i += 2) { + // only fill xPx values - positions[i] = mXAxis.mEntries[i / 2]; + if (centeringEnabled) { + positions[i] = mXAxis.mCenteredEntries[i / 2]; + } else { + positions[i] = mXAxis.mEntries[i / 2]; + } } mTrans.pointValuesToPixel(positions); @@ -203,7 +209,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { if (mViewPortHandler.isInBoundsX(x)) { - String label = String.valueOf(mXAxis.mEntries[i / 2]); + String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis); if (mXAxis.isAvoidFirstLastClippingEnabled()) { @@ -223,13 +229,12 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { } } - drawLabel(c, mXAxis.mEntries[i / 2], x, pos, anchor, labelRotationAngleDegrees); + drawLabel(c, label, x, pos, anchor, labelRotationAngleDegrees); } } } - protected void drawLabel(Canvas c, float xValue, float x, float y, PointF anchor, float angleDegrees) { - String formattedLabel = mXAxis.getValueFormatter().getFormattedValue(xValue, mXAxis); + protected void drawLabel(Canvas c, String formattedLabel, float x, float y, PointF anchor, float angleDegrees) { Utils.drawXAxisValue(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java index 0c16dc2a65..ad36599444 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java @@ -22,92 +22,62 @@ public XAxisRendererBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, Trans this.mChart = chart; } - /** - * draws the xPx-labels on the specified yPx-position - * - * @param pos - */ - @Override - protected void drawLabels(Canvas c, float pos, PointF anchor) { - - final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); - - // pre allocate to save performance (dont allocate in loop) - float[] position = new float[] { - 0f, 0f - }; - - BarData bd = mChart.getData(); - int step = bd.getDataSetCount(); - -// for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { -// -// position[0] = i * step + i * bd.getGroupSpace() -// + bd.getGroupSpace() / 2f; -// -// // consider groups (center label for each group) -// if (step > 1) { -// position[0] += ((float) step - 1f) / 2f; -// } -// -// mTrans.pointValuesToPixel(position); -// -// if (mViewPortHandler.isInBoundsX(position[0]) && i >= 0 -// && i < mXAxis.getValues().size()) { -// -// String label = mXAxis.getValues().get(i).getLabel(); -// -// if (mXAxis.isAvoidFirstLastClippingEnabled()) { -// -// // avoid clipping of the last -// if (i == mXAxis.getValues().size() - 1) { -// float width = Utils.calcTextWidth(mAxisLabelPaint, label); -// -// if (position[0] + width / 2.f > mViewPortHandler.contentRight()) -// position[0] = mViewPortHandler.contentRight() - (width / 2.f); -// -// // avoid clipping of the first -// } else if (i == 0) { -// -// float width = Utils.calcTextWidth(mAxisLabelPaint, label); -// -// if (position[0] - width / 2.f < mViewPortHandler.contentLeft()) -// position[0] = mViewPortHandler.contentLeft() + (width / 2.f); -// } -// } -// -// drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees); -// } -// } - } - - @Override - public void renderGridLines(Canvas c) { - - if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) - return; - - float[] position = new float[] { - 0f, 0f - }; - - mGridPaint.setColor(mXAxis.getGridColor()); - mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); - - BarData bd = mChart.getData(); - int step = bd.getDataSetCount(); - -// for (int i = mMinX; i < mMaxX; i += mXAxis.mAxisLabelModulus) { -// -// position[0] = i * step + i * bd.getGroupSpace() - 0.5f; -// -// mTrans.pointValuesToPixel(position); -// -// if (mViewPortHandler.isInBoundsX(position[0])) { -// -// c.drawLine(position[0], mViewPortHandler.offsetTop(), position[0], -// mViewPortHandler.contentBottom(), mGridPaint); -// } -// } - } +// /** +// * draws the xPx-labels on the specified yPx-position +// * +// * @param pos +// */ +// @Override +// protected void drawLabels(Canvas c, float pos, PointF anchor) { +// +// final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); +// +// // pre allocate to save performance (dont allocate in loop) +// float[] position = new float[] { +// 0f, 0f +// }; +// +// BarData bd = mChart.getData(); +// int step = bd.getDataSetCount(); +// +//// for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { +//// +//// position[0] = i * step + i * bd.getGroupSpace() +//// + bd.getGroupSpace() / 2f; +//// +//// // consider groups (center label for each group) +//// if (step > 1) { +//// position[0] += ((float) step - 1f) / 2f; +//// } +//// +//// mTrans.pointValuesToPixel(position); +//// +//// if (mViewPortHandler.isInBoundsX(position[0]) && i >= 0 +//// && i < mXAxis.getValues().size()) { +//// +//// String label = mXAxis.getValues().get(i).getLabel(); +//// +//// if (mXAxis.isAvoidFirstLastClippingEnabled()) { +//// +//// // avoid clipping of the last +//// if (i == mXAxis.getValues().size() - 1) { +//// float width = Utils.calcTextWidth(mAxisLabelPaint, label); +//// +//// if (position[0] + width / 2.f > mViewPortHandler.contentRight()) +//// position[0] = mViewPortHandler.contentRight() - (width / 2.f); +//// +//// // avoid clipping of the first +//// } else if (i == 0) { +//// +//// float width = Utils.calcTextWidth(mAxisLabelPaint, label); +//// +//// if (position[0] - width / 2.f < mViewPortHandler.contentLeft()) +//// position[0] = mViewPortHandler.contentLeft() + (width / 2.f); +//// } +//// } +//// +//// drawLabel(c, label, i, position[0], pos, 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 0f861aaa72..6c505bc5ba 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 @@ -42,14 +42,14 @@ public void renderAxisLabels(Canvas c) { int mod = mXAxis.mAxisLabelModulus; for (int i = 0; i < mXAxis.mEntryCount; i += mod) { - String label = String.valueOf(mXAxis.mEntries[i]); + String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i], mXAxis); float angle = (sliceangle * i + mChart.getRotationAngle()) % 360f; PointF p = Utils.getPosition(center, mChart.getYRange() * factor + mXAxis.mLabelRotatedWidth / 2f, angle); - drawLabel(c, 0f, p.x, p.y - mXAxis.mLabelRotatedHeight / 2.f, + drawLabel(c, label, p.x, p.y - mXAxis.mLabelRotatedHeight / 2.f, drawLabelAnchor, labelRotationAngleDegrees); } } From 5292cfc8184ae7bde2393bc0c1fdcc708f892f31 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 14:59:06 +0200 Subject: [PATCH 154/606] Improve examples --- .../mpchartexample/BarChartActivity.java | 108 ++++++++++++++++-- .../mpchartexample/ScatterChartActivity.java | 2 - .../custom/MonthXAxisFormatter.java | 31 ----- 3 files changed, 100 insertions(+), 41 deletions(-) delete mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 561756882d..6c8fcca1c2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -16,6 +16,7 @@ import android.widget.Toast; 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.LegendForm; import com.github.mikephil.charting.components.Legend.LegendPosition; @@ -85,6 +86,8 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setPosition(XAxisPosition.BOTTOM); xAxis.setTypeface(mTf); xAxis.setDrawGridLines(false); + xAxis.setGranularity(1f); // only intervals of 1 day + xAxis.setValueFormatter(new DayAxisFormatter()); AxisValueFormatter custom = new MyAxisValueFormatter(); @@ -145,7 +148,7 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { + if (mChart.getData() != null) { mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); mChart.invalidate(); } @@ -167,7 +170,7 @@ 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; @@ -230,12 +233,14 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - mChart.getXAxis().setAxisMinValue(0f); - mChart.getXAxis().setAxisMaxValue(count+1f); + float start = 0.5f; + + mChart.getXAxis().setAxisMinValue(start); + mChart.getXAxis().setAxisMaxValue(start + count); ArrayList yVals1 = new ArrayList(); - for (int i = 0; i < count; i++) { + for (int i = (int) start; i < start + count; i++) { float mult = (range + 1); float val = (float) (Math.random() * mult); yVals1.add(new BarEntry(i + 1f, val)); @@ -245,12 +250,12 @@ private void setData(int count, float range) { if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { - set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); + set1 = (BarDataSet) mChart.getData().getDataSetByIndex(0); set1.setYVals(yVals1); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { - set1 = new BarDataSet(yVals1, "DataSet"); + set1 = new BarDataSet(yVals1, "The year 2017"); set1.setColors(ColorTemplate.MATERIAL_COLORS); ArrayList dataSets = new ArrayList(); @@ -284,5 +289,92 @@ public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { } public void onNothingSelected() { - }; + } + + private class DayAxisFormatter implements AxisValueFormatter { + + protected String[] mMonths = new String[]{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" + }; + + @Override + public String getFormattedValue(float value, AxisBase axis) { + + int dayOfYear = (int) value; + + int month = determineMonth(dayOfYear); + int dayOfMonth = determineDayOfMonth(dayOfYear, month); + + String appendix = "th"; + + switch (dayOfMonth) { + case 1: + appendix = "st"; + break; + case 2: + appendix = "nd"; + break; + case 3: + appendix = "rd"; + break; + case 21: + appendix = "st"; + break; + case 22: + appendix = "nd"; + break; + case 23: + appendix = "rd"; + break; + case 31: + appendix = "st"; + break; + } + + return dayOfMonth + appendix + " " + mMonths[month % mMonths.length]; + } + + private int getDaysForMonth(int month) { + + if (month == 1) { + return 28; + } + + if (month == 3 || month == 5 || month == 8 || month == 10) + return 30; + else + return 31; + } + + private int determineMonth(int dayOfYear) { + + int month = -1; + int days = 0; + + while (days < dayOfYear) { + month++; + days += getDaysForMonth(month); + } + + return month; + } + + private int determineDayOfMonth(int dayOfYear, int month) { + + int count = 0; + int days = 0; + + while (count < month) { + days += getDaysForMonth(count); + count++; + } + + return dayOfYear - days; + } + + @Override + public int getDecimalDigits() { + return 0; + } + } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index 916b211fd9..0bac51c1c6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -24,7 +24,6 @@ import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.custom.MonthXAxisFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -89,7 +88,6 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xl = mChart.getXAxis(); xl.setTypeface(tf); xl.setDrawGridLines(false); - xl.setValueFormatter(new MonthXAxisFormatter()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java deleted file mode 100644 index ffa49ec24f..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MonthXAxisFormatter.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.custom; - -import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.formatter.AxisValueFormatter; - -/** - * Created by Philipp Jahoda on 14/09/15. - */ -public class MonthXAxisFormatter implements AxisValueFormatter { - - protected String[] mMonths = new String[]{ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" - }; - - public MonthXAxisFormatter() { - // maybe do something here or provide parameters in constructor - - } - - @Override - public String getFormattedValue(float value, AxisBase axis) { - - float percent = value / axis.mAxisRange; - return mMonths[(int) (mMonths.length * percent)]; - } - - @Override - public int getDecimalDigits() { - return 0; - } -} From ec880022ab3d214255e23a18c45912e5b192f019 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 15:53:03 +0200 Subject: [PATCH 155/606] Fine tuning of days example --- .../mpchartexample/BarChartActivity.java | 72 +++++++++++-------- .../charting/charts/BarLineChartBase.java | 12 +++- 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 6c8fcca1c2..3979f4983a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -35,6 +35,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.ViewPortHandler; import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -87,6 +88,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTypeface(mTf); xAxis.setDrawGridLines(false); xAxis.setGranularity(1f); // only intervals of 1 day + xAxis.setLabelCount(7); xAxis.setValueFormatter(new DayAxisFormatter()); AxisValueFormatter custom = new MyAxisValueFormatter(); @@ -233,7 +235,7 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - float start = 0.5f; + float start = 0f; mChart.getXAxis().setAxisMinValue(start); mChart.getXAxis().setAxisMaxValue(start + count); @@ -299,39 +301,47 @@ private class DayAxisFormatter implements AxisValueFormatter { @Override public String getFormattedValue(float value, AxisBase axis) { - + int dayOfYear = (int) value; int month = determineMonth(dayOfYear); - int dayOfMonth = determineDayOfMonth(dayOfYear, month); - - String appendix = "th"; - - switch (dayOfMonth) { - case 1: - appendix = "st"; - break; - case 2: - appendix = "nd"; - break; - case 3: - appendix = "rd"; - break; - case 21: - appendix = "st"; - break; - case 22: - appendix = "nd"; - break; - case 23: - appendix = "rd"; - break; - case 31: - appendix = "st"; - break; - } + String monthName = mMonths[month % mMonths.length]; + + if (mChart.getVisibleXRange() > 30 * axis.getLabelCount()) { + + return monthName; + } else { + + int dayOfMonth = determineDayOfMonth(dayOfYear, month); + + String appendix = "th"; + + switch (dayOfMonth) { + case 1: + appendix = "st"; + break; + case 2: + appendix = "nd"; + break; + case 3: + appendix = "rd"; + break; + case 21: + appendix = "st"; + break; + case 22: + appendix = "nd"; + break; + case 23: + appendix = "rd"; + break; + case 31: + appendix = "st"; + break; + } - return dayOfMonth + appendix + " " + mMonths[month % mMonths.length]; + return dayOfMonth + appendix + " " + monthName; + } } private int getDaysForMonth(int month) { @@ -356,7 +366,7 @@ private int determineMonth(int dayOfYear) { days += getDaysForMonth(month); } - return month; + return Math.max(month, 0); } private int determineDayOfMonth(int dayOfYear, int month) { 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 bac69f2633..2c31f9ba92 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 @@ -717,7 +717,8 @@ public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, floa PointD origin = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - Runnable job = new AnimatedZoomJob(mViewPortHandler, this, getTransformer(axis), getAxis(axis), mXAxis.mAxisRange, scaleX, scaleY, mViewPortHandler.getScaleX(), mViewPortHandler.getScaleY(), + Runnable job = new AnimatedZoomJob(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); @@ -1328,6 +1329,15 @@ public float getHighestVisibleX() { return (float) Math.min(mXAxis.mAxisMaximum, pos.x); } + /** + * Returns the range visible on the x-axis. + * + * @return + */ + public float getVisibleXRange() { + return Math.abs(getHighestVisibleX() - getLowestVisibleX()); + } + /** * returns the current xPx-scale factor */ From 11212eb72dcc67bbbed4c09928f98b580b2fc524 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 17:16:34 +0200 Subject: [PATCH 156/606] Write example of day axis formatter --- .../mpchartexample/BarChartActivity.java | 112 +------------ .../custom/DayAxisValueFormatter.java | 147 ++++++++++++++++++ 2 files changed, 155 insertions(+), 104 deletions(-) create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 3979f4983a..d77962f5cf 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -36,6 +36,7 @@ import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.ViewPortHandler; +import com.xxmassdeveloper.mpchartexample.custom.DayAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -89,7 +90,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setDrawGridLines(false); xAxis.setGranularity(1f); // only intervals of 1 day xAxis.setLabelCount(7); - xAxis.setValueFormatter(new DayAxisFormatter()); + xAxis.setValueFormatter(new DayAxisValueFormatter(mChart)); AxisValueFormatter custom = new MyAxisValueFormatter(); @@ -214,23 +215,21 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 1)); + tvX.setText("" + (mSeekBarX.getProgress() + 2)); tvY.setText("" + (mSeekBarY.getProgress())); - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); + setData(mSeekBarX.getProgress() + 1 , mSeekBarY.getProgress()); 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) { @@ -238,11 +237,11 @@ private void setData(int count, float range) { float start = 0f; mChart.getXAxis().setAxisMinValue(start); - mChart.getXAxis().setAxisMaxValue(start + count); + mChart.getXAxis().setAxisMaxValue(start + count + 2); ArrayList yVals1 = new ArrayList(); - for (int i = (int) start; i < start + count; i++) { + 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)); @@ -290,101 +289,6 @@ public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + mChart.getHighestVisibleX()); } - public void onNothingSelected() { - } - - private class DayAxisFormatter implements AxisValueFormatter { - - protected String[] mMonths = new String[]{ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" - }; - - @Override - public String getFormattedValue(float value, AxisBase axis) { - - int dayOfYear = (int) value; - - int month = determineMonth(dayOfYear); - String monthName = mMonths[month % mMonths.length]; - - if (mChart.getVisibleXRange() > 30 * axis.getLabelCount()) { - - return monthName; - } else { - - int dayOfMonth = determineDayOfMonth(dayOfYear, month); - - String appendix = "th"; - - switch (dayOfMonth) { - case 1: - appendix = "st"; - break; - case 2: - appendix = "nd"; - break; - case 3: - appendix = "rd"; - break; - case 21: - appendix = "st"; - break; - case 22: - appendix = "nd"; - break; - case 23: - appendix = "rd"; - break; - case 31: - appendix = "st"; - break; - } - - return dayOfMonth + appendix + " " + monthName; - } - } - - private int getDaysForMonth(int month) { - - if (month == 1) { - return 28; - } - - if (month == 3 || month == 5 || month == 8 || month == 10) - return 30; - else - return 31; - } - - private int determineMonth(int dayOfYear) { - - int month = -1; - int days = 0; - - while (days < dayOfYear) { - month++; - days += getDaysForMonth(month); - } - - return Math.max(month, 0); - } - - private int determineDayOfMonth(int dayOfYear, int month) { - - int count = 0; - int days = 0; - - while (count < month) { - days += getDaysForMonth(count); - count++; - } - - return dayOfYear - days; - } - - @Override - public int getDecimalDigits() { - return 0; - } - } + @Override + public void onNothingSelected() { } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java new file mode 100644 index 0000000000..4299f48bce --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -0,0 +1,147 @@ +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; + +/** + * Created by philipp on 02/06/16. + */ +public class DayAxisValueFormatter implements AxisValueFormatter { + + protected String[] mMonths = new String[]{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" + }; + + private BarLineChartBase chart; + + public DayAxisValueFormatter(BarLineChartBase chart) { + this.chart = chart; + } + + @Override + public String getFormattedValue(float value, AxisBase axis) { + + int days = (int) value; + + if (days == 0) + return ""; + + int year = determineYear(days); + + int month = determineMonth(days); + String monthName = mMonths[month % mMonths.length]; + String yearName = String.valueOf(year); + + if (year == 2017) { + System.out.println(""); + } + + if (chart.getVisibleXRange() > 30 * axis.getLabelCount()) { + + return monthName + " " + yearName; + } else { + + int dayOfMonth = determineDayOfMonth(days, month + 12 * (year - 2016)); + + String appendix = "th"; + + switch (dayOfMonth) { + case 1: + appendix = "st"; + break; + case 2: + appendix = "nd"; + break; + case 3: + appendix = "rd"; + break; + case 21: + appendix = "st"; + break; + case 22: + appendix = "nd"; + break; + case 23: + appendix = "rd"; + break; + case 31: + appendix = "st"; + break; + } + + return dayOfMonth + appendix + " " + monthName; + } + } + + private int getDaysForMonth(int month, int year) { + + if (month == 1) { + + if (year == 2016 || year == 2020) + return 29; + else + return 28; + } + + if (month == 3 || month == 5 || month == 8 || month == 10) + return 30; + else + return 31; + } + + private int determineMonth(int dayOfYear) { + + int month = -1; + int days = 0; + + while (days < dayOfYear) { + month = month + 1; + + if (month >= 12) + month = 0; + + int year = determineYear(days); + days += getDaysForMonth(month, year); + } + + return Math.max(month, 0); + } + + private int determineDayOfMonth(int dayOfYear, int month) { + + int count = 0; + int days = 0; + + while (count < month) { + + int year = determineYear(days); + days += getDaysForMonth(count % 12, year); + count++; + } + + return dayOfYear - days; + } + + private int determineYear(int days) { + + if (days <= 366) + return 2016; + else if (days <= 730) + return 2017; + else if (days <= 1094) + return 2018; + else if (days <= 1458) + return 2019; + else + return 2020; + + } + + @Override + public int getDecimalDigits() { + return 0; + } +} From 0632fedfd287e43c91195ca855c327305dbd801d Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 19:16:05 +0200 Subject: [PATCH 157/606] Work on improving example --- .../HorizontalBarChartActivity.java | 12 +-- .../InvertedLineChartActivity.java | 18 ++-- .../ListViewBarChartActivity.java | 2 +- .../StackedBarActivityNegative.java | 43 ++++++--- .../mikephil/charting/charts/BarChart.java | 3 - .../renderer/XAxisRendererBarChart.java | 34 +++---- .../XAxisRendererHorizontalBarChart.java | 89 ++++++++++++++++--- ...xComparator.java => EntryXComparator.java} | 4 +- 8 files changed, 138 insertions(+), 67 deletions(-) rename MPChartLib/src/main/java/com/github/mikephil/charting/utils/{EntryXIndexComparator.java => EntryXComparator.java} (76%) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index afe35d1e10..b571fc7d2b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -75,12 +75,8 @@ protected void onCreate(Bundle savedInstanceState) { // draw shadows for each bar that show the maximum yValue // mChart.setDrawBarShadow(true); - // mChart.setDrawXLabels(false); - mChart.setDrawGridBackground(false); - // mChart.setDrawYLabels(false); - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); XAxis xl = mChart.getXAxis(); @@ -89,6 +85,7 @@ protected void onCreate(Bundle savedInstanceState) { xl.setDrawAxisLine(true); xl.setDrawGridLines(true); xl.setGridLineWidth(0.3f); + xl.setGranularity(10f); YAxis yl = mChart.getAxisLeft(); yl.setTypeface(tf); @@ -232,11 +229,13 @@ public void onStopTrackingTouch(SeekBar seekBar) { 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); - yVals1.add(new BarEntry(i, val)); + yVals1.add(new BarEntry(i * spaceForBar, val)); } BarDataSet set1; @@ -256,7 +255,7 @@ private void setData(int count, float range) { BarData data = new BarData(dataSets); data.setValueTextSize(10f); data.setValueTypeface(tf); - + data.setBarWidth(barWidth); mChart.setData(data); } } @@ -276,6 +275,7 @@ public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { Log.i("position", position.toString()); } + @Override public void onNothingSelected() { }; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index fe0a4cc6f1..0b6900637f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -22,10 +22,12 @@ import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.EntryXComparator; import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class InvertedLineChartActivity extends DemoBase implements OnSeekBarChangeListener, @@ -83,6 +85,7 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xl = mChart.getXAxis(); xl.setAvoidFirstLastClipping(true); + xl.setAxisMinValue(0f); YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setInverted(true); @@ -253,18 +256,19 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - ArrayList yVals = new ArrayList(); + ArrayList entries = 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(val, 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(yVals, "DataSet 1"); + LineDataSet set1 = new LineDataSet(entries, "DataSet 1"); set1.setLineWidth(1.5f); set1.setCircleRadius(4f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index ce58acaf41..0a998bce96 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java @@ -132,7 +132,7 @@ private BarData generateData(int cnt) { ArrayList entries = new ArrayList(); for (int i = 0; i < 12; i++) { - entries.add(new BarEntry((int) (Math.random() * 70) + 30, i)); + entries.add(new BarEntry(i, (float) (Math.random() * 70) + 30)); } BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index cbf4bb4fe5..9aa82fc614 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -71,6 +71,25 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setDrawGridLines(false); xAxis.setDrawAxisLine(false); xAxis.setTextSize(9f); + xAxis.setAxisMinValue(0f); + xAxis.setAxisMaxValue(110f); + xAxis.setCenterAxisLabels(true); + xAxis.setLabelCount(12); + xAxis.setGranularity(10f); + xAxis.setValueFormatter(new AxisValueFormatter() { + + private DecimalFormat format = new DecimalFormat("###"); + + @Override + 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(); l.setPosition(LegendPosition.BELOW_CHART_RIGHT); @@ -80,17 +99,17 @@ protected void onCreate(Bundle savedInstanceState) { // 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(0, new float[]{ -10, 10 })); - yValues.add(new BarEntry(1, new float[]{ -12, 13 })); - yValues.add(new BarEntry(2, new float[]{ -15, 15 })); - yValues.add(new BarEntry(3, new float[]{ -17, 17 })); - yValues.add(new BarEntry(4, new float[]{ -19, 20 })); - yValues.add(new BarEntry(5, new float[]{ -19, 19 })); - yValues.add(new BarEntry(6, new float[]{ -16, 16 })); - yValues.add(new BarEntry(7, new float[]{ -13, 14 })); - yValues.add(new BarEntry(8, new float[]{ -10, 11 })); - yValues.add(new BarEntry(9, new float[]{ -5, 6 })); - yValues.add(new BarEntry(10, new float[]{ -1, 2 })); + 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(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"); set.setValueFormatter(new CustomFormatter()); @@ -104,7 +123,7 @@ protected void onCreate(Bundle savedInstanceState) { 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(0.8f); + data.setBarWidth(8.5f); mChart.setData(data); mChart.invalidate(); } 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 2c387c8488..050c1be8d2 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 @@ -5,7 +5,6 @@ import android.util.AttributeSet; import android.util.Log; -import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.highlight.BarHighlighter; @@ -13,7 +12,6 @@ import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.renderer.BarChartRenderer; -import com.github.mikephil.charting.renderer.XAxisRendererBarChart; /** * Chart that draws bars. @@ -52,7 +50,6 @@ protected void init() { super.init(); mRenderer = new BarChartRenderer(this, mAnimator, mViewPortHandler); - mXAxisRenderer = new XAxisRendererBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer, this); setHighlighter(new BarHighlighter(this)); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java index ad36599444..0be78ca0e9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java @@ -1,27 +1,17 @@ package com.github.mikephil.charting.renderer; -import android.graphics.Canvas; -import android.graphics.PointF; - -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -public class XAxisRendererBarChart extends XAxisRenderer { - - protected BarChart mChart; - - public XAxisRendererBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans, - BarChart chart) { - super(viewPortHandler, xAxis, trans); - - this.mChart = chart; - } - +//public class XAxisRendererBarChart extends XAxisRenderer { +// +// protected BarChart mChart; +// +// public XAxisRendererBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans, +// BarChart chart) { +// super(viewPortHandler, xAxis, trans); +// +// this.mChart = chart; +// } +// // /** // * draws the xPx-labels on the specified yPx-position // * @@ -80,4 +70,4 @@ public XAxisRendererBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, Trans //// } //// } // } -} +//} 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 9e37f729f0..4b40a35778 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 @@ -13,17 +13,46 @@ import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; import java.util.List; -public class XAxisRendererHorizontalBarChart extends XAxisRendererBarChart { +public class XAxisRendererHorizontalBarChart extends XAxisRenderer { + + protected BarChart mChart; public XAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans, BarChart chart) { - super(viewPortHandler, xAxis, trans, chart); + super(viewPortHandler, xAxis, trans); + + this.mChart = chart; + } + + @Override + public void computeAxis(float min, float max, boolean inverted) { + + // calculate the starting and entry point of the yPx-labels (depending on + // zoom / contentrect bounds) + if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { + + PointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); + PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); + + if (inverted) { + + min = (float) p2.y; + max = (float) p1.y; + } else { + + min = (float) p1.y; + max = (float) p2.y; + } + } + + computeAxisValues(min, max); } @Override @@ -91,24 +120,56 @@ public void renderAxisLabels(Canvas c) { } } - /** - * draws the xPx-labels on the specified yPx-position - * - * @param pos - */ @Override protected void drawLabels(Canvas c, float pos, PointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); + boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); - // pre allocate to save performance (dont allocate in loop) - float[] position = new float[] { - 0f, 0f - }; + float[] positions = new float[mXAxis.mEntryCount * 2]; - BarData bd = mChart.getData(); - int step = bd.getDataSetCount(); + for (int i = 0; i < positions.length; i += 2) { + + // only fill xPx values + if (centeringEnabled) { + positions[i + 1] = mXAxis.mCenteredEntries[i / 2]; + } else { + positions[i + 1] = mXAxis.mEntries[i / 2]; + } + } + + mTrans.pointValuesToPixel(positions); + for (int i = 0; i < positions.length; i += 2) { + + float y = positions[i + 1]; + + if (mViewPortHandler.isInBoundsY(y)) { + + String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis); + drawLabel(c, label, pos, y, anchor, labelRotationAngleDegrees); + } + } + } + +// /** +// * draws the xPx-labels on the specified yPx-position +// * +// * @param pos +// */ +// @Override +// protected void drawLabels(Canvas c, float pos, PointF anchor) { +// +// final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); +// +// // pre allocate to save performance (dont allocate in loop) +// float[] position = new float[] { +// 0f, 0f +// }; +// +// BarData bd = mChart.getData(); +// int step = bd.getDataSetCount(); +// // for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { // // position[1] = i * step + i * bd.getGroupSpace() @@ -127,7 +188,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { // drawLabel(c, label, i, pos, position[1], anchor, labelRotationAngleDegrees); // } // } - } +// } @Override public void renderGridLines(Canvas c) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXIndexComparator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXComparator.java similarity index 76% rename from MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXIndexComparator.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXComparator.java index ed9045a25e..8f59c12d07 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXIndexComparator.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXComparator.java @@ -5,10 +5,10 @@ import java.util.Comparator; /** - * Comparator for comparing Entry-objects by their xPx-index. + * Comparator for comparing Entry-objects by their x-value. * Created by philipp on 17/06/15. */ -public class EntryXIndexComparator implements Comparator { +public class EntryXComparator implements Comparator { @Override public int compare(Entry entry1, Entry entry2) { float diff = entry1.getX() - entry2.getX(); From b4268fc5022e34394e79d99cf12d7e8f62521c12 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 19:22:24 +0200 Subject: [PATCH 158/606] Work on candlestick chart rendering --- .../renderer/CandleStickChartRenderer.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) 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 04b97f31cc..df1fb3da60 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 @@ -7,6 +7,8 @@ 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.data.DataSet; +import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; @@ -54,6 +56,8 @@ public void drawData(Canvas c) { @SuppressWarnings("ResourceAsColor") protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { + int entryCount = dataSet.getEntryCount(); + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); @@ -61,8 +65,15 @@ protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { float barSpace = dataSet.getBarSpace(); boolean showCandleBar = dataSet.getShowCandleBar(); - int minx = Math.max((int) mChart.getLowestVisibleX(), 0); - int maxx = Math.min((int) mChart.getHighestVisibleX(), dataSet.getEntryCount()); + float lowX = mChart.getLowestVisibleX(); + float highX = mChart.getHighestVisibleX(); + + CandleEntry entryFrom = dataSet.getEntryForXPos(lowX, DataSet.Rounding.DOWN); + CandleEntry entryTo = dataSet.getEntryForXPos(highX, DataSet.Rounding.UP); + + int diff = (entryFrom == entryTo) ? 1 : 0; + int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); + int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); mRenderPaint.setStrokeWidth(dataSet.getShadowWidth()); From a25290742df991004d4c6d4654295446cd00bdc0 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 19:49:01 +0200 Subject: [PATCH 159/606] Work on dynamic adding examples --- .../DynamicalAddingActivity.java | 74 ++++++++----------- .../LineChartActivityColored.java | 2 +- .../RealtimeLineChartActivity.java | 33 ++++++--- 3 files changed, 55 insertions(+), 54 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index 48aace6cb4..886a8f7ed2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -36,7 +36,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); mChart.setDescription(""); - + // add an empty data object mChart.setData(new LineData()); // mChart.getXAxis().setDrawLabels(false); @@ -50,42 +50,39 @@ protected void onCreate(Bundle savedInstanceState) { private void addEntry() { LineData data = mChart.getData(); - - if(data != null) { - ILineDataSet set = data.getDataSetByIndex(0); - // set.addEntry(...); // can be called as well + ILineDataSet set = data.getDataSetByIndex(0); + // set.addEntry(...); // can be called as well - if (set == null) { - set = createSet(); - data.addDataSet(set); - } + if (set == null) { + set = createSet(); + data.addDataSet(set); + } - // add a new xPx-yValue first - //data.addXValue(new XAxisValue(set.getEntryCount(), set.getEntryCount() + "")); - - // choose a random dataSet - int randomDataSetIndex = (int) (Math.random() * data.getDataSetCount()); - - data.addEntry(new Entry((float) (Math.random() * 10) + 50f, set.getEntryCount()), randomDataSetIndex); + // choose a random dataSet + int randomDataSetIndex = (int) (Math.random() * data.getDataSetCount()); + float yValue = (float) (Math.random() * 10) + 50f; - // let the chart know it's data has changed - mChart.notifyDataSetChanged(); - - mChart.setVisibleXRangeMaximum(6); - mChart.setVisibleYRangeMaximum(15, AxisDependency.LEFT); + data.addEntry(new Entry(data.getDataSetByIndex(randomDataSetIndex).getEntryCount(), yValue), randomDataSetIndex); + data.notifyDataChanged(); + + // let the chart know it's data has changed + mChart.notifyDataSetChanged(); + + mChart.setVisibleXRangeMaximum(6); + //mChart.setVisibleYRangeMaximum(15, AxisDependency.LEFT); // // // this automatically refreshes the chart (calls invalidate()) - mChart.moveViewTo(data.getEntryCount()-7, 50f, AxisDependency.LEFT); - } + mChart.moveViewTo(data.getEntryCount() - 7, 50f, AxisDependency.LEFT); + } private void removeLastEntry() { LineData data = mChart.getData(); - - if(data != null) { - + + if (data != null) { + ILineDataSet set = data.getDataSetByIndex(0); if (set != null) { @@ -95,7 +92,7 @@ private void removeLastEntry() { data.removeEntry(e, 0); // or remove by index // mData.removeEntry(xIndex, dataSetIndex); - + data.notifyDataChanged(); mChart.notifyDataSetChanged(); mChart.invalidate(); } @@ -105,23 +102,15 @@ private void removeLastEntry() { private void addDataSet() { LineData data = mChart.getData(); - - if(data != null) { + + if (data != null) { int count = (data.getDataSetCount() + 1); - // create 10 yPx-vals ArrayList yVals = new ArrayList(); - - if(data.getEntryCount() == 0) { - // add 10 xPx-entries - for (int i = 0; i < 10; i++) { - //data.addXValue(new XAxisValue(i, i + "")); - } - } for (int i = 0; i < data.getEntryCount(); i++) { - yVals.add(new Entry((float) (Math.random() * 50f) + 50f * count, i)); + yVals.add(new Entry(i, (float) (Math.random() * 50f) + 50f * count)); } LineDataSet set = new LineDataSet(yVals, "DataSet " + count); @@ -137,21 +126,22 @@ private void addDataSet() { set.setValueTextColor(color); data.addDataSet(set); + data.notifyDataChanged(); mChart.notifyDataSetChanged(); - mChart.invalidate(); + mChart.invalidate(); } } private void removeDataSet() { LineData data = mChart.getData(); - - if(data != null) { + + if (data != null) { data.removeDataSet(data.getDataSetByIndex(data.getDataSetCount() - 1)); mChart.notifyDataSetChanged(); - mChart.invalidate(); + mChart.invalidate(); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index c452dda6bb..2ae7d0e4df 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -104,7 +104,7 @@ private LineData getData(int count, float range) { for (int i = 0; i < count; i++) { float val = (float) (Math.random() * range) + 3; - yVals.add(new Entry(val, i)); + yVals.add(new Entry(i, val)); } // create a dataset and give it a type diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 833428e613..95f260bce9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -121,8 +121,6 @@ public boolean onOptionsItemSelected(MenuItem item) { return true; } - private int year = 2015; - private void addEntry() { LineData data = mChart.getData(); @@ -137,11 +135,8 @@ private void addEntry() { data.addDataSet(set); } - // add a new xPx-yValue first -// data.addXValue(new XAxisValue(data.getXValCount() ,mMonths[data.getXValCount() % 12] + " " -// + (year + data.getXValCount() / 12))); - data.addEntry(new Entry((float) (Math.random() * 40) + 30f, set.getEntryCount()), 0); - + data.addEntry(new Entry(set.getEntryCount(), (float) (Math.random() * 40) + 30f), 0); + data.notifyDataChanged(); // let the chart know it's data has changed mChart.notifyDataSetChanged(); @@ -176,13 +171,18 @@ private LineDataSet createSet() { return set; } + private Thread thread; + private void feedMultiple() { - new Thread(new Runnable() { + if(thread != null) + thread.interrupt(); + + thread = new Thread(new Runnable() { @Override public void run() { - for(int i = 0; i < 500; i++) { + for(int i = 0; i < 1000; i++) { runOnUiThread(new Runnable() { @@ -193,14 +193,16 @@ public void run() { }); try { - Thread.sleep(35); + Thread.sleep(25); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } - }).start(); + }); + + thread.start(); } @Override @@ -212,4 +214,13 @@ public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { public void onNothingSelected() { Log.i("Nothing selected", "Nothing selected."); } + + @Override + protected void onPause() { + super.onPause(); + + if(thread != null) { + thread.interrupt(); + } + } } From bfcbae4263b53a410305e81cc4f8c04b4e66633e Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 20:02:49 +0200 Subject: [PATCH 160/606] Fix issue related to barchart value labels --- .../BarChartPositiveNegative.java | 31 +++++++++++++++---- .../charting/renderer/BarChartRenderer.java | 3 +- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 029e7936f5..8dfad58845 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -7,6 +7,7 @@ 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; @@ -14,6 +15,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.utils.ViewPortHandler; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -58,6 +60,11 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setDrawAxisLine(false); xAxis.setTextColor(Color.LTGRAY); xAxis.setTextSize(13f); + xAxis.setAxisMinValue(0f); + xAxis.setAxisMaxValue(5f); + xAxis.setLabelCount(5); + xAxis.setCenterAxisLabels(true); + xAxis.setGranularity(1f); YAxis left = mChart.getAxisLeft(); left.setDrawLabels(false); @@ -73,12 +80,24 @@ protected void onCreate(Bundle savedInstanceState) { mChart.getLegend().setEnabled(false); // THIS IS THE ORIGINAL DATA YOU WANT TO PLOT - List data = new ArrayList<>(); - data.add(new Data(0, -224.1f, "12-29")); - data.add(new Data(1, 238.5f, "12-30")); - data.add(new Data(2, 1280.1f, "12-31")); - data.add(new Data(3, -442.3f, "01-01")); - data.add(new Data(4, -2280.1f, "01-02")); + 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")); + + xAxis.setValueFormatter(new AxisValueFormatter() { + @Override + 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/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java index 7aeec4ce90..8c81fb5eba 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 @@ -236,7 +236,7 @@ public void drawValues(Canvas c) { float val = entry.getY(); drawValue(c, dataSet.getValueFormatter(), val, entry, i, x, - buffer.buffer[j + 1] + (val >= 0 ? posOffset : negOffset), dataSet.getValueTextColor + val >= 0 ? (buffer.buffer[j + 1] + posOffset) : (buffer.buffer[j + 3] + negOffset), dataSet.getValueTextColor (j / 4)); } @@ -327,7 +327,6 @@ public void drawValues(Canvas c) { public void drawHighlighted(Canvas c, Highlight[] indices) { BarData barData = mChart.getBarData(); - int setCount = barData.getDataSetCount(); for (Highlight high : indices) { From 4ae9950632a38aad0cf2083fa2b667ee02c3a90e Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 20:10:37 +0200 Subject: [PATCH 161/606] Work on radarchart rendering --- .../mpchartexample/BarChartPositiveNegative.java | 1 - .../github/mikephil/charting/charts/RadarChart.java | 5 ++--- .../charting/renderer/RadarChartRenderer.java | 11 +++-------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 8dfad58845..5e279db25f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -68,7 +68,6 @@ protected void onCreate(Bundle savedInstanceState) { YAxis left = mChart.getAxisLeft(); left.setDrawLabels(false); - left.setStartAtZero(false); left.setSpaceTop(25f); left.setSpaceBottom(25f); left.setDrawAxisLine(false); 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 b0adde7f69..6f2a19e79d 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 @@ -179,8 +179,7 @@ protected void onDraw(Canvas canvas) { */ public float getFactor() { RectF content = mViewPortHandler.getContentRect(); - return (float) Math.min(content.width() / 2f, content.height() / 2f) - / mYAxis.mAxisRange; + return Math.min(content.width() / 2f, content.height() / 2f) / mYAxis.mAxisRange; } /** @@ -189,7 +188,7 @@ public float getFactor() { * @return */ public float getSliceAngle() { - return 360f / (float) mData.getEntryCount(); + return 360f / (float) mData.getMaxEntryCountSet().getEntryCount(); } @Override 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 ffdd8685cb..e8c086c9d8 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 @@ -59,13 +59,7 @@ public void drawData(Canvas c) { RadarData radarData = mChart.getData(); - int mostEntries = 0; - - for (IRadarDataSet set : radarData.getDataSets()) { - if (set.getEntryCount() > mostEntries) { - mostEntries = set.getEntryCount(); - } - } + int mostEntries = radarData.getMaxEntryCountSet().getEntryCount(); for (IRadarDataSet set : radarData.getDataSets()) { @@ -217,8 +211,9 @@ protected void drawWeb(Canvas c) { mWebPaint.setAlpha(mChart.getWebAlpha()); final int xIncrements = 1 + mChart.getSkipWebLineCount(); + int maxEntryCount = mChart.getData().getMaxEntryCountSet().getEntryCount(); - for (int i = 0; i < mChart.getData().getEntryCount(); i += xIncrements) { + for (int i = 0; i < maxEntryCount; i += xIncrements) { PointF p = Utils.getPosition( center, From 639d3359093ac91f52e5da7718834b034ed491b9 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 22:57:48 +0200 Subject: [PATCH 162/606] Work on radarchart rendering, cleanup xaxis --- .../mpchartexample/RadarChartActivitry.java | 13 ++++ .../mpchartexample/ScrollViewActivity.java | 1 - .../charting/charts/BarLineChartBase.java | 30 -------- .../charting/charts/HorizontalBarChart.java | 13 ---- .../charting/charts/PieRadarChartBase.java | 2 +- .../mikephil/charting/charts/RadarChart.java | 8 ++- .../mikephil/charting/components/XAxis.java | 49 ------------- .../mikephil/charting/data/BaseEntry.java | 63 ++++++++++++++++ .../github/mikephil/charting/data/Entry.java | 71 ++++--------------- .../mikephil/charting/data/RadarData.java | 25 +++++++ .../charting/renderer/XAxisRenderer.java | 24 ------- .../renderer/XAxisRendererRadarChart.java | 10 +-- 12 files changed, 128 insertions(+), 181 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseEntry.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index 672fa2b261..66d9f537e5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -10,6 +10,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.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; @@ -17,6 +18,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.RadarData; import com.github.mikephil.charting.data.RadarDataSet; +import com.github.mikephil.charting.formatter.AxisValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -64,6 +66,17 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xAxis = mChart.getXAxis(); xAxis.setTypeface(tf); xAxis.setTextSize(9f); + xAxis.setValueFormatter(new AxisValueFormatter() { + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mMonths[(int) value % mMonths.length]; + } + + @Override + public int getDecimalDigits() { + return 0; + } + }); YAxis yAxis = mChart.getYAxis(); yAxis.setTypeface(tf); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java index 3d9f07f85d..0204147df7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java @@ -38,7 +38,6 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setLabelsToSkip(0); xAxis.setDrawGridLines(false); mChart.getAxisLeft().setDrawGridLines(false); 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 2c31f9ba92..2768ad89df 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 @@ -197,7 +197,6 @@ protected void onDraw(Canvas canvas) { return; long starttime = System.currentTimeMillis(); - calcModulus(); // execute all drawing commands drawGridBackground(canvas); @@ -523,35 +522,6 @@ public void calculateOffsets() { prepareValuePxMatrix(); } - /** - * calculates the modulus for xPx-labels and grid - */ - protected void calcModulus() { - -// if (mXAxis == null || !mXAxis.isEnabled()) -// return; -// -// if (!mXAxis.isAxisModulusCustom()) { -// -// float[] values = new float[9]; -// mViewPortHandler.getMatrixTouch().getValues(values); -// -// mXAxis.mAxisLabelModulus = (int) Math -// .ceil((mData.getXValCount() * mXAxis.mLabelRotatedWidth) -// / (mViewPortHandler.contentWidth() * values[Matrix.MSCALE_X])); -// -// } -// -// if (mLogEnabled) -// Log.i(LOG_TAG, "X-Axis modulus: " + mXAxis.mAxisLabelModulus + -// ", xPx-axis label width: " + mXAxis.mLabelWidth + -// ", xPx-axis label rotated width: " + mXAxis.mLabelRotatedWidth + -// ", content width: " + mViewPortHandler.contentWidth()); -// -// if (mXAxis.mAxisLabelModulus < 1) -// mXAxis.mAxisLabelModulus = 1; - } - @Override protected float[] getMarkerPosition(Entry e, Highlight highlight) { 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 d917b8547d..c51d102bda 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 @@ -131,19 +131,6 @@ protected void prepareValuePxMatrix() { mXAxis.mAxisMinimum); } - @Override - protected void calcModulus() { -// float[] values = new float[9]; -// mViewPortHandler.getMatrixTouch().getValues(values); -// -// mXAxis.mAxisLabelModulus = -// (int) Math.ceil((mData.getXValCount() * mXAxis.mLabelRotatedHeight) -// / (mViewPortHandler.contentHeight() * values[Matrix.MSCALE_Y])); -// -// if (mXAxis.mAxisLabelModulus < 1) -// mXAxis.mAxisLabelModulus = 1; - } - @Override public RectF getBarBounds(BarEntry e) { 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 c3d14c042a..9dbdfc8974 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 @@ -217,7 +217,7 @@ public void calculateOffsets() { XAxis x = this.getXAxis(); if (x.isEnabled() && x.isDrawLabelsEnabled()) { - minOffset = Math.max(minOffset, x.mLabelRotatedWidth); + minOffset = Math.max(minOffset, x.mLabelRotatedWidth * 2f); } } 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 6f2a19e79d..94af22eab0 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 @@ -104,6 +104,7 @@ protected void calcMinMax() { // mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); mYAxis.calculate(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); + mXAxis.calculate(0, mData.getMaxEntryCountSet().getEntryCount()); } @Override @@ -134,7 +135,6 @@ public void notifyDataSetChanged() { mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted()); mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); - //mXAxisRenderer.computeSize(mData.getXValMaximumLength(), mData.getXVals()); if (mLegend != null && !mLegend.isLegendCustom()) mLegendRenderer.computeLegend(mData); @@ -149,6 +149,12 @@ protected void onDraw(Canvas canvas) { if (mData == null) return; + if (mYAxis.isEnabled()) + mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted()); + + if (mXAxis.isEnabled()) + mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); + mXAxisRenderer.renderAxisLabels(canvas); if (mDrawWeb) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java index 3e796f5365..be48c164b7 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java @@ -41,18 +41,6 @@ public class XAxis extends AxisBase { */ protected float mLabelRotationAngle = 0f; - /** - * the modulus that indicates if a yValue at a specified index in an - * array(list) for the xPx-axis-labels is drawn or not. If index % modulus == - * 0 DRAW, else dont draw. - */ - public int mAxisLabelModulus = 1; - - /** - * Is axisLabelModulus a custom yValue or auto calculated? If false, then - * it's auto, if true, then custom. default: false (automatic modulus) - */ - private boolean mIsAxisModulusCustom = false; /** * if set to true, the chart will avoid that the first and last label entry * in the chart "clip" off the edge of the chart @@ -109,43 +97,6 @@ public void setLabelRotationAngle(float angle) { mLabelRotationAngle = angle; } - /** - * Sets the number of labels that should be skipped on the axis before the - * next label is drawn. This will disable the feature that automatically - * calculates an adequate space between the axis labels and set the number - * of labels to be skipped to the fixed number provided by this method. Call - * resetLabelsToSkip(...) to re-enable automatic calculation. - * - * @param count - */ - public void setLabelsToSkip(int count) { - - if (count < 0) - count = 0; - - mIsAxisModulusCustom = true; - mAxisLabelModulus = count + 1; - } - - /** - * Calling this will disable a custom number of labels to be skipped (set by - * setLabelsToSkip(...)) while drawing the xPx-axis. Instead, the number of - * values to skip will again be calculated automatically. - */ - public void resetLabelsToSkip() { - mIsAxisModulusCustom = false; - } - - /** - * Returns true if a custom axis-modulus has been set that determines the - * number of labels to skip when drawing. - * - * @return - */ - public boolean isAxisModulusCustom() { - return mIsAxisModulusCustom; - } - /** * if set to true, the chart will avoid that the first and last label entry * in the chart "clip" off the edge of the chart or the screen 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 new file mode 100644 index 0000000000..48e773edfe --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseEntry.java @@ -0,0 +1,63 @@ +package com.github.mikephil.charting.data; + +/** + * Created by Philipp Jahoda on 02/06/16. + */ +public abstract class BaseEntry { + + /** the y value */ + private float y = 0f; + + /** optional spot for additional data this Entry represents */ + private Object mData = null; + + public BaseEntry() { + + } + + public BaseEntry(float y) { + this.y = y; + } + + public BaseEntry(float y, Object data) { + this(y); + this.mData = data; + } + + /** + * Returns the yPx yValue of this Entry. + * + * @return + */ + public float getY() { + return y; + } + + /** + * Sets the yPx-yValue for the Entry. + * + * @param y + */ + public void setY(float y) { + this.y = y; + } + + /** + * Returns the data, additional information that this Entry represents, or + * null, if no data has been specified. + * + * @return + */ + public Object getData() { + return mData; + } + + /** + * Sets additional data this Entry should represent. + * + * @param data + */ + public void setData(Object data) { + this.mData = data; + } +} 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 834f46b202..7c23e500e2 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 @@ -11,17 +11,11 @@ * * @author Philipp Jahoda */ -public class Entry implements Parcelable { +public class Entry extends BaseEntry implements Parcelable { - /** the yPx yValue */ - private float y = 0f; - - /** the xPx yValue */ + /** the x value */ private float x = 0f; - /** optional spot for additional data this Entry represents */ - private Object mData = null; - public Entry() { } @@ -33,7 +27,7 @@ public Entry() { * @param y the yPx yValue (the actual yValue of the entry) */ public Entry(float x, float y) { - this.y = y; + super(y); this.x = x; } @@ -45,8 +39,8 @@ public Entry(float x, float y) { * @param data Spot for additional data this Entry represents. */ public Entry(float x, float y, Object data) { - this(x, y); - this.mData = data; + super(y, data); + this.x = x; } /** @@ -67,50 +61,13 @@ public void setX(float x) { this.x = x; } - /** - * Returns the yPx yValue of this Entry. - * - * @return - */ - public float getY() { - return y; - } - - /** - * Sets the yPx-yValue for the Entry. - * - * @param y - */ - public void setY(float y) { - this.y = y; - } - - /** - * Returns the data, additional information that this Entry represents, or - * null, if no data has been specified. - * - * @return - */ - public Object getData() { - return mData; - } - - /** - * Sets additional data this Entry should represent. - * - * @param data - */ - public void setData(Object data) { - this.mData = data; - } - /** * returns an exact copy of the entry * * @return */ public Entry copy() { - Entry e = new Entry(x, y, mData); + Entry e = new Entry(x, getY(), getData()); return e; } @@ -127,13 +84,13 @@ public boolean equalTo(Entry e) { if (e == null) return false; - if (e.mData != this.mData) + if (e.getData() != this.getData()) return false; if (Math.abs(e.x - this.x) > 0.000001f) return false; - if (Math.abs(e.y - this.y) > 0.000001f) + if (Math.abs(e.getY() - this.getY()) > 0.000001f) return false; return true; @@ -155,11 +112,11 @@ public int describeContents() { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeFloat(this.x); - dest.writeFloat(this.y); - if (mData != null) { - if (mData instanceof Parcelable) { + dest.writeFloat(this.getY()); + if (getData() != null) { + if (getData() instanceof Parcelable) { dest.writeInt(1); - dest.writeParcelable((Parcelable) this.mData, flags); + dest.writeParcelable((Parcelable) this.getData(), flags); } else { throw new ParcelFormatException("Cannot parcel an Entry with non-parcelable data"); } @@ -170,9 +127,9 @@ public void writeToParcel(Parcel dest, int flags) { protected Entry(Parcel in) { this.x = in.readFloat(); - this.y = in.readFloat(); + this.setY(in.readFloat()); if (in.readInt() == 1) { - this.mData = in.readParcelable(Object.class.getClassLoader()); + this.setData(in.readParcelable(Object.class.getClassLoader())); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java index bfb38c3e13..811414139a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java @@ -4,6 +4,7 @@ import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -13,6 +14,8 @@ */ public class RadarData extends ChartData { + private List mLabels; + public RadarData() { super(); } @@ -24,4 +27,26 @@ public RadarData(List dataSets) { public RadarData(IRadarDataSet... dataSets) { super(dataSets); } + + /** + * Sets the labels that should be drawn around the RadarChart at the end of each web line. + * + * @param labels + */ + public void setLabels(List labels) { + this.mLabels = labels; + } + + /** + * Sets the labels that should be drawn around the RadarChart at the end of each web line. + * + * @param labels + */ + public void setLabels(String... labels) { + this.mLabels = Arrays.asList(labels); + } + + public List getLabels() { + return mLabels; + } } 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 0f13346701..c9354056c0 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 @@ -59,30 +59,6 @@ public void computeAxis(float min, float max, boolean inverted) { @Override protected void computeAxisValues(float min, float max) { - -// int labelCount = mXAxis.getLabelCount(); -// float range = Math.abs(max - min); -// -// float interval = range / (labelCount - 1); -// -// if (mXAxis.mEntries == null || mXAxis.mEntries.length != labelCount) { -// mXAxis.mEntries = new float[labelCount]; -// mXAxis.mEntryCount = labelCount; -// } -// -// mXAxis.mEntries[0] = min; -// -// for (int i = 1; i < labelCount; i++) { -// mXAxis.mEntries[i] = min + interval * (float) i; -// } -// -// // set decimals -// if (interval < 1) { -// mXAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); -// } else { -// mXAxis.mDecimals = 0; -// } - super.computeAxisValues(min, max); computeSize(); 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 6c505bc5ba..8dd2191001 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 @@ -26,7 +26,7 @@ public void renderAxisLabels(Canvas c) { return; final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); - final PointF drawLabelAnchor = new PointF(0.5f, 0.0f); + final PointF drawLabelAnchor = new PointF(0.5f, 0.25f); mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); @@ -40,14 +40,14 @@ public void renderAxisLabels(Canvas c) { PointF center = mChart.getCenterOffsets(); - int mod = mXAxis.mAxisLabelModulus; - for (int i = 0; i < mXAxis.mEntryCount; i += mod) { - String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i], mXAxis); + for (int i = 0; i < mChart.getData().getMaxEntryCountSet().getEntryCount(); i++) { + + String label = mXAxis.getValueFormatter().getFormattedValue(i, mXAxis); float angle = (sliceangle * i + mChart.getRotationAngle()) % 360f; PointF p = Utils.getPosition(center, mChart.getYRange() * factor - + mXAxis.mLabelRotatedWidth / 2f, angle); + + mXAxis.mLabelRotatedWidth, angle); drawLabel(c, label, p.x, p.y - mXAxis.mLabelRotatedHeight / 2.f, drawLabelAnchor, labelRotationAngleDegrees); From 8751f608a4b74df01c31e19dd1e793795c202553 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 23:10:02 +0200 Subject: [PATCH 163/606] Work on radarchart rendering --- .../mikephil/charting/charts/RadarChart.java | 4 +- .../charting/renderer/AxisRenderer.java | 4 +- .../renderer/YAxisRendererRadarChart.java | 91 ++++++++++++------- 3 files changed, 64 insertions(+), 35 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 94af22eab0..9e5118d63b 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 @@ -149,8 +149,8 @@ protected void onDraw(Canvas canvas) { if (mData == null) return; - if (mYAxis.isEnabled()) - mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted()); +// if (mYAxis.isEnabled()) +// mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted()); if (mXAxis.isEnabled()) mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); 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 c52b1cb0bb..26260c320e 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 @@ -19,8 +19,10 @@ */ public abstract class AxisRenderer extends Renderer { - private AxisBase mAxis; + /** base axis this axis renderer works with */ + protected AxisBase mAxis; + /** transformer to transform values to screen pixels and return */ protected Transformer mTrans; /** 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 5dcaff1fcc..670c8a4eca 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 @@ -24,21 +24,30 @@ public YAxisRendererRadarChart(ViewPortHandler viewPortHandler, YAxis yAxis, Rad @Override protected void computeAxisValues(float min, float max) { + float yMin = min; float yMax = max; - int labelCount = mYAxis.getLabelCount(); + int labelCount = mAxis.getLabelCount(); double range = Math.abs(yMax - yMin); if (labelCount == 0 || range <= 0) { - mYAxis.mEntries = new float[]{}; - mYAxis.mEntryCount = 0; + mAxis.mEntries = new float[]{}; + mAxis.mEntryCount = 0; return; } + // Find out how much spacing (in yPx yValue space) between axis values double rawInterval = range / labelCount; double interval = Utils.roundToNextSignificant(rawInterval); - double intervalMagnitude = Math.pow(10, (int) Math.log10(interval)); + + // If granularity is enabled, then do not allow the interval to go below specified granularity. + // This is used to avoid repeated values when rounding values for display. + if (mAxis.isGranularityEnabled()) + interval = interval < mAxis.getGranularity() ? mAxis.getGranularity() : interval; + + // Normalize interval + 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 @@ -46,71 +55,89 @@ 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 (mYAxis.isForceLabelsEnabled()) { + if (mAxis.isForceLabelsEnabled()) { float step = (float) range / (float) (labelCount - 1); - mYAxis.mEntryCount = labelCount; + mAxis.mEntryCount = labelCount; - if (mYAxis.mEntries.length < labelCount) { + if (mAxis.mEntries.length < labelCount) { // Ensure stops contains at least numStops elements. - mYAxis.mEntries = new float[labelCount]; + mAxis.mEntries = new float[labelCount]; } float v = min; for (int i = 0; i < labelCount; i++) { - mYAxis.mEntries[i] = v; + mAxis.mEntries[i] = v; v += step; } + n = labelCount; + // no forced count } else { - final double rawCount = yMin / interval; - double first = rawCount < 0.0 ? Math.floor(rawCount) * interval : Math.ceil(rawCount) * interval; - - if (first == 0.0) // Fix for IEEE negative zero case (Where yValue == -0.0, and 0.0 == -0.0) - first = 0.0; + double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; + if (centeringEnabled) { + first -= interval; + } - double last = Utils.nextUp(Math.floor(yMax / interval) * interval); + double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); double f; int i; - int n = 0; - for (f = first; f <= last; f += interval) { - ++n; + + if (interval != 0.0) { + for (f = first; f <= last; f += interval) { + ++n; + } } - if (!mYAxis.isAxisMaxCustom()) - n += 1; + n++; - mYAxis.mEntryCount = n; + mAxis.mEntryCount = n; - if (mYAxis.mEntries.length < n) { + if (mAxis.mEntries.length < n) { // Ensure stops contains at least numStops elements. - mYAxis.mEntries = new float[n]; + mAxis.mEntries = new float[n]; } for (f = first, i = 0; i < n; f += interval, ++i) { - mYAxis.mEntries[i] = (float) f; + + if (f == 0.0) // Fix for negative zero case (Where yValue == -0.0, and 0.0 == -0.0) + f = 0.0; + + mAxis.mEntries[i] = (float) f; } } + // set decimals if (interval < 1) { - mYAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); + mAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); } else { - mYAxis.mDecimals = 0; + mAxis.mDecimals = 0; } - if (mYAxis.mEntries[0] < yMin) { - // If startAtZero is disabled, and the first label is lower that the axis minimum, - // Then adjust the axis minimum - mYAxis.mAxisMinimum = mYAxis.mEntries[0]; + if (centeringEnabled) { + + if (mAxis.mCenteredEntries.length < n) { + mAxis.mCenteredEntries = new float[n]; + } + + float offset = (mAxis.mEntries[1] - mAxis.mEntries[0]) / 2f; + + for (int i = 0; i < n; i++) { + mAxis.mCenteredEntries[i] = mAxis.mEntries[i] + offset; + } } - mYAxis.mAxisMaximum = mYAxis.mEntries[mYAxis.mEntryCount - 1]; - mYAxis.mAxisRange = Math.abs(mYAxis.mAxisMaximum - mYAxis.mAxisMinimum); + mAxis.mAxisMinimum = mAxis.mEntries[0]; + mAxis.mAxisMaximum = mAxis.mEntries[n-1]; + mAxis.mAxisRange = Math.abs(mAxis.mAxisMaximum - mAxis.mAxisMinimum); } @Override From 423b8857455597a37e3882d1d344168844a834c8 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 2 Jun 2016 23:43:16 +0200 Subject: [PATCH 164/606] Work on combined highlighting --- .../charting/highlight/ChartHighlighter.java | 2 +- .../highlight/CombinedHighlighter.java | 24 ++++++++----------- .../charting/highlight/Highlight.java | 2 +- .../renderer/CombinedChartRenderer.java | 1 + 4 files changed, 13 insertions(+), 16 deletions(-) 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 c4896842de..30d7550ede 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 @@ -108,7 +108,7 @@ protected List getSelectionDetailsAtIndex(float xVal) { return vals; } - private SelectionDetail getDetails(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + protected SelectionDetail getDetails(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { final Entry e = set.getEntryForXPos(xVal, rounding); 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 8422b1e6a4..78aa797458 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 @@ -2,6 +2,7 @@ import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.CombinedData; +import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; import com.github.mikephil.charting.utils.SelectionDetail; @@ -19,7 +20,7 @@ public CombinedHighlighter(BarLineScatterCandleBubbleDataProvider chart) { } /** - * Returns a list of SelectionDetail object corresponding to the given xIndex. + * Returns a list of SelectionDetail object corresponding to the given xValue. * * @param xVal * @return @@ -28,7 +29,6 @@ public CombinedHighlighter(BarLineScatterCandleBubbleDataProvider chart) { protected List getSelectionDetailsAtIndex(float xVal) { List vals = new ArrayList(); - float[] pts = new float[2]; CombinedData data = (CombinedData) mChart.getData(); @@ -37,25 +37,21 @@ protected List getSelectionDetailsAtIndex(float xVal) { for (int i = 0; i < dataObjects.size(); i++) { - for(int j = 0; j < dataObjects.get(i).getDataSetCount(); j++) { + for (int j = 0, dataSetCount = dataObjects.get(i).getDataSetCount(); j < dataSetCount; j++) { IDataSet dataSet = dataObjects.get(i).getDataSetByIndex(j); - // dont include datasets that cannot be highlighted + // don't include datasets that cannot be highlighted if (!dataSet.isHighlightEnabled()) continue; - // extract all yPx-values from all DataSets at the given xPx-index - final float yVals[] = dataSet.getYValuesForXPos(xVal); - for (float yVal : yVals) { - pts[1] = yVal; + SelectionDetail s1 = getDetails(dataSet, j, xVal, DataSet.Rounding.UP); + s1.dataIndex = i; + vals.add(s1); - mChart.getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); - - if (!Float.isNaN(pts[1])) { - vals.add(new SelectionDetail(0f, pts[1], yVal, i, j, dataSet)); - } - } + SelectionDetail s2 = getDetails(dataSet, j, xVal, DataSet.Rounding.DOWN); + s2.dataIndex = i; + vals.add(s2); } } 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 4c267338b1..11244c80ba 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 @@ -148,7 +148,7 @@ public boolean equalTo(Highlight h) { return false; else { if (this.mDataSetIndex == h.mDataSetIndex && this.mX == h.mX - && this.mStackIndex == h.mStackIndex) + && this.mStackIndex == h.mStackIndex && this.mDataIndex == h.mDataIndex) return true; else return false; 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 c96db83df6..be8d98e476 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 @@ -10,6 +10,7 @@ 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.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ViewPortHandler; import java.lang.ref.WeakReference; From ac3eafaba92d986e7d848f6888bf5e07dd21de64 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 3 Jun 2016 10:26:05 +0200 Subject: [PATCH 165/606] Work on highlighting, introduced highlight max distance --- .../mikephil/charting/charts/Chart.java | 32 +++++-- .../charting/formatter/ColorFormatter.java | 14 +++- .../charting/highlight/BarHighlighter.java | 44 ++-------- .../charting/highlight/ChartHighlighter.java | 83 ++++++++++++++++++- .../highlight/HorizontalBarHighlighter.java | 24 +++++- .../dataprovider/ChartInterface.java | 8 ++ .../renderer/ScatterChartRenderer.java | 25 ------ .../github/mikephil/charting/utils/Utils.java | 64 -------------- 8 files changed, 157 insertions(+), 137 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 12c29c78da..7586d08035 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 @@ -228,6 +228,7 @@ public void onAnimationUpdate(ValueAnimator animation) { // initialize the utils Utils.init(getContext()); + mMaxHighlightDistance = Utils.convertDpToPixel(70f); mDefaultFormatter = new DefaultValueFormatter(1); @@ -483,6 +484,26 @@ protected void drawDescription(Canvas c) { */ protected Highlight[] mIndicesToHighlight; + /** + * The maximum distance in screen pixels away from an entry causing it to highlight. + */ + protected float mMaxHighlightDistance = 0f; + + @Override + public float getMaxHighlightDistance() { + return mMaxHighlightDistance; + } + + /** + * Sets the maximum distance in screen dp a touch can be away from an entry to cause it to get highlighted. + * Default: 70dp + * + * @param distDp + */ + public void setMaxHighlightDistance(float distDp) { + mMaxHighlightDistance = Utils.convertDpToPixel(distDp); + } + /** * Returns the array of currently highlighted values. This might a null or * empty array if nothing is highlighted. @@ -610,7 +631,7 @@ public void highlightValue(Highlight high, boolean callListener) { high = null; } else { if (this instanceof BarLineChartBase - && ((BarLineChartBase)this).isHighlightFullBarEnabled()) + && ((BarLineChartBase) this).isHighlightFullBarEnabled()) high = new Highlight(high.getX(), Float.NaN, -1, -1, -1); // set the indices to highlight @@ -681,9 +702,9 @@ protected void drawMarkers(Canvas canvas) { float xVal = highlight.getX(); int dataSetIndex = highlight.getDataSetIndex(); - float deltaX = mXAxis != null - ? mXAxis.mAxisRange - : 1f; + float deltaX = mXAxis != null + ? mXAxis.mAxisRange + : 1f; if (xVal <= deltaX && xVal <= deltaX * mAnimator.getPhaseX()) { @@ -1539,7 +1560,8 @@ public boolean saveToPath(String title, String pathOnSD) { * @param quality e.g. 50, min = 0, max = 100 * @return returns true if saving was successful, false if not */ - public boolean saveToGallery(String fileName, String subFolderPath, String fileDescription, Bitmap.CompressFormat format, int quality) { + public boolean saveToGallery(String fileName, String subFolderPath, String fileDescription, Bitmap.CompressFormat + format, int quality) { // restrain quality if (quality < 0 || quality > 100) quality = 50; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ColorFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ColorFormatter.java index 3a7d119e60..2db66fd43f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ColorFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ColorFormatter.java @@ -1,14 +1,24 @@ package com.github.mikephil.charting.formatter; +import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; /** * Interface that can be used to return a customized color instead of setting * colors via the setColor(...) method of the DataSet. - * + * * @author Philipp Jahoda */ public interface ColorFormatter { - int getColor(Entry e, int index); + /** + * Returns the color to be used for the given Entry at the given index (in the entries array) + * + * @param index index in the entries array + * @param e the entry to color + * @param set the DataSet the entry belongs to + * @return + */ + int getColor(int index, Entry e, IDataSet set); } \ No newline at end of file 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 2768728dce..1175887aba 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 @@ -9,6 +9,9 @@ import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.SelectionDetail; +import com.github.mikephil.charting.utils.Utils; + +import java.util.List; /** * Created by Philipp Jahoda on 22/07/15. @@ -47,42 +50,6 @@ public Highlight getHighlight(float x, float y) { -1); } - @Override - protected SelectionDetail getSelectionDetail(float xVal, float x, float y) { - - BarData barData = mChart.getBarData(); - - int closestDataSetIndex = 0; - float closestDistance = Float.MAX_VALUE; - Entry closestEntry = null; - - for (int i = 0; i < barData.getDataSets().size(); i++) { - - IBarDataSet dataSet = barData.getDataSetByIndex(i); - - final Entry entry = dataSet.getEntryForXPos(xVal); - - if (entry != null) { - - final float distance = Math.abs(xVal - entry.getX()); - - if (distance < closestDistance) { - closestDataSetIndex = i; - closestDistance = distance; - closestEntry = entry; - } - } - } - - if(closestEntry == null) - return null; - - return new SelectionDetail(x, y, closestEntry.getX(), - closestEntry.getY(), - closestDataSetIndex, - barData.getDataSetByIndex(closestDataSetIndex)); - } - /** * This method creates the Highlight object that also indicates which yValue of a stacked BarEntry has been * selected. @@ -190,4 +157,9 @@ protected Range[] getRanges(BarEntry entry) { return ranges; } + + @Override + protected float getDistance(float x, float y, float selX, float selY) { + return Math.abs(x - selX); + } } 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 30d7550ede..265c8a66c1 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 @@ -71,18 +71,54 @@ protected SelectionDetail getSelectionDetail(float xVal, float x, float y) { List valsAtIndex = getSelectionDetailsAtIndex(xVal); - float leftdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.LEFT); - float rightdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.RIGHT); + float leftdist = getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.LEFT); + float rightdist = getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.RIGHT); YAxis.AxisDependency axis = leftdist < rightdist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT; - SelectionDetail detail = Utils.getClosestSelectionDetailByPixel(valsAtIndex, x, y, axis); + SelectionDetail detail = getClosestSelectionDetailByPixel(valsAtIndex, x, y, axis, mChart + .getMaxHighlightDistance()); return detail; } /** - * Returns a list of SelectionDetail object corresponding to the given xIndex. + * Returns the minimum distance from a touch value (in pixels) to the + * closest value (in pixels) that is displayed in the chart. + * + * @param valsAtIndex + * @param pos + * @param axis + * @return + */ + protected float getMinimumDistance(List valsAtIndex, + float pos, + YAxis.AxisDependency axis) { + + float distance = Float.MAX_VALUE; + + for (int i = 0; i < valsAtIndex.size(); i++) { + + SelectionDetail sel = valsAtIndex.get(i); + + if (sel.dataSet.getAxisDependency() == axis) { + + float cdistance = Math.abs(getSelectionPos(sel) - pos); + if (cdistance < distance) { + distance = cdistance; + } + } + } + + return distance; + } + + protected float getSelectionPos(SelectionDetail sel) { + return sel.yPx; + } + + /** + * Returns a list of SelectionDetail object corresponding to the given xVal. * * @param xVal * @return @@ -116,4 +152,43 @@ protected SelectionDetail getDetails(IDataSet set, int dataSetIndex, float xVal, return new SelectionDetail((float) pixels.x, (float) pixels.y, e.getX(), e.getY(), dataSetIndex, set); } + + /** + * Returns the SelectionDetail of the DataSet that contains the closest yValue on the + * yPx-axis. + * + * @param valsAtIndex all the values at a specific index + * @return + */ + public SelectionDetail getClosestSelectionDetailByPixel( + List valsAtIndex, + float x, float y, + YAxis.AxisDependency axis, float minSelectionDistance) { + + SelectionDetail closest = null; + float distance = minSelectionDistance; + + System.out.println(distance); + + for (int i = 0; i < valsAtIndex.size(); i++) { + + SelectionDetail sel = valsAtIndex.get(i); + + if (axis == null || sel.dataSet.getAxisDependency() == axis) { + + float cDistance = getDistance(x, y, sel.xPx, sel.yPx); + + if (cDistance < distance) { + closest = sel; + distance = cDistance; + } + } + } + + return closest; + } + + protected float getDistance(float x, float y, float selX, float selY) { + return (float) Math.hypot(x - selX, y - selY); + } } 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 868f703a34..5e3cbdd3fb 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 @@ -1,10 +1,17 @@ package com.github.mikephil.charting.highlight; +import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.SelectionDetail; +import com.github.mikephil.charting.utils.Utils; + +import java.util.List; /** * Created by Philipp Jahoda on 22/07/15. @@ -22,7 +29,7 @@ public Highlight getHighlight(float x, float y) { PointD pos = getValsForTouch(y, x); - SelectionDetail selectionDetail = getSelectionDetail((float) pos.y, x, y); + SelectionDetail selectionDetail = getSelectionDetail((float) pos.y, y, x); if (selectionDetail == null) return null; @@ -42,4 +49,19 @@ public Highlight getHighlight(float x, float y) { selectionDetail.dataSetIndex, -1); } + + @Override + protected SelectionDetail getDetails(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + + final Entry e = set.getEntryForXPos(xVal, rounding); + + PointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getY(), e.getX()); + + return new SelectionDetail((float) pixels.x, (float) pixels.y, e.getX(), e.getY(), dataSetIndex, set); + } + + @Override + protected float getDistance(float x, float y, float selX, float selY) { + return Math.abs(y - selY); + } } 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 a77ecaf393..14a61521de 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 @@ -5,6 +5,7 @@ import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.utils.Utils; /** * Interface that provides everything there is to know about the dimensions, @@ -44,6 +45,13 @@ public interface ChartInterface { */ float getYChartMax(); + /** + * Returns the maximum distance in scren dp a touch can be away from an entry to cause it to get highlighted. + * + * @return + */ + float getMaxHighlightDistance(); + int getWidth(); int getHeight(); 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 99cf49d1f5..ff876208f2 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 @@ -300,31 +300,6 @@ protected void drawDataSet(Canvas c, IScatterDataSet dataSet) { default: break; } - - // else { // draw the custom-shape - // - // Path customShape = dataSet.getCustomScatterShape(); - // - // for (int j = 0; j < entries.size() * mAnimator.getPhaseX(); j += 2) { - // - // Entry e = entries.get(j / 2); - // - // if (!fitsBounds(e.getX(), mMinX, mMaxX)) - // continue; - // - // if (customShape == null) - // return; - // - // mRenderPaint.setColor(dataSet.getColor(j)); - // - // Path newPath = new Path(customShape); - // newPath.offset(e.getX(), e.getY()); - // - // // transform the provided custom path - // trans.pathValueToPixel(newPath); - // c.drawPath(newPath, mRenderPaint); - // } - // } } @Override 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 bf9f3157d3..9ec8a5e808 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 @@ -431,70 +431,6 @@ public static SelectionDetail getClosestSelectionDetailByValue( return closest; } - /** - * Returns the SelectionDetail of the DataSet that contains the closest yValue on the - * yPx-axis. - * - * @param valsAtIndex all the values at a specific index - * @return - */ - public static SelectionDetail getClosestSelectionDetailByPixel( - List valsAtIndex, - float x, float y, - AxisDependency axis) { - - SelectionDetail closest = null; - float distance = Float.MAX_VALUE; - - for (int i = 0; i < valsAtIndex.size(); i++) { - - SelectionDetail sel = valsAtIndex.get(i); - - if (axis == null || sel.dataSet.getAxisDependency() == axis) { - - float cDistance = (float) Math.hypot(x - sel.xPx, y - sel.yPx); - - if (cDistance < distance) { - closest = sel; - distance = cDistance; - } - } - } - - return closest; - } - - /** - * Returns the minimum distance from a touch-yPx-yValue (in pixels) to the - * closest yPx-yValue (in pixels) that is displayed in the chart. - * - * @param valsAtIndex - * @param y - * @param axis - * @return - */ - public static float getMinimumDistance(List valsAtIndex, - float y, - AxisDependency axis) { - - float distance = Float.MAX_VALUE; - - for (int i = 0; i < valsAtIndex.size(); i++) { - - SelectionDetail sel = valsAtIndex.get(i); - - if (sel.dataSet.getAxisDependency() == axis) { - - float cdistance = Math.abs(sel.yPx - y); - if (cdistance < distance) { - distance = cdistance; - } - } - } - - return distance; - } - /** * If this component has no ValueFormatter or is only equipped with the * default one (no custom set), return true. From 4b058a594fddbd4035489df32553e37d327f3283 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 3 Jun 2016 11:35:16 +0200 Subject: [PATCH 166/606] Fixes regarding drawing of markerview --- .../mpchartexample/BarChartActivity.java | 2 +- .../mikephil/charting/charts/Chart.java | 59 ++++++++----------- 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index d77962f5cf..3a9b9e3533 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -234,7 +234,7 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - float start = 0f; + float start = 1f; mChart.getXAxis().setAxisMinValue(start); mChart.getXAxis().setAxisMaxValue(start + count + 2); 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 7586d08035..42e40d0f65 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 @@ -699,48 +699,39 @@ protected void drawMarkers(Canvas canvas) { for (int i = 0; i < mIndicesToHighlight.length; i++) { Highlight highlight = mIndicesToHighlight[i]; - float xVal = highlight.getX(); - int dataSetIndex = highlight.getDataSetIndex(); - float deltaX = mXAxis != null - ? mXAxis.mAxisRange - : 1f; + Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]); - if (xVal <= deltaX && xVal <= deltaX * mAnimator.getPhaseX()) { + // make sure entry not null + if (e == null || e.getX() != mIndicesToHighlight[i].getX()) + continue; - Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]); + float[] pos = getMarkerPosition(e, highlight); - // make sure entry not null - if (e == null || e.getX() != mIndicesToHighlight[i].getX()) - continue; + // check bounds + if (!mViewPortHandler.isInBounds(pos[0], pos[1])) + continue; - float[] pos = getMarkerPosition(e, highlight); + // callbacks to update the content + mMarkerView.refreshContent(e, highlight); - // check bounds - if (!mViewPortHandler.isInBounds(pos[0], pos[1])) - continue; + // mMarkerView.measure(MeasureSpec.makeMeasureSpec(0, + // MeasureSpec.UNSPECIFIED), + // MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + // mMarkerView.layout(0, 0, mMarkerView.getMeasuredWidth(), + // mMarkerView.getMeasuredHeight()); + // mMarkerView.draw(mDrawCanvas, pos[0], pos[1]); - // callbacks to update the content - mMarkerView.refreshContent(e, highlight); + mMarkerView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + mMarkerView.layout(0, 0, mMarkerView.getMeasuredWidth(), + mMarkerView.getMeasuredHeight()); - // mMarkerView.measure(MeasureSpec.makeMeasureSpec(0, - // MeasureSpec.UNSPECIFIED), - // MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - // mMarkerView.layout(0, 0, mMarkerView.getMeasuredWidth(), - // mMarkerView.getMeasuredHeight()); - // mMarkerView.draw(mDrawCanvas, pos[0], pos[1]); - - 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]); - } + 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]); } } } From f39d8911d3b0a70941c2e4afd86c1163d60c0ad3 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 3 Jun 2016 12:11:42 +0200 Subject: [PATCH 167/606] Work on radarchart --- .../layout/activity_radarchart_noseekbar.xml | 20 +++++-- .../mpchartexample/RadarChartActivitry.java | 59 ++++++++++++------- .../mpchartexample/ScatterChartActivity.java | 2 +- .../charting/charts/PieRadarChartBase.java | 2 +- .../charting/renderer/RadarChartRenderer.java | 2 +- .../renderer/XAxisRendererRadarChart.java | 2 +- 6 files changed, 57 insertions(+), 30 deletions(-) diff --git a/MPChartExample/res/layout/activity_radarchart_noseekbar.xml b/MPChartExample/res/layout/activity_radarchart_noseekbar.xml index 1de38d5660..2e8d7b5ebf 100644 --- a/MPChartExample/res/layout/activity_radarchart_noseekbar.xml +++ b/MPChartExample/res/layout/activity_radarchart_noseekbar.xml @@ -1,11 +1,23 @@ - + android:layout_height="match_parent" + android:orientation="vertical"> + + + + android:layout_height="0dp" + android:layout_weight="2" /> - \ No newline at end of file + \ No newline at end of file diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index 66d9f537e5..5062ab3c7f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -1,11 +1,13 @@ package com.xxmassdeveloper.mpchartexample; +import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; 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; @@ -37,16 +39,23 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_radarchart); - - mChart = (RadarChart) findViewById(R.id.chart1); + setContentView(R.layout.activity_radarchart_noseekbar); tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + TextView tv = (TextView) findViewById(R.id.textView); + tv.setTypeface(tf); + tv.setTextColor(Color.WHITE); + tv.setBackgroundColor(Color.rgb(60, 65, 82)); + + mChart = (RadarChart) findViewById(R.id.chart1); + mChart.setBackgroundColor(Color.rgb(60, 65, 82)); + mChart.setDescription(""); - mChart.setWebLineWidth(1.5f); - mChart.setWebLineWidthInner(0.75f); + mChart.setWebLineWidth(1f); + mChart.setWebColor(Color.LTGRAY); + mChart.setWebLineWidthInner(1f); mChart.setWebAlpha(100); // create a custom MarkerView (extend MarkerView) and specify the layout @@ -66,10 +75,14 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xAxis = mChart.getXAxis(); xAxis.setTypeface(tf); xAxis.setTextSize(9f); + xAxis.setYOffset(0f); + xAxis.setXOffset(0f); xAxis.setValueFormatter(new AxisValueFormatter() { + + private String[] mActivities = new String[]{"Burger", "Steak", "Salad", "Pasta", "Pizza"}; @Override public String getFormattedValue(float value, AxisBase axis) { - return mMonths[(int) value % mMonths.length]; + return mActivities[(int) value % mActivities.length]; } @Override @@ -77,18 +90,21 @@ public int getDecimalDigits() { return 0; } }); + xAxis.setTextColor(Color.WHITE); YAxis yAxis = mChart.getYAxis(); yAxis.setTypeface(tf); yAxis.setLabelCount(5, false); yAxis.setTextSize(9f); yAxis.setAxisMinValue(0f); + yAxis.setDrawLabels(false); Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); + l.setPosition(LegendPosition.ABOVE_CHART_CENTER); l.setTypeface(tf); l.setXEntrySpace(7f); l.setYEntrySpace(5f); + l.setTextColor(Color.WHITE); } @Override @@ -190,15 +206,11 @@ public boolean onOptionsItemSelected(MenuItem item) { return true; } - private String[] mParties = new String[]{ - "Party A", "Party B", "Party C", "Party D", "Party E", "Party F", "Party G", "Party H", - "Party I" - }; - public void setData() { - float mult = 150; - int cnt = 9; + float mult = 80; + float min = 20; + int cnt = 5; ArrayList yVals1 = new ArrayList(); ArrayList yVals2 = new ArrayList(); @@ -207,25 +219,27 @@ public void setData() { // xIndex (even if from different DataSets), since no values can be // drawn above each other. for (int i = 0; i < cnt; i++) { - float val = (float) (Math.random() * mult) + mult / 2; + float val = (float) (Math.random() * mult) + min; yVals1.add(new Entry(i, val)); } for (int i = 0; i < cnt; i++) { - float val = (float) (Math.random() * mult) + mult / 2; + float val = (float) (Math.random() * mult) + min; yVals2.add(new Entry(i, val)); } - RadarDataSet set1 = new RadarDataSet(yVals1, "Set 1"); - set1.setColor(ColorTemplate.VORDIPLOM_COLORS[0]); - set1.setFillColor(ColorTemplate.VORDIPLOM_COLORS[0]); + RadarDataSet set1 = new RadarDataSet(yVals1, "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); - RadarDataSet set2 = new RadarDataSet(yVals2, "Set 2"); - set2.setColor(ColorTemplate.VORDIPLOM_COLORS[4]); - set2.setFillColor(ColorTemplate.VORDIPLOM_COLORS[4]); + RadarDataSet set2 = new RadarDataSet(yVals2, "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); ArrayList sets = new ArrayList(); @@ -236,6 +250,7 @@ public void setData() { data.setValueTypeface(tf); data.setValueTextSize(8f); data.setDrawValues(false); + data.setValueTextColor(Color.WHITE); mChart.setData(data); mChart.invalidate(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index 0bac51c1c6..7c5652f109 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -62,8 +62,8 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); - mChart.setTouchEnabled(true); + mChart.setMaxHighlightDistance(50f); // enable scaling and dragging mChart.setDragEnabled(true); 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 9dbdfc8974..c3d14c042a 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 @@ -217,7 +217,7 @@ public void calculateOffsets() { XAxis x = this.getXAxis(); if (x.isEnabled() && x.isDrawLabelsEnabled()) { - minOffset = Math.max(minOffset, x.mLabelRotatedWidth * 2f); + minOffset = Math.max(minOffset, x.mLabelRotatedWidth); } } 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 e8c086c9d8..7519d50693 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 @@ -228,7 +228,7 @@ protected void drawWeb(Canvas c) { mWebPaint.setColor(mChart.getWebColorInner()); mWebPaint.setAlpha(mChart.getWebAlpha()); - int labelCount = mChart.getYAxis().mEntries.length; + int labelCount = mChart.getYAxis().mEntryCount; for (int j = 0; j < labelCount; j++) { 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 8dd2191001..ed30084f7f 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 @@ -47,7 +47,7 @@ public void renderAxisLabels(Canvas c) { float angle = (sliceangle * i + mChart.getRotationAngle()) % 360f; PointF p = Utils.getPosition(center, mChart.getYRange() * factor - + mXAxis.mLabelRotatedWidth, angle); + + mXAxis.mLabelRotatedWidth / 2f, angle); drawLabel(c, label, p.x, p.y - mXAxis.mLabelRotatedHeight / 2.f, drawLabelAnchor, labelRotationAngleDegrees); From 6d39afa02881b700ad126cbd513570f484249326 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 3 Jun 2016 16:13:04 +0200 Subject: [PATCH 168/606] Brush up radarchart example --- .../res/drawable-nodpi/radar_marker.png | Bin 0 -> 9753 bytes .../layout/activity_radarchart_noseekbar.xml | 3 +- .../res/layout/radar_markerview.xml | 23 ++++++++ .../mpchartexample/RadarChartActivitry.java | 12 +++- .../custom/RadarMarkerView.java | 52 ++++++++++++++++++ 5 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 MPChartExample/res/drawable-nodpi/radar_marker.png create mode 100644 MPChartExample/res/layout/radar_markerview.xml create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java diff --git a/MPChartExample/res/drawable-nodpi/radar_marker.png b/MPChartExample/res/drawable-nodpi/radar_marker.png new file mode 100644 index 0000000000000000000000000000000000000000..a84b93b5391a4d54c448ac12ab3998dac1206954 GIT binary patch literal 9753 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 literal 0 HcmV?d00001 diff --git a/MPChartExample/res/layout/activity_radarchart_noseekbar.xml b/MPChartExample/res/layout/activity_radarchart_noseekbar.xml index 2e8d7b5ebf..d8e3b35aab 100644 --- a/MPChartExample/res/layout/activity_radarchart_noseekbar.xml +++ b/MPChartExample/res/layout/activity_radarchart_noseekbar.xml @@ -4,11 +4,10 @@ android:layout_height="match_parent" android:orientation="vertical"> - + + + + + diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index 5062ab3c7f..17866470b1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -15,6 +15,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.MarkerView; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.Entry; @@ -25,6 +26,7 @@ import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; +import com.xxmassdeveloper.mpchartexample.custom.RadarMarkerView; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -41,7 +43,7 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_radarchart_noseekbar); - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + tf = Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf"); TextView tv = (TextView) findViewById(R.id.textView); tv.setTypeface(tf); @@ -56,11 +58,12 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setWebLineWidth(1f); mChart.setWebColor(Color.LTGRAY); mChart.setWebLineWidthInner(1f); + mChart.setWebColorInner(Color.LTGRAY); mChart.setWebAlpha(100); // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it - MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); + MarkerView mv = new RadarMarkerView(this, R.layout.radar_markerview); // set the marker to the chart mChart.setMarkerView(mv); @@ -97,6 +100,7 @@ public int getDecimalDigits() { yAxis.setLabelCount(5, false); yAxis.setTextSize(9f); yAxis.setAxisMinValue(0f); + yAxis.setAxisMaxValue(80f); yAxis.setDrawLabels(false); Legend l = mChart.getLegend(); @@ -234,6 +238,8 @@ public void setData() { set1.setDrawFilled(true); set1.setFillAlpha(180); set1.setLineWidth(2f); + set1.setDrawHighlightCircleEnabled(true); + set1.setDrawHighlightIndicators(false); RadarDataSet set2 = new RadarDataSet(yVals2, "This Week"); set2.setColor(Color.rgb(121, 162, 175)); @@ -241,6 +247,8 @@ public void setData() { set2.setDrawFilled(true); set2.setFillAlpha(180); set2.setLineWidth(2f); + set2.setDrawHighlightCircleEnabled(true); + set2.setDrawHighlightIndicators(false); ArrayList sets = new ArrayList(); sets.add(set1); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java new file mode 100644 index 0000000000..cac42ef0fd --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java @@ -0,0 +1,52 @@ + +package com.xxmassdeveloper.mpchartexample.custom; + +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.Utils; +import com.xxmassdeveloper.mpchartexample.R; + +import java.text.DecimalFormat; + +/** + * Custom implementation of the MarkerView. + * + * @author Philipp Jahoda + */ +public class RadarMarkerView extends MarkerView { + + private TextView tvContent; + private DecimalFormat format = new DecimalFormat("##0"); + + public RadarMarkerView(Context context, int layoutResource) { + super(context, layoutResource); + + tvContent = (TextView) findViewById(R.id.tvContent); + tvContent.setTypeface(Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf")); + } + + // callbacks everytime 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()) + " %"); + } + + @Override + public int getXOffset(float xpos) { + // this will center the marker-view horizontally + return -(getWidth() / 2); + } + + @Override + public int getYOffset(float ypos) { + // this will cause the marker-view to be above the selected yValue + return -getHeight()-10; + } +} From 589bd0cbadc7ee6bc812217c3031f9065d2b1243 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 3 Jun 2016 16:52:32 +0200 Subject: [PATCH 169/606] Work on making realm example work again --- .../mpchartexample/custom/RealmDemoData.java | 60 +++++++++---------- .../realm/RealmBaseActivity.java | 12 ++-- .../realm/RealmDatabaseActivityBar.java | 6 +- .../realm/RealmDatabaseActivityBubble.java | 6 +- .../realm/RealmDatabaseActivityCandle.java | 6 +- .../RealmDatabaseActivityHorizontalBar.java | 6 +- .../realm/RealmDatabaseActivityLine.java | 6 +- .../realm/RealmDatabaseActivityPie.java | 6 +- .../realm/RealmDatabaseActivityRadar.java | 2 +- .../realm/RealmDatabaseActivityScatter.java | 5 +- .../realm/RealmWikiExample.java | 43 +++++++++---- .../mpchartexample/realm/Score.java | 8 +-- ...ealmBarLineScatterCandleBubbleDataSet.java | 6 +- .../data/realm/base/RealmBaseDataSet.java | 13 ++-- .../RealmLineScatterCandleRadarDataSet.java | 1 - .../realm/implementation/RealmBarDataSet.java | 14 ++--- .../implementation/RealmBubbleDataSet.java | 2 +- .../implementation/RealmCandleDataSet.java | 2 +- .../implementation/RealmLineDataSet.java | 8 +-- .../realm/implementation/RealmPieDataSet.java | 14 ----- .../implementation/RealmRadarDataSet.java | 8 +-- .../implementation/RealmScatterDataSet.java | 8 +-- 22 files changed, 124 insertions(+), 118 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java index a84c65d003..4f3323374e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java @@ -10,7 +10,8 @@ */ public class RealmDemoData extends RealmObject { - private float value; + private float yValue; + private float xValue; private float open, close, high, low; @@ -18,8 +19,6 @@ public class RealmDemoData extends RealmObject { private RealmList stackValues; - private int xIndex; - private String xAxisLabel; private double xAxisPosition; @@ -31,9 +30,9 @@ public RealmDemoData() { } - public RealmDemoData(float value, int xIndex, double xAxisPosition, String xAxisLabel) { - this.value = value; - this.xIndex = xIndex; + public RealmDemoData(float xValue, float yValue, double xAxisPosition, String xAxisLabel) { + this.xValue = xValue; + this.yValue = yValue; this.xAxisPosition = xAxisPosition; this.xAxisLabel = xAxisLabel; } @@ -41,13 +40,13 @@ public RealmDemoData(float value, int xIndex, double xAxisPosition, String xAxis /** * Constructor for stacked bars. * + * @param xValue * @param stackValues - * @param xIndex * @param xAxisPosition * @param xAxisLabel */ - public RealmDemoData(float[] stackValues, int xIndex, double xAxisPosition, String xAxisLabel) { - this.xIndex = xIndex; + public RealmDemoData(float xValue, float[] stackValues, double xAxisPosition, String xAxisLabel) { + this.xValue = xValue; this.xAxisPosition = xAxisPosition; this.xAxisLabel = xAxisLabel; this.stackValues = new RealmList(); @@ -60,21 +59,22 @@ public RealmDemoData(float[] stackValues, int xIndex, double xAxisPosition, Stri /** * Constructor for candles. * + * @param xValue * @param high * @param low * @param open * @param close - * @param xIndex * @param xAxisPosition * @param xAxisLabel */ - public RealmDemoData(float high, float low, float open, float close, int xIndex, double xAxisPosition, String xAxisLabel) { - this.value = (high + low) / 2f; + public RealmDemoData(float xValue, float high, float low, float open, float close, double xAxisPosition, String + xAxisLabel) { + this.yValue = (high + low) / 2f; this.high = high; this.low = low; this.open = open; this.close = close; - this.xIndex = xIndex; + this.xValue = xValue; this.xAxisPosition = xAxisPosition; this.xAxisLabel = xAxisLabel; } @@ -82,42 +82,42 @@ public RealmDemoData(float high, float low, float open, float close, int xIndex, /** * Constructor for bubbles. * - * @param value - * @param xIndex + * @param xValue + * @param yValue * @param bubbleSize * @param xAxisPosition * @param xAxisLabel */ - public RealmDemoData(float value, int xIndex, float bubbleSize, double xAxisPosition, String xAxisLabel) { - this.value = value; - this.xIndex = xIndex; + public RealmDemoData(float xValue, float yValue, float bubbleSize, double xAxisPosition, String xAxisLabel) { + this.xValue = xValue; + this.yValue = yValue; this.bubbleSize = bubbleSize; this.xAxisPosition = xAxisPosition; this.xAxisLabel = xAxisLabel; } - public float getValue() { - return value; + public float getyValue() { + return yValue; } - public void setValue(float value) { - this.value = value; + public void setyValue(float yValue) { + this.yValue = yValue; } - public RealmList getStackValues() { - return stackValues; + public float getxValue() { + return xValue; } - public void setStackValues(RealmList stackValues) { - this.stackValues = stackValues; + public void setxValue(float xValue) { + this.xValue = xValue; } - public int getxIndex() { - return xIndex; + public RealmList getStackValues() { + return stackValues; } - public void setxIndex(int xIndex) { - this.xIndex = xIndex; + public void setStackValues(RealmList stackValues) { + this.stackValues = stackValues; } public float getOpen() { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java index 9e3197b259..4eef9d13af 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -110,7 +110,7 @@ protected void writeToDB(int objectCount) { float value = 40f + (float) (Math.random() * 60f); - RealmDemoData d = new RealmDemoData(value, i, i, "" + i); + RealmDemoData d = new RealmDemoData(i, value, i, "" + i); mRealm.copyToRealm(d); } @@ -129,7 +129,7 @@ protected void writeToDBStack(int objectCount) { float val2 = 34f + (float) (Math.random() * 12.0f); float[] stack = new float[]{val1, val2, 100 - val1 - val2}; - RealmDemoData d = new RealmDemoData(stack, i, i, "" + i); + RealmDemoData d = new RealmDemoData(i, stack, i, "" + i); mRealm.copyToRealm(d); } @@ -155,8 +155,8 @@ protected void writeToDBCandle(int objectCount) { boolean even = i % 2 == 0; - RealmDemoData d = new RealmDemoData(val + high, val - low, even ? val + open : val - open, - even ? val - close : val + close, i, i, i + ""); + RealmDemoData d = new RealmDemoData(i, val + high, val - low, even ? val + open : val - open, + even ? val - close : val + close, i, i + ""); mRealm.copyToRealm(d); } @@ -175,7 +175,7 @@ protected void writeToDBBubble(int objectCount) { float value = 30f + (float) (Math.random() * 100.0); float size = 15f + (float) (Math.random() * 20.0); - RealmDemoData d = new RealmDemoData(value, i, size, "" + i); + RealmDemoData d = new RealmDemoData(i, value, size, "" + i); mRealm.copyToRealm(d); } @@ -198,7 +198,7 @@ protected void writeToDBPie() { String[] xValues = new String[]{ "iOS", "Android", "WP 10", "BlackBerry", "Other"}; for (int i = 0; i < values.length; i++) { - RealmDemoData d = new RealmDemoData(values[i], i, i, xValues[i]); + RealmDemoData d = new RealmDemoData(i, values[i], i, xValues[i]); mRealm.copyToRealm(d); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java index 9527f3e6be..0e612bee58 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java @@ -5,7 +5,7 @@ import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.data.realm.implementation.RealmBarData; +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; @@ -50,7 +50,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet(result, "yValue", "xIndex"); // stacked entries + RealmBarDataSet set = new RealmBarDataSet(result, "yValue", "xValue"); // stacked entries set.setColors(new int[] {ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); set.setLabel("Realm BarDataSet"); @@ -58,7 +58,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RealmBarData data = new RealmBarData(result, "xAxisPosition", "xAxisLabel", dataSets); + BarData data = new BarData(dataSets); styleData(data); // set data diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java index cd188a4266..c2c2003d2e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java @@ -5,7 +5,7 @@ import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.BubbleChart; -import com.github.mikephil.charting.data.realm.implementation.RealmBubbleData; +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; @@ -53,7 +53,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); - RealmBubbleDataSet set = new RealmBubbleDataSet(result, "yValue", "xIndex", "bubbleSize"); + RealmBubbleDataSet set = new RealmBubbleDataSet(result, "xValue", "yValue", "bubbleSize"); set.setLabel("Realm BubbleDataSet"); set.setColors(ColorTemplate.COLORFUL_COLORS, 110); @@ -61,7 +61,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RealmBubbleData data = new RealmBubbleData(result, "xAxisPosition", "xAxisLabel", dataSets); + BubbleData data = new BubbleData(dataSets); styleData(data); // set data diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java index 97a0036567..c8f7abb84d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java @@ -7,7 +7,7 @@ import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.CandleStickChart; -import com.github.mikephil.charting.data.realm.implementation.RealmCandleData; +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; @@ -53,7 +53,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); - RealmCandleDataSet set = new RealmCandleDataSet(result, "high", "low", "open", "close", "xIndex"); + RealmCandleDataSet set = new RealmCandleDataSet(result, "xValue", "high", "low", "open", "close"); set.setLabel("Realm Realm CandleDataSet"); set.setShadowColor(Color.DKGRAY); set.setShadowWidth(0.7f); @@ -67,7 +67,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RealmCandleData data = new RealmCandleData(result, "xAxisPosition", "xAxisLabel", dataSets); + CandleData data = new CandleData(dataSets); styleData(data); // set data diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java index 40bbf10910..2c6081e40c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java @@ -6,7 +6,7 @@ import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.HorizontalBarChart; -import com.github.mikephil.charting.data.realm.implementation.RealmBarData; +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; @@ -54,7 +54,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex", "floatValue"); // stacked 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"}); @@ -63,7 +63,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RealmBarData data = new RealmBarData(result, "xAxisPosition", "xAxisLabel", dataSets); + BarData data = new BarData(dataSets); styleData(data); data.setValueTextColor(Color.WHITE); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java index 36a8dc5abd..4e04cf09df 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java @@ -5,7 +5,7 @@ import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.data.realm.implementation.RealmLineData; +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; @@ -54,7 +54,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); - RealmLineDataSet set = new RealmLineDataSet(result, "yValue", "xIndex"); + RealmLineDataSet set = new RealmLineDataSet(result, "xValue", "yValue"); set.setDrawCubic(false); set.setLabel("Realm LineDataSet"); set.setDrawCircleHole(false); @@ -67,7 +67,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RealmLineData data = new RealmLineData(result, "xAxisPosition", "xAxisLabel", dataSets); + LineData data = new LineData(dataSets); styleData(data); // set data diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java index dfe37e61f6..bb97dd6681 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java @@ -10,7 +10,7 @@ import android.view.WindowManager; import com.github.mikephil.charting.charts.PieChart; -import com.github.mikephil.charting.data.realm.implementation.RealmPieData; +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; @@ -54,13 +54,13 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmPieDataSet set = new RealmPieDataSet(result, "yValue", "xIndex"); // stacked entries + RealmPieDataSet set = new RealmPieDataSet(result, "yValue"); // stacked entries set.setColors(ColorTemplate.VORDIPLOM_COLORS); set.setLabel("Example market share"); set.setSliceSpace(2); // create a data object with the dataset list - RealmPieData data = new RealmPieData(result, "xAxisPosition", "xAxisLabel", set); + PieData data = new PieData(set); styleData(data); data.setValueTextColor(Color.WHITE); data.setValueTextSize(12f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java index 3b57ca9ceb..5995ebb5a2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java @@ -55,7 +55,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmRadarDataSet set = new RealmRadarDataSet(result, "yValue", "xIndex"); // stacked entries + RealmRadarDataSet set = new RealmRadarDataSet(result, "xValue", "yValue"); // stacked entries set.setLabel("Realm RadarDataSet"); set.setDrawFilled(true); set.setColor(ColorTemplate.rgb("#009688")); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java index a465433d00..35d598f6cf 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java @@ -5,6 +5,7 @@ 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.RealmScatterData; import com.github.mikephil.charting.data.realm.implementation.RealmScatterDataSet; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; @@ -53,7 +54,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); - RealmScatterDataSet set = new RealmScatterDataSet(result, "yValue", "xIndex"); + RealmScatterDataSet set = new RealmScatterDataSet(result, "xValue", "yValue"); set.setLabel("Realm ScatterDataSet"); set.setScatterShapeSize(9f); set.setColor(ColorTemplate.rgb("#CDDC39")); @@ -63,7 +64,7 @@ private void setData() { dataSets.add(set); // add the dataset // create a data object with the dataset list - RealmScatterData data = new RealmScatterData(result, "xAxisPosition", "xAxisLabel", dataSets); + ScatterData data = new ScatterData(dataSets); styleData(data); // set data diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java index 2d5e3a90a5..7a9386bfbe 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java @@ -6,10 +6,14 @@ 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.RealmBarData; import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; import com.github.mikephil.charting.data.realm.implementation.RealmLineData; 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; @@ -46,6 +50,7 @@ protected void onCreate(Bundle savedInstanceState) { lineChart.getXAxis().setDrawGridLines(false); barChart.getAxisLeft().setDrawGridLines(false); barChart.getXAxis().setDrawGridLines(false); + barChart.getXAxis().setCenterAxisLabels(true); } @Override @@ -55,15 +60,15 @@ protected void onResume() { mRealm.beginTransaction(); // write some demo-data into the realm.io database - Score score1 = new Score(100f, 0, "Peter"); + Score score1 = new Score(100f, 0f, "Peter"); mRealm.copyToRealm(score1); - Score score2 = new Score(110f, 1, "Lisa"); + Score score2 = new Score(110f, 1f, "Lisa"); mRealm.copyToRealm(score2); - Score score3 = new Score(130f, 2, "Dennis"); + Score score3 = new Score(130f, 2f, "Dennis"); mRealm.copyToRealm(score3); - Score score4 = new Score(70f, 3, "Luke"); + Score score4 = new Score(70f, 3f, "Luke"); mRealm.copyToRealm(score4); - Score score5 = new Score(80f, 4, "Sarah"); + Score score5 = new Score(80f, 4f, "Sarah"); mRealm.copyToRealm(score5); mRealm.commitTransaction(); @@ -75,11 +80,27 @@ protected void onResume() { private void setData() { // LINE-CHART - RealmResults results = mRealm.allObjects(Score.class); + final RealmResults results = mRealm.allObjects(Score.class); - RealmLineDataSet lineDataSet = new RealmLineDataSet(results, "totalScore", "scoreNr"); + + 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("Realm LineDataSet"); + lineDataSet.setLabel("Result Scores"); lineDataSet.setDrawCircleHole(false); lineDataSet.setColor(ColorTemplate.rgb("#FF5722")); lineDataSet.setCircleColor(ColorTemplate.rgb("#FF5722")); @@ -89,7 +110,7 @@ private void setData() { ArrayList dataSets = new ArrayList(); dataSets.add(lineDataSet); - RealmLineData lineData = new RealmLineData(results, "scoreNr", "playerName", dataSets); + LineData lineData = new LineData(dataSets); styleData(lineData); // set data @@ -98,14 +119,14 @@ private void setData() { // BAR-CHART - RealmBarDataSet barDataSet = new RealmBarDataSet(results, "totalScore", "scoreNr"); + 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); - RealmBarData barData = new RealmBarData(results, "scoreNr", "playerName", barDataSets); + BarData barData = new BarData(barDataSets); styleData(barData); barChart.setData(barData); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java index cf3afcf46d..870e371491 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java @@ -10,14 +10,14 @@ public class Score extends RealmObject { private float totalScore; - private int scoreNr; + private float scoreNr; private String playerName; public Score() { } - public Score(float totalScore, int scoreNr, String playerName) { + public Score(float totalScore, float scoreNr, String playerName) { this.scoreNr = scoreNr; this.playerName = playerName; this.totalScore = totalScore; @@ -33,11 +33,11 @@ public void setTotalScore(float totalScore) { this.totalScore = totalScore; } - public int getScoreNr() { + public float getScoreNr() { return scoreNr; } - public void setScoreNr(int scoreNr) { + public void setScoreNr(float scoreNr) { this.scoreNr = scoreNr; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java index cfff7b8b28..55e33227c9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java @@ -24,11 +24,11 @@ public RealmBarLineScatterCandleBubbleDataSet(RealmResults results, String yV * Constructor that takes the realm RealmResults, sorts & stores them. * * @param results + * @param xValuesField * @param yValuesField - * @param xIndexField */ - public RealmBarLineScatterCandleBubbleDataSet(RealmResults results, String yValuesField, String xIndexField) { - super(results, yValuesField, xIndexField); + public RealmBarLineScatterCandleBubbleDataSet(RealmResults results, String xValuesField, String yValuesField) { + super(results, xValuesField, yValuesField); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java index 168903dbac..ee17c51587 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java @@ -48,12 +48,12 @@ public abstract class RealmBaseDataSet e protected float mXMin = 0.0f; /** - * fieldname of the column that contains the yPx-values of this dataset + * fieldname of the column that contains the y-values of this dataset */ protected String mYValuesField; /** - * fieldname of the column that contains the xPx-values of this dataset + * fieldname of the column that contains the x-values of this dataset */ protected String mXValuesField; @@ -70,13 +70,13 @@ public RealmBaseDataSet(RealmResults results, String yValuesField) { * Constructor that takes the realm RealmResults, sorts & stores them. * * @param results + * @param xValuesField * @param yValuesField - * @param xIndexField */ - public RealmBaseDataSet(RealmResults results, String yValuesField, String xIndexField) { + public RealmBaseDataSet(RealmResults results, String xValuesField, String yValuesField) { this.results = results; this.mYValuesField = yValuesField; - this.mXValuesField = xIndexField; + this.mXValuesField = xValuesField; this.mValues = new ArrayList(); if (mXValuesField != null) @@ -97,8 +97,7 @@ public void build(RealmResults results) { public S buildEntryFromResultObject(T realmObject, float x) { DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - return (S) new Entry(dynamicObject.getFloat(mYValuesField), - mXValuesField == null ? x : dynamicObject.getInt(mXValuesField)); + return (S) new Entry(mXValuesField == null ? x : dynamicObject.getFloat(mXValuesField), dynamicObject.getFloat(mYValuesField)); } @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java index d86b98d0b0..9024bb6389 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java @@ -23,7 +23,6 @@ public abstract class RealmLineScatterCandleRadarDataSet results, String yValuesField) { super(results, yValuesField); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java index fae88776fa..fa9c12c4a0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java @@ -47,8 +47,8 @@ public class RealmBarDataSet extends RealmBarLineScatterC "Stack" }; - public RealmBarDataSet(RealmResults results, String yValuesField, String xIndexField) { - super(results, yValuesField, xIndexField); + public RealmBarDataSet(RealmResults results, String xValuesField, String yValuesField) { + super(results, xValuesField, yValuesField); mHighLightColor = Color.rgb(0, 0, 0); build(this.results); @@ -59,13 +59,13 @@ public RealmBarDataSet(RealmResults results, String yValuesField, String xInd * Constructor for supporting stacked values. * * @param results + * @param xValuesField * @param yValuesField - * @param xIndexField * @param stackValueFieldName */ - public RealmBarDataSet(RealmResults results, String yValuesField, String xIndexField, String + public RealmBarDataSet(RealmResults results, String xValuesField, String yValuesField, String stackValueFieldName) { - super(results, yValuesField, xIndexField); + super(results, xValuesField, yValuesField); this.mStackValueFieldName = stackValueFieldName; mHighLightColor = Color.rgb(0, 0, 0); @@ -97,10 +97,10 @@ public BarEntry buildEntryFromResultObject(T realmObject, float x) { } return new BarEntry( - mXValuesField == null ? x : dynamicObject.getInt(mXValuesField), values); + mXValuesField == null ? x : dynamicObject.getFloat(mXValuesField), values); } else { float value = dynamicObject.getFloat(mYValuesField); - return new BarEntry(mXValuesField == null ? x : dynamicObject.getInt(mXValuesField), value); + return new BarEntry(mXValuesField == null ? x : dynamicObject.getFloat(mXValuesField), value); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java index 8c6e1d71c6..5a2e0c5f93 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java @@ -57,7 +57,7 @@ public BubbleEntry buildEntryFromResultObject(T realmObject, float x) { DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); return new BubbleEntry( - mXValuesField == null ? x : dynamicObject.getInt(mXValuesField), + mXValuesField == null ? x : dynamicObject.getFloat(mXValuesField), dynamicObject.getFloat(mYValuesField), dynamicObject.getFloat(mSizeField)); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java index 53279e5008..e4288af115 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java @@ -123,7 +123,7 @@ public CandleEntry buildEntryFromResultObject(T realmObject, int xIndex) { DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); return new CandleEntry( - mXValuesField == null ? xIndex : dynamicObject.getInt(mXValuesField), + mXValuesField == null ? xIndex : dynamicObject.getFloat(mXValuesField), dynamicObject.getFloat(mHighField), dynamicObject.getFloat(mLowField), dynamicObject.getFloat(mOpenField), diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java index d5ac1405c7..b2ea4a7de9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java @@ -87,11 +87,11 @@ public RealmLineDataSet(RealmResults result, String yValuesField) { * Constructor for creating a LineDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the yPx-yValue - * @param xIndexField the name of the field in your data object that represents the xPx-index + * @param xValuesField the name of the field in your data object that represents the x-axis value + * @param yValuesField the name of the field in your data object that represents the y-axis value */ - public RealmLineDataSet(RealmResults result, String yValuesField, String xIndexField) { - super(result, yValuesField, xIndexField); + public RealmLineDataSet(RealmResults result, String xValuesField, String yValuesField) { + super(result, xValuesField, yValuesField); mCircleColors = new ArrayList(); // default color diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java index 8ce3fc0df1..4d22de97c8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java @@ -48,20 +48,6 @@ public RealmPieDataSet(RealmResults result, String yValuesField) { calcMinMax(); } - /** - * Constructor for creating a PieDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the yPx-yValue - * @param xIndexField the name of the field in your data object that represents the xPx-index - */ - public RealmPieDataSet(RealmResults result, String yValuesField, String xIndexField) { - super(result, yValuesField, xIndexField); - - build(this.results); - calcMinMax(); - } - /** * 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/realm/implementation/RealmRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java index 77b0b19da8..0d2ed766aa 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java @@ -45,11 +45,11 @@ public RealmRadarDataSet(RealmResults result, String yValuesField) { * Constructor for creating a RadarDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the yPx-yValue - * @param xIndexField the name of the field in your data object that represents the xPx-index + * @param xValuesField the name of the field in your data object that represents the x value + * @param yValuesField the name of the field in your data object that represents the y value */ - public RealmRadarDataSet(RealmResults result, String yValuesField, String xIndexField) { - super(result, yValuesField, xIndexField); + public RealmRadarDataSet(RealmResults result, String xValuesField, String yValuesField) { + super(result, xValuesField, yValuesField); build(this.results); calcMinMax(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java index 18075feccc..d8126e7e06 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java @@ -57,11 +57,11 @@ public RealmScatterDataSet(RealmResults result, String yValuesField) { * Constructor for creating a ScatterDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the yPx-yValue - * @param xIndexField the name of the field in your data object that represents the xPx-index + * @param xValuesField the name of the field in your data object that represents the x value + * @param yValuesField the name of the field in your data object that represents the y value */ - public RealmScatterDataSet(RealmResults result, String yValuesField, String xIndexField) { - super(result, yValuesField, xIndexField); + public RealmScatterDataSet(RealmResults result, String xValuesField, String yValuesField) { + super(result, xValuesField, yValuesField); build(this.results); calcMinMax(); From 40f078a2bfed61cac57c8547baface85b2e9ff39 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 3 Jun 2016 23:30:49 +0200 Subject: [PATCH 170/606] Work on realm and barchart --- .../mpchartexample/AnotherBarActivity.java | 1 + .../ListViewBarChartActivity.java | 1 + .../mpchartexample/custom/RealmDemoData.java | 41 +-- .../listviewitems/BarChartItem.java | 1 + .../realm/RealmBaseActivity.java | 10 +- .../realm/RealmDatabaseActivityBar.java | 3 +- .../mikephil/charting/charts/BarChart.java | 336 ++++++++++-------- .../charting/charts/BarLineChartBase.java | 2 +- 8 files changed, 194 insertions(+), 201 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index b7c2a50b06..3daac14bc0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -196,6 +196,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { BarData data = new BarData(dataSets); mChart.setData(data); + mChart.setFitBars(true); } mChart.invalidate(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index 0a998bce96..84360bd7b5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java @@ -108,6 +108,7 @@ public View getView(int position, View convertView, ViewGroup parent) { // set data holder.chart.setData(data); + holder.chart.setFitBars(true); // do not forget to refresh the chart // holder.chart.invalidate(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java index 4f3323374e..d753d3e8c8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java @@ -19,9 +19,6 @@ public class RealmDemoData extends RealmObject { private RealmList stackValues; - private String xAxisLabel; - private double xAxisPosition; - private String someStringField; // ofc there could me more fields here... @@ -30,11 +27,9 @@ public RealmDemoData() { } - public RealmDemoData(float xValue, float yValue, double xAxisPosition, String xAxisLabel) { + public RealmDemoData(float xValue, float yValue) { this.xValue = xValue; this.yValue = yValue; - this.xAxisPosition = xAxisPosition; - this.xAxisLabel = xAxisLabel; } /** @@ -42,13 +37,9 @@ public RealmDemoData(float xValue, float yValue, double xAxisPosition, String xA * * @param xValue * @param stackValues - * @param xAxisPosition - * @param xAxisLabel */ - public RealmDemoData(float xValue, float[] stackValues, double xAxisPosition, String xAxisLabel) { + public RealmDemoData(float xValue, float[] stackValues) { this.xValue = xValue; - this.xAxisPosition = xAxisPosition; - this.xAxisLabel = xAxisLabel; this.stackValues = new RealmList(); for (float val : stackValues) { @@ -64,19 +55,14 @@ public RealmDemoData(float xValue, float[] stackValues, double xAxisPosition, St * @param low * @param open * @param close - * @param xAxisPosition - * @param xAxisLabel */ - public RealmDemoData(float xValue, float high, float low, float open, float close, double xAxisPosition, String - xAxisLabel) { + 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; - this.xAxisPosition = xAxisPosition; - this.xAxisLabel = xAxisLabel; } /** @@ -85,15 +71,11 @@ public RealmDemoData(float xValue, float high, float low, float open, float clos * @param xValue * @param yValue * @param bubbleSize - * @param xAxisPosition - * @param xAxisLabel */ - public RealmDemoData(float xValue, float yValue, float bubbleSize, double xAxisPosition, String xAxisLabel) { + public RealmDemoData(float xValue, float yValue, float bubbleSize) { this.xValue = xValue; this.yValue = yValue; this.bubbleSize = bubbleSize; - this.xAxisPosition = xAxisPosition; - this.xAxisLabel = xAxisLabel; } public float getyValue() { @@ -168,19 +150,4 @@ public void setSomeStringField(String someStringField) { this.someStringField = someStringField; } - public double getxAxisPosition() { - return xAxisPosition; - } - - public void setxAxisPosition(double xAxisPosition) { - this.xAxisPosition = xAxisPosition; - } - - public String getxAxisLabel() { - return xAxisLabel; - } - - public void setxAxisLabel(String xAxisLabel) { - this.xAxisLabel = xAxisLabel; - } } \ No newline at end of file diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java index 70463a7534..008c849786 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java @@ -74,6 +74,7 @@ public View getView(int position, View convertView, Context c) { // set data holder.chart.setData((BarData) mChartData); + holder.chart.setFitBars(true); // do not forget to refresh the chart // holder.chart.invalidate(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java index 4eef9d13af..29007c8c97 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -110,7 +110,7 @@ protected void writeToDB(int objectCount) { float value = 40f + (float) (Math.random() * 60f); - RealmDemoData d = new RealmDemoData(i, value, i, "" + i); + RealmDemoData d = new RealmDemoData(i, value); mRealm.copyToRealm(d); } @@ -129,7 +129,7 @@ protected void writeToDBStack(int objectCount) { float val2 = 34f + (float) (Math.random() * 12.0f); float[] stack = new float[]{val1, val2, 100 - val1 - val2}; - RealmDemoData d = new RealmDemoData(i, stack, i, "" + i); + RealmDemoData d = new RealmDemoData(i, stack); mRealm.copyToRealm(d); } @@ -156,7 +156,7 @@ protected void writeToDBCandle(int objectCount) { boolean even = i % 2 == 0; RealmDemoData d = new RealmDemoData(i, val + high, val - low, even ? val + open : val - open, - even ? val - close : val + close, i, i + ""); + even ? val - close : val + close); mRealm.copyToRealm(d); } @@ -175,7 +175,7 @@ protected void writeToDBBubble(int objectCount) { float value = 30f + (float) (Math.random() * 100.0); float size = 15f + (float) (Math.random() * 20.0); - RealmDemoData d = new RealmDemoData(i, value, size, "" + i); + RealmDemoData d = new RealmDemoData(i, value, size); mRealm.copyToRealm(d); } @@ -198,7 +198,7 @@ protected void writeToDBPie() { String[] xValues = new String[]{ "iOS", "Android", "WP 10", "BlackBerry", "Other"}; for (int i = 0; i < values.length; i++) { - RealmDemoData d = new RealmDemoData(i, values[i], i, xValues[i]); + RealmDemoData d = new RealmDemoData(i, values[i]); mRealm.copyToRealm(d); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java index 0e612bee58..54e4c55bfd 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java @@ -50,7 +50,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet(result, "yValue", "xValue"); // stacked entries + RealmBarDataSet set = new RealmBarDataSet(result, "xValue", "yValue"); // stacked entries set.setColors(new int[] {ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); set.setLabel("Realm BarDataSet"); @@ -63,6 +63,7 @@ private void setData() { // set data mChart.setData(data); + mChart.setFitBars(true); mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); } } 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 050c1be8d2..d02c947bac 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 @@ -5,6 +5,7 @@ import android.util.AttributeSet; import android.util.Log; +import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.highlight.BarHighlighter; @@ -15,170 +16,191 @@ /** * Chart that draws bars. - * + * * @author Philipp Jahoda */ public class BarChart extends BarLineChartBase implements BarDataProvider { - /** flag that enables or disables the highlighting arrow */ - private boolean mDrawHighlightArrow = false; + /** + * flag that enables or disables the highlighting arrow + */ + private boolean mDrawHighlightArrow = false; - /** - * if set to true, all values are drawn above their bars, instead of below their top - */ - private boolean mDrawValueAboveBar = true; + /** + * if set to true, all values are drawn above their bars, instead of below their top + */ + private boolean mDrawValueAboveBar = true; - /** - * if set to true, a grey area is drawn behind each bar that indicates the maximum yValue - */ - private boolean mDrawBarShadow = false; + /** + * if set to true, a grey area is drawn behind each bar that indicates the maximum yValue + */ + private boolean mDrawBarShadow = false; + + private boolean mFitBars = false; + + public BarChart(Context context) { + super(context); + } + + public BarChart(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public BarChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void init() { + super.init(); + + mRenderer = new BarChartRenderer(this, mAnimator, mViewPortHandler); + + setHighlighter(new BarHighlighter(this)); + + //mXAxis.mAxisMinimum = -0.5f; + } + + @Override + protected void calcMinMax() { + + if (mAutoScaleMinMaxEnabled) + mData.calcMinMax(); + + if (mFitBars) { + mXAxis.calculate(mData.getXMin() - mData.getBarWidth() / 2f, mData.getXMax() + mData.getBarWidth() / 2f); + } else { + mXAxis.calculate(mData.getXMin(), mData.getXMax()); + } + + // calculate axis range (min / max) according to provided data + mAxisLeft.calculate(mData.getYMin(YAxis.AxisDependency.LEFT), mData.getYMax(YAxis.AxisDependency.LEFT)); + mAxisRight.calculate(mData.getYMin(YAxis.AxisDependency.RIGHT), mData.getYMax(YAxis.AxisDependency + .RIGHT)); + } + + /** + * Returns the Highlight object (contains xPx-index and DataSet index) of the selected yValue at the given touch + * point + * inside the BarChart. + * + * @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 + return getHighlighter().getHighlight(x, y); + } + + /** + * Returns the bounding box of the specified Entry in the specified DataSet. Returns null if the Entry could not be + * found in the charts data. + * + * @param e + * @return + */ + public RectF getBarBounds(BarEntry e) { + + IBarDataSet set = mData.getDataSetForEntry(e); + + if (set == null) + return null; + + float y = e.getY(); + float x = e.getX(); + + float barWidth = mData.getBarWidth(); + + float left = x - barWidth / 2f; + float right = x + barWidth / 2f; + float top = y >= 0 ? y : 0; + float bottom = y <= 0 ? y : 0; + + RectF bounds = new RectF(left, top, right, bottom); + + getTransformer(set.getAxisDependency()).rectValueToPixel(bounds); + + return bounds; + } + + /** + * set this to true to draw the highlightning arrow + * + * @param enabled + */ + public void setDrawHighlightArrow(boolean enabled) { + mDrawHighlightArrow = enabled; + } + + /** + * returns true if drawing the highlighting arrow is enabled, false if not + * + * @return + */ + public boolean isDrawHighlightArrowEnabled() { + return mDrawHighlightArrow; + } + + /** + * If set to true, all values are drawn above their bars, instead of below their top. + * + * @param enabled + */ + public void setDrawValueAboveBar(boolean enabled) { + mDrawValueAboveBar = enabled; + } + + /** + * returns true if drawing values above bars is enabled, false if not + * + * @return + */ + public boolean isDrawValueAboveBarEnabled() { + return mDrawValueAboveBar; + } + + /** + * If set to true, a grey area is drawn behind each bar that indicates the maximum yValue. Enabling his will reduce + * performance by about 50%. + * + * @param enabled + */ + public void setDrawBarShadow(boolean enabled) { + mDrawBarShadow = enabled; + } + + /** + * returns true if drawing shadows (maxvalue) for each bar is enabled, false if not + * + * @return + */ + public boolean isDrawBarShadowEnabled() { + return mDrawBarShadow; + } + + @Override + public BarData getBarData() { + return mData; + } + + + /** + * Adds half of the bar width to each side of the x-axis range in order to allow the bars of the barchart to be + * fully displayed. + * Default: false + * + * @param enabled + */ + public void setFitBars(boolean enabled) { + mFitBars = enabled; + } - public BarChart(Context context) { - super(context); - } - - public BarChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public BarChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mRenderer = new BarChartRenderer(this, mAnimator, mViewPortHandler); - - setHighlighter(new BarHighlighter(this)); - - //mXAxis.mAxisMinimum = -0.5f; - } - - @Override - protected void calcMinMax() { - super.calcMinMax(); -// -// // increase deltax by 1 because the bars have a width of 1 -// mXAxis.mAxisRange += 0.5f; -// -// // extend xDelta to make space for multiple datasets (if ther are one) -// mXAxis.mAxisRange *= mData.getDataSetCount(); -// -// float groupSpace = mData.getGroupSpace(); -// mXAxis.mAxisRange += mData.getXValCount() * groupSpace; -// mXAxis.mAxisMaximum = mXAxis.mAxisRange - mXAxis.mAxisMinimum; - } - - /** - * Returns the Highlight object (contains xPx-index and DataSet index) of the selected yValue at the given touch point - * inside the BarChart. - * - * @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 - return getHighlighter().getHighlight(x, y); - } - - /** - * Returns the bounding box of the specified Entry in the specified DataSet. Returns null if the Entry could not be - * found in the charts data. - * - * @param e - * @return - */ - public RectF getBarBounds(BarEntry e) { - - IBarDataSet set = mData.getDataSetForEntry(e); - - if (set == null) - return null; - - float y = e.getY(); - float x = e.getX(); - - float barWidth = mData.getBarWidth(); - - float left = x - barWidth / 2f; - float right = x + barWidth / 2f; - float top = y >= 0 ? y : 0; - float bottom = y <= 0 ? y : 0; - - RectF bounds = new RectF(left, top, right, bottom); - - getTransformer(set.getAxisDependency()).rectValueToPixel(bounds); - - return bounds; - } - - /** - * set this to true to draw the highlightning arrow - * - * @param enabled - */ - public void setDrawHighlightArrow(boolean enabled) { - mDrawHighlightArrow = enabled; - } - - /** - * returns true if drawing the highlighting arrow is enabled, false if not - * - * @return - */ - public boolean isDrawHighlightArrowEnabled() { - return mDrawHighlightArrow; - } - - /** - * If set to true, all values are drawn above their bars, instead of below their top. - * - * @param enabled - */ - public void setDrawValueAboveBar(boolean enabled) { - mDrawValueAboveBar = enabled; - } - - /** - * returns true if drawing values above bars is enabled, false if not - * - * @return - */ - public boolean isDrawValueAboveBarEnabled() { - return mDrawValueAboveBar; - } - - /** - * If set to true, a grey area is drawn behind each bar that indicates the maximum yValue. Enabling his will reduce - * performance by about 50%. - * - * @param enabled - */ - public void setDrawBarShadow(boolean enabled) { - mDrawBarShadow = enabled; - } - - /** - * returns true if drawing shadows (maxvalue) for each bar is enabled, false if not - * - * @return - */ - public boolean isDrawBarShadowEnabled() { - return mDrawBarShadow; - } - - @Override - public BarData getBarData() { - return mData; - } // /** // * Returns the lowest xPx-index (yValue on the xPx-axis) that is still visible on the chart. 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 2768ad89df..f50200ab39 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 @@ -57,7 +57,7 @@ public abstract class BarLineChartBase Date: Sat, 4 Jun 2016 00:24:42 +0200 Subject: [PATCH 171/606] Bugfixes related to realm support --- .../mpchartexample/custom/RealmDemoData.java | 27 +++++++++++++ .../realm/RealmBaseActivity.java | 4 +- .../realm/RealmDatabaseActivityCandle.java | 2 +- .../realm/RealmDatabaseActivityPie.java | 2 +- .../realm/RealmDatabaseActivityRadar.java | 1 + .../realm/RealmWikiExample.java | 6 ++- .../RealmLineScatterCandleRadarDataSet.java | 6 +-- .../implementation/RealmCandleDataSet.java | 38 ++++++++++--------- .../realm/implementation/RealmPieDataSet.java | 21 ++++++++++ .../charting/renderer/PieChartRenderer.java | 8 ++-- 10 files changed, 86 insertions(+), 29 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java index d753d3e8c8..7becc88c90 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java @@ -21,12 +21,21 @@ public class RealmDemoData extends RealmObject { 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; @@ -78,6 +87,17 @@ public RealmDemoData(float xValue, float yValue, float bubbleSize) { 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; } @@ -150,4 +170,11 @@ 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/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java index 29007c8c97..5d5eb0d06a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -195,10 +195,10 @@ protected void writeToDBPie() { float value5 = 100f - value1 - value2 - value3 - value4; float[] values = new float[] { value1, value2, value3, value4, value5 }; - String[] xValues = new String[]{ "iOS", "Android", "WP 10", "BlackBerry", "Other"}; + String[] labels = new String[]{ "iOS", "Android", "WP 10", "BlackBerry", "Other"}; for (int i = 0; i < values.length; i++) { - RealmDemoData d = new RealmDemoData(i, values[i]); + RealmDemoData d = new RealmDemoData(values[i], labels[i]); mRealm.copyToRealm(d); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java index c8f7abb84d..1dd544fcce 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java @@ -54,7 +54,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); RealmCandleDataSet set = new RealmCandleDataSet(result, "xValue", "high", "low", "open", "close"); - set.setLabel("Realm Realm CandleDataSet"); + set.setLabel("Realm CandleDataSet"); set.setShadowColor(Color.DKGRAY); set.setShadowWidth(0.7f); set.setDecreasingColor(Color.RED); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java index bb97dd6681..8bcda98356 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java @@ -54,7 +54,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmPieDataSet set = new RealmPieDataSet(result, "yValue"); // stacked entries + RealmPieDataSet set = new RealmPieDataSet(result, "yValue", "label"); // stacked entries 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 5995ebb5a2..fe408bdc00 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java @@ -34,6 +34,7 @@ protected void onCreate(Bundle savedInstanceState) { setup(mChart); mChart.getYAxis().setEnabled(false); + mChart.getXAxis().setEnabled(false); mChart.setWebAlpha(180); mChart.setWebColorInner(Color.DKGRAY); mChart.setWebColor(Color.GRAY); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java index 7a9386bfbe..06408f6878 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java @@ -48,9 +48,12 @@ protected void onCreate(Bundle savedInstanceState) { 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().setCenterAxisLabels(true); + barChart.getXAxis().setLabelCount(5); + barChart.getXAxis().setGranularity(1f); } @Override @@ -130,6 +133,7 @@ public int getDecimalDigits() { styleData(barData); barChart.setData(barData); + barChart.setFitBars(true); barChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java index 9024bb6389..1f0c4b064b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java @@ -31,11 +31,11 @@ public RealmLineScatterCandleRadarDataSet(RealmResults results, String yValue * Constructor that takes the realm RealmResults, sorts & stores them. * * @param results + * @param xValueField * @param yValuesField - * @param xIndexField */ - public RealmLineScatterCandleRadarDataSet(RealmResults results, String yValuesField, String xIndexField) { - super(results, yValuesField, xIndexField); + public RealmLineScatterCandleRadarDataSet(RealmResults results, String xValueField, String yValuesField) { + super(results, xValueField, yValuesField); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java index e4288af115..1328fb1e4f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java @@ -15,7 +15,8 @@ /** * Created by Philipp Jahoda on 07/11/15. */ -public class RealmCandleDataSet extends RealmLineScatterCandleRadarDataSet implements ICandleDataSet { +public class RealmCandleDataSet extends RealmLineScatterCandleRadarDataSet + implements ICandleDataSet { private String mHighField; private String mLowField; @@ -30,7 +31,7 @@ public class RealmCandleDataSet extends RealmLineScatterC /** * should the candle bars show? * when false, only "ticks" will show - * + *

* - default: true */ private boolean mShowCandleBar = true; @@ -82,12 +83,13 @@ public class RealmCandleDataSet extends RealmLineScatterC * Constructor for creating a LineDataSet with realm data. * * @param result the queried results from the realm database - * @param highField the name of the field in your data object that represents the "high" yValue - * @param lowField the name of the field in your data object that represents the "low" yValue - * @param openField the name of the field in your data object that represents the "open" yValue - * @param closeField the name of the field in your data object that represents the "close" yValue + * @param highField the name of the field in your data object that represents the "high" value + * @param lowField the name of the field in your data object that represents the "low" value + * @param openField the name of the field in your data object that represents the "open" value + * @param closeField the name of the field in your data object that represents the "close" value */ - public RealmCandleDataSet(RealmResults result, String highField, String lowField, String openField, String closeField) { + public RealmCandleDataSet(RealmResults result, String highField, String lowField, String openField, String + closeField) { super(result, null); this.mHighField = highField; this.mLowField = lowField; @@ -102,14 +104,15 @@ public RealmCandleDataSet(RealmResults result, String highField, String lowFi * Constructor for creating a LineDataSet with realm data. * * @param result the queried results from the realm database - * @param highField the name of the field in your data object that represents the "high" yValue - * @param lowField the name of the field in your data object that represents the "low" yValue - * @param openField the name of the field in your data object that represents the "open" yValue - * @param closeField the name of the field in your data object that represents the "close" yValue - * @param xIndexField the name of the field in your data object that represents the xPx-index + * @param xValueField the name of the field in your data object that represents the "x" value + * @param highField the name of the field in your data object that represents the "high" value + * @param lowField the name of the field in your data object that represents the "low" value + * @param openField the name of the field in your data object that represents the "open" value + * @param closeField the name of the field in your data object that represents the "close" value */ - public RealmCandleDataSet(RealmResults result, String highField, String lowField, String openField, String closeField, String xIndexField) { - super(result, null, xIndexField); + public RealmCandleDataSet(RealmResults result, String xValueField, String highField, String lowField, String openField, String + closeField) { + super(result, xValueField, null); this.mHighField = highField; this.mLowField = lowField; this.mOpenField = openField; @@ -119,11 +122,12 @@ public RealmCandleDataSet(RealmResults result, String highField, String lowFi calcMinMax(); } - public CandleEntry buildEntryFromResultObject(T realmObject, int xIndex) { + @Override + public CandleEntry buildEntryFromResultObject(T realmObject, float x) { DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); return new CandleEntry( - mXValuesField == null ? xIndex : dynamicObject.getFloat(mXValuesField), + mXValuesField == null ? x : dynamicObject.getFloat(mXValuesField), dynamicObject.getFloat(mHighField), dynamicObject.getFloat(mLowField), dynamicObject.getFloat(mOpenField), @@ -165,7 +169,7 @@ public void calcMinMax() { mYMax = 0.f; } - if(mXMin == Float.MAX_VALUE) { + if (mXMin == Float.MAX_VALUE) { mXMin = 0.f; mXMax = 0.f; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java index 4d22de97c8..01d923eef5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java @@ -1,5 +1,6 @@ package com.github.mikephil.charting.data.realm.implementation; +import com.github.mikephil.charting.data.CandleEntry; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.data.PieEntry; @@ -35,6 +36,8 @@ public class RealmPieDataSet extends RealmBaseDataSet result, String yValuesField) { calcMinMax(); } + public RealmPieDataSet(RealmResults result, String yValuesField, String labelField) { + super(result, yValuesField); + this.mLabelField = labelField; + build(this.results); + calcMinMax(); + } + + @Override + public PieEntry buildEntryFromResultObject(T realmObject, float x) { + DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); + + if(mLabelField == null) { + return new PieEntry(dynamicObject.getFloat(mYValuesField)); + } else { + return new PieEntry(dynamicObject.getFloat(mYValuesField), dynamicObject.getString(mLabelField)); + } + } + /** * 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/renderer/PieChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java index 43f70b0809..dfe3a96f66 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 @@ -506,13 +506,13 @@ public void drawValues(Canvas c) { labelPty, dataSet.getValueTextColor(j)); - if (j < data.getEntryCount()) { + if (j < data.getEntryCount() && entry.getLabel() != null) { c.drawText(entry.getLabel(), labelPtx, labelPty + lineHeight, mValuePaint); } } else if (drawXOutside) { - if (j < data.getEntryCount()) { + if (j < data.getEntryCount() && entry.getLabel() != null) { mValuePaint.setColor(dataSet.getValueTextColor(j)); c.drawText(entry.getLabel(), labelPtx, labelPty + lineHeight / 2.f, mValuePaint); } @@ -535,13 +535,13 @@ public void drawValues(Canvas c) { drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j)); - if (j < data.getEntryCount()) { + if (j < data.getEntryCount() && entry.getLabel() != null) { c.drawText(entry.getLabel(), x, y + lineHeight, mValuePaint); } } else if (drawXInside) { - if (j < data.getEntryCount()) { + if (j < data.getEntryCount() && entry.getLabel() != null) { mValuePaint.setColor(dataSet.getValueTextColor(j)); c.drawText(entry.getLabel(), x, y + lineHeight / 2f, mValuePaint); } From 3076f259f96551ea3fa3d660d24a83a622c51ef4 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 4 Jun 2016 10:28:30 +0200 Subject: [PATCH 172/606] Fix drawing of horizontal x-axis grid lines --- .../HorizontalBarChartActivity.java | 4 +- .../XAxisRendererHorizontalBarChart.java | 76 +++++-------------- 2 files changed, 21 insertions(+), 59 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index b571fc7d2b..9797ae799f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -83,7 +83,7 @@ protected void onCreate(Bundle savedInstanceState) { xl.setPosition(XAxisPosition.BOTTOM); xl.setTypeface(tf); xl.setDrawAxisLine(true); - xl.setDrawGridLines(true); + xl.setDrawGridLines(false); xl.setGridLineWidth(0.3f); xl.setGranularity(10f); @@ -103,6 +103,7 @@ protected void onCreate(Bundle savedInstanceState) { // yr.setInverted(true); setData(12, 50); + mChart.setFitBars(true); mChart.animateY(2500); // setting data @@ -212,6 +213,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { tvY.setText("" + (mSeekBarY.getProgress())); setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); + mChart.setFitBars(true); mChart.invalidate(); } 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 4b40a35778..3985343ed1 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 @@ -152,73 +152,33 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { } } -// /** -// * draws the xPx-labels on the specified yPx-position -// * -// * @param pos -// */ -// @Override -// protected void drawLabels(Canvas c, float pos, PointF anchor) { -// -// final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); -// -// // pre allocate to save performance (dont allocate in loop) -// float[] position = new float[] { -// 0f, 0f -// }; -// -// BarData bd = mChart.getData(); -// int step = bd.getDataSetCount(); -// -// for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { -// -// position[1] = i * step + i * bd.getGroupSpace() -// + bd.getGroupSpace() / 2f; -// -// // consider groups (center label for each group) -// if (step > 1) { -// position[1] += ((float) step - 1f) / 2f; -// } -// -// mTrans.pointValuesToPixel(position); -// -// if (mViewPortHandler.isInBoundsY(position[1])) { -// -// String label = mXAxis.getValues().get(i).getLabel(); -// drawLabel(c, label, i, pos, position[1], anchor, labelRotationAngleDegrees); -// } -// } -// } - @Override public void renderGridLines(Canvas c) { if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) return; - - float[] position = new float[] { - 0f, 0f - }; mGridPaint.setColor(mXAxis.getGridColor()); mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); - BarData bd = mChart.getData(); - // take into consideration that multiple DataSets increase mDeltaX - int step = bd.getDataSetCount(); - -// for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { -// -// position[1] = i * step + i * bd.getGroupSpace() - 0.5f; -// -// mTrans.pointValuesToPixel(position); -// -// if (mViewPortHandler.isInBoundsY(position[1])) { -// -// c.drawLine(mViewPortHandler.contentLeft(), position[1], -// mViewPortHandler.contentRight(), position[1], mGridPaint); -// } -// } + float[] positions = new float[mXAxis.mEntryCount * 2]; + + for (int i = 0; i < positions.length; i += 2) { + positions[i + 1] = mXAxis.mEntries[i / 2]; + } + + mTrans.pointValuesToPixel(positions); + + for (int i = 0; i < positions.length; i += 2) { + + float y = positions[i + 1]; + + if (mViewPortHandler.isInBoundsY(y)) { + + c.drawLine(mViewPortHandler.contentLeft(), y, + mViewPortHandler.contentRight(), y, mGridPaint); + } + } } @Override From 084935d9e30334d8c8937fc1ff76e1cf7a56c1fa Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 4 Jun 2016 11:14:39 +0200 Subject: [PATCH 173/606] Cleanup y-axis renderers --- .../HorizontalBarChartActivity.java | 1 - .../renderer/XAxisRendererBarChart.java | 73 ---------------- .../charting/renderer/YAxisRenderer.java | 84 +++++++++++-------- .../YAxisRendererHorizontalBarChart.java | 62 +++++++------- 4 files changed, 76 insertions(+), 144 deletions(-) delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 9797ae799f..3d6905f757 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -91,7 +91,6 @@ protected void onCreate(Bundle savedInstanceState) { yl.setTypeface(tf); yl.setDrawAxisLine(true); yl.setDrawGridLines(true); - yl.setGridLineWidth(0.3f); yl.setAxisMinValue(0f); // this replaces setStartAtZero(true) // yl.setInverted(true); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java deleted file mode 100644 index 0be78ca0e9..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java +++ /dev/null @@ -1,73 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -//public class XAxisRendererBarChart extends XAxisRenderer { -// -// protected BarChart mChart; -// -// public XAxisRendererBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans, -// BarChart chart) { -// super(viewPortHandler, xAxis, trans); -// -// this.mChart = chart; -// } -// -// /** -// * draws the xPx-labels on the specified yPx-position -// * -// * @param pos -// */ -// @Override -// protected void drawLabels(Canvas c, float pos, PointF anchor) { -// -// final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); -// -// // pre allocate to save performance (dont allocate in loop) -// float[] position = new float[] { -// 0f, 0f -// }; -// -// BarData bd = mChart.getData(); -// int step = bd.getDataSetCount(); -// -//// for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { -//// -//// position[0] = i * step + i * bd.getGroupSpace() -//// + bd.getGroupSpace() / 2f; -//// -//// // consider groups (center label for each group) -//// if (step > 1) { -//// position[0] += ((float) step - 1f) / 2f; -//// } -//// -//// mTrans.pointValuesToPixel(position); -//// -//// if (mViewPortHandler.isInBoundsX(position[0]) && i >= 0 -//// && i < mXAxis.getValues().size()) { -//// -//// String label = mXAxis.getValues().get(i).getLabel(); -//// -//// if (mXAxis.isAvoidFirstLastClippingEnabled()) { -//// -//// // avoid clipping of the last -//// if (i == mXAxis.getValues().size() - 1) { -//// float width = Utils.calcTextWidth(mAxisLabelPaint, label); -//// -//// if (position[0] + width / 2.f > mViewPortHandler.contentRight()) -//// position[0] = mViewPortHandler.contentRight() - (width / 2.f); -//// -//// // avoid clipping of the first -//// } else if (i == 0) { -//// -//// float width = Utils.calcTextWidth(mAxisLabelPaint, label); -//// -//// if (position[0] - width / 2.f < mViewPortHandler.contentLeft()) -//// position[0] = mViewPortHandler.contentLeft() + (width / 2.f); -//// } -//// } -//// -//// drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees); -//// } -//// } -// } -//} 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 cf4c30ddb7..372174c3be 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 @@ -147,16 +147,7 @@ public void renderAxisLabels(Canvas c) { if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) return; - float[] positions = new float[mYAxis.mEntryCount * 2]; - - for (int i = 0; i < positions.length; i += 2) { - // only fill yPx values, xPx values are not needed since the yPx-labels - // are - // static on the xPx-axis - positions[i + 1] = mYAxis.mEntries[i / 2]; - } - - mTrans.pointValuesToPixel(positions); + float[] positions = getTransformedPositions(); mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); @@ -238,61 +229,80 @@ public void renderGridLines(Canvas c) { if (!mYAxis.isEnabled()) return; - // pre alloc - float[] position = new float[2]; - if (mYAxis.isDrawGridLinesEnabled()) { + float[] positions = getTransformedPositions(); + mGridPaint.setColor(mYAxis.getGridColor()); mGridPaint.setStrokeWidth(mYAxis.getGridLineWidth()); mGridPaint.setPathEffect(mYAxis.getGridDashPathEffect()); Path gridLinePath = new Path(); - // draw the horizontal grid - for (int i = 0; i < mYAxis.mEntryCount; i++) { - - position[1] = mYAxis.mEntries[i]; - mTrans.pointValuesToPixel(position); - - gridLinePath.moveTo(mViewPortHandler.offsetLeft(), position[1]); - gridLinePath.lineTo(mViewPortHandler.contentRight(), position[1]); + // draw the grid + for (int i = 0; i < positions.length; i += 2) { // draw a path because lines don't support dashing on lower android versions - c.drawPath(gridLinePath, mGridPaint); - + c.drawPath(linePath(gridLinePath, i, positions), mGridPaint); gridLinePath.reset(); } } if (mYAxis.isDrawZeroLineEnabled()) { + drawZeroLine(c); + } + } + + /** + * Calculates the path for a grid line. + * + * @param p + * @param i + * @param positions + * @return + */ + protected Path linePath(Path p, int i, float[] positions) { - // draw zero line - position[1] = 0f; - mTrans.pointValuesToPixel(position); + p.moveTo(mViewPortHandler.offsetLeft(), positions[i + 1]); + p.lineTo(mViewPortHandler.contentRight(), positions[i + 1]); - drawZeroLine(c, mViewPortHandler.offsetLeft(), mViewPortHandler.contentRight(), position[1] - 1, position[1] - 1); - } + return p; } /** - * Draws the zero line at the specified position. + * Transforms the values contained in the axis entries to screen pixels and returns them in form of a float array + * of x- and y-coordinates. * - * @param c - * @param x1 - * @param x2 - * @param y1 - * @param y2 + * @return */ - protected void drawZeroLine(Canvas c, float x1, float x2, float y1, float y2) { + protected float[] getTransformedPositions() { + + float[] positions = new float[mYAxis.mEntryCount * 2]; + + for (int i = 0; i < positions.length; i += 2) { + // only fill y values, x values are not needed for y-labels + positions[i + 1] = mYAxis.mEntries[i / 2]; + } + + mTrans.pointValuesToPixel(positions); + return positions; + } + + /** + * Draws the zero line. + */ + protected void drawZeroLine(Canvas c) { + + // draw zero line + PointD pos = mTrans.getPixelsForValues(0f, 0f); mZeroLinePaint.setColor(mYAxis.getZeroLineColor()); mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth()); Path zeroLinePath = new Path(); - zeroLinePath.moveTo(x1, y1); - zeroLinePath.lineTo(x2, y2); + zeroLinePath.moveTo(mViewPortHandler.contentLeft(), (float) pos.y - 1); + zeroLinePath.lineTo(mViewPortHandler.contentRight(), (float) pos.y - 1); // draw a path because lines don't support dashing on lower android versions c.drawPath(zeroLinePath, mZeroLinePaint); 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 330ac05eb6..7b5ff68b8a 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 @@ -66,16 +66,7 @@ public void renderAxisLabels(Canvas c) { if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) return; - float[] positions = new float[mYAxis.mEntryCount * 2]; - - for (int i = 0; i < positions.length; i += 2) { - // only fill yPx values, xPx values are not needed since the yPx-labels - // are - // static on the xPx-axis - positions[i] = mYAxis.mEntries[i / 2]; - } - - mTrans.pointValuesToPixel(positions); + float[] positions = getTransformedPositions(); mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); @@ -155,39 +146,44 @@ protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, flo } @Override - public void renderGridLines(Canvas c) { + protected float[] getTransformedPositions() { - if (!mYAxis.isEnabled()) - return; + float[] positions = new float[mYAxis.mEntryCount * 2]; + + for (int i = 0; i < positions.length; i += 2) { + // only fill x values, y values are not needed for x-labels + positions[i] = mYAxis.mEntries[i / 2]; + } - // pre alloc - float[] position = new float[2]; + mTrans.pointValuesToPixel(positions); + return positions; + } - if (mYAxis.isDrawGridLinesEnabled()) { + @Override + protected Path linePath(Path p, int i, float[] positions) { - mGridPaint.setColor(mYAxis.getGridColor()); - mGridPaint.setStrokeWidth(mYAxis.getGridLineWidth()); + p.moveTo(positions[i], mViewPortHandler.contentTop()); + p.lineTo(positions[i], mViewPortHandler.contentBottom()); - // draw the horizontal grid - for (int i = 0; i < mYAxis.mEntryCount; i++) { + return p; + } - position[0] = mYAxis.mEntries[i]; - mTrans.pointValuesToPixel(position); + @Override + protected void drawZeroLine(Canvas c) { - c.drawLine(position[0], mViewPortHandler.contentTop(), position[0], - mViewPortHandler.contentBottom(), - mGridPaint); - } - } + // draw zero line + PointD pos = mTrans.getPixelsForValues(0f, 0f); - if (mYAxis.isDrawZeroLineEnabled()) { + mZeroLinePaint.setColor(mYAxis.getZeroLineColor()); + mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth()); - // draw zero line - position[0] = 0f; - mTrans.pointValuesToPixel(position); + Path zeroLinePath = new Path(); - drawZeroLine(c, position[0]+1, position[0]+1, mViewPortHandler.contentTop(), mViewPortHandler.contentBottom()); - } + zeroLinePath.moveTo((float) pos.x - 1, mViewPortHandler.contentTop()); + zeroLinePath.lineTo((float) pos.x - 1, mViewPortHandler.contentBottom()); + + // draw a path because lines don't support dashing on lower android versions + c.drawPath(zeroLinePath, mZeroLinePaint); } /** From 66434c6a8eaccce157d7da58558a945fff3e9f2d Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 4 Jun 2016 14:18:43 +0200 Subject: [PATCH 174/606] Test axis renderer --- .../mikephil/charting/components/XAxis.java | 36 ------ .../charting/renderer/AxisRenderer.java | 29 +++-- .../charting/renderer/YAxisRenderer.java | 15 ++- .../charting/test/AxisRendererTest.java | 111 ++++++++++++++++++ 4 files changed, 136 insertions(+), 55 deletions(-) create mode 100644 MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java index be48c164b7..5a21292866 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java @@ -115,40 +115,4 @@ public void setAvoidFirstLastClipping(boolean enabled) { public boolean isAvoidFirstLastClippingEnabled() { return mAvoidFirstLastClipping; } - -// /** -// * Sets the labels for this axis. -// * -// * @param values -// */ -// public void setValues(List values) { -// mValues = values; -// } -// -// /** -// * Returns the labels for this axis. -// * -// * @return -// */ -// public List getValues() { -// return mValues; -// } -// -// /** -// * Adds a new xPx-yValue to the chart data. -// * -// * @param xVal -// */ -// public void addXValue(XAxisValue xVal) { -// mValues.add(xVal); -// } -// -// /** -// * Removes the xPx-yValue at the specified index. -// * -// * @param index -// */ -// public void removeXValue(int index) { -// mValues.remove(index); -// } } 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 26260c320e..0c641a5ffd 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 @@ -51,21 +51,24 @@ public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans, AxisBase this.mTrans = trans; this.mAxis = axis; - mAxisLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + if(mTrans != null) { - mGridPaint = new Paint(); - mGridPaint.setColor(Color.GRAY); - mGridPaint.setStrokeWidth(1f); - mGridPaint.setStyle(Style.STROKE); - mGridPaint.setAlpha(90); + mAxisLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mAxisLinePaint = new Paint(); - mAxisLinePaint.setColor(Color.BLACK); - mAxisLinePaint.setStrokeWidth(1f); - mAxisLinePaint.setStyle(Style.STROKE); + mGridPaint = new Paint(); + mGridPaint.setColor(Color.GRAY); + mGridPaint.setStrokeWidth(1f); + mGridPaint.setStyle(Style.STROKE); + mGridPaint.setAlpha(90); - mLimitLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mLimitLinePaint.setStyle(Paint.Style.STROKE); + mAxisLinePaint = new Paint(); + mAxisLinePaint.setColor(Color.BLACK); + mAxisLinePaint.setStrokeWidth(1f); + mAxisLinePaint.setStyle(Style.STROKE); + + mLimitLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mLimitLinePaint.setStyle(Paint.Style.STROKE); + } } /** @@ -116,7 +119,7 @@ public void computeAxis(float min, float max, boolean inverted) { // calculate the starting and entry point of the yPx-labels (depending on // zoom / contentrect bounds) - if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { + if (mViewPortHandler != null && mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { PointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); 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 372174c3be..f5b1539af1 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 @@ -28,13 +28,16 @@ public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer t this.mYAxis = yAxis; - mAxisLabelPaint.setColor(Color.BLACK); - mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); + if(mTrans != null) { - mZeroLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mZeroLinePaint.setColor(Color.GRAY); - mZeroLinePaint.setStrokeWidth(1f); - mZeroLinePaint.setStyle(Paint.Style.STROKE); + mAxisLabelPaint.setColor(Color.BLACK); + mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); + + mZeroLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mZeroLinePaint.setColor(Color.GRAY); + mZeroLinePaint.setStrokeWidth(1f); + mZeroLinePaint.setStyle(Paint.Style.STROKE); + } } // @Override diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java new file mode 100644 index 0000000000..b50bd4f9a5 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java @@ -0,0 +1,111 @@ +package com.github.mikephil.charting.test; + +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.data.ScatterDataSet; +import com.github.mikephil.charting.renderer.AxisRenderer; +import com.github.mikephil.charting.renderer.YAxisRenderer; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by philipp on 31/05/16. + */ +public class AxisRendererTest { + + + @Test + public void testComputeAxisValues() { + + YAxis yAxis = new YAxis(); + yAxis.setLabelCount(6); + AxisRenderer renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 100, false); + float[] entries = yAxis.mEntries; + + assertEquals(6, entries.length); + assertEquals(20, entries[1] - entries[0], 0.01); // interval 20 + assertEquals(0, entries[0], 0.01); + assertEquals(100, entries[entries.length - 1], 0.01); + + yAxis = new YAxis(); + yAxis.setLabelCount(6); + yAxis.setGranularity(50f); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 100, false); + entries = yAxis.mEntries; + + assertEquals(3, entries.length); + assertEquals(50, entries[1] - entries[0], 0.01); // interval 50 + assertEquals(0, entries[0], 0.01); + assertEquals(100, entries[entries.length - 1], 0.01); + + yAxis = new YAxis(); + yAxis.setLabelCount(5, true); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 100, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(25, entries[1] - entries[0], 0.01); // interval 25 + assertEquals(0, entries[0], 0.01); + assertEquals(100, entries[entries.length - 1], 0.01); + + yAxis = new YAxis(); + yAxis.setLabelCount(5, true); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 0.01f, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(0.0025, entries[1] - entries[0], 0.0001); + assertEquals(0, entries[0], 0.0001); + assertEquals(0.01, entries[entries.length - 1], 0.0001); + + yAxis = new YAxis(); + yAxis.setLabelCount(5, false); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 0.01f, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(0.0020, entries[1] - entries[0], 0.0001); + assertEquals(0, entries[0], 0.0001); + assertEquals(0.0080, entries[entries.length - 1], 0.0001); + + yAxis = new YAxis(); + yAxis.setLabelCount(6); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(-50, 50, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(-40, entries[0], 0.0001); + assertEquals(0, entries[2], 0.0001); + assertEquals(40, entries[entries.length - 1], 0.0001); + + yAxis = new YAxis(); + yAxis.setLabelCount(6); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(-50, 100, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(-30, entries[0], 0.0001); + assertEquals(30, entries[2], 0.0001); + assertEquals(90, entries[entries.length - 1], 0.0001); + } +} From b5cf9e120b5b0025b4729459a2902ca6760d3c46 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 4 Jun 2016 14:31:47 +0200 Subject: [PATCH 175/606] Remove highlight-arrow feature, this can be done by markerview --- MPChartExample/res/menu/bar.xml | 4 -- .../mpchartexample/AnotherBarActivity.java | 8 ---- .../mpchartexample/BarChartActivity.java | 8 ---- .../BarChartActivityMultiDataset.java | 8 ---- .../mpchartexample/BarChartActivitySinus.java | 8 ---- .../HorizontalBarChartActivity.java | 8 ---- .../mpchartexample/StackedBarActivity.java | 8 ---- .../StackedBarActivityNegative.java | 8 ---- .../mikephil/charting/charts/BarChart.java | 34 +++++++------- .../charting/charts/CombinedChart.java | 5 -- .../dataprovider/BarDataProvider.java | 1 - .../charting/renderer/BarChartRenderer.java | 47 +++++++++---------- 12 files changed, 40 insertions(+), 107 deletions(-) diff --git a/MPChartExample/res/menu/bar.xml b/MPChartExample/res/menu/bar.xml index 4b35e2ff5d..9e0b3c3ce1 100644 --- a/MPChartExample/res/menu/bar.xml +++ b/MPChartExample/res/menu/bar.xml @@ -9,10 +9,6 @@ android:id="@+id/actionToggleHighlight" android:title="Toggle Highlight"> - - diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 3daac14bc0..fe967ffb8d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -130,14 +130,6 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } case R.id.animateX: { mChart.animateX(3000); break; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 3a9b9e3533..80dd81db2b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -178,14 +178,6 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } case R.id.animateX: { mChart.animateX(3000); break; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 71328613d6..ccaf24b348 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -151,14 +151,6 @@ public boolean onOptionsItemSelected(MenuItem item) { } break; } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } case R.id.actionSave: { // mChart.saveToGallery("title"+System.currentTimeMillis()); mChart.saveToPath("title" + System.currentTimeMillis(), ""); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index 09c03e3cef..c9f6e3c48e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -152,14 +152,6 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } case R.id.animateX: { mChart.animateX(1500); break; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 3d6905f757..738e2ab40c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -171,14 +171,6 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } case R.id.animateX: { mChart.animateX(3000); break; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 2e281d797e..2b75a119c8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -146,14 +146,6 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } case R.id.animateX: { mChart.animateX(3000); break; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 9aa82fc614..45f040c168 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -179,14 +179,6 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.invalidate(); break; } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } case R.id.animateX: { mChart.animateX(3000); break; 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 d02c947bac..5187051d44 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 @@ -129,23 +129,23 @@ public RectF getBarBounds(BarEntry e) { return bounds; } - /** - * set this to true to draw the highlightning arrow - * - * @param enabled - */ - public void setDrawHighlightArrow(boolean enabled) { - mDrawHighlightArrow = enabled; - } - - /** - * returns true if drawing the highlighting arrow is enabled, false if not - * - * @return - */ - public boolean isDrawHighlightArrowEnabled() { - return mDrawHighlightArrow; - } +// /** +// * set this to true to draw the highlightning arrow +// * +// * @param enabled +// */ +// public void setDrawHighlightArrow(boolean enabled) { +// mDrawHighlightArrow = enabled; +// } +// +// /** +// * returns true if drawing the highlighting arrow is enabled, false if not +// * +// * @return +// */ +// public boolean isDrawHighlightArrowEnabled() { +// return mDrawHighlightArrow; +// } /** * If set to true, all values are drawn above their bars, instead of below their top. 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 a5f6d77de6..f6419d9cb7 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 @@ -167,11 +167,6 @@ public boolean isDrawValueAboveBarEnabled() { return mDrawValueAboveBar; } - @Override - public boolean isDrawHighlightArrowEnabled() { - return mDrawHighlightArrow; - } - /** * set this to true to draw the highlightning arrow * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java index 2f0675fc4a..631060d2d6 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java @@ -7,5 +7,4 @@ public interface BarDataProvider extends BarLineScatterCandleBubbleDataProvider BarData getBarData(); boolean isDrawBarShadowEnabled(); boolean isDrawValueAboveBarEnabled(); - boolean isDrawHighlightArrowEnabled(); } 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 8c81fb5eba..a946467426 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 @@ -3,7 +3,9 @@ import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Matrix; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.RectF; import com.github.mikephil.charting.animation.ChartAnimator; @@ -236,7 +238,8 @@ public void drawValues(Canvas c) { 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 + val >= 0 ? (buffer.buffer[j + 1] + posOffset) : (buffer.buffer[j + 3] + negOffset), + dataSet.getValueTextColor (j / 4)); } @@ -355,7 +358,6 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { float x = high.getX(); BarEntry e = set.getEntryForXPos(x); - int entryIndex = set.getEntryIndex(e); if (e == null) continue; @@ -375,37 +377,34 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans); - //prepareBarHighlight(y1, y2, interval, entryIndex, dataSetIndex, setCount, barSpace, groupSpace, - // trans); - c.drawRect(mBarRect, mHighlightPaint); -// if (mChart.isDrawHighlightArrowEnabled()) { +// if (mChart.isDrawHighlightArrowEnabled()) { // -// mHighlightPaint.setAlpha(255); +// mHighlightPaint.setAlpha(255); // -// // distance between highlight arrow and bar -// float offsetY = mAnimator.getPhaseY() * 0.07f; +// // distance between highlight arrow and bar +// float offsetY = mAnimator.getPhaseY() * 0.07f; // -// float[] values = new float[9]; -// trans.getPixelToValueMatrix().getValues(values); -// final float xToYRel = Math.abs( -// values[Matrix.MSCALE_Y] / values[Matrix.MSCALE_X]); +// float[] values = new float[9]; +// trans.getPixelToValueMatrix().getValues(values); +// final float xToYRel = Math.abs( +// values[Matrix.MSCALE_Y] / values[Matrix.MSCALE_X]); // -// final float arrowWidth = set.getBarSpace() / 2.f; -// final float arrowHeight = arrowWidth * xToYRel; +// final float arrowWidth = barData.getBarWidth(); +// final float arrowWidthHalf = arrowWidth / 2f; +// final float arrowHeight = arrowWidth * xToYRel; // -// final float yArrow = (y1 > -y2 ? y1 : y1) * mAnimator.getPhaseY(); +// final float yArrow = (y1 > -y2 ? y1 : y1) * mAnimator.getPhaseY(); // -// Path arrow = new Path(); -// arrow.moveTo(xPx + 0.4f, yArrow + offsetY); -// arrow.lineTo(xPx + 0.4f + arrowWidth, yArrow + offsetY - arrowHeight); -// arrow.lineTo(xPx + 0.4f + arrowWidth, yArrow + offsetY + arrowHeight); +// Path arrow = new Path(); +// arrow.moveTo(e.getX() - arrowWidthHalf, yArrow + offsetY + arrowHeight); +// arrow.lineTo(e.getX(), yArrow + offsetY); +// arrow.lineTo(e.getX() + arrowWidthHalf, yArrow + offsetY + arrowHeight); // -// trans.pathValueToPixel(arrow); -// c.drawPath(arrow, mHighlightPaint); -// } - +// trans.pathValueToPixel(arrow); +// c.drawPath(arrow, mHighlightPaint); +// } } } From cee380be6cdd57ce00d0078e27ba6b7dfa6d3a87 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 4 Jun 2016 14:44:39 +0200 Subject: [PATCH 176/606] Fix issue in highlight animation for barchart --- .../charting/renderer/BarChartRenderer.java | 5 +++-- .../renderer/HorizontalBarChartRenderer.java | 2 +- .../github/mikephil/charting/utils/Transformer.java | 13 ++++++++++++- 3 files changed, 16 insertions(+), 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 a946467426..899ef7ce0b 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 @@ -178,7 +178,7 @@ protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHa mBarRect.set(left, top, right, bottom); - trans.rectValueToPixel(mBarRect, mAnimator.getPhaseY()); + trans.rectToPixelPhase(mBarRect, mAnimator.getPhaseY()); } @Override @@ -358,8 +358,9 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { float x = high.getX(); BarEntry e = set.getEntryForXPos(x); + float entryIndex = set.getEntryIndex(e); - if (e == null) + if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX()) continue; boolean isStack = high.getStackIndex() < 0 ? false : true; 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 8c1d64dd73..e4cb3150d2 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 @@ -288,7 +288,7 @@ protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHa mBarRect.set(left, top, right, bottom); - trans.rectValueToPixel(mBarRect, mAnimator.getPhaseY()); + trans.rectToPixelPhaseHorizontal(mBarRect, mAnimator.getPhaseY()); } @Override 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 40d738e9d6..189de13cd1 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 @@ -300,7 +300,7 @@ public void rectValueToPixel(RectF r) { * @param r * @param phaseY */ - public void rectValueToPixel(RectF r, float phaseY) { + public void rectToPixelPhase(RectF r, float phaseY) { // multiply the height of the rect with the phase r.top *= phaseY; @@ -311,6 +311,17 @@ public void rectValueToPixel(RectF r, float phaseY) { mMatrixOffset.mapRect(r); } + public void rectToPixelPhaseHorizontal(RectF r, float phaseY) { + + // multiply the height of the rect with the phase + r.left *= phaseY; + r.right *= phaseY; + + mMatrixValueToPx.mapRect(r); + mViewPortHandler.getMatrixTouch().mapRect(r); + mMatrixOffset.mapRect(r); + } + /** * Transform a rectangle with all matrices with potential animation phases. * From 1dd8c901d613bff9fc025d05bde81a4b8235f8fd Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 4 Jun 2016 14:51:39 +0200 Subject: [PATCH 177/606] Documentation cleanup --- .../charting/charts/BarLineChartBase.java | 49 +++++++++---------- .../dataprovider/ChartInterface.java | 8 +-- 2 files changed, 28 insertions(+), 29 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 f50200ab39..e4676a5b71 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 @@ -655,7 +655,7 @@ public void zoom(float scaleX, float scaleY, float x, float y) { /** * Zooms in or out by the given scale factor. - * xPx and yPx are the values (NOT PIXELS) which to zoom to or from (the values of the zoom center). + * x and y are the values (NOT PIXELS) which to zoom to or from (the values of the zoom center). * * @param scaleX * @param scaleY @@ -789,11 +789,11 @@ public void moveViewToX(float xIndex) { } /** - * Centers the viewport to the specified yPx-yValue on the yPx-axis. + * Centers the viewport to the specified y value on the y-axis. * This also refreshes the chart by calling invalidate(). * * @param yValue - * @param axis - which axis should be used as a reference for the yPx-axis + * @param axis - which axis should be used as a reference for the y-axis */ public void moveViewToY(float yValue, AxisDependency axis) { @@ -807,36 +807,35 @@ public void moveViewToY(float yValue, AxisDependency axis) { /** * This will move the left side of the current viewport to the specified - * xPx-yValue on the xPx-axis, and center the viewport to the specified yPx-yValue - * on the yPx-axis. + * x value on the x-axis, and center the viewport to the specified y value on the y-axis. * This also refreshes the chart by calling invalidate(). * - * @param xIndex + * @param xValue * @param yValue - * @param axis - which axis should be used as a reference for the yPx-axis + * @param axis - which axis should be used as a reference for the y-axis */ - public void moveViewTo(float xIndex, float yValue, AxisDependency axis) { + public void moveViewTo(float xValue, float yValue, AxisDependency axis) { float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - Runnable job = new MoveViewJob(mViewPortHandler, xIndex, yValue + valsInView / 2f, + Runnable job = new MoveViewJob(mViewPortHandler, xValue, yValue + valsInView / 2f, getTransformer(axis), this); addViewportJob(job); } /** - * This will move the left side of the current viewport to the specified xPx-position - * and center the viewport to the specified yPx-position animated. + * This will move the left side of the current viewport to the specified x value + * and center the viewport to the y value animated. * This also refreshes the chart by calling invalidate(). * - * @param xIndex + * @param xValue * @param yValue * @param axis * @param duration the duration of the animation in milliseconds */ @TargetApi(11) - public void moveViewToAnimated(float xIndex, float yValue, AxisDependency axis, long duration) { + public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration) { if (android.os.Build.VERSION.SDK_INT >= 11) { @@ -844,7 +843,7 @@ public void moveViewToAnimated(float xIndex, float yValue, AxisDependency axis, float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - Runnable job = new AnimatedMoveViewJob(mViewPortHandler, xIndex, yValue + valsInView / 2f, + Runnable job = new AnimatedMoveViewJob(mViewPortHandler, xValue, yValue + valsInView / 2f, getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); addViewportJob(job); @@ -855,20 +854,20 @@ public void moveViewToAnimated(float xIndex, float yValue, AxisDependency axis, /** * This will move the center of the current viewport to the specified - * xPx-yValue and yPx-yValue. + * x and y value. * This also refreshes the chart by calling invalidate(). * - * @param xIndex + * @param xValue * @param yValue - * @param axis - which axis should be used as a reference for the yPx-axis + * @param axis - which axis should be used as a reference for the y axis */ - public void centerViewTo(float xIndex, float yValue, AxisDependency axis) { + public void centerViewTo(float xValue, float yValue, AxisDependency axis) { float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); float xsInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); Runnable job = new MoveViewJob(mViewPortHandler, - xIndex - xsInView / 2f, yValue + valsInView / 2f, + xValue - xsInView / 2f, yValue + valsInView / 2f, getTransformer(axis), this); addViewportJob(job); @@ -876,15 +875,15 @@ public void centerViewTo(float xIndex, float yValue, AxisDependency axis) { /** * This will move the center of the current viewport to the specified - * xPx-yValue and yPx-yValue animated. + * x and y value animated. * - * @param xIndex + * @param xValue * @param yValue * @param axis * @param duration the duration of the animation in milliseconds */ @TargetApi(11) - public void centerViewToAnimated(float xIndex, float yValue, AxisDependency axis, long duration) { + public void centerViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration) { if (android.os.Build.VERSION.SDK_INT >= 11) { @@ -894,7 +893,7 @@ public void centerViewToAnimated(float xIndex, float yValue, AxisDependency axis float xsInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); Runnable job = new AnimatedMoveViewJob(mViewPortHandler, - xIndex - xsInView / 2f, yValue + valsInView / 2f, + xValue - xsInView / 2f, yValue + valsInView / 2f, getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); addViewportJob(job); @@ -1206,7 +1205,7 @@ public Highlight getHighlightByTouchPoint(float x, float y) { } /** - * Returns the xPx and yPx values in the chart at the given touch point + * Returns the x and y values in the chart at the given touch point * (encapsulated in a PointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to * getPixelsForValues(...). @@ -1232,7 +1231,7 @@ public PointD getPixelsForValues(float x, float y, AxisDependency axis) { } /** - * returns the yPx-yValue at the given touch position (must not necessarily be + * Returns y value at the given touch position (must not necessarily be * a yValue contained in one of the datasets) * * @param x 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 14a61521de..6660e2e4fb 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 @@ -16,14 +16,14 @@ public interface ChartInterface { /** - * Returns the minimum xPx-yValue of the chart, regardless of zoom or translation. + * Returns the minimum x value of the chart, regardless of zoom or translation. * * @return */ float getXChartMin(); /** - * Returns the maximum xPx-yValue of the chart, regardless of zoom or translation. + * Returns the maximum x value of the chart, regardless of zoom or translation. * * @return */ @@ -32,14 +32,14 @@ public interface ChartInterface { float getXRange(); /** - * Returns the minimum yPx-yValue of the chart, regardless of zoom or translation. + * Returns the minimum y value of the chart, regardless of zoom or translation. * * @return */ float getYChartMin(); /** - * Returns the maximum yPx-yValue of the chart, regardless of zoom or translation. + * Returns the maximum y value of the chart, regardless of zoom or translation. * * @return */ From 22c7a6dc0377969210a2640a6bd0232c3509611a Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 4 Jun 2016 14:57:11 +0200 Subject: [PATCH 178/606] Cleanup recently noticed refactoring fail --- .../mpchartexample/AnotherBarActivity.java | 2 +- .../mpchartexample/BarChartActivity.java | 4 +- .../BarChartActivityMultiDataset.java | 2 +- .../mpchartexample/BarChartActivitySinus.java | 2 +- .../BarChartPositiveNegative.java | 2 +- .../CandleStickChartActivity.java | 2 +- .../CubicLineChartActivity.java | 2 +- .../mpchartexample/DrawChartActivity.java | 2 +- .../HorizontalBarChartActivity.java | 2 +- .../InvertedLineChartActivity.java | 2 +- .../mpchartexample/LineChartActivity1.java | 10 +-- .../mpchartexample/LineChartActivity2.java | 2 +- .../LineChartActivityColored.java | 2 +- .../mpchartexample/LineChartTime.java | 4 +- .../MultiLineChartActivity.java | 2 +- .../mpchartexample/PerformanceLineChart.java | 2 +- .../RealtimeLineChartActivity.java | 2 +- .../mpchartexample/ScrollViewActivity.java | 2 +- .../mpchartexample/StackedBarActivity.java | 4 +- .../StackedBarActivityNegative.java | 2 +- .../custom/MyCustomXAxisValueFormatter.java | 4 +- .../notimportant/MainActivity.java | 6 +- .../realm/RealmBaseActivity.java | 2 +- .../charting/animation/ChartAnimator.java | 32 ++++---- .../charting/buffer/AbstractBuffer.java | 14 ++-- .../mikephil/charting/charts/BarChart.java | 6 +- .../charting/charts/BarLineChartBase.java | 74 +++++++++---------- .../mikephil/charting/charts/Chart.java | 66 ++++++++--------- .../charting/charts/HorizontalBarChart.java | 14 ++-- .../mikephil/charting/charts/PieChart.java | 8 +- .../charting/charts/PieRadarChartBase.java | 4 +- .../mikephil/charting/charts/RadarChart.java | 12 +-- .../charting/components/AxisBase.java | 14 ++-- .../charting/components/ComponentBase.java | 10 +-- .../charting/components/LimitLine.java | 8 +- .../charting/components/MarkerView.java | 8 +- .../mikephil/charting/components/XAxis.java | 18 ++--- .../mikephil/charting/components/YAxis.java | 24 +++--- .../mikephil/charting/data/BaseDataSet.java | 2 +- .../mikephil/charting/data/BaseEntry.java | 4 +- .../mikephil/charting/data/BubbleEntry.java | 8 +- .../mikephil/charting/data/CandleEntry.java | 4 +- .../mikephil/charting/data/ChartData.java | 36 ++++----- .../mikephil/charting/data/DataSet.java | 14 ++-- .../github/mikephil/charting/data/Entry.java | 14 ++-- .../mikephil/charting/data/PieData.java | 2 +- .../charting/data/filter/Approximator.java | 4 +- .../data/realm/base/RealmBaseDataSet.java | 16 ++-- .../implementation/RealmBubbleDataSet.java | 6 +- .../implementation/RealmLineDataSet.java | 2 +- .../realm/implementation/RealmPieDataSet.java | 2 +- .../implementation/RealmRadarDataSet.java | 2 +- .../implementation/RealmScatterDataSet.java | 2 +- .../charting/formatter/FillFormatter.java | 2 +- .../charting/highlight/ChartHighlighter.java | 8 +- .../charting/highlight/Highlight.java | 22 +++--- .../interfaces/datasets/IDataSet.java | 26 +++---- .../listener/BarLineChartTouchListener.java | 16 ++-- .../listener/OnChartGestureListener.java | 8 +- .../listener/PieRadarChartTouchListener.java | 2 +- .../charting/renderer/AxisRenderer.java | 6 +- .../renderer/BubbleChartRenderer.java | 4 +- .../renderer/CandleStickChartRenderer.java | 2 +- .../LineScatterCandleRadarRenderer.java | 4 +- .../charting/renderer/XAxisRenderer.java | 8 +- .../XAxisRendererHorizontalBarChart.java | 4 +- .../charting/renderer/YAxisRenderer.java | 6 +- .../YAxisRendererHorizontalBarChart.java | 10 +-- .../renderer/YAxisRendererRadarChart.java | 2 +- .../github/mikephil/charting/utils/FSize.java | 2 +- .../mikephil/charting/utils/PointD.java | 2 +- .../mikephil/charting/utils/Transformer.java | 28 +++---- .../github/mikephil/charting/utils/Utils.java | 6 +- .../charting/utils/ViewPortHandler.java | 70 +++++++++--------- 74 files changed, 367 insertions(+), 367 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index fe967ffb8d..159aafce7b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -53,7 +53,7 @@ protected void onCreate(Bundle savedInstanceState) { // drawn mChart.setMaxVisibleValueCount(60); - // scaling can now only be done on xPx- and yPx-axis separately + // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); mChart.setDrawBarShadow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 80dd81db2b..37232cdbf1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -76,7 +76,7 @@ protected void onCreate(Bundle savedInstanceState) { // drawn mChart.setMaxVisibleValueCount(60); - // scaling can now only be done on xPx- and yPx-axis separately + // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); mChart.setDrawGridBackground(false); @@ -276,7 +276,7 @@ public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { Log.i("bounds", bounds.toString()); Log.i("position", position.toString()); - Log.i("xPx-index", + Log.i("x-index", "low: " + mChart.getLowestVisibleX() + ", high: " + mChart.getHighestVisibleX()); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index ccaf24b348..93150589b7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -61,7 +61,7 @@ protected void onCreate(Bundle savedInstanceState) { // mChart.setDrawBorders(true); - // scaling can now only be done on xPx- and yPx-axis separately + // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); mChart.setDrawBarShadow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index c9f6e3c48e..ce00167fb0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -62,7 +62,7 @@ protected void onCreate(Bundle savedInstanceState) { // drawn mChart.setMaxVisibleValueCount(60); - // scaling can now only be done on xPx- and yPx-axis separately + // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); // draw shadows for each bar that show the maximum yValue diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 5e279db25f..932e049674 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -48,7 +48,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDescription(""); - // scaling can now only be done on xPx- and yPx-axis separately + // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java index 8eabc4be56..51e7aa6bf9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -56,7 +56,7 @@ protected void onCreate(Bundle savedInstanceState) { // drawn mChart.setMaxVisibleValueCount(60); - // scaling can now only be done on xPx- and yPx-axis separately + // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 6e8b0ec5d3..d2b1e923e0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -68,7 +68,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDragEnabled(true); mChart.setScaleEnabled(true); - // if disabled, scaling can be done on xPx- and yPx-axis separately + // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(false); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java index ba1fc3073c..4d103bedfc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java @@ -69,7 +69,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.getLegend().setEnabled(false); // mChart.setYRange(-40f, 40f, true); - // call this to reset the changed yPx-range + // call this to reset the changed y-range // mChart.resetYRange(true); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 738e2ab40c..d70e7d219a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -69,7 +69,7 @@ protected void onCreate(Bundle savedInstanceState) { // drawn mChart.setMaxVisibleValueCount(60); - // scaling can now only be done on xPx- and yPx-axis separately + // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); // draw shadows for each bar that show the maximum yValue diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index 0b6900637f..a28c7b1630 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -70,7 +70,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDragEnabled(true); mChart.setScaleEnabled(true); - // if disabled, scaling can be done on xPx- and yPx-axis separately + // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(true); // set an alternative background color diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 3a06bdeeb9..1e34863659 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -83,7 +83,7 @@ protected void onCreate(Bundle savedInstanceState) { // mChart.setScaleXEnabled(true); // mChart.setScaleYEnabled(true); - // if disabled, scaling can be done on xPx- and yPx-axis separately + // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(true); // set an alternative background color @@ -96,7 +96,7 @@ protected void onCreate(Bundle savedInstanceState) { // set the marker to the chart mChart.setMarkerView(mv); - // xPx-axis limit line + // x-axis limit line LimitLine llXAxis = new LimitLine(10f, "Index 10"); llXAxis.setLineWidth(4f); llXAxis.enableDashedLine(10f, 10f, 0f); @@ -105,7 +105,7 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xAxis = mChart.getXAxis(); //xAxis.setValueFormatter(new MyCustomXAxisValueFormatter()); - //xAxis.addLimitLine(llXAxis); // add xPx-axis limit line + //xAxis.addLimitLine(llXAxis); // add x-axis limit line Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); @@ -344,7 +344,7 @@ private void setData(int count, float range) { float mult = (range + 1); float val = (float) (Math.random() * mult) + 3;// + (float) // ((mult * - // 0.1) / 10);xPx + // 0.1) / 10);x yVals.add(new Entry(i, val)); } @@ -396,7 +396,7 @@ private void setData(int count, float range) { @Override public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { - Log.i("Gesture", "START, xPx: " + me.getX() + ", yPx: " + me.getY()); + Log.i("Gesture", "START, x: " + me.getX() + ", y: " + me.getY()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 236fceb28c..68ede3e0e7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -75,7 +75,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); mChart.setHighlightPerDragEnabled(true); - // if disabled, scaling can be done on xPx- and yPx-axis separately + // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(true); // set an alternative background color diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index 2ae7d0e4df..b66b166320 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -72,7 +72,7 @@ private void setupChart(LineChart chart, LineData data, int color) { chart.setDragEnabled(true); chart.setScaleEnabled(true); - // if disabled, scaling can be done on xPx- and yPx-axis separately + // if disabled, scaling can be done on x- and y-axis separately chart.setPinchZoom(false); chart.setBackgroundColor(color); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index f12d003555..c6f1ea85c7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -72,7 +72,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); mChart.setHighlightPerDragEnabled(true); - // if disabled, scaling can be done on xPx- and yPx-axis separately + // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(true); // set an alternative background color @@ -102,7 +102,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setTextColor(Color.WHITE); xAxis.setDrawAxisLine(false); - // custom xPx-axis min / max + // custom x-axis min / max xAxis.setAxisMinValue(5000); xAxis.setAxisMaxValue(30000); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index d354aedc5b..03ebedd57b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -68,7 +68,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDragEnabled(true); mChart.setScaleEnabled(true); - // if disabled, scaling can be done on xPx- and yPx-axis separately + // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(false); mSeekBarX.setProgress(20); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java index 036e12ace7..8d2fc7f170 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -52,7 +52,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDragEnabled(true); mChart.setScaleEnabled(true); - // if disabled, scaling can be done on xPx- and yPx-axis separately + // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(false); mChart.getAxisLeft().setDrawGridLines(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 95f260bce9..62e82dff28 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -52,7 +52,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setScaleEnabled(true); mChart.setDrawGridBackground(false); - // if disabled, scaling can be done on xPx- and yPx-axis separately + // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(true); // set an alternative background color diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java index 0204147df7..f04d843904 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java @@ -30,7 +30,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDescription(""); - // scaling can now only be done on xPx- and yPx-axis separately + // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); mChart.setDrawBarShadow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 2b75a119c8..8e86eaf158 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -62,7 +62,7 @@ protected void onCreate(Bundle savedInstanceState) { // drawn mChart.setMaxVisibleValueCount(40); - // scaling can now only be done on xPx- and yPx-axis separately + // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); mChart.setDrawGridBackground(false); @@ -70,7 +70,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawValueAboveBar(false); - // change the position of the yPx-labels + // change the position of the y-labels YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setValueFormatter(new MyAxisValueFormatter()); leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 45f040c168..4bbe293c5f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -51,7 +51,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); mChart.setDescription(""); - // scaling can now only be done on xPx- and yPx-axis separately + // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); mChart.setDrawBarShadow(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index 73c1ef4681..16d5752905 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -23,9 +23,9 @@ public MyCustomXAxisValueFormatter(ViewPortHandler viewPortHandler) { @Override public String getFormattedValue(float value, AxisBase axis) { - //Log.i("TRANS", "xPx: " + viewPortHandler.getTransX() + ", yPx: " + viewPortHandler.getTransY()); + //Log.i("TRANS", "x: " + viewPortHandler.getTransX() + ", y: " + viewPortHandler.getTransY()); - // e.g. adjust the xPx-axis values depending on scale / zoom level + // e.g. adjust the x-axis values depending on scale / zoom level if (mViewPortHandler.getScaleX() > 5) return "4"; else if (mViewPortHandler.getScaleX() > 3) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index 96b383848a..dcade871f2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -66,7 +66,7 @@ protected void onCreate(Bundle savedInstanceState) { 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 yPx-axis.")); + "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.")); @@ -97,7 +97,7 @@ protected void onCreate(Bundle savedInstanceState) { "Demonstrates the usage of different chart types inside a ListView.")); objects.add(new ContentItem( "Inverted Line Chart", - "Demonstrates the feature of inverting the yPx-axis.")); + "Demonstrates the feature of inverting the y-axis.")); objects.add(new ContentItem( "Candle Stick Chart", "Demonstrates usage of the CandleStickChart.")); @@ -112,7 +112,7 @@ protected void onCreate(Bundle savedInstanceState) { "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 xPx-axis.")); + "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).")); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java index 5d5eb0d06a..05aa742864 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -52,7 +52,7 @@ protected void setup(Chart chart) { mChart.setDragEnabled(true); mChart.setScaleEnabled(true); - // if disabled, scaling can be done on xPx- and yPx-axis separately + // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(false); YAxis leftAxis = mChart.getAxisLeft(); 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 455f7be3e2..639442a4c9 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 @@ -7,7 +7,7 @@ /** * Object responsible for all animations in the Chart. ANIMATIONS ONLY WORK FOR - * API LEVEL 11 (Android 3.0.xPx) AND HIGHER. + * API LEVEL 11 (Android 3.0.x) AND HIGHER. * * @author Philipp Jahoda */ @@ -29,10 +29,10 @@ public ChartAnimator(AnimatorUpdateListener listener) { */ /** CODE BELOW THIS RELATED TO ANIMATION */ - /** the phase that is animated and influences the drawn values on the yPx-axis */ + /** 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 xPx-axis */ + /** the phase that is animated and influences the drawn values on the x-axis */ protected float mPhaseX = 1f; /** @@ -41,7 +41,7 @@ public ChartAnimator(AnimatorUpdateListener listener) { /** METHODS FOR CUSTOM EASING */ /** - * Animates the drawing / rendering of the chart on both xPx- and yPx-axis with + * 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. * @@ -78,7 +78,7 @@ public void animateXY(int durationMillisX, int durationMillisY, EasingFunction e } /** - * Animates the rendering of the chart on the xPx-axis with the specified + * 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. * @@ -98,7 +98,7 @@ public void animateX(int durationMillis, EasingFunction easing) { } /** - * Animates the rendering of the chart on the yPx-axis with the specified + * 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. * @@ -123,7 +123,7 @@ public void animateY(int durationMillis, EasingFunction easing) { /** METHODS FOR PREDEFINED EASING */ /** - * Animates the drawing / rendering of the chart on both xPx- and yPx-axis with + * 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. * @@ -160,7 +160,7 @@ public void animateXY(int durationMillisX, int durationMillisY, Easing.EasingOpt } /** - * Animates the rendering of the chart on the xPx-axis with the specified + * 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. * @@ -180,7 +180,7 @@ public void animateX(int durationMillis, Easing.EasingOption easing) { } /** - * Animates the rendering of the chart on the yPx-axis with the specified + * 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. * @@ -205,7 +205,7 @@ public void animateY(int durationMillis, Easing.EasingOption easing) { /** METHODS FOR ANIMATION WITHOUT EASING */ /** - * Animates the drawing / rendering of the chart on both xPx- and yPx-axis with + * 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. * @@ -237,7 +237,7 @@ public void animateXY(int durationMillisX, int durationMillisY) { } /** - * Animates the rendering of the chart on the xPx-axis with the specified + * 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. * @@ -255,7 +255,7 @@ public void animateX(int durationMillis) { } /** - * Animates the rendering of the chart on the yPx-axis with the specified + * 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. * @@ -273,7 +273,7 @@ public void animateY(int durationMillis) { } /** - * This gets the yPx-phase that is used to animate the values. + * This gets the y-phase that is used to animate the values. * * @return */ @@ -282,7 +282,7 @@ public float getPhaseY() { } /** - * This modifys the yPx-phase that is used to animate the values. + * This modifys the y-phase that is used to animate the values. * * @param phase */ @@ -291,7 +291,7 @@ public void setPhaseY(float phase) { } /** - * This gets the xPx-phase that is used to animate the values. + * This gets the x-phase that is used to animate the values. * * @return */ @@ -300,7 +300,7 @@ public float getPhaseX() { } /** - * This modifys the xPx-phase that is used to animate the values. + * This modifys the x-phase that is used to animate the values. * * @param phase */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java index 6df0c01ba8..958d12afba 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java @@ -15,19 +15,19 @@ public abstract class AbstractBuffer { /** index in the buffer */ protected int index = 0; - /** float-buffer that holds the data points to draw, order: xPx,yPx,xPx,yPx,... */ + /** float-buffer that holds the data points to draw, order: x,y,x,y,... */ public final float[] buffer; - /** animation phase xPx-axis */ + /** animation phase x-axis */ protected float phaseX = 1f; - /** animation phase yPx-axis */ + /** animation phase y-axis */ protected float phaseY = 1f; - /** indicates from which xPx-index the visible data begins */ + /** indicates from which x-index the visible data begins */ protected int mFrom = 0; - /** indicates to which xPx-index the visible data ranges */ + /** indicates to which x-index the visible data ranges */ protected int mTo = 0; /** @@ -40,14 +40,14 @@ public AbstractBuffer(int size) { buffer = new float[size]; } - /** limits the drawing on the xPx-axis */ + /** limits the drawing on the x-axis */ public void limitFrom(int from) { if (from < 0) from = 0; mFrom = from; } - /** limits the drawing on the xPx-axis */ + /** limits the drawing on the x-axis */ public void limitTo(int to) { if (to < 0) to = 0; 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 5187051d44..e025728920 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 @@ -80,7 +80,7 @@ protected void calcMinMax() { } /** - * Returns the Highlight object (contains xPx-index and DataSet index) of the selected yValue at the given touch + * Returns the Highlight object (contains x-index and DataSet index) of the selected yValue at the given touch * point * inside the BarChart. * @@ -203,7 +203,7 @@ public void setFitBars(boolean enabled) { // /** -// * Returns the lowest xPx-index (yValue on the xPx-axis) that is still visible on the chart. +// * Returns the lowest x-index (yValue on the x-axis) that is still visible on the chart. // * // * @return // */ @@ -220,7 +220,7 @@ public void setFitBars(boolean enabled) { // } // // /** -// * Returns the highest xPx-index (yValue on the xPx-axis) that is still visible on the chart. +// * Returns the highest x-index (yValue on the x-axis) that is still visible on the chart. // * // * @return // */ 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 e4676a5b71..e9055fec74 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 @@ -55,15 +55,15 @@ public abstract class BarLineChartBase zoom out, if > 1f --> zoom in @@ -722,12 +722,12 @@ public void setScaleMinima(float scaleX, float scaleY) { } /** - * Sets the size of the area (range on the xPx-axis) that should be maximum + * Sets the size of the area (range on the x-axis) that should be maximum * visible at once (no further zooming out allowed). If this is e.g. set to - * 10, no more than 10 values on the xPx-axis can be viewed at once without + * 10, no more than 10 values on the x-axis can be viewed at once without * scrolling. * - * @param maxXRange The maximum visible range of xPx-values. + * @param maxXRange The maximum visible range of x-values. */ public void setVisibleXRangeMaximum(float maxXRange) { float xScale = mXAxis.mAxisRange / (maxXRange); @@ -735,12 +735,12 @@ public void setVisibleXRangeMaximum(float maxXRange) { } /** - * Sets the size of the area (range on the xPx-axis) that should be minimum + * Sets the size of the area (range on the x-axis) that should be minimum * visible at once (no further zooming in allowed). If this is e.g. set to - * 10, no less than 10 values on the xPx-axis can be viewed at once without + * 10, no less than 10 values on the x-axis can be viewed at once without * scrolling. * - * @param minXRange The minimum visible range of xPx-values. + * @param minXRange The minimum visible range of x-values. */ public void setVisibleXRangeMinimum(float minXRange) { float xScale = mXAxis.mAxisRange / (minXRange); @@ -763,10 +763,10 @@ public void setVisibleXRange(float minXRange, float maxXRange) { } /** - * Sets the size of the area (range on the yPx-axis) that should be maximum + * Sets the size of the area (range on the y-axis) that should be maximum * visible at once. * - * @param maxYRange the maximum visible range on the yPx-axis + * @param maxYRange the maximum visible range on the y-axis * @param axis - the axis for which this limit should apply */ public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { @@ -775,7 +775,7 @@ public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { } /** - * Moves the left side of the current viewport to the specified xPx-index. + * Moves the left side of the current viewport to the specified x-index. * This also refreshes the chart by calling invalidate(). * * @param xIndex @@ -950,7 +950,7 @@ public void resetViewPortOffsets() { /** CODE BELOW IS GETTERS AND SETTERS */ /** - * Returns the delta-yPx yValue (yPx-yValue range) of the specified axis. + * Returns the delta-y yValue (y-yValue range) of the specified axis. * * @param axis * @return @@ -1132,7 +1132,7 @@ 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 xPx- and yPx-axis. + * no point drawing the axis-lines of x- and y-axis. * * @param enabled */ @@ -1187,7 +1187,7 @@ public void setKeepPositionOnRotation(boolean keepPositionOnRotation) { } /** - * Returns the Highlight object (contains xPx-index and DataSet index) of the + * Returns the Highlight object (contains x-index and DataSet index) of the * selected yValue at the given touch point inside the Line-, Scatter-, or * CandleStick-Chart. * @@ -1273,7 +1273,7 @@ public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float } /** - * Returns the lowest xPx-index (yValue on the xPx-axis) that is still visible on + * Returns the lowest x-index (yValue on the x-axis) that is still visible on * the chart. * * @return @@ -1286,7 +1286,7 @@ public float getLowestVisibleX() { } /** - * Returns the highest xPx-index (yValue on the xPx-axis) that is still visible + * Returns the highest x-index (yValue on the x-axis) that is still visible * on the chart. * * @return @@ -1308,7 +1308,7 @@ public float getVisibleXRange() { } /** - * returns the current xPx-scale factor + * returns the current x-scale factor */ public float getScaleX() { if (mViewPortHandler == null) @@ -1318,7 +1318,7 @@ public float getScaleX() { } /** - * returns the current yPx-scale factor + * returns the current y-scale factor */ public float getScaleY() { if (mViewPortHandler == null) @@ -1337,7 +1337,7 @@ public boolean isFullyZoomedOut() { } /** - * Returns the left yPx-axis object. In the horizontal bar-chart, this is the + * Returns the left y-axis object. In the horizontal bar-chart, this is the * top axis. * * @return @@ -1347,7 +1347,7 @@ public YAxis getAxisLeft() { } /** - * Returns the right yPx-axis object. In the horizontal bar-chart, this is the + * Returns the right y-axis object. In the horizontal bar-chart, this is the * bottom axis. * * @return @@ -1357,7 +1357,7 @@ public YAxis getAxisRight() { } /** - * Returns the yPx-axis object to the corresponding AxisDependency. In the + * Returns the y-axis object to the corresponding AxisDependency. In the * horizontal bar-chart, LEFT == top, RIGHT == BOTTOM * * @param axis @@ -1376,8 +1376,8 @@ public boolean isInverted(AxisDependency axis) { } /** - * If set to true, both xPx and yPx axis can be scaled simultaneously with 2 fingers, if false, - * xPx and yPx axis can be scaled separately. default: false + * If set to true, both x and y axis can be scaled simultaneously with 2 fingers, if false, + * x and y axis can be scaled separately. default: false * * @param enabled */ @@ -1396,7 +1396,7 @@ public boolean isPinchZoomEnabled() { /** * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the xPx-axis. + * bounds on the x-axis. * * @param offset */ @@ -1406,7 +1406,7 @@ public void setDragOffsetX(float offset) { /** * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the yPx-axis. + * bounds on the y-axis. * * @param offset */ @@ -1415,7 +1415,7 @@ public void setDragOffsetY(float offset) { } /** - * Returns true if both drag offsets (xPx and yPx) are zero or smaller. + * Returns true if both drag offsets (x and y) are zero or smaller. * * @return */ @@ -1486,11 +1486,11 @@ public boolean isAnyAxisInverted() { } /** - * Flag that indicates if auto scaling on the yPx axis is enabled. This is + * Flag that indicates if auto scaling on the y axis is enabled. This is * especially interesting for charts displaying financial data. * - * @param enabled the yPx axis automatically adjusts to the min and max yPx - * values of the current xPx axis range whenever the viewport + * @param enabled the y axis automatically adjusts to the min and max y + * values of the current x axis range whenever the viewport * changes */ public void setAutoScaleMinMaxEnabled(boolean enabled) { @@ -1498,7 +1498,7 @@ public void setAutoScaleMinMaxEnabled(boolean enabled) { } /** - * @return true if auto scaling on the yPx axis is enabled. + * @return true if auto scaling on the y axis is enabled. * @default false */ public boolean isAutoScaleMinMaxEnabled() { 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 42e40d0f65..13165ae147 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 @@ -118,7 +118,7 @@ public abstract class Chart { private RectF mCircleBox = new RectF(); /** - * flag indicating if the xPx-labels should be drawn or not + * flag indicating if the x-labels should be drawn or not */ private boolean mDrawXLabels = true; @@ -302,7 +302,7 @@ public int getIndexForAngle(float angle) { } /** - * Returns the index of the DataSet this xPx-index belongs to. + * Returns the index of the DataSet this x-index belongs to. * * @param xIndex * @return @@ -559,7 +559,7 @@ public void setTransparentCircleAlpha(int alpha) { } /** - * set this to true to draw the xPx-yValue text into the pie slices + * set this to true to draw the x-yValue text into the pie slices * * @param enabled */ @@ -568,7 +568,7 @@ public void setDrawSliceText(boolean enabled) { } /** - * returns true if drawing xPx-values is enabled, false if not + * returns true if drawing x-values is enabled, false if not * * @return */ 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 c3d14c042a..2c54ec3abc 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 @@ -441,7 +441,7 @@ public float getYChartMin() { } /** - * Returns an array of SelectionDetail objects for the given xPx-index. The SelectionDetail + * Returns an array of SelectionDetail objects for the given x-index. The SelectionDetail * objects give information about the yValue at the selected index and the * DataSet it belongs to. INFORMATION: This method does calculations at * runtime. Do not over-use in performance critical situations. @@ -456,7 +456,7 @@ public List getSelectionDetailsAtIndex(int xIndex) { IDataSet dataSet = mData.getDataSetByIndex(i); - // extract all yPx-values from all DataSets at the given xPx-index + // extract all y-values from all DataSets at the given x-index final float yVal = dataSet.getYValueForXValue(xIndex); if (Float.isNaN(yVal)) continue; 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 9e5118d63b..854f5bef63 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 @@ -62,7 +62,7 @@ public class RadarChart extends PieRadarChartBase { private int mSkipWebLineCount = 0; /** - * the object reprsenting the yPx-axis labels + * the object reprsenting the y-axis labels */ private YAxis mYAxis; @@ -99,7 +99,7 @@ protected void init() { protected void calcMinMax() { super.calcMinMax(); - // calculate / set xPx-axis range + // calculate / set x-axis range // mXAxis.mAxisMaximum = mData.getXVals().size() - 1; // mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); @@ -214,7 +214,7 @@ public int getIndexForAngle(float angle) { } /** - * Returns the object that represents all yPx-labels of the RadarChart. + * Returns the object that represents all y-labels of the RadarChart. * * @return */ @@ -347,21 +347,21 @@ public float getRadius() { } /** - * Returns the maximum yValue this chart can display on it's yPx-axis. + * Returns the maximum yValue this chart can display on it's y-axis. */ public float getYChartMax() { return mYAxis.mAxisMaximum; } /** - * Returns the minimum yValue this chart can display on it's yPx-axis. + * Returns the minimum yValue this chart can display on it's y-axis. */ public float getYChartMin() { return mYAxis.mAxisMinimum; } /** - * Returns the range of yPx-values this chart can display. + * Returns the range of y-values this chart can display. * * @return */ 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 2c717ea6de..7f852342e2 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 @@ -71,7 +71,7 @@ public abstract class AxisBase extends ComponentBase { protected boolean mGranularityEnabled = false; /** - * if true, the set number of yPx-labels will be forced + * if true, the set number of y-labels will be forced */ protected boolean mForceLabels = false; @@ -282,10 +282,10 @@ public boolean isDrawLabelsEnabled() { } /** - * Sets the number of label entries for the yPx-axis max = 25, min = 2, default: 6, be aware + * 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 yPx-axis labels that sould be displayed + * @param count the number of y-axis labels that sould be displayed */ public void setLabelCount(int count) { @@ -299,11 +299,11 @@ public void setLabelCount(int count) { } /** - * sets the number of label entries for the yPx-axis max = 25, min = 2, default: 6, be aware + * sets the number of label entries for the y-axis max = 25, min = 2, default: 6, be aware * that this number is not * fixed (if force == false) and can only be approximated. * - * @param count the number of yPx-axis labels that sould be displayed + * @param count the number of y-axis labels that sould 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 @@ -316,7 +316,7 @@ public void setLabelCount(int count, boolean force) { } /** - * Returns true if focing the yPx-label count is enabled. Default: false + * Returns true if focing the y-label count is enabled. Default: false * * @return */ @@ -325,7 +325,7 @@ public boolean isForceLabelsEnabled() { } /** - * Returns the number of label entries the yPx-axis should have + * Returns the number of label entries the y-axis should have * * @return */ 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 77bbe97ca7..713f89dd79 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,7 +19,7 @@ public abstract class ComponentBase { protected boolean mEnabled = true; /** - * the offset in pixels this axis labels have on the xPx-axis + * the offset in pixels this axis labels have on the x-axis */ protected float mXOffset = 5f; @@ -48,7 +48,7 @@ public ComponentBase() { } /** - * Returns the used offset on the xPx-axis for drawing the axis or legend + * Returns the used offset on the x-axis for drawing the axis or legend * labels. This offset is applied before and after the label. * * @return @@ -58,7 +58,7 @@ public float getXOffset() { } /** - * Sets the used xPx-axis offset for the labels on this axis. + * Sets the used x-axis offset for the labels on this axis. * * @param xOffset */ @@ -67,7 +67,7 @@ public void setXOffset(float xOffset) { } /** - * Returns the used offset on the xPx-axis for drawing the axis labels. This + * Returns the used offset on the x-axis for drawing the axis labels. This * offset is applied before and after the label. * * @return @@ -77,7 +77,7 @@ public float getYOffset() { } /** - * Sets the used yPx-axis offset for the labels on this axis. For the legend, + * Sets the used y-axis offset for the labels on this axis. For the legend, * higher offset means the legend as a whole will be placed further away * from the top. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java index 7b826fae30..791e56e2e0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java @@ -11,13 +11,13 @@ /** * The limit line is an additional feature for all Line-, Bar- and * ScatterCharts. It allows the displaying of an additional line in the chart - * that marks a certain maximum / limit on the specified axis (xPx- or yPx-axis). + * that marks a certain maximum / limit on the specified axis (x- or y-axis). * * @author Philipp Jahoda */ public class LimitLine extends ComponentBase { - /** limit / maximum (the yPx-yValue or xIndex) */ + /** limit / maximum (the y-yValue or xIndex) */ private float mLimit = 0f; /** the width of the limit line */ @@ -46,7 +46,7 @@ public enum LimitLabelPosition { /** * Constructor with limit. * - * @param limit - the position (the yValue) on the yPx-axis (yPx-yValue) or xPx-axis + * @param limit - the position (the yValue) on the y-axis (y-yValue) or x-axis * (xIndex) where this line should appear */ public LimitLine(float limit) { @@ -56,7 +56,7 @@ public LimitLine(float limit) { /** * Constructor with limit and label. * - * @param limit - the position (the yValue) on the yPx-axis (yPx-yValue) or xPx-axis + * @param limit - the position (the yValue) on the y-axis (y-yValue) or x-axis * (xIndex) where this line should appear * @param label - provide "" if no label is required */ 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 ca99c59b09..be0786133f 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 @@ -74,19 +74,19 @@ public void draw(Canvas canvas, float posx, float posy) { public abstract void refreshContent(Entry e, Highlight highlight); /** - * Use this to return the desired offset you wish the MarkerView to have on the xPx-axis. By returning -(getWidth() / + * 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 xPx-axis in pixels where the marker is drawn + * @param xpos the position on the x-axis in pixels where the marker is drawn * @return */ public abstract int getXOffset(float xpos); /** - * Use this to return the desired position offset you wish the MarkerView to have on the yPx-axis. By returning + * 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 yValue. * - * @param ypos the position on the yPx-axis in pixels where the marker is drawn + * @param ypos the position on the y-axis in pixels where the marker is drawn * @return */ public abstract int getYOffset(float ypos); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java index 5a21292866..77d4aaf98f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java @@ -4,7 +4,7 @@ import com.github.mikephil.charting.utils.Utils; /** - * Class representing the xPx-axis labels settings. Only use the setter methods to + * Class representing the x-axis labels settings. Only use the setter methods to * modify it. Do not access public variables directly. Be aware that not all * features the XLabels class provides are suitable for the RadarChart. * @@ -13,25 +13,25 @@ public class XAxis extends AxisBase { /** - * width of the xPx-axis labels in pixels - this is automatically + * width of the x-axis labels in pixels - this is automatically * calculated by the computeSize() methods in the renderers */ public int mLabelWidth = 1; /** - * height of the xPx-axis labels in pixels - this is automatically + * height of the x-axis labels in pixels - this is automatically * calculated by the computeSize() methods in the renderers */ public int mLabelHeight = 1; /** - * width of the (rotated) xPx-axis labels in pixels - this is automatically + * width of the (rotated) x-axis labels in pixels - this is automatically * calculated by the computeSize() methods in the renderers */ public int mLabelRotatedWidth = 1; /** - * height of the (rotated) xPx-axis labels in pixels - this is automatically + * height of the (rotated) x-axis labels in pixels - this is automatically * calculated by the computeSize() methods in the renderers */ public int mLabelRotatedHeight = 1; @@ -48,12 +48,12 @@ public class XAxis extends AxisBase { private boolean mAvoidFirstLastClipping = false; /** - * the position of the xPx-labels relative to the chart + * the position of the x-labels relative to the chart */ private XAxisPosition mPosition = XAxisPosition.TOP; /** - * enum for the position of the xPx-labels relative to the chart + * enum for the position of the x-labels relative to the chart */ public enum XAxisPosition { TOP, BOTTOM, BOTH_SIDED, TOP_INSIDE, BOTTOM_INSIDE @@ -66,14 +66,14 @@ public XAxis() { } /** - * returns the position of the xPx-labels + * returns the position of the x-labels */ public XAxisPosition getPosition() { return mPosition; } /** - * sets the position of the xPx-labels + * sets the position of the x-labels * * @param pos */ 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 6b0c6adedf..317d7a3a97 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 @@ -6,7 +6,7 @@ import com.github.mikephil.charting.utils.Utils; /** - * Class representing the yPx-axis labels settings and its entries. Only use the setter methods to + * Class representing the y-axis labels settings and its entries. Only use the setter methods to * modify it. Do not * access public variables directly. Be aware that not all features the YLabels class provides * are suitable for the @@ -19,7 +19,7 @@ public class YAxis extends AxisBase { /** - * indicates if the top yPx-label entry is drawn or not + * indicates if the top y-label entry is drawn or not */ private boolean mDrawTopYLabelEntry = true; @@ -54,12 +54,12 @@ public class YAxis extends AxisBase { protected float mSpacePercentBottom = 10f; /** - * the position of the yPx-labels relative to the chart + * the position of the y-labels relative to the chart */ private YAxisLabelPosition mPosition = YAxisLabelPosition.OUTSIDE_CHART; /** - * enum for the position of the yPx-labels relative to the chart + * enum for the position of the y-labels relative to the chart */ public enum YAxisLabelPosition { OUTSIDE_CHART, INSIDE_CHART @@ -144,14 +144,14 @@ public void setMaxWidth(float maxWidth) { } /** - * returns the position of the yPx-labels + * returns the position of the y-labels */ public YAxisLabelPosition getLabelPosition() { return mPosition; } /** - * sets the position of the yPx-labels + * sets the position of the y-labels * * @param pos */ @@ -160,7 +160,7 @@ public void setPosition(YAxisLabelPosition pos) { } /** - * returns true if drawing the top yPx-axis label entry is enabled + * returns true if drawing the top y-axis label entry is enabled * * @return */ @@ -169,9 +169,9 @@ public boolean isDrawTopYLabelEntryEnabled() { } /** - * set this to true to enable drawing the top yPx-label entry. Disabling this can be helpful - * when the top yPx-label and - * left xPx-label interfere with each other. default: true + * set this to true to enable drawing the top y-label entry. Disabling this can be helpful + * when the top y-label and + * left x-label interfere with each other. default: true * * @param enabled */ @@ -180,7 +180,7 @@ public void setDrawTopYLabelEntry(boolean enabled) { } /** - * If this is set to true, the yPx-axis is inverted which means that low values are on top of + * If this is set to true, the y-axis is inverted which means that low values are on top of * the chart, high values * on bottom. * @@ -191,7 +191,7 @@ public void setInverted(boolean enabled) { } /** - * If this returns true, the yPx-axis is inverted. + * If this returns true, the y-axis is inverted. * * @return */ 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 1cdddda683..866f78f678 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 @@ -57,7 +57,7 @@ public abstract class BaseDataSet implements IDataSet { protected Typeface mValueTypeface; /** - * if true, yPx-values are drawn on the chart + * if true, y-values are drawn on the chart */ protected boolean mDrawValues = true; 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 48e773edfe..09896c84a4 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 @@ -25,7 +25,7 @@ public BaseEntry(float y, Object data) { } /** - * Returns the yPx yValue of this Entry. + * Returns the y yValue of this Entry. * * @return */ @@ -34,7 +34,7 @@ public float getY() { } /** - * Sets the yPx-yValue for the Entry. + * Sets the y-yValue for the Entry. * * @param y */ 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 0778e57673..d0371cc7fc 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 @@ -19,8 +19,8 @@ public class BubbleEntry extends Entry { /** * Constructor. * - * @param x The yValue on the xPx-axis. - * @param y The yValue on the yPx-axis. + * @param x The yValue on the x-axis. + * @param y The yValue on the y-axis. * @param size The size of the bubble. */ public BubbleEntry(float x, float y, float size) { @@ -31,8 +31,8 @@ public BubbleEntry(float x, float y, float size) { /** * Constructor. * - * @param x The yValue on the xPx-axis. - * @param y The yValue on the yPx-axis. + * @param x The yValue on the x-axis. + * @param y The yValue on the y-axis. * @param size The size of the bubble. * @param data Spot for additional data this Entry represents. */ 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 b499540a64..41e69ffba7 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 @@ -26,7 +26,7 @@ public class CandleEntry extends Entry { /** * Constructor. * - * @param x The yValue on the xPx-axis. + * @param x The yValue on the x-axis. * @param shadowH The (shadow) high yValue. * @param shadowL The (shadow) low yValue. * @param open The open yValue. @@ -44,7 +44,7 @@ public CandleEntry(float x, float shadowH, float shadowL, float open, float clos /** * Constructor. * - * @param x The yValue on the xPx-axis. + * @param x The yValue on the x-axis. * @param shadowH The (shadow) high yValue. * @param shadowL The (shadow) low yValue. * @param open 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 d29456774b..0cd23e873c 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 @@ -16,29 +16,29 @@ /** * Class that holds all relevant data that represents the chart. That involves - * at least one (or more) DataSets, and an array of xPx-values. + * at least one (or more) DataSets, and an array of x-values. * * @author Philipp Jahoda */ public abstract class ChartData> { /** - * maximum yPx-yValue in the yValue array across all axes + * maximum y-yValue in the yValue array across all axes */ protected float mYMax = 0.0f; /** - * the minimum yPx-yValue in the yValue array across all axes + * the minimum y-yValue in the yValue array across all axes */ protected float mYMin = 0.0f; /** - * maximum xPx-yValue in the yValue array + * maximum x-yValue in the yValue array */ protected float mXMax = 0f; /** - * minimum xPx-yValue in the yValue array + * minimum x-yValue in the yValue array */ protected float mXMin = 0f; @@ -51,12 +51,12 @@ public abstract class ChartData> { protected float mRightAxisMin = 0.0f; /** - * total number of yPx-values across all DataSet objects + * total number of y-values across all DataSet objects */ private int mYValCount = 0; /** - * contains the maximum length (in characters) an entry in the xPx-vals array + * contains the maximum length (in characters) an entry in the x-vals array * has */ private float mXValMaximumLength = 0; @@ -105,7 +105,7 @@ public void notifyDataChanged() { } /** - * calc minimum and maximum values (both xPx and yPx) over all DataSets + * calc minimum and maximum values (both x and y) over all DataSets */ public void calcMinMax() { @@ -192,7 +192,7 @@ public void calcMinMax() { } /** - * Calculates the total number of yPx-values across all DataSets the ChartData + * Calculates the total number of y-values across all DataSets the ChartData * represents. * * @return @@ -227,7 +227,7 @@ public int getDataSetCount() { } /** - * Returns the smallest yPx-yValue the data object contains. + * Returns the smallest y-yValue the data object contains. * * @return */ @@ -236,7 +236,7 @@ public float getYMin() { } /** - * Returns the minimum yPx-yValue for the specified axis. + * Returns the minimum y-yValue for the specified axis. * * @param axis * @return @@ -249,7 +249,7 @@ public float getYMin(AxisDependency axis) { } /** - * Returns the greatest yPx-yValue the data object contains. + * Returns the greatest y-yValue the data object contains. * * @return */ @@ -258,7 +258,7 @@ public float getYMax() { } /** - * Returns the maximum yPx-yValue for the specified axis. + * Returns the maximum y-yValue for the specified axis. * * @param axis * @return @@ -271,7 +271,7 @@ public float getYMax(AxisDependency axis) { } /** - * Returns the minimum xPx-yValue this data object contains. + * Returns the minimum x-yValue this data object contains. * * @return */ @@ -280,7 +280,7 @@ public float getXMin() { } /** - * Returns the maximum xPx-yValue this data object contains. + * Returns the maximum x-yValue this data object contains. * * @return */ @@ -290,7 +290,7 @@ public float getXMax() { /** * returns the maximum length (in characters) across all values in the - * xPx-vals array + * x-vals array * * @return */ @@ -299,7 +299,7 @@ public float getXValMaximumLength() { } /** - * Returns the total number of yPx-values across all DataSet objects the this + * Returns the total number of y-values across all DataSet objects the this * object represents. * * @return @@ -733,7 +733,7 @@ public T getFirstRight() { } // /** -// * Generates an xPx-values array filled with numbers in range specified by the +// * Generates an x-values array filled with numbers in range specified by the // * parameters. Can be used for convenience. // * // * @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 8c2eeb2346..2cc8422843 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 @@ -20,22 +20,22 @@ public abstract class DataSet extends BaseDataSet { protected List mValues = null; /** - * maximum yPx-yValue in the yValue array + * maximum y-yValue in the yValue array */ protected float mYMax = 0.0f; /** - * minimum yPx-yValue in the yValue array + * minimum y-yValue in the yValue array */ protected float mYMin = 0.0f; /** - * maximum xPx-yValue in the yValue array + * maximum x-yValue in the yValue array */ protected float mXMax = 0.0f; /** - * minimum xPx-yValue in the yValue array + * minimum x-yValue in the yValue array */ protected float mXMin = 0.0f; @@ -108,7 +108,7 @@ public int getEntryCount() { } /** - * Returns the array of yPx-values that this DataSet represents. + * Returns the array of y-values that this DataSet represents. * * @return */ @@ -117,7 +117,7 @@ public List getYVals() { } /** - * Sets the array of yPx-values that this DataSet represents, and calls notifyDataSetChanged() + * Sets the array of y-values that this DataSet represents, and calls notifyDataSetChanged() * * @return */ @@ -396,7 +396,7 @@ public List getEntriesForXPos(float xVal) { /** * Determines how to round DataSet index values for * {@link DataSet#getEntryIndex(float, Rounding)} DataSet.getEntryIndex()} - * when an exact xPx-index is not found. + * when an exact x-index is not found. */ public enum Rounding { UP, 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 7c23e500e2..a210093fad 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 @@ -23,8 +23,8 @@ public Entry() { /** * A Entry represents one single entry in the chart. * - * @param x the xPx yValue - * @param y the yPx yValue (the actual yValue of the entry) + * @param x the x yValue + * @param y the y yValue (the actual yValue of the entry) */ public Entry(float x, float y) { super(y); @@ -34,8 +34,8 @@ public Entry(float x, float y) { /** * A Entry represents one single entry in the chart. * - * @param x the xPx yValue - * @param y the yPx yValue (the actual yValue of the entry) + * @param x the x yValue + * @param y the y yValue (the actual yValue of the entry) * @param data Spot for additional data this Entry represents. */ public Entry(float x, float y, Object data) { @@ -44,7 +44,7 @@ public Entry(float x, float y, Object data) { } /** - * Returns the xPx-yValue of this Entry object. + * Returns the x-yValue of this Entry object. * * @return */ @@ -53,7 +53,7 @@ public float getX() { } /** - * Sets the xPx-yValue of this Entry object. + * Sets the x-yValue of this Entry object. * * @param x */ @@ -97,7 +97,7 @@ public boolean equalTo(Entry e) { } /** - * returns a string representation of the entry containing xPx-index and yValue + * returns a string representation of the entry containing x-index and yValue */ @Override public String toString() { 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 62b5284c14..801114943c 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 @@ -8,7 +8,7 @@ /** * A PieData object can only represent one DataSet. Unlike all other charts, the - * legend labels of the PieChart are created from the xPx-values array, and not + * legend labels of the PieChart are created from the x-values array, and not * from the DataSet labels. Each PieData object can only represent one * PieDataSet (multiple PieDataSets inside a single PieChart are not possible). * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java index 4fac3f615d..f562f22f06 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java @@ -81,7 +81,7 @@ public void setType(ApproximatorType type) { } /** - * Sets the ratios for xPx- and yPx-axis, as well as the ratio of the scale + * Sets the ratios for x- and y-axis, as well as the ratio of the scale * levels * * @param deltaRatio @@ -163,7 +163,7 @@ private List reduceWithDouglasPeuker(List entries, double epsilon) * epsilon (tolerance) * * @param entries - * @param epsilon as yPx-yValue + * @param epsilon as y-yValue * @param start * @param end */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java index ee17c51587..b0fad9d14f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java @@ -28,22 +28,22 @@ public abstract class RealmBaseDataSet e protected List mValues; /** - * maximum yPx-yValue in the yPx-yValue array + * maximum y-yValue in the y-yValue array */ protected float mYMax = 0.0f; /** - * the minimum yPx-yValue in the yPx-yValue array + * the minimum y-yValue in the y-yValue array */ protected float mYMin = 0.0f; /** - * maximum xPx-yValue in the yValue array + * maximum x-yValue in the yValue array */ protected float mXMax = 0.0f; /** - * minimum xPx-yValue in the yValue array + * minimum x-yValue in the yValue array */ protected float mXMin = 0.0f; @@ -384,7 +384,7 @@ public RealmResults getResults() { } /** - * Returns the fieldname that represents the "yPx-values" in the realm-data. + * Returns the fieldname that represents the "y-values" in the realm-data. * * @return */ @@ -393,7 +393,7 @@ public String getYValuesField() { } /** - * Sets the field name that is used for getting the yPx-values out of the RealmResultSet. + * Sets the field name that is used for getting the y-values out of the RealmResultSet. * * @param yValuesField */ @@ -402,7 +402,7 @@ public void setYValuesField(String yValuesField) { } /** - * Returns the fieldname that represents the "xPx-values" in the realm-data. + * Returns the fieldname that represents the "x-values" in the realm-data. * * @return */ @@ -411,7 +411,7 @@ public String getXValuesField() { } /** - * Sets the field name that is used for getting the xPx-values out of the RealmResultSet. + * Sets the field name that is used for getting the x-values out of the RealmResultSet. * * @param xValuesField */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java index 5a2e0c5f93..67700a428e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java @@ -25,7 +25,7 @@ public class RealmBubbleDataSet extends RealmBarLineScatt * Constructor for creating a CandleDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the yPx-yValue + * @param yValuesField the name of the field in your data object that represents the y-yValue * @param sizeField the name of the field in your data object that represents the bubble size */ public RealmBubbleDataSet(RealmResults result, String yValuesField, String sizeField) { @@ -40,8 +40,8 @@ public RealmBubbleDataSet(RealmResults result, String yValuesField, String si * Constructor for creating a CandleDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the yPx-yValue - * @param xIndexField the name of the field in your data object that represents the xPx-index + * @param yValuesField the name of the field in your data object that represents the y-yValue + * @param xIndexField the name of the field in your data object that represents the x-index * @param sizeField the name of the field in your data object that represents the bubble size */ public RealmBubbleDataSet(RealmResults result, String yValuesField, String xIndexField, String sizeField) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java index b2ea4a7de9..0713cb55bb 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java @@ -70,7 +70,7 @@ public class RealmLineDataSet extends RealmLineRadarDataS * Constructor for creating a LineDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the yPx-yValue + * @param yValuesField the name of the field in your data object that represents the y-yValue */ public RealmLineDataSet(RealmResults result, String yValuesField) { super(result, yValuesField); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java index 01d923eef5..d7640fee4a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java @@ -42,7 +42,7 @@ public class RealmPieDataSet extends RealmBaseDataSet result, String yValuesField) { super(result, yValuesField); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java index 0d2ed766aa..7421e3a8ad 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java @@ -32,7 +32,7 @@ public class RealmRadarDataSet extends RealmLineRadarData * Constructor for creating a RadarDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the yPx-yValue + * @param yValuesField the name of the field in your data object that represents the y-yValue */ public RealmRadarDataSet(RealmResults result, String yValuesField) { super(result, yValuesField); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java index d8126e7e06..cd9afa98fc 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java @@ -44,7 +44,7 @@ public class RealmScatterDataSet extends RealmLineScatter * Constructor for creating a ScatterDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the yPx-yValue + * @param yValuesField the name of the field in your data object that represents the y-yValue */ public RealmScatterDataSet(RealmResults result, String yValuesField) { super(result, yValuesField); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FillFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FillFormatter.java index 22ba60528e..66895d77e7 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FillFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FillFormatter.java @@ -12,7 +12,7 @@ public interface FillFormatter { /** - * Returns the vertical (yPx-axis) position where the filled-line of the + * Returns the vertical (y-axis) position where the filled-line of the * LineDataSet should end. * * @param dataSet the ILineDataSet that is currently drawn 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 265c8a66c1..65a6dab0a9 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 @@ -27,7 +27,7 @@ public ChartHighlighter(T chart) { } /** - * Returns a Highlight object corresponding to the given xPx- and yPx- touch positions in pixels. + * Returns a Highlight object corresponding to the given x- and y- touch positions in pixels. * * @param x * @param y @@ -55,13 +55,13 @@ public Highlight getHighlight(float x, float y) { */ protected PointD getValsForTouch(float x, float y) { - // take any transformer to determine the xPx-axis yValue + // take any transformer to determine the x-axis yValue PointD pos = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(x, y); return pos; } /** - * Returns the corresponding SelectionDetail for a given xVal and yPx-touch position in pixels. + * Returns the corresponding SelectionDetail for a given xVal and y-touch position in pixels. * * @param xVal * @param y @@ -155,7 +155,7 @@ protected SelectionDetail getDetails(IDataSet set, int dataSetIndex, float xVal, /** * Returns the SelectionDetail of the DataSet that contains the closest yValue on the - * yPx-axis. + * y-axis. * * @param valsAtIndex all the values at a specific index * @return 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 11244c80ba..63664c9f45 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 @@ -8,10 +8,10 @@ */ public class Highlight { - /** the xPx-yValue of the highlighted yValue */ + /** the x-yValue of the highlighted yValue */ private float mX = Float.NaN; - /** the yPx-yValue of the highlighted yValue */ + /** the y-yValue of the highlighted yValue */ private float mY = Float.NaN; /** the index of the data object - in case it refers to more than one */ @@ -29,8 +29,8 @@ public class Highlight { /** * constructor * - * @param x the xPx-yValue of the highlighted yValue - * @param y the yPx-yValue of the highlighted yValue + * @param x the x-yValue of the highlighted yValue + * @param y the y-yValue of the highlighted yValue * @param dataIndex the index of the Data the highlighted yValue belongs to * @param dataSetIndex the index of the DataSet the highlighted yValue belongs to */ @@ -43,8 +43,8 @@ public Highlight(float x, float y, int dataIndex, int dataSetIndex) { /** * Constructor, only used for stacked-barchart. * - * @param x the xPx-yValue of the highlighted yValue on the xPx-axis - * @param y the yPx-yValue of the highlighted yValue + * @param x the x-yValue of the highlighted yValue on the x-axis + * @param y the y-yValue of the highlighted yValue * @param dataIndex the index of the Data the highlighted yValue belongs to * @param dataSetIndex the index of the DataSet the highlighted yValue belongs to * @param stackIndex references which yValue of a stacked-bar entry has been @@ -58,8 +58,8 @@ public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackInd /** * Constructor, only used for stacked-barchart. * - * @param x the index of the highlighted yValue on the xPx-axis - * @param y the yPx-yValue of the highlighted yValue + * @param x the index of the highlighted yValue on the x-axis + * @param y the y-yValue of the highlighted yValue * @param dataIndex the index of the Data the highlighted yValue belongs to * @param dataSetIndex the index of the DataSet the highlighted yValue belongs to * @param stackIndex references which yValue of a stacked-bar entry has been @@ -82,7 +82,7 @@ public Highlight(float x, int dataSetIndex) { } /** - * returns the xPx-yValue of the highlighted yValue + * returns the x-yValue of the highlighted yValue * * @return */ @@ -91,7 +91,7 @@ public float getX() { } /** - * returns the yPx-yValue of the highlighted yValue + * returns the y-yValue of the highlighted yValue * * @return */ @@ -157,7 +157,7 @@ public boolean equalTo(Highlight h) { @Override public String toString() { - return "Highlight, xPx: " + mX + "yPx: " + mY + ", dataSetIndex: " + mDataSetIndex + return "Highlight, x: " + mX + "y: " + mY + ", dataSetIndex: " + mDataSetIndex + ", stackIndex (only stacked barentry): " + mStackIndex; } } 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 dbfd39e1c2..271ab4e216 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 @@ -17,35 +17,35 @@ public interface IDataSet { /** ###### ###### DATA RELATED METHODS ###### ###### */ /** - * returns the minimum yPx-yValue this DataSet holds + * returns the minimum y-yValue this DataSet holds * * @return */ float getYMin(); /** - * returns the maximum yPx-yValue this DataSet holds + * returns the maximum y-yValue this DataSet holds * * @return */ float getYMax(); /** - * returns the minimum xPx-yValue this DataSet holds + * returns the minimum x-yValue this DataSet holds * * @return */ float getXMin(); /** - * returns the maximum xPx-yValue this DataSet holds + * returns the maximum x-yValue this DataSet holds * * @return */ float getXMax(); /** - * Returns the number of yPx-values this DataSet represents -> the size of the yPx-values array + * Returns the number of y-values this DataSet represents -> the size of the y-values array * -> yvals.size() * * @return @@ -53,7 +53,7 @@ public interface IDataSet { int getEntryCount(); /** - * Calculates the minimum and maximum xPx and yPx values (mXMin, mXMax, mYMin, mYMax). + * Calculates the minimum and maximum x and y values (mXMin, mXMax, mYMin, mYMax). */ void calcMinMax(); @@ -65,7 +65,7 @@ public interface IDataSet { * not over-use in performance critical situations. * * @param xPos - * @param rounding determine to round up/down/closest if there is no Entry matching the provided xPx-index + * @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); @@ -109,7 +109,7 @@ public interface IDataSet { * not over-use in performance critical situations. * * @param xPos - * @param rounding determine to round up/down/closest if there is no Entry matching the provided xPx-index + * @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); @@ -135,7 +135,7 @@ public interface IDataSet { float getYValueForXValue(float xVal); /** - * Returns all of the yPx values of the Entry objects at the given xPos. Returns + * Returns all of the y values of the Entry objects at the given xPos. Returns * Float.NaN if no yValue is at the given xPos. INFORMATION: This method * does calculations at runtime. Do not over-use in performance critical * situations. @@ -178,7 +178,7 @@ public interface IDataSet { /** * Adds an Entry to the DataSet dynamically. - * Entries are added to their appropriate index respective to it's xPx-index. + * Entries are added to their appropriate index respective to it's x-index. * This will also recalculate the current minimum and maximum * values of the DataSet and the yValue-sum. * @@ -250,7 +250,7 @@ public interface IDataSet { YAxis.AxisDependency getAxisDependency(); /** - * Set the yPx-axis this DataSet should be plotted against (either LEFT or + * Set the y-axis this DataSet should be plotted against (either LEFT or * RIGHT). Default: LEFT * * @param dependency @@ -373,7 +373,7 @@ public interface IDataSet { float getValueTextSize(); /** - * set this to true to draw yPx-values on the chart NOTE (for bar and + * 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 * @@ -382,7 +382,7 @@ public interface IDataSet { void setDrawValues(boolean enabled); /** - * Returns true if yPx-yValue drawing is enabled, false if not + * Returns true if y-yValue drawing is enabled, false if not * * @return */ 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 7f293090bd..02713af88a 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 @@ -126,10 +126,10 @@ public boolean onTouch(View v, MotionEvent event) { saveTouchStart(event); - // get the distance between the pointers on the xPx-axis + // get the distance between the pointers on the x-axis mSavedXDist = getXDist(event); - // get the distance between the pointers on the yPx-axis + // get the distance between the pointers on the y-axis mSavedYDist = getYDist(event); // get the total distance between the pointers @@ -363,7 +363,7 @@ private void performZoom(MotionEvent event) { mLastGesture = ChartGesture.X_ZOOM; float xDist = getXDist(event); - float scaleX = xDist / mSavedXDist; // xPx-axis scale + float scaleX = xDist / mSavedXDist; // x-axis scale boolean isZoomingOut = (scaleX < 1); boolean canZoomMoreX = isZoomingOut ? @@ -384,7 +384,7 @@ private void performZoom(MotionEvent event) { mLastGesture = ChartGesture.Y_ZOOM; float yDist = getYDist(event); - float scaleY = yDist / mSavedYDist; // yPx-axis scale + float scaleY = yDist / mSavedYDist; // y-axis scale boolean isZoomingOut = (scaleY < 1); boolean canZoomMoreY = isZoomingOut ? @@ -450,7 +450,7 @@ private static float spacing(MotionEvent event) { } /** - * calculates the distance on the xPx-axis between two pointers (fingers on + * calculates the distance on the x-axis between two pointers (fingers on * the display) * * @param e @@ -462,7 +462,7 @@ private static float getXDist(MotionEvent e) { } /** - * calculates the distance on the yPx-axis between two pointers (fingers on + * calculates the distance on the y-axis between two pointers (fingers on * the display) * * @param e @@ -474,7 +474,7 @@ private static float getYDist(MotionEvent e) { } /** - * returns the correct translation depending on the provided xPx and yPx touch + * returns the correct translation depending on the provided x and y touch * points * * @param x @@ -532,7 +532,7 @@ public boolean onDoubleTap(MotionEvent e) { mChart.zoom(mChart.isScaleXEnabled() ? 1.4f : 1f, mChart.isScaleYEnabled() ? 1.4f : 1f, trans.x, trans.y); if (mChart.isLogEnabled()) - Log.i("BarlineChartTouch", "Double-Tap, Zooming In, xPx: " + trans.x + ", yPx: " + Log.i("BarlineChartTouch", "Double-Tap, Zooming In, x: " + trans.x + ", y: " + trans.y); } 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 0449e79228..a17cdde941 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 @@ -60,8 +60,8 @@ public interface OnChartGestureListener { * Callbacks when the chart is scaled / zoomed via pinch zoom gesture. * * @param me - * @param scaleX scalefactor on the xPx-axis - * @param scaleY scalefactor on the yPx-axis + * @param scaleX scalefactor on the x-axis + * @param scaleY scalefactor on the y-axis */ void onChartScale(MotionEvent me, float scaleX, float scaleY); @@ -69,8 +69,8 @@ public interface OnChartGestureListener { * Callbacks when the chart is moved / translated via drag gesture. * * @param me - * @param dX translation distance on the xPx-axis - * @param dY translation distance on the yPx-axis + * @param dX translation distance on the x-axis + * @param dY translation distance on the y-axis */ void onChartTranslate(MotionEvent me, float dX, float dY); } 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 59ec3d1101..d40e2fdb4e 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 @@ -283,7 +283,7 @@ private float calculateVelocity() { /** * sets the starting angle of the rotation, this is only used by the touch - * listener, xPx and yPx is the touch position + * listener, x and y is the touch position * * @param x * @param y 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 0c641a5ffd..55d8a03ad4 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 @@ -31,7 +31,7 @@ public abstract class AxisRenderer extends Renderer { protected Paint mGridPaint; /** - * paint for the xPx-label values + * paint for the x-label values */ protected Paint mAxisLabelPaint; @@ -117,7 +117,7 @@ public Transformer getTransformer() { */ public void computeAxis(float min, float max, boolean inverted) { - // calculate the starting and entry point of the yPx-labels (depending on + // calculate the starting and entry point of the y-labels (depending on // zoom / contentrect bounds) if (mViewPortHandler != null && mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { @@ -157,7 +157,7 @@ protected void computeAxisValues(float min, float max) { return; } - // Find out how much spacing (in yPx yValue space) between axis values + // Find out how much spacing (in y yValue space) between axis values double rawInterval = range / labelCount; double interval = Utils.roundToNextSignificant(rawInterval); 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 3fcaca0913..a1d9521a55 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 @@ -91,7 +91,7 @@ protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { boolean normalizeSize = dataSet.isNormalizeSizeEnabled(); - // calcualte the full width of 1 step on the xPx-axis + // 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); @@ -245,7 +245,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { boolean normalizeSize = dataSet.isNormalizeSizeEnabled(); - // calcualte the full width of 1 step on the xPx-axis + // 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()); 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 df1fb3da60..39630d8978 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 @@ -339,7 +339,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { dataSetIndex++) { float x = high.getX(); // get the - // xPx-position + // x-position ICandleDataSet set = mChart.getCandleData().getDataSetByIndex(dataSetIndex); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java index 1f19c44492..122e90f80c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java @@ -25,8 +25,8 @@ public LineScatterCandleRadarRenderer(ChartAnimator animator, ViewPortHandler vi * Draws vertical & horizontal highlight-lines if enabled. * * @param c - * @param x xPx-position of the highlight line intersection - * @param y yPx-position of the highlight line intersection + * @param x x-position of the highlight line intersection + * @param y y-position of the highlight line intersection * @param set the currently drawn dataset */ protected void drawHighlightLines(Canvas c, float x, float y, ILineScatterCandleRadarDataSet set) { 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 c9354056c0..8afc282b01 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 @@ -36,7 +36,7 @@ public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer t @Override public void computeAxis(float min, float max, boolean inverted) { - // calculate the starting and entry point of the yPx-labels (depending on + // calculate the starting and entry point of the y-labels (depending on // zoom / contentrect bounds) if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { @@ -156,7 +156,7 @@ public void renderAxisLine(Canvas c) { } /** - * draws the xPx-labels on the specified yPx-position + * draws the x-labels on the specified y-position * * @param pos */ @@ -169,7 +169,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { for (int i = 0; i < positions.length; i += 2) { - // only fill xPx values + // only fill x values if (centeringEnabled) { positions[i] = mXAxis.mCenteredEntries[i / 2]; } else { @@ -223,7 +223,7 @@ public void renderGridLines(Canvas c) { float[] positions = new float[mXAxis.mEntryCount * 2]; for (int i = 0; i < positions.length; i += 2) { - // only fill xPx values + // only fill x values positions[i] = mXAxis.mEntries[i / 2]; } 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 3985343ed1..ba398fc6d7 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 @@ -34,7 +34,7 @@ public XAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, XAxis xA @Override public void computeAxis(float min, float max, boolean inverted) { - // calculate the starting and entry point of the yPx-labels (depending on + // calculate the starting and entry point of the y-labels (depending on // zoom / contentrect bounds) if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { @@ -130,7 +130,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { for (int i = 0; i < positions.length; i += 2) { - // only fill xPx values + // only fill x values if (centeringEnabled) { positions[i + 1] = mXAxis.mCenteredEntries[i / 2]; } else { 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 f5b1539af1..6c18d098c6 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 @@ -55,7 +55,7 @@ public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer t // return; // } // -// // Find out how much spacing (in yPx yValue space) between axis values +// // Find out how much spacing (in y yValue space) between axis values // double rawInterval = range / labelCount; // double interval = Utils.roundToNextSignificant(rawInterval); // @@ -142,7 +142,7 @@ public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer t // } /** - * draws the yPx-axis labels to the screen + * draws the y-axis labels to the screen */ @Override public void renderAxisLabels(Canvas c) { @@ -207,7 +207,7 @@ public void renderAxisLine(Canvas c) { } /** - * draws the yPx-labels on the specified xPx-position + * draws the y-labels on the specified x-position * * @param fixedPosition * @param positions 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 7b5ff68b8a..2841839510 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 @@ -30,13 +30,13 @@ public YAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, YAxis yA /** * Computes the axis values. * - * @param yMin - the minimum yPx-yValue in the data object for this axis - * @param yMax - the maximum yPx-yValue in the data object for this axis + * @param yMin - the minimum y-yValue in the data object for this axis + * @param yMax - the maximum y-yValue in the data object for this axis */ @Override public void computeAxis(float yMin, float yMax, boolean inverted) { - // calculate the starting and entry point of the yPx-labels (depending on + // calculate the starting and entry point of the y-labels (depending on // zoom / contentrect bounds) if (mViewPortHandler.contentHeight() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { @@ -58,7 +58,7 @@ public void computeAxis(float yMin, float yMax, boolean inverted) { } /** - * draws the yPx-axis labels to the screen + * draws the y-axis labels to the screen */ @Override public void renderAxisLabels(Canvas c) { @@ -122,7 +122,7 @@ public void renderAxisLine(Canvas c) { } /** - * draws the yPx-labels on the specified xPx-position + * draws the y-labels on the specified x-position * * @param fixedPosition * @param positions 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 670c8a4eca..db8dec3e73 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 @@ -37,7 +37,7 @@ protected void computeAxisValues(float min, float max) { return; } - // Find out how much spacing (in yPx yValue space) between axis values + // Find out how much spacing (in y yValue space) between axis values double rawInterval = range / labelCount; double interval = Utils.roundToNextSignificant(rawInterval); 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 6cb5c837e7..fa7ff4489a 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 @@ -32,7 +32,7 @@ public boolean equals(final Object obj) { @Override public String toString() { - return width + "xPx" + height; + return width + "x" + height; } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java index e5d7abce23..7335d771ab 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java @@ -20,6 +20,6 @@ public PointD(double x, double y) { * returns a string representation of the object */ public String toString() { - return "PointD, xPx: " + x + ", yPx: " + y; + return "PointD, x: " + x + ", y: " + y; } } 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 189de13cd1..337049f058 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 @@ -96,8 +96,8 @@ public void prepareMatrixOffset(boolean inverted) { } /** - * Transforms an List of Entry into a float array containing the xPx and - * yPx values transformed with all matrices for the SCATTERCHART. + * Transforms an List of Entry into a float array containing the x and + * y values transformed with all matrices for the SCATTERCHART. * * @param data * @return @@ -123,8 +123,8 @@ public float[] generateTransformedValuesScatter(IScatterDataSet data, } /** - * Transforms an List of Entry into a float array containing the xPx and - * yPx values transformed with all matrices for the BUBBLECHART. + * Transforms an List of Entry into a float array containing the x and + * y values transformed with all matrices for the BUBBLECHART. * * @param data * @return @@ -152,8 +152,8 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, } /** - * Transforms an List of Entry into a float array containing the xPx and - * yPx values transformed with all matrices for the LINECHART. + * Transforms an List of Entry into a float array containing the x and + * y values transformed with all matrices for the LINECHART. * * @param data * @return @@ -181,8 +181,8 @@ public float[] generateTransformedValuesLine(ILineDataSet data, } /** - * Transforms an List of Entry into a float array containing the xPx and - * yPx values transformed with all matrices for the CANDLESTICKCHART. + * Transforms an List of Entry into a float array containing the x and + * y values transformed with all matrices for the CANDLESTICKCHART. * * @param data * @return @@ -210,8 +210,8 @@ public float[] generateTransformedValuesCandle(ICandleDataSet data, } /** - * Transforms an List of Entry into a float array containing the xPx and - * yPx values transformed with all matrices for the BARCHART. + * Transforms an List of Entry into a float array containing the x and + * y values transformed with all matrices for the BARCHART. * * @param data * @param dataSet the dataset index @@ -230,7 +230,7 @@ public float[] generateTransformedValuesHorizontalBarChart(IBarDataSet data, Entry e = data.getEntryForIndex(j / 2); float i = e.getX(); - // calculate the xPx-position, depending on datasetcount + // calculate the x-position, depending on datasetcount float x = i + i * (setCount - 1) + dataSet + space * i + space / 2f; float y = e.getY(); @@ -365,7 +365,7 @@ public void rectValuesToPixel(List rects) { } /** - * Transforms the given array of touch positions (pixels) (xPx, yPx, xPx, yPx, ...) + * Transforms the given array of touch positions (pixels) (x, y, x, y, ...) * into values on the chart. * * @param pixels @@ -391,7 +391,7 @@ public void pixelsToValue(float[] pixels) { float[] ptsBuffer = new float[2]; /** - * Returns the xPx and yPx values in the chart at the given touch point + * Returns the x and y values in the chart at the given touch point * (encapsulated in a PointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to * getPixelsForValues(...). @@ -414,7 +414,7 @@ public PointD getValuesByTouchPoint(float x, float y) { } /** - * Returns the xPx and yPx coordinates (pixels) for a given xPx and yPx yValue in the chart. + * Returns the x and y coordinates (pixels) for a given x and y yValue in the chart. * * @param x * @param y 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 9ec8a5e808..e705bd6592 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 @@ -383,7 +383,7 @@ public static double nextUp(double d) { /** * Returns the index of the DataSet that contains the closest yValue on the - * yPx-axis. This is needed for highlighting. This will return -Integer.MAX_VALUE if failure. + * y-axis. This is needed for highlighting. This will return -Integer.MAX_VALUE if failure. * * @param valsAtIndex all the values at a specific index * @return @@ -401,7 +401,7 @@ public static int getClosestDataSetIndexByValue(List valsAtInde /** * Returns the SelectionDetail of the DataSet that contains the closest yValue on the - * yPx-axis. + * y-axis. * * @param valsAtIndex all the values at a specific index * @return @@ -721,7 +721,7 @@ public static int getSDKInt() { */ public static double granularity(float range, int labelCount) { - // Find out how much spacing (in yPx yValue space) between axis values + // Find out how much spacing (in y yValue space) between axis values double rawInterval = range / labelCount; double interval = Utils.roundToNextSignificant(rawInterval); 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 5c37f6eaea..078af45b5b 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 @@ -28,52 +28,52 @@ public class ViewPortHandler { protected float mChartHeight = 0f; /** - * minimum scale yValue on the yPx-axis + * minimum scale yValue on the y-axis */ private float mMinScaleY = 1f; /** - * maximum scale yValue on the yPx-axis + * maximum scale yValue on the y-axis */ private float mMaxScaleY = Float.MAX_VALUE; /** - * minimum scale yValue on the xPx-axis + * minimum scale yValue on the x-axis */ private float mMinScaleX = 1f; /** - * maximum scale yValue on the xPx-axis + * maximum scale yValue on the x-axis */ private float mMaxScaleX = Float.MAX_VALUE; /** - * contains the current scale factor of the xPx-axis + * contains the current scale factor of the x-axis */ private float mScaleX = 1f; /** - * contains the current scale factor of the yPx-axis + * contains the current scale factor of the y-axis */ private float mScaleY = 1f; /** - * current translation (drag distance) on the xPx-axis + * current translation (drag distance) on the x-axis */ private float mTransX = 0f; /** - * current translation (drag distance) on the yPx-axis + * current translation (drag distance) on the y-axis */ private float mTransY = 0f; /** - * offset that allows the chart to be dragged over its bounds on the xPx-axis + * offset that allows the chart to be dragged over its bounds on the x-axis */ private float mTransOffsetX = 0f; /** - * offset that allows the chart to be dragged over its bounds on the xPx-axis + * offset that allows the chart to be dragged over its bounds on the x-axis */ private float mTransOffsetY = 0f; @@ -180,7 +180,7 @@ public float getChartWidth() { /** CODE BELOW THIS RELATED TO SCALING AND GESTURES */ /** - * Zooms in by 1.4f, xPx and yPx are the coordinates (in pixels) of the zoom + * Zooms in by 1.4f, x and y are the coordinates (in pixels) of the zoom * center. * * @param x @@ -197,7 +197,7 @@ public Matrix zoomIn(float x, float y) { } /** - * Zooms out by 0.7f, xPx and yPx are the coordinates (in pixels) of the zoom + * Zooms out by 0.7f, x and y are the coordinates (in pixels) of the zoom * center. */ public Matrix zoomOut(float x, float y) { @@ -228,7 +228,7 @@ public Matrix zoom(float scaleX, float scaleY) { } /** - * Post-scales by the specified scale factors. xPx and yPx is pivot. + * Post-scales by the specified scale factors. x and y is pivot. * * @param scaleX * @param scaleY @@ -264,7 +264,7 @@ public Matrix setZoom(float scaleX, float scaleY) { } /** - * Sets the scale factor to the specified values. xPx and yPx is pivot. + * Sets the scale factor to the specified values. x and y is pivot. * * @param scaleX * @param scaleY @@ -329,7 +329,7 @@ public Matrix translate(final float[] transformedPts) { } /** - * Centers the viewport around the specified position (xPx-index and yPx-yValue) + * Centers the viewport around the specified position (x-index and y-yValue) * in the chart. Centering the viewport outside the bounds of the chart is * not possible. Makes most sense in combination with the * setScaleMinima(...) method. @@ -391,10 +391,10 @@ public void limitTransAndScale(Matrix matrix, RectF content) { float curTransY = matrixBuffer[Matrix.MTRANS_Y]; float curScaleY = matrixBuffer[Matrix.MSCALE_Y]; - // min scale-xPx is 1f + // min scale-x is 1f mScaleX = Math.min(Math.max(mMinScaleX, curScaleX), mMaxScaleX); - // min scale-yPx is 1f + // min scale-y is 1f mScaleY = Math.min(Math.max(mMinScaleY, curScaleY), mMaxScaleY); float width = 0f; @@ -421,7 +421,7 @@ public void limitTransAndScale(Matrix matrix, RectF content) { } /** - * Sets the minimum scale factor for the xPx-axis + * Sets the minimum scale factor for the x-axis * * @param xScale */ @@ -436,7 +436,7 @@ public void setMinimumScaleX(float xScale) { } /** - * Sets the maximum scale factor for the xPx-axis + * Sets the maximum scale factor for the x-axis * * @param xScale */ @@ -451,7 +451,7 @@ public void setMaximumScaleX(float xScale) { } /** - * Sets the minimum and maximum scale factors for the xPx-axis + * Sets the minimum and maximum scale factors for the x-axis * * @param minScaleX * @param maxScaleX @@ -471,7 +471,7 @@ public void setMinMaxScaleX(float minScaleX, float maxScaleX) { } /** - * Sets the minimum scale factor for the yPx-axis + * Sets the minimum scale factor for the y-axis * * @param yScale */ @@ -486,7 +486,7 @@ public void setMinimumScaleY(float yScale) { } /** - * Sets the maximum scale factor for the yPx-axis + * Sets the maximum scale factor for the y-axis * * @param yScale */ @@ -556,14 +556,14 @@ public boolean isInBoundsBottom(float y) { } /** - * returns the current xPx-scale factor + * returns the current x-scale factor */ public float getScaleX() { return mScaleX; } /** - * returns the current yPx-scale factor + * returns the current y-scale factor */ public float getScaleY() { return mScaleY; @@ -586,7 +586,7 @@ public float getMaxScaleY() { } /** - * Returns the translation (drag / pan) distance on the xPx-axis + * Returns the translation (drag / pan) distance on the x-axis * * @return */ @@ -595,7 +595,7 @@ public float getTransX() { } /** - * Returns the translation (drag / pan) distance on the yPx-axis + * Returns the translation (drag / pan) distance on the y-axis * * @return */ @@ -617,7 +617,7 @@ public boolean isFullyZoomedOut() { } /** - * Returns true if the chart is fully zoomed out on it's yPx-axis (vertical). + * Returns true if the chart is fully zoomed out on it's y-axis (vertical). * * @return */ @@ -629,7 +629,7 @@ public boolean isFullyZoomedOutY() { } /** - * Returns true if the chart is fully zoomed out on it's xPx-axis + * Returns true if the chart is fully zoomed out on it's x-axis * (horizontal). * * @return @@ -643,7 +643,7 @@ public boolean isFullyZoomedOutX() { /** * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the xPx-axis. + * bounds on the x-axis. * * @param offset */ @@ -653,7 +653,7 @@ public void setDragOffsetX(float offset) { /** * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the yPx-axis. + * bounds on the y-axis. * * @param offset */ @@ -662,7 +662,7 @@ public void setDragOffsetY(float offset) { } /** - * Returns true if both drag offsets (xPx and yPx) are zero or smaller. + * Returns true if both drag offsets (x and y) are zero or smaller. * * @return */ @@ -671,7 +671,7 @@ public boolean hasNoDragOffset() { } /** - * Returns true if the chart is not yet fully zoomed out on the xPx-axis + * Returns true if the chart is not yet fully zoomed out on the x-axis * * @return */ @@ -680,7 +680,7 @@ public boolean canZoomOutMoreX() { } /** - * Returns true if the chart is not yet fully zoomed in on the xPx-axis + * Returns true if the chart is not yet fully zoomed in on the x-axis * * @return */ @@ -689,7 +689,7 @@ public boolean canZoomInMoreX() { } /** - * Returns true if the chart is not yet fully zoomed out on the yPx-axis + * Returns true if the chart is not yet fully zoomed out on the y-axis * * @return */ @@ -698,7 +698,7 @@ public boolean canZoomOutMoreY() { } /** - * Returns true if the chart is not yet fully zoomed in on the yPx-axis + * Returns true if the chart is not yet fully zoomed in on the y-axis * * @return */ From 565c8a465eccbe1ad03954c0250394fd7eef5c1d Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 4 Jun 2016 15:13:08 +0200 Subject: [PATCH 179/606] More fixes for RadarChart --- .../github/mikephil/charting/charts/RadarChart.java | 12 ++---------- .../mikephil/charting/renderer/AxisRenderer.java | 2 +- .../mikephil/charting/renderer/YAxisRenderer.java | 2 +- 3 files changed, 4 insertions(+), 12 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 854f5bef63..b0da7219d0 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 @@ -99,10 +99,6 @@ protected void init() { protected void calcMinMax() { super.calcMinMax(); - // calculate / set x-axis range -// mXAxis.mAxisMaximum = mData.getXVals().size() - 1; -// mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); - mYAxis.calculate(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); mXAxis.calculate(0, mData.getMaxEntryCountSet().getEntryCount()); } @@ -110,8 +106,8 @@ protected void calcMinMax() { @Override protected float[] getMarkerPosition(Entry e, Highlight highlight) { - float angle = getSliceAngle() * e.getX() + getRotationAngle(); - float val = e.getY() * getFactor(); + float angle = getSliceAngle() * e.getX() * mAnimator.getPhaseX() + getRotationAngle(); + float val = e.getY() * getFactor() * mAnimator.getPhaseY(); PointF c = getCenterOffsets(); PointF p = new PointF((float) (c.x + val * Math.cos(Math.toRadians(angle))), @@ -129,10 +125,6 @@ public void notifyDataSetChanged() { calcMinMax(); -// if (mYAxis.needsDefaultFormatter()) { -// mYAxis.setValueFormatter(mDefaultFormatter); -// } - mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted()); mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); 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 55d8a03ad4..f98c9b2547 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 @@ -51,7 +51,7 @@ public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans, AxisBase this.mTrans = trans; this.mAxis = axis; - if(mTrans != null) { + if(mViewPortHandler != null) { mAxisLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 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 6c18d098c6..03ff4af9f8 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 @@ -28,7 +28,7 @@ public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer t this.mYAxis = yAxis; - if(mTrans != null) { + if(mViewPortHandler != null) { mAxisLabelPaint.setColor(Color.BLACK); mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); From 6fd2ffa2786f29fa7b26f544832569128bd88332 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 4 Jun 2016 15:15:26 +0200 Subject: [PATCH 180/606] Comment refactoring --- .../mpchartexample/BarChartActivitySinus.java | 2 +- .../HorizontalBarChartActivity.java | 2 +- .../mpchartexample/custom/MyMarkerView.java | 2 +- .../custom/RadarMarkerView.java | 2 +- .../custom/StackedBarsMarkerView.java | 4 +- .../notimportant/MainActivity.java | 2 +- .../realm/RealmDatabaseActivityBar.java | 2 +- .../realm/RealmDatabaseActivityBubble.java | 2 +- .../realm/RealmDatabaseActivityLine.java | 2 +- .../realm/RealmDatabaseActivityPie.java | 2 +- .../realm/RealmDatabaseActivityRadar.java | 2 +- .../realm/RealmDatabaseActivityScatter.java | 2 +- .../mikephil/charting/charts/BarChart.java | 10 ++-- .../charting/charts/BarLineChartBase.java | 22 ++++---- .../mikephil/charting/charts/Chart.java | 28 +++++----- .../charting/charts/CombinedChart.java | 4 +- .../charting/charts/HorizontalBarChart.java | 6 +-- .../mikephil/charting/charts/PieChart.java | 10 ++-- .../charting/charts/PieRadarChartBase.java | 4 +- .../mikephil/charting/charts/RadarChart.java | 8 +-- .../charting/components/AxisBase.java | 28 +++++----- .../mikephil/charting/components/Legend.java | 2 +- .../charting/components/LimitLine.java | 14 ++--- .../charting/components/MarkerView.java | 4 +- .../mikephil/charting/components/YAxis.java | 8 +-- .../mikephil/charting/data/BarDataSet.java | 12 ++--- .../mikephil/charting/data/BarEntry.java | 4 +- .../mikephil/charting/data/BaseDataSet.java | 10 ++-- .../mikephil/charting/data/BaseEntry.java | 4 +- .../mikephil/charting/data/BubbleEntry.java | 12 ++--- .../mikephil/charting/data/CandleEntry.java | 34 ++++++------ .../mikephil/charting/data/ChartData.java | 36 ++++++------- .../mikephil/charting/data/CombinedData.java | 4 +- .../mikephil/charting/data/DataSet.java | 8 +-- .../github/mikephil/charting/data/Entry.java | 18 +++---- .../mikephil/charting/data/LineDataSet.java | 6 +-- .../charting/data/LineRadarDataSet.java | 2 +- .../charting/data/filter/Approximator.java | 2 +- .../data/realm/base/RealmBaseDataSet.java | 8 +-- .../realm/base/RealmLineRadarDataSet.java | 2 +- .../realm/implementation/RealmBarDataSet.java | 8 +-- .../implementation/RealmBubbleDataSet.java | 4 +- .../implementation/RealmLineDataSet.java | 8 +-- .../realm/implementation/RealmPieDataSet.java | 2 +- .../implementation/RealmRadarDataSet.java | 2 +- .../implementation/RealmScatterDataSet.java | 2 +- .../formatter/DefaultAxisValueFormatter.java | 2 +- .../formatter/DefaultValueFormatter.java | 4 +- .../formatter/LargeValueFormatter.java | 4 +- .../charting/formatter/PercentFormatter.java | 2 +- .../formatter/StackedValueFormatter.java | 8 +-- .../charting/formatter/ValueFormatter.java | 6 +-- .../charting/highlight/BarHighlighter.java | 4 +- .../charting/highlight/ChartHighlighter.java | 4 +- .../charting/highlight/Highlight.java | 52 +++++++++---------- .../mikephil/charting/highlight/Range.java | 4 +- .../interfaces/datasets/IBarDataSet.java | 6 +-- .../interfaces/datasets/IDataSet.java | 30 +++++------ .../datasets/ILineRadarDataSet.java | 2 +- .../charting/listener/ChartTouchListener.java | 2 +- .../OnChartValueSelectedListener.java | 2 +- .../charting/renderer/AxisRenderer.java | 8 +-- .../charting/renderer/BarChartRenderer.java | 4 +- .../charting/renderer/DataRenderer.java | 12 ++--- .../renderer/HorizontalBarChartRenderer.java | 10 ++-- .../charting/renderer/LineChartRenderer.java | 2 +- .../charting/renderer/PieChartRenderer.java | 8 +-- .../charting/renderer/RadarChartRenderer.java | 6 +-- .../mikephil/charting/renderer/Renderer.java | 2 +- .../charting/renderer/XAxisRenderer.java | 2 +- .../XAxisRendererHorizontalBarChart.java | 2 +- .../renderer/XAxisRendererRadarChart.java | 2 +- .../charting/renderer/YAxisRenderer.java | 6 +-- .../YAxisRendererHorizontalBarChart.java | 6 +-- .../renderer/YAxisRendererRadarChart.java | 6 +-- .../charting/utils/SelectionDetail.java | 4 +- .../mikephil/charting/utils/Transformer.java | 8 +-- .../github/mikephil/charting/utils/Utils.java | 14 ++--- .../charting/utils/ViewPortHandler.java | 10 ++-- 79 files changed, 303 insertions(+), 303 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index ce00167fb0..ae0a20ddb5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -65,7 +65,7 @@ protected void onCreate(Bundle savedInstanceState) { // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); - // draw shadows for each bar that show the maximum yValue + // draw shadows for each bar that show the maximum value // mChart.setDrawBarShadow(true); // mChart.setDrawXLabels(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index d70e7d219a..676da05e7f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -72,7 +72,7 @@ protected void onCreate(Bundle savedInstanceState) { // scaling can now only be done on x- and y-axis separately mChart.setPinchZoom(false); - // draw shadows for each bar that show the maximum yValue + // draw shadows for each bar that show the maximum value // mChart.setDrawBarShadow(true); mChart.setDrawGridBackground(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java index 4389f11c04..53a3bc47f4 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java @@ -50,7 +50,7 @@ public int getXOffset(float xpos) { @Override public int getYOffset(float ypos) { - // this will cause the marker-view to be above the selected yValue + // this will cause the marker-view to be above the selected value return -getHeight(); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java index cac42ef0fd..b86e8ba603 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java @@ -46,7 +46,7 @@ public int getXOffset(float xpos) { @Override public int getYOffset(float ypos) { - // this will cause the marker-view to be above the selected yValue + // this will cause the marker-view to be above the selected value return -getHeight()-10; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java index 4a28f772e8..0b8938778d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java @@ -37,7 +37,7 @@ public void refreshContent(Entry e, Highlight highlight) { if(be.getYVals() != null) { - // draw the stack yValue + // draw the stack value tvContent.setText("" + Utils.formatNumber(be.getYVals()[highlight.getStackIndex()], 0, true)); } else { tvContent.setText("" + Utils.formatNumber(be.getY(), 0, true)); @@ -56,7 +56,7 @@ public int getXOffset(float xpos) { @Override public int getYOffset(float ypos) { - // this will cause the marker-view to be above the selected yValue + // this will cause the marker-view to be above the selected value return -getHeight(); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index dcade871f2..06dbb7b865 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -73,7 +73,7 @@ protected void onCreate(Bundle savedInstanceState) { 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 yValue lines", "A simple demonstration of the pie chart with polyline notes.")); + 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", diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java index 54e4c55bfd..a03a1ad959 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java @@ -50,7 +50,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet(result, "xValue", "yValue"); // stacked entries + RealmBarDataSet set = new RealmBarDataSet(result, "xValue", "value"); // stacked entries set.setColors(new int[] {ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); set.setLabel("Realm BarDataSet"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java index c2c2003d2e..24e3931e6b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java @@ -53,7 +53,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); - RealmBubbleDataSet set = new RealmBubbleDataSet(result, "xValue", "yValue", "bubbleSize"); + RealmBubbleDataSet set = new RealmBubbleDataSet(result, "xValue", "value", "bubbleSize"); set.setLabel("Realm BubbleDataSet"); set.setColors(ColorTemplate.COLORFUL_COLORS, 110); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java index 4e04cf09df..b73d122855 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java @@ -54,7 +54,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); - RealmLineDataSet set = new RealmLineDataSet(result, "xValue", "yValue"); + RealmLineDataSet set = new RealmLineDataSet(result, "xValue", "value"); set.setDrawCubic(false); set.setLabel("Realm LineDataSet"); set.setDrawCircleHole(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java index 8bcda98356..8a2ca4545a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java @@ -54,7 +54,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmPieDataSet set = new RealmPieDataSet(result, "yValue", "label"); // stacked entries + RealmPieDataSet set = new RealmPieDataSet(result, "value", "label"); // stacked entries 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 fe408bdc00..b4cb14840f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java @@ -56,7 +56,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmRadarDataSet set = new RealmRadarDataSet(result, "xValue", "yValue"); // stacked entries + RealmRadarDataSet set = new RealmRadarDataSet(result, "xValue", "value"); // stacked entries set.setLabel("Realm RadarDataSet"); set.setDrawFilled(true); set.setColor(ColorTemplate.rgb("#009688")); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java index 35d598f6cf..caa0190922 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java @@ -54,7 +54,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); - RealmScatterDataSet set = new RealmScatterDataSet(result, "xValue", "yValue"); + RealmScatterDataSet set = new RealmScatterDataSet(result, "xValue", "value"); set.setLabel("Realm ScatterDataSet"); set.setScatterShapeSize(9f); set.setColor(ColorTemplate.rgb("#CDDC39")); 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 e025728920..d48b04d417 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 @@ -32,7 +32,7 @@ public class BarChart extends BarLineChartBase implements BarDataProvid private boolean mDrawValueAboveBar = true; /** - * if set to true, a grey area is drawn behind each bar that indicates the maximum yValue + * if set to true, a grey area is drawn behind each bar that indicates the maximum value */ private boolean mDrawBarShadow = false; @@ -80,7 +80,7 @@ protected void calcMinMax() { } /** - * Returns the Highlight object (contains x-index and DataSet index) of the selected yValue at the given touch + * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch * point * inside the BarChart. * @@ -166,7 +166,7 @@ public boolean isDrawValueAboveBarEnabled() { } /** - * If set to true, a grey area is drawn behind each bar that indicates the maximum yValue. Enabling his will reduce + * If set to true, a grey area is drawn behind each bar that indicates the maximum value. Enabling his will reduce * performance by about 50%. * * @param enabled @@ -203,7 +203,7 @@ public void setFitBars(boolean enabled) { // /** -// * Returns the lowest x-index (yValue on the x-axis) that is still visible on the chart. +// * Returns the lowest x-index (value on the x-axis) that is still visible on the chart. // * // * @return // */ @@ -220,7 +220,7 @@ public void setFitBars(boolean enabled) { // } // // /** -// * Returns the highest x-index (yValue on the x-axis) that is still visible on the chart. +// * Returns the highest x-index (value on the x-axis) that is still visible on the chart. // * // * @return // */ 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 e9055fec74..5a1a860b56 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 @@ -50,7 +50,7 @@ public abstract class BarLineChartBase implements Lin /** * if set to true, a grey area is drawn behind each bar that indicates the - * maximum yValue + * maximum value */ private boolean mDrawBarShadow = false; @@ -189,7 +189,7 @@ public void setDrawValueAboveBar(boolean enabled) { /** * If set to true, a grey area is drawn behind each bar that indicates the - * maximum yValue. Enabling his will reduce performance by about 50%. + * maximum value. Enabling his will reduce performance by about 50%. * * @param enabled */ 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 7ed1ef5666..e8265af86d 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 @@ -170,7 +170,7 @@ public PointF getPosition(Entry e, AxisDependency axis) { } /** - * Returns the Highlight object (contains x-index and DataSet index) of the selected yValue at the given touch point + * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point * inside the BarChart. * * @param x @@ -203,7 +203,7 @@ public float getHighestVisibleX() { } // /** -// * Returns the lowest x-index (yValue on the x-axis) that is still visible on the chart. +// * Returns the lowest x-index (value on the x-axis) that is still visible on the chart. // * // * @return // */ @@ -220,7 +220,7 @@ public float getHighestVisibleX() { // } // // /** -// * Returns the highest x-index (yValue on the x-axis) that is still visible on the chart. +// * Returns the highest x-index (value on the x-axis) that is still visible on the chart. // * // * @return // */ 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 48cce8c707..ba8e37c8ea 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 @@ -256,7 +256,7 @@ public boolean needsHighlight(int xIndex, int dataSetIndex) { } /** - * calculates the needed angle for a given yValue + * calculates the needed angle for a given value * * @param value * @return @@ -266,7 +266,7 @@ private float calcAngle(float value) { } /** - * calculates the needed angle for a given yValue + * calculates the needed angle for a given value * * @param value * @param yValueSum @@ -550,7 +550,7 @@ public float getTransparentCircleRadius() { /** * Sets the amount of transparency the transparent circle should have 0 = fully transparent, * 255 = fully opaque. - * Default yValue is 100. + * Default value is 100. * * @param alpha 0-255 */ @@ -559,7 +559,7 @@ public void setTransparentCircleAlpha(int alpha) { } /** - * set this to true to draw the x-yValue text into the pie slices + * set this to true to draw the x-value text into the pie slices * * @param enabled */ @@ -588,7 +588,7 @@ public boolean isDrawRoundedSlicesEnabled() { /** * If this is enabled, values inside the PieChart are drawn in percent and - * not with their original yValue. Values provided for the ValueFormatter to + * not with their original value. Values provided for the ValueFormatter to * format are then provided in percent. * * @param enabled 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 2c54ec3abc..ea18e39ee7 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 @@ -343,7 +343,7 @@ public void setRotationAngle(float angle) { /** * gets the raw version of the current rotation angle of the pie chart the - * returned yValue could be any yValue, negative or positive, outside of the + * returned value could be any value, negative or positive, outside of the * 360 degrees. this is used when working with rotation direction, mainly by * gestures and animations. * @@ -442,7 +442,7 @@ public float getYChartMin() { /** * Returns an array of SelectionDetail objects for the given x-index. The SelectionDetail - * objects give information about the yValue at the selected index and the + * objects give information about the value at the selected index and the * DataSet it belongs to. INFORMATION: This method does calculations at * runtime. Do not over-use in performance critical situations. * 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 b0da7219d0..fb9d9fb46c 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 @@ -242,7 +242,7 @@ public float getWebLineWidthInner() { } /** - * Sets the transparency (alpha) yValue for all web lines, default: 150, 255 + * Sets the transparency (alpha) value for all web lines, default: 150, 255 * = 100% opaque, 0 = 100% transparent * * @param alpha @@ -252,7 +252,7 @@ public void setWebAlpha(int alpha) { } /** - * Returns the alpha yValue for all web lines. + * Returns the alpha value for all web lines. * * @return */ @@ -339,14 +339,14 @@ public float getRadius() { } /** - * Returns the maximum yValue this chart can display on it's y-axis. + * Returns the maximum value this chart can display on it's y-axis. */ public float getYChartMax() { return mYAxis.mAxisMaximum; } /** - * Returns the minimum yValue this chart can display on it's y-axis. + * Returns the minimum value this chart can display on it's y-axis. */ public float getYChartMin() { return mYAxis.mAxisMinimum; 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 7f852342e2..dcb613b926 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 @@ -65,7 +65,7 @@ public abstract class AxisBase extends ComponentBase { /** * When true, axis labels are controlled by the `granularity` property. * When false, axis values could possibly be repeated. - * This could happen if two adjacent axis values are rounded to same yValue. + * This could happen if two adjacent axis values are rounded to same value. * If using granularity this could be avoided by having fewer axis values visible. */ protected boolean mGranularityEnabled = false; @@ -108,12 +108,12 @@ public abstract class AxisBase extends ComponentBase { protected boolean mDrawLimitLineBehindData = false; /** - * flag indicating that the axis-min yValue has been customized + * flag indicating that the axis-min value has been customized */ protected boolean mCustomAxisMin = false; /** - * flag indicating that the axis-max yValue has been customized + * flag indicating that the axis-max value has been customized */ protected boolean mCustomAxisMax = false; @@ -341,7 +341,7 @@ public boolean isGranularityEnabled() { } /** - * Enabled/disable granularity control on axis yValue intervals. If enabled, the axis + * Enabled/disable granularity control on axis value intervals. If enabled, the axis * interval is not allowed to go below a certain granularity. Default: false * * @param enabled @@ -538,7 +538,7 @@ public float getAxisMinimum() { } /** - * By calling this method, any custom maximum yValue that has been previously set is reseted, + * By calling this method, any custom maximum value that has been previously set is reseted, * and the calculation is * done automatically. */ @@ -547,7 +547,7 @@ public void resetAxisMaxValue() { } /** - * Returns true if the axis max yValue has been customized (and is not calculated automatically) + * Returns true if the axis max value has been customized (and is not calculated automatically) * * @return */ @@ -556,7 +556,7 @@ public boolean isAxisMaxCustom() { } /** - * By calling this method, any custom minimum yValue that has been previously set is reseted, + * By calling this method, any custom minimum value that has been previously set is reseted, * and the calculation is * done automatically. */ @@ -565,7 +565,7 @@ public void resetAxisMinValue() { } /** - * Returns true if the axis min yValue has been customized (and is not calculated automatically) + * Returns true if the axis min value has been customized (and is not calculated automatically) * * @return */ @@ -574,11 +574,11 @@ public boolean isAxisMinCustom() { } /** - * Set a custom minimum yValue for this axis. If set, this yValue will not be calculated + * Set a custom minimum value for this axis. If set, this value will not be calculated * automatically depending on * the provided data. Use resetAxisMinValue() to undo this. Do not forget to call * setStartAtZero(false) if you use - * this method. Otherwise, the axis-minimum yValue will still be forced to 0. + * this method. Otherwise, the axis-minimum value will still be forced to 0. * * @param min */ @@ -589,7 +589,7 @@ public void setAxisMinValue(float min) { } /** - * Set a custom maximum yValue for this axis. If set, this yValue will not be calculated + * Set a custom maximum value for this axis. If set, this value will not be calculated * automatically depending on * the provided data. Use resetAxisMaxValue() to undo this. * @@ -605,12 +605,12 @@ public void setAxisMaxValue(float max) { * Calculates the minimum / maximum and range values of the axis with the given * minimum and maximum values from the chart data. * - * @param dataMin the min yValue according to chart data - * @param dataMax the max yValue according to chart data + * @param dataMin the min value according to chart data + * @param dataMax the max value according to chart data */ public void calculate(float dataMin, float dataMax) { - // if custom, use yValue as is, else use data yValue + // if custom, use value as is, else use data value float min = mCustomAxisMin ? mAxisMinimum : dataMin; float max = mCustomAxisMax ? mAxisMaximum : dataMax; 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 0514d91f32..77f1710f27 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 @@ -76,7 +76,7 @@ public enum LegendDirection { private String[] mExtraLabels; /** - * Are the legend labels/colors a custom yValue or auto calculated? If false, + * Are the legend labels/colors a custom value or auto calculated? If false, * then it's auto, if true, then custom. default false (automatic legend) */ private boolean mIsLegendCustom = false; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java index 791e56e2e0..8fcdee8fc1 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java @@ -17,7 +17,7 @@ */ public class LimitLine extends ComponentBase { - /** limit / maximum (the y-yValue or xIndex) */ + /** limit / maximum (the y-value or xIndex) */ private float mLimit = 0f; /** the width of the limit line */ @@ -46,7 +46,7 @@ public enum LimitLabelPosition { /** * Constructor with limit. * - * @param limit - the position (the yValue) on the y-axis (y-yValue) or x-axis + * @param limit - the position (the value) on the y-axis (y-value) or x-axis * (xIndex) where this line should appear */ public LimitLine(float limit) { @@ -56,7 +56,7 @@ public LimitLine(float limit) { /** * Constructor with limit and label. * - * @param limit - the position (the yValue) on the y-axis (y-yValue) or x-axis + * @param limit - the position (the value) on the y-axis (y-value) or x-axis * (xIndex) where this line should appear * @param label - provide "" if no label is required */ @@ -157,7 +157,7 @@ public DashPathEffect getDashPathEffect() { } /** - * Sets the color of the yValue-text that is drawn next to the LimitLine. + * Sets the color of the value-text that is drawn next to the LimitLine. * Default: Paint.Style.FILL_AND_STROKE * * @param style @@ -167,7 +167,7 @@ public void setTextStyle(Paint.Style style) { } /** - * Returns the color of the yValue-text that is drawn next to the LimitLine. + * Returns the color of the value-text that is drawn next to the LimitLine. * * @return */ @@ -176,7 +176,7 @@ public Paint.Style getTextStyle() { } /** - * Sets the position of the LimitLine yValue label (either on the right or on + * Sets the position of the LimitLine value label (either on the right or on * the left edge of the chart). Not supported for RadarChart. * * @param pos @@ -186,7 +186,7 @@ public void setLabelPosition(LimitLabelPosition pos) { } /** - * Returns the position of the LimitLine label (yValue). + * Returns the position of the LimitLine label (value). * * @return */ 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 be0786133f..523376c786 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 @@ -68,7 +68,7 @@ public void draw(Canvas canvas, float posx, float posy) { * * @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 yValue 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). */ public abstract void refreshContent(Entry e, Highlight highlight); @@ -84,7 +84,7 @@ public void draw(Canvas canvas, float posx, float posy) { /** * 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 yValue. + * -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 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 317d7a3a97..e2663dde6b 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 @@ -10,7 +10,7 @@ * modify it. Do not * access public variables directly. Be aware that not all features the YLabels class provides * are suitable for the - * RadarChart. Customizations that affect the yValue range of the axis need to be applied before + * RadarChart. Customizations that affect the value range of the axis need to be applied before * setting data for the * chart. * @@ -44,12 +44,12 @@ public class YAxis extends AxisBase { protected float mZeroLineWidth = 1f; /** - * axis space from the largest yValue to the top in percent of the total axis range + * axis space from the largest value to the top in percent of the total axis range */ protected float mSpacePercentTop = 10f; /** - * axis space from the smallest yValue to the bottom in percent of the total axis range + * axis space from the smallest value to the bottom in percent of the total axis range */ protected float mSpacePercentBottom = 10f; @@ -346,7 +346,7 @@ public boolean needsOffset() { @Override public void calculate(float dataMin, float dataMax) { - // if custom, use yValue as is, else use data yValue + // if custom, use value as is, else use data value float min = mCustomAxisMin ? mAxisMinimum : dataMin; float max = mCustomAxisMax ? mAxisMaximum : dataMax; 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 14738aa4bc..f77a26e446 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 @@ -11,7 +11,7 @@ public class BarDataSet extends BarLineScatterCandleBubbleDataSet implements IBarDataSet { /** - * the maximum number of bars that are stacked upon each other, this yValue + * the maximum number of bars that are stacked upon each other, this value * is calculated from the Entries that are added to the DataSet */ private int mStackSize = 1; @@ -26,12 +26,12 @@ public class BarDataSet extends BarLineScatterCandleBubbleDataSet impl private int mBarBorderColor = Color.BLACK; /** - * the alpha yValue used to draw the highlight indicator bar + * the alpha value used to draw the highlight indicator bar */ private int mHighLightAlpha = 120; /** - * the overall entry count, including counting each stack-yValue individually + * the overall entry count, including counting each stack-value individually */ private int mEntryCountStacks = 0; @@ -170,7 +170,7 @@ public boolean isStacked() { } /** - * returns the overall entry count, including counting each stack-yValue + * returns the overall entry count, including counting each stack-value * individually * * @return @@ -181,7 +181,7 @@ public int getEntryCountStacks() { /** * Sets the color used for drawing the bar-shadows. The bar shadows is a - * surface behind the bar that indicates the maximum yValue. Don't for get to + * surface behind the bar that indicates the maximum value. Don't for get to * use getResources().getColor(...) to set this. Or Color.rgb(...). * * @param color @@ -236,7 +236,7 @@ public int getBarBorderColor() { } /** - * Set the alpha yValue (transparency) that is used for drawing the highlight + * Set the alpha value (transparency) that is used for drawing the highlight * indicator bar. min = 0 (fully transparent), max = 255 (fully opaque) * * @param alpha 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 62b2a8a940..97358ae378 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 @@ -84,7 +84,7 @@ public BarEntry copy() { } /** - * Returns the stacked values this BarEntry represents, or null, if only a single yValue is represented (then, use + * Returns the stacked values this BarEntry represents, or null, if only a single value is represented (then, use * getY()). * * @return @@ -105,7 +105,7 @@ public void setVals(float[] vals) { } /** - * Returns the yValue of this BarEntry. If the entry is stacked, it returns the positive sum of all values. + * Returns the value of this BarEntry. If the entry is stacked, it returns the positive sum of all values. * * @return */ 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 866f78f678..e218dec225 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 @@ -42,7 +42,7 @@ public abstract class BaseDataSet implements IDataSet { protected YAxis.AxisDependency mAxisDependency = YAxis.AxisDependency.LEFT; /** - * if true, yValue highlightning is enabled + * if true, value highlightning is enabled */ protected boolean mHighlightEnabled = true; @@ -52,7 +52,7 @@ public abstract class BaseDataSet implements IDataSet { protected transient ValueFormatter mValueFormatter; /** - * the typeface used for the yValue text + * the typeface used for the value text */ protected Typeface mValueTypeface; @@ -62,7 +62,7 @@ public abstract class BaseDataSet implements IDataSet { protected boolean mDrawValues = true; /** - * the size of the yValue-text labels + * the size of the value-text labels */ protected float mValueTextSize = 17f; @@ -196,7 +196,7 @@ public void setColor(int color) { } /** - * Sets a color with a specific alpha yValue. + * Sets a color with a specific alpha value. * * @param color * @param alpha from 0-255 @@ -206,7 +206,7 @@ public void setColor(int color, int alpha) { } /** - * Sets colors with a specific alpha yValue. + * Sets colors with a specific alpha value. * * @param colors * @param alpha 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 09896c84a4..099fee86d8 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 @@ -25,7 +25,7 @@ public BaseEntry(float y, Object data) { } /** - * Returns the y yValue of this Entry. + * Returns the y value of this Entry. * * @return */ @@ -34,7 +34,7 @@ public float getY() { } /** - * Sets the y-yValue for the Entry. + * Sets the y-value for the Entry. * * @param y */ 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 d0371cc7fc..c6ff908b85 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 @@ -4,7 +4,7 @@ import android.annotation.SuppressLint; /** - * Subclass of Entry that holds a yValue for one entry in a BubbleChart. Bubble + * Subclass of Entry that holds a value for one entry in a BubbleChart. Bubble * chart implementation: Copyright 2015 Pierre-Marc Airoldi Licensed under * Apache License 2.0 * @@ -13,14 +13,14 @@ @SuppressLint("ParcelCreator") public class BubbleEntry extends Entry { - /** size yValue */ + /** size value */ private float mSize = 0f; /** * Constructor. * - * @param x The yValue on the x-axis. - * @param y The yValue on the y-axis. + * @param x The value on the x-axis. + * @param y The value on the y-axis. * @param size The size of the bubble. */ public BubbleEntry(float x, float y, float size) { @@ -31,8 +31,8 @@ public BubbleEntry(float x, float y, float size) { /** * Constructor. * - * @param x The yValue on the x-axis. - * @param y The yValue on the y-axis. + * @param x The value on the x-axis. + * @param y The value on the y-axis. * @param size The size of the bubble. * @param data Spot for additional data this Entry represents. */ 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 41e69ffba7..efe87d077a 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 @@ -11,26 +11,26 @@ @SuppressLint("ParcelCreator") public class CandleEntry extends Entry { - /** shadow-high yValue */ + /** shadow-high value */ private float mShadowHigh = 0f; - /** shadow-low yValue */ + /** shadow-low value */ private float mShadowLow = 0f; - /** close yValue */ + /** close value */ private float mClose = 0f; - /** open yValue */ + /** open value */ private float mOpen = 0f; /** * Constructor. * - * @param x The yValue on the x-axis. - * @param shadowH The (shadow) high yValue. - * @param shadowL The (shadow) low yValue. - * @param open The open yValue. - * @param close The close yValue. + * @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); @@ -44,9 +44,9 @@ public CandleEntry(float x, float shadowH, float shadowL, float open, float clos /** * Constructor. * - * @param x The yValue on the x-axis. - * @param shadowH The (shadow) high yValue. - * @param shadowL The (shadow) low yValue. + * @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. @@ -81,7 +81,7 @@ public float getBodyRange() { } /** - * Returns the center yValue of the candle. (Middle yValue between high and + * Returns the center value of the candle. (Middle value between high and * low) */ @Override @@ -98,7 +98,7 @@ public CandleEntry copy() { } /** - * Returns the upper shadows highest yValue. + * Returns the upper shadows highest value. * * @return */ @@ -111,7 +111,7 @@ public void setHigh(float mShadowHigh) { } /** - * Returns the lower shadows lowest yValue. + * Returns the lower shadows lowest value. * * @return */ @@ -124,7 +124,7 @@ public void setLow(float mShadowLow) { } /** - * Returns the bodys close yValue. + * Returns the bodys close value. * * @return */ @@ -137,7 +137,7 @@ public void setClose(float mClose) { } /** - * Returns the bodys open yValue. + * Returns the bodys open value. * * @return */ 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 0cd23e873c..abc1fd034e 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 @@ -23,22 +23,22 @@ public abstract class ChartData> { /** - * maximum y-yValue in the yValue array across all axes + * maximum y-value in the value array across all axes */ protected float mYMax = 0.0f; /** - * the minimum y-yValue in the yValue array across all axes + * the minimum y-value in the value array across all axes */ protected float mYMin = 0.0f; /** - * maximum x-yValue in the yValue array + * maximum x-value in the value array */ protected float mXMax = 0f; /** - * minimum x-yValue in the yValue array + * minimum x-value in the value array */ protected float mXMin = 0f; @@ -87,7 +87,7 @@ public ChartData(List sets) { /** * performs all kinds of initialization calculations, such as min-max and - * yValue count and sum + * value count and sum */ protected void init() { @@ -227,7 +227,7 @@ public int getDataSetCount() { } /** - * Returns the smallest y-yValue the data object contains. + * Returns the smallest y-value the data object contains. * * @return */ @@ -236,7 +236,7 @@ public float getYMin() { } /** - * Returns the minimum y-yValue for the specified axis. + * Returns the minimum y-value for the specified axis. * * @param axis * @return @@ -249,7 +249,7 @@ public float getYMin(AxisDependency axis) { } /** - * Returns the greatest y-yValue the data object contains. + * Returns the greatest y-value the data object contains. * * @return */ @@ -258,7 +258,7 @@ public float getYMax() { } /** - * Returns the maximum y-yValue for the specified axis. + * Returns the maximum y-value for the specified axis. * * @param axis * @return @@ -271,7 +271,7 @@ public float getYMax(AxisDependency axis) { } /** - * Returns the minimum x-yValue this data object contains. + * Returns the minimum x-value this data object contains. * * @return */ @@ -280,7 +280,7 @@ public float getXMin() { } /** - * Returns the maximum x-yValue this data object contains. + * Returns the maximum x-value this data object contains. * * @return */ @@ -372,8 +372,8 @@ public Entry getEntryForHighlight(Highlight highlight) { // if (highlight.getDataSetIndex() >= mDataSets.size()) // return null; // else { -// // The yValue of the highlighted entry could be NaN - -// // if we are not interested in highlighting a specific yValue. +// // The value of the highlighted entry could be NaN - +// // if we are not interested in highlighting a specific value. // // List entries = mDataSets.get(highlight.getDataSetIndex()) // .getEntriesForXPos(highlight.getX()); @@ -765,7 +765,7 @@ public void setValueFormatter(ValueFormatter f) { } /** - * Sets the color of the yValue-text (color in which the yValue-labels are + * Sets the color of the value-text (color in which the value-labels are * drawn) for all DataSets this data object contains. * * @param color @@ -777,7 +777,7 @@ public void setValueTextColor(int color) { } /** - * Sets the same list of yValue-colors for all DataSets this + * Sets the same list of value-colors for all DataSets this * data object contains. * * @param colors @@ -789,7 +789,7 @@ public void setValueTextColors(List colors) { } /** - * Sets the Typeface for all yValue-labels for all DataSets this data object + * Sets the Typeface for all value-labels for all DataSets this data object * contains. * * @param tf @@ -801,7 +801,7 @@ public void setValueTypeface(Typeface tf) { } /** - * Sets the size (in dp) of the yValue-text for all DataSets this data object + * Sets the size (in dp) of the value-text for all DataSets this data object * contains. * * @param size @@ -813,7 +813,7 @@ public void setValueTextSize(float size) { } /** - * Enables / disables drawing values (yValue-text) for all DataSets this data + * Enables / disables drawing values (value-text) for all DataSets this data * object contains. * * @param enabled 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 174cac65a8..65bb6cca62 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 @@ -131,8 +131,8 @@ public Entry getEntryForHighlight(Highlight highlight) { if (highlight.getDataSetIndex() >= data.getDataSetCount()) return null; else { - // The yValue of the highlighted entry could be NaN - - // if we are not interested in highlighting a specific yValue. + // 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()) .getEntriesForXPos(highlight.getX()); 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 2cc8422843..f260339ca7 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 @@ -20,22 +20,22 @@ public abstract class DataSet extends BaseDataSet { protected List mValues = null; /** - * maximum y-yValue in the yValue array + * maximum y-value in the value array */ protected float mYMax = 0.0f; /** - * minimum y-yValue in the yValue array + * minimum y-value in the value array */ protected float mYMin = 0.0f; /** - * maximum x-yValue in the yValue array + * maximum x-value in the value array */ protected float mXMax = 0.0f; /** - * minimum x-yValue in the yValue array + * minimum x-value in the value array */ protected float mXMin = 0.0f; 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 a210093fad..f5847a029d 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 @@ -7,7 +7,7 @@ /** * Class representing one entry in the chart. Might contain multiple values. - * Might only contain a single yValue depending on the used constructor. + * Might only contain a single value depending on the used constructor. * * @author Philipp Jahoda */ @@ -23,8 +23,8 @@ public Entry() { /** * A Entry represents one single entry in the chart. * - * @param x the x yValue - * @param y the y yValue (the actual yValue of the entry) + * @param x the x value + * @param y the y value (the actual value of the entry) */ public Entry(float x, float y) { super(y); @@ -34,8 +34,8 @@ public Entry(float x, float y) { /** * A Entry represents one single entry in the chart. * - * @param x the x yValue - * @param y the y yValue (the actual yValue of the entry) + * @param x the x value + * @param y the y value (the actual value of the entry) * @param data Spot for additional data this Entry represents. */ public Entry(float x, float y, Object data) { @@ -44,7 +44,7 @@ public Entry(float x, float y, Object data) { } /** - * Returns the x-yValue of this Entry object. + * Returns the x-value of this Entry object. * * @return */ @@ -53,7 +53,7 @@ public float getX() { } /** - * Sets the x-yValue of this Entry object. + * Sets the x-value of this Entry object. * * @param x */ @@ -72,7 +72,7 @@ public Entry copy() { } /** - * Compares yValue, xIndex and data of the entries. Returns true if entries + * Compares value, xIndex and data of the entries. Returns true if entries * are equal in those points, false if not. Does not check by hash-code like * it's done by the "equals" method. * @@ -97,7 +97,7 @@ public boolean equalTo(Entry e) { } /** - * returns a string representation of the entry containing x-index and yValue + * returns a string representation of the entry containing x-index and value */ @Override public String toString() { 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 b8c3f5e642..d0d7cb3e82 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 @@ -25,10 +25,10 @@ public class LineDataSet extends LineRadarDataSet implements ILineDataSet /** the color of the inner circles */ private int mCircleColorHole = Color.WHITE; - /** the radius of the circle-shaped yValue indicators */ + /** the radius of the circle-shaped value indicators */ private float mCircleRadius = 8f; - /** the hole radius of the circle-shaped yValue indicators */ + /** the hole radius of the circle-shaped value indicators */ private float mCircleHoleRadius = 4f; /** sets the intensity of the cubic lines */ @@ -155,7 +155,7 @@ public float getCircleHoleRadius() { } /** - * sets the size (radius) of the circle shpaed yValue indicators, + * sets the size (radius) of the circle shpaed value indicators, * default size = 4f * * This method is deprecated because of unclarity. Use setCircleRadius instead. 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 14dc008243..deced96ee9 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 @@ -84,7 +84,7 @@ public int getFillAlpha() { } /** - * sets the alpha yValue (transparency) that is used for filling the line + * sets the alpha value (transparency) that is used for filling the line * surface (0-255), default: 85 * * @param alpha diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java index f562f22f06..cbc630c562 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java @@ -163,7 +163,7 @@ private List reduceWithDouglasPeuker(List entries, double epsilon) * epsilon (tolerance) * * @param entries - * @param epsilon as y-yValue + * @param epsilon as y-value * @param start * @param end */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java index b0fad9d14f..63e10754b5 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java @@ -28,22 +28,22 @@ public abstract class RealmBaseDataSet e protected List mValues; /** - * maximum y-yValue in the y-yValue array + * maximum y-value in the y-value array */ protected float mYMax = 0.0f; /** - * the minimum y-yValue in the y-yValue array + * the minimum y-value in the y-value array */ protected float mYMin = 0.0f; /** - * maximum x-yValue in the yValue array + * maximum x-value in the value array */ protected float mXMax = 0.0f; /** - * minimum x-yValue in the yValue array + * minimum x-value in the value array */ protected float mXMin = 0.0f; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java index 73e95f80c2..31010f2489 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java @@ -82,7 +82,7 @@ public int getFillAlpha() { } /** - * sets the alpha yValue (transparency) that is used for filling the line + * sets the alpha value (transparency) that is used for filling the line * surface (0-255), default: 85 * * @param alpha diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java index fa9c12c4a0..f74379f5a6 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java @@ -21,7 +21,7 @@ public class RealmBarDataSet extends RealmBarLineScatterC private String mStackValueFieldName; /** - * the maximum number of bars that are stacked upon each other, this yValue + * the maximum number of bars that are stacked upon each other, this value * is calculated from the Entries that are added to the DataSet */ private int mStackSize = 1; @@ -36,7 +36,7 @@ public class RealmBarDataSet extends RealmBarLineScatterC private int mBarBorderColor = Color.BLACK; /** - * the alpha yValue used to draw the highlight indicator bar + * the alpha value used to draw the highlight indicator bar */ private int mHighLightAlpha = 120; @@ -181,7 +181,7 @@ public boolean isStacked() { /** * Sets the color used for drawing the bar-shadows. The bar shadows is a - * surface behind the bar that indicates the maximum yValue. Don't for get to + * surface behind the bar that indicates the maximum value. Don't for get to * use getResources().getColor(...) to set this. Or Color.rgb(...). * * @param color @@ -236,7 +236,7 @@ public int getBarBorderColor() { } /** - * Set the alpha yValue (transparency) that is used for drawing the highlight + * Set the alpha value (transparency) that is used for drawing the highlight * indicator bar. min = 0 (fully transparent), max = 255 (fully opaque) * * @param alpha diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java index 67700a428e..05a7ff523d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java @@ -25,7 +25,7 @@ public class RealmBubbleDataSet extends RealmBarLineScatt * Constructor for creating a CandleDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-yValue + * @param yValuesField the name of the field in your data object that represents the y-value * @param sizeField the name of the field in your data object that represents the bubble size */ public RealmBubbleDataSet(RealmResults result, String yValuesField, String sizeField) { @@ -40,7 +40,7 @@ public RealmBubbleDataSet(RealmResults result, String yValuesField, String si * Constructor for creating a CandleDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-yValue + * @param yValuesField the name of the field in your data object that represents the y-value * @param xIndexField the name of the field in your data object that represents the x-index * @param sizeField the name of the field in your data object that represents the bubble size */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java index 0713cb55bb..c985aee55c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java @@ -37,11 +37,11 @@ public class RealmLineDataSet extends RealmLineRadarDataS private int mCircleColorHole = Color.WHITE; /** - * the radius of the circle-shaped yValue indicators + * the radius of the circle-shaped value indicators */ private float mCircleRadius = 8f; - /** the hole radius of the circle-shaped yValue indicators */ + /** the hole radius of the circle-shaped value indicators */ private float mCircleHoleRadius = 4f; /** @@ -70,7 +70,7 @@ public class RealmLineDataSet extends RealmLineRadarDataS * Constructor for creating a LineDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-yValue + * @param yValuesField the name of the field in your data object that represents the y-value */ public RealmLineDataSet(RealmResults result, String yValuesField) { super(result, yValuesField); @@ -142,7 +142,7 @@ public float getCubicIntensity() { } /** - * sets the size (radius) of the circle shpaed yValue indicators, default + * sets the size (radius) of the circle shpaed value indicators, default * size = 4f * * @param size diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java index d7640fee4a..985fc1d143 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java @@ -42,7 +42,7 @@ public class RealmPieDataSet extends RealmBaseDataSet result, String yValuesField) { super(result, yValuesField); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java index 7421e3a8ad..880e68586f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java @@ -32,7 +32,7 @@ public class RealmRadarDataSet extends RealmLineRadarData * Constructor for creating a RadarDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-yValue + * @param yValuesField the name of the field in your data object that represents the y-value */ public RealmRadarDataSet(RealmResults result, String yValuesField) { super(result, yValuesField); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java index cd9afa98fc..35f3863c02 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java @@ -44,7 +44,7 @@ public class RealmScatterDataSet extends RealmLineScatter * Constructor for creating a ScatterDataSet with realm data. * * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-yValue + * @param yValuesField the name of the field in your data object that represents the y-value */ public RealmScatterDataSet(RealmResults result, String yValuesField) { super(result, yValuesField); 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 80294399e9..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 @@ -20,7 +20,7 @@ public class DefaultAxisValueFormatter implements AxisValueFormatter { protected int digits = 0; /** - * Constructor that specifies to how many digits the yValue should be + * Constructor that specifies to how many digits the value should be * formatted. * * @param digits 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 00c514aff0..a2de6d99bb 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 @@ -8,7 +8,7 @@ /** * Default formatter used for formatting values inside the chart. Uses a DecimalFormat with - * pre-calculated number of digits (depending on max and min yValue). + * pre-calculated number of digits (depending on max and min value). * * @author Philipp Jahoda */ @@ -18,7 +18,7 @@ public class DefaultValueFormatter implements ValueFormatter { protected DecimalFormat mFormat; /** - * Constructor that specifies to how many digits the yValue should be + * Constructor that specifies to how many digits the value should be * formatted. * * @param digits 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 ba9d355830..0f852b6f74 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 @@ -8,7 +8,7 @@ import java.text.DecimalFormat; /** - * Predefined yValue-formatter that formats large numbers in a pretty way. + * Predefined value-formatter that formats large numbers in a pretty way. * Outputs: 856 = 856; 1000 = 1k; 5821 = 5.8k; 10500 = 10k; 101800 = 102k; * 2000000 = 2m; 7800000 = 7.8m; 92150000 = 92m; 123200000 = 123m; 9999999 = * 10m; 1000000000 = 1b; Special thanks to Roman Gromov @@ -53,7 +53,7 @@ public String getFormattedValue(float value, AxisBase axis) { } /** - * Set an appendix text to be added at the end of the formatted yValue. + * Set an appendix text to be added at the end of the formatted value. * * @param appendix */ 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 b4a5fd9dee..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 @@ -9,7 +9,7 @@ /** * This ValueFormatter is just for convenience and simply puts a "%" sign after - * each yValue. (Recommeded for PieChart) + * each value. (Recommeded for PieChart) * * @author Philipp Jahoda */ 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 4c5dce6b50..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 @@ -10,7 +10,7 @@ * Created by Philipp Jahoda on 28/01/16. *

* A formatter specifically for stacked BarChart that allows to specify whether the all stack values - * or just the top yValue should be drawn. + * or just the top value should be drawn. */ public class StackedValueFormatter implements ValueFormatter { @@ -20,7 +20,7 @@ public class StackedValueFormatter implements ValueFormatter { private boolean mDrawWholeStack; /** - * a string that should be appended behind the yValue + * a string that should be appended behind the value */ private String mAppendix; @@ -30,7 +30,7 @@ public class StackedValueFormatter implements ValueFormatter { * 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 yValue + * @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 appendix, int decimals) { @@ -68,7 +68,7 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View } } - // return the "proposed" yValue + // return the "proposed" value 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 index 12e5ac6a72..1f056bf786 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java @@ -14,12 +14,12 @@ public interface ValueFormatter { /** - * Called when a yValue (from labels inside the chart) is formatted + * Called when a value (from labels inside the chart) is formatted * before being drawn. For performance reasons, avoid excessive calculations * and memory allocations inside this method. * - * @param value the yValue to be formatted - * @param entry the entry the yValue belongs to - in e.g. BarChart, this is of class BarEntry + * @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 the formatted label ready for being drawn 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 1175887aba..1d23073740 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 @@ -51,7 +51,7 @@ public Highlight getHighlight(float x, float y) { } /** - * This method creates the Highlight object that also indicates which yValue of a stacked BarEntry has been + * This method creates the Highlight object that also indicates which value of a stacked BarEntry has been * selected. * * @param selectionDetail the selection detail to work with looking for stacked values @@ -97,7 +97,7 @@ protected Highlight getStackedHighlight( } /** - * Returns the index of the closest yValue inside the values array / ranges (stacked barchart) to the yValue + * Returns the index of the closest value inside the values array / ranges (stacked barchart) to the value * given as * a parameter. * 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 65a6dab0a9..a12d5c3fdc 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 @@ -55,7 +55,7 @@ public Highlight getHighlight(float x, float y) { */ protected PointD getValsForTouch(float x, float y) { - // take any transformer to determine the x-axis yValue + // take any transformer to determine the x-axis value PointD pos = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(x, y); return pos; } @@ -154,7 +154,7 @@ protected SelectionDetail getDetails(IDataSet set, int dataSetIndex, float xVal, } /** - * Returns the SelectionDetail of the DataSet that contains the closest yValue on the + * Returns the SelectionDetail of the DataSet that contains the closest value on the * y-axis. * * @param valsAtIndex all the values at a specific index 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 63664c9f45..f46329f336 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 @@ -2,25 +2,25 @@ package com.github.mikephil.charting.highlight; /** - * Contains information needed to determine the highlighted yValue. + * Contains information needed to determine the highlighted value. * * @author Philipp Jahoda */ public class Highlight { - /** the x-yValue of the highlighted yValue */ + /** the x-value of the highlighted value */ private float mX = Float.NaN; - /** the y-yValue of the highlighted yValue */ + /** the y-value of the highlighted value */ private float mY = Float.NaN; /** the index of the data object - in case it refers to more than one */ private int mDataIndex; - /** the index of the dataset the highlighted yValue is in */ + /** the index of the dataset the highlighted value is in */ private int mDataSetIndex; - /** index which yValue of a stacked bar entry is highlighted, default -1 */ + /** index which value of a stacked bar entry is highlighted, default -1 */ private int mStackIndex = -1; /** the range of the bar that is selected (only for stacked-barchart) */ @@ -29,10 +29,10 @@ public class Highlight { /** * constructor * - * @param x the x-yValue of the highlighted yValue - * @param y the y-yValue of the highlighted yValue - * @param dataIndex the index of the Data the highlighted yValue belongs to - * @param dataSetIndex the index of the DataSet the highlighted yValue belongs to + * @param x the x-value of the highlighted value + * @param y the y-value of the highlighted value + * @param dataIndex the index of the Data the highlighted value belongs to + * @param dataSetIndex the index of the DataSet the highlighted value belongs to */ public Highlight(float x, float y, int dataIndex, int dataSetIndex) { this.mX = x; @@ -43,11 +43,11 @@ public Highlight(float x, float y, int dataIndex, int dataSetIndex) { /** * Constructor, only used for stacked-barchart. * - * @param x the x-yValue of the highlighted yValue on the x-axis - * @param y the y-yValue of the highlighted yValue - * @param dataIndex the index of the Data the highlighted yValue belongs to - * @param dataSetIndex the index of the DataSet the highlighted yValue belongs to - * @param stackIndex references which yValue of a stacked-bar entry has been + * @param x the x-value of the highlighted value on the x-axis + * @param y the y-value of the highlighted value + * @param dataIndex the index of the Data the highlighted value belongs to + * @param dataSetIndex the index of the DataSet the highlighted value belongs to + * @param stackIndex references which value of a stacked-bar entry has been * selected */ public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackIndex) { @@ -58,13 +58,13 @@ public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackInd /** * Constructor, only used for stacked-barchart. * - * @param x the index of the highlighted yValue on the x-axis - * @param y the y-yValue of the highlighted yValue - * @param dataIndex the index of the Data the highlighted yValue belongs to - * @param dataSetIndex the index of the DataSet the highlighted yValue belongs to - * @param stackIndex references which yValue of a stacked-bar entry has been + * @param x the index of the highlighted value on the x-axis + * @param y the y-value of the highlighted value + * @param dataIndex the index of the Data the highlighted value belongs to + * @param dataSetIndex the index of the DataSet the highlighted value belongs to + * @param stackIndex references which value of a stacked-bar entry has been * selected - * @param range the range the selected stack-yValue is in + * @param range the range the selected stack-value is in */ public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackIndex, Range range) { this(x, y, dataIndex, dataSetIndex, stackIndex); @@ -75,14 +75,14 @@ public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackInd * Constructor, only used for stacked-barchart. * * @param x the x-value of the highlighted value on the x-axis - * @param dataSetIndex the index of the DataSet the highlighted yValue belongs to + * @param dataSetIndex the index of the DataSet the highlighted value belongs to */ public Highlight(float x, int dataSetIndex) { this(x, Float.NaN, 0, dataSetIndex, -1); } /** - * returns the x-yValue of the highlighted yValue + * returns the x-value of the highlighted value * * @return */ @@ -91,7 +91,7 @@ public float getX() { } /** - * returns the y-yValue of the highlighted yValue + * returns the y-value of the highlighted value * * @return */ @@ -109,7 +109,7 @@ public int getDataIndex() { } /** - * returns the index of the DataSet the highlighted yValue is in + * returns the index of the DataSet the highlighted value is in * * @return */ @@ -119,7 +119,7 @@ public int getDataSetIndex() { /** * Only needed if a stacked-barchart entry was highlighted. References the - * selected yValue within the stacked-entry. + * selected value within the stacked-entry. * * @return */ @@ -128,7 +128,7 @@ public int getStackIndex() { } /** - * Returns the range of values the selected yValue of a stacked bar is in. (this is only relevant for stacked-barchart) + * Returns the range of values the selected value of a stacked bar is in. (this is only relevant for stacked-barchart) * @return */ public Range getRange() { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java index 60391124ec..96dd592b8f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java @@ -1,7 +1,7 @@ package com.github.mikephil.charting.highlight; /** - * Created by Philipp Jahoda on 24/07/15. Class that represents the range of one yValue in a stacked bar entry. e.g. + * Created by Philipp Jahoda on 24/07/15. Class that represents the range of one value in a stacked bar entry. e.g. * stack values are -10, 5, 20 -> then ranges are (-10 - 0, 0 - 5, 5 - 25). */ public final class Range { @@ -15,7 +15,7 @@ public Range(float from, float to) { } /** - * Returns true if this range contains (if the yValue is in between) the given yValue, false if not. + * Returns true if this range contains (if the value is in between) the given value, false if not. * * @param value * @return 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 87e651b16f..fbdfd79531 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 @@ -24,7 +24,7 @@ public interface IBarDataSet extends IBarLineScatterCandleBubbleDataSet { /** ###### ###### DATA RELATED METHODS ###### ###### */ /** - * returns the minimum y-yValue this DataSet holds + * returns the minimum y-value this DataSet holds * * @return */ float getYMin(); /** - * returns the maximum y-yValue this DataSet holds + * returns the maximum y-value this DataSet holds * * @return */ float getYMax(); /** - * returns the minimum x-yValue this DataSet holds + * returns the minimum x-value this DataSet holds * * @return */ float getXMin(); /** - * returns the maximum x-yValue this DataSet holds + * returns the maximum x-value this DataSet holds * * @return */ @@ -124,8 +124,8 @@ public interface IDataSet { int getEntryIndex(T e); /** - * Returns the yValue of the Entry object at the given xVal. Returns - * Float.NaN if no yValue is at the given xVal. INFORMATION: This method + * Returns the value of the Entry object at the given xVal. Returns + * Float.NaN if no value is at the given xVal. INFORMATION: This method * does calculations at runtime. Do not over-use in performance critical * situations. * @@ -136,7 +136,7 @@ public interface IDataSet { /** * Returns all of the y values of the Entry objects at the given xPos. Returns - * Float.NaN if no yValue is at the given xPos. INFORMATION: This method + * Float.NaN if no value is at the given xPos. INFORMATION: This method * does calculations at runtime. Do not over-use in performance critical * situations. * @@ -160,7 +160,7 @@ public interface IDataSet { * Adds an Entry to the DataSet dynamically. * Entries are added to the end of the list. * This will also recalculate the current minimum and maximum - * values of the DataSet and the yValue-sum. + * values of the DataSet and the value-sum. * * @param e */ @@ -169,7 +169,7 @@ public interface IDataSet { /** * Removes an Entry from the DataSets entries array. This will also * recalculate the current minimum and maximum values of the DataSet and the - * yValue-sum. Returns true if an Entry was removed, false if no Entry could + * value-sum. Returns true if an Entry was removed, false if no Entry could * be removed. * * @param e @@ -180,7 +180,7 @@ public interface IDataSet { * Adds an Entry to the DataSet dynamically. * Entries are added to their appropriate index respective to it's x-index. * This will also recalculate the current minimum and maximum - * values of the DataSet and the yValue-sum. + * values of the DataSet and the value-sum. * * @param e */ @@ -289,7 +289,7 @@ public interface IDataSet { boolean isHighlightEnabled(); /** - * If set to true, yValue highlighting is enabled which means that values can + * If set to true, value highlighting is enabled which means that values can * be highlighted programmatically or by touch gesture. * * @param enabled @@ -315,7 +315,7 @@ public interface IDataSet { ValueFormatter getValueFormatter(); /** - * Sets the color the yValue-labels of this DataSet should have. + * Sets the color the value-labels of this DataSet should have. * * @param color */ @@ -329,14 +329,14 @@ public interface IDataSet { void setValueTextColors(List colors); /** - * Sets a Typeface for the yValue-labels of this DataSet. + * Sets a Typeface for the value-labels of this DataSet. * * @param tf */ void setValueTypeface(Typeface tf); /** - * Sets the text-size of the yValue-labels of this DataSet in dp. + * Sets the text-size of the value-labels of this DataSet in dp. * * @param size */ @@ -382,7 +382,7 @@ public interface IDataSet { void setDrawValues(boolean enabled); /** - * Returns true if y-yValue drawing is enabled, false if not + * Returns true if y-value drawing is enabled, false if not * * @return */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java index ebf7e4f565..ce89822716 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java @@ -24,7 +24,7 @@ public interface ILineRadarDataSet extends ILineScatterCandleRa Drawable getFillDrawable(); /** - * Returns the alpha yValue that is used for filling the line surface, + * Returns the alpha value that is used for filling the line surface, * default: 85 * * @return diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java index aae6050916..75c8e864b4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java @@ -83,7 +83,7 @@ public void endAction(MotionEvent me) { } /** - * Sets the last yValue that was highlighted via touch. + * Sets the last value that was highlighted via touch. * * @param high */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java index e0153dada6..4c6da2e8c9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java @@ -12,7 +12,7 @@ public interface OnChartValueSelectedListener { /** - * Called when a yValue has been selected inside the chart. + * Called when a value has been selected inside the chart. * * @param e The selected Entry. * @param dataSetIndex The index in the datasets array of the data object 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 f98c9b2547..e0d71b7a59 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 @@ -112,8 +112,8 @@ public Transformer getTransformer() { /** * Computes the axis values. * - * @param min - the minimum yValue in the data object for this axis - * @param max - the maximum yValue in the data object for this axis + * @param min - the minimum value in the data object for this axis + * @param max - the maximum value in the data object for this axis */ public void computeAxis(float min, float max, boolean inverted) { @@ -157,7 +157,7 @@ protected void computeAxisValues(float min, float max) { return; } - // Find out how much spacing (in y yValue space) between axis values + // Find out how much spacing (in y value space) between axis values double rawInterval = range / labelCount; double interval = Utils.roundToNextSignificant(rawInterval); @@ -226,7 +226,7 @@ protected void computeAxisValues(float min, float max) { for (f = first, i = 0; i < n; f += interval, ++i) { - if (f == 0.0) // Fix for negative zero case (Where yValue == -0.0, and 0.0 == -0.0) + if (f == 0.0) // Fix for negative zero case (Where value == -0.0, and 0.0 == -0.0) f = 0.0; mAxis.mEntries[i] = (float) f; 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 899ef7ce0b..ed1008f12c 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 @@ -135,7 +135,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) break; - // Set the color for the currently drawn yValue. If the index + // 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], @@ -207,7 +207,7 @@ public void drawValues(Canvas c) { boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); // calculate the correct offset depending on the draw position of - // the yValue + // the value float valueTextHeight = Utils.calcTextHeight(mValuePaint, "8"); posOffset = (drawValueAboveBar ? -valueOffsetPlus : valueTextHeight + valueOffsetPlus); negOffset = (drawValueAboveBar ? valueTextHeight + valueOffsetPlus : -valueOffsetPlus); 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 20a28ea9ae..f4a88fe08d 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 @@ -67,7 +67,7 @@ public DataRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { /** * Returns the Paint object this renderer uses for drawing the values - * (yValue-text). + * (value-text). * * @return */ @@ -95,7 +95,7 @@ public Paint getPaintRender() { } /** - * Applies the required styling (provided by the DataSet) to the yValue-paint + * Applies the required styling (provided by the DataSet) to the value-paint * object. * * @param set @@ -128,12 +128,12 @@ protected void applyValueTextStyle(IDataSet set) { public abstract void drawValues(Canvas c); /** - * Draws the yValue of the given entry by using the provided ValueFormatter. + * Draws the value of the given entry by using the provided ValueFormatter. * * @param c canvas - * @param formatter formatter for custom yValue-formatting - * @param value the yValue to be drawn - * @param entry the entry the yValue belongs to + * @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 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 e4cb3150d2..dda51b0a48 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 @@ -85,7 +85,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { buffer.buffer[j + 3], mShadowPaint); } - // Set the color for the currently drawn yValue. If the index + // 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], @@ -148,7 +148,7 @@ public void drawValues(Canvas c) { float val = e.getY(); String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); - // calculate the correct offset depending on the draw position of the yValue + // 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); @@ -162,7 +162,7 @@ public void drawValues(Canvas c) { y + halfTextHeight, dataSet.getValueTextColor(j / 2)); } - // if each yValue of a potential stack should be drawn + // if each value of a potential stack should be drawn } else { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); @@ -194,7 +194,7 @@ public void drawValues(Canvas c) { float val = e.getY(); String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); - // calculate the correct offset depending on the draw position of the yValue + // 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); @@ -238,7 +238,7 @@ public void drawValues(Canvas c) { float val = vals[k / 2]; String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); - // calculate the correct offset depending on the draw position of the yValue + // 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); 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 d7070cb7d5..7135f569a3 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 @@ -29,7 +29,7 @@ public class LineChartRenderer extends LineRadarRenderer { protected LineDataProvider mChart; /** - * paint for the inner circle of the yValue indicators + * paint for the inner circle of the value indicators */ protected Paint mCirclePaintInner; 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 dfe3a96f66..4c8fa78747 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 void drawDataSet(Canvas c, IPieDataSet dataSet) { int visibleAngleCount = 0; for (int j = 0; j < entryCount; j++) { - // draw only if the yValue is greater than zero + // draw only if the value is greater than zero if ((Math.abs(dataSet.getEntryForIndex(j).getY()) > 0.000001)) { visibleAngleCount++; } @@ -214,7 +214,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { Entry e = dataSet.getEntryForIndex(j); - // draw only if the yValue is greater than zero + // draw only if the value is greater than zero if ((Math.abs(e.getY()) > 0.000001)) { if (!mChart.needsHighlight((int) e.getX(), @@ -712,7 +712,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { final int entryCount = set.getEntryCount(); int visibleAngleCount = 0; for (int j = 0; j < entryCount; j++) { - // draw only if the yValue is greater than zero + // draw only if the value is greater than zero if ((Math.abs(set.getEntryForIndex(j).getY()) > 0.000001)) { visibleAngleCount++; } @@ -897,7 +897,7 @@ protected void drawRoundedSlices(Canvas c) { Entry e = dataSet.getEntryForIndex(j); - // draw only if the yValue is greater than zero + // draw only if the value is greater than zero if ((Math.abs(e.getY()) > 0.000001)) { float x = (float) ((r - circleRadius) 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 7519d50693..ebf8435696 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 @@ -83,7 +83,7 @@ protected void drawDataSet(Canvas c, IRadarDataSet dataSet, int mostEntries) { float sliceangle = mChart.getSliceAngle(); - // calculate the factor that is needed for transforming the yValue to + // calculate the factor that is needed for transforming the value to // pixels float factor = mChart.getFactor(); @@ -157,7 +157,7 @@ public void drawValues(Canvas c) { float sliceangle = mChart.getSliceAngle(); - // calculate the factor that is needed for transforming the yValue to + // calculate the factor that is needed for transforming the value to // pixels float factor = mChart.getFactor(); @@ -198,7 +198,7 @@ protected void drawWeb(Canvas c) { float sliceangle = mChart.getSliceAngle(); - // calculate the factor that is needed for transforming the yValue to + // calculate the factor that is needed for transforming the value to // pixels float factor = mChart.getFactor(); float rotationangle = mChart.getRotationAngle(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java index dcaaeee2a8..337bb54061 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java @@ -21,7 +21,7 @@ public Renderer(ViewPortHandler viewPortHandler) { } /** - * Returns true if the specified yValue fits in between the provided min + * Returns true if the specified value fits in between the provided min * and max bounds, false if not. * * @param val 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 8afc282b01..dd17aec6c4 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 @@ -309,7 +309,7 @@ public void renderLimitLineLine(Canvas c, LimitLine limitLine, float[] position) public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position, float yOffset) { String label = limitLine.getLabel(); - // if drawing the limit-yValue label is enabled + // if drawing the limit-value label is enabled if (label != null && !label.equals("")) { mLimitLinePaint.setStyle(limitLine.getTextStyle()); 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 ba398fc6d7..10332ab99a 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 @@ -249,7 +249,7 @@ public void renderLimitLines(Canvas c) { String label = l.getLabel(); - // if drawing the limit-yValue label is enabled + // if drawing the limit-value label is enabled if (label != null && !label.equals("")) { mLimitLinePaint.setStyle(l.getTextStyle()); 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 ed30084f7f..6ccfb09a2a 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 @@ -34,7 +34,7 @@ public void renderAxisLabels(Canvas c) { float sliceangle = mChart.getSliceAngle(); - // calculate the factor that is needed for transforming the yValue to + // calculate the factor that is needed for transforming the value to // pixels float factor = mChart.getFactor(); 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 03ff4af9f8..27d56b63a1 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 @@ -55,7 +55,7 @@ public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer t // return; // } // -// // Find out how much spacing (in y yValue space) between axis values +// // Find out how much spacing (in y value space) between axis values // double rawInterval = range / labelCount; // double interval = Utils.roundToNextSignificant(rawInterval); // @@ -125,7 +125,7 @@ public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer t // // for (f = first, i = 0; i < n; f += interval, ++i) { // -// if (f == 0.0) // Fix for negative zero case (Where yValue == -0.0, and 0.0 == -0.0) +// if (f == 0.0) // Fix for negative zero case (Where value == -0.0, and 0.0 == -0.0) // f = 0.0; // // mYAxis.mEntries[i] = (float) f; @@ -352,7 +352,7 @@ public void renderLimitLines(Canvas c) { String label = l.getLabel(); - // if drawing the limit-yValue label is enabled + // if drawing the limit-value label is enabled if (label != null && !label.equals("")) { mLimitLinePaint.setStyle(l.getTextStyle()); 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 2841839510..1bb4598831 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 @@ -30,8 +30,8 @@ public YAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, YAxis yA /** * Computes the axis values. * - * @param yMin - the minimum y-yValue in the data object for this axis - * @param yMax - the maximum y-yValue in the data object for this axis + * @param yMin - the minimum y-value in the data object for this axis + * @param yMax - the maximum y-value in the data object for this axis */ @Override public void computeAxis(float yMin, float yMax, boolean inverted) { @@ -231,7 +231,7 @@ public void renderLimitLines(Canvas c) { String label = l.getLabel(); - // if drawing the limit-yValue label is enabled + // if drawing the limit-value label is enabled if (label != null && !label.equals("")) { mLimitLinePaint.setStyle(l.getTextStyle()); 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 db8dec3e73..05ac1ab2dd 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 @@ -37,7 +37,7 @@ protected void computeAxisValues(float min, float max) { return; } - // Find out how much spacing (in y yValue space) between axis values + // Find out how much spacing (in y value space) between axis values double rawInterval = range / labelCount; double interval = Utils.roundToNextSignificant(rawInterval); @@ -108,7 +108,7 @@ protected void computeAxisValues(float min, float max) { for (f = first, i = 0; i < n; f += interval, ++i) { - if (f == 0.0) // Fix for negative zero case (Where yValue == -0.0, and 0.0 == -0.0) + if (f == 0.0) // Fix for negative zero case (Where value == -0.0, and 0.0 == -0.0) f = 0.0; mAxis.mEntries[i] = (float) f; @@ -180,7 +180,7 @@ public void renderLimitLines(Canvas c) { float sliceangle = mChart.getSliceAngle(); - // calculate the factor that is needed for transforming the yValue to + // calculate the factor that is needed for transforming the value to // pixels float factor = mChart.getFactor(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java index 0934883b02..0adeb4b787 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java @@ -3,9 +3,9 @@ import com.github.mikephil.charting.interfaces.datasets.IDataSet; /** - * Class that encapsulates information of a yValue that has been + * Class that encapsulates information of a value that has been * selected/highlighted and its DataSet index. The SelectionDetail objects give - * information about the yValue at the selected index and the DataSet it belongs + * information about the value at the selected index and the DataSet it belongs * to. Needed only for highlighting onTouch(). * * @author Philipp Jahoda 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 337049f058..6203c2a27d 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 @@ -246,7 +246,7 @@ public float[] generateTransformedValuesHorizontalBarChart(IBarDataSet data, /** * transform a path with all the given matrices VERY IMPORTANT: keep order - * to yValue-touch-offset + * to value-touch-offset * * @param path */ @@ -271,7 +271,7 @@ public void pathValuesToPixel(List paths) { /** * Transform an array of points with all matrices. VERY IMPORTANT: Keep - * matrix order "yValue-touch-offset" when transforming. + * matrix order "value-touch-offset" when transforming. * * @param pts */ @@ -374,7 +374,7 @@ public void pixelsToValue(float[] pixels) { Matrix tmp = new Matrix(); - // invert all matrixes to convert back to the original yValue + // invert all matrixes to convert back to the original value mMatrixOffset.invert(tmp); tmp.mapPoints(pixels); @@ -414,7 +414,7 @@ public PointD getValuesByTouchPoint(float x, float y) { } /** - * Returns the x and y coordinates (pixels) for a given x and y yValue in the chart. + * Returns the x and y coordinates (pixels) for a given x and y value in the chart. * * @param x * @param y 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 e705bd6592..4e5fb6ac00 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 @@ -89,9 +89,9 @@ public static void init(Resources res) { * This method converts dp unit to equivalent pixels, depending on device * density. NEEDS UTILS TO BE INITIALIZED BEFORE USAGE. * - * @param dp A yValue in dp (density independent pixels) unit. Which we need + * @param dp A value in dp (density independent pixels) unit. Which we need * to convert into pixels - * @return A float yValue to represent px equivalent to dp depending on + * @return A float value to represent px equivalent to dp depending on * device density */ public static float convertDpToPixel(float dp) { @@ -117,8 +117,8 @@ public static float convertDpToPixel(float dp) { * This method converts device specific pixels to density independent * pixels. NEEDS UTILS TO BE INITIALIZED BEFORE USAGE. * - * @param px A yValue in px (pixels) unit. Which we need to convert into db - * @return A float yValue to represent dp equivalent to px yValue + * @param px A value in px (pixels) unit. Which we need to convert into db + * @return A float value to represent dp equivalent to px value */ public static float convertPixelsToDp(float px) { @@ -382,7 +382,7 @@ public static double nextUp(double d) { } /** - * Returns the index of the DataSet that contains the closest yValue on the + * Returns the index of the DataSet that contains the closest value on the * y-axis. This is needed for highlighting. This will return -Integer.MAX_VALUE if failure. * * @param valsAtIndex all the values at a specific index @@ -400,7 +400,7 @@ public static int getClosestDataSetIndexByValue(List valsAtInde } /** - * Returns the SelectionDetail of the DataSet that contains the closest yValue on the + * Returns the SelectionDetail of the DataSet that contains the closest value on the * y-axis. * * @param valsAtIndex all the values at a specific index @@ -721,7 +721,7 @@ public static int getSDKInt() { */ public static double granularity(float range, int labelCount) { - // Find out how much spacing (in y yValue space) between axis values + // Find out how much spacing (in y value space) between axis values double rawInterval = range / labelCount; double interval = Utils.roundToNextSignificant(rawInterval); 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 078af45b5b..b2aff63e8b 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 @@ -28,22 +28,22 @@ public class ViewPortHandler { protected float mChartHeight = 0f; /** - * minimum scale yValue on the y-axis + * minimum scale value on the y-axis */ private float mMinScaleY = 1f; /** - * maximum scale yValue on the y-axis + * maximum scale value on the y-axis */ private float mMaxScaleY = Float.MAX_VALUE; /** - * minimum scale yValue on the x-axis + * minimum scale value on the x-axis */ private float mMinScaleX = 1f; /** - * maximum scale yValue on the x-axis + * maximum scale value on the x-axis */ private float mMaxScaleX = Float.MAX_VALUE; @@ -329,7 +329,7 @@ public Matrix translate(final float[] transformedPts) { } /** - * Centers the viewport around the specified position (x-index and y-yValue) + * Centers the viewport around the specified position (x-index and y-value) * in the chart. Centering the viewport outside the bounds of the chart is * not possible. Makes most sense in combination with the * setScaleMinima(...) method. From 01f8a57550ce7e57f019fc8e4ad6a011fd97ebd9 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 4 Jun 2016 23:07:19 +0200 Subject: [PATCH 181/606] Work on zooming methods --- .../com/xxmassdeveloper/mpchartexample/LineChartActivity1.java | 2 +- .../com/github/mikephil/charting/charts/BarLineChartBase.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 1e34863659..30b9391415 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -149,7 +149,7 @@ protected void onCreate(Bundle savedInstanceState) { // mChart.centerViewTo(20, 50, AxisDependency.LEFT); mChart.animateX(2500, Easing.EasingOption.EaseInOutQuart); -// mChart.invalidate(); + //mChart.invalidate(); // get the legend (only possible after setting data) Legend l = mChart.getLegend(); 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 5a1a860b56..ca286a6fe6 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 @@ -663,7 +663,7 @@ public void zoom(float scaleX, float scaleY, float x, float y) { * @param yValue * @param axis the axis relative to which the zoom should take place */ - public void zoom(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis) { + public void zoomAndCenter(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis) { Runnable job = new ZoomJob(mViewPortHandler, scaleX, scaleY, xValue, yValue, getTransformer(axis), axis, this); addViewportJob(job); From 073b4778114403ee8a1f8560b501b2eea7475474 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 4 Jun 2016 23:15:56 +0200 Subject: [PATCH 182/606] Fixes related to markerview drawing in animation --- .../mikephil/charting/charts/BarChart.java | 37 ------------------- .../charting/charts/BarLineChartBase.java | 2 +- .../mikephil/charting/charts/Chart.java | 12 ++---- 3 files changed, 5 insertions(+), 46 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 d48b04d417..a76bd6ebce 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,8 +57,6 @@ protected void init() { mRenderer = new BarChartRenderer(this, mAnimator, mViewPortHandler); setHighlighter(new BarHighlighter(this)); - - //mXAxis.mAxisMinimum = -0.5f; } @Override @@ -200,39 +198,4 @@ public BarData getBarData() { public void setFitBars(boolean enabled) { mFitBars = enabled; } - - -// /** -// * Returns the lowest x-index (value on the x-axis) that is still visible on the chart. -// * -// * @return -// */ -// @Override -// public float getLowestVisibleX() { -// -// float step = mData.getDataSetCount(); -// float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); -// -// float[] pts = new float[] { mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom() }; -// -// getTransformer(AxisDependency.LEFT).pixelsToValue(pts); -// return ((pts[0] <= getXChartMin()) ? 0 : (pts[0] / div) + 1); -// } -// -// /** -// * Returns the highest x-index (value on the x-axis) that is still visible on the chart. -// * -// * @return -// */ -// @Override -// public float getHighestVisibleX() { -// -// float step = mData.getDataSetCount(); -// float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); -// -// float[] pts = new float[] { mViewPortHandler.contentRight(), mViewPortHandler.contentBottom() }; -// -// getTransformer(AxisDependency.LEFT).pixelsToValue(pts); -// return ((pts[0] >= getXChartMax()) ? getXChartMax() / div : (pts[0] / div)); -// } } 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 ca286a6fe6..b75e6567b6 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 @@ -527,7 +527,7 @@ protected float[] getMarkerPosition(Entry e, Highlight highlight) { int dataSetIndex = highlight.getDataSetIndex(); float xPos = e.getX(); - float yPos = e.getY(); + float yPos = e.getY() * mAnimator.getPhaseY(); // position of the marker depends on selected value index and value float[] pts = new float[]{ 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 48eca130b7..359d2228c8 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 @@ -700,10 +700,13 @@ protected void drawMarkers(Canvas canvas) { Highlight highlight = mIndicesToHighlight[i]; + IDataSet set = mData.getDataSetByIndex(highlight.getDataSetIndex()); + Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]); + int entryIndex = set.getEntryIndex(e); // make sure entry not null - if (e == null || e.getX() != mIndicesToHighlight[i].getX()) + if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX()) continue; float[] pos = getMarkerPosition(e, highlight); @@ -715,13 +718,6 @@ protected void drawMarkers(Canvas canvas) { // callbacks to update the content mMarkerView.refreshContent(e, highlight); - // mMarkerView.measure(MeasureSpec.makeMeasureSpec(0, - // MeasureSpec.UNSPECIFIED), - // MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - // mMarkerView.layout(0, 0, mMarkerView.getMeasuredWidth(), - // mMarkerView.getMeasuredHeight()); - // mMarkerView.draw(mDrawCanvas, pos[0], pos[1]); - mMarkerView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); mMarkerView.layout(0, 0, mMarkerView.getMeasuredWidth(), From 33fbf34a7c7d6893510a9020a29f7a793c0fe3d6 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 4 Jun 2016 23:32:39 +0200 Subject: [PATCH 183/606] Cleanup --- .../mpchartexample/RealtimeLineChartActivity.java | 10 +++++----- .../mikephil/charting/charts/BarLineChartBase.java | 4 ---- .../mikephil/charting/highlight/ChartHighlighter.java | 2 -- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 62e82dff28..2ec8b19e45 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -146,7 +146,7 @@ private void addEntry() { // mChart.setVisibleYRange(30, AxisDependency.LEFT); // move to the latest entry - mChart.moveViewToX(data.getEntryCount() - 121); + mChart.moveViewToX(data.getEntryCount()); // this automatically refreshes the chart (calls invalidate()) // mChart.moveViewTo(data.getXValCount()-7, 55f, @@ -175,14 +175,14 @@ private LineDataSet createSet() { private void feedMultiple() { - if(thread != null) + if (thread != null) thread.interrupt(); - thread = new Thread(new Runnable() { + thread = new Thread(new Runnable() { @Override public void run() { - for(int i = 0; i < 1000; i++) { + for (int i = 0; i < 1000; i++) { runOnUiThread(new Runnable() { @@ -219,7 +219,7 @@ public void onNothingSelected() { protected void onPause() { super.onPause(); - if(thread != null) { + if (thread != null) { thread.interrupt(); } } 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 b75e6567b6..cc4a14bb9f 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 @@ -357,10 +357,6 @@ protected void calcMinMax() { if (mAutoScaleMinMaxEnabled) mData.calcMinMax(); -// // calculate / set x-axis range -// mXAxis.mAxisMaximum = mData.getXVals().size() - 1; -// mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); - 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/highlight/ChartHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java index a12d5c3fdc..7ba76d6a77 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 @@ -168,8 +168,6 @@ public SelectionDetail getClosestSelectionDetailByPixel( SelectionDetail closest = null; float distance = minSelectionDistance; - System.out.println(distance); - for (int i = 0; i < valsAtIndex.size(); i++) { SelectionDetail sel = valsAtIndex.get(i); From 92504fa81de9c3cf935fa3d37babc5f0721734d0 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 4 Jun 2016 23:35:16 +0200 Subject: [PATCH 184/606] Update gradle files --- MPChartExample/AndroidManifest.xml | 4 ++-- MPChartExample/build.gradle | 4 ++-- MPChartLib/build.gradle | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index 98bfc41b70..c0e6931a4c 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="52" + android:versionName="3.0.0" > Date: Mon, 6 Jun 2016 16:44:56 +0200 Subject: [PATCH 185/606] Improve & test LargeValueFormatter --- .../BarChartActivityMultiDataset.java | 1 + .../formatter/LargeValueFormatter.java | 14 +-- .../charting/utils/ViewPortHandler.java | 1 - .../charting/test/AxisRendererTest.java | 6 -- .../mikephil/charting/test/DataSetTest.java | 3 +- .../test/LargeValueFormatterTest.java | 95 +++++++++++++++++++ 6 files changed, 105 insertions(+), 15 deletions(-) create mode 100644 MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 93150589b7..69a646fa9c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -247,6 +247,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mChart.getBarData().groupBars(startYear, groupSpace, barSpace); mChart.getXAxis().setAxisMinValue(startYear); mChart.getXAxis().setAxisMaxValue(mChart.getBarData().getIntervalWidth(groupSpace, barSpace) * mSeekBarX.getProgress() + startYear); + mChart.notifyDataSetChanged(); mChart.invalidate(); } 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 0f852b6f74..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 @@ -22,12 +22,12 @@ public class LargeValueFormatter implements ValueFormatter, AxisValueFormatter { private static String[] SUFFIX = new String[]{ "", "k", "m", "b", "t" }; - private static final int MAX_LENGTH = 4; + private static final int MAX_LENGTH = 5; private DecimalFormat mFormat; private String mText = ""; public LargeValueFormatter() { - mFormat = new DecimalFormat("###E0"); + mFormat = new DecimalFormat("###E00"); } /** @@ -68,9 +68,7 @@ public void setAppendix(String appendix) { * @param suff new suffix */ public void setSuffix(String[] suff) { - if (suff.length == 5) { - SUFFIX = suff; - } + SUFFIX = suff; } /** @@ -81,7 +79,11 @@ private String makePretty(double number) { String r = mFormat.format(number); - r = r.replaceAll("E[0-9]", SUFFIX[Character.getNumericValue(r.charAt(r.length() - 1)) / 3]); + int numericValue1 = Character.getNumericValue(r.charAt(r.length() - 1)); + 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]); while (r.length() > MAX_LENGTH || r.matches("[0-9]+\\.[a-z]")) { r = r.substring(0, r.length() - 2) + r.substring(r.length() - 1); 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 b2aff63e8b..47ff9aa02e 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 @@ -102,7 +102,6 @@ public void setChartDimens(float width, float height) { mChartWidth = width; restrainViewPort(offsetLeft, offsetTop, offsetRight, offsetBottom); - } public boolean hasChartDimens() { diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java index b50bd4f9a5..05cb2f3592 100644 --- a/MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java @@ -1,17 +1,11 @@ package com.github.mikephil.charting.test; 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.data.ScatterDataSet; import com.github.mikephil.charting.renderer.AxisRenderer; import com.github.mikephil.charting.renderer.YAxisRenderer; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; - import static junit.framework.Assert.assertEquals; /** 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 5e86a89e66..51922f5835 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 @@ -3,14 +3,13 @@ import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.ScatterDataSet; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; import org.junit.Test; import java.util.ArrayList; import java.util.List; -import static junit.framework.Assert.*; +import static junit.framework.Assert.assertEquals; /** * Created by philipp on 31/05/16. 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 new file mode 100644 index 0000000000..f1e1e0279e --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java @@ -0,0 +1,95 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.formatter.LargeValueFormatter; + +import org.junit.Test; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by philipp on 06/06/16. + */ +public class LargeValueFormatterTest { + + @Test + public void test() { + + LargeValueFormatter formatter = new LargeValueFormatter(); + + String result = formatter.getFormattedValue(5f, null); + assertEquals("5", result); + + result = formatter.getFormattedValue(5.5f, null); + assertEquals("5.5", result); + + result = formatter.getFormattedValue(50f, null); + assertEquals("50", result); + + result = formatter.getFormattedValue(50.5f, null); + assertEquals("50.5", result); + + result = formatter.getFormattedValue(500f, null); + assertEquals("500", result); + + result = formatter.getFormattedValue(1100f, null); + assertEquals("1.1k", result); + + result = formatter.getFormattedValue(10000f, null); + assertEquals("10k", result); + + result = formatter.getFormattedValue(10500f, null); + assertEquals("10.5k", result); + + result = formatter.getFormattedValue(100000f, null); + assertEquals("100k", result); + + result = formatter.getFormattedValue(1000000f, null); + assertEquals("1m", result); + + result = formatter.getFormattedValue(1500000f, null); + assertEquals("1.5m", result); + + result = formatter.getFormattedValue(9500000f, null); + assertEquals("9.5m", result); + + result = formatter.getFormattedValue(22200000f, null); + assertEquals("22.2m", result); + + result = formatter.getFormattedValue(222000000f, null); + assertEquals("222m", result); + + result = formatter.getFormattedValue(1000000000f, null); + assertEquals("1b", result); + + result = formatter.getFormattedValue(9900000000f, null); + assertEquals("9.9b", result); + + result = formatter.getFormattedValue(99000000000f, null); + assertEquals("99b", result); + + result = formatter.getFormattedValue(99500000000f, null); + assertEquals("99.5b", result); + + result = formatter.getFormattedValue(999000000000f, null); + assertEquals("999b", result); + + result = formatter.getFormattedValue(1000000000000f, null); + assertEquals("1t", result); + + formatter.setSuffix(new String[]{"", "k", "m", "b", "t", "q"}); // quadrillion support + result = formatter.getFormattedValue(1000000000000000f, null); + assertEquals("1q", result); + + result = formatter.getFormattedValue(1100000000000000f, null); + assertEquals("1.1q", result); + + result = formatter.getFormattedValue(10000000000000000f, null); + assertEquals("10q", result); + + result = formatter.getFormattedValue(13300000000000000f, null); + assertEquals("13.3q", result); + + result = formatter.getFormattedValue(100000000000000000f, null); + assertEquals("100q", result); + } +} From 40917f0ddaa8b7da142efc384ae7fb83c392c50f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 6 Jun 2016 17:13:10 +0200 Subject: [PATCH 186/606] Refactoring --- .../mpchartexample/AnotherBarActivity.java | 2 +- .../mpchartexample/BarChartActivity.java | 4 +-- .../BarChartActivityMultiDataset.java | 26 ++++++++++++++----- .../mpchartexample/BarChartActivitySinus.java | 2 +- .../BarChartPositiveNegative.java | 2 +- .../CubicLineChartActivity.java | 2 +- .../HorizontalBarChartActivity.java | 2 +- .../mpchartexample/LineChartActivity1.java | 2 +- .../mpchartexample/LineChartActivity2.java | 4 +-- .../mpchartexample/StackedBarActivity.java | 2 +- .../mikephil/charting/data/DataSet.java | 22 ++++++++-------- 11 files changed, 41 insertions(+), 29 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 159aafce7b..4b12f8c98c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -175,7 +175,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); - set1.setYVals(yVals1); + set1.setValues(yVals1); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 37232cdbf1..448b0f8ba5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -16,7 +16,6 @@ import android.widget.Toast; 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.LegendForm; import com.github.mikephil.charting.components.Legend.LegendPosition; @@ -35,7 +34,6 @@ 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.ViewPortHandler; import com.xxmassdeveloper.mpchartexample.custom.DayAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -244,7 +242,7 @@ private void setData(int count, float range) { if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set1 = (BarDataSet) mChart.getData().getDataSetByIndex(0); - set1.setYVals(yVals1); + set1.setValues(yVals1); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 69a646fa9c..9b8eb43973 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -13,6 +13,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.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; @@ -21,6 +22,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.LargeValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; @@ -95,6 +97,17 @@ protected void onCreate(Bundle savedInstanceState) { xl.setTypeface(tf); xl.setGranularity(1f); xl.setCenterAxisLabels(true); + xl.setValueFormatter(new AxisValueFormatter() { + @Override + public String getFormattedValue(float value, AxisBase axis) { + return String.valueOf((int) value); + } + + @Override + public int getDecimalDigits() { + return 0; + } + }); YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(tf); @@ -177,8 +190,9 @@ public boolean onOptionsItemSelected(MenuItem item) { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { float groupSpace = 0.04f; - float barSpace = 0.02f; - float barWidth = 0.3f; + float barSpace = 0.02f; // x3 + float barWidth = 0.3f; // x3 + // (0.3 + 0.02) * 3 + 0.04 = 1.00 int startYear = 1980; int endYear = startYear + mSeekBarX.getProgress(); @@ -213,9 +227,9 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); set2 = (BarDataSet)mChart.getData().getDataSetByIndex(1); set3 = (BarDataSet)mChart.getData().getDataSetByIndex(2); - set1.setYVals(yVals1); - set2.setYVals(yVals2); - set3.setYVals(yVals3); + set1.setValues(yVals1); + set2.setValues(yVals2); + set3.setValues(yVals3); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { @@ -235,7 +249,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { dataSets.add(set3); BarData data = new BarData(dataSets); -// data.setValueFormatter(new LargeValueFormatter()); + data.setValueFormatter(new LargeValueFormatter()); // add space between the dataset groups in percent of bar-width data.setValueTypeface(tf); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index ae0a20ddb5..31937a7a31 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -212,7 +212,7 @@ private void setData(int count) { if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set = (BarDataSet) mChart.getData().getDataSetByIndex(0); - set.setYVals(entries); + set.setValues(entries); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 932e049674..1c22ae41c6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -127,7 +127,7 @@ private void setData(List dataList) { if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set = (BarDataSet)mChart.getData().getDataSetByIndex(0); - set.setYVals(values); + set.setValues(values); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index d2b1e923e0..10e874ac48 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -279,7 +279,7 @@ private void setData(int count, float range) { if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); - set1.setYVals(yVals); + set1.setValues(yVals); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 676da05e7f..301501f77f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -236,7 +236,7 @@ private void setData(int count, float range) { if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0); - set1.setYVals(yVals1); + set1.setValues(yVals1); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index 30b9391415..ac0a864266 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -353,7 +353,7 @@ private void setData(int count, float range) { if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); - set1.setYVals(yVals); + set1.setValues(yVals); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 68ede3e0e7..e114db8521 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -310,8 +310,8 @@ private void setData(int count, float range) { mChart.getData().getDataSetCount() > 0) { set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); set2 = (LineDataSet)mChart.getData().getDataSetByIndex(1); - set1.setYVals(yVals1); - set2.setYVals(yVals2); + set1.setValues(yVals1); + set2.setValues(yVals2); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 8e86eaf158..24038acbe9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -192,7 +192,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set1 = (BarDataSet) mChart.getData().getDataSetByIndex(0); - set1.setYVals(yVals1); + set1.setValues(yVals1); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { 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 f260339ca7..68128e7c39 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 @@ -15,7 +15,7 @@ public abstract class DataSet extends BaseDataSet { /** - * the entries that this dataset represents / holds together + * the entries that this DataSet represents / holds together */ protected List mValues = null; @@ -41,16 +41,16 @@ public abstract class DataSet extends BaseDataSet { /** - * Creates a new DataSet object with the given values it represents. Also, a + * Creates a new DataSet object with the given values (entries) it represents. Also, a * label that describes the DataSet can be specified. The label can also be * used to retrieve the DataSet from a ChartData object. * - * @param yVals + * @param values * @param label */ - public DataSet(List yVals, String label) { + public DataSet(List values, String label) { super(label); - this.mValues = yVals; + this.mValues = values; if (mValues == null) mValues = new ArrayList(); @@ -108,21 +108,21 @@ public int getEntryCount() { } /** - * Returns the array of y-values that this DataSet represents. + * Returns the array of entries that this DataSet represents. * * @return */ - public List getYVals() { + public List getValues() { return mValues; } /** - * Sets the array of y-values that this DataSet represents, and calls notifyDataSetChanged() + * Sets the array of entries that this DataSet represents, and calls notifyDataSetChanged() * * @return */ - public void setYVals(List yVals) { - mValues = yVals; + public void setValues(List values) { + mValues = values; notifyDataSetChanged(); } @@ -221,7 +221,7 @@ public boolean addEntry(T e) { float val = e.getY(); - List yVals = getYVals(); + List yVals = getValues(); if (yVals == null) { yVals = new ArrayList(); } From 68947023d47b5135e9ae580a122315c48b08e1da Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 6 Jun 2016 17:23:26 +0200 Subject: [PATCH 187/606] Refactoring and improvements related to grouped bars --- .../BarChartActivityMultiDataset.java | 12 ++-- .../mikephil/charting/buffer/BarBuffer.java | 4 +- .../charting/buffer/HorizontalBarBuffer.java | 4 +- .../mikephil/charting/charts/BarChart.java | 19 ++++++ .../mikephil/charting/data/BarData.java | 66 ++++--------------- .../charting/renderer/BarChartRenderer.java | 1 - .../renderer/HorizontalBarChartRenderer.java | 1 - .../mikephil/charting/utils/Transformer.java | 35 ---------- 8 files changed, 41 insertions(+), 101 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index 9b8eb43973..e49e2ecadd 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -190,9 +190,10 @@ public boolean onOptionsItemSelected(MenuItem item) { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { float groupSpace = 0.04f; - float barSpace = 0.02f; // x3 - float barWidth = 0.3f; // x3 - // (0.3 + 0.02) * 3 + 0.04 = 1.00 + float barSpace = 0.02f; // x3 dataset + float barWidth = 0.3f; // x3 dataset + // (0.3 + 0.02) * 3 + 0.04 = 1.00 -> interval per "group" + int startYear = 1980; int endYear = startYear + mSeekBarX.getProgress(); @@ -258,10 +259,9 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } mChart.getBarData().setBarWidth(barWidth); - mChart.getBarData().groupBars(startYear, groupSpace, barSpace); mChart.getXAxis().setAxisMinValue(startYear); - mChart.getXAxis().setAxisMaxValue(mChart.getBarData().getIntervalWidth(groupSpace, barSpace) * mSeekBarX.getProgress() + startYear); - mChart.notifyDataSetChanged(); + mChart.getXAxis().setAxisMaxValue(mChart.getBarData().getGroupWidth(groupSpace, barSpace) * mSeekBarX.getProgress() + startYear); + mChart.groupBars(startYear, groupSpace, barSpace); mChart.invalidate(); } 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 fdcae06af7..1a6b85bcb3 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 @@ -6,7 +6,6 @@ public class BarBuffer extends AbstractBuffer { - protected float mGroupSpace = 0f; protected int mDataSetIndex = 0; protected int mDataSetCount = 1; protected boolean mContainsStacks = false; @@ -15,9 +14,8 @@ public class BarBuffer extends AbstractBuffer { /** width of the bar on the x-axis, in values (not pixels) */ protected float mBarWidth = 1f; - public BarBuffer(int size, float groupspace, int dataSetCount, boolean containsStacks) { + public BarBuffer(int size, int dataSetCount, boolean containsStacks) { super(size); - this.mGroupSpace = groupspace; this.mDataSetCount = dataSetCount; this.mContainsStacks = containsStacks; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java index c97c909508..6b63bc30ea 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java @@ -6,8 +6,8 @@ public class HorizontalBarBuffer extends BarBuffer { - public HorizontalBarBuffer(int size, float groupspace, int dataSetCount, boolean containsStacks) { - super(size, groupspace, dataSetCount, containsStacks); + public HorizontalBarBuffer(int size, int dataSetCount, boolean containsStacks) { + super(size, dataSetCount, containsStacks); } @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 a76bd6ebce..c5bd2c8fdc 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 @@ -198,4 +198,23 @@ public BarData getBarData() { public void setFitBars(boolean enabled) { mFitBars = enabled; } + + /** + * Groups all BarDataSet objects this data object holds together by modifying the x-position of their entries. + * Leaves space as specified by the parameters. + * Calls notifyDataSetChanged() afterwards. + * + * @param fromX the starting point on the x-axis where the grouping should begin + * @param groupSpace the space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f + * @param barSpace the space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f + */ + public void groupBars(float fromX, float groupSpace, float barSpace) { + + if (getBarData() == null) { + throw new RuntimeException("You need to set data for the chart before grouping bars."); + } else { + getBarData().groupBars(fromX, groupSpace, barSpace); + notifyDataSetChanged(); + } + } } 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 0bc79d56e9..92109c7a2c 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 @@ -3,8 +3,6 @@ import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** @@ -19,11 +17,6 @@ public class BarData extends BarLineScatterCandleBubbleData { */ private float mBarWidth = 1f; - // /** - // * The maximum space (in pixels on the screen) a single bar can consume. - // */ - // private float mMaximumBarWidth = 100f; - public BarData() { super(); } @@ -36,17 +29,6 @@ public BarData(List dataSets) { super(dataSets); } - /** - * Returns the space that is left out between groups of bars. Always returns - * 0 if the BarData object only contains one DataSet (because for one - * DataSet, there is no group-space needed). - * - * @return - */ - public float getGroupSpace() { - return 0f; - } - /** * Sets the width each bar should have on the x-axis (in values, not pixels). * Default 1f @@ -61,20 +43,12 @@ public float getBarWidth() { return mBarWidth; } - // /** -// * Returns true if this BarData object contains grouped DataSets (more than -// * 1 DataSet). -// * -// * @return -// */ -// public boolean isGrouped() { -// return mDataSets.size() > 1 ? true : false; -// } - /** - * Groups all BarDataSet objects this data object holds together. Leaves space as specified by the parameters. + * Groups all BarDataSet objects this data object holds together by modifying the x-position of their entries. + * Leaves space as specified by the parameters. + * Do not forget to call notifyDataSetChanged() on your BarChart object after calling this method. * - * @param fromX + * @param fromX the starting point on the x-axis where the grouping should begin * @param groupSpace the space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f * @param barSpace the space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f */ @@ -92,7 +66,7 @@ public void groupBars(float fromX, float groupSpace, float barSpace) { float barSpaceHalf = barSpace / 2f; float barWidthHalf = mBarWidth / 2f; - float interval = getIntervalWidth(groupSpace, barSpace); + float interval = getGroupWidth(groupSpace, barSpace); for (int i = 0; i < maxEntryCount; i++) { @@ -131,28 +105,14 @@ public void groupBars(float fromX, float groupSpace, float barSpace) { notifyDataChanged(); } - public float getIntervalWidth(float groupSpace, float barSpace) { + /** + * In case of grouped bars, this method returns the space an individual group of bar needs on the x-axis. + * + * @param groupSpace + * @param barSpace + * @return + */ + public float getGroupWidth(float groupSpace, float barSpace) { return mDataSets.size() * (mBarWidth + barSpace) + groupSpace; } - - // - // /** - // * Sets the maximum width (in density pixels) a single bar in the barchart - // * should consume. - // * - // * @param max - // */ - // public void setBarWidthMaximum(float max) { - // mMaximumBarWidth = Utils.convertDpToPixel(max); - // } - // - // /** - // * Returns the maximum width (in density pixels) a single bar in the - // * barchart should consume. - // * - // * @return - // */ - // public float getBarWidthMaximum() { - // return mMaximumBarWidth; - // } } 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 ed1008f12c..82f91ed847 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 @@ -62,7 +62,6 @@ public void initBuffers() { for (int i = 0; i < mBarBuffers.length; i++) { IBarDataSet set = barData.getDataSetByIndex(i); mBarBuffers[i] = new BarBuffer(set.getEntryCount() * 4 * (set.isStacked() ? set.getStackSize() : 1), - barData.getGroupSpace(), barData.getDataSetCount(), set.isStacked()); } } 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 dda51b0a48..21e386e19c 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 @@ -41,7 +41,6 @@ public void initBuffers() { for (int i = 0; i < mBarBuffers.length; i++) { IBarDataSet set = barData.getDataSetByIndex(i); mBarBuffers[i] = new HorizontalBarBuffer(set.getEntryCount() * 4 * (set.isStacked() ? set.getStackSize() : 1), - barData.getGroupSpace(), barData.getDataSetCount(), set.isStacked()); } } 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 6203c2a27d..5c2d98e7c7 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 @@ -209,41 +209,6 @@ public float[] generateTransformedValuesCandle(ICandleDataSet data, return valuePoints; } - /** - * Transforms an List of Entry into a float array containing the x and - * y values transformed with all matrices for the BARCHART. - * - * @param data - * @param dataSet the dataset index - * @return - */ - public float[] generateTransformedValuesHorizontalBarChart(IBarDataSet data, - int dataSet, BarData bd, float phaseY) { - - float[] valuePoints = new float[data.getEntryCount() * 2]; - - int setCount = bd.getDataSetCount(); - float space = bd.getGroupSpace(); - - for (int j = 0; j < valuePoints.length; j += 2) { - - Entry e = data.getEntryForIndex(j / 2); - float i = e.getX(); - - // calculate the x-position, depending on datasetcount - float x = i + i * (setCount - 1) + dataSet + space * i - + space / 2f; - float y = e.getY(); - - valuePoints[j] = y * phaseY; - valuePoints[j + 1] = x; - } - - getValueToPixelMatrix().mapPoints(valuePoints); - - return valuePoints; - } - /** * transform a path with all the given matrices VERY IMPORTANT: keep order * to value-touch-offset From 67911b1317467691e50010b485d9baea67049e8e Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 6 Jun 2016 17:47:40 +0200 Subject: [PATCH 188/606] Write unit tests for groupBars --- .../mikephil/charting/test/BarDataTest.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 MPChartLib/src/test/java/com/github/mikephil/charting/test/BarDataTest.java diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/BarDataTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/BarDataTest.java new file mode 100644 index 0000000000..99954bce27 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/BarDataTest.java @@ -0,0 +1,72 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by philipp on 06/06/16. + */ +public class BarDataTest { + + @Test + public void testGroupBars() { + + float groupSpace = 5f; + float barSpace = 1f; + + List values1 = new ArrayList<>(); + List values2 = new ArrayList<>(); + + for(int i = 0; i < 5; i++) { + values1.add(new BarEntry(i, 50)); + values2.add(new BarEntry(i, 60)); + } + + BarDataSet barDataSet1 = new BarDataSet(values1, "Set1"); + BarDataSet barDataSet2 = new BarDataSet(values2, "Set2"); + + BarData data = new BarData(barDataSet1, barDataSet2); + data.setBarWidth(10f); + + float groupWidth = data.getGroupWidth(groupSpace, barSpace); + assertEquals(27f, groupWidth, 0.01f); + + assertEquals(0f, values1.get(0).getX(), 0.01f); + assertEquals(1f, values1.get(1).getX(), 0.01f); + + data.groupBars(1000, groupSpace, barSpace); + + // 1000 + 2.5 + 0.5 + 5 + assertEquals(1008f, values1.get(0).getX(), 0.01f); + assertEquals(1019f, values2.get(0).getX(), 0.01f); + assertEquals(1035f, values1.get(1).getX(), 0.01f); + assertEquals(1046f, values2.get(1).getX(), 0.01f); + + data.groupBars(-1000, groupSpace, barSpace); + + assertEquals(-992f, values1.get(0).getX(), 0.01f); + assertEquals(-981f, values2.get(0).getX(), 0.01f); + assertEquals(-965f, values1.get(1).getX(), 0.01f); + assertEquals(-954f, values2.get(1).getX(), 0.01f); + + data.setBarWidth(20f); + groupWidth = data.getGroupWidth(groupSpace, barSpace); + assertEquals(47f, groupWidth, 0.01f); + + data.setBarWidth(10f); + data.groupBars(-20, groupSpace, barSpace); + + assertEquals(-12f, values1.get(0).getX(), 0.01f); + assertEquals(-1f, values2.get(0).getX(), 0.01f); + assertEquals(15f, values1.get(1).getX(), 0.01f); + assertEquals(26f, values2.get(1).getX(), 0.01f); + } +} From b4109d3248b34cbdb7035475a2a3385be9f4ae75 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 6 Jun 2016 22:51:20 +0200 Subject: [PATCH 189/606] Testing of calculate min max of dataset, changes in remove entry methods --- .../DynamicalAddingActivity.java | 2 +- .../mikephil/charting/data/BaseDataSet.java | 11 +++- .../mikephil/charting/data/ChartData.java | 1 - .../mikephil/charting/data/DataSet.java | 65 ++++++++----------- .../data/realm/base/RealmBaseDataSet.java | 43 ++++++------ .../interfaces/datasets/IDataSet.java | 36 ++++++---- .../mikephil/charting/test/DataSetTest.java | 62 ++++++++++++++++++ 7 files changed, 144 insertions(+), 76 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index 886a8f7ed2..a505e4a4f9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -91,7 +91,7 @@ private void removeLastEntry() { data.removeEntry(e, 0); // or remove by index - // mData.removeEntry(xIndex, dataSetIndex); + // mData.removeEntryByXPos(xIndex, dataSetIndex); data.notifyDataChanged(); mChart.notifyDataSetChanged(); mChart.invalidate(); 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 e218dec225..77a8367069 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 @@ -363,9 +363,16 @@ public boolean removeLast() { } @Override - public boolean removeEntry(int xIndex) { + public boolean removeEntryByXPos(float xPos) { - T e = getEntryForXPos(xIndex); + T e = getEntryForXPos(xPos); + return removeEntry(e); + } + + @Override + public boolean removeEntry(int index) { + + T e = getEntryForIndex(index); 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 abc1fd034e..02ac723ba2 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 @@ -7,7 +7,6 @@ import com.github.mikephil.charting.components.YAxis.AxisDependency; 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; import java.util.ArrayList; 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 68128e7c39..407c1e653b 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 @@ -76,18 +76,7 @@ public void calcMinMax() { for (T e : mValues) { if (e != null && !Float.isNaN(e.getY())) { - - if (e.getY() < mYMin) - mYMin = e.getY(); - - if (e.getY() > mYMax) - mYMax = e.getY(); - - if (e.getX() < mXMin) - mXMin = e.getX(); - - if (e.getX() > mXMax) - mXMax = e.getX(); + calcMinMax(e); } } @@ -102,6 +91,26 @@ public void calcMinMax() { } } + /** + * Updates the min and max x and y value of this DataSet based on the given Entry. + * + * @param e + */ + protected void calcMinMax(T e) { + + if (e.getY() < mYMin) + mYMin = e.getY(); + + if (e.getY() > mYMax) + mYMax = e.getY(); + + if (e.getX() < mXMin) + mXMin = e.getX(); + + if (e.getX() > mXMax) + mXMax = e.getX(); + } + @Override public int getEntryCount() { return mValues.size(); @@ -182,21 +191,11 @@ public void addEntryOrdered(T e) { if (e == null) return; - float val = e.getY(); - if (mValues == null) { mValues = new ArrayList(); } - if (mValues.size() == 0) { - mYMax = val; - mYMin = val; - } else { - if (mYMax < val) - mYMax = val; - if (mYMin > val) - mYMin = val; - } + calcMinMax(e); if (mValues.size() > 0 && mValues.get(mValues.size() - 1).getX() > e.getX()) { int closestIndex = getEntryIndex(e.getX(), Rounding.UP); @@ -219,25 +218,15 @@ public boolean addEntry(T e) { if (e == null) return false; - float val = e.getY(); - - List yVals = getValues(); - if (yVals == null) { - yVals = new ArrayList(); + List values = getValues(); + if (values == null) { + values = new ArrayList(); } - if (yVals.size() == 0) { - mYMax = val; - mYMin = val; - } else { - if (mYMax < val) - mYMax = val; - if (mYMin > val) - mYMin = val; - } + calcMinMax(e); // add the entry - yVals.add(e); + values.add(e); return true; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java index 63e10754b5..189c1221ae 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java @@ -145,18 +145,7 @@ public void calcMinMax() { for (S e : mValues) { if (e != null && !Float.isNaN(e.getY())) { - - if (e.getY() < mYMin) - mYMin = e.getY(); - - if (e.getY() > mYMax) - mYMax = e.getY(); - - if (e.getX() < mXMin) - mXMin = e.getX(); - - if (e.getX() > mXMax) - mXMax = e.getX(); + calcMinMax(e); } } @@ -171,6 +160,26 @@ public void calcMinMax() { } } + /** + * Updates the min and max x and y value of this DataSet based on the given Entry. + * + * @param e + */ + protected void calcMinMax(S e) { + + if (e.getY() < mYMin) + mYMin = e.getY(); + + if (e.getY() > mYMax) + mYMax = e.getY(); + + if (e.getX() < mXMin) + mXMin = e.getX(); + + if (e.getX() > mXMax) + mXMax = e.getX(); + } + @Override public S getEntryForXPos(float xPos) { //DynamicRealmObject o = new DynamicRealmObject(results.where().equalTo(mXValuesField, xIndex).findFirst()); @@ -298,15 +307,7 @@ public boolean addEntry(S e) { mValues = new ArrayList(); } - if (mValues.size() == 0) { - mYMax = val; - mYMin = val; - } else { - if (mYMax < val) - mYMax = val; - if (mYMin > val) - mYMin = val; - } + calcMinMax(e); // add the entry mValues.add(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 5e62fc9224..4d69438465 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 @@ -166,19 +166,10 @@ public interface IDataSet { */ boolean addEntry(T e); - /** - * Removes an Entry from the DataSets entries array. This will also - * recalculate the current minimum and maximum values of the DataSet and the - * value-sum. Returns true if an Entry was removed, false if no Entry could - * be removed. - * - * @param e - */ - boolean removeEntry(T e); /** * Adds an Entry to the DataSet dynamically. - * Entries are added to their appropriate index respective to it's x-index. + * Entries are added to their appropriate index in the values array respective to their x-position. * This will also recalculate the current minimum and maximum * values of the DataSet and the value-sum. * @@ -203,12 +194,31 @@ public interface IDataSet { boolean removeLast(); /** - * Removes the Entry object that has the given xIndex from the DataSet. + * Removes an Entry from the DataSets entries array. This will also + * recalculate the current minimum and maximum values of the DataSet and the + * value-sum. Returns true if an Entry was removed, false if no Entry could + * be removed. + * + * @param e + */ + boolean removeEntry(T e); + + /** + * Removes the Entry object that has the given xPos from the DataSet. * Returns true if an Entry was removed, false if no Entry could be removed. * - * @param xIndex + * @param xPos + */ + boolean removeEntryByXPos(float xPos); + + /** + * Removes the Entry object at the given index in the values array from the DataSet. + * Returns true if an Entry was removed, false if no Entry could be removed. + * + * @param index + * @return */ - boolean removeEntry(int xIndex); + boolean removeEntry(int index); /** * Checks if this DataSet contains the specified Entry. Returns true if so, 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 51922f5835..7da83018d4 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 @@ -16,6 +16,68 @@ */ public class DataSetTest { + @Test + public void testCalcMinMax() { + + List entries = new ArrayList(); + entries.add(new Entry(10, 10)); + entries.add(new Entry(15, 2)); + entries.add(new Entry(21, 5)); + + ScatterDataSet set = new ScatterDataSet(entries, ""); + + assertEquals(10f, set.getXMin(), 0.01f); + assertEquals(21f, set.getXMax(), 0.01f); + + assertEquals(2f, set.getYMin(), 0.01f); + assertEquals(10f, set.getYMax(), 0.01f); + + assertEquals(3, set.getEntryCount()); + + set.addEntry(new Entry(25, 1)); + + assertEquals(10f, set.getXMin(), 0.01f); + assertEquals(25f, set.getXMax(), 0.01f); + + assertEquals(1f, set.getYMin(), 0.01f); + assertEquals(10f, set.getYMax(), 0.01f); + + assertEquals(4, set.getEntryCount()); + } + + @Test + public void testAddRemoveEntry() { + + List entries = new ArrayList(); + entries.add(new Entry(10, 10)); + entries.add(new Entry(15, 2)); + entries.add(new Entry(21, 5)); + + ScatterDataSet set = new ScatterDataSet(entries, ""); + + assertEquals(3, set.getEntryCount()); + + set.addEntryOrdered(new Entry(5, 1)); + + assertEquals(4, set.getEntryCount()); + + assertEquals(5, set.getXMin(), 0.01f); + assertEquals(21, set.getXMax(), 0.01f); + + assertEquals(1f, set.getYMin(), 0.01f); + assertEquals(10f, set.getYMax(), 0.01f); + + assertEquals(5, set.getEntryForIndex(0).getX(), 0.01f); + assertEquals(1, set.getEntryForIndex(0).getY(), 0.01f); + + set.addEntryOrdered(new Entry(20, 50)); + + assertEquals(5, set.getEntryCount()); + + assertEquals(20, set.getEntryForIndex(3).getX(), 0.01f); + assertEquals(50, set.getEntryForIndex(3).getY(), 0.01f); + + } @Test public void testGetEntryForXPos() { From 280101bafc8fe84081e031b30f2b96bd3b9580e2 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 6 Jun 2016 22:53:09 +0200 Subject: [PATCH 190/606] IDataSet interface cleanup --- .../charting/interfaces/datasets/IDataSet.java | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) 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 4d69438465..49a78d7e52 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 @@ -124,18 +124,7 @@ public interface IDataSet { int getEntryIndex(T e); /** - * Returns the value of the Entry object at the given xVal. Returns - * Float.NaN if no value is at the given xVal. INFORMATION: This method - * does calculations at runtime. Do not over-use in performance critical - * situations. - * - * @param xVal - * @return - */ - float getYValueForXValue(float xVal); - - /** - * Returns all of the y values of the Entry objects at the given xPos. Returns + * Returns the value of the Entry object at the given xPos. Returns * Float.NaN if no value is at the given xPos. INFORMATION: This method * does calculations at runtime. Do not over-use in performance critical * situations. @@ -143,7 +132,8 @@ public interface IDataSet { * @param xPos * @return */ - float[] getYValuesForXPos(float xPos); + float getYValueForXValue(float xPos); + /** * This method returns the actual @@ -204,7 +194,7 @@ public interface IDataSet { boolean removeEntry(T e); /** - * Removes the Entry object that has the given xPos from the DataSet. + * Removes the Entry object closest to the given xPos from the DataSet. * Returns true if an Entry was removed, false if no Entry could be removed. * * @param xPos From dc03cf2593b7c8dbf379f2448e6d58c1c78ce62f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 6 Jun 2016 23:11:18 +0200 Subject: [PATCH 191/606] Test adding and removing entries --- .../mikephil/charting/data/BaseDataSet.java | 32 +++++++---- .../mikephil/charting/data/DataSet.java | 19 +------ .../data/realm/base/RealmBaseDataSet.java | 14 ----- .../mikephil/charting/test/DataSetTest.java | 53 +++++++++++++++++++ 4 files changed, 78 insertions(+), 40 deletions(-) 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 77a8367069..14a8241451 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 @@ -110,7 +110,9 @@ public List getColors() { return mColors; } - public List getValueColors() { return mValueColors; } + public List getValueColors() { + return mValueColors; + } @Override public int getColor() { @@ -225,7 +227,9 @@ public void resetColors() { mColors = new ArrayList(); } - /** ###### ###### OTHER STYLING RELATED METHODS ##### ###### */ + /** + * ###### ###### OTHER STYLING RELATED METHODS ##### ###### + */ @Override public void setLabel(String label) { @@ -335,7 +339,9 @@ public void setAxisDependency(YAxis.AxisDependency dependency) { } - /** ###### ###### DATA RELATED METHODS ###### ###### */ + /** + * ###### ###### DATA RELATED METHODS ###### ###### + */ @Override public int getIndexInEntries(int xIndex) { @@ -351,15 +357,23 @@ public int getIndexInEntries(int xIndex) { @Override public boolean removeFirst() { - T entry = getEntryForIndex(0); - return removeEntry(entry); + if (getEntryCount() > 0) { + + T entry = getEntryForIndex(0); + return removeEntry(entry); + } else + return false; } @Override public boolean removeLast() { - T entry = getEntryForIndex(getEntryCount() - 1); - return removeEntry(entry); + if (getEntryCount() > 0) { + + T e = getEntryForIndex(getEntryCount() - 1); + return removeEntry(e); + } else + return false; } @Override @@ -379,8 +393,8 @@ public boolean removeEntry(int index) { @Override public boolean contains(T e) { - for(int i = 0; i < getEntryCount(); i++) { - if(getEntryForIndex(i).equals(e)) + for (int i = 0; i < getEntryCount(); i++) { + if (getEntryForIndex(i).equals(e)) return true; } 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 407c1e653b..39beb6405f 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 @@ -200,10 +200,9 @@ public void addEntryOrdered(T e) { if (mValues.size() > 0 && mValues.get(mValues.size() - 1).getX() > e.getX()) { int closestIndex = getEntryIndex(e.getX(), Rounding.UP); mValues.add(closestIndex, e); - return; + } else { + mValues.add(e); } - - mValues.add(e); } @Override @@ -322,20 +321,6 @@ public float getYValueForXValue(float xVal) { return Float.NaN; } - @Override - public float[] getYValuesForXPos(float xVal) { - - List entries = getEntriesForXPos(xVal); - - float[] yVals = new float[entries.size()]; - int i = 0; - - for (T e : entries) - yVals[i++] = e.getY(); - - return yVals; - } - /** * Returns all Entry objects at the given xIndex. INFORMATION: This method * does calculations at runtime. Do not over-use in performance critical diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java index 189c1221ae..3e06759f66 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java @@ -281,20 +281,6 @@ public float getYValueForXValue(float xVal) { return Float.NaN; } - @Override - public float[] getYValuesForXPos(float xVal) { - - List entries = getEntriesForXPos(xVal); - - float[] yVals = new float[entries.size()]; - int i = 0; - - for (S e : entries) - yVals[i++] = e.getY(); - - return yVals; - } - @Override public boolean addEntry(S e) { 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 7da83018d4..2d2d2f7555 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 @@ -10,6 +10,8 @@ import java.util.List; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; /** * Created by philipp on 31/05/16. @@ -77,6 +79,57 @@ public void testAddRemoveEntry() { assertEquals(20, set.getEntryForIndex(3).getX(), 0.01f); assertEquals(50, set.getEntryForIndex(3).getY(), 0.01f); + assertTrue(set.removeEntry(3)); + + assertEquals(4, set.getEntryCount()); + + assertEquals(21, set.getEntryForIndex(3).getX(), 0.01f); + assertEquals(5, set.getEntryForIndex(3).getY(), 0.01f); + + assertEquals(5, set.getEntryForIndex(0).getX(), 0.01f); + assertEquals(1, set.getEntryForIndex(0).getY(), 0.01f); + + assertTrue(set.removeFirst()); + + assertEquals(3, set.getEntryCount()); + + assertEquals(10, set.getEntryForIndex(0).getX(), 0.01f); + assertEquals(10, set.getEntryForIndex(0).getY(), 0.01f); + + set.addEntryOrdered(new Entry(15, 3)); + + assertEquals(4, set.getEntryCount()); + + assertEquals(15, set.getEntryForIndex(1).getX(), 0.01f); + assertEquals(3, set.getEntryForIndex(1).getY(), 0.01f); + + assertEquals(21, set.getEntryForIndex(3).getX(), 0.01f); + assertEquals(5, set.getEntryForIndex(3).getY(), 0.01f); + + assertTrue(set.removeLast()); + + assertEquals(3, set.getEntryCount()); + + assertEquals(15, set.getEntryForIndex(2).getX(), 0.01f); + assertEquals(2, set.getEntryForIndex(2).getY(), 0.01f); + + assertTrue(set.removeLast()); + + assertEquals(2, set.getEntryCount()); + + assertTrue(set.removeLast()); + + assertEquals(1, set.getEntryCount()); + + assertEquals(10, set.getEntryForIndex(0).getX(), 0.01f); + assertEquals(10, set.getEntryForIndex(0).getY(), 0.01f); + + assertTrue(set.removeLast()); + + assertEquals(0, set.getEntryCount()); + + assertFalse(set.removeLast()); + assertFalse(set.removeFirst()); } @Test From 0a55308975c769bd7fafdcb8085d48c1ac181553 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 6 Jun 2016 23:54:54 +0200 Subject: [PATCH 192/606] Fixes and testing related to dynamic data adding and removing from chartdata --- .../mikephil/charting/data/ChartData.java | 285 +++++++----------- .../mikephil/charting/test/ChartDataTest.java | 95 ++++++ 2 files changed, 203 insertions(+), 177 deletions(-) create mode 100644 MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java 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 02ac723ba2..0b88010cbc 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 @@ -41,18 +41,18 @@ public abstract class ChartData> { */ protected float mXMin = 0f; - protected float mLeftAxisMax = 0.0f; + protected float mLeftAxisMax = Float.MIN_VALUE; - protected float mLeftAxisMin = 0.0f; + protected float mLeftAxisMin = Float.MAX_VALUE; - protected float mRightAxisMax = 0.0f; + protected float mRightAxisMax = Float.MIN_VALUE; - protected float mRightAxisMin = 0.0f; + protected float mRightAxisMin = Float.MAX_VALUE; /** - * total number of y-values across all DataSet objects + * total number of values (entries) across all DataSet objects */ - private int mYValCount = 0; + private int mValueCount = 0; /** * contains the maximum length (in characters) an entry in the x-vals array @@ -70,10 +70,27 @@ public ChartData() { } public ChartData(T... dataSets) { - mDataSets = Arrays.asList(dataSets); + mDataSets = arrayToList(dataSets); init(); } + /** + * Created because Arrays.asList(...) does not support modification. + * + * @param array + * @return + */ + private List arrayToList(T[] array) { + + List list = new ArrayList<>(); + + for (T set : array) { + list.add(set); + } + + return list; + } + /** * constructor for chart data * @@ -126,20 +143,8 @@ public void calcMinMax() { for (int i = 0; i < mDataSets.size(); i++) { - IDataSet set = mDataSets.get(i); - set.calcMinMax(); - - if (set.getYMin() < mYMin) - mYMin = set.getYMin(); - - if (set.getYMax() > mYMax) - mYMax = set.getYMax(); - - if (set.getXMin() < mXMin) - mXMin = set.getXMin(); - - if (set.getXMax() > mXMax) - mXMax = set.getXMax(); + T set = mDataSets.get(i); + calcMinMax(set); } if (mYMin == Float.MAX_VALUE) { @@ -184,9 +189,6 @@ public void calcMinMax() { } } } - - // in case there is only one axis, adjust the second axis - handleEmptyAxis(firstLeft, firstRight); } } @@ -198,7 +200,7 @@ public void calcMinMax() { */ protected void calcYValueCount() { - mYValCount = 0; + mValueCount = 0; if (mDataSets == null) return; @@ -209,7 +211,7 @@ protected void calcYValueCount() { count += mDataSets.get(i).getEntryCount(); } - mYValCount = count; + mValueCount = count; } /** ONLY GETTERS AND SETTERS BELOW THIS */ @@ -241,10 +243,18 @@ public float getYMin() { * @return */ public float getYMin(AxisDependency axis) { - if (axis == AxisDependency.LEFT) - return mLeftAxisMin; - else - return mRightAxisMin; + if (axis == AxisDependency.LEFT) { + + if (mLeftAxisMin == Float.MAX_VALUE) { + return mRightAxisMin; + } else + return mLeftAxisMin; + } else { + if (mRightAxisMin == Float.MAX_VALUE) { + return mLeftAxisMin; + } else + return mRightAxisMin; + } } /** @@ -263,10 +273,18 @@ public float getYMax() { * @return */ public float getYMax(AxisDependency axis) { - if (axis == AxisDependency.LEFT) - return mLeftAxisMax; - else - return mRightAxisMax; + if (axis == AxisDependency.LEFT) { + + if (mLeftAxisMax == Float.MIN_VALUE) { + return mRightAxisMax; + } else + return mLeftAxisMax; + } else { + if (mRightAxisMax == Float.MIN_VALUE) { + return mLeftAxisMax; + } else + return mRightAxisMax; + } } /** @@ -304,7 +322,7 @@ public float getXValMaximumLength() { * @return */ public int getYValCount() { - return mYValCount; + return mValueCount; } public List getDataSets() { @@ -367,23 +385,6 @@ public Entry getEntryForHighlight(Highlight highlight) { return mDataSets.get(highlight.getDataSetIndex()).getEntryForXPos(highlight.getX()); } } -// public Entry getEntryForHighlight(Highlight highlight) { -// if (highlight.getDataSetIndex() >= mDataSets.size()) -// return null; -// else { -// // The value of the highlighted entry could be NaN - -// // if we are not interested in highlighting a specific value. -// -// List entries = mDataSets.get(highlight.getDataSetIndex()) -// .getEntriesForXPos(highlight.getX()); -// for (Object entry : entries) -// if (((Entry)entry).getY() == highlight.getY() || -// Float.isNaN(highlight.getY())) -// return (Entry)entry; -// -// return null; -// } -// } /** * Returns the DataSet object with the given label. Search can be case @@ -422,63 +423,11 @@ public void addDataSet(T d) { if (d == null) return; - mYValCount += d.getEntryCount(); - - if (mDataSets.size() <= 0) { - - mYMax = d.getYMax(); - mYMin = d.getYMin(); - - if (d.getAxisDependency() == AxisDependency.LEFT) { - - mLeftAxisMax = d.getYMax(); - mLeftAxisMin = d.getYMin(); - } else { - mRightAxisMax = d.getYMax(); - mRightAxisMin = d.getYMin(); - } - } else { + mValueCount += d.getEntryCount(); - if (mYMax < d.getYMax()) - mYMax = d.getYMax(); - if (mYMin > d.getYMin()) - mYMin = d.getYMin(); - - if (d.getAxisDependency() == AxisDependency.LEFT) { - - if (mLeftAxisMax < d.getYMax()) - mLeftAxisMax = d.getYMax(); - if (mLeftAxisMin > d.getYMin()) - mLeftAxisMin = d.getYMin(); - } else { - if (mRightAxisMax < d.getYMax()) - mRightAxisMax = d.getYMax(); - if (mRightAxisMin > d.getYMin()) - mRightAxisMin = d.getYMin(); - } - } + calcMinMax(d); mDataSets.add(d); - - handleEmptyAxis(getFirstLeft(), getFirstRight()); - } - - /** - * This adjusts the other axis if one axis is empty and the other is not. - * - * @param firstLeft - * @param firstRight - */ - private void handleEmptyAxis(T firstLeft, T firstRight) { - - // in case there is only one axis, adjust the second axis - if (firstLeft == null) { - mLeftAxisMax = mRightAxisMax; - mLeftAxisMin = mRightAxisMin; - } else if (firstRight == null) { - mRightAxisMax = mLeftAxisMax; - mRightAxisMin = mLeftAxisMin; - } } /** @@ -498,7 +447,7 @@ public boolean removeDataSet(T d) { // if a DataSet was removed if (removed) { - mYValCount -= d.getEntryCount(); + mValueCount -= d.getEntryCount(); calcMinMax(); } @@ -538,47 +487,64 @@ public void addEntry(Entry e, int dataSetIndex) { if (!set.addEntry(e)) return; - float val = e.getY(); + calcMinMax(e, set.getAxisDependency()); - if (mYValCount == 0) { - mYMin = val; - mYMax = val; + mValueCount += 1; - if (set.getAxisDependency() == AxisDependency.LEFT) { + } else { + Log.e("addEntry", "Cannot add Entry because dataSetIndex too high or too low."); + } + } - mLeftAxisMax = e.getY(); - mLeftAxisMin = e.getY(); - } else { - mRightAxisMax = e.getY(); - mRightAxisMin = e.getY(); - } - } else { - - if (mYMax < val) - mYMax = val; - if (mYMin > val) - mYMin = val; - - if (set.getAxisDependency() == AxisDependency.LEFT) { - - if (mLeftAxisMax < e.getY()) - mLeftAxisMax = e.getY(); - if (mLeftAxisMin > e.getY()) - mLeftAxisMin = e.getY(); - } else { - if (mRightAxisMax < e.getY()) - mRightAxisMax = e.getY(); - if (mRightAxisMin > e.getY()) - mRightAxisMin = e.getY(); - } - } + protected void calcMinMax(Entry e, AxisDependency axis) { + + if (mYMax < e.getY()) + mYMax = e.getY(); + if (mYMin > e.getY()) + mYMin = e.getY(); - mYValCount += 1; + if (mXMax < e.getX()) + mXMax = e.getX(); + if (mXMin > e.getX()) + mXMin = e.getX(); - handleEmptyAxis(getFirstLeft(), getFirstRight()); + if (axis == AxisDependency.LEFT) { + if (mLeftAxisMax < e.getY()) + mLeftAxisMax = e.getY(); + if (mLeftAxisMin > e.getY()) + mLeftAxisMin = e.getY(); } else { - Log.e("addEntry", "Cannot add Entry because dataSetIndex too high or too low."); + if (mRightAxisMax < e.getY()) + mRightAxisMax = e.getY(); + if (mRightAxisMin > e.getY()) + mRightAxisMin = e.getY(); + } + } + + protected void calcMinMax(T d) { + + if (mYMax < d.getYMax()) + mYMax = d.getYMax(); + if (mYMin > d.getYMin()) + mYMin = d.getYMin(); + + if (mXMax < d.getXMax()) + mXMax = d.getXMax(); + if (mXMin > d.getXMin()) + mXMin = d.getXMin(); + + if (d.getAxisDependency() == AxisDependency.LEFT) { + + if (mLeftAxisMax < d.getYMax()) + mLeftAxisMax = d.getYMax(); + if (mLeftAxisMin > d.getYMin()) + mLeftAxisMin = d.getYMin(); + } else { + if (mRightAxisMax < d.getYMax()) + mRightAxisMax = d.getYMax(); + if (mRightAxisMin > d.getYMin()) + mRightAxisMin = d.getYMin(); } } @@ -601,7 +567,7 @@ public boolean removeEntry(Entry e, int dataSetIndex) { boolean removed = set.removeEntry(e); if (removed) { - mYValCount -= 1; + mValueCount -= 1; calcMinMax(); } @@ -731,23 +697,6 @@ public T getFirstRight() { return null; } -// /** -// * Generates an x-values array filled with numbers in range specified by the -// * parameters. Can be used for convenience. -// * -// * @return -// */ -// public static List generateXVals(int from, int to) { -// -// List xvals = new ArrayList(); -// -// for (int i = from; i < to; i++) { -// xvals.add(new XAxisValue(i, i + "")); -// } -// -// return xvals; -// } - /** * Sets a custom ValueFormatter for all DataSets this data object contains. * @@ -857,24 +806,6 @@ public void clearValues() { notifyDataChanged(); } -// /** -// * Checks if this data object contains the specified Entry. Returns true if -// * so, false if not. NOTE: Performance is pretty bad on this one, do not -// * over-use in performance critical situations. -// * -// * @param e -// * @return -// */ -// public boolean contains(Entry e) { -// -// for (T set : mDataSets) { -// if (set.contains(e)) -// return true; -// } -// -// return false; -// } - /** * Checks if this data object contains the specified DataSet. Returns true * if so, false if not. diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java new file mode 100644 index 0000000000..487fd1eac1 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java @@ -0,0 +1,95 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.data.ScatterDataSet; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by philipp on 06/06/16. + */ +public class ChartDataTest { + + @Test + public void testDynamicChartData() { + + List entries1 = new ArrayList(); + entries1.add(new Entry(10, 10)); + entries1.add(new Entry(15, -2)); + entries1.add(new Entry(21, 50)); + + ScatterDataSet set1 = new ScatterDataSet(entries1, ""); + + List entries2 = new ArrayList(); + entries2.add(new Entry(-1, 10)); + entries2.add(new Entry(10, 2)); + entries2.add(new Entry(20, 5)); + + ScatterDataSet set2 = new ScatterDataSet(entries2, ""); + + ScatterData data = new ScatterData(set1, set2); + + assertEquals(-2, data.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(50f, data.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(6, data.getEntryCount()); + + assertEquals(-1f, data.getXMin(), 0.01f); + assertEquals(21f, data.getXMax(), 0.01f); + + assertEquals(-2f, data.getYMin(), 0.01f); + assertEquals(50f, data.getYMax(), 0.01f); + + assertEquals(3, data.getMaxEntryCountSet().getEntryCount()); + + + // now add and remove values + data.addEntry(new Entry(-10, -10), 0); + + assertEquals(set1, data.getMaxEntryCountSet()); + assertEquals(4, data.getMaxEntryCountSet().getEntryCount()); + + assertEquals(-10f, data.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(50f, data.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(-10f, data.getXMin(), 0.01f); + assertEquals(21f, data.getXMax(), 0.01f); + + assertEquals(-10f, data.getYMin(), 0.01f); + assertEquals(50f, data.getYMax(), 0.01f); + + data.addEntry(new Entry(-100, 100), 0); + data.addEntry(new Entry(0, -100), 0); + + assertEquals(-100f, data.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(100f, data.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + // right axis will adapt left axis values if no right axis values are present + assertEquals(-100, data.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(100f, data.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + List entries3 = new ArrayList(); + entries3.add(new Entry(0, 200)); + entries3.add(new Entry(0, -50)); + + ScatterDataSet set3 = new ScatterDataSet(entries3, ""); + set3.setAxisDependency(YAxis.AxisDependency.RIGHT); + + data.addDataSet(set3); + + assertEquals(3, data.getDataSetCount()); + + assertEquals(-100f, data.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(100f, data.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(-50f, data.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(200f, data.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + } +} From 33b158147e346b23e3aaba37fe2b96823f65b60b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 6 Jun 2016 23:55:58 +0200 Subject: [PATCH 193/606] Minor fix in example --- .../xxmassdeveloper/mpchartexample/MultiLineChartActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index 03ebedd57b..487fd4baa9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -196,7 +196,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { for (int i = 0; i < mSeekBarX.getProgress(); i++) { double val = (Math.random() * mSeekBarY.getProgress()) + 3; - values.add(new Entry((float) val, i)); + values.add(new Entry(i, (float) val)); } LineDataSet d = new LineDataSet(values, "DataSet " + (z + 1)); From ae2d917fa75f91c635c9bc67a00b74ea1b42d9c2 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 7 Jun 2016 14:00:45 +0200 Subject: [PATCH 194/606] Refactoring, tests and fixes related to min and max calculation --- MPChartExample/build.gradle | 2 +- .../mikephil/charting/components/YAxis.java | 7 - .../mikephil/charting/data/BarDataSet.java | 20 +-- .../mikephil/charting/data/BubbleDataSet.java | 20 +-- .../mikephil/charting/data/CandleDataSet.java | 20 +-- .../mikephil/charting/data/ChartData.java | 135 +++++++++--------- .../mikephil/charting/data/DataSet.java | 39 ++--- .../mikephil/charting/test/ChartDataTest.java | 82 +++++++++++ .../mikephil/charting/test/DataSetTest.java | 8 ++ build.gradle | 2 +- 10 files changed, 179 insertions(+), 156 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index c0fe48d40c..dd8194aa12 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -38,7 +38,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.android.tools.build:gradle:2.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/src/main/java/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java index e2663dde6b..21dc5f7367 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 @@ -375,12 +375,5 @@ public void calculate(float dataMin, float dataMax) { // calc actual range this.mAxisRange = Math.abs(this.mAxisMaximum - this.mAxisMinimum); - -// // in case granularity is not customized, auto-calculate it -// if (!mCustomGranularity && mGranularityEnabled) { -// -// double granularity = Utils.granularity(mAxisRange, mLabelCount); -// this.mGranularity = (float) granularity; -// } } } 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 f77a26e446..6cc820ceb4 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 @@ -108,17 +108,13 @@ private void calcStackSize(List yVals) { @Override public void calcMinMax() { - if (mValues == null) - return; - - if (mValues.size() == 0) + if (mValues == null || mValues.isEmpty()) return; + mYMax = Float.MIN_VALUE; mYMin = Float.MAX_VALUE; - mYMax = -Float.MAX_VALUE; - + mXMax = Float.MIN_VALUE; mXMin = Float.MAX_VALUE; - mXMax = -Float.MAX_VALUE; for (BarEntry e : mValues) { @@ -147,16 +143,6 @@ public void calcMinMax() { mXMax = e.getX(); } } - - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; - } - - if(mXMin == Float.MAX_VALUE) { - mXMin = 0.f; - mXMax = 0.f; - } } @Override 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 3cb824f54d..6814adc8a1 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 @@ -31,17 +31,13 @@ public float getHighlightCircleWidth() { @Override public void calcMinMax() { - if (mValues == null) - return; - - if (mValues.size() == 0) + if (mValues == null || mValues.isEmpty()) return; + mYMax = Float.MIN_VALUE; mYMin = Float.MAX_VALUE; - mYMax = -Float.MAX_VALUE; - + mXMax = Float.MIN_VALUE; mXMin = Float.MAX_VALUE; - mXMax = -Float.MAX_VALUE; // need chart width to guess this properly @@ -75,16 +71,6 @@ public void calcMinMax() { mMaxSize = size; } } - - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; - } - - if(mXMin == Float.MAX_VALUE) { - mXMin = 0.f; - mXMax = 0.f; - } } @Override 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 2a10024e80..2331b3e589 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 @@ -102,17 +102,13 @@ public DataSet copy() { @Override public void calcMinMax() { - if (mValues == null) - return; - - if (mValues.size() == 0) + if (mValues == null || mValues.isEmpty()) return; + mYMax = Float.MIN_VALUE; mYMin = Float.MAX_VALUE; - mYMax = -Float.MAX_VALUE; - + mXMax = Float.MIN_VALUE; mXMin = Float.MAX_VALUE; - mXMax = -Float.MAX_VALUE; for (CandleEntry e : mValues) { @@ -128,16 +124,6 @@ public void calcMinMax() { if (e.getX() > mXMax) mXMax = e.getX(); } - - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; - } - - if(mXMin == Float.MAX_VALUE) { - mXMin = 0.f; - mXMax = 0.f; - } } /** 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 0b88010cbc..cea9befdb4 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 @@ -24,28 +24,29 @@ public abstract class ChartData> { /** * maximum y-value in the value array across all axes */ - protected float mYMax = 0.0f; + protected float mYMax = -Float.MAX_VALUE; /** * the minimum y-value in the value array across all axes */ - protected float mYMin = 0.0f; + protected float mYMin = Float.MAX_VALUE; /** * maximum x-value in the value array */ - protected float mXMax = 0f; + protected float mXMax = -Float.MAX_VALUE; /** * minimum x-value in the value array */ - protected float mXMin = 0f; + protected float mXMin = Float.MAX_VALUE; - protected float mLeftAxisMax = Float.MIN_VALUE; + + protected float mLeftAxisMax = -Float.MAX_VALUE; protected float mLeftAxisMin = Float.MAX_VALUE; - protected float mRightAxisMax = Float.MIN_VALUE; + protected float mRightAxisMax = -Float.MAX_VALUE; protected float mRightAxisMin = Float.MAX_VALUE; @@ -125,68 +126,57 @@ public void notifyDataChanged() { */ public void calcMinMax() { - if (mDataSets == null || mDataSets.size() < 1) { - - mYMax = 0f; - mYMin = 0f; - - mXMax = 0f; - mXMin = 0f; - - } else { - - mYMin = Float.MAX_VALUE; - mYMax = -Float.MAX_VALUE; - - mXMin = Float.MAX_VALUE; - mXMax = -Float.MAX_VALUE; + if (mDataSets == null) + return; - for (int i = 0; i < mDataSets.size(); i++) { + mYMax = -Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; - T set = mDataSets.get(i); - calcMinMax(set); - } + for (T set : mDataSets) { + calcMinMax(set); + } - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; - } + mLeftAxisMax = -Float.MAX_VALUE; + mLeftAxisMin = Float.MAX_VALUE; + mRightAxisMax = -Float.MAX_VALUE; + mRightAxisMin = Float.MAX_VALUE; - // left axis - T firstLeft = getFirstLeft(); + // left axis + T firstLeft = getFirstLeft(); - if (firstLeft != null) { + if (firstLeft != null) { - mLeftAxisMax = firstLeft.getYMax(); - mLeftAxisMin = firstLeft.getYMin(); + mLeftAxisMax = firstLeft.getYMax(); + mLeftAxisMin = firstLeft.getYMin(); - for (IDataSet dataSet : mDataSets) { - if (dataSet.getAxisDependency() == AxisDependency.LEFT) { - if (dataSet.getYMin() < mLeftAxisMin) - mLeftAxisMin = dataSet.getYMin(); + for (IDataSet dataSet : mDataSets) { + if (dataSet.getAxisDependency() == AxisDependency.LEFT) { + if (dataSet.getYMin() < mLeftAxisMin) + mLeftAxisMin = dataSet.getYMin(); - if (dataSet.getYMax() > mLeftAxisMax) - mLeftAxisMax = dataSet.getYMax(); - } + if (dataSet.getYMax() > mLeftAxisMax) + mLeftAxisMax = dataSet.getYMax(); } } + } - // right axis - T firstRight = getFirstRight(); + // right axis + T firstRight = getFirstRight(); - if (firstRight != null) { + if (firstRight != null) { - mRightAxisMax = firstRight.getYMax(); - mRightAxisMin = firstRight.getYMin(); + mRightAxisMax = firstRight.getYMax(); + mRightAxisMin = firstRight.getYMin(); - for (IDataSet dataSet : mDataSets) { - if (dataSet.getAxisDependency() == AxisDependency.RIGHT) { - if (dataSet.getYMin() < mRightAxisMin) - mRightAxisMin = dataSet.getYMin(); + for (IDataSet dataSet : mDataSets) { + if (dataSet.getAxisDependency() == AxisDependency.RIGHT) { + if (dataSet.getYMin() < mRightAxisMin) + mRightAxisMin = dataSet.getYMin(); - if (dataSet.getYMax() > mRightAxisMax) - mRightAxisMax = dataSet.getYMax(); - } + if (dataSet.getYMax() > mRightAxisMax) + mRightAxisMax = dataSet.getYMax(); } } } @@ -275,12 +265,12 @@ public float getYMax() { public float getYMax(AxisDependency axis) { if (axis == AxisDependency.LEFT) { - if (mLeftAxisMax == Float.MIN_VALUE) { + if (mLeftAxisMax == -Float.MAX_VALUE) { return mRightAxisMax; } else return mLeftAxisMax; } else { - if (mRightAxisMax == Float.MIN_VALUE) { + if (mRightAxisMax == -Float.MAX_VALUE) { return mLeftAxisMax; } else return mRightAxisMax; @@ -325,6 +315,11 @@ public int getYValCount() { return mValueCount; } + /** + * Returns all DataSet objects this ChartData object holds. + * + * @return + */ public List getDataSets() { return mDataSets; } @@ -578,23 +573,23 @@ public boolean removeEntry(Entry e, int dataSetIndex) { } /** - * Removes the Entry object at the given xIndex from the DataSet at the + * Removes the Entry object closest to the given DataSet at the * specified index. Returns true if an Entry was removed, false if no Entry * was found that meets the specified requirements. * - * @param xIndex + * @param xPos * @param dataSetIndex * @return */ - public boolean removeEntry(int xIndex, int dataSetIndex) { + public boolean removeEntry(float xPos, int dataSetIndex) { if (dataSetIndex >= mDataSets.size()) return false; IDataSet dataSet = mDataSets.get(dataSetIndex); - Entry e = dataSet.getEntryForXPos(xIndex); + Entry e = dataSet.getEntryForXPos(xPos); - if (e == null || e.getX() != xIndex) + if (e == null) return false; return removeEntry(e, dataSetIndex); @@ -658,13 +653,14 @@ public int[] getColors() { return colors; } + /** + * Returns the index of the provided DataSet in the DataSet array of this data object, or -1 if it does not exist. + * + * @param dataSet + * @return + */ public int getIndexOfDataSet(T dataSet) { - for (int i = 0; i < mDataSets.size(); i++) { - if (mDataSets.get(i) == dataSet) - return i; - } - - return -1; + return mDataSets.indexOf(dataSet); } /** @@ -678,7 +674,6 @@ public T getFirstLeft() { if (dataSet.getAxisDependency() == AxisDependency.LEFT) return dataSet; } - return null; } @@ -693,7 +688,6 @@ public T getFirstRight() { if (dataSet.getAxisDependency() == AxisDependency.RIGHT) return dataSet; } - return null; } @@ -840,12 +834,15 @@ public int getEntryCount() { } /** - * Returns the DataSet object with the maximum number of entries. + * Returns the DataSet object with the maximum number of entries or null if there are no DataSets. * * @return */ public T getMaxEntryCountSet() { + if (mDataSets == null || mDataSets.isEmpty()) + return null; + T max = mDataSets.get(0); for (T set : mDataSets) { 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 39beb6405f..0abdb49fdf 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 @@ -22,22 +22,22 @@ public abstract class DataSet extends BaseDataSet { /** * maximum y-value in the value array */ - protected float mYMax = 0.0f; + protected float mYMax = -Float.MAX_VALUE; /** * minimum y-value in the value array */ - protected float mYMin = 0.0f; + protected float mYMin = Float.MAX_VALUE; /** * maximum x-value in the value array */ - protected float mXMax = 0.0f; + protected float mXMax = -Float.MAX_VALUE; /** * minimum x-value in the value array */ - protected float mXMin = 0.0f; + protected float mXMin = Float.MAX_VALUE; /** @@ -61,33 +61,16 @@ public DataSet(List values, String label) { @Override public void calcMinMax() { - if (mValues == null) - return; - - if (mValues.size() == 0) + if (mValues == null || mValues.isEmpty()) return; - mYMin = Float.MAX_VALUE; mYMax = -Float.MAX_VALUE; - - mXMin = Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; mXMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; for (T e : mValues) { - - if (e != null && !Float.isNaN(e.getY())) { - calcMinMax(e); - } - } - - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; - } - - if (mXMin == Float.MAX_VALUE) { - mXMin = 0.f; - mXMax = 0.f; + calcMinMax(e); } } @@ -98,6 +81,9 @@ public void calcMinMax() { */ protected void calcMinMax(T e) { + if (e == null) + return; + if (e.getY() < mYMin) mYMin = e.getY(); @@ -225,8 +211,7 @@ public boolean addEntry(T e) { calcMinMax(e); // add the entry - values.add(e); - return true; + return values.add(e); } @Override diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java index 487fd1eac1..2fa1a12994 100644 --- a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java @@ -2,6 +2,8 @@ import com.github.mikephil.charting.components.YAxis; 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.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; @@ -11,6 +13,7 @@ import java.util.List; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; /** * Created by philipp on 06/06/16. @@ -91,5 +94,84 @@ public void testDynamicChartData() { assertEquals(-50f, data.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); assertEquals(200f, data.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + LineData lineData = new LineData(); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertEquals(0, lineData.getDataSetCount()); + + List lineEntries1 = new ArrayList(); + lineEntries1.add(new Entry(10, 90)); + lineEntries1.add(new Entry(1000, 1000)); + + LineDataSet lineSet1 = new LineDataSet(lineEntries1, ""); + + lineData.addDataSet(lineSet1); + + assertEquals(1, lineData.getDataSetCount()); + assertEquals(2, lineSet1.getEntryCount()); + assertEquals(2, lineData.getEntryCount()); + + assertEquals(10, lineData.getXMin(), 0.01f); + assertEquals(1000f, lineData.getXMax(), 0.01f); + + assertEquals(90, lineData.getYMin(), 0.01f); + assertEquals(1000, lineData.getYMax(), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(1000f, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(1000, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + List lineEntries2 = new ArrayList(); + lineEntries2.add(new Entry(-1000, 2000)); + lineEntries2.add(new Entry(2000, -3000)); + + Entry e = new Entry(-1000, 2500); + lineEntries2.add(e); + + LineDataSet lineSet2 = new LineDataSet(lineEntries2, ""); + lineSet2.setAxisDependency(YAxis.AxisDependency.RIGHT); + + lineData.addDataSet(lineSet2); + + assertEquals(2, lineData.getDataSetCount()); + assertEquals(3, lineSet2.getEntryCount()); + assertEquals(5, lineData.getEntryCount()); + + assertEquals(-1000, lineData.getXMin(), 0.01f); + assertEquals(2000, lineData.getXMax(), 0.01f); + + assertEquals(-3000, lineData.getYMin(), 0.01f); + assertEquals(2500, lineData.getYMax(), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(1000f, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(-3000, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(2500, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertTrue(lineData.removeEntry(e, 1)); + + assertEquals(-1000, lineData.getXMin(), 0.01f); + assertEquals(2000, lineData.getXMax(), 0.01f); + + assertEquals(-3000, lineData.getYMin(), 0.01f); + assertEquals(2000, lineData.getYMax(), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(1000f, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(-3000, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(2000, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); } } 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 2d2d2f7555..4ade7977b1 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 @@ -45,6 +45,14 @@ public void testCalcMinMax() { assertEquals(10f, set.getYMax(), 0.01f); assertEquals(4, set.getEntryCount()); + + set.removeEntry(3); + + assertEquals(10f, set.getXMin(), 0.01f); + assertEquals(21, set.getXMax(), 0.01f); + + assertEquals(2f, set.getYMin(), 0.01f); + assertEquals(10f, set.getYMax(), 0.01f); } @Test diff --git a/build.gradle b/build.gradle index 564d957cc5..2bda1ed031 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.android.tools.build:gradle:2.1.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } } From 72b4948aa3cbfc55eb02952160e6b2e3d3541147 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 7 Jun 2016 16:55:48 +0200 Subject: [PATCH 195/606] Cleanup of chartdata object --- .../mikephil/charting/charts/Chart.java | 11 +--- .../mikephil/charting/charts/PieChart.java | 8 +-- .../charting/charts/PieRadarChartBase.java | 5 ++ .../charting/charts/ScatterChart.java | 20 +++--- .../mikephil/charting/data/ChartData.java | 64 ------------------- ...arLineScatterCandleBubbleDataProvider.java | 1 - .../dataprovider/ChartInterface.java | 2 + .../charting/renderer/BarChartRenderer.java | 9 +-- .../renderer/BubbleChartRenderer.java | 3 +- .../renderer/CandleStickChartRenderer.java | 3 +- .../charting/renderer/DataRenderer.java | 7 ++ .../renderer/HorizontalBarChartRenderer.java | 7 +- .../charting/renderer/LineChartRenderer.java | 3 +- .../renderer/ScatterChartRenderer.java | 3 +- .../mikephil/charting/test/ChartDataTest.java | 36 ++++++++++- 15 files changed, 73 insertions(+), 109 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 359d2228c8..ddcb1f8485 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 @@ -354,7 +354,7 @@ public boolean isEmpty() { return true; else { - if (mData.getYValCount() <= 0) + if (mData.getEntryCount() <= 0) return true; else return false; @@ -1028,15 +1028,6 @@ public float getXRange() { return mXAxis.mAxisRange; } - /** - * Returns the total number of (y) values the chart holds (across all DataSets). - * - * @return - */ - public int getValueCount() { - return mData.getYValCount(); - } - /** * Returns the center point of the chart (the whole View) in pixels. * 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 ba8e37c8ea..7440ca3141 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 @@ -161,7 +161,6 @@ public void calculateOffsets() { @Override protected void calcMinMax() { - calcAngles(); } @@ -202,8 +201,10 @@ protected float[] getMarkerPosition(Entry e, Highlight highlight) { */ private void calcAngles() { - mDrawAngles = new float[mData.getYValCount()]; - mAbsoluteAngles = new float[mData.getYValCount()]; + int entryCount = mData.getEntryCount(); + + mDrawAngles = new float[entryCount]; + mAbsoluteAngles = new float[entryCount]; float yValueSum = mData.getYValueSum(); @@ -653,5 +654,4 @@ protected void onDetachedFromWindow() { } super.onDetachedFromWindow(); } - } 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 ea18e39ee7..749346e38b 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 @@ -69,6 +69,11 @@ protected void calcMinMax() { //mXAxis.mAxisRange = mData.getXVals().size() - 1; } + @Override + public int getMaxVisibleCount() { + return mData.getEntryCount(); + } + @Override public boolean onTouchEvent(MotionEvent event) { // use the pie- and radarchart listener own listener 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 292568a6b3..00043e3088 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 @@ -44,16 +44,16 @@ protected void init() { mXAxis.mAxisMinimum = -0.5f; } - @Override - protected void calcMinMax() { - super.calcMinMax(); - - if (mXAxis.mAxisRange == 0 && mData.getYValCount() > 0) - mXAxis.mAxisRange = 1; - - mXAxis.mAxisMaximum += 0.5f; - mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); - } +// @Override +// protected void calcMinMax() { +// super.calcMinMax(); +// +// if (mXAxis.mAxisRange == 0 && mData.getEntryCount() > 0) +// mXAxis.mAxisRange = 1; +// +// mXAxis.mAxisMaximum += 0.5f; +// mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); +// } /** * Returns all possible predefined ScatterShapes. 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 cea9befdb4..2e2d1a91c8 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 @@ -50,17 +50,6 @@ public abstract class ChartData> { protected float mRightAxisMin = Float.MAX_VALUE; - /** - * total number of values (entries) across all DataSet objects - */ - private int mValueCount = 0; - - /** - * contains the maximum length (in characters) an entry in the x-vals array - * has - */ - private float mXValMaximumLength = 0; - /** * array that holds all DataSets the ChartData object represents */ @@ -107,8 +96,6 @@ public ChartData(List sets) { * value count and sum */ protected void init() { - - calcYValueCount(); calcMinMax(); } @@ -182,28 +169,6 @@ public void calcMinMax() { } } - /** - * Calculates the total number of y-values across all DataSets the ChartData - * represents. - * - * @return - */ - protected void calcYValueCount() { - - mValueCount = 0; - - if (mDataSets == null) - return; - - int count = 0; - - for (int i = 0; i < mDataSets.size(); i++) { - count += mDataSets.get(i).getEntryCount(); - } - - mValueCount = count; - } - /** ONLY GETTERS AND SETTERS BELOW THIS */ /** @@ -295,26 +260,6 @@ public float getXMax() { return mXMax; } - /** - * returns the maximum length (in characters) across all values in the - * x-vals array - * - * @return - */ - public float getXValMaximumLength() { - return mXValMaximumLength; - } - - /** - * Returns the total number of y-values across all DataSet objects the this - * object represents. - * - * @return - */ - public int getYValCount() { - return mValueCount; - } - /** * Returns all DataSet objects this ChartData object holds. * @@ -418,8 +363,6 @@ public void addDataSet(T d) { if (d == null) return; - mValueCount += d.getEntryCount(); - calcMinMax(d); mDataSets.add(d); @@ -441,9 +384,6 @@ public boolean removeDataSet(T d) { // if a DataSet was removed if (removed) { - - mValueCount -= d.getEntryCount(); - calcMinMax(); } @@ -484,8 +424,6 @@ public void addEntry(Entry e, int dataSetIndex) { calcMinMax(e, set.getAxisDependency()); - mValueCount += 1; - } else { Log.e("addEntry", "Cannot add Entry because dataSetIndex too high or too low."); } @@ -562,8 +500,6 @@ public boolean removeEntry(Entry e, int dataSetIndex) { boolean removed = set.removeEntry(e); if (removed) { - mValueCount -= 1; - calcMinMax(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java index 0628356991..68beeb8d62 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java @@ -7,7 +7,6 @@ public interface BarLineScatterCandleBubbleDataProvider extends ChartInterface { Transformer getTransformer(AxisDependency axis); - int getMaxVisibleCount(); boolean isInverted(AxisDependency axis); float getLowestVisibleX(); 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 6660e2e4fb..1083e34863 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 @@ -65,4 +65,6 @@ public interface ChartInterface { ValueFormatter getDefaultValueFormatter(); ChartData getData(); + + int getMaxVisibleCount(); } 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 82f91ed847..9ec7ad3aab 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 @@ -3,9 +3,7 @@ import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Matrix; import android.graphics.Paint; -import android.graphics.Path; import android.graphics.RectF; import com.github.mikephil.charting.animation.ChartAnimator; @@ -184,7 +182,7 @@ protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHa public void drawValues(Canvas c) { // if values are drawn - if (passesCheck()) { + if (isDrawingValuesAllowed(mChart)) { List dataSets = mChart.getBarData().getDataSets(); @@ -410,11 +408,6 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } - protected boolean passesCheck() { - return mChart.getBarData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX(); - } - @Override public void drawExtras(Canvas c) { } 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 a1d9521a55..47542dc591 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 @@ -132,8 +132,7 @@ public void drawValues(Canvas c) { return; // if values are drawn - if (bubbleData.getYValCount() < (int) (Math.ceil((float) (mChart.getMaxVisibleCount()) - * mViewPortHandler.getScaleX()))) { + if (isDrawingValuesAllowed(mChart)) { final List dataSets = bubbleData.getDataSets(); 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 39630d8978..3720e61153 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 @@ -271,8 +271,7 @@ else if (open < close) public void drawValues(Canvas c) { // if values are drawn - if (mChart.getCandleData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX()) { + if (isDrawingValuesAllowed(mChart)) { List dataSets = mChart.getCandleData().getDataSets(); 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 f4a88fe08d..c1822a7c05 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 @@ -8,9 +8,11 @@ import android.graphics.Paint.Style; import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.charts.Chart; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.formatter.ValueFormatter; 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.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -65,6 +67,11 @@ public DataRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { mHighlightPaint.setColor(Color.rgb(255, 187, 115)); } + protected boolean isDrawingValuesAllowed(ChartInterface chart) { + return chart.getData().getEntryCount() < chart.getMaxVisibleCount() + * mViewPortHandler.getScaleX(); + } + /** * Returns the Paint object this renderer uses for drawing the values * (value-text). 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 21e386e19c..3952b90d90 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 @@ -11,6 +11,7 @@ import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.formatter.ValueFormatter; 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.Transformer; import com.github.mikephil.charting.utils.Utils; @@ -100,7 +101,7 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { @Override public void drawValues(Canvas c) { // if values are drawn - if (passesCheck()) { + if (isDrawingValuesAllowed(mChart)) { List dataSets = mChart.getBarData().getDataSets(); @@ -291,8 +292,8 @@ protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHa } @Override - protected boolean passesCheck() { - return mChart.getBarData().getYValCount() < mChart.getMaxVisibleCount() + protected boolean isDrawingValuesAllowed(ChartInterface chart) { + return chart.getData().getEntryCount() < chart.getMaxVisibleCount() * mViewPortHandler.getScaleY(); } } 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 7135f569a3..276d2f7971 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 @@ -543,8 +543,7 @@ private Path generateFilledPath(ILineDataSet dataSet, int from, int to) { @Override public void drawValues(Canvas c) { - if (mChart.getLineData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX()) { + if (isDrawingValuesAllowed(mChart)) { List dataSets = mChart.getLineData().getDataSets(); 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 ff876208f2..c3583c6579 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 @@ -306,8 +306,7 @@ protected void drawDataSet(Canvas c, IScatterDataSet dataSet) { public void drawValues(Canvas c) { // if values are drawn - if (mChart.getScatterData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX()) { + if (isDrawingValuesAllowed(mChart)) { List dataSets = mChart.getScatterData().getDataSets(); diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java index 2fa1a12994..ae464eefd0 100644 --- a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java @@ -13,6 +13,7 @@ import java.util.List; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; /** @@ -52,7 +53,6 @@ public void testDynamicChartData() { assertEquals(3, data.getMaxEntryCountSet().getEntryCount()); - // now add and remove values data.addEntry(new Entry(-10, -10), 0); @@ -173,5 +173,39 @@ public void testDynamicChartData() { assertEquals(-3000, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); assertEquals(2000, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertEquals(2, lineData.getDataSetCount()); + assertTrue(lineData.removeDataSet(lineSet2)); + assertEquals(1, lineData.getDataSetCount()); + + assertEquals(10, lineData.getXMin(), 0.01f); + assertEquals(1000, lineData.getXMax(), 0.01f); + + assertEquals(90, lineData.getYMin(), 0.01f); + assertEquals(1000, lineData.getYMax(), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(1000f, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(1000, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertTrue(lineData.removeDataSet(lineSet1)); + assertEquals(0, lineData.getDataSetCount()); + + assertEquals(Float.MAX_VALUE, lineData.getXMin(), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getXMax(), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertFalse(lineData.removeDataSet(lineSet1)); + assertFalse(lineData.removeDataSet(lineSet2)); } } From 4367fbb8f6d77ac28b77e669b42f297edb2358b6 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 7 Jun 2016 16:57:13 +0200 Subject: [PATCH 196/606] Minor fixes --- .../mikephil/charting/charts/CandleStickChart.java | 14 +++++++------- .../mikephil/charting/charts/ScatterChart.java | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) 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 87008d4a83..26fb82043f 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 @@ -35,13 +35,13 @@ protected void init() { mXAxis.mAxisMinimum = -0.5f; } - @Override - protected void calcMinMax() { - super.calcMinMax(); - - mXAxis.mAxisMaximum += 0.5f; - mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); - } +// @Override +// protected void calcMinMax() { +// super.calcMinMax(); +// +// mXAxis.mAxisMaximum += 0.5f; +// mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); +// } @Override public CandleData getCandleData() { 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 00043e3088..9624a64d34 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 @@ -41,7 +41,7 @@ protected void init() { super.init(); mRenderer = new ScatterChartRenderer(this, mAnimator, mViewPortHandler); - mXAxis.mAxisMinimum = -0.5f; +// mXAxis.mAxisMinimum = -0.5f; } // @Override From 1ff1c8fcfa7db2fea5a1e94a03a50f130673f44f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 7 Jun 2016 16:59:22 +0200 Subject: [PATCH 197/606] Documentation --- .../java/com/github/mikephil/charting/data/ChartData.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 2e2d1a91c8..0938fb1f11 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 @@ -55,10 +55,18 @@ public abstract class ChartData> { */ protected List mDataSets; + /** + * Default constructor. + */ public ChartData() { mDataSets = new ArrayList(); } + /** + * Constructor taking single or multiple DataSet objects. + * + * @param dataSets + */ public ChartData(T... dataSets) { mDataSets = arrayToList(dataSets); init(); From 42ceaa9aa20a115dda316150d0e887eb66487cfd Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 7 Jun 2016 21:48:26 +0200 Subject: [PATCH 198/606] Work on line approximation --- .../charting/charts/BarLineChartBase.java | 2 - .../charting/charts/CombinedChart.java | 24 +- .../charting/data/filter/Approximator.java | 300 ++++-------------- .../charting/test/ApproximatorTest.java | 43 +++ 4 files changed, 115 insertions(+), 254 deletions(-) create mode 100644 MPChartLib/src/test/java/com/github/mikephil/charting/test/ApproximatorTest.java 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 cc4a14bb9f..a4cf5b3b0b 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 @@ -18,8 +18,6 @@ import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.ChartHighlighter; 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 c40b4a242c..f6a7c0c814 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 @@ -28,10 +28,10 @@ public class CombinedChart extends BarLineChartBase implements LineDataProvider, BarDataProvider, ScatterDataProvider, CandleDataProvider, BubbleDataProvider { - /** - * flag that enables or disables the highlighting arrow - */ - private boolean mDrawHighlightArrow = false; +// /** +// * flag that enables or disables the highlighting arrow +// */ +// private boolean mDrawHighlightArrow = false; /** * if set to true, all values are drawn above their bars, instead of below @@ -167,14 +167,14 @@ public boolean isDrawValueAboveBarEnabled() { return mDrawValueAboveBar; } - /** - * set this to true to draw the highlightning arrow - * - * @param enabled - */ - public void setDrawHighlightArrow(boolean enabled) { - mDrawHighlightArrow = enabled; - } +// /** +// * set this to true to draw the highlightning arrow +// * +// * @param enabled +// */ +// public void setDrawHighlightArrow(boolean enabled) { +// mDrawHighlightArrow = enabled; +// } /** * If set to true, all values are drawn above their bars, instead of below diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java index cbc630c562..920da8d212 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java @@ -1,282 +1,102 @@ package com.github.mikephil.charting.data.filter; -import com.github.mikephil.charting.data.Entry; +import android.annotation.TargetApi; +import android.os.Build; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; /** * Implemented according to Wiki-Pseudocode {@link} * http://en.wikipedia.org/wiki/Ramer�Douglas�Peucker_algorithm - * + * * @author Philipp Baldauf & Phliipp Jahoda */ public class Approximator { - /** the type of filtering algorithm to use */ - private ApproximatorType mType = ApproximatorType.DOUGLAS_PEUCKER; + @TargetApi(Build.VERSION_CODES.GINGERBREAD) + public float[] reduceWithDouglasPeucker(float[] points, float tolerance) { - /** the tolerance to be filtered with */ - private double mTolerance = 0; + int greatestIndex = 0; + float greatestDistance = 0f; - private float mScaleRatio = 1f; - private float mDeltaRatio = 1f; + Line line = new Line(points[0], points[1], points[points.length - 2], points[points.length - 1]); - /** - * array that contains "true" on all indices that will be kept after - * filtering - */ - private boolean[] keep; - - /** enums for the different types of filtering algorithms */ - public enum ApproximatorType { - NONE, DOUGLAS_PEUCKER - } - - /** - * Initializes the approximator with type NONE - */ - public Approximator() { - this.mType = ApproximatorType.NONE; - } - - /** - * Initializes the approximator with the given type and tolerance. If - * toleranec <= 0, no filtering will be done. - * - * @param type - */ - public Approximator(ApproximatorType type, double tolerance) { - setup(type, tolerance); - } - - /** - * sets type and tolerance, if tolerance <= 0, no filtering will be done - * - * @param type - * @param tolerance - */ - public void setup(ApproximatorType type, double tolerance) { - mType = type; - mTolerance = tolerance; - } - - /** - * sets the tolerance for the Approximator. When using the - * Douglas-Peucker-Algorithm, the tolerance is an angle in degrees, that - * will trigger the filtering. - */ - public void setTolerance(double tolerance) { - mTolerance = tolerance; - } - - /** - * Sets the filtering algorithm that should be used. - * - * @param type - */ - public void setType(ApproximatorType type) { - this.mType = type; - } - - /** - * Sets the ratios for x- and y-axis, as well as the ratio of the scale - * levels - * - * @param deltaRatio - * @param scaleRatio - */ - public void setRatios(float deltaRatio, float scaleRatio) { - mDeltaRatio = deltaRatio; - mScaleRatio = scaleRatio; - } - - /** - * Filters according to type. Uses the pre set set tolerance - * - * @param points the points to filter - * @return - */ - public List filter(List points) { - return filter(points, mTolerance); - } - - /** - * Filters according to type. - * - * @param points the points to filter - * @param tolerance the angle in degrees that will trigger the filtering - * @return - */ - public List filter(List points, double tolerance) { - - if (tolerance <= 0) - return points; + for (int i = 2; i < points.length - 2; i += 2) { - keep = new boolean[points.size()]; + float distance = line.distance(points[i], points[i + 1]); - switch (mType) { - case DOUGLAS_PEUCKER: - return reduceWithDouglasPeuker(points, tolerance); - case NONE: - return points; - default: - return points; + if (distance > greatestDistance) { + greatestDistance = distance; + greatestIndex = i; + } } - } - /** - * uses the douglas peuker algorithm to reduce the given List of - * entries - * - * @param entries - * @param epsilon - * @return - */ - private List reduceWithDouglasPeuker(List entries, double epsilon) { - // if a shape has 2 or less points it cannot be reduced - if (epsilon <= 0 || entries.size() < 3) { - return entries; - } + if (greatestDistance > tolerance) { - // first and last always stay - keep[0] = true; - keep[entries.size() - 1] = true; + float[] reduced1 = reduceWithDouglasPeucker(Arrays.copyOfRange(points, 0, greatestIndex + 2), tolerance); + float[] reduced2 = reduceWithDouglasPeucker(Arrays.copyOfRange(points, greatestIndex, points.length), + tolerance); - // first and last entry are entry point to recursion - algorithmDouglasPeucker(entries, epsilon, 0, entries.size() - 1); + float[] result1 = reduced1; + float[] result2 = Arrays.copyOfRange(reduced2, 2, reduced2.length); - // create a new array with series, only take the kept ones - List reducedEntries = new ArrayList(); - for (int i = 0; i < entries.size(); i++) { - if (keep[i]) { - Entry curEntry = entries.get(i); - reducedEntries.add(new Entry(curEntry.getY(), curEntry.getX())); - } + return concat(result1, result2); + } else { + return line.getPoints(); } - return reducedEntries; } /** - * apply the Douglas-Peucker-Reduction to an List of Entry with a given - * epsilon (tolerance) - * - * @param entries - * @param epsilon as y-value - * @param start - * @param end + * Combine arrays. + * + * @param arrays + * @return */ - private void algorithmDouglasPeucker(List entries, double epsilon, int start, - int end) { - if (end <= start + 1) { - // recursion finished - return; + float[] concat(float[]... arrays) { + int length = 0; + for (float[] array : arrays) { + length += array.length; } - - // find the greatest distance between start and endpoint - int maxDistIndex = 0; - double distMax = 0; - - Entry firstEntry = entries.get(start); - Entry lastEntry = entries.get(end); - - for (int i = start + 1; i < end; i++) { - double dist = calcAngleBetweenLines(firstEntry, lastEntry, firstEntry, entries.get(i)); - - // keep the point with the greatest distance - if (dist > distMax) { - distMax = dist; - maxDistIndex = i; + float[] result = new float[length]; + int pos = 0; + for (float[] array : arrays) { + for (float element : array) { + result[pos] = element; + pos++; } } - - // Log.i("maxangle", "" + distMax); - - if (distMax > epsilon) { - // keep max dist point - keep[maxDistIndex] = true; - - // recursive call - algorithmDouglasPeucker(entries, epsilon, start, maxDistIndex); - algorithmDouglasPeucker(entries, epsilon, maxDistIndex, end); - } // else don't keep the point... - } - - /** - * calculate the distance between a line between two entries and an entry - * (point) - * - * @param startEntry line startpoint - * @param endEntry line endpoint - * @param entryPoint the point to which the distance is measured from the - * line - * @return - */ - public double calcPointToLineDistance(Entry startEntry, Entry endEntry, Entry entryPoint) { - - float xDiffEndStart = (float) endEntry.getX() - (float) startEntry.getX(); - float xDiffEntryStart = (float) entryPoint.getX() - (float) startEntry.getX(); - - double normalLength = Math.sqrt((xDiffEndStart) - * (xDiffEndStart) - + (endEntry.getY() - startEntry.getY()) - * (endEntry.getY() - startEntry.getY())); - return Math.abs((xDiffEntryStart) - * (endEntry.getY() - startEntry.getY()) - - (entryPoint.getY() - startEntry.getY()) - * (xDiffEndStart)) - / normalLength; + return result; } - /** - * Calculates the angle between two given lines. The provided Entry objects - * mark the starting and end points of the lines. - * - * @param start1 - * @param end1 - * @param start2 - * @param end2 - * @return - */ - public double calcAngleBetweenLines(Entry start1, Entry end1, Entry start2, Entry end2) { + private class Line { - double angle1 = calcAngleWithRatios(start1, end1); - double angle2 = calcAngleWithRatios(start2, end2); + private float[] points; - return Math.abs(angle1 - angle2); - } + private float sxey; + private float exsy; - /** - * calculates the angle between two Entries (points) in the chart taking - * ratios into consideration - * - * @param p1 - * @param p2 - * @return - */ - public double calcAngleWithRatios(Entry p1, Entry p2) { + private float dx; + private float dy; - float dx = p2.getX() * mDeltaRatio - p1.getX() * mDeltaRatio; - float dy = p2.getY() * mScaleRatio - p1.getY() * mScaleRatio; - double angle = Math.atan2(dy, dx) * 180.0 / Math.PI; + private float length; - return angle; - } + public Line(float x1, float y1, float x2, float y2) { + dx = x1 - x2; + dy = y1 - y2; + sxey = x1 * y2; + exsy = x2 * y1; + length = (float) Math.sqrt(dx * dx + dy * dy); - /** - * calculates the angle between two Entries (points) in the chart - * - * @param p1 - * @param p2 - * @return - */ - public double calcAngle(Entry p1, Entry p2) { + points = new float[]{x1, y1, x2, y2}; + } - float dx = p2.getX() - p1.getX(); - float dy = p2.getY() - p1.getY(); - double angle = Math.atan2(dy, dx) * 180.0 / Math.PI; + float distance(float x, float y) { + return Math.abs(dy * x - dx * y + sxey - exsy) / length; + } - return angle; + public float[] getPoints() { + return points; + } } } diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ApproximatorTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ApproximatorTest.java new file mode 100644 index 0000000000..784c290ceb --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ApproximatorTest.java @@ -0,0 +1,43 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.filter.Approximator; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by philipp on 07/06/16. + */ +public class ApproximatorTest { + + @Test + public void testApproximation() { + + float[] points = new float[]{ + 10, 20, + 20, 30, + 25, 25, + 30, 28, + 31, 31, + 33, 33, + 40, 40, + 44, 40, + 48, 23, + 50, 20, + 55, 20, + 60, 25}; + + assertEquals(24, points.length); + + Approximator a = new Approximator(); + + float[] reduced = a.reduceWithDouglasPeucker(points, 2); + + assertEquals(18, reduced.length); + } +} From f927517bea36a2471b31fd2abffc7b196859c5f0 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 7 Jun 2016 21:58:44 +0200 Subject: [PATCH 199/606] Comment cleanup --- .../mikephil/charting/charts/BubbleChart.java | 27 --------------- .../charting/charts/CandleStickChart.java | 8 ----- .../charting/charts/CombinedChart.java | 31 ----------------- .../charting/charts/HorizontalBarChart.java | 34 ------------------- .../mikephil/charting/charts/LineChart.java | 8 ----- .../charting/charts/ScatterChart.java | 20 +++-------- .../charting/data/filter/Approximator.java | 2 +- .../mikephil/charting/renderer/Renderer.java | 18 ---------- 8 files changed, 5 insertions(+), 143 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java index a0681e652c..58532d2084 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java @@ -38,33 +38,6 @@ protected void init() { mRenderer = new BubbleChartRenderer(this, mAnimator, mViewPortHandler); } -// @Override -// protected void calcMinMax() { -// super.calcMinMax(); -// -// if (mXAxis.mAxisRange == 0 && mData.getYValCount() > 0) -// mXAxis.mAxisRange = 1; -// -// mXAxis.mAxisMinimum = -0.5f; -// mXAxis.mAxisMaximum = (float) mData.getXValCount() - 0.5f; -// -// if (mRenderer != null) { -// for (IBubbleDataSet set : mData.getDataSets()) { -// -// final float xmin = set.getXMin(); -// final float xmax = set.getXMax(); -// -// if (xmin < mXAxis.mAxisMinimum) -// mXAxis.mAxisMinimum = xmin; -// -// if (xmax > mXAxis.mAxisMaximum) -// mXAxis.mAxisMaximum = xmax; -// } -// } -// -// mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); -// } - public BubbleData getBubbleData() { return mData; } 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 26fb82043f..029a2bb490 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 @@ -35,14 +35,6 @@ protected void init() { mXAxis.mAxisMinimum = -0.5f; } -// @Override -// protected void calcMinMax() { -// super.calcMinMax(); -// -// mXAxis.mAxisMaximum += 0.5f; -// mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); -// } - @Override public CandleData getCandleData() { return mData; 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 f6a7c0c814..b1259b683a 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 @@ -82,37 +82,6 @@ protected void init() { // mViewPortHandler); } -// @Override -// protected void calcMinMax() { -// super.calcMinMax(); -// -// if (getBarData() != null || getCandleData() != null || getBubbleData() != null) { -// mXAxis.mAxisMinimum = -0.5f; -// mXAxis.mAxisMaximum = mData.getXVals().size() - 0.5f; -// -// if (getBubbleData() != null) { -// -// for (IBubbleDataSet set : getBubbleData().getDataSets()) { -// -// final float xmin = set.getXMin(); -// final float xmax = set.getXMax(); -// -// if (xmin < mXAxis.mAxisMinimum) -// mXAxis.mAxisMinimum = xmin; -// -// if (xmax > mXAxis.mAxisMaximum) -// mXAxis.mAxisMaximum = xmax; -// } -// } -// } -// -// mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); -// -// if (mXAxis.mAxisRange == 0.f && getLineData() != null && getLineData().getYValCount() > 0) { -// mXAxis.mAxisRange = 1.f; -// } -// } - @Override public void setData(CombinedData data) { mData = null; 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 e8265af86d..a7a1cdf687 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 @@ -201,38 +201,4 @@ public float getHighestVisibleX() { mViewPortHandler.contentTop()); return (float) Math.min(mXAxis.mAxisMaximum, pos.y); } - -// /** -// * Returns the lowest x-index (value on the x-axis) that is still visible on the chart. -// * -// * @return -// */ -// @Override -// public float getLowestVisibleX() { -// -// float step = mData.getDataSetCount(); -// float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); -// -// float[] pts = new float[] { mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom() }; -// -// getTransformer(AxisDependency.LEFT).pixelsToValue(pts); -// return (((pts[1] <= 0) ? 0 : ((pts[1])) / div) + 1); -// } -// -// /** -// * Returns the highest x-index (value on the x-axis) that is still visible on the chart. -// * -// * @return -// */ -// @Override -// public float getHighestVisibleX() { -// -// float step = mData.getDataSetCount(); -// float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); -// -// float[] pts = new float[] { mViewPortHandler.contentLeft(), mViewPortHandler.contentTop() }; -// -// getTransformer(AxisDependency.LEFT).pixelsToValue(pts); -// return ((pts[1] >= getXChartMax()) ? getXChartMax() / div : (pts[1] / div)); -// } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java index 71acbb8d01..c96926017f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java @@ -33,14 +33,6 @@ protected void init() { mRenderer = new LineChartRenderer(this, mAnimator, mViewPortHandler); } - -// @Override -// protected void calcMinMax() { -// super.calcMinMax(); -// -// if (mXAxis.mAxisRange == 0 && mData.getYValCount() > 0) -// mXAxis.mAxisRange = 1; -// } @Override public LineData getLineData() { 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 9624a64d34..25fdadae55 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 @@ -12,7 +12,7 @@ * The ScatterChart. Draws dots, triangles, squares and custom shapes into the * Chart-View. CIRCLE and SCQUARE offer the best performance, TRIANGLE has the * worst performance. - * + * * @author Philipp Jahoda */ public class ScatterChart extends BarLineChartBase implements ScatterDataProvider { @@ -41,32 +41,20 @@ protected void init() { super.init(); mRenderer = new ScatterChartRenderer(this, mAnimator, mViewPortHandler); -// mXAxis.mAxisMinimum = -0.5f; } -// @Override -// protected void calcMinMax() { -// super.calcMinMax(); -// -// if (mXAxis.mAxisRange == 0 && mData.getEntryCount() > 0) -// mXAxis.mAxisRange = 1; -// -// mXAxis.mAxisMaximum += 0.5f; -// mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); -// } - /** * Returns all possible predefined ScatterShapes. - * + * * @return */ public static ScatterShape[] getAllPossibleShapes() { - return new ScatterShape[] { + return new ScatterShape[]{ ScatterShape.SQUARE, ScatterShape.CIRCLE, ScatterShape.TRIANGLE, ScatterShape.CROSS }; } public ScatterData getScatterData() { return mData; - }; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java index 920da8d212..542188e602 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java @@ -91,7 +91,7 @@ public Line(float x1, float y1, float x2, float y2) { points = new float[]{x1, y1, x2, y2}; } - float distance(float x, float y) { + public float distance(float x, float y) { return Math.abs(dy * x - dx * y + sxey - exsy) / length; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java index 337bb54061..90f49683ae 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java @@ -1,7 +1,6 @@ package com.github.mikephil.charting.renderer; -import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; import com.github.mikephil.charting.utils.ViewPortHandler; /** @@ -19,21 +18,4 @@ public abstract class Renderer { public Renderer(ViewPortHandler viewPortHandler) { this.mViewPortHandler = viewPortHandler; } - - /** - * Returns true if the specified value fits in between the provided min - * and max bounds, false if not. - * - * @param val - * @param min - * @param max - * @return - */ - protected boolean fitsBounds(float val, float min, float max) { - - if (val < min || val > max) - return false; - else - return true; - } } From 5a4b6f1532282ef9338bbadbe6cce78e6cfa43ba Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 7 Jun 2016 23:20:06 +0200 Subject: [PATCH 200/606] Fixes related to combined chart and refinement of combined chart example --- .../mpchartexample/CombinedChartActivity.java | 55 ++++++++++++++----- .../renderer/CandleStickChartRenderer.java | 31 ++++++----- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index e9b5a6e81b..007f36c837 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -2,6 +2,7 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; +import android.graphics.Paint; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; @@ -9,6 +10,8 @@ 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; import com.github.mikephil.charting.components.YAxis; @@ -27,6 +30,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.AxisValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -50,12 +54,16 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setBackgroundColor(Color.WHITE); mChart.setDrawGridBackground(false); mChart.setDrawBarShadow(false); - + // draw bars behind lines mChart.setDrawOrder(new DrawOrder[]{ DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.CANDLE, DrawOrder.LINE, DrawOrder.SCATTER }); + Legend l = mChart.getLegend(); + l.setWordWrapEnabled(true); + l.setPosition(Legend.LegendPosition.BELOW_CHART_CENTER); + YAxis rightAxis = mChart.getAxisRight(); rightAxis.setDrawGridLines(false); rightAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) @@ -66,14 +74,29 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTH_SIDED); + xAxis.setAxisMinValue(0f); + xAxis.setGranularity(1f); + xAxis.setValueFormatter(new AxisValueFormatter() { + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mMonths[(int) value % mMonths.length]; + } + + @Override + public int getDecimalDigits() { + return 0; + } + }); CombinedData data = new CombinedData(); data.setData(generateLineData()); data.setData(generateBarData()); -// data.setData(generateBubbleData()); -// data.setData(generateScatterData()); -// data.setData(generateCandleData()); + data.setData(generateBubbleData()); + data.setData(generateScatterData()); + data.setData(generateCandleData()); + + xAxis.setAxisMaxValue(data.getXMax() + 0.25f); mChart.setData(data); mChart.invalidate(); @@ -86,7 +109,7 @@ private LineData generateLineData() { ArrayList entries = new ArrayList(); for (int index = 0; index < itemcount; index++) - entries.add(new Entry(index, getRandom(15, 10))); + entries.add(new Entry(index + 0.5f, getRandom(15, 5))); LineDataSet set = new LineDataSet(entries, "Line DataSet"); set.setColor(Color.rgb(240, 238, 70)); @@ -114,7 +137,7 @@ private BarData generateBarData() { ArrayList entries = new ArrayList(); for (int index = 0; index < itemcount; index++) - entries.add(new BarEntry(index, getRandom(15, 30))); + entries.add(new BarEntry(index + 0.5f, getRandom(25, 25))); BarDataSet set = new BarDataSet(entries, "Bar DataSet"); set.setColor(Color.rgb(60, 220, 78)); @@ -133,11 +156,11 @@ protected ScatterData generateScatterData() { ArrayList entries = new ArrayList(); - for (int index = 0; index < itemcount; index++) - entries.add(new Entry(index, getRandom(20, 15))); + for (float index = 0; index < itemcount; index += 0.5f) + entries.add(new Entry(index + 0.25f, getRandom(10, 55))); ScatterDataSet set = new ScatterDataSet(entries, "Scatter DataSet"); - set.setColor(Color.GREEN); + set.setColors(ColorTemplate.MATERIAL_COLORS); set.setScatterShapeSize(7.5f); set.setDrawValues(false); set.setValueTextSize(10f); @@ -152,11 +175,12 @@ protected CandleData generateCandleData() { ArrayList entries = new ArrayList(); - for (int index = 0; index < itemcount; index++) - entries.add(new CandleEntry(index, 20f, 10f, 13f, 17f)); + for (int index = 0; index < itemcount; index += 2) + entries.add(new CandleEntry(index + 1f, 90, 70, 85, 75f)); CandleDataSet set = new CandleDataSet(entries, "Candle DataSet"); - set.setColor(Color.rgb(80, 80, 80)); + set.setDecreasingColor(Color.rgb(142, 150, 175)); + set.setShadowColor(Color.DKGRAY); set.setBarSpace(0.3f); set.setValueTextSize(10f); set.setDrawValues(false); @@ -164,7 +188,7 @@ protected CandleData generateCandleData() { return d; } - + protected BubbleData generateBubbleData() { BubbleData bd = new BubbleData(); @@ -172,8 +196,9 @@ protected BubbleData generateBubbleData() { ArrayList entries = new ArrayList(); for (int index = 0; index < itemcount; index++) { - float rnd = getRandom(20, 30); - entries.add(new BubbleEntry(index, rnd, rnd)); + float y = getRandom(10, 105); + float size = getRandom(50, 105); + entries.add(new BubbleEntry(index + 0.5f, y, size)); } BubbleDataSet set = new BubbleDataSet(entries, "Bubble 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 3720e61153..998668e863 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 @@ -56,8 +56,6 @@ public void drawData(Canvas c) { @SuppressWarnings("ResourceAsColor") protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { - int entryCount = dataSet.getEntryCount(); - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); @@ -71,26 +69,22 @@ protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { CandleEntry entryFrom = dataSet.getEntryForXPos(lowX, DataSet.Rounding.DOWN); CandleEntry entryTo = dataSet.getEntryForXPos(highX, DataSet.Rounding.UP); - int diff = (entryFrom == entryTo) ? 1 : 0; - int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); - int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); + int minx = dataSet.getEntryIndex(entryFrom); + int maxx = dataSet.getEntryIndex(entryTo); mRenderPaint.setStrokeWidth(dataSet.getShadowWidth()); // draw the body - for (int j = minx, - count = (int) Math.ceil((maxx - minx) * phaseX + (float)minx); - j < count; - j++) { + for (int j = minx; j <= maxx * phaseX; j++) { // get the entry CandleEntry e = dataSet.getEntryForIndex(j); - final float xPos = e.getX(); - - if (xPos < minx || xPos >= maxx) + if(e == null) continue; + final float xPos = e.getX(); + final float open = e.getOpen(); final float close = e.getClose(); final float high = e.getHigh(); @@ -287,8 +281,14 @@ public void drawValues(Canvas c) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - int minx = Math.max((int) mChart.getLowestVisibleX(), 0); - int maxx = Math.min((int) mChart.getHighestVisibleX() + 1, dataSet.getEntryCount()); + float low = mChart.getLowestVisibleX(); + float high = mChart.getHighestVisibleX(); + + CandleEntry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); + CandleEntry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); + + int minx = dataSet.getEntryIndex(entryFrom); + int maxx = dataSet.getEntryIndex(entryTo); float[] positions = trans.generateTransformedValuesCandle( dataSet, mAnimator.getPhaseX(), mAnimator.getPhaseY(), minx, maxx); @@ -308,7 +308,8 @@ public void drawValues(Canvas c) { CandleEntry entry = dataSet.getEntryForIndex(j / 2 + minx); - drawValue(c, dataSet.getValueFormatter(), entry.getHigh(), entry, i, x, y - yOffset, dataSet.getValueTextColor(j / 2)); + drawValue(c, dataSet.getValueFormatter(), entry.getHigh(), entry, i, x, y - yOffset, dataSet + .getValueTextColor(j / 2)); } } } From dad6cc0ce516f8a5e1ff65ad83cb5f85d8c14fd0 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 7 Jun 2016 23:42:33 +0200 Subject: [PATCH 201/606] Modify build.gradle --- MPChartExample/build.gradle | 2 +- MPChartLib/build.gradle | 1 + build.gradle | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 12d3a86424..2293cb5448 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -37,7 +37,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.android.tools.build:gradle:2.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 1c7ca0e099..69db788eef 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'com.android.library' apply plugin: 'maven' apply plugin: 'com.github.dcendents.android-maven' +//apply plugin: 'realm-android' android { compileSdkVersion 23 diff --git a/build.gradle b/build.gradle index 564d957cc5..f2bd5c0e14 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,8 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' + //classpath "io.realm:realm-gradle-plugin:1.0.0" + classpath 'com.android.tools.build:gradle:2.1.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } } From 3c47d0ade9e62e1d798b89129653c9f49e7c6a8d Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 8 Jun 2016 16:50:20 +0200 Subject: [PATCH 202/606] Create example for timechart --- .../res/layout/activity_linechart_time.xml | 35 ++++++ .../mpchartexample/CombinedChartActivity.java | 4 - .../mpchartexample/LineChartTime.java | 111 +++++++++--------- .../mpchartexample/notimportant/DemoBase.java | 48 +------- .../notimportant/MainActivity.java | 3 +- .../charting/components/AxisBase.java | 2 +- .../charting/highlight/ChartHighlighter.java | 7 +- 7 files changed, 101 insertions(+), 109 deletions(-) create mode 100644 MPChartExample/res/layout/activity_linechart_time.xml diff --git a/MPChartExample/res/layout/activity_linechart_time.xml b/MPChartExample/res/layout/activity_linechart_time.xml new file mode 100644 index 0000000000..27f70f8ba3 --- /dev/null +++ b/MPChartExample/res/layout/activity_linechart_time.xml @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 007f36c837..aacced57f3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -212,10 +212,6 @@ protected BubbleData generateBubbleData() { return bd; } - private float getRandom(float range, float startsfrom) { - return (float) (Math.random() * range) + startsfrom; - } - @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.combined, menu); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index c6f1ea85c7..e53840362c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -6,6 +6,7 @@ import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; +import android.view.View; import android.view.WindowManager; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; @@ -13,6 +14,7 @@ import android.widget.Toast; 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.Legend.LegendForm; import com.github.mikephil.charting.components.Legend.LegendPosition; @@ -22,40 +24,37 @@ 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.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; public class LineChartTime extends DemoBase implements OnSeekBarChangeListener { private LineChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - private long curTime = System.currentTimeMillis(); + private SeekBar mSeekBarX; + private TextView tvX; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_linechart); + setContentView(R.layout.activity_linechart_time); tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarX.setProgress(100); + tvX.setText("100"); - mSeekBarX.setProgress(45); - mSeekBarY.setProgress(100); - - mSeekBarY.setOnSeekBarChangeListener(this); mSeekBarX.setOnSeekBarChangeListener(this); mChart = (LineChart) findViewById(R.id.chart1); - mChart.setLogEnabled(true); // no description text mChart.setDescription(""); @@ -72,47 +71,54 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); mChart.setHighlightPerDragEnabled(true); - // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(true); - // set an alternative background color - mChart.setBackgroundColor(Color.LTGRAY); + mChart.setBackgroundColor(Color.WHITE); + mChart.setViewPortOffsets(0f, 0f, 0f, 0f); // add data - setData(20, 30); + setData(100, 30); mChart.invalidate(); - Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf"); // get the legend (only possible after setting data) Legend l = mChart.getLegend(); - - // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); - l.setForm(LegendForm.LINE); - l.setTypeface(tf); - l.setTextSize(11f); - l.setTextColor(Color.WHITE); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); -// l.setYOffset(11f); + l.setEnabled(false); XAxis xAxis = mChart.getXAxis(); + xAxis.setPosition(XAxis.XAxisPosition.TOP_INSIDE); xAxis.setTypeface(tf); - xAxis.setTextSize(12f); + xAxis.setTextSize(10f); xAxis.setTextColor(Color.WHITE); xAxis.setDrawAxisLine(false); + xAxis.setDrawGridLines(true); + xAxis.setTextColor(Color.rgb(255, 192, 56)); + xAxis.setCenterAxisLabels(true); + xAxis.setValueFormatter(new AxisValueFormatter() { + + private SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm"); - // custom x-axis min / max - xAxis.setAxisMinValue(5000); - xAxis.setAxisMaxValue(30000); + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mFormat.format(new Date((long) value)); + } + + @Override + public int getDecimalDigits() { + return 0; + } + }); YAxis leftAxis = mChart.getAxisLeft(); + leftAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); leftAxis.setTypeface(tf); leftAxis.setTextColor(ColorTemplate.getHoloBlue()); - leftAxis.setAxisMaxValue(200f); - leftAxis.setAxisMinValue(0f); leftAxis.setDrawGridLines(true); leftAxis.setGranularityEnabled(true); + leftAxis.setAxisMinValue(0f); + leftAxis.setAxisMaxValue(170f); + leftAxis.setYOffset(-9f); + leftAxis.setTextColor(Color.rgb(255, 192, 56)); YAxis rightAxis = mChart.getAxisRight(); rightAxis.setEnabled(false); @@ -254,43 +260,38 @@ 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("" + (mSeekBarX.getProgress())); - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); + setData(mSeekBarX.getProgress(), 50); // redraw mChart.invalidate(); - - System.out.println("xmin: " + mChart.getXAxis().getAxisMinimum()); - System.out.println("xmax: " + mChart.getXAxis().getAxisMaximum()); - System.out.println("lvx: " + mChart.getLowestVisibleX()); - System.out.println("hvx: " + mChart.getHighestVisibleX()); } private void setData(int count, float range) { - ArrayList yVals1 = new ArrayList(); + long now = System.currentTimeMillis(); + long hourMillis = 3600000L; - for (int i = 0; i < count; i++) { - float mult = range / 2f; - float val = (float) (Math.random() * mult) + 50;// + (float) - // ((mult * - // 0.1) / 10); - //yVals1.add(new Entry(val, 10000 + i * 1000)); - } + ArrayList values = new ArrayList(); - yVals1.add(new Entry(100, 10000)); - yVals1.add(new Entry(130, 15000)); - yVals1.add(new Entry(120, 20000)); + float from = now - (count / 2) * hourMillis; + float to = now + (count / 2) * hourMillis; + + for (float x = from; x < to; x += hourMillis) { + + 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(yVals1, "DataSet 1"); + LineDataSet set1 = new LineDataSet(values, "DataSet 1"); set1.setAxisDependency(AxisDependency.LEFT); set1.setColor(ColorTemplate.getHoloBlue()); - set1.setCircleColor(Color.WHITE); - set1.setLineWidth(2f); - set1.setCircleRadius(3f); + 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)); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java index fb3eb51cf9..7141be0aaa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java @@ -23,51 +23,9 @@ public abstract class DemoBase extends FragmentActivity { "Party Y", "Party Z" }; - -// protected ArrayList getMonths() { -// -// ArrayList m = new ArrayList(); -// m.add(new XAxisValue(0, "Jan")); -// m.add(new XAxisValue(1, "Feb")); -// m.add(new XAxisValue(2, "Mar")); -// m.add(new XAxisValue(3, "Apr")); -// m.add(new XAxisValue(4, "May")); -// m.add(new XAxisValue(5, "Jun")); -// m.add(new XAxisValue(6, "Jul")); -// m.add(new XAxisValue(7, "Aug")); -// m.add(new XAxisValue(8, "Sep")); -// m.add(new XAxisValue(9, "Okt")); -// m.add(new XAxisValue(10, "Nov")); -// m.add(new XAxisValue(11, "Dec")); -// -// return m; -// } -// -// -// protected ArrayList getQuarters() { -// -// ArrayList q = new ArrayList(); -// q.add(new XAxisValue(0, "Quarter 1")); -// q.add(new XAxisValue(1, "Quarter 2")); -// q.add(new XAxisValue(2, "Quarter 3")); -// q.add(new XAxisValue(3, "Quarter 4")); -// -// return q; -// } -// -// protected List getYears() { -// ArrayList years = new ArrayList(); -// -// years.add(new XAxisValue(0, "2013")); -// years.add(new XAxisValue(1, "2014")); -// years.add(new XAxisValue(2, "2015")); -// years.add(new XAxisValue(3, "2016")); -// years.add(new XAxisValue(4, "2017")); -// years.add(new XAxisValue(5, "2018")); -// years.add(new XAxisValue(6, "2019")); -// -// return years; -// } + protected float getRandom(float range, float startsfrom) { + return (float) (Math.random() * range) + startsfrom; + } @Override public void onBackPressed() { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index 06dbb7b865..1cf582ce6d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -132,12 +132,11 @@ protected void onCreate(Bundle savedInstanceState) { ContentItem realm = new ContentItem( "Realm.io Database", "This demonstrates how to use this library with Realm.io mobile database."); - realm.isNew = true; objects.add(realm); ContentItem time = new ContentItem( "Time Chart", - "Simple demonstration of a 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); 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 dcb613b926..e92cdcd54c 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 @@ -436,7 +436,7 @@ public String getLongestLabel() { for (int i = 0; i < mEntries.length; i++) { String text = getFormattedLabel(i); - if (longest.length() < text.length()) + if (text != null && longest.length() < text.length()) longest = text; } 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 7ba76d6a77..bc9a2aa84c 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 @@ -92,8 +92,8 @@ protected SelectionDetail getSelectionDetail(float xVal, float x, float y) { * @return */ protected float getMinimumDistance(List valsAtIndex, - float pos, - YAxis.AxisDependency axis) { + float pos, + YAxis.AxisDependency axis) { float distance = Float.MAX_VALUE; @@ -148,6 +148,9 @@ protected SelectionDetail getDetails(IDataSet set, int dataSetIndex, float xVal, final Entry e = set.getEntryForXPos(xVal, rounding); + if (e == null) + return null; + PointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), e.getY()); return new SelectionDetail((float) pixels.x, (float) pixels.y, e.getX(), e.getY(), dataSetIndex, set); From e704fb87a0119bf1c9e3dfbb8b7c28d47ea0a66e Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 9 Jun 2016 09:45:20 +0200 Subject: [PATCH 203/606] Fixes related to line renering --- .../res/layout/activity_linechart_time.xml | 35 ++++++ .../mpchartexample/CombinedChartActivity.java | 4 - .../mpchartexample/LineChartActivity1.java | 18 +-- .../mpchartexample/LineChartTime.java | 113 +++++++++--------- .../mpchartexample/notimportant/DemoBase.java | 48 +------- .../notimportant/MainActivity.java | 3 +- .../charting/components/AxisBase.java | 2 +- .../mikephil/charting/data/CombinedData.java | 3 +- .../charting/highlight/ChartHighlighter.java | 7 +- .../charting/renderer/LineChartRenderer.java | 82 ++++--------- .../charting/renderer/LineRadarRenderer.java | 11 +- .../mikephil/charting/utils/Transformer.java | 2 +- 12 files changed, 143 insertions(+), 185 deletions(-) create mode 100644 MPChartExample/res/layout/activity_linechart_time.xml diff --git a/MPChartExample/res/layout/activity_linechart_time.xml b/MPChartExample/res/layout/activity_linechart_time.xml new file mode 100644 index 0000000000..27f70f8ba3 --- /dev/null +++ b/MPChartExample/res/layout/activity_linechart_time.xml @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 007f36c837..aacced57f3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -212,10 +212,6 @@ protected BubbleData generateBubbleData() { return bd; } - private float getRandom(float range, float startsfrom) { - return (float) (Math.random() * range) + startsfrom; - } - @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.combined, menu); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index ac0a864266..bfa5c2dde2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -148,7 +148,7 @@ protected void onCreate(Bundle savedInstanceState) { // mChart.setVisibleYRange(20f, AxisDependency.LEFT); // mChart.centerViewTo(20, 50, AxisDependency.LEFT); - mChart.animateX(2500, Easing.EasingOption.EaseInOutQuart); + mChart.animateX(2500); //mChart.invalidate(); // get the legend (only possible after setting data) @@ -337,15 +337,12 @@ public void onStopTrackingTouch(SeekBar seekBar) { 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);x - yVals.add(new Entry(i, val)); + float val = (float) (Math.random() * range) + 3; + values.add(new Entry(i, val)); } LineDataSet set1; @@ -353,15 +350,12 @@ private void setData(int count, float range) { if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); - set1.setValues(yVals); + set1.setValues(values); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { // create a dataset and give it a type - set1 = new LineDataSet(yVals, "DataSet 1"); - - // set1.setFillAlpha(110); - // set1.setFillColor(Color.RED); + set1 = new LineDataSet(values, "DataSet 1"); // set the line to be drawn like this "- - - - - -" set1.enableDashedLine(10f, 5f, 0f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index c6f1ea85c7..46677a51cf 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -6,6 +6,7 @@ import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; +import android.view.View; import android.view.WindowManager; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; @@ -13,6 +14,7 @@ import android.widget.Toast; 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.Legend.LegendForm; import com.github.mikephil.charting.components.Legend.LegendPosition; @@ -22,40 +24,38 @@ 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.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; public class LineChartTime extends DemoBase implements OnSeekBarChangeListener { private LineChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - private long curTime = System.currentTimeMillis(); + private SeekBar mSeekBarX; + private TextView tvX; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_linechart); + setContentView(R.layout.activity_linechart_time); tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + mSeekBarX.setProgress(100); + tvX.setText("100"); - mSeekBarX.setProgress(45); - mSeekBarY.setProgress(100); - - mSeekBarY.setOnSeekBarChangeListener(this); mSeekBarX.setOnSeekBarChangeListener(this); mChart = (LineChart) findViewById(R.id.chart1); - mChart.setLogEnabled(true); + mChart.setHardwareAccelerationEnabled(true); // no description text mChart.setDescription(""); @@ -72,47 +72,55 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); mChart.setHighlightPerDragEnabled(true); - // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(true); - // set an alternative background color - mChart.setBackgroundColor(Color.LTGRAY); + mChart.setBackgroundColor(Color.WHITE); + mChart.setViewPortOffsets(0f, 0f, 0f, 0f); // add data - setData(20, 30); + setData(100, 30); mChart.invalidate(); - Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf"); // get the legend (only possible after setting data) Legend l = mChart.getLegend(); - - // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); - l.setForm(LegendForm.LINE); - l.setTypeface(tf); - l.setTextSize(11f); - l.setTextColor(Color.WHITE); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); -// l.setYOffset(11f); + l.setEnabled(false); XAxis xAxis = mChart.getXAxis(); + xAxis.setPosition(XAxis.XAxisPosition.TOP_INSIDE); xAxis.setTypeface(tf); - xAxis.setTextSize(12f); + xAxis.setTextSize(10f); xAxis.setTextColor(Color.WHITE); xAxis.setDrawAxisLine(false); + xAxis.setDrawGridLines(true); + xAxis.setTextColor(Color.rgb(255, 192, 56)); + xAxis.setCenterAxisLabels(true); + xAxis.setGranularity(60000L); // one minute in millis + xAxis.setValueFormatter(new AxisValueFormatter() { + + private SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm"); - // custom x-axis min / max - xAxis.setAxisMinValue(5000); - xAxis.setAxisMaxValue(30000); + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mFormat.format(new Date((long) value)); + } + + @Override + public int getDecimalDigits() { + return 0; + } + }); YAxis leftAxis = mChart.getAxisLeft(); + leftAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); leftAxis.setTypeface(tf); leftAxis.setTextColor(ColorTemplate.getHoloBlue()); - leftAxis.setAxisMaxValue(200f); - leftAxis.setAxisMinValue(0f); leftAxis.setDrawGridLines(true); leftAxis.setGranularityEnabled(true); + leftAxis.setAxisMinValue(0f); + leftAxis.setAxisMaxValue(170f); + leftAxis.setYOffset(-9f); + leftAxis.setTextColor(Color.rgb(255, 192, 56)); YAxis rightAxis = mChart.getAxisRight(); rightAxis.setEnabled(false); @@ -254,43 +262,38 @@ 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("" + (mSeekBarX.getProgress())); - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); + setData(mSeekBarX.getProgress(), 50); // redraw mChart.invalidate(); - - System.out.println("xmin: " + mChart.getXAxis().getAxisMinimum()); - System.out.println("xmax: " + mChart.getXAxis().getAxisMaximum()); - System.out.println("lvx: " + mChart.getLowestVisibleX()); - System.out.println("hvx: " + mChart.getHighestVisibleX()); } private void setData(int count, float range) { - ArrayList yVals1 = new ArrayList(); + long now = System.currentTimeMillis(); + long hourMillis = 3600000L; - for (int i = 0; i < count; i++) { - float mult = range / 2f; - float val = (float) (Math.random() * mult) + 50;// + (float) - // ((mult * - // 0.1) / 10); - //yVals1.add(new Entry(val, 10000 + i * 1000)); - } + ArrayList values = new ArrayList(); - yVals1.add(new Entry(100, 10000)); - yVals1.add(new Entry(130, 15000)); - yVals1.add(new Entry(120, 20000)); + float from = now - (count / 2) * hourMillis; + float to = now + (count / 2) * hourMillis; + + for (float x = from; x < to; x += hourMillis) { + + 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(yVals1, "DataSet 1"); + LineDataSet set1 = new LineDataSet(values, "DataSet 1"); set1.setAxisDependency(AxisDependency.LEFT); set1.setColor(ColorTemplate.getHoloBlue()); - set1.setCircleColor(Color.WHITE); - set1.setLineWidth(2f); - set1.setCircleRadius(3f); + 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)); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java index fb3eb51cf9..7141be0aaa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java @@ -23,51 +23,9 @@ public abstract class DemoBase extends FragmentActivity { "Party Y", "Party Z" }; - -// protected ArrayList getMonths() { -// -// ArrayList m = new ArrayList(); -// m.add(new XAxisValue(0, "Jan")); -// m.add(new XAxisValue(1, "Feb")); -// m.add(new XAxisValue(2, "Mar")); -// m.add(new XAxisValue(3, "Apr")); -// m.add(new XAxisValue(4, "May")); -// m.add(new XAxisValue(5, "Jun")); -// m.add(new XAxisValue(6, "Jul")); -// m.add(new XAxisValue(7, "Aug")); -// m.add(new XAxisValue(8, "Sep")); -// m.add(new XAxisValue(9, "Okt")); -// m.add(new XAxisValue(10, "Nov")); -// m.add(new XAxisValue(11, "Dec")); -// -// return m; -// } -// -// -// protected ArrayList getQuarters() { -// -// ArrayList q = new ArrayList(); -// q.add(new XAxisValue(0, "Quarter 1")); -// q.add(new XAxisValue(1, "Quarter 2")); -// q.add(new XAxisValue(2, "Quarter 3")); -// q.add(new XAxisValue(3, "Quarter 4")); -// -// return q; -// } -// -// protected List getYears() { -// ArrayList years = new ArrayList(); -// -// years.add(new XAxisValue(0, "2013")); -// years.add(new XAxisValue(1, "2014")); -// years.add(new XAxisValue(2, "2015")); -// years.add(new XAxisValue(3, "2016")); -// years.add(new XAxisValue(4, "2017")); -// years.add(new XAxisValue(5, "2018")); -// years.add(new XAxisValue(6, "2019")); -// -// return years; -// } + protected float getRandom(float range, float startsfrom) { + return (float) (Math.random() * range) + startsfrom; + } @Override public void onBackPressed() { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index 06dbb7b865..1cf582ce6d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -132,12 +132,11 @@ protected void onCreate(Bundle savedInstanceState) { ContentItem realm = new ContentItem( "Realm.io Database", "This demonstrates how to use this library with Realm.io mobile database."); - realm.isNew = true; objects.add(realm); ContentItem time = new ContentItem( "Time Chart", - "Simple demonstration of a 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); 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 dcb613b926..e92cdcd54c 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 @@ -436,7 +436,7 @@ public String getLongestLabel() { for (int i = 0; i < mEntries.length; i++) { String text = getFormattedLabel(i); - if (longest.length() < text.length()) + if (text != null && longest.length() < text.length()) longest = text; } 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 65bb6cca62..fc1b566a07 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 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; @@ -13,7 +14,7 @@ * * @author Philipp Jahoda */ -public class CombinedData extends BarLineScatterCandleBubbleData> { +public class CombinedData extends BarLineScatterCandleBubbleData> { private LineData mLineData; private BarData mBarData; 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 7ba76d6a77..bc9a2aa84c 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 @@ -92,8 +92,8 @@ protected SelectionDetail getSelectionDetail(float xVal, float x, float y) { * @return */ protected float getMinimumDistance(List valsAtIndex, - float pos, - YAxis.AxisDependency axis) { + float pos, + YAxis.AxisDependency axis) { float distance = Float.MAX_VALUE; @@ -148,6 +148,9 @@ protected SelectionDetail getDetails(IDataSet set, int dataSetIndex, float xVal, final Entry e = set.getEntryForXPos(xVal, rounding); + if (e == null) + return null; + PointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), e.getY()); return new SelectionDetail((float) pixels.x, (float) pixels.y, e.getX(), e.getY(), dataSetIndex, 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 276d2f7971..3510509374 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 @@ -286,7 +286,7 @@ protected void drawCubicFill(Canvas c, ILineDataSet dataSet, Path spline, Transf float fillMin = dataSet.getFillFormatter() .getFillLinePosition(dataSet, mChart); - + // Take the from/to xIndex from the entries themselves, // so missing entries won't screw up the filling. // What we need to draw is line from points of the xIndexes - not arbitrary entry indexes! @@ -349,11 +349,9 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); - int diff = (entryFrom == entryTo) ? 1 : 0; - int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); - int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); - - final int count = (int)(Math.ceil((float)(maxx - minx) * phaseX + (float)(minx))); + int minx = dataSet.getEntryIndex(entryFrom); + int maxx = dataSet.getEntryIndex(entryTo); + int count = (int) ((maxx - minx) * phaseX); // more than 1 color if (dataSet.getColors().size() > 1) { @@ -361,14 +359,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { if (mLineBuffer.length != pointsPerEntryPair * 2) mLineBuffer = new float[pointsPerEntryPair * 2]; - for (int j = minx; - j < count; - j++) { - - if (count > 1 && j == count - 1) { - // Last point, we have already drawn a line to this point - break; - } + for (int j = minx; j <= count + minx; j++) { Entry e = dataSet.getEntryForIndex(j); if (e == null) continue; @@ -376,7 +367,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { mLineBuffer[0] = e.getX(); mLineBuffer[1] = e.getY() * phaseY; - if (j + 1 < count) { + if (j < maxx) { e = dataSet.getEntryForIndex(j + 1); @@ -421,8 +412,8 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { } else { // only one color per dataset - if (mLineBuffer.length != Math.max((entryCount - 1) * pointsPerEntryPair, pointsPerEntryPair) * 2) - mLineBuffer = new float[Math.max((entryCount - 1) * pointsPerEntryPair, pointsPerEntryPair) * 2]; + if (mLineBuffer.length != Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 2) + mLineBuffer = new float[Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 2]; Entry e1, e2; @@ -431,7 +422,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { if (e1 != null) { int j = 0; - for (int x = count > 1 ? minx + 1 : minx; x < count; x++) { + for (int x = minx; x <= count + minx; x++) { e1 = dataSet.getEntryForIndex(x == 0 ? 0 : (x - 1)); e2 = dataSet.getEntryForIndex(x); @@ -455,14 +446,11 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { if (j > 0) { trans.pointValuesToPixel(mLineBuffer); - final int size = - Math.max((count - minx - 1) * pointsPerEntryPair, pointsPerEntryPair) * - 2; + final int size = Math.max((maxx - minx + 1) * pointsPerEntryPair, pointsPerEntryPair) * 2; mRenderPaint.setColor(dataSet.getColor()); - canvas.drawLines(mLineBuffer, 0, size, - mRenderPaint); + canvas.drawLines(mLineBuffer, 0, size, mRenderPaint); } } } @@ -479,8 +467,7 @@ protected void drawLinearFill(Canvas c, ILineDataSet dataSet, int minx, int maxx, Transformer trans) { - Path filled = generateFilledPath( - dataSet, minx, maxx); + Path filled = generateFilledPath(dataSet, minx, maxx); trans.pathValueToPixel(filled); @@ -500,21 +487,23 @@ protected void drawLinearFill(Canvas c, ILineDataSet dataSet, int minx, * @param dataSet * @return */ - private Path generateFilledPath(ILineDataSet dataSet, int from, int to) { + private Path generateFilledPath(ILineDataSet dataSet, int minx, int maxx) { float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart); - float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); - final boolean isDrawSteppedEnabled = dataSet.isDrawSteppedEnabled(); + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); + final boolean isDrawSteppedEnabled = dataSet.getMode() == LineDataSet.Mode.STEPPED; + + int count = (int) ((maxx - minx) * phaseX); Path filled = new Path(); - Entry entry = dataSet.getEntryForIndex(from); + Entry entry = dataSet.getEntryForIndex(minx); filled.moveTo(entry.getX(), fillMin); filled.lineTo(entry.getX(), entry.getY() * phaseY); // create a new path - for (int x = from + 1, count = (int) Math.ceil((to - from) * phaseX + from); x < count; x++) { + for (int x = minx + 1; x <= count + minx; x++) { Entry e = dataSet.getEntryForIndex(x); @@ -529,12 +518,7 @@ private Path generateFilledPath(ILineDataSet dataSet, int from, int to) { } // close up - filled.lineTo( - dataSet.getEntryForIndex( - Math.max( - Math.min((int) Math.ceil((to - from) * phaseX + from) - 1, - dataSet.getEntryCount() - 1), 0)).getX(), fillMin); - + filled.lineTo(dataSet.getEntryForIndex(count + minx).getX(), fillMin); filled.close(); return filled; @@ -565,20 +549,14 @@ public void drawValues(Canvas c) { if (!dataSet.isDrawCirclesEnabled()) valOffset = valOffset / 2; - int entryCount = dataSet.getEntryCount(); - float low = mChart.getLowestVisibleX(); float high = mChart.getHighestVisibleX(); Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); - int diff = (entryFrom == entryTo) ? 1 : 0; - if (dataSet.getMode() == LineDataSet.Mode.CUBIC_BEZIER) - diff += 1; - - int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); - int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); + int minx = dataSet.getEntryIndex(entryFrom); + int maxx = dataSet.getEntryIndex(entryTo); float[] positions = trans.generateTransformedValuesLine( dataSet, mAnimator.getPhaseX(), mAnimator.getPhaseY(), minx, maxx); @@ -633,20 +611,15 @@ protected void drawCircles(Canvas c) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - int entryCount = dataSet.getEntryCount(); - float low = mChart.getLowestVisibleX(); float high = mChart.getHighestVisibleX(); Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); - int diff = (entryFrom == entryTo) ? 1 : 0; - if (dataSet.getMode() == LineDataSet.Mode.CUBIC_BEZIER) - diff += 1; - - int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); - int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); + int minx = dataSet.getEntryIndex(entryFrom); + int maxx = dataSet.getEntryIndex(entryTo); + int count = (int) ((maxx - minx) * phaseX); float circleRadius = dataSet.getCircleRadius(); float circleHoleRadius = dataSet.getCircleHoleRadius(); @@ -656,10 +629,7 @@ protected void drawCircles(Canvas c) { boolean drawTransparentCircleHole = drawCircleHole && dataSet.getCircleHoleColor() == ColorTemplate.COLOR_NONE; - for (int j = minx, - count = (int) Math.ceil((maxx - minx) * phaseX + minx); - j < count; - j ++) { + for (int j = minx; j <= count + minx; j++) { Entry e = dataSet.getEntryForIndex(j); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineRadarRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineRadarRenderer.java index f29c77513e..288eff64bf 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineRadarRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineRadarRenderer.java @@ -3,8 +3,6 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; @@ -31,7 +29,7 @@ protected void drawFilledPath(Canvas c, Path filledPath, Drawable drawable) { if (clipPathSupported()) { - c.save(); + int save = c.save(); c.clipPath(filledPath); drawable.setBounds((int) mViewPortHandler.contentLeft(), @@ -40,7 +38,7 @@ protected void drawFilledPath(Canvas c, Path filledPath, Drawable drawable) { (int) mViewPortHandler.contentBottom()); drawable.draw(c); - c.restore(); + c.restoreToCount(save); } else { throw new RuntimeException("Fill-drawables not (yet) supported below API level 18, " + "this code was run on API level " + Utils.getSDKInt() + "."); @@ -62,11 +60,12 @@ protected void drawFilledPath(Canvas c, Path filledPath, int fillColor, int fill if (clipPathSupported()) { - c.save(); + int save = c.save(); + c.clipPath(filledPath); c.drawColor(color); - c.restore(); + c.restoreToCount(save); } else { // save 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 5c2d98e7c7..581708640b 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 @@ -161,7 +161,7 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, public float[] generateTransformedValuesLine(ILineDataSet data, float phaseX, float phaseY, int from, int to) { - final int count = (int) Math.ceil((to - from) * phaseX) * 2; + final int count = (int) ((to - from) * phaseX + 1) * 2; float[] valuePoints = new float[count]; From 0c90c44a010e5cce2c8511ee9fd8d140c430e89a Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 9 Jun 2016 10:45:11 +0200 Subject: [PATCH 204/606] Work on improving line rendering --- .../charting/renderer/LineChartRenderer.java | 185 +++++++----------- 1 file changed, 71 insertions(+), 114 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 3510509374..ace49879cc 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 @@ -15,6 +15,7 @@ import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.PointD; @@ -115,11 +116,11 @@ protected void drawDataSet(Canvas c, ILineDataSet dataSet) { break; case CUBIC_BEZIER: - drawCubicBezier(c, dataSet); + drawCubicBezier(dataSet); break; case HORIZONTAL_BEZIER: - drawHorizontalBezier(c, dataSet); + drawHorizontalBezier(dataSet); break; } @@ -129,41 +130,27 @@ protected void drawDataSet(Canvas c, ILineDataSet dataSet) { /** * Draws a cubic line. * - * @param c * @param dataSet */ - protected void drawHorizontalBezier(Canvas c, ILineDataSet dataSet) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + protected void drawHorizontalBezier(ILineDataSet dataSet) { - int entryCount = dataSet.getEntryCount(); - - float low = mChart.getLowestVisibleX(); - float high = mChart.getHighestVisibleX(); - - Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); + float phaseY = mAnimator.getPhaseY(); - int diff = (entryFrom == entryTo) ? 1 : 0; - int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff, 0); - int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); - float phaseY = mAnimator.getPhaseY(); + XBounds bounds = getXBounds(dataSet); cubicPath.reset(); - int size = (int) Math.ceil((maxx - minx) * phaseX + minx); - - if (size - minx >= 2) { + if (bounds.range >= 1) { - Entry prev = dataSet.getEntryForIndex(minx); + Entry prev = dataSet.getEntryForIndex(bounds.min); Entry cur = prev; // let the spline start cubicPath.moveTo(cur.getX(), cur.getY() * phaseY); - for (int j = minx + 1, count = Math.min(size, entryCount); j < count; j++) { + for (int j = bounds.min + 1; j <= bounds.range + bounds.min; j++) { prev = dataSet.getEntryForIndex(j - 1); cur = dataSet.getEntryForIndex(j); @@ -184,8 +171,7 @@ protected void drawHorizontalBezier(Canvas c, ILineDataSet dataSet) { cubicFillPath.reset(); cubicFillPath.addPath(cubicPath); // create a new path, this is bad for performance - drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, - minx, size); + drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, bounds); } mRenderPaint.setColor(dataSet.getColor()); @@ -199,52 +185,40 @@ protected void drawHorizontalBezier(Canvas c, ILineDataSet dataSet) { mRenderPaint.setPathEffect(null); } - protected void drawCubicBezier(Canvas c, ILineDataSet dataSet) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - int entryCount = dataSet.getEntryCount(); - - float low = mChart.getLowestVisibleX(); - float high = mChart.getHighestVisibleX(); - - Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); - - int diff = (entryFrom == entryTo) ? 1 : 0; - int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff - 1, 0); - int maxx = Math.min(Math.max(minx + 2, dataSet.getEntryIndex(entryTo) + 1), entryCount); + 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()); + + XBounds bounds = getXBounds(dataSet); + float intensity = dataSet.getCubicIntensity(); cubicPath.reset(); - int size = (int) Math.ceil((maxx - minx) * phaseX + minx); - - if (size - minx >= 2) { + if (bounds.range >= 1) { float prevDx = 0f; float prevDy = 0f; float curDx = 0f; float curDy = 0f; - Entry prevPrev = dataSet.getEntryForIndex(minx); + Entry prevPrev = dataSet.getEntryForIndex(bounds.min); Entry prev = prevPrev; Entry cur = prev; - Entry next = dataSet.getEntryForIndex(minx + 1); + Entry next = dataSet.getEntryForIndex(bounds.min + 1); // let the spline start cubicPath.moveTo(cur.getX(), cur.getY() * phaseY); - for (int j = minx + 1, count = Math.min(size, entryCount); j < count; j++) { + for (int j = bounds.min + 1; j <= bounds.range + bounds.min; j++) { prevPrev = dataSet.getEntryForIndex(j == 1 ? 0 : j - 2); prev = dataSet.getEntryForIndex(j - 1); cur = dataSet.getEntryForIndex(j); - next = entryCount > j + 1 ? dataSet.getEntryForIndex(j + 1) : cur; + next = bounds.max > j + 1 ? dataSet.getEntryForIndex(j + 1) : cur; prevDx = (cur.getX() - prevPrev.getX()) * intensity; prevDy = (cur.getY() - prevPrev.getY()) * intensity; @@ -263,8 +237,7 @@ protected void drawCubicBezier(Canvas c, ILineDataSet dataSet) { cubicFillPath.reset(); cubicFillPath.addPath(cubicPath); // create a new path, this is bad for performance - drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, - minx, size); + drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, bounds); } mRenderPaint.setColor(dataSet.getColor()); @@ -278,26 +251,13 @@ protected void drawCubicBezier(Canvas c, ILineDataSet dataSet) { mRenderPaint.setPathEffect(null); } - protected void drawCubicFill(Canvas c, ILineDataSet dataSet, Path spline, Transformer trans, - int from, int to) { - - if (to - from <= 1) - return; + protected void drawCubicFill(Canvas c, ILineDataSet dataSet, Path spline, Transformer trans, XBounds bounds) { float fillMin = dataSet.getFillFormatter() .getFillLinePosition(dataSet, mChart); - // Take the from/to xIndex from the entries themselves, - // so missing entries won't screw up the filling. - // What we need to draw is line from points of the xIndexes - not arbitrary entry indexes! - - final Entry toEntry = dataSet.getEntryForIndex(to - 1); - final Entry fromEntry = dataSet.getEntryForIndex(from); - final float xTo = toEntry == null ? 0 : toEntry.getX(); - final float xFrom = fromEntry == null ? 0 : fromEntry.getX(); - - spline.lineTo(xTo, fillMin); - spline.lineTo(xFrom, fillMin); + spline.lineTo(bounds.min + bounds.range, fillMin); + spline.lineTo(bounds.min, fillMin); spline.close(); trans.pathValueToPixel(spline); @@ -329,7 +289,6 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); mRenderPaint.setStyle(Paint.Style.STROKE); @@ -343,15 +302,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { canvas = c; } - float low = mChart.getLowestVisibleX(); - float high = mChart.getHighestVisibleX(); - - Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); - - int minx = dataSet.getEntryIndex(entryFrom); - int maxx = dataSet.getEntryIndex(entryTo); - int count = (int) ((maxx - minx) * phaseX); + XBounds bounds = getXBounds(dataSet); // more than 1 color if (dataSet.getColors().size() > 1) { @@ -359,7 +310,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { if (mLineBuffer.length != pointsPerEntryPair * 2) mLineBuffer = new float[pointsPerEntryPair * 2]; - for (int j = minx; j <= count + minx; j++) { + for (int j = bounds.min; j <= bounds.range + bounds.min; j++) { Entry e = dataSet.getEntryForIndex(j); if (e == null) continue; @@ -367,7 +318,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { mLineBuffer[0] = e.getX(); mLineBuffer[1] = e.getY() * phaseY; - if (j < maxx) { + if (j < bounds.max) { e = dataSet.getEntryForIndex(j + 1); @@ -417,12 +368,12 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { Entry e1, e2; - e1 = dataSet.getEntryForIndex(minx); + e1 = dataSet.getEntryForIndex(bounds.min); if (e1 != null) { int j = 0; - for (int x = minx; x <= count + minx; x++) { + for (int x = bounds.min; x <= bounds.range + bounds.min; x++) { e1 = dataSet.getEntryForIndex(x == 0 ? 0 : (x - 1)); e2 = dataSet.getEntryForIndex(x); @@ -446,7 +397,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { if (j > 0) { trans.pointValuesToPixel(mLineBuffer); - final int size = Math.max((maxx - minx + 1) * pointsPerEntryPair, pointsPerEntryPair) * 2; + final int size = Math.max((bounds.range + 1) * pointsPerEntryPair, pointsPerEntryPair) * 2; mRenderPaint.setColor(dataSet.getColor()); @@ -459,15 +410,13 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { // if drawing filled is enabled if (dataSet.isDrawFilledEnabled() && entryCount > 0) { - drawLinearFill(c, dataSet, minx, maxx, trans); + drawLinearFill(c, dataSet, trans, bounds); } } - protected void drawLinearFill(Canvas c, ILineDataSet dataSet, int minx, - int maxx, - Transformer trans) { + protected void drawLinearFill(Canvas c, ILineDataSet dataSet, Transformer trans, XBounds bounds) { - Path filled = generateFilledPath(dataSet, minx, maxx); + Path filled = generateFilledPath(dataSet, bounds); trans.pathValueToPixel(filled); @@ -487,23 +436,20 @@ protected void drawLinearFill(Canvas c, ILineDataSet dataSet, int minx, * @param dataSet * @return */ - private Path generateFilledPath(ILineDataSet dataSet, int minx, int maxx) { + private Path generateFilledPath(ILineDataSet dataSet, XBounds bounds) { float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart); float phaseY = mAnimator.getPhaseY(); - float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); final boolean isDrawSteppedEnabled = dataSet.getMode() == LineDataSet.Mode.STEPPED; - int count = (int) ((maxx - minx) * phaseX); - Path filled = new Path(); - Entry entry = dataSet.getEntryForIndex(minx); + Entry entry = dataSet.getEntryForIndex(bounds.min); filled.moveTo(entry.getX(), fillMin); filled.lineTo(entry.getX(), entry.getY() * phaseY); // create a new path - for (int x = minx + 1; x <= count + minx; x++) { + for (int x = bounds.min + 1; x <= bounds.range + bounds.min; x++) { Entry e = dataSet.getEntryForIndex(x); @@ -518,7 +464,7 @@ private Path generateFilledPath(ILineDataSet dataSet, int minx, int maxx) { } // close up - filled.lineTo(dataSet.getEntryForIndex(count + minx).getX(), fillMin); + filled.lineTo(dataSet.getEntryForIndex(bounds.range + bounds.min).getX(), fillMin); filled.close(); return filled; @@ -549,17 +495,10 @@ public void drawValues(Canvas c) { if (!dataSet.isDrawCirclesEnabled()) valOffset = valOffset / 2; - float low = mChart.getLowestVisibleX(); - float high = mChart.getHighestVisibleX(); - - Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); + XBounds bounds = getXBounds(dataSet); - int minx = dataSet.getEntryIndex(entryFrom); - int maxx = dataSet.getEntryIndex(entryTo); - - float[] positions = trans.generateTransformedValuesLine( - dataSet, mAnimator.getPhaseX(), mAnimator.getPhaseY(), minx, maxx); + float[] positions = trans.generateTransformedValuesLine(dataSet, mAnimator.getPhaseX(), mAnimator + .getPhaseY(), bounds.min, bounds.max); for (int j = 0; j < positions.length; j += 2) { @@ -572,7 +511,7 @@ public void drawValues(Canvas c) { if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) continue; - Entry entry = dataSet.getEntryForIndex(j / 2 + minx); + Entry entry = dataSet.getEntryForIndex(j / 2 + bounds.min); drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, y - valOffset, dataSet.getValueTextColor(j / 2)); @@ -592,7 +531,6 @@ protected void drawCircles(Canvas c) { mRenderPaint.setStyle(Paint.Style.FILL); - float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); float[] circlesBuffer = new float[2]; @@ -611,15 +549,7 @@ protected void drawCircles(Canvas c) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - float low = mChart.getLowestVisibleX(); - float high = mChart.getHighestVisibleX(); - - Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); - - int minx = dataSet.getEntryIndex(entryFrom); - int maxx = dataSet.getEntryIndex(entryTo); - int count = (int) ((maxx - minx) * phaseX); + XBounds bounds = getXBounds(dataSet); float circleRadius = dataSet.getCircleRadius(); float circleHoleRadius = dataSet.getCircleHoleRadius(); @@ -629,7 +559,7 @@ protected void drawCircles(Canvas c) { boolean drawTransparentCircleHole = drawCircleHole && dataSet.getCircleHoleColor() == ColorTemplate.COLOR_NONE; - for (int j = minx; j <= count + minx; j++) { + for (int j = bounds.min; j <= bounds.range + bounds.min; j++) { Entry e = dataSet.getEntryForIndex(j); @@ -757,4 +687,31 @@ public void releaseBitmap() { mDrawBitmap = null; } } + + private XBounds getXBounds(IBarLineScatterCandleBubbleDataSet dataSet) { + return new XBounds(dataSet); + } + + private class XBounds { + + public final int min; + public final int max; + + public final int range; + + public XBounds(IBarLineScatterCandleBubbleDataSet dataSet) { + + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); + + float low = mChart.getLowestVisibleX(); + float high = mChart.getHighestVisibleX(); + + Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); + Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); + + min = dataSet.getEntryIndex(entryFrom); + max = dataSet.getEntryIndex(entryTo); + range = (int) ((max - min) * phaseX); + } + } } From c0bee1c1cd3542375eb28e9235ce51b72198aa6d Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 9 Jun 2016 10:52:51 +0200 Subject: [PATCH 205/606] Further abstraction of XBounds calculation --- .../BarLineScatterCandleBubbleRenderer.java | 61 +++++++++++++++++++ .../charting/renderer/LineChartRenderer.java | 50 ++++----------- .../LineScatterCandleRadarRenderer.java | 2 +- 3 files changed, 75 insertions(+), 38 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java 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 new file mode 100644 index 0000000000..9b6fdfabd6 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java @@ -0,0 +1,61 @@ +package com.github.mikephil.charting.renderer; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 09/06/16. + */ +public abstract class BarLineScatterCandleBubbleRenderer extends DataRenderer { + + + public BarLineScatterCandleBubbleRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + } + + /** + * Calculates and returns the x-bounds for the given DataSet in terms of index in their values array. This + * includes minimum and maximum visible x, as well as range. + * + * @param dataSet + * @return + */ + protected XBounds getXBounds(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet + dataSet) { + return new XBounds(chart, dataSet); + } + + /** + * Class representing the bounds of the current viewport in terms of indices in the values array of a DataSet. + */ + protected class XBounds { + + /** minimum visible entry index */ + public final int min; + + /** maximum visible entry index */ + public final int max; + + /** range of visible entry indices */ + public final int range; + + public XBounds(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet) { + + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); + + float low = chart.getLowestVisibleX(); + float high = chart.getHighestVisibleX(); + + Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); + Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); + + min = dataSet.getEntryIndex(entryFrom); + max = dataSet.getEntryIndex(entryTo); + range = (int) ((max - min) * phaseX); + } + } +} 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 ace49879cc..a61b8ad8ec 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 @@ -127,18 +127,13 @@ protected void drawDataSet(Canvas c, ILineDataSet dataSet) { mRenderPaint.setPathEffect(null); } - /** - * Draws a cubic line. - * - * @param dataSet - */ protected void drawHorizontalBezier(ILineDataSet dataSet) { float phaseY = mAnimator.getPhaseY(); Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - XBounds bounds = getXBounds(dataSet); + XBounds bounds = getXBounds(mChart, dataSet); cubicPath.reset(); @@ -192,7 +187,7 @@ protected void drawCubicBezier(ILineDataSet dataSet) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - XBounds bounds = getXBounds(dataSet); + XBounds bounds = getXBounds(mChart, dataSet); float intensity = dataSet.getCubicIntensity(); @@ -302,7 +297,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { canvas = c; } - XBounds bounds = getXBounds(dataSet); + XBounds bounds = getXBounds(mChart, dataSet); // more than 1 color if (dataSet.getColors().size() > 1) { @@ -414,6 +409,14 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { } } + /** + * Draws a filled linear path on the canvas. + * + * @param c + * @param dataSet + * @param trans + * @param bounds + */ protected void drawLinearFill(Canvas c, ILineDataSet dataSet, Transformer trans, XBounds bounds) { Path filled = generateFilledPath(dataSet, bounds); @@ -495,7 +498,7 @@ public void drawValues(Canvas c) { if (!dataSet.isDrawCirclesEnabled()) valOffset = valOffset / 2; - XBounds bounds = getXBounds(dataSet); + XBounds bounds = getXBounds(mChart, dataSet); float[] positions = trans.generateTransformedValuesLine(dataSet, mAnimator.getPhaseX(), mAnimator .getPhaseY(), bounds.min, bounds.max); @@ -549,7 +552,7 @@ protected void drawCircles(Canvas c) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - XBounds bounds = getXBounds(dataSet); + XBounds bounds = getXBounds(mChart, dataSet); float circleRadius = dataSet.getCircleRadius(); float circleHoleRadius = dataSet.getCircleHoleRadius(); @@ -687,31 +690,4 @@ public void releaseBitmap() { mDrawBitmap = null; } } - - private XBounds getXBounds(IBarLineScatterCandleBubbleDataSet dataSet) { - return new XBounds(dataSet); - } - - private class XBounds { - - public final int min; - public final int max; - - public final int range; - - public XBounds(IBarLineScatterCandleBubbleDataSet dataSet) { - - float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); - - float low = mChart.getLowestVisibleX(); - float high = mChart.getHighestVisibleX(); - - Entry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); - - min = dataSet.getEntryIndex(entryFrom); - max = dataSet.getEntryIndex(entryTo); - range = (int) ((max - min) * phaseX); - } - } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java index 122e90f80c..53f54cbfd9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java @@ -10,7 +10,7 @@ /** * Created by Philipp Jahoda on 11/07/15. */ -public abstract class LineScatterCandleRadarRenderer extends DataRenderer { +public abstract class LineScatterCandleRadarRenderer extends BarLineScatterCandleBubbleRenderer { /** * path that is used for drawing highlight-lines (drawLines(...) cannot be used because of dashes) From 455ff894cbfe1022a76ce53d1f31b3c6affe6366 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 9 Jun 2016 12:04:01 +0200 Subject: [PATCH 206/606] Fixes related to minmax and bubblechart rendering --- .../mpchartexample/BubbleChartActivity.java | 6 +- .../mikephil/charting/data/BarDataSet.java | 4 +- .../mikephil/charting/data/BubbleDataSet.java | 32 ++------- .../mikephil/charting/data/CandleDataSet.java | 4 +- .../renderer/BubbleChartRenderer.java | 67 ++++++------------- .../charting/renderer/LineChartRenderer.java | 2 - .../mikephil/charting/utils/Transformer.java | 5 +- 7 files changed, 33 insertions(+), 87 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java index 63107114b5..fb46034d5f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -73,7 +73,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setMaxVisibleValueCount(200); mChart.setPinchZoom(true); - mSeekBarX.setProgress(5); + mSeekBarX.setProgress(1); mSeekBarY.setProgress(50); Legend l = mChart.getLegend(); @@ -132,7 +132,6 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } case R.id.actionSave: { - // mChart.saveToGallery("title"+System.currentTimeMillis()); mChart.saveToPath("title" + System.currentTimeMillis(), ""); break; } @@ -156,7 +155,7 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - int count = mSeekBarX.getProgress() + 1; + int count = mSeekBarX.getProgress(); int range = mSeekBarY.getProgress(); tvX.setText("" + count); @@ -205,6 +204,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // create a data object with the datasets BubbleData data = new BubbleData(dataSets); + data.setDrawValues(false); data.setValueTypeface(tf); data.setValueTextSize(8f); data.setValueTextColor(Color.WHITE); 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 6cc820ceb4..2802ebc4c2 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 @@ -111,9 +111,9 @@ public void calcMinMax() { if (mValues == null || mValues.isEmpty()) return; - mYMax = Float.MIN_VALUE; + mYMax = -Float.MAX_VALUE; mYMin = Float.MAX_VALUE; - mXMax = Float.MIN_VALUE; + mXMax = -Float.MAX_VALUE; mXMin = Float.MAX_VALUE; for (BarEntry e : mValues) { 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 6814adc8a1..ec288123e2 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 @@ -34,38 +34,16 @@ public void calcMinMax() { if (mValues == null || mValues.isEmpty()) return; - mYMax = Float.MIN_VALUE; + mYMax = -Float.MAX_VALUE; mYMin = Float.MAX_VALUE; - mXMax = Float.MIN_VALUE; + mXMax = -Float.MAX_VALUE; mXMin = Float.MAX_VALUE; - // need chart width to guess this properly + for (BubbleEntry e : mValues) { - for (BubbleEntry entry : mValues) { + calcMinMax(e); - float ymin = entry.getY(); - float ymax = entry.getY(); - - if (ymin < mYMin) { - mYMin = ymin; - } - - if (ymax > mYMax) { - mYMax = ymax; - } - - final float xmin = entry.getX(); - final float xmax = entry.getX(); - - if (xmin < mXMin) { - mXMin = xmin; - } - - if (xmax > mXMax) { - mXMax = xmax; - } - - final float size = entry.getSize(); + final float size = e.getSize(); 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 2331b3e589..124a6a3253 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 @@ -105,9 +105,9 @@ public void calcMinMax() { if (mValues == null || mValues.isEmpty()) return; - mYMax = Float.MIN_VALUE; + mYMax = -Float.MAX_VALUE; mYMin = Float.MAX_VALUE; - mXMax = Float.MIN_VALUE; + mXMax = -Float.MAX_VALUE; mXMin = Float.MAX_VALUE; for (CandleEntry e : mValues) { 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 47542dc591..b23cf3721e 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 @@ -8,7 +8,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.data.DataSet; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; @@ -22,7 +21,7 @@ * Bubble chart implementation: Copyright 2015 Pierre-Marc Airoldi Licensed * under Apache License 2.0 Ported by Daniel Cohen Gindi */ -public class BubbleChartRenderer extends DataRenderer { +public class BubbleChartRenderer extends BarLineScatterCandleBubbleRenderer { protected BubbleDataProvider mChart; @@ -57,13 +56,9 @@ public void drawData(Canvas c) { private float[] sizeBuffer = new float[4]; private float[] pointBuffer = new float[2]; - protected float getShapeSize(float entrySize, - float maxSize, - float reference, - boolean normalizeSize) { - final float factor = normalizeSize - ? ((maxSize == 0f) ? 1f : (float) Math.sqrt(entrySize / maxSize)) - : entrySize; + protected float getShapeSize(float entrySize, float maxSize, float reference, boolean normalizeSize) { + final float factor = normalizeSize ? ((maxSize == 0f) ? 1f : (float) Math.sqrt(entrySize / maxSize)) : + entrySize; final float shapeSize = reference * factor; return shapeSize; } @@ -72,17 +67,9 @@ protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); - float low = mChart.getLowestVisibleX(); - float high = mChart.getHighestVisibleX(); - - BubbleEntry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); - BubbleEntry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); - - int minx = Math.max(dataSet.getEntryIndex(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount()); + XBounds bounds = getXBounds(mChart, dataSet); sizeBuffer[0] = 0f; sizeBuffer[2] = 1f; @@ -96,12 +83,12 @@ protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { final float maxBubbleHeight = Math.abs(mViewPortHandler.contentBottom() - mViewPortHandler.contentTop()); final float referenceSize = Math.min(maxBubbleHeight, maxBubbleWidth); - for (int j = minx; j < maxx; j++) { + for (int j = bounds.min; j <= bounds.range + bounds.min; j++) { final BubbleEntry entry = dataSet.getEntryForIndex(j); - pointBuffer[0] = (float) (entry.getX() - minx) * phaseX + (float) minx; - pointBuffer[1] = (float) (entry.getY()) * phaseY; + pointBuffer[0] = entry.getX(); + pointBuffer[1] = (entry.getY()) * phaseY; trans.pointValuesToPixel(pointBuffer); float shapeHalf = getShapeSize(entry.getSize(), dataSet.getMaxSize(), referenceSize, normalizeSize) / 2f; @@ -151,23 +138,16 @@ public void drawValues(Canvas c) { final float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); final float phaseY = mAnimator.getPhaseY(); - float low = mChart.getLowestVisibleX(); - float high = mChart.getHighestVisibleX(); - - BubbleEntry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); - BubbleEntry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); - - int minx = dataSet.getEntryIndex(entryFrom); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount()); + XBounds bounds = getXBounds(mChart, dataSet); final float[] positions = mChart.getTransformer(dataSet.getAxisDependency()) - .generateTransformedValuesBubble(dataSet, phaseX, phaseY, minx, maxx); + .generateTransformedValuesBubble(dataSet, phaseY, bounds.min, bounds.max); final float alpha = phaseX == 1 ? phaseY : phaseX; for (int j = 0; j < positions.length; j += 2) { - int valueTextColor = dataSet.getValueTextColor(j / 2 + minx); + int valueTextColor = dataSet.getValueTextColor(j / 2 + bounds.min); valueTextColor = Color.argb(Math.round(255.f * alpha), Color.red(valueTextColor), Color.green(valueTextColor), Color.blue(valueTextColor)); @@ -180,7 +160,7 @@ public void drawValues(Canvas c) { if ((!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y))) continue; - BubbleEntry entry = dataSet.getEntryForIndex(j / 2 + minx); + BubbleEntry entry = dataSet.getEntryForIndex(j / 2 + bounds.min); drawValue(c, dataSet.getValueFormatter(), entry.getSize(), entry, i, x, y + (0.5f * lineHeight), valueTextColor); @@ -200,7 +180,6 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { BubbleData bubbleData = mChart.getBubbleData(); - float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); for (Highlight high : indices) { @@ -213,9 +192,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { : (high.getDataSetIndex() + 1); if (maxDataSetIndex - minDataSetIndex < 1) continue; - for (int dataSetIndex = minDataSetIndex; - dataSetIndex < maxDataSetIndex; - dataSetIndex++) { + for (int dataSetIndex = minDataSetIndex; dataSetIndex < maxDataSetIndex; dataSetIndex++) { IBubbleDataSet dataSet = bubbleData.getDataSetByIndex(dataSetIndex); @@ -223,17 +200,11 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { continue; final BubbleEntry entry = (BubbleEntry) bubbleData.getEntryForHighlight(high); - if (entry == null || entry.getX() != high.getX()) - continue; - - float low = mChart.getLowestVisibleX(); - float highX = mChart.getHighestVisibleX(); - BubbleEntry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); - BubbleEntry entryTo = dataSet.getEntryForXPos(highX, DataSet.Rounding.UP); + if (entry == null) + continue; - int minx = dataSet.getEntryIndex(entryFrom); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount()); + XBounds bounds = getXBounds(mChart, dataSet); Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); @@ -250,8 +221,8 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { mViewPortHandler.contentBottom() - mViewPortHandler.contentTop()); final float referenceSize = Math.min(maxBubbleHeight, maxBubbleWidth); - pointBuffer[0] = (float) (entry.getX() - minx) * phaseX + (float) minx; - pointBuffer[1] = (float) (entry.getY()) * phaseY; + pointBuffer[0] = entry.getX(); + pointBuffer[1] = (entry.getY()) * phaseY; trans.pointValuesToPixel(pointBuffer); float shapeHalf = getShapeSize(entry.getSize(), @@ -269,7 +240,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) break; - if (high.getX() < minx || high.getX() >= maxx) + if (high.getX() < bounds.min || high.getX() > bounds.max) continue; final int originalColor = dataSet.getColor((int) entry.getX()); 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 a61b8ad8ec..ac0e57d2de 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 @@ -9,13 +9,11 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.data.DataSet; 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.dataprovider.LineDataProvider; -import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.PointD; 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 581708640b..6d16b2107e 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 @@ -129,8 +129,7 @@ public float[] generateTransformedValuesScatter(IScatterDataSet data, * @param data * @return */ - public float[] generateTransformedValuesBubble(IBubbleDataSet data, - float phaseX, float phaseY, int from, int to) { + public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY, int from, int to) { final int count = (int) Math.ceil(to - from) * 2; // (int) Math.ceil((to - from) * phaseX) * 2; @@ -141,7 +140,7 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, Entry e = data.getEntryForIndex(j / 2 + from); if (e != null) { - valuePoints[j] = (float) (e.getX() - from) * phaseX + from; + valuePoints[j] = e.getX(); valuePoints[j + 1] = e.getY() * phaseY; } } From d507234cc84f064e4be475b8422f903657cbe942 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 9 Jun 2016 12:10:15 +0200 Subject: [PATCH 207/606] Further improve rendering x-bounds calculation --- .../charting/renderer/BarChartRenderer.java | 1 - .../renderer/CandleStickChartRenderer.java | 33 ++++--------------- .../renderer/CombinedChartRenderer.java | 3 +- 3 files changed, 8 insertions(+), 29 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 9ec7ad3aab..27cfb77aa5 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 @@ -98,7 +98,6 @@ protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { buffer.setDataSet(index); buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); buffer.setBarWidth(mChart.getBarData().getBarWidth()); - //buffer.setInterval(mChart.getXRange() / dataSet.getEntryCount()); buffer.feed(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 998668e863..9ced6efa05 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 @@ -7,8 +7,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.data.DataSet; -import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; @@ -58,29 +56,21 @@ protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float phaseY = mAnimator.getPhaseY(); float barSpace = dataSet.getBarSpace(); boolean showCandleBar = dataSet.getShowCandleBar(); - float lowX = mChart.getLowestVisibleX(); - float highX = mChart.getHighestVisibleX(); - - CandleEntry entryFrom = dataSet.getEntryForXPos(lowX, DataSet.Rounding.DOWN); - CandleEntry entryTo = dataSet.getEntryForXPos(highX, DataSet.Rounding.UP); - - int minx = dataSet.getEntryIndex(entryFrom); - int maxx = dataSet.getEntryIndex(entryTo); + XBounds bounds = getXBounds(mChart, dataSet); mRenderPaint.setStrokeWidth(dataSet.getShadowWidth()); // draw the body - for (int j = minx; j <= maxx * phaseX; j++) { + for (int j = bounds.min; j <= bounds.range + bounds.min; j++) { // get the entry CandleEntry e = dataSet.getEntryForIndex(j); - if(e == null) + if (e == null) continue; final float xPos = e.getX(); @@ -256,7 +246,6 @@ else if (open < close) mCloseBuffers[0], mCloseBuffers[1], mCloseBuffers[2], mCloseBuffers[3], mRenderPaint); - } } } @@ -281,17 +270,10 @@ public void drawValues(Canvas c) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - float low = mChart.getLowestVisibleX(); - float high = mChart.getHighestVisibleX(); - - CandleEntry entryFrom = dataSet.getEntryForXPos(low, DataSet.Rounding.DOWN); - CandleEntry entryTo = dataSet.getEntryForXPos(high, DataSet.Rounding.UP); - - int minx = dataSet.getEntryIndex(entryFrom); - int maxx = dataSet.getEntryIndex(entryTo); + XBounds bounds = getXBounds(mChart, dataSet); float[] positions = trans.generateTransformedValuesCandle( - dataSet, mAnimator.getPhaseX(), mAnimator.getPhaseY(), minx, maxx); + dataSet, mAnimator.getPhaseX(), mAnimator.getPhaseY(), bounds.min, bounds.max); float yOffset = Utils.convertDpToPixel(5f); @@ -306,7 +288,7 @@ public void drawValues(Canvas c) { if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) continue; - CandleEntry entry = dataSet.getEntryForIndex(j / 2 + minx); + CandleEntry entry = dataSet.getEntryForIndex(j / 2 + bounds.min); drawValue(c, dataSet.getValueFormatter(), entry.getHigh(), entry, i, x, y - yOffset, dataSet .getValueTextColor(j / 2)); @@ -348,7 +330,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { CandleEntry e = set.getEntryForXPos(x); - if (e == null || e.getX() != x) + if (e == null) continue; float lowValue = e.getLow() * mAnimator.getPhaseY(); @@ -362,5 +344,4 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } } } - } 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 be8d98e476..7718b95e2c 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 @@ -124,8 +124,7 @@ else if (renderer instanceof ScatterChartRenderer) else if (renderer instanceof BubbleChartRenderer) data = ((BubbleChartRenderer)renderer).mChart.getBubbleData(); - int dataIndex = data == null - ? -1 + int dataIndex = data == null ? -1 : ((CombinedData)chart.getData()).getAllData().indexOf(data); ArrayList dataIndices = new ArrayList<>(); From 6cb3034b0ca39f787729823d7511bbe9486e0f11 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 9 Jun 2016 12:17:03 +0200 Subject: [PATCH 208/606] Fix minmax calculations for realm implementation --- .../data/realm/base/RealmBaseDataSet.java | 37 ++++---------- .../realm/implementation/RealmBarDataSet.java | 20 ++------ .../implementation/RealmBubbleDataSet.java | 48 +++---------------- .../implementation/RealmCandleDataSet.java | 20 ++------ 4 files changed, 22 insertions(+), 103 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java index 3e06759f66..7a55da224d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java @@ -28,24 +28,24 @@ public abstract class RealmBaseDataSet e protected List mValues; /** - * maximum y-value in the y-value array + * maximum y-value in the value array */ - protected float mYMax = 0.0f; + protected float mYMax = -Float.MAX_VALUE; /** - * the minimum y-value in the y-value array + * minimum y-value in the value array */ - protected float mYMin = 0.0f; + protected float mYMin = Float.MAX_VALUE; /** * maximum x-value in the value array */ - protected float mXMax = 0.0f; + protected float mXMax = -Float.MAX_VALUE; /** * minimum x-value in the value array */ - protected float mXMin = 0.0f; + protected float mXMin = Float.MAX_VALUE; /** * fieldname of the column that contains the y-values of this dataset @@ -130,33 +130,16 @@ public int getEntryCount() { @Override public void calcMinMax() { - if (mValues == null) - return; - - if (mValues.size() == 0) + if (mValues == null || mValues.isEmpty()) return; - mYMin = Float.MAX_VALUE; mYMax = -Float.MAX_VALUE; - - mXMin = Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; mXMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; for (S e : mValues) { - - if (e != null && !Float.isNaN(e.getY())) { - calcMinMax(e); - } - } - - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; - } - - if (mXMin == Float.MAX_VALUE) { - mXMin = 0.f; - mXMax = 0.f; + calcMinMax(e); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java index f74379f5a6..13254d28c7 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java @@ -107,17 +107,13 @@ public BarEntry buildEntryFromResultObject(T realmObject, float x) { @Override public void calcMinMax() { - if (mValues == null) + if (mValues == null || mValues.isEmpty()) return; - if (mValues.size() == 0) - return; - - mYMin = Float.MAX_VALUE; mYMax = -Float.MAX_VALUE; - - mXMin = Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; mXMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; for (BarEntry e : mValues) { @@ -146,16 +142,6 @@ public void calcMinMax() { mXMax = e.getX(); } } - - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; - } - - if (mXMin == Float.MAX_VALUE) { - mXMin = 0.f; - mXMax = 0.f; - } } private void calcStackSize() { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java index 05a7ff523d..27c4b308e0 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java @@ -65,60 +65,24 @@ public BubbleEntry buildEntryFromResultObject(T realmObject, float x) { @Override public void calcMinMax() { - if (mValues == null) + if (mValues == null || mValues.isEmpty()) return; - if (mValues.size() == 0) - return; - - mYMin = Float.MAX_VALUE; mYMax = -Float.MAX_VALUE; - - mXMin = Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; mXMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; - // need chart width to guess this properly - - for (BubbleEntry entry : mValues) { - - float ymin = entry.getY(); - float ymax = entry.getY(); - - if (ymin < mYMin) { - mYMin = ymin; - } - - if (ymax > mYMax) { - mYMax = ymax; - } - - final float xmin = entry.getX(); - final float xmax = entry.getX(); - - if (xmin < mXMin) { - mXMin = xmin; - } + for (BubbleEntry e : mValues) { - if (xmax > mXMax) { - mXMax = xmax; - } + calcMinMax(e); - final float size = entry.getSize(); + final float size = e.getSize(); if (size > mMaxSize) { mMaxSize = size; } } - - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; - } - - if(mXMin == Float.MAX_VALUE) { - mXMin = 0.f; - mXMax = 0.f; - } } @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java index 1328fb1e4f..1c08290656 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java @@ -137,17 +137,13 @@ public CandleEntry buildEntryFromResultObject(T realmObject, float x) { @Override public void calcMinMax() { - if (mValues == null) + if (mValues == null || mValues.isEmpty()) return; - if (mValues.size() == 0) - return; - - mYMin = Float.MAX_VALUE; mYMax = -Float.MAX_VALUE; - - mXMin = Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; mXMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; for (CandleEntry e : mValues) { @@ -163,16 +159,6 @@ public void calcMinMax() { if (e.getX() > mXMax) mXMax = e.getX(); } - - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; - } - - if (mXMin == Float.MAX_VALUE) { - mXMin = 0.f; - mXMax = 0.f; - } } /** From 2e5217edefc80ccbe3307d5a3cdff07adeb64337 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 9 Jun 2016 12:23:58 +0200 Subject: [PATCH 209/606] Cleanup realm --- .../realm/RealmDatabaseActivityBar.java | 2 +- .../realm/RealmDatabaseActivityBubble.java | 2 +- .../realm/RealmDatabaseActivityLine.java | 2 +- .../realm/RealmDatabaseActivityPie.java | 2 +- .../realm/RealmDatabaseActivityRadar.java | 2 +- .../realm/RealmDatabaseActivityScatter.java | 3 +-- .../realm/RealmWikiExample.java | 2 -- .../realm/implementation/RealmBarData.java | 21 ------------------- .../realm/implementation/RealmBubbleData.java | 21 ------------------- .../realm/implementation/RealmCandleData.java | 21 ------------------- .../realm/implementation/RealmLineData.java | 21 ------------------- .../realm/implementation/RealmPieData.java | 19 ----------------- .../realm/implementation/RealmRadarData.java | 21 ------------------- .../implementation/RealmScatterData.java | 21 ------------------- 14 files changed, 6 insertions(+), 154 deletions(-) delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java index a03a1ad959..54e4c55bfd 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java @@ -50,7 +50,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet(result, "xValue", "value"); // stacked entries + RealmBarDataSet set = new RealmBarDataSet(result, "xValue", "yValue"); // stacked entries set.setColors(new int[] {ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); set.setLabel("Realm BarDataSet"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java index 24e3931e6b..c2c2003d2e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java @@ -53,7 +53,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); - RealmBubbleDataSet set = new RealmBubbleDataSet(result, "xValue", "value", "bubbleSize"); + RealmBubbleDataSet set = new RealmBubbleDataSet(result, "xValue", "yValue", "bubbleSize"); set.setLabel("Realm BubbleDataSet"); set.setColors(ColorTemplate.COLORFUL_COLORS, 110); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java index b73d122855..4e04cf09df 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java @@ -54,7 +54,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); - RealmLineDataSet set = new RealmLineDataSet(result, "xValue", "value"); + RealmLineDataSet set = new RealmLineDataSet(result, "xValue", "yValue"); set.setDrawCubic(false); set.setLabel("Realm LineDataSet"); set.setDrawCircleHole(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java index 8a2ca4545a..8bcda98356 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java @@ -54,7 +54,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmPieDataSet set = new RealmPieDataSet(result, "value", "label"); // stacked entries + RealmPieDataSet set = new RealmPieDataSet(result, "yValue", "label"); // stacked entries 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 b4cb14840f..fe408bdc00 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java @@ -56,7 +56,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmRadarDataSet set = new RealmRadarDataSet(result, "xValue", "value"); // stacked entries + RealmRadarDataSet set = new RealmRadarDataSet(result, "xValue", "yValue"); // stacked entries set.setLabel("Realm RadarDataSet"); set.setDrawFilled(true); set.setColor(ColorTemplate.rgb("#009688")); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java index caa0190922..d89545a949 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java @@ -6,7 +6,6 @@ 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.RealmScatterData; import com.github.mikephil.charting.data.realm.implementation.RealmScatterDataSet; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -54,7 +53,7 @@ private void setData() { RealmResults result = mRealm.allObjects(RealmDemoData.class); - RealmScatterDataSet set = new RealmScatterDataSet(result, "xValue", "value"); + RealmScatterDataSet set = new RealmScatterDataSet(result, "xValue", "yValue"); set.setLabel("Realm ScatterDataSet"); set.setScatterShapeSize(9f); set.setColor(ColorTemplate.rgb("#CDDC39")); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java index 06408f6878..c31e6b238c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java @@ -9,9 +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.realm.implementation.RealmBarData; import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; -import com.github.mikephil.charting.data.realm.implementation.RealmLineData; import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; import com.github.mikephil.charting.formatter.AxisValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java deleted file mode 100644 index a230385032..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; - -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmBarData extends BarData { - - public RealmBarData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { - super(dataSets); - //RealmUtils.toXVals(result, xPositionField, xLabelField) - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java deleted file mode 100644 index ec23ec516b..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.BubbleData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; - -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmBubbleData extends BubbleData { - - public RealmBubbleData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { - super(dataSets); - ////RealmUtils.toXVals(result, xPositionField, xLabelField) - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java deleted file mode 100644 index fbe2cb6a92..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.CandleData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; - -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmCandleData extends CandleData { - - public RealmCandleData(RealmResults result,String xPositionField, String xLabelField, List dataSets) { - super(dataSets); - //RealmUtils.toXVals(result, xPositionField, xLabelField) - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java deleted file mode 100644 index cee63fa6cc..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; - -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmLineData extends LineData { - - public RealmLineData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { - super(dataSets); - //RealmUtils.toXVals(result, xPositionField, xLabelField) - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java deleted file mode 100644 index 3a08452813..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.PieData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmPieData extends PieData { - - public RealmPieData(RealmResults result,String xPositionField, String xLabelField, IPieDataSet dataSet) { - super(dataSet); - //RealmUtils.toXVals(result, xPositionField, xLabelField) - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java deleted file mode 100644 index e2a1d95811..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.RadarData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; - -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmRadarData extends RadarData{ - - public RealmRadarData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { - super(dataSets); - //RealmUtils.toXVals(result, xPositionField, xLabelField) - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java deleted file mode 100644 index 480455c30c..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.ScatterData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; - -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmScatterData extends ScatterData { - - public RealmScatterData(RealmResults result, String xPositionField, String xLabelField, List dataSets) { - super(dataSets); - //RealmUtils.toXVals(result, xPositionField, xLabelField) - } -} From df91cedee3108d56498d4613ff4d9fe47d4ec858 Mon Sep 17 00:00:00 2001 From: wajdi chamakhi Date: Thu, 9 Jun 2016 12:09:16 +0100 Subject: [PATCH 210/606] Adding control on text Boldness in XAxis, YAxis, Limit Legends --- .../charting/components/ComponentBase.java | 23 +++++++++++++++++++ .../charting/renderer/LegendRenderer.java | 2 ++ .../charting/renderer/XAxisRenderer.java | 4 ++++ .../XAxisRendererHorizontalBarChart.java | 3 +++ .../renderer/XAxisRendererRadarChart.java | 1 + .../charting/renderer/YAxisRenderer.java | 2 ++ .../YAxisRendererHorizontalBarChart.java | 4 +++- .../renderer/YAxisRendererRadarChart.java | 1 + 8 files changed, 39 insertions(+), 1 deletion(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java b/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java index 713f89dd79..5c0c596508 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java @@ -43,6 +43,11 @@ public abstract class ComponentBase { */ protected int mTextColor = Color.BLACK; + /** + * the text style boldness {Default = false} + */ + protected boolean mTextBold = false; + public ComponentBase() { } @@ -130,6 +135,24 @@ public float getTextSize() { return mTextSize; } + /** + * set the text to be bold + * + * @param bold + */ + public void setTextBold(boolean bold) { + mTextBold = bold; + } + + /** + * returns the text boldness + * + * @return + */ + public boolean getTextBold() { + return mTextBold; + } + /** * Sets the text color to use for the labels. Make sure to use * getResources().getColor(...) when using a color from the resources. diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java index f005a1f2ac..a35653446e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -167,6 +167,7 @@ public void computeLegend(ChartData data) { mLegendLabelPaint.setTypeface(tf); mLegendLabelPaint.setTextSize(mLegend.getTextSize()); + mLegendLabelPaint.setFakeBoldText(mLegend.getTextBold()); mLegendLabelPaint.setColor(mLegend.getTextColor()); // calculate all dimensions of the mLegend @@ -184,6 +185,7 @@ public void renderLegend(Canvas c) { mLegendLabelPaint.setTypeface(tf); mLegendLabelPaint.setTextSize(mLegend.getTextSize()); + mLegendLabelPaint.setFakeBoldText(mLegend.getTextBold()); mLegendLabelPaint.setColor(mLegend.getTextColor()); float labelLineHeight = Utils.getLineHeight(mLegendLabelPaint); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java index f3e0ac40b3..a8b6056f81 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -36,6 +36,7 @@ public void computeAxis(float xValMaximumLength, List xValues) { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + mAxisLabelPaint.setFakeBoldText(mXAxis.getTextBold()); StringBuilder widthText = new StringBuilder(); @@ -82,6 +83,7 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + mAxisLabelPaint.setFakeBoldText(mXAxis.getTextBold()); mAxisLabelPaint.setColor(mXAxis.getTextColor()); if (mXAxis.getPosition() == XAxisPosition.TOP) { @@ -293,6 +295,8 @@ public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position mLimitLinePaint.setColor(limitLine.getTextColor()); mLimitLinePaint.setStrokeWidth(0.5f); mLimitLinePaint.setTextSize(limitLine.getTextSize()); + mLimitLinePaint.setFakeBoldText(limitLine.getTextBold()); + float xOffset = limitLine.getLineWidth() + limitLine.getXOffset(); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java index e862263520..0fe0f46ad8 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java @@ -31,6 +31,7 @@ public void computeAxis(float xValAverageLength, List xValues) { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + mAxisLabelPaint.setFakeBoldText(mXAxis.getTextBold()); mXAxis.setValues(xValues); String longest = mXAxis.getLongestLabel(); @@ -61,6 +62,7 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + mAxisLabelPaint.setFakeBoldText(mXAxis.getTextBold()); mAxisLabelPaint.setColor(mXAxis.getTextColor()); if (mXAxis.getPosition() == XAxisPosition.TOP) { @@ -237,6 +239,7 @@ public void renderLimitLines(Canvas c) { mLimitLinePaint.setColor(l.getTextColor()); mLimitLinePaint.setStrokeWidth(0.5f); mLimitLinePaint.setTextSize(l.getTextSize()); + mLimitLinePaint.setFakeBoldText(l.getTextBold()); final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); float xOffset = Utils.convertDpToPixel(4f) + l.getXOffset(); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java index 50a2fc7dd4..228e818d83 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java @@ -30,6 +30,7 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + mAxisLabelPaint.setFakeBoldText(mXAxis.getTextBold()); mAxisLabelPaint.setColor(mXAxis.getTextColor()); float sliceangle = mChart.getSliceAngle(); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java index fdae353e4b..9e37d71e00 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -193,6 +193,7 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); + mAxisLabelPaint.setFakeBoldText(mYAxis.getTextBold()); mAxisLabelPaint.setColor(mYAxis.getTextColor()); float xoffset = mYAxis.getXOffset(); @@ -381,6 +382,7 @@ public void renderLimitLines(Canvas c) { mLimitLinePaint.setTypeface(l.getTypeface()); mLimitLinePaint.setStrokeWidth(0.5f); mLimitLinePaint.setTextSize(l.getTextSize()); + mLimitLinePaint.setFakeBoldText(l.getTextBold()); final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); float xOffset = Utils.convertDpToPixel(4f) + l.getXOffset(); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java index bcd63778a0..098542f3fc 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java @@ -7,7 +7,6 @@ import android.graphics.Path; import com.github.mikephil.charting.components.LimitLine; -import com.github.mikephil.charting.components.LimitLine.LimitLabelPosition; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; @@ -78,6 +77,7 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); + mAxisLabelPaint.setFakeBoldText(mYAxis.getTextBold()); mAxisLabelPaint.setColor(mYAxis.getTextColor()); mAxisLabelPaint.setTextAlign(Align.CENTER); @@ -140,6 +140,7 @@ protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, flo mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); + mAxisLabelPaint.setFakeBoldText(mYAxis.getTextBold()); mAxisLabelPaint.setColor(mYAxis.getTextColor()); for (int i = 0; i < mYAxis.mEntryCount; i++) { @@ -243,6 +244,7 @@ public void renderLimitLines(Canvas c) { mLimitLinePaint.setTypeface(l.getTypeface()); mLimitLinePaint.setStrokeWidth(0.5f); mLimitLinePaint.setTextSize(l.getTextSize()); + mLimitLinePaint.setFakeBoldText(l.getTextBold()); float xOffset = l.getLineWidth() + l.getXOffset(); float yOffset = Utils.convertDpToPixel(2f) + l.getYOffset(); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java index 66ddd3ceea..220a4735d2 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -137,6 +137,7 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); + mAxisLabelPaint.setFakeBoldText(mYAxis.getTextBold()); mAxisLabelPaint.setColor(mYAxis.getTextColor()); PointF center = mChart.getCenterOffsets(); From e5af5692e4689061c2a98bda8df1b10998222e82 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 9 Jun 2016 18:04:01 +0200 Subject: [PATCH 211/606] Improvements to example --- .../mpchartexample/BarChartActivity.java | 2 +- .../mpchartexample/ScrollViewActivity.java | 1 + .../mpchartexample/StackedBarActivity.java | 1 + .../custom/DayAxisValueFormatter.java | 5 +---- .../fragments/ComplexityFragment.java | 1 - .../mpchartexample/fragments/SimpleFragment.java | 16 ++-------------- .../mikephil/charting/utils/FileUtils.java | 2 +- 7 files changed, 7 insertions(+), 21 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 448b0f8ba5..410ffbdc82 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -224,7 +224,7 @@ public void onStopTrackingTouch(SeekBar seekBar) { private void setData(int count, float range) { - float start = 1f; + float start = 0f; mChart.getXAxis().setAxisMinValue(start); mChart.getXAxis().setAxisMaxValue(start + count + 2); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java index f04d843904..617a25b2e0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java @@ -45,6 +45,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.getLegend().setEnabled(false); setData(10); + mChart.setFitBars(true); } private void setData(int count) { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 24038acbe9..ae17135bad 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -210,6 +210,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mChart.setData(data); } + mChart.setFitBars(true); mChart.invalidate(); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java index 4299f48bce..11e411a7c9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -26,9 +26,6 @@ public String getFormattedValue(float value, AxisBase axis) { int days = (int) value; - if (days == 0) - return ""; - int year = determineYear(days); int month = determineMonth(days); @@ -72,7 +69,7 @@ public String getFormattedValue(float value, AxisBase axis) { break; } - return dayOfMonth + appendix + " " + monthName; + return dayOfMonth == 0 ? "" : dayOfMonth + appendix + " " + monthName; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java index 8213f4f017..98b904b2b7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java @@ -41,7 +41,6 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setTypeface(tf); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) mChart.getAxisRight().setEnabled(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java index 2e837b1a19..3d4d9893be 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java @@ -54,7 +54,7 @@ protected BarData generateBarData(int dataSets, float range, int count) { // entries = FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "stacked_bars.txt"); for(int j = 0; j < count; j++) { - entries.add(new BarEntry((float) (Math.random() * range) + range / 4, j)); + entries.add(new BarEntry(j, (float) (Math.random() * range) + range / 4)); } BarDataSet ds = new BarDataSet(entries, getLabel(i)); @@ -78,7 +78,7 @@ protected ScatterData generateScatterData(int dataSets, float range, int count) ArrayList entries = new ArrayList(); for(int j = 0; j < count; j++) { - entries.add(new Entry((float) (Math.random() * range) + range / 4, j)); + entries.add(new Entry(j, (float) (Math.random() * range) + range / 4)); } ScatterDataSet ds = new ScatterDataSet(entries, getLabel(i)); @@ -122,11 +122,6 @@ protected PieData generatePieData() { protected LineData generateLineData() { -// DataSet ds1 = new DataSet(n, "O(n)"); -// DataSet ds2 = new DataSet(nlogn, "O(nlogn)"); -// DataSet ds3 = new DataSet(nsquare, "O(n\u00B2)"); -// DataSet ds4 = new DataSet(nthree, "O(n\u00B3)"); - ArrayList sets = new ArrayList(); LineDataSet ds1 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "sine.txt"), "Sine function"); @@ -145,13 +140,6 @@ protected LineData generateLineData() { sets.add(ds1); sets.add(ds2); -// sets.add(FileUtils.dataSetFromAssets(getActivity().getAssets(), "n.txt")); -// sets.add(FileUtils.dataSetFromAssets(getActivity().getAssets(), "nlogn.txt")); -// sets.add(FileUtils.dataSetFromAssets(getActivity().getAssets(), "square.txt")); -// sets.add(FileUtils.dataSetFromAssets(getActivity().getAssets(), "three.txt")); - - int max = Math.max(sets.get(0).getEntryCount(), sets.get(1).getEntryCount()); - LineData d = new LineData(sets); d.setValueTypeface(tf); return d; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java index dac8573d5d..5aff51ff84 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java @@ -122,7 +122,7 @@ public static List loadEntriesFromAssets(AssetManager am, String path) { String[] split = line.split("#"); if (split.length <= 2) { - entries.add(new Entry(Float.parseFloat(split[0]), Integer.parseInt(split[1]))); + entries.add(new Entry(Float.parseFloat(split[1]), Float.parseFloat(split[0]))); } else { float[] vals = new float[split.length - 1]; From b0f5ec1bf1ede58cfa02fff176a4dd896d390827 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 9 Jun 2016 18:17:32 +0200 Subject: [PATCH 212/606] Example cleanup and improvements --- .../mpchartexample/AnotherBarActivity.java | 8 -------- .../mpchartexample/BarChartActivity.java | 13 ++++--------- .../BarChartActivityMultiDataset.java | 17 ++++------------- .../mpchartexample/BarChartActivitySinus.java | 11 +++-------- .../mpchartexample/BubbleChartActivity.java | 13 ++++--------- .../CandleStickChartActivity.java | 8 -------- .../mpchartexample/CombinedChartActivity.java | 1 + .../mpchartexample/CubicLineChartActivity.java | 9 ++------- .../HorizontalBarChartActivity.java | 13 ++++--------- .../mpchartexample/LineChartActivity2.java | 11 ++++------- .../mpchartexample/LineChartTime.java | 10 ++-------- .../ListViewBarChartActivity.java | 12 ++++-------- .../mpchartexample/PieChartActivity.java | 8 ++------ .../mpchartexample/RadarChartActivitry.java | 16 +++++----------- .../RealtimeLineChartActivity.java | 8 +++----- .../mpchartexample/ScatterChartActivity.java | 12 ++++-------- .../mpchartexample/notimportant/DemoBase.java | 15 +++++++++++++++ 17 files changed, 61 insertions(+), 124 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java index 4b12f8c98c..7e867af488 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -73,14 +73,6 @@ protected void onCreate(Bundle savedInstanceState) { mChart.animateY(2500); mChart.getLegend().setEnabled(false); - - // Legend l = mChart.getLegend(); - // l.setPosition(LegendPosition.BELOW_CHART_CENTER); - // l.setFormSize(8f); - // l.setFormToTextSpace(4f); - // l.setXEntrySpace(6f); - - // mChart.setDrawLegend(false); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 410ffbdc82..57d28ce0fb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -4,7 +4,6 @@ import android.annotation.SuppressLint; import android.graphics.PointF; import android.graphics.RectF; -import android.graphics.Typeface; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -47,8 +46,6 @@ public class BarChartActivity extends DemoBase implements OnSeekBarChangeListene private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - private Typeface mTf; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -80,11 +77,9 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); // mChart.setDrawYLabels(false); - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setTypeface(mTf); + xAxis.setTypeface(mTfLight); xAxis.setDrawGridLines(false); xAxis.setGranularity(1f); // only intervals of 1 day xAxis.setLabelCount(7); @@ -93,7 +88,7 @@ protected void onCreate(Bundle savedInstanceState) { AxisValueFormatter custom = new MyAxisValueFormatter(); YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(mTf); + leftAxis.setTypeface(mTfLight); leftAxis.setLabelCount(8, false); leftAxis.setValueFormatter(custom); leftAxis.setPosition(YAxisLabelPosition.OUTSIDE_CHART); @@ -102,7 +97,7 @@ protected void onCreate(Bundle savedInstanceState) { YAxis rightAxis = mChart.getAxisRight(); rightAxis.setDrawGridLines(false); - rightAxis.setTypeface(mTf); + rightAxis.setTypeface(mTfLight); rightAxis.setLabelCount(8, false); rightAxis.setValueFormatter(custom); rightAxis.setSpaceTop(15f); @@ -254,7 +249,7 @@ private void setData(int count, float range) { BarData data = new BarData(dataSets); data.setValueTextSize(10f); - data.setValueTypeface(mTf); + data.setValueTypeface(mTfLight); data.setBarWidth(0.9f); mChart.setData(data); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index e49e2ecadd..e43c4f33d1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.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; @@ -38,8 +37,6 @@ public class BarChartActivityMultiDataset extends DemoBase implements OnSeekBarC private BarChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - - private Typeface tf; @Override protected void onCreate(Bundle savedInstanceState) { @@ -74,27 +71,21 @@ protected void onCreate(Bundle savedInstanceState) { // to use for it MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - // define an offset to change the original position of the marker - // (optional) - // mv.setOffsets(-mv.getMeasuredWidth() / 2, -mv.getMeasuredHeight()); - // set the marker to the chart mChart.setMarkerView(mv); mSeekBarX.setProgress(10); mSeekBarY.setProgress(100); - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - Legend l = mChart.getLegend(); l.setPosition(LegendPosition.RIGHT_OF_CHART_INSIDE); - l.setTypeface(tf); + l.setTypeface(mTfLight); l.setYOffset(0f); l.setYEntrySpace(0f); l.setTextSize(8f); XAxis xl = mChart.getXAxis(); - xl.setTypeface(tf); + xl.setTypeface(mTfLight); xl.setGranularity(1f); xl.setCenterAxisLabels(true); xl.setValueFormatter(new AxisValueFormatter() { @@ -110,7 +101,7 @@ public int getDecimalDigits() { }); YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); + leftAxis.setTypeface(mTfLight); leftAxis.setValueFormatter(new LargeValueFormatter()); leftAxis.setDrawGridLines(false); leftAxis.setSpaceTop(30f); @@ -253,7 +244,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { data.setValueFormatter(new LargeValueFormatter()); // add space between the dataset groups in percent of bar-width - data.setValueTypeface(tf); + data.setValueTypeface(mTfLight); mChart.setData(data); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java index 31937a7a31..a61c357b13 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -2,7 +2,6 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; -import android.graphics.Typeface; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; @@ -34,8 +33,6 @@ public class BarChartActivitySinus extends DemoBase implements OnSeekBarChangeLi private SeekBar mSeekBarX; private TextView tvX; - private Typeface mTf; - private List mSinusData; @Override @@ -73,13 +70,11 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); // mChart.setDrawYLabels(false); - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - XAxis xAxis = mChart.getXAxis(); xAxis.setEnabled(false); YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(mTf); + leftAxis.setTypeface(mTfLight); leftAxis.setLabelCount(6, false); leftAxis.setAxisMinValue(-2.5f); leftAxis.setAxisMaxValue(2.5f); @@ -88,7 +83,7 @@ protected void onCreate(Bundle savedInstanceState) { YAxis rightAxis = mChart.getAxisRight(); rightAxis.setDrawGridLines(false); - rightAxis.setTypeface(mTf); + rightAxis.setTypeface(mTfLight); rightAxis.setLabelCount(6, false); rightAxis.setAxisMinValue(-2.5f); rightAxis.setAxisMaxValue(2.5f); @@ -222,7 +217,7 @@ private void setData(int count) { BarData data = new BarData(set); data.setValueTextSize(10f); - data.setValueTypeface(mTf); + data.setValueTypeface(mTfLight); data.setDrawValues(false); data.setBarWidth(0.8f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java index fb46034d5f..72e09797f5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.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; @@ -36,8 +35,6 @@ public class BubbleChartActivity extends DemoBase implements OnSeekBarChangeList private BubbleChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - - private Typeface tf; @Override protected void onCreate(Bundle savedInstanceState) { @@ -58,8 +55,6 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (BubbleChart) findViewById(R.id.chart1); mChart.setDescription(""); - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); @@ -78,10 +73,10 @@ protected void onCreate(Bundle savedInstanceState) { Legend l = mChart.getLegend(); l.setPosition(LegendPosition.RIGHT_OF_CHART); - l.setTypeface(tf); + l.setTypeface(mTfLight); YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(tf); + yl.setTypeface(mTfLight); yl.setSpaceTop(30f); yl.setSpaceBottom(30f); yl.setDrawZeroLine(false); @@ -90,7 +85,7 @@ protected void onCreate(Bundle savedInstanceState) { XAxis xl = mChart.getXAxis(); xl.setPosition(XAxis.XAxisPosition.BOTTOM); - xl.setTypeface(tf); + xl.setTypeface(mTfLight); } @Override @@ -205,7 +200,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // create a data object with the datasets BubbleData data = new BubbleData(dataSets); data.setDrawValues(false); - data.setValueTypeface(tf); + data.setValueTypeface(mTfLight); data.setValueTextSize(8f); data.setValueTextColor(Color.WHITE); data.setHighlightCircleWidth(1.5f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java index 51e7aa6bf9..1a9d899624 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -80,14 +80,6 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarY.setProgress(100); mChart.getLegend().setEnabled(false); - - // Legend l = mChart.getLegend(); - // l.setPosition(LegendPosition.BELOW_CHART_CENTER); - // l.setFormSize(8f); - // l.setFormToTextSpace(4f); - // l.setXEntrySpace(6f); - - // mChart.setDrawLegend(false); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index aacced57f3..49adb19e7f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -95,6 +95,7 @@ public int getDecimalDigits() { data.setData(generateBubbleData()); data.setData(generateScatterData()); data.setData(generateCandleData()); + data.setValueTypeface(mTfLight); xAxis.setAxisMaxValue(data.getXMax() + 0.25f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index 10e874ac48..a775b5d8fb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -2,7 +2,6 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; -import android.graphics.Typeface; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; @@ -32,8 +31,6 @@ public class CubicLineChartActivity extends DemoBase implements OnSeekBarChangeL private LineChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - - private Typeface tf; @Override protected void onCreate(Bundle savedInstanceState) { @@ -73,13 +70,11 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - XAxis x = mChart.getXAxis(); x.setEnabled(false); YAxis y = mChart.getAxisLeft(); - y.setTypeface(tf); + y.setTypeface(mTfLight); y.setLabelCount(6, false); y.setTextColor(Color.WHITE); y.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); @@ -307,7 +302,7 @@ public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProv // create a data object with the datasets LineData data = new LineData(set1); - data.setValueTypeface(tf); + data.setValueTypeface(mTfLight); data.setValueTextSize(9f); data.setDrawValues(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 301501f77f..5151b48d85 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -4,7 +4,6 @@ import android.annotation.SuppressLint; import android.graphics.PointF; import android.graphics.RectF; -import android.graphics.Typeface; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -40,8 +39,6 @@ public class HorizontalBarChartActivity extends DemoBase implements OnSeekBarCha private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - private Typeface tf; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -77,25 +74,23 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - XAxis xl = mChart.getXAxis(); xl.setPosition(XAxisPosition.BOTTOM); - xl.setTypeface(tf); + xl.setTypeface(mTfLight); xl.setDrawAxisLine(true); xl.setDrawGridLines(false); xl.setGridLineWidth(0.3f); xl.setGranularity(10f); YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(tf); + yl.setTypeface(mTfLight); yl.setDrawAxisLine(true); yl.setDrawGridLines(true); yl.setAxisMinValue(0f); // this replaces setStartAtZero(true) // yl.setInverted(true); YAxis yr = mChart.getAxisRight(); - yr.setTypeface(tf); + yr.setTypeface(mTfLight); yr.setDrawAxisLine(true); yr.setDrawGridLines(false); yr.setAxisMinValue(0f); // this replaces setStartAtZero(true) @@ -247,7 +242,7 @@ private void setData(int count, float range) { BarData data = new BarData(dataSets); data.setValueTextSize(10f); - data.setValueTypeface(tf); + data.setValueTypeface(mTfLight); data.setBarWidth(barWidth); mChart.setData(data); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index e114db8521..24972f36ff 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.Typeface; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -86,29 +85,27 @@ protected void onCreate(Bundle savedInstanceState) { mChart.animateX(2500); - Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - // get the legend (only possible after setting data) Legend l = mChart.getLegend(); // modify the legend ... // l.setPosition(LegendPosition.LEFT_OF_CHART); l.setForm(LegendForm.LINE); - l.setTypeface(tf); + l.setTypeface(mTfLight); l.setTextSize(11f); l.setTextColor(Color.WHITE); l.setPosition(LegendPosition.BELOW_CHART_LEFT); // l.setYOffset(11f); XAxis xAxis = mChart.getXAxis(); - xAxis.setTypeface(tf); + xAxis.setTypeface(mTfLight); xAxis.setTextSize(11f); xAxis.setTextColor(Color.WHITE); xAxis.setDrawGridLines(false); xAxis.setDrawAxisLine(false); YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); + leftAxis.setTypeface(mTfLight); leftAxis.setTextColor(ColorTemplate.getHoloBlue()); leftAxis.setAxisMaxValue(200f); leftAxis.setAxisMinValue(0f); @@ -116,7 +113,7 @@ protected void onCreate(Bundle savedInstanceState) { leftAxis.setGranularityEnabled(true); YAxis rightAxis = mChart.getAxisRight(); - rightAxis.setTypeface(tf); + rightAxis.setTypeface(mTfLight); rightAxis.setTextColor(Color.RED); rightAxis.setAxisMaxValue(900); rightAxis.setAxisMinValue(-200); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 5ab32778eb..7d833625aa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -2,11 +2,9 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; -import android.graphics.Typeface; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; -import android.view.View; import android.view.WindowManager; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; @@ -16,8 +14,6 @@ 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.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; @@ -79,15 +75,13 @@ protected void onCreate(Bundle savedInstanceState) { setData(100, 30); mChart.invalidate(); - Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf"); - // get the legend (only possible after setting data) Legend l = mChart.getLegend(); l.setEnabled(false); XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxis.XAxisPosition.TOP_INSIDE); - xAxis.setTypeface(tf); + xAxis.setTypeface(mTfLight); xAxis.setTextSize(10f); xAxis.setTextColor(Color.WHITE); xAxis.setDrawAxisLine(false); @@ -112,7 +106,7 @@ public int getDecimalDigits() { YAxis leftAxis = mChart.getAxisLeft(); leftAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); - leftAxis.setTypeface(tf); + leftAxis.setTypeface(mTfLight); leftAxis.setTextColor(ColorTemplate.getHoloBlue()); leftAxis.setDrawGridLines(true); leftAxis.setGranularityEnabled(true); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index 84360bd7b5..c3eb7fbf5e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java @@ -56,12 +56,8 @@ protected void onCreate(Bundle savedInstanceState) { private class ChartDataAdapter extends ArrayAdapter { - private Typeface mTf; - public ChartDataAdapter(Context context, List objects) { super(context, 0, objects); - - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); } @Override @@ -86,23 +82,23 @@ public View getView(int position, View convertView, ViewGroup parent) { } // apply styling - data.setValueTypeface(mTf); + data.setValueTypeface(mTfLight); data.setValueTextColor(Color.BLACK); holder.chart.setDescription(""); holder.chart.setDrawGridBackground(false); XAxis xAxis = holder.chart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setTypeface(mTf); + xAxis.setTypeface(mTfLight); xAxis.setDrawGridLines(false); YAxis leftAxis = holder.chart.getAxisLeft(); - leftAxis.setTypeface(mTf); + leftAxis.setTypeface(mTfLight); leftAxis.setLabelCount(5, false); leftAxis.setSpaceTop(15f); YAxis rightAxis = holder.chart.getAxisRight(); - rightAxis.setTypeface(mTf); + rightAxis.setTypeface(mTfLight); rightAxis.setLabelCount(5, false); rightAxis.setSpaceTop(15f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 38d4799b99..c33fb9aadf 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -39,8 +39,6 @@ public class PieChartActivity extends DemoBase implements OnSeekBarChangeListene private PieChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - - private Typeface tf; @Override protected void onCreate(Bundle savedInstanceState) { @@ -67,9 +65,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDragDecelerationFrictionCoef(0.95f); - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - mChart.setCenterTextTypeface(Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf")); + mChart.setCenterTextTypeface(mTfLight); mChart.setCenterText(generateCenterSpannableText()); mChart.setDrawHoleEnabled(true); @@ -228,7 +224,7 @@ private void setData(int count, float range) { data.setValueFormatter(new PercentFormatter()); data.setValueTextSize(11f); data.setValueTextColor(Color.WHITE); - data.setValueTypeface(tf); + data.setValueTypeface(mTfLight); mChart.setData(data); // undo all highlights diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index 17866470b1..0d3cacf35a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -2,7 +2,6 @@ package com.xxmassdeveloper.mpchartexample; import android.graphics.Color; -import android.graphics.Typeface; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; @@ -24,8 +23,6 @@ import com.github.mikephil.charting.formatter.AxisValueFormatter; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; import com.xxmassdeveloper.mpchartexample.custom.RadarMarkerView; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -34,7 +31,6 @@ public class RadarChartActivitry extends DemoBase { private RadarChart mChart; - private Typeface tf; @Override protected void onCreate(Bundle savedInstanceState) { @@ -43,10 +39,8 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_radarchart_noseekbar); - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf"); - TextView tv = (TextView) findViewById(R.id.textView); - tv.setTypeface(tf); + tv.setTypeface(mTfLight); tv.setTextColor(Color.WHITE); tv.setBackgroundColor(Color.rgb(60, 65, 82)); @@ -76,7 +70,7 @@ protected void onCreate(Bundle savedInstanceState) { Easing.EasingOption.EaseInOutQuad); XAxis xAxis = mChart.getXAxis(); - xAxis.setTypeface(tf); + xAxis.setTypeface(mTfLight); xAxis.setTextSize(9f); xAxis.setYOffset(0f); xAxis.setXOffset(0f); @@ -96,7 +90,7 @@ public int getDecimalDigits() { xAxis.setTextColor(Color.WHITE); YAxis yAxis = mChart.getYAxis(); - yAxis.setTypeface(tf); + yAxis.setTypeface(mTfLight); yAxis.setLabelCount(5, false); yAxis.setTextSize(9f); yAxis.setAxisMinValue(0f); @@ -105,7 +99,7 @@ public int getDecimalDigits() { Legend l = mChart.getLegend(); l.setPosition(LegendPosition.ABOVE_CHART_CENTER); - l.setTypeface(tf); + l.setTypeface(mTfLight); l.setXEntrySpace(7f); l.setYEntrySpace(5f); l.setTextColor(Color.WHITE); @@ -255,7 +249,7 @@ public void setData() { sets.add(set2); RadarData data = new RadarData(sets); - data.setValueTypeface(tf); + data.setValueTypeface(mTfLight); data.setValueTextSize(8f); data.setDrawValues(false); data.setValueTextColor(Color.WHITE); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 2ec8b19e45..ab72539792 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -64,26 +64,24 @@ protected void onCreate(Bundle savedInstanceState) { // add empty data mChart.setData(data); - Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - // get the legend (only possible after setting data) Legend l = mChart.getLegend(); // modify the legend ... // l.setPosition(LegendPosition.LEFT_OF_CHART); l.setForm(LegendForm.LINE); - l.setTypeface(tf); + l.setTypeface(mTfLight); l.setTextColor(Color.WHITE); XAxis xl = mChart.getXAxis(); - xl.setTypeface(tf); + xl.setTypeface(mTfLight); xl.setTextColor(Color.WHITE); xl.setDrawGridLines(false); xl.setAvoidFirstLastClipping(true); xl.setEnabled(true); YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); + leftAxis.setTypeface(mTfLight); leftAxis.setTextColor(Color.WHITE); leftAxis.setAxisMaxValue(100f); leftAxis.setAxisMinValue(0f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index 7c5652f109..a0b3103e1e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -35,8 +35,6 @@ public class ScatterChartActivity extends DemoBase implements OnSeekBarChangeLis private ScatterChart mChart; private SeekBar mSeekBarX, mSeekBarY; private TextView tvX, tvY; - - private Typeface tf; @Override protected void onCreate(Bundle savedInstanceState) { @@ -57,8 +55,6 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (ScatterChart) findViewById(R.id.chart1); mChart.setDescription(""); - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - mChart.setOnChartValueSelectedListener(this); mChart.setDrawGridBackground(false); @@ -77,16 +73,16 @@ protected void onCreate(Bundle savedInstanceState) { Legend l = mChart.getLegend(); l.setPosition(LegendPosition.RIGHT_OF_CHART); - l.setTypeface(tf); + l.setTypeface(mTfLight); YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(tf); + yl.setTypeface(mTfLight); yl.setAxisMinValue(0f); // this replaces setStartAtZero(true) mChart.getAxisRight().setEnabled(false); XAxis xl = mChart.getXAxis(); - xl.setTypeface(tf); + xl.setTypeface(mTfLight); xl.setDrawGridLines(false); } @@ -205,7 +201,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // create a data object with the datasets ScatterData data = new ScatterData(dataSets); - data.setValueTypeface(tf); + data.setValueTypeface(mTfLight); mChart.setData(data); mChart.invalidate(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java index 7141be0aaa..59b73b1ad7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java @@ -1,6 +1,10 @@ package com.xxmassdeveloper.mpchartexample.notimportant; +import android.graphics.Typeface; +import android.os.Bundle; +import android.renderscript.Type; +import android.support.annotation.Nullable; import android.support.v4.app.FragmentActivity; import com.xxmassdeveloper.mpchartexample.R; @@ -23,6 +27,17 @@ public abstract class DemoBase extends FragmentActivity { "Party Y", "Party Z" }; + protected Typeface mTfRegular; + protected Typeface mTfLight; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mTfRegular = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + mTfLight = Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf"); + } + protected float getRandom(float range, float startsfrom) { return (float) (Math.random() * range) + startsfrom; } From 95dd249cf349778ff111193d61d64e08016a8843 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 10 Jun 2016 14:12:44 +0200 Subject: [PATCH 213/606] Fixes related to slice space #1857, #1856, #1807, #1699, #1665, #1660, ... --- .../mpchartexample/PieChartActivity.java | 9 ++++---- .../charting/renderer/BarChartRenderer.java | 3 +-- .../charting/renderer/PieChartRenderer.java | 21 +++++++++++++++++-- .../charting/utils/ViewPortHandler.java | 12 ++++++++++- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index c33fb9aadf..d050f2d410 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -163,7 +163,8 @@ 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.EasingOption + .EaseInCubic); break; } } @@ -183,16 +184,16 @@ private void setData(int count, float range) { float mult = range; - ArrayList yVals1 = new ArrayList(); + ArrayList values = new ArrayList(); // IMPORTANT: In a PieChart, no values (Entry) should have the same // xIndex (even if from different DataSets), since no values can be // drawn above each other. for (int i = 0; i < count + 1; i++) { - yVals1.add(new PieEntry((float) ((Math.random() * mult) + mult / 5), mParties[i % mParties.length])); + values.add(new PieEntry((float) ((Math.random() * mult) + mult / 5), mParties[i % mParties.length])); } - PieDataSet dataSet = new PieDataSet(yVals1, "Election Results"); + PieDataSet dataSet = new PieDataSet(values, "Election Results"); dataSet.setSliceSpace(3f); dataSet.setSelectionShift(5f); 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 27cfb77aa5..1698113de3 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 @@ -235,8 +235,7 @@ public void drawValues(Canvas c) { drawValue(c, dataSet.getValueFormatter(), val, entry, i, x, val >= 0 ? (buffer.buffer[j + 1] + posOffset) : (buffer.buffer[j + 3] + negOffset), - dataSet.getValueTextColor - (j / 4)); + dataSet.getValueTextColor(j / 4)); } // if we have stacks 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 4c8fa78747..58973f0158 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 @@ -178,6 +178,22 @@ protected float calculateMinimumRadiusForSpacedSlice( return spacedRadius; } + /** + * Calculates the sliceSpace to use based on visible values and their size compared to the set sliceSpace. + * + * @param dataSet + * @return + */ + protected float getSliceSpace(IPieDataSet dataSet) { + + float spaceSizeRatio = dataSet.getSliceSpace() / mViewPortHandler.getSmallestContentExtension(); + float minValueRatio = dataSet.getYMin() / mChart.getData().getYValueSum() * 2; + + float sliceSpace = spaceSizeRatio > minValueRatio ? 0f : dataSet.getSliceSpace(); + + return sliceSpace; + } + protected void drawDataSet(Canvas c, IPieDataSet dataSet) { float angle = 0; @@ -205,7 +221,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { } } - final float sliceSpace = visibleAngleCount <= 1 ? 0.f : dataSet.getSliceSpace(); + final float sliceSpace = visibleAngleCount <= 1 ? 0.f : getSliceSpace(dataSet); for (int j = 0; j < entryCount; j++) { @@ -411,6 +427,8 @@ public void drawValues(Canvas c) { float offset = Utils.convertDpToPixel(5.f); + final float sliceSpace = getSliceSpace(dataSet); + for (int j = 0; j < entryCount; j++) { PieEntry entry = dataSet.getEntryForIndex(j); @@ -421,7 +439,6 @@ public void drawValues(Canvas c) { angle = absoluteAngles[xIndex - 1] * phaseX; final float sliceAngle = drawAngles[xIndex]; - final float sliceSpace = dataSet.getSliceSpace(); final float sliceSpaceMiddleAngle = sliceSpace / (Utils.FDEG2RAD * labelRadius); // offset needed to center the drawn text in the slice 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 47ff9aa02e..e7cfac56b3 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 @@ -8,7 +8,8 @@ import android.view.View; /** - * Class that contains information about the charts current viewport settings, including offsets, scale & translation levels, ... + * Class that contains information about the charts current viewport settings, including offsets, scale & translation + * levels, ... * * @author Philipp Jahoda */ @@ -173,6 +174,15 @@ public float getChartWidth() { return mChartWidth; } + /** + * Returns the smallest extension of the content rect (width or height). + * + * @return + */ + public float getSmallestContentExtension() { + return Math.min(mContentRect.width(), mContentRect.height()); + } + /** * ################ ################ ################ ################ */ From 4826f3be24e2b36f4d0f3a294536628d381bb7cb Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 10 Jun 2016 15:52:17 +0200 Subject: [PATCH 214/606] Improved combined example, add grouped bars --- .../mpchartexample/CombinedChartActivity.java | 39 +++++++++++++------ .../mikephil/charting/charts/BarChart.java | 3 +- .../mikephil/charting/data/BarData.java | 3 +- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 49adb19e7f..5034bd126b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -132,21 +132,36 @@ private LineData generateLineData() { private BarData generateBarData() { - BarData d = new BarData(); - d.setBarWidth(0.9f); + ArrayList entries1 = new ArrayList(); + ArrayList entries2 = new ArrayList(); - ArrayList entries = new ArrayList(); + for (int index = 0; index < itemcount; index++) { + entries1.add(new BarEntry(0, getRandom(25, 25))); + entries2.add(new BarEntry(0, getRandom(25, 25))); + } - for (int index = 0; index < itemcount; index++) - entries.add(new BarEntry(index + 0.5f, getRandom(25, 25))); + BarDataSet set1 = new BarDataSet(entries1, "Bar 1"); + set1.setColor(Color.rgb(60, 220, 78)); + set1.setValueTextColor(Color.rgb(60, 220, 78)); + set1.setValueTextSize(10f); + set1.setAxisDependency(YAxis.AxisDependency.LEFT); - BarDataSet set = new BarDataSet(entries, "Bar DataSet"); - set.setColor(Color.rgb(60, 220, 78)); - set.setValueTextColor(Color.rgb(60, 220, 78)); - set.setValueTextSize(10f); - d.addDataSet(set); + BarDataSet set2 = new BarDataSet(entries2, "Bar 2"); + set2.setColor(Color.rgb(61, 165, 255)); + set2.setValueTextColor(Color.rgb(61, 165, 255)); + set2.setValueTextSize(10f); + set2.setAxisDependency(YAxis.AxisDependency.LEFT); - set.setAxisDependency(YAxis.AxisDependency.LEFT); + float groupSpace = 0.06f; + float barSpace = 0.02f; // x2 dataset + float barWidth = 0.45f; // x2 dataset + // (0.45 + 0.02) * 2 + 0.06 = 1.00 -> interval per "group" + + BarData d = new BarData(set1, set2); + d.setBarWidth(barWidth); + + // make this BarData object grouped + d.groupBars(0, groupSpace, barSpace); // start at x = 0 return d; } @@ -198,7 +213,7 @@ protected BubbleData generateBubbleData() { for (int index = 0; index < itemcount; index++) { float y = getRandom(10, 105); - float size = getRandom(50, 105); + float size = getRandom(100, 105); entries.add(new BubbleEntry(index + 0.5f, y, size)); } 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 c5bd2c8fdc..60b15cc8c2 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 @@ -201,7 +201,8 @@ public void setFitBars(boolean enabled) { /** * Groups all BarDataSet objects this data object holds together by modifying the x-position of their entries. - * Leaves space as specified by the parameters. + * Previously set x-positions of entries will be overwritten. Leaves space between bars and groups as specified + * by the parameters. * Calls notifyDataSetChanged() afterwards. * * @param fromX the starting point on the x-axis where the grouping should begin 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 92109c7a2c..2cc22c8b72 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 @@ -45,7 +45,8 @@ public float getBarWidth() { /** * Groups all BarDataSet objects this data object holds together by modifying the x-position of their entries. - * Leaves space as specified by the parameters. + * Previously set x-positions 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. * * @param fromX the starting point on the x-axis where the grouping should begin From 694eb49162d06193067bb07f23456c865cc40a9e Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 10 Jun 2016 16:03:19 +0200 Subject: [PATCH 215/606] Fix issue in LegendRenderer --- .../mikephil/charting/data/CandleDataSet.java | 8 +++---- .../charting/renderer/LegendRenderer.java | 21 ++++++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) 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 124a6a3253..5288d7e3a6 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 @@ -55,23 +55,23 @@ public class CandleDataSet extends LineScatterCandleRadarDataSet im /** * color for open == close */ - protected int mNeutralColor = ColorTemplate.COLOR_NONE; + protected int mNeutralColor = ColorTemplate.COLOR_SKIP; /** * color for open < close */ - protected int mIncreasingColor = ColorTemplate.COLOR_NONE; + protected int mIncreasingColor = ColorTemplate.COLOR_SKIP; /** * color for open > close */ - protected int mDecreasingColor = ColorTemplate.COLOR_NONE; + protected int mDecreasingColor = ColorTemplate.COLOR_SKIP; /** * shadow line color, set -1 for backward compatibility and uses default * color */ - protected int mShadowColor = ColorTemplate.COLOR_NONE; + protected int mShadowColor = ColorTemplate.COLOR_SKIP; public CandleDataSet(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 9092f42857..c3eccae292 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,6 +2,7 @@ package com.github.mikephil.charting.renderer; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Typeface; @@ -124,10 +125,15 @@ public void computeLegend(ChartData data) { labels.add(pds.getLabel()); } - } else if(dataSet instanceof ICandleDataSet && ((ICandleDataSet) dataSet).getDecreasingColor() != ColorTemplate.COLOR_NONE) { + } else if (dataSet instanceof ICandleDataSet && ((ICandleDataSet) dataSet).getDecreasingColor() != + ColorTemplate.COLOR_NONE) { + + int decreasingColor = ((ICandleDataSet) dataSet).getDecreasingColor(); + colors.add(decreasingColor); + + int increasingColor = ((ICandleDataSet) dataSet).getIncreasingColor(); + colors.add(increasingColor); - colors.add(((ICandleDataSet) dataSet).getDecreasingColor()); - colors.add(((ICandleDataSet) dataSet).getIncreasingColor()); labels.add(null); labels.add(dataSet.getLabel()); @@ -252,7 +258,7 @@ public void renderLegend(Canvas c) { : mLegend.mNeededWidth / 2.0 - xoffset); } - break; + break; } switch (orientation) { @@ -288,8 +294,8 @@ public void renderLegend(Canvas c) { } if (posX == originPosX && - horizontalAlignment == Legend.LegendHorizontalAlignment.CENTER && - lineIndex < calculatedLineSizes.length) { + horizontalAlignment == Legend.LegendHorizontalAlignment.CENTER && + lineIndex < calculatedLineSizes.length) { posX += (direction == Legend.LegendDirection.RIGHT_TO_LEFT ? calculatedLineSizes[lineIndex].width : -calculatedLineSizes[lineIndex].width) / 2.f; @@ -311,7 +317,8 @@ public void renderLegend(Canvas c) { if (!isStacked) { if (drawingForm) - posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -formToTextSpace : formToTextSpace; + posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -formToTextSpace : + formToTextSpace; if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) posX -= calculatedLabelSizes[i].width; From 82ca5e7e91fec244dc33c35650c9abf4d73f3c7a Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 10 Jun 2016 16:55:13 +0200 Subject: [PATCH 216/606] Highlighter cleanup --- .../charting/highlight/BarHighlighter.java | 18 +---- .../charting/highlight/ChartHighlighter.java | 76 ++++++++++++------- .../highlight/CombinedHighlighter.java | 12 +-- .../highlight/HorizontalBarHighlighter.java | 2 +- 4 files changed, 55 insertions(+), 53 deletions(-) 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 1d23073740..0b40dc0062 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 @@ -1,17 +1,11 @@ package com.github.mikephil.charting.highlight; -import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.SelectionDetail; -import com.github.mikephil.charting.utils.Utils; - -import java.util.List; /** * Created by Philipp Jahoda on 22/07/15. @@ -60,11 +54,7 @@ public Highlight getHighlight(float x, float y) { * @param yValue * @return */ - protected Highlight getStackedHighlight( - SelectionDetail selectionDetail, - IBarDataSet set, - float xVal, - double yValue) { + protected Highlight getStackedHighlight(SelectionDetail selectionDetail, IBarDataSet set, float xVal, double yValue) { BarEntry entry = set.getEntryForXPos(xVal); @@ -159,7 +149,7 @@ protected Range[] getRanges(BarEntry entry) { } @Override - protected float getDistance(float x, float y, float selX, float selY) { - return Math.abs(x - selX); + protected float getDistance(float x1, float y1, float x2, float y2) { + return Math.abs(x1 - x2); } } 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 bc9a2aa84c..99a9c757eb 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 @@ -10,7 +10,6 @@ import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.SelectionDetail; -import com.github.mikephil.charting.utils.Utils; /** * Created by Philipp Jahoda on 21/07/15. @@ -69,14 +68,14 @@ protected PointD getValsForTouch(float x, float y) { */ protected SelectionDetail getSelectionDetail(float xVal, float x, float y) { - List valsAtIndex = getSelectionDetailsAtIndex(xVal); + List closestValues = getSelectionDetailsAtXPos(xVal); - float leftdist = getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.LEFT); - float rightdist = getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.RIGHT); + float leftAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.LEFT); + float rightAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.RIGHT); - YAxis.AxisDependency axis = leftdist < rightdist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT; + YAxis.AxisDependency axis = leftAxisMinDist < rightAxisMinDist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT; - SelectionDetail detail = getClosestSelectionDetailByPixel(valsAtIndex, x, y, axis, mChart + SelectionDetail detail = getClosestSelectionDetailByPixel(closestValues, x, y, axis, mChart .getMaxHighlightDistance()); return detail; @@ -86,26 +85,24 @@ protected SelectionDetail getSelectionDetail(float xVal, float x, float y) { * Returns the minimum distance from a touch value (in pixels) to the * closest value (in pixels) that is displayed in the chart. * - * @param valsAtIndex + * @param closestValues * @param pos * @param axis * @return */ - protected float getMinimumDistance(List valsAtIndex, - float pos, - YAxis.AxisDependency axis) { + protected float getMinimumDistance(List closestValues, float pos, YAxis.AxisDependency axis) { float distance = Float.MAX_VALUE; - for (int i = 0; i < valsAtIndex.size(); i++) { + for (int i = 0; i < closestValues.size(); i++) { - SelectionDetail sel = valsAtIndex.get(i); + SelectionDetail sel = closestValues.get(i); if (sel.dataSet.getAxisDependency() == axis) { - float cdistance = Math.abs(getSelectionPos(sel) - pos); - if (cdistance < distance) { - distance = cdistance; + float tempDistance = Math.abs(getSelectionPos(sel) - pos); + if (tempDistance < distance) { + distance = tempDistance; } } } @@ -118,12 +115,13 @@ protected float getSelectionPos(SelectionDetail sel) { } /** - * Returns a list of SelectionDetail object corresponding to the given xVal. + * Returns a list of SelectionDetail objects representing the entries closest to the given xVal. + * The returned list contains two objects per DataSet (closest rounding up, closest rounding down). * * @param xVal * @return */ - protected List getSelectionDetailsAtIndex(float xVal) { + protected List getSelectionDetailsAtXPos(float xVal) { List vals = new ArrayList(); @@ -137,14 +135,23 @@ protected List getSelectionDetailsAtIndex(float xVal) { if (!dataSet.isHighlightEnabled()) continue; - vals.add(getDetails(dataSet, i, xVal, DataSet.Rounding.UP)); - vals.add(getDetails(dataSet, i, xVal, DataSet.Rounding.DOWN)); + vals.add(getDetail(dataSet, i, xVal, DataSet.Rounding.UP)); + vals.add(getDetail(dataSet, i, xVal, DataSet.Rounding.DOWN)); } return vals; } - protected SelectionDetail getDetails(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + /** + * Returns the SelectionDetail object corresponding to the selected xValue and dataSetIndex. + * + * @param set + * @param dataSetIndex + * @param xVal + * @param rounding + * @return + */ + protected SelectionDetail getDetail(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { final Entry e = set.getEntryForXPos(xVal, rounding); @@ -160,20 +167,22 @@ protected SelectionDetail getDetails(IDataSet set, int dataSetIndex, float xVal, * Returns the SelectionDetail of the DataSet that contains the closest value on the * y-axis. * - * @param valsAtIndex all the values at a specific index + * @param closestValues contains two values per DataSet closest to the selected x-position (determined by rounding up and + * down) + * @param x + * @param y + * @param axis the closest axis * @return */ - public SelectionDetail getClosestSelectionDetailByPixel( - List valsAtIndex, - float x, float y, - YAxis.AxisDependency axis, float minSelectionDistance) { + public SelectionDetail getClosestSelectionDetailByPixel(List closestValues, float x, float y, + YAxis.AxisDependency axis, float minSelectionDistance) { SelectionDetail closest = null; float distance = minSelectionDistance; - for (int i = 0; i < valsAtIndex.size(); i++) { + for (int i = 0; i < closestValues.size(); i++) { - SelectionDetail sel = valsAtIndex.get(i); + SelectionDetail sel = closestValues.get(i); if (axis == null || sel.dataSet.getAxisDependency() == axis) { @@ -189,7 +198,16 @@ public SelectionDetail getClosestSelectionDetailByPixel( return closest; } - protected float getDistance(float x, float y, float selX, float selY) { - return (float) Math.hypot(x - selX, y - selY); + /** + * Calculates the distance between the two given points. + * + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @return + */ + protected float getDistance(float x1, float y1, float x2, float y2) { + return (float) Math.hypot(x1 - x2, y1 - y2); } } 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 78aa797458..2c12598a45 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 @@ -19,14 +19,8 @@ public CombinedHighlighter(BarLineScatterCandleBubbleDataProvider chart) { super(chart); } - /** - * Returns a list of SelectionDetail object corresponding to the given xValue. - * - * @param xVal - * @return - */ @Override - protected List getSelectionDetailsAtIndex(float xVal) { + protected List getSelectionDetailsAtXPos(float xVal) { List vals = new ArrayList(); @@ -45,11 +39,11 @@ protected List getSelectionDetailsAtIndex(float xVal) { if (!dataSet.isHighlightEnabled()) continue; - SelectionDetail s1 = getDetails(dataSet, j, xVal, DataSet.Rounding.UP); + SelectionDetail s1 = getDetail(dataSet, j, xVal, DataSet.Rounding.UP); s1.dataIndex = i; vals.add(s1); - SelectionDetail s2 = getDetails(dataSet, j, xVal, DataSet.Rounding.DOWN); + SelectionDetail s2 = getDetail(dataSet, j, xVal, DataSet.Rounding.DOWN); s2.dataIndex = i; vals.add(s2); } 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 5e3cbdd3fb..73c420c4a7 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 @@ -51,7 +51,7 @@ public Highlight getHighlight(float x, float y) { } @Override - protected SelectionDetail getDetails(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + protected SelectionDetail getDetail(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { final Entry e = set.getEntryForXPos(xVal, rounding); From 858600dcd2666cb7b563a808c088d8a42e8df7c1 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 10 Jun 2016 16:59:18 +0200 Subject: [PATCH 217/606] Cleanup renderers --- .../BarLineScatterCandleBubbleRenderer.java | 21 +++- .../renderer/CombinedChartRenderer.java | 2 - .../charting/renderer/DataRenderer.java | 1 - .../charting/renderer/LegendRenderer.java | 1 - .../charting/renderer/RadarChartRenderer.java | 8 -- .../renderer/ScatterChartRenderer.java | 1 - .../XAxisRendererHorizontalBarChart.java | 1 - .../charting/renderer/YAxisRenderer.java | 101 ------------------ .../YAxisRendererHorizontalBarChart.java | 1 - 9 files changed, 16 insertions(+), 121 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 9b6fdfabd6..b21cbef0e4 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 @@ -24,8 +24,7 @@ public BarLineScatterCandleBubbleRenderer(ChartAnimator animator, ViewPortHandle * @param dataSet * @return */ - protected XBounds getXBounds(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet - dataSet) { + protected XBounds getXBounds(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet) { return new XBounds(chart, dataSet); } @@ -34,15 +33,27 @@ protected XBounds getXBounds(BarLineScatterCandleBubbleDataProvider chart, IBarL */ protected class XBounds { - /** minimum visible entry index */ + /** + * minimum visible entry index + */ public final int min; - /** maximum visible entry index */ + /** + * maximum visible entry index + */ public final int max; - /** range of visible entry indices */ + /** + * range of visible entry indices + */ public final int range; + /** + * Calculates the minimum and maximum x values as well as the range between them. + * + * @param chart + * @param dataSet + */ public XBounds(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet) { float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); 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 7718b95e2c..1f79868185 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,8 +9,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.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ViewPortHandler; import java.lang.ref.WeakReference; 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 c1822a7c05..39afe9389a 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 @@ -8,7 +8,6 @@ import android.graphics.Paint.Style; import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.charts.Chart; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.highlight.Highlight; 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 c3eccae292..0add1004e0 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,7 +2,6 @@ package com.github.mikephil.charting.renderer; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Typeface; 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 ebf8435696..d593af582c 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 @@ -139,14 +139,6 @@ protected void drawDataSet(Canvas c, IRadarDataSet dataSet, int mostEntries) { // draw the line (only if filled is disabled or alpha is below 255) if (!dataSet.isDrawFilledEnabled() || dataSet.getFillAlpha() < 255) c.drawPath(surface, mRenderPaint); -// -// // draw filled -// if (dataSet.isDrawFilledEnabled()) { -// mRenderPaint.setStyle(Paint.Style.FILL); -// mRenderPaint.setAlpha(dataSet.getFillAlpha()); -// c.drawPath(surface, mRenderPaint); -// mRenderPaint.setAlpha(255); -// } } @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 c3583c6579..94c881c82a 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 @@ -4,7 +4,6 @@ import android.graphics.Canvas; import android.graphics.Paint.Style; import android.graphics.Path; -import android.graphics.Point; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.buffer.ScatterBuffer; 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 10332ab99a..9f070e8ed0 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 @@ -11,7 +11,6 @@ import com.github.mikephil.charting.components.LimitLine; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.utils.FSize; import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.Transformer; 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 27d56b63a1..f626ac752e 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 @@ -40,107 +40,6 @@ public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer t } } -// @Override -// protected void computeAxisValues(float min, float max) { -// -// float yMin = min; -// float yMax = max; -// -// int labelCount = mYAxis.getLabelCount(); -// double range = Math.abs(yMax - yMin); -// -// if (labelCount == 0 || range <= 0) { -// mYAxis.mEntries = new float[]{}; -// mYAxis.mEntryCount = 0; -// return; -// } -// -// // Find out how much spacing (in y value space) between axis values -// double rawInterval = range / labelCount; -// double interval = Utils.roundToNextSignificant(rawInterval); -// -// // If granularity is enabled, then do not allow the interval to go below specified granularity. -// // This is used to avoid repeated values when rounding values for display. -// if (mYAxis.isGranularityEnabled()) -// interval = interval < mYAxis.getGranularity() ? mYAxis.getGranularity() : interval; -// -// // Normalize interval -// 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); -// } -// -// // force label count -// if (mYAxis.isForceLabelsEnabled()) { -// -// float step = (float) range / (float) (labelCount - 1); -// mYAxis.mEntryCount = labelCount; -// -// if (mYAxis.mEntries.length < labelCount) { -// // Ensure stops contains at least numStops elements. -// mYAxis.mEntries = new float[labelCount]; -// } -// -// float v = min; -// -// for (int i = 0; i < labelCount; i++) { -// mYAxis.mEntries[i] = v; -// v += step; -// } -// -// // no forced count -// } else { -// -// // if the labels should only show min and max -// if (mYAxis.isShowOnlyMinMaxEnabled()) { -// -// mYAxis.mEntryCount = 2; -// mYAxis.mEntries = new float[2]; -// mYAxis.mEntries[0] = yMin; -// mYAxis.mEntries[1] = yMax; -// -// } else { -// -// double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; -// double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); -// -// double f; -// int i; -// int n = 0; -// if (interval != 0.0) { -// for (f = first; f <= last; f += interval) { -// ++n; -// } -// } -// -// mYAxis.mEntryCount = n; -// -// if (mYAxis.mEntries.length < n) { -// // Ensure stops contains at least numStops elements. -// mYAxis.mEntries = new float[n]; -// } -// -// for (f = first, i = 0; i < n; f += interval, ++i) { -// -// if (f == 0.0) // Fix for negative zero case (Where value == -0.0, and 0.0 == -0.0) -// f = 0.0; -// -// mYAxis.mEntries[i] = (float) f; -// } -// } -// } -// -// // set decimals -// if (interval < 1) { -// mYAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); -// } else { -// mYAxis.mDecimals = 0; -// } -// } - /** * draws the y-axis labels to the screen */ 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 1bb4598831..c1f510a3cf 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 @@ -7,7 +7,6 @@ import android.graphics.Path; import com.github.mikephil.charting.components.LimitLine; -import com.github.mikephil.charting.components.LimitLine.LimitLabelPosition; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; From c28b41b05ed9d22aaac4816eccd46602cf3803dd Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 10 Jun 2016 23:35:29 +0200 Subject: [PATCH 218/606] Work on highlighting --- .../mpchartexample/CombinedChartActivity.java | 7 +++++-- .../mikephil/charting/charts/BarChart.java | 1 - .../github/mikephil/charting/charts/Chart.java | 5 +++-- .../mikephil/charting/charts/CombinedChart.java | 2 +- .../charting/highlight/ChartHighlighter.java | 10 ++-------- .../charting/highlight/CombinedHighlighter.java | 17 +++++++++++++++-- .../charting/highlight/Highlighter.java | 16 ++++++++++++++++ 7 files changed, 42 insertions(+), 16 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlighter.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 5034bd126b..5ce21a6a3f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -36,6 +36,7 @@ import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.Arrays; public class CombinedChartActivity extends DemoBase { @@ -137,7 +138,9 @@ private BarData generateBarData() { for (int index = 0; index < itemcount; index++) { entries1.add(new BarEntry(0, getRandom(25, 25))); - entries2.add(new BarEntry(0, getRandom(25, 25))); + + // stacked + entries2.add(new BarEntry(0, new float[]{getRandom(13, 12), getRandom(13, 12)})); } BarDataSet set1 = new BarDataSet(entries1, "Bar 1"); @@ -147,7 +150,7 @@ private BarData generateBarData() { set1.setAxisDependency(YAxis.AxisDependency.LEFT); BarDataSet set2 = new BarDataSet(entries2, "Bar 2"); - set2.setColor(Color.rgb(61, 165, 255)); + set2.setColors(new int[]{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); 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 60b15cc8c2..dae084571f 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 @@ -187,7 +187,6 @@ public BarData getBarData() { return mData; } - /** * Adds half of the bar width to each side of the x-axis range in order to allow the bars of the barchart to be * fully displayed. 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 ddcb1f8485..6da5c21d74 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 @@ -37,6 +37,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.interfaces.dataprovider.ChartInterface; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.ChartTouchListener; @@ -162,7 +163,7 @@ public abstract class Chart { +public class ChartHighlighter implements Highlighter { /** * instance of the data-provider @@ -25,13 +25,7 @@ public ChartHighlighter(T chart) { this.mChart = chart; } - /** - * Returns a Highlight object corresponding to the given x- and y- touch positions in pixels. - * - * @param x - * @param y - * @return - */ + @Override public Highlight getHighlight(float x, float y) { float xVal = (float) getValsForTouch(x, y).x; 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 2c12598a45..424f6e4af6 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 @@ -3,6 +3,7 @@ import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.CombinedData; import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; import com.github.mikephil.charting.utils.SelectionDetail; @@ -13,10 +14,22 @@ /** * Created by Philipp Jahoda on 12/09/15. */ -public class CombinedHighlighter extends ChartHighlighter { +public class CombinedHighlighter extends ChartHighlighter implements Highlighter { - public CombinedHighlighter(BarLineScatterCandleBubbleDataProvider chart) { + protected BarHighlighter barHighlighter; + + public CombinedHighlighter(BarLineScatterCandleBubbleDataProvider chart, BarDataProvider barChart) { super(chart); + barHighlighter = new BarHighlighter(barChart); + } + + @Override + public Highlight getHighlight(float x, float y) { + + Highlight h1 = super.getHighlight(x, y); + Highlight h2 = barHighlighter.getHighlight(x, y); + + return h1; } @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlighter.java new file mode 100644 index 0000000000..cee50a9e57 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlighter.java @@ -0,0 +1,16 @@ +package com.github.mikephil.charting.highlight; + +/** + * Created by philipp on 10/06/16. + */ +public interface Highlighter { + + /** + * Returns a Highlight object corresponding to the given x- and y- touch positions in pixels. + * + * @param x + * @param y + * @return + */ + Highlight getHighlight(float x, float y); +} From f283de16983595235c4efc213f88f927adb7f383 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 11 Jun 2016 00:07:53 +0200 Subject: [PATCH 219/606] Work on highlight --- .../mikephil/charting/charts/Chart.java | 2 +- .../charting/highlight/BarHighlighter.java | 9 +++++-- .../charting/highlight/ChartHighlighter.java | 18 +++++++++---- .../highlight/CombinedHighlighter.java | 6 ++++- .../charting/highlight/Highlight.java | 25 +++++++------------ .../highlight/HorizontalBarHighlighter.java | 3 +-- 6 files changed, 36 insertions(+), 27 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 6da5c21d74..86795eaa34 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 @@ -633,7 +633,7 @@ public void highlightValue(Highlight high, boolean callListener) { } else { if (this instanceof BarLineChartBase && ((BarLineChartBase) this).isHighlightFullBarEnabled()) - high = new Highlight(high.getX(), Float.NaN, -1, -1, -1); + high = new Highlight(high.getX(), Float.NaN, -1, -1); // set the indices to highlight mIndicesToHighlight = new Highlight[]{ 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 0b40dc0062..981063b710 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 @@ -2,6 +2,7 @@ import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.utils.PointD; @@ -40,8 +41,7 @@ public Highlight getHighlight(float x, float y) { selectionDetail.xValue, selectionDetail.yValue, selectionDetail.dataIndex, - selectionDetail.dataSetIndex, - -1); + selectionDetail.dataSetIndex); } /** @@ -152,4 +152,9 @@ protected Range[] getRanges(BarEntry entry) { protected float getDistance(float x1, float y1, float x2, float y2) { return Math.abs(x1 - x2); } + + @Override + protected BarLineScatterCandleBubbleData getData() { + return mChart.getBarData(); + } } 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 a2700840a3..9b50cf6842 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 @@ -4,6 +4,8 @@ import java.util.List; import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; +import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.interfaces.datasets.IDataSet; @@ -69,8 +71,7 @@ protected SelectionDetail getSelectionDetail(float xVal, float x, float y) { YAxis.AxisDependency axis = leftAxisMinDist < rightAxisMinDist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT; - SelectionDetail detail = getClosestSelectionDetailByPixel(closestValues, x, y, axis, mChart - .getMaxHighlightDistance()); + SelectionDetail detail = getClosestSelectionDetailByPixel(closestValues, x, y, axis, mChart.getMaxHighlightDistance()); return detail; } @@ -119,11 +120,14 @@ protected List getSelectionDetailsAtXPos(float xVal) { List vals = new ArrayList(); - if (mChart.getData() == null) return vals; + BarLineScatterCandleBubbleData data = getData(); - for (int i = 0, dataSetCount = mChart.getData().getDataSetCount(); i < dataSetCount; i++) { + if (data == null) + return vals; - IDataSet dataSet = mChart.getData().getDataSetByIndex(i); + for (int i = 0, dataSetCount = data.getDataSetCount(); i < dataSetCount; i++) { + + IDataSet dataSet = data.getDataSetByIndex(i); // dont include datasets that cannot be highlighted if (!dataSet.isHighlightEnabled()) @@ -204,4 +208,8 @@ public SelectionDetail getClosestSelectionDetailByPixel(List cl protected float getDistance(float x1, float y1, float x2, float y2) { return (float) Math.hypot(x1 - x2, y1 - y2); } + + protected BarLineScatterCandleBubbleData getData() { + return mChart.getData(); + } } 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 424f6e4af6..14a5bf832d 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 @@ -29,7 +29,11 @@ public Highlight getHighlight(float x, float y) { Highlight h1 = super.getHighlight(x, y); Highlight h2 = barHighlighter.getHighlight(x, y); - return h1; + return getClosest(x, y, h1, h2); + } + + protected Highlight getClosest(float x, float y, Highlight... highs) { + return null; } @Override 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 f46329f336..daf967b2af 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 @@ -14,6 +14,12 @@ public class Highlight { /** the y-value of the highlighted value */ private float mY = Float.NaN; + /** the x-pixel of the highlight */ + private float mXPx; + + /** the y-pixel of the highlight */ + private float mYPx; + /** the index of the data object - in case it refers to more than one */ private int mDataIndex; @@ -40,20 +46,6 @@ public Highlight(float x, float y, int dataIndex, int dataSetIndex) { this.mDataIndex = dataIndex; this.mDataSetIndex = dataSetIndex; } - /** - * Constructor, only used for stacked-barchart. - * - * @param x the x-value of the highlighted value on the x-axis - * @param y the y-value of the highlighted value - * @param dataIndex the index of the Data the highlighted value belongs to - * @param dataSetIndex the index of the DataSet the highlighted value belongs to - * @param stackIndex references which value of a stacked-bar entry has been - * selected - */ - public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackIndex) { - this(x, y, dataIndex, dataSetIndex); - mStackIndex = stackIndex; - } /** * Constructor, only used for stacked-barchart. @@ -67,7 +59,8 @@ public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackInd * @param range the range the selected stack-value is in */ public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackIndex, Range range) { - this(x, y, dataIndex, dataSetIndex, stackIndex); + this(x, y, dataIndex, dataSetIndex); + this.mStackIndex = stackIndex; this.mRange = range; } @@ -78,7 +71,7 @@ public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackInd * @param dataSetIndex the index of the DataSet the highlighted value belongs to */ public Highlight(float x, int dataSetIndex) { - this(x, Float.NaN, 0, dataSetIndex, -1); + this(x, Float.NaN, 0, dataSetIndex); } /** 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 73c420c4a7..7cdea1f930 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 @@ -46,8 +46,7 @@ public Highlight getHighlight(float x, float y) { selectionDetail.xValue, selectionDetail.yValue, selectionDetail.dataIndex, - selectionDetail.dataSetIndex, - -1); + selectionDetail.dataSetIndex); } @Override From 4b00e83479c1ed28b5a80b514a58331c9c07cc67 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 11 Jun 2016 00:44:54 +0200 Subject: [PATCH 220/606] Remove SelectionDetail class --- .../mikephil/charting/charts/Chart.java | 3 +- .../charting/charts/PieRadarChartBase.java | 65 ++++++---- .../charting/highlight/BarHighlighter.java | 42 +++---- .../charting/highlight/ChartHighlighter.java | 72 +++++------ .../highlight/CombinedHighlighter.java | 42 +++++-- .../charting/highlight/Highlight.java | 119 +++++++++++++----- .../highlight/HorizontalBarHighlighter.java | 27 ++-- .../listener/PieRadarChartTouchListener.java | 3 +- .../charting/utils/SelectionDetail.java | 40 ------ .../github/mikephil/charting/utils/Utils.java | 21 ++-- 10 files changed, 228 insertions(+), 206 deletions(-) delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java 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 86795eaa34..c1e8f55fc7 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 @@ -31,6 +31,7 @@ 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.components.YAxis; import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.formatter.DefaultValueFormatter; @@ -633,7 +634,7 @@ public void highlightValue(Highlight high, boolean callListener) { } else { if (this instanceof BarLineChartBase && ((BarLineChartBase) this).isHighlightFullBarEnabled()) - high = new Highlight(high.getX(), Float.NaN, -1, -1); + high = new Highlight(high.getX(), Float.NaN, Float.NaN, Float.NaN, -1, YAxis.AxisDependency.LEFT); // set the indices to highlight mIndicesToHighlight = new Highlight[]{ 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 749346e38b..ca98146ef0 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 @@ -17,9 +17,9 @@ import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.PieRadarChartTouchListener; -import com.github.mikephil.charting.utils.SelectionDetail; import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; @@ -27,22 +27,30 @@ /** * Baseclass of PieChart and RadarChart. - * + * * @author Philipp Jahoda */ public abstract class PieRadarChartBase>> extends Chart { - /** holds the normalized version of the current rotation angle of the chart */ + /** + * holds the normalized version of the current rotation angle of the chart + */ private float mRotationAngle = 270f; - /** holds the raw version of the current rotation angle of the chart */ + /** + * holds the raw version of the current rotation angle of the chart + */ private float mRawRotationAngle = 270f; - /** flag that indicates if rotation is enabled or not */ + /** + * flag that indicates if rotation is enabled or not + */ protected boolean mRotateEnabled = true; - /** Sets the minimum offset (padding) around the chart, defaults to 0.f */ + /** + * Sets the minimum offset (padding) around the chart, defaults to 0.f + */ protected float mMinOffset = 0.f; public PieRadarChartBase(Context context) { @@ -115,8 +123,7 @@ public void calculateOffsets() { mLegend.getFormSize() + mLegend.getFormToTextSpace(); switch (mLegend.getOrientation()) { - case VERTICAL: - { + case VERTICAL: { float xLegendOffset = 0.f; if (mLegend.getHorizontalAlignment() == Legend.LegendHorizontalAlignment.LEFT @@ -182,7 +189,7 @@ public void calculateOffsets() { break; } } - break; + break; case HORIZONTAL: float yLegendOffset = 0.f; @@ -247,7 +254,7 @@ public void calculateOffsets() { * returns the angle relative to the chart center for the given point on the * chart in degrees. The angle is always between 0 and 360°, 0° is NORTH, * 90° is EAST, ... - * + * * @param x * @param y * @return @@ -278,10 +285,10 @@ public float getAngleForPoint(float x, float y) { /** * Calculates the position around a center point, depending on the distance * from the center, and the angle of the position around the center. - * + * * @param center * @param dist - * @param angle in degrees, converted to radians internally + * @param angle in degrees, converted to radians internally * @return */ protected PointF getPosition(PointF center, float dist, float angle) { @@ -329,7 +336,7 @@ public float distanceToCenter(float x, float y) { /** * Returns the xIndex for the given angle around the center of the chart. * Returns -1 if not found / outofbounds. - * + * * @param angle * @return */ @@ -338,7 +345,7 @@ public float distanceToCenter(float x, float y) { /** * Set an offset for the rotation of the RadarChart in degrees. Default 270f * --> top (NORTH) - * + * * @param angle */ public void setRotationAngle(float angle) { @@ -371,7 +378,7 @@ public float getRotationAngle() { /** * Set this to true to enable the rotation / spinning of the chart by touch. * Set it to false to disable it. Default: true - * + * * @param enabled */ public void setRotationEnabled(boolean enabled) { @@ -380,26 +387,30 @@ public void setRotationEnabled(boolean enabled) { /** * Returns true if rotation of the chart by touch is enabled, false if not. - * + * * @return */ public boolean isRotationEnabled() { return mRotateEnabled; } - /** Gets the minimum offset (padding) around the chart, defaults to 0.f */ + /** + * Gets the minimum offset (padding) around the chart, defaults to 0.f + */ public float getMinOffset() { return mMinOffset; } - /** Sets the minimum offset (padding) around the chart, defaults to 0.f */ + /** + * Sets the minimum offset (padding) around the chart, defaults to 0.f + */ public void setMinOffset(float minOffset) { mMinOffset = minOffset; } /** * returns the diameter of the pie- or radar-chart - * + * * @return */ public float getDiameter() { @@ -413,14 +424,14 @@ public float getDiameter() { /** * Returns the radius of the chart in pixels. - * + * * @return */ public abstract float getRadius(); /** * Returns the required offset for the chart legend. - * + * * @return */ protected abstract float getRequiredLegendOffset(); @@ -428,7 +439,7 @@ public float getDiameter() { /** * Returns the base offset needed for the chart without calculating the * legend size. - * + * * @return */ protected abstract float getRequiredBaseOffset(); @@ -446,16 +457,16 @@ public float getYChartMin() { } /** - * Returns an array of SelectionDetail objects for the given x-index. The SelectionDetail + * Returns an array of Highlight objects for the given x-index. The Highlight * objects give information about the value at the selected index and the * DataSet it belongs to. INFORMATION: This method does calculations at * runtime. Do not over-use in performance critical situations. * * @return */ - public List getSelectionDetailsAtIndex(int xIndex) { + public List getSelectionDetailsAtIndex(int xIndex) { - List vals = new ArrayList(); + List vals = new ArrayList(); for (int i = 0; i < mData.getDataSetCount(); i++) { @@ -466,7 +477,7 @@ public List getSelectionDetailsAtIndex(int xIndex) { if (Float.isNaN(yVal)) continue; - vals.add(new SelectionDetail(0f, yVal, i, dataSet)); + vals.add(new Highlight(0f, yVal, 0f, 0f, i, dataSet.getAxisDependency())); } return vals; @@ -479,7 +490,7 @@ public List getSelectionDetailsAtIndex(int xIndex) { /** * Applys a spin animation to the Chart. - * + * * @param durationmillis * @param fromangle * @param toangle 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 981063b710..8d6b971561 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 @@ -6,7 +6,6 @@ import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.utils.PointD; -import com.github.mikephil.charting.utils.SelectionDetail; /** * Created by Philipp Jahoda on 22/07/15. @@ -24,37 +23,33 @@ public Highlight getHighlight(float x, float y) { PointD pos = getValsForTouch(x, y); - SelectionDetail selectionDetail = getSelectionDetail((float) pos.x, x, y); - if (selectionDetail == null) + Highlight high = getHighlightForX((float) pos.x, x, y); + if (high == null) return null; - IBarDataSet set = barData.getDataSetByIndex(selectionDetail.dataSetIndex); + IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex()); if (set.isStacked()) { - return getStackedHighlight(selectionDetail, + return getStackedHighlight(high, set, (float) pos.x, (float) pos.y); } - return new Highlight( - selectionDetail.xValue, - selectionDetail.yValue, - selectionDetail.dataIndex, - selectionDetail.dataSetIndex); + return high; } /** * This method creates the Highlight object that also indicates which value of a stacked BarEntry has been * selected. * - * @param selectionDetail the selection detail to work with looking for stacked values + * @param high the Highlight to work with looking for stacked values * @param set * @param xVal - * @param yValue + * @param yVal * @return */ - protected Highlight getStackedHighlight(SelectionDetail selectionDetail, IBarDataSet set, float xVal, double yValue) { + protected Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal, float yVal) { BarEntry entry = set.getEntryForXPos(xVal); @@ -63,23 +58,26 @@ protected Highlight getStackedHighlight(SelectionDetail selectionDetail, IBarDat // not stacked if (entry.getYVals() == null) { - return new Highlight(entry.getX(), - entry.getY(), - selectionDetail.dataIndex, - selectionDetail.dataSetIndex); + return high; } else { Range[] ranges = getRanges(entry); if (ranges.length > 0) { - int stackIndex = getClosestStackIndex(ranges, (float) yValue); - return new Highlight( + int stackIndex = getClosestStackIndex(ranges, yVal); + + + Highlight stackedHigh = new Highlight( entry.getX(), entry.getPositiveSum() - entry.getNegativeSum(), - selectionDetail.dataIndex, - selectionDetail.dataSetIndex, + high.getXPx(), + high.getYPx(), + high.getDataSetIndex(), stackIndex, - ranges[stackIndex] + ranges[stackIndex], + high.getAxis() ); + + return stackedHigh; } } 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 9b50cf6842..035087cb1e 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 @@ -5,13 +5,11 @@ import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; -import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; import com.github.mikephil.charting.utils.PointD; -import com.github.mikephil.charting.utils.SelectionDetail; /** * Created by Philipp Jahoda on 21/07/15. @@ -32,14 +30,8 @@ public Highlight getHighlight(float x, float y) { float xVal = (float) getValsForTouch(x, y).x; - SelectionDetail selectionDetail = getSelectionDetail(xVal, x, y); - if (selectionDetail == null) - return null; - - return new Highlight(selectionDetail.xValue, - selectionDetail.yValue, - selectionDetail.dataIndex, - selectionDetail.dataSetIndex); + Highlight high = getHighlightForX(xVal, x, y); + return high; } /** @@ -56,22 +48,23 @@ protected PointD getValsForTouch(float x, float y) { } /** - * Returns the corresponding SelectionDetail for a given xVal and y-touch position in pixels. + * Returns the corresponding Highlight for a given xVal and x- and y-touch position in pixels. * * @param xVal + * @param x * @param y * @return */ - protected SelectionDetail getSelectionDetail(float xVal, float x, float y) { + protected Highlight getHighlightForX(float xVal, float x, float y) { - List closestValues = getSelectionDetailsAtXPos(xVal); + List closestValues = getHighlightsAtXPos(xVal); float leftAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.LEFT); float rightAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.RIGHT); YAxis.AxisDependency axis = leftAxisMinDist < rightAxisMinDist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT; - SelectionDetail detail = getClosestSelectionDetailByPixel(closestValues, x, y, axis, mChart.getMaxHighlightDistance()); + Highlight detail = getClosestHighlightByPixel(closestValues, x, y, axis, mChart.getMaxHighlightDistance()); return detail; } @@ -85,17 +78,17 @@ protected SelectionDetail getSelectionDetail(float xVal, float x, float y) { * @param axis * @return */ - protected float getMinimumDistance(List closestValues, float pos, YAxis.AxisDependency axis) { + protected float getMinimumDistance(List closestValues, float pos, YAxis.AxisDependency axis) { float distance = Float.MAX_VALUE; for (int i = 0; i < closestValues.size(); i++) { - SelectionDetail sel = closestValues.get(i); + Highlight high = closestValues.get(i); - if (sel.dataSet.getAxisDependency() == axis) { + if (high.getAxis() == axis) { - float tempDistance = Math.abs(getSelectionPos(sel) - pos); + float tempDistance = Math.abs(getHighlightPos(high) - pos); if (tempDistance < distance) { distance = tempDistance; } @@ -105,20 +98,20 @@ protected float getMinimumDistance(List closestValues, float po return distance; } - protected float getSelectionPos(SelectionDetail sel) { - return sel.yPx; + protected float getHighlightPos(Highlight h) { + return h.getYPx(); } /** - * Returns a list of SelectionDetail objects representing the entries closest to the given xVal. + * Returns a list of Highlight objects representing the entries closest to the given xVal. * The returned list contains two objects per DataSet (closest rounding up, closest rounding down). * * @param xVal * @return */ - protected List getSelectionDetailsAtXPos(float xVal) { + protected List getHighlightsAtXPos(float xVal) { - List vals = new ArrayList(); + List vals = new ArrayList(); BarLineScatterCandleBubbleData data = getData(); @@ -133,15 +126,15 @@ protected List getSelectionDetailsAtXPos(float xVal) { if (!dataSet.isHighlightEnabled()) continue; - vals.add(getDetail(dataSet, i, xVal, DataSet.Rounding.UP)); - vals.add(getDetail(dataSet, i, xVal, DataSet.Rounding.DOWN)); + vals.add(buildHighlight(dataSet, i, xVal, DataSet.Rounding.UP)); + vals.add(buildHighlight(dataSet, i, xVal, DataSet.Rounding.DOWN)); } return vals; } /** - * Returns the SelectionDetail object corresponding to the selected xValue and dataSetIndex. + * Returns the Highlight object corresponding to the selected xValue and dataSetIndex. * * @param set * @param dataSetIndex @@ -149,7 +142,7 @@ protected List getSelectionDetailsAtXPos(float xVal) { * @param rounding * @return */ - protected SelectionDetail getDetail(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + protected Highlight buildHighlight(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { final Entry e = set.getEntryForXPos(xVal, rounding); @@ -158,36 +151,37 @@ protected SelectionDetail getDetail(IDataSet set, int dataSetIndex, float xVal, PointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), e.getY()); - return new SelectionDetail((float) pixels.x, (float) pixels.y, e.getX(), e.getY(), dataSetIndex, set); + return new Highlight(e.getX(), e.getY(), (float) pixels.x, (float) pixels.y, dataSetIndex, set.getAxisDependency()); } /** - * Returns the SelectionDetail of the DataSet that contains the closest value on the + * Returns the Highlight of the DataSet that contains the closest value on the * y-axis. * - * @param closestValues contains two values per DataSet closest to the selected x-position (determined by rounding up and - * down) + * @param closestValues contains two Highlight objects per DataSet closest to the selected x-position (determined by + * rounding up an down) * @param x * @param y - * @param axis the closest axis + * @param axis the closest axis + * @param minSelectionDistance * @return */ - public SelectionDetail getClosestSelectionDetailByPixel(List closestValues, float x, float y, - YAxis.AxisDependency axis, float minSelectionDistance) { + public Highlight getClosestHighlightByPixel(List closestValues, float x, float y, + YAxis.AxisDependency axis, float minSelectionDistance) { - SelectionDetail closest = null; + Highlight closest = null; float distance = minSelectionDistance; for (int i = 0; i < closestValues.size(); i++) { - SelectionDetail sel = closestValues.get(i); + Highlight high = closestValues.get(i); - if (axis == null || sel.dataSet.getAxisDependency() == axis) { + if (axis == null || high.getAxis() == axis) { - float cDistance = getDistance(x, y, sel.xPx, sel.yPx); + float cDistance = getDistance(x, y, high.getXPx(), high.getYPx()); if (cDistance < distance) { - closest = sel; + closest = high; distance = cDistance; } } 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 14a5bf832d..dd66eb396d 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 @@ -6,7 +6,6 @@ import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; -import com.github.mikephil.charting.utils.SelectionDetail; import java.util.ArrayList; import java.util.List; @@ -16,30 +15,47 @@ */ public class CombinedHighlighter extends ChartHighlighter implements Highlighter { + /** + * bar highlighter for supporting stacked highlighting + */ protected BarHighlighter barHighlighter; public CombinedHighlighter(BarLineScatterCandleBubbleDataProvider chart, BarDataProvider barChart) { super(chart); - barHighlighter = new BarHighlighter(barChart); + barHighlighter = barChart.getBarData() == null ? null : new BarHighlighter(barChart); } @Override public Highlight getHighlight(float x, float y) { Highlight h1 = super.getHighlight(x, y); - Highlight h2 = barHighlighter.getHighlight(x, y); + Highlight h2 = barHighlighter == null ? null : barHighlighter.getHighlight(x, y); - return getClosest(x, y, h1, h2); + return (h2 != null && h2.isStacked()) ? h2 : h1; } - protected Highlight getClosest(float x, float y, Highlight... highs) { - return null; - } +// protected Highlight getClosest(float x, float y, Highlight... highs) { +// +// Highlight closest = null; +// float minDistance = Float.MAX_VALUE; +// +// for (Highlight high : highs) { +// +// float tempDistance = getDistance(x, y, high.getXPx(), high.getYPx()); +// +// if (tempDistance < minDistance) { +// minDistance = tempDistance; +// closest = high; +// } +// } +// +// return closest; +// } @Override - protected List getSelectionDetailsAtXPos(float xVal) { + protected List getHighlightsAtXPos(float xVal) { - List vals = new ArrayList(); + List vals = new ArrayList(); CombinedData data = (CombinedData) mChart.getData(); @@ -56,12 +72,12 @@ protected List getSelectionDetailsAtXPos(float xVal) { if (!dataSet.isHighlightEnabled()) continue; - SelectionDetail s1 = getDetail(dataSet, j, xVal, DataSet.Rounding.UP); - s1.dataIndex = i; + Highlight s1 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.UP); + s1.setDataIndex(i); vals.add(s1); - SelectionDetail s2 = getDetail(dataSet, j, xVal, DataSet.Rounding.DOWN); - s2.dataIndex = i; + Highlight s2 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.DOWN); + s2.setDataIndex(i); vals.add(s2); } } 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 daf967b2af..d023c935ab 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 @@ -1,77 +1,96 @@ package com.github.mikephil.charting.highlight; +import com.github.mikephil.charting.components.YAxis; + /** * Contains information needed to determine the highlighted value. - * + * * @author Philipp Jahoda */ public class Highlight { - /** the x-value of the highlighted value */ + /** + * the x-value of the highlighted value + */ private float mX = Float.NaN; - /** the y-value of the highlighted value */ + /** + * the y-value of the highlighted value + */ private float mY = Float.NaN; - /** the x-pixel of the highlight */ + /** + * the x-pixel of the highlight + */ private float mXPx; - /** the y-pixel of the highlight */ + /** + * the y-pixel of the highlight + */ private float mYPx; - /** the index of the data object - in case it refers to more than one */ + /** + * the index of the data object - in case it refers to more than one + */ private int mDataIndex; - /** the index of the dataset the highlighted value is in */ + /** + * the index of the dataset the highlighted value is in + */ private int mDataSetIndex; - /** index which value of a stacked bar entry is highlighted, default -1 */ + /** + * index which value of a stacked bar entry is highlighted, default -1 + */ private int mStackIndex = -1; - /** the range of the bar that is selected (only for stacked-barchart) */ + /** + * the range of the bar that is selected (only for stacked-barchart) + */ private Range mRange; /** - * constructor - * - * @param x the x-value of the highlighted value - * @param y the y-value of the highlighted value - * @param dataIndex the index of the Data the highlighted value belongs to - * @param dataSetIndex the index of the DataSet the highlighted value belongs to + * the axis the highlighted value belongs to */ - public Highlight(float x, float y, int dataIndex, int dataSetIndex) { + private YAxis.AxisDependency axis; + + public Highlight(float x, int dataSetIndex) { this.mX = x; - this.mY = y; - this.mDataIndex = dataIndex; this.mDataSetIndex = dataSetIndex; } /** - * Constructor, only used for stacked-barchart. + * constructor * - * @param x the index of the highlighted value on the x-axis - * @param y the y-value of the highlighted value - * @param dataIndex the index of the Data the highlighted value belongs to + * @param x the x-value of the highlighted value + * @param y the y-value of the highlighted value * @param dataSetIndex the index of the DataSet the highlighted value belongs to - * @param stackIndex references which value of a stacked-bar entry has been - * selected - * @param range the range the selected stack-value is in */ - public Highlight(float x, float y, int dataIndex, int dataSetIndex, int stackIndex, Range range) { - this(x, y, dataIndex, dataSetIndex); - this.mStackIndex = stackIndex; - this.mRange = range; + public Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, YAxis.AxisDependency axis) { + this.mX = x; + this.mY = y; + this.mXPx = xPx; + this.mYPx = yPx; + this.mDataSetIndex = dataSetIndex; + this.axis = axis; } /** * Constructor, only used for stacked-barchart. * - * @param x the x-value of the highlighted value on the x-axis + * @param x the index of the highlighted value on the x-axis + * @param y the y-value of the highlighted value * @param dataSetIndex the index of the DataSet the highlighted value belongs to + * @param stackIndex references which value of a stacked-bar entry has been + * selected + * @param range the range the selected stack-value is in */ - public Highlight(float x, int dataSetIndex) { - this(x, Float.NaN, 0, dataSetIndex); + public Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, int stackIndex, Range range, + YAxis.AxisDependency axis) { + this(x, y, xPx, yPx, dataSetIndex, axis); + this.mStackIndex = stackIndex; + this.mRange = range; } /** @@ -92,6 +111,20 @@ public float getY() { return mY; } + /** + * returns the x-position of the highlight in pixels + */ + public float getXPx() { + return mXPx; + } + + /** + * returns the y-position of the highlight in pixels + */ + public float getYPx() { + return mYPx; + } + /** * the index of the data object - in case it refers to more than one * @@ -101,6 +134,10 @@ public int getDataIndex() { return mDataIndex; } + public void setDataIndex(int mDataIndex) { + this.mDataIndex = mDataIndex; + } + /** * returns the index of the DataSet the highlighted value is in * @@ -113,7 +150,7 @@ public int getDataSetIndex() { /** * Only needed if a stacked-barchart entry was highlighted. References the * selected value within the stacked-entry. - * + * * @return */ public int getStackIndex() { @@ -122,16 +159,30 @@ public int getStackIndex() { /** * Returns the range of values the selected value of a stacked bar is in. (this is only relevant for stacked-barchart) + * * @return */ public Range getRange() { return mRange; } + public boolean isStacked() { + return mStackIndex >= 0; + } + + /** + * Returns the axis the highlighted value belongs to. + * + * @return + */ + public YAxis.AxisDependency getAxis() { + return axis; + } + /** * Returns true if this highlight object is equal to the other (compares * xIndex and dataSetIndex) - * + * * @param h * @return */ 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 7cdea1f930..c9f89dbe2f 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 @@ -1,6 +1,5 @@ package com.github.mikephil.charting.highlight; -import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; @@ -8,10 +7,6 @@ import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.PointD; -import com.github.mikephil.charting.utils.SelectionDetail; -import com.github.mikephil.charting.utils.Utils; - -import java.util.List; /** * Created by Philipp Jahoda on 22/07/15. @@ -29,38 +24,34 @@ public Highlight getHighlight(float x, float y) { PointD pos = getValsForTouch(y, x); - SelectionDetail selectionDetail = getSelectionDetail((float) pos.y, y, x); - if (selectionDetail == null) + Highlight high = getHighlightForX((float) pos.y, y, x); + if (high == null) return null; - IBarDataSet set = barData.getDataSetByIndex(selectionDetail.dataSetIndex); + IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex()); if (set.isStacked()) { - return getStackedHighlight(selectionDetail, + return getStackedHighlight(high, set, (float) pos.y, (float) pos.x); } - return new Highlight( - selectionDetail.xValue, - selectionDetail.yValue, - selectionDetail.dataIndex, - selectionDetail.dataSetIndex); + return high; } @Override - protected SelectionDetail getDetail(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + protected Highlight buildHighlight(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { final Entry e = set.getEntryForXPos(xVal, rounding); PointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getY(), e.getX()); - return new SelectionDetail((float) pixels.x, (float) pixels.y, e.getX(), e.getY(), dataSetIndex, set); + return new Highlight(e.getX(), e.getY(), (float) pixels.x, (float) pixels.y, dataSetIndex, set.getAxisDependency()); } @Override - protected float getDistance(float x, float y, float selX, float selY) { - return Math.abs(y - selY); + protected float getDistance(float x1, float y1, float x2, float y2) { + return Math.abs(y1 - y2); } } 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 d40e2fdb4e..ca125dba41 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 @@ -11,7 +11,6 @@ import com.github.mikephil.charting.charts.PieRadarChartBase; import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.utils.SelectionDetail; import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; @@ -183,7 +182,7 @@ public boolean onSingleTapUp(MotionEvent e) { } else { - List valsAtIndex = mChart.getSelectionDetailsAtIndex(index); + List valsAtIndex = mChart.getSelectionDetailsAtIndex(index); int dataSetIndex = 0; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java deleted file mode 100644 index 0adeb4b787..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/SelectionDetail.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.github.mikephil.charting.utils; - -import com.github.mikephil.charting.interfaces.datasets.IDataSet; - -/** - * Class that encapsulates information of a value that has been - * selected/highlighted and its DataSet index. The SelectionDetail objects give - * information about the value at the selected index and the DataSet it belongs - * to. Needed only for highlighting onTouch(). - * - * @author Philipp Jahoda - */ -public class SelectionDetail { - - public float yPx; - public float xPx; - public float yValue; - public float xValue; - public int dataIndex; - public int dataSetIndex; - public IDataSet dataSet; - - public SelectionDetail(float xPx, float yPx, float xValue, float yValue, int dataIndex, int dataSetIndex, IDataSet set) { - this.xPx = xPx; - this.yPx = yPx; - this.xValue = xValue; - this.yValue = yValue; - this.dataIndex = dataIndex; - this.dataSetIndex = dataSetIndex; - this.dataSet = set; - } - - public SelectionDetail(float xPx, float yPx, float xValue, float yValue, int dataSetIndex, IDataSet set) { - this(xPx, yPx, xValue, yValue, 0, dataSetIndex, set); - } - - public SelectionDetail(float xValue, float yValue, int dataSetIndex, IDataSet set) { - this(Float.NaN, Float.NaN, xValue, yValue, 0, dataSetIndex, set); - } -} \ No newline at end of file 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 4e5fb6ac00..e375bd1534 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 @@ -22,6 +22,7 @@ import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.formatter.DefaultValueFormatter; import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; import java.util.List; @@ -388,15 +389,15 @@ public static double nextUp(double d) { * @param valsAtIndex all the values at a specific index * @return */ - public static int getClosestDataSetIndexByValue(List valsAtIndex, float value, - AxisDependency axis) { + public static int getClosestDataSetIndexByValue(List valsAtIndex, float value, + AxisDependency axis) { - SelectionDetail sel = getClosestSelectionDetailByValue(valsAtIndex, value, axis); + Highlight sel = getClosestSelectionDetailByValue(valsAtIndex, value, axis); if (sel == null) return -Integer.MAX_VALUE; - return sel.dataSetIndex; + return sel.getDataSetIndex(); } /** @@ -406,21 +407,21 @@ public static int getClosestDataSetIndexByValue(List valsAtInde * @param valsAtIndex all the values at a specific index * @return */ - public static SelectionDetail getClosestSelectionDetailByValue( - List valsAtIndex, + public static Highlight getClosestSelectionDetailByValue( + List valsAtIndex, float value, AxisDependency axis) { - SelectionDetail closest = null; + Highlight closest = null; float distance = Float.MAX_VALUE; for (int i = 0; i < valsAtIndex.size(); i++) { - SelectionDetail sel = valsAtIndex.get(i); + Highlight sel = valsAtIndex.get(i); - if (axis == null || sel.dataSet.getAxisDependency() == axis) { + if (axis == null || sel.getAxis() == axis) { - float cdistance = Math.abs(sel.yValue - value); + float cdistance = Math.abs(sel.getY() - value); if (cdistance < distance) { closest = sel; distance = cdistance; From 0d9c8545f2efe476214096801c72544653a384d3 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 11 Jun 2016 23:44:30 +0200 Subject: [PATCH 221/606] Work on combined highlighting --- .../mpchartexample/CombinedChartActivity.java | 3 +- .../charting/charts/CombinedChart.java | 10 ++- .../mikephil/charting/data/CombinedData.java | 8 ++ .../charting/highlight/BarHighlighter.java | 13 +-- .../charting/highlight/ChartHighlighter.java | 16 ++-- .../highlight/CombinedHighlighter.java | 88 ++++++++++--------- .../charting/highlight/Highlight.java | 2 +- .../dataprovider/CombinedDataProvider.java | 11 +++ .../renderer/BubbleChartRenderer.java | 2 +- 9 files changed, 95 insertions(+), 58 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CombinedDataProvider.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 5ce21a6a3f..237d7d6cfc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -149,7 +149,8 @@ private BarData generateBarData() { set1.setValueTextSize(10f); set1.setAxisDependency(YAxis.AxisDependency.LEFT); - BarDataSet set2 = new BarDataSet(entries2, "Bar 2"); + 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.setValueTextColor(Color.rgb(61, 165, 255)); set2.setValueTextSize(10f); 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 d33dec05c9..710c82e4a5 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 @@ -14,6 +14,7 @@ import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; +import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider; import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; @@ -25,8 +26,7 @@ * * @author Philipp Jahoda */ -public class CombinedChart extends BarLineChartBase implements LineDataProvider, - BarDataProvider, ScatterDataProvider, CandleDataProvider, BubbleDataProvider { +public class CombinedChart extends BarLineChartBase implements CombinedDataProvider { // /** // * flag that enables or disables the highlighting arrow @@ -82,11 +82,17 @@ protected void init() { // mViewPortHandler); } + @Override + public CombinedData getCombinedData() { + return mData; + } + @Override public void setData(CombinedData data) { mData = null; mRenderer = null; super.setData(data); + setHighlighter(new CombinedHighlighter(this, this)); mRenderer = new CombinedChartRenderer(this, mAnimator, mViewPortHandler); mRenderer.initBuffers(); } 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 fc1b566a07..33cacc304a 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 @@ -97,6 +97,10 @@ public List getAllData() { return data; } + public ChartData getDataByIndex(int index) { + return getAllData().get(index); + } + @Override public void notifyDataChanged() { if (mLineData != null) @@ -145,4 +149,8 @@ public Entry getEntryForHighlight(Highlight highlight) { return null; } } + + public int getDataIndex(ChartData data) { + return getAllData().indexOf(data); + } } 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 8d6b971561..9f39fc599f 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 @@ -49,7 +49,7 @@ public Highlight getHighlight(float x, float y) { * @param yVal * @return */ - protected Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal, float yVal) { + public Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal, float yVal) { BarEntry entry = set.getEntryForXPos(xVal); @@ -65,15 +65,18 @@ protected Highlight getStackedHighlight(Highlight high, IBarDataSet set, float x if (ranges.length > 0) { int stackIndex = getClosestStackIndex(ranges, yVal); + Range range = ranges[stackIndex]; + + PointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(high.getX(), range.to); Highlight stackedHigh = new Highlight( entry.getX(), - entry.getPositiveSum() - entry.getNegativeSum(), - high.getXPx(), - high.getYPx(), + entry.getY(), + (float) pixels.x, + (float) pixels.y, high.getDataSetIndex(), stackIndex, - ranges[stackIndex], + range, high.getAxis() ); 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 035087cb1e..9674504d4b 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 @@ -1,16 +1,16 @@ package com.github.mikephil.charting.highlight; -import java.util.ArrayList; -import java.util.List; - import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.PointD; +import java.util.ArrayList; +import java.util.List; + /** * Created by Philipp Jahoda on 21/07/15. */ @@ -57,7 +57,7 @@ protected PointD getValsForTouch(float x, float y) { */ protected Highlight getHighlightForX(float xVal, float x, float y) { - List closestValues = getHighlightsAtXPos(xVal); + List closestValues = getHighlightsAtXPos(xVal, x, y); float leftAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.LEFT); float rightAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.RIGHT); @@ -106,10 +106,12 @@ protected float getHighlightPos(Highlight h) { * Returns a list of Highlight objects representing the entries closest to the given xVal. * The returned list contains two objects per DataSet (closest rounding up, closest rounding down). * - * @param xVal + * @param xVal the transformed x-value of the x-touch position + * @param x touch position + * @param y touch position * @return */ - protected List getHighlightsAtXPos(float xVal) { + protected List getHighlightsAtXPos(float xVal, float x, float y) { List vals = new ArrayList(); 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 dd66eb396d..418d7a38e4 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 @@ -1,11 +1,11 @@ package com.github.mikephil.charting.highlight; +import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.data.CombinedData; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider; import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; import java.util.ArrayList; import java.util.List; @@ -13,25 +13,61 @@ /** * Created by Philipp Jahoda on 12/09/15. */ -public class CombinedHighlighter extends ChartHighlighter implements Highlighter { +public class CombinedHighlighter extends ChartHighlighter implements Highlighter { /** * bar highlighter for supporting stacked highlighting */ protected BarHighlighter barHighlighter; - public CombinedHighlighter(BarLineScatterCandleBubbleDataProvider chart, BarDataProvider barChart) { + public CombinedHighlighter(CombinedDataProvider chart, BarDataProvider barChart) { super(chart); + + // if there is BarData, create a BarHighlighter barHighlighter = barChart.getBarData() == null ? null : new BarHighlighter(barChart); } @Override - public Highlight getHighlight(float x, float y) { + protected List getHighlightsAtXPos(float xVal, float x, float y) { + + List vals = new ArrayList(); + + List dataObjects = mChart.getCombinedData().getAllData(); + + for (int i = 0; i < dataObjects.size(); i++) { + + ChartData dataObject = dataObjects.get(i); + + // in case of BarData, let the BarHighlighter take over + if (barHighlighter != null && dataObject instanceof BarData) { + Highlight high = barHighlighter.getHighlight(x, y); + + if (high != null) { + high.setDataIndex(i); + vals.add(high); + } + } else { - Highlight h1 = super.getHighlight(x, y); - Highlight h2 = barHighlighter == null ? null : barHighlighter.getHighlight(x, y); + for (int j = 0, dataSetCount = dataObject.getDataSetCount(); j < dataSetCount; j++) { - return (h2 != null && h2.isStacked()) ? h2 : h1; + IDataSet dataSet = dataObjects.get(i).getDataSetByIndex(j); + + // don't include datasets that cannot be highlighted + if (!dataSet.isHighlightEnabled()) + continue; + + Highlight s1 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.UP); + s1.setDataIndex(i); + vals.add(s1); + + Highlight s2 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.DOWN); + s2.setDataIndex(i); + vals.add(s2); + } + } + } + + return vals; } // protected Highlight getClosest(float x, float y, Highlight... highs) { @@ -41,6 +77,9 @@ public Highlight getHighlight(float x, float y) { // // for (Highlight high : highs) { // +// if (high == null) +// continue; +// // float tempDistance = getDistance(x, y, high.getXPx(), high.getYPx()); // // if (tempDistance < minDistance) { @@ -51,37 +90,4 @@ public Highlight getHighlight(float x, float y) { // // return closest; // } - - @Override - protected List getHighlightsAtXPos(float xVal) { - - List vals = new ArrayList(); - - CombinedData data = (CombinedData) mChart.getData(); - - // get all chartdata objects - List dataObjects = data.getAllData(); - - for (int i = 0; i < dataObjects.size(); i++) { - - for (int j = 0, dataSetCount = dataObjects.get(i).getDataSetCount(); j < dataSetCount; j++) { - - IDataSet dataSet = dataObjects.get(i).getDataSetByIndex(j); - - // don't include datasets that cannot be highlighted - if (!dataSet.isHighlightEnabled()) - continue; - - Highlight s1 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.UP); - s1.setDataIndex(i); - vals.add(s1); - - Highlight s2 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.DOWN); - s2.setDataIndex(i); - vals.add(s2); - } - } - - return vals; - } } 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 d023c935ab..6a9be2af34 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 @@ -33,7 +33,7 @@ public class Highlight { /** * the index of the data object - in case it refers to more than one */ - private int mDataIndex; + private int mDataIndex = -1; /** * the index of the dataset the highlighted value is in diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CombinedDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CombinedDataProvider.java new file mode 100644 index 0000000000..574d26a2a2 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CombinedDataProvider.java @@ -0,0 +1,11 @@ +package com.github.mikephil.charting.interfaces.dataprovider; + +import com.github.mikephil.charting.data.CombinedData; + +/** + * Created by philipp on 11/06/16. + */ +public interface CombinedDataProvider extends LineDataProvider, BarDataProvider, BubbleDataProvider, CandleDataProvider, ScatterDataProvider { + + CombinedData getCombinedData(); +} 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 b23cf3721e..7cf908ab8a 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 @@ -199,7 +199,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (dataSet == null || !dataSet.isHighlightEnabled()) continue; - final BubbleEntry entry = (BubbleEntry) bubbleData.getEntryForHighlight(high); + final BubbleEntry entry = dataSet.getEntryForXPos(high.getX()); if (entry == null) continue; From 2b886a463e85a0e35e9641b75250b0460b6307c8 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 12 Jun 2016 16:11:37 +0200 Subject: [PATCH 222/606] Work on improving pie and radar highlighting --- .../mikephil/charting/charts/BarChart.java | 1 + .../charting/charts/BarLineChartBase.java | 53 ++++-------- .../mikephil/charting/charts/Chart.java | 29 +++++-- .../mikephil/charting/charts/PieChart.java | 18 ++-- .../charting/charts/PieRadarChartBase.java | 32 +------ .../mikephil/charting/charts/RadarChart.java | 34 ++++---- .../charting/highlight/PieHighlighter.java | 25 ++++++ .../highlight/PieRadarHighlighter.java | 66 ++++++++++++++ .../charting/highlight/RadarHighlighter.java | 85 +++++++++++++++++++ .../listener/PieRadarChartTouchListener.java | 61 +------------ .../charting/renderer/PieChartRenderer.java | 17 ++-- .../charting/renderer/RadarChartRenderer.java | 28 +----- 12 files changed, 259 insertions(+), 190 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieHighlighter.java create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieRadarHighlighter.java create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java 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 dae084571f..fbcf5ccc0b 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 @@ -8,6 +8,7 @@ import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.BarHighlighter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; 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 a4cf5b3b0b..ceeccd2b1a 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 @@ -516,23 +516,24 @@ public void calculateOffsets() { prepareValuePxMatrix(); } - @Override - protected float[] getMarkerPosition(Entry e, Highlight highlight) { - - int dataSetIndex = highlight.getDataSetIndex(); - float xPos = e.getX(); - float yPos = e.getY() * mAnimator.getPhaseY(); - - // position of the marker depends on selected value index and value - float[] pts = new float[]{ - xPos, yPos - }; - - getTransformer(mData.getDataSetByIndex(dataSetIndex).getAxisDependency()) - .pointValuesToPixel(pts); - - return pts; - } +// @Override +// protected float[] getMarkerPosition(Highlight high) { +// return new float[] { high.getXPx(), high.getYPx() }; +// +// int dataSetIndex = highlight.getDataSetIndex(); +// float xPos = e.getX(); +// float yPos = e.getY() * mAnimator.getPhaseY(); +// +// // position of the marker depends on selected value index and value +// float[] pts = new float[]{ +// xPos, yPos +// }; +// +// getTransformer(mData.getDataSetByIndex(dataSetIndex).getAxisDependency()) +// .pointValuesToPixel(pts); +// +// return pts; +// } /** * draws the grid background @@ -1180,24 +1181,6 @@ public void setKeepPositionOnRotation(boolean keepPositionOnRotation) { mKeepPositionOnRotation = keepPositionOnRotation; } - /** - * Returns the Highlight object (contains x-index and DataSet index) of the - * selected value at the given touch point inside the Line-, Scatter-, or - * CandleStick-Chart. - * - * @param x - * @param y - * @return - */ - 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); - } - /** * Returns the x and y values in the chart at the given touch point * (encapsulated in a PointD). This method transforms pixel coordinates to 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 c1e8f55fc7..92812576fd 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 @@ -664,6 +664,24 @@ public void highlightTouch(Highlight high) { highlightValue(high, true); } + /** + * Returns the Highlight object (contains x-index and DataSet index) of the + * selected value at the given touch point inside the Line-, Scatter-, or + * CandleStick-Chart. + * + * @param x + * @param y + * @return + */ + 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); + } + /** * Set a new (e.g. custom) ChartTouchListener NOTE: make sure to * setTouchEnabled(true); if you need touch gestures on the chart @@ -711,7 +729,7 @@ protected void drawMarkers(Canvas canvas) { if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX()) continue; - float[] pos = getMarkerPosition(e, highlight); + float[] pos = getMarkerPosition(highlight); // check bounds if (!mViewPortHandler.isInBounds(pos[0], pos[1])) @@ -736,13 +754,14 @@ protected void drawMarkers(Canvas canvas) { /** * Returns the actual position in pixels of the MarkerView for the given - * Entry in the given DataSet. + * Highlight object. * - * @param e - * @param highlight + * @param high * @return */ - protected abstract float[] getMarkerPosition(Entry e, Highlight highlight); + protected float[] getMarkerPosition(Highlight high) { + return new float[]{high.getXPx(), high.getYPx()}; + } /** * ################ ################ ################ ################ 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 7440ca3141..f070da9988 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 @@ -14,6 +14,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.highlight.PieHighlighter; import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; import com.github.mikephil.charting.renderer.PieChartRenderer; import com.github.mikephil.charting.utils.Utils; @@ -111,6 +112,8 @@ protected void init() { mRenderer = new PieChartRenderer(this, mAnimator, mViewPortHandler); mXAxis = null; + + mHighlighter = new PieHighlighter(this); } @Override @@ -165,7 +168,7 @@ protected void calcMinMax() { } @Override - protected float[] getMarkerPosition(Entry e, Highlight highlight) { + protected float[] getMarkerPosition(Highlight highlight) { PointF center = getCenterCircleBox(); float r = getRadius(); @@ -233,24 +236,21 @@ private void calcAngles() { } /** - * checks if the given index in the given DataSet is set for highlighting or - * not + * Checks if the given index is set to be highlighted. * - * @param xIndex - * @param dataSetIndex + * @param index * @return */ - public boolean needsHighlight(int xIndex, int dataSetIndex) { + public boolean needsHighlight(int index) { // no highlight - if (!valuesToHighlight() || dataSetIndex < 0) + if (!valuesToHighlight()) return false; for (int i = 0; i < mIndicesToHighlight.length; i++) // check if the xvalue for the given dataset needs highlight - if (mIndicesToHighlight[i].getX() == xIndex - && mIndicesToHighlight[i].getDataSetIndex() == dataSetIndex) + if ((int) mIndicesToHighlight[i].getX() == index) return true; return false; 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 ca98146ef0..6a82bc13a8 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 @@ -22,9 +22,6 @@ import com.github.mikephil.charting.listener.PieRadarChartTouchListener; import com.github.mikephil.charting.utils.Utils; -import java.util.ArrayList; -import java.util.List; - /** * Baseclass of PieChart and RadarChart. * @@ -291,7 +288,7 @@ public float getAngleForPoint(float x, float y) { * @param angle in degrees, converted to radians internally * @return */ - protected PointF getPosition(PointF center, float dist, float angle) { + public PointF getPosition(PointF center, float dist, float angle) { PointF p = new PointF((float) (center.x + dist * Math.cos(Math.toRadians(angle))), (float) (center.y + dist * Math.sin(Math.toRadians(angle)))); @@ -456,33 +453,6 @@ public float getYChartMin() { return 0; } - /** - * Returns an array of Highlight objects for the given x-index. The Highlight - * objects give information about the value at the selected index and the - * DataSet it belongs to. INFORMATION: This method does calculations at - * runtime. Do not over-use in performance critical situations. - * - * @return - */ - public List getSelectionDetailsAtIndex(int xIndex) { - - List vals = new ArrayList(); - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - IDataSet dataSet = mData.getDataSetByIndex(i); - - // extract all y-values from all DataSets at the given x-index - final float yVal = dataSet.getYValueForXValue(xIndex); - if (Float.isNaN(yVal)) - continue; - - vals.add(new Highlight(0f, yVal, 0f, 0f, i, dataSet.getAxisDependency())); - } - - return vals; - } - /** * ################ ################ ################ ################ */ 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 fb9d9fb46c..27c36fbad1 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 @@ -13,6 +13,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.RadarData; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.highlight.RadarHighlighter; import com.github.mikephil.charting.renderer.RadarChartRenderer; import com.github.mikephil.charting.renderer.XAxisRendererRadarChart; import com.github.mikephil.charting.renderer.YAxisRendererRadarChart; @@ -93,6 +94,8 @@ protected void init() { mRenderer = new RadarChartRenderer(this, mAnimator, mViewPortHandler); mYAxisRenderer = new YAxisRendererRadarChart(mViewPortHandler, mYAxis, this); mXAxisRenderer = new XAxisRendererRadarChart(mViewPortHandler, mXAxis, this); + + mHighlighter = new RadarHighlighter(this); } @Override @@ -103,20 +106,21 @@ protected void calcMinMax() { mXAxis.calculate(0, mData.getMaxEntryCountSet().getEntryCount()); } - @Override - protected float[] getMarkerPosition(Entry e, Highlight highlight) { - - float angle = getSliceAngle() * e.getX() * mAnimator.getPhaseX() + getRotationAngle(); - float val = e.getY() * getFactor() * mAnimator.getPhaseY(); - PointF c = getCenterOffsets(); - - PointF p = new PointF((float) (c.x + val * Math.cos(Math.toRadians(angle))), - (float) (c.y + val * Math.sin(Math.toRadians(angle)))); - - return new float[]{ - p.x, p.y - }; - } +// @Override +// protected float[] getMarkerPosition(Highlight highlight) { +// return null; +// +//// float angle = getSliceAngle() * e.getX() * mAnimator.getPhaseX() + getRotationAngle(); +//// float val = e.getY() * getFactor() * mAnimator.getPhaseY(); +//// PointF c = getCenterOffsets(); +//// +//// PointF p = new PointF((float) (c.x + val * Math.cos(Math.toRadians(angle))), +//// (float) (c.y + val * Math.sin(Math.toRadians(angle)))); +//// +//// return new float[]{ +//// p.x, p.y +//// }; +// } @Override public void notifyDataSetChanged() { @@ -197,7 +201,7 @@ public int getIndexForAngle(float angle) { float sliceangle = getSliceAngle(); - for (int i = 0; i < mData.getEntryCount(); i++) { + for (int i = 0; i < mData.getMaxEntryCountSet().getEntryCount(); i++) { if (sliceangle * (i + 1) - sliceangle / 2f > a) return i; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieHighlighter.java new file mode 100644 index 0000000000..b4c5a1b24b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieHighlighter.java @@ -0,0 +1,25 @@ +package com.github.mikephil.charting.highlight; + +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; + +/** + * Created by philipp on 12/06/16. + */ +public class PieHighlighter extends PieRadarHighlighter { + + public PieHighlighter(PieChart chart) { + super(chart); + } + + @Override + protected Highlight getClosestHighlight(int index, float x, float y) { + + IPieDataSet set = mChart.getData().getDataSet(); + + final Entry entry = set.getEntryForIndex(index); + + return new Highlight(index, entry.getY(), x, y, 0, set.getAxisDependency()); + } +} 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 new file mode 100644 index 0000000000..029212c84e --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieRadarHighlighter.java @@ -0,0 +1,66 @@ +package com.github.mikephil.charting.highlight; + +import android.graphics.PointF; + +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.charts.PieRadarChartBase; +import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.Utils; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by philipp on 12/06/16. + */ +public abstract class PieRadarHighlighter implements Highlighter { + + protected T mChart; + + public PieRadarHighlighter(T chart) { + this.mChart = chart; + } + + @Override + public Highlight getHighlight(float x, float y) { + + float touchDistanceToCenter = mChart.distanceToCenter(x, y); + + // check if a slice was touched + if (touchDistanceToCenter > mChart.getRadius()) { + + // if no slice was touched, highlight nothing + return null; + + } else { + + float angle = mChart.getAngleForPoint(x, y); + + if (mChart instanceof PieChart) { + angle /= mChart.getAnimator().getPhaseY(); + } + + int index = mChart.getIndexForAngle(angle); + + // check if the index could be found + if (index < 0 || index >= mChart.getData().getMaxEntryCountSet().getEntryCount()) { + return null; + + } else { + return getClosestHighlight(index, x, y); + } + } + } + + /** + * Returns the closest Highlight object of the given objects based on the touch position inside the chart. + * + * @param index + * @param x + * @param y + * @return + */ + protected abstract Highlight getClosestHighlight(int index, float x, float y); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java new file mode 100644 index 0000000000..ab49232c89 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java @@ -0,0 +1,85 @@ +package com.github.mikephil.charting.highlight; + +import android.graphics.PointF; + +import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.Utils; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by philipp on 12/06/16. + */ +public class RadarHighlighter extends PieRadarHighlighter { + + public RadarHighlighter(RadarChart chart) { + super(chart); + } + + @Override + protected Highlight getClosestHighlight(int index, float x, float y) { + + List highlights = getHighlightsAtIndex(index); + + float distanceToCenter = mChart.distanceToCenter(x, y) / mChart.getFactor(); + + Highlight closest = null; + float distance = Float.MAX_VALUE; + + for (int i = 0; i < highlights.size(); i++) { + + Highlight high = highlights.get(i); + + float cdistance = Math.abs(high.getY() - distanceToCenter); + if (cdistance < distance) { + closest = high; + distance = cdistance; + } + } + + return closest; + } + + /** + * Returns an array of Highlight objects for the given index. The Highlight + * objects give information about the value at the selected index and the + * DataSet it belongs to. INFORMATION: This method does calculations at + * runtime. Do not over-use in performance critical situations. + * + * @param index + * @return + */ + protected List getHighlightsAtIndex(int index) { + + List vals = new ArrayList(); + + float phaseX = mChart.getAnimator().getPhaseX(); + float phaseY = mChart.getAnimator().getPhaseY(); + float sliceangle = mChart.getSliceAngle(); + float factor = mChart.getFactor(); + + for (int i = 0; i < mChart.getData().getDataSetCount(); i++) { + + IDataSet dataSet = mChart.getData().getDataSetByIndex(i); + + final Entry entry = dataSet.getEntryForIndex(index); + + float y = (entry.getY() - mChart.getYChartMin()); + + if (Float.isNaN(y)) + continue; + + PointF p = Utils.getPosition( + mChart.getCenterOffsets(), + y * factor * phaseY, + sliceangle * index * phaseX + mChart.getRotationAngle()); + + vals.add(new Highlight(entry.getX(), entry.getY(), p.x, p.y, i, dataSet.getAxisDependency())); + } + + return vals; + } +} 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 ca125dba41..c6d82d6193 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 @@ -7,14 +7,11 @@ import android.view.View; import android.view.animation.AnimationUtils; -import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.charts.PieRadarChartBase; -import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; -import java.util.List; /** * Touchlistener for the PieChart. @@ -150,62 +147,8 @@ public boolean onSingleTapUp(MotionEvent e) { return false; } - float distance = mChart.distanceToCenter(e.getX(), e.getY()); - - // check if a slice was touched - if (distance > mChart.getRadius()) { - - // if no slice was touched, highlight nothing - - if (mLastHighlighted == null) - mChart.highlightValues(null); // no listener callback - else - mChart.highlightTouch(null); // listener callback - - mLastHighlighted = null; - - } else { - - float angle = mChart.getAngleForPoint(e.getX(), e.getY()); - - if (mChart instanceof PieChart) { - angle /= mChart.getAnimator().getPhaseY(); - } - - int index = mChart.getIndexForAngle(angle); - - // check if the index could be found - if (index < 0) { - - mChart.highlightValues(null); - mLastHighlighted = null; - - } else { - - List valsAtIndex = mChart.getSelectionDetailsAtIndex(index); - - int dataSetIndex = 0; - - // get the dataset that is closest to the selection (PieChart - // only - // has one DataSet) - if (mChart instanceof RadarChart) { - - dataSetIndex = Utils.getClosestDataSetIndexByValue( - valsAtIndex, - distance / ((RadarChart) mChart).getFactor(), - null); - } - - if (dataSetIndex < 0) { - mChart.highlightValues(null); - mLastHighlighted = null; - } else { - Highlight h = new Highlight(index, dataSetIndex); - performHighlight(h, e); - } - } - } + Highlight high = mChart.getHighlightByTouchPoint(e.getX(), e.getY()); + performHighlight(high, e); return true; } 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 58973f0158..05c9d531ed 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 @@ -233,8 +233,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { // draw only if the value is greater than zero if ((Math.abs(e.getY()) > 0.000001)) { - if (!mChart.needsHighlight((int) e.getX(), - mChart.getData().getIndexOfDataSet(dataSet))) { + if (!mChart.needsHighlight(j)) { final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; @@ -712,17 +711,15 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { for (int i = 0; i < indices.length; i++) { // get the index to highlight - float x = indices[i].getX(); + int index = (int) indices[i].getX(); - if (x >= drawAngles.length) + if (index >= drawAngles.length) continue; IPieDataSet set = mChart.getData() .getDataSetByIndex(indices[i] .getDataSetIndex()); - int entryIndex = set.getEntryIndex(x, DataSet.Rounding.CLOSEST); - if (set == null || !set.isHighlightEnabled()) continue; @@ -735,14 +732,14 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } } - if (x == 0) + if (index == 0) angle = 0.f; else - angle = absoluteAngles[entryIndex - 1] * phaseX; + angle = absoluteAngles[index - 1] * phaseX; final float sliceSpace = visibleAngleCount <= 1 ? 0.f : set.getSliceSpace(); - float sliceAngle = drawAngles[entryIndex]; + float sliceAngle = drawAngles[index]; float innerRadius = userInnerRadius; float shift = set.getSelectionShift(); @@ -752,7 +749,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; - mRenderPaint.setColor(set.getColor(entryIndex)); + mRenderPaint.setColor(set.getColor(index)); final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? 0.f : 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 d593af582c..42a934f0ef 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 @@ -239,14 +239,6 @@ protected void drawWeb(Canvas c) { @Override public void drawHighlighted(Canvas c, Highlight[] indices) { - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - float sliceangle = mChart.getSliceAngle(); - float factor = mChart.getFactor(); - - PointF center = mChart.getCenterOffsets(); - for (int i = 0; i < indices.length; i++) { IRadarDataSet set = mChart.getData() @@ -256,24 +248,8 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - // get the index to highlight - float x = indices[i].getX(); - - Entry e = set.getEntryForXPos(x); - if (e == null || e.getX() != x) - continue; - - int j = set.getEntryIndex(e); - float y = (e.getY() - mChart.getYChartMin()); - - if (Float.isNaN(y)) - continue; - - PointF p = Utils.getPosition( - center, - y * factor * phaseY, - sliceangle * j * phaseX + mChart.getRotationAngle()); - + Highlight high = indices[i]; + PointF p = new PointF(high.getXPx(), high.getYPx()); // draw the lines drawHighlightLines(c, p.x, p.y, set); From dfc0b46f4860a9e85e511186b2ded49b512c4764 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 12 Jun 2016 16:16:51 +0200 Subject: [PATCH 223/606] Improve OnChartValueSelectedListener --- .../mpchartexample/BarChartActivity.java | 2 +- .../BarChartActivityMultiDataset.java | 4 ++-- .../mpchartexample/BubbleChartActivity.java | 4 ++-- .../mpchartexample/DrawChartActivity.java | 4 ++-- .../mpchartexample/DynamicalAddingActivity.java | 2 +- .../mpchartexample/HorizontalBarChartActivity.java | 4 ++-- .../mpchartexample/InvertedLineChartActivity.java | 4 ++-- .../mpchartexample/LineChartActivity1.java | 2 +- .../mpchartexample/LineChartActivity2.java | 4 ++-- .../mpchartexample/MultiLineChartActivity.java | 4 ++-- .../mpchartexample/PieChartActivity.java | 6 +++--- .../mpchartexample/PiePolylineChartActivity.java | 4 ++-- .../mpchartexample/RealtimeLineChartActivity.java | 2 +- .../mpchartexample/ScatterChartActivity.java | 4 ++-- .../mpchartexample/StackedBarActivity.java | 2 +- .../mpchartexample/StackedBarActivityNegative.java | 2 +- .../com/github/mikephil/charting/charts/Chart.java | 2 +- .../mikephil/charting/highlight/Highlight.java | 2 +- .../listener/OnChartValueSelectedListener.java | 14 ++++++-------- 19 files changed, 35 insertions(+), 37 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 57d28ce0fb..ce090da068 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -258,7 +258,7 @@ private void setData(int count, float range) { @SuppressLint("NewApi") @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { if (e == null) return; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index e43c4f33d1..f76fee3b8c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -269,8 +269,8 @@ public void onStopTrackingTouch(SeekBar seekBar) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - Log.i("Activity", "Selected: " + e.toString() + ", dataSet: " + dataSetIndex); + public void onValueSelected(Entry e, Highlight h) { + Log.i("Activity", "Selected: " + e.toString() + ", dataSet: " + h.getDataSetIndex()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java index 72e09797f5..7ffeef9aa6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -210,10 +210,10 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Log.i("VAL SELECTED", "Value: " + e.getY() + ", xIndex: " + e.getX() - + ", DataSet index: " + dataSetIndex); + + ", DataSet index: " + h.getDataSetIndex()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java index 4d103bedfc..970ba12909 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java @@ -142,10 +142,10 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Log.i("VAL SELECTED", "Value: " + e.getY() + ", xIndex: " + e.getX() - + ", DataSet index: " + dataSetIndex); + + ", DataSet index: " + h.getDataSetIndex()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java index a505e4a4f9..2d1cbcd907 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -146,7 +146,7 @@ private void removeDataSet() { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show(); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 5151b48d85..e828d4dcf7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -250,13 +250,13 @@ private void setData(int count, float range) { @SuppressLint("NewApi") @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { if (e == null) return; RectF bounds = mChart.getBarBounds((BarEntry) e); - PointF position = mChart.getPosition(e, mChart.getData().getDataSetByIndex(dataSetIndex) + PointF position = mChart.getPosition(e, mChart.getData().getDataSetByIndex(h.getDataSetIndex()) .getAxisDependency()); Log.i("bounds", bounds.toString()); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index a28c7b1630..1f760f871e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -230,10 +230,10 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Log.i("VAL SELECTED", "Value: " + e.getY() + ", xIndex: " + e.getX() - + ", DataSet index: " + dataSetIndex); + + ", DataSet index: " + h.getDataSetIndex()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index bfa5c2dde2..fd8c8f9e4f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -433,7 +433,7 @@ public void onChartTranslate(MotionEvent me, float dX, float dY) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + 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()); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 24972f36ff..afdf3eadd7 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -357,10 +357,10 @@ private void setData(int count, float range) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Log.i("Entry selected", e.toString()); - mChart.centerViewToAnimated(e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex).getAxisDependency(), 500); + mChart.centerViewToAnimated(e.getX(), e.getY(), mChart.getData().getDataSetByIndex(h.getDataSetIndex()).getAxisDependency(), 500); //mChart.zoomAndCenterAnimated(2.5f, 2.5f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex).getAxisDependency(), 1000); //mChart.zoomAndCenterAnimated(1.8f, 1.8f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex).getAxisDependency(), 1000); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java index 487fd4baa9..8d9060a2b6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -220,10 +220,10 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Log.i("VAL SELECTED", "Value: " + e.getY() + ", xIndex: " + e.getX() - + ", DataSet index: " + dataSetIndex); + + ", DataSet index: " + h.getDataSetIndex()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index d050f2d410..1da4792f36 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -247,13 +247,13 @@ private SpannableString generateCenterSpannableText() { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { if (e == null) return; Log.i("VAL SELECTED", - "Value: " + e.getY() + ", xIndex: " + e.getX() - + ", DataSet index: " + dataSetIndex); + "Value: " + e.getY() + ", index: " + h.getX() + + ", DataSet index: " + h.getDataSetIndex()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index 30a1dcd238..1cbfd58638 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -253,13 +253,13 @@ private SpannableString generateCenterSpannableText() { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { if (e == null) return; Log.i("VAL SELECTED", "Value: " + e.getY() + ", xIndex: " + e.getX() - + ", DataSet index: " + dataSetIndex); + + ", DataSet index: " + h.getDataSetIndex()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index ab72539792..5b8d3f6f3e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -204,7 +204,7 @@ public void run() { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + 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 a0b3103e1e..b3821e6b12 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -208,10 +208,10 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { Log.i("VAL SELECTED", "Value: " + e.getY() + ", xIndex: " + e.getX() - + ", DataSet index: " + dataSetIndex); + + ", DataSet index: " + h.getDataSetIndex()); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index ae17135bad..9b9767001c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -227,7 +227,7 @@ public void onStopTrackingTouch(SeekBar seekBar) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { BarEntry entry = (BarEntry) e; diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 4bbe293c5f..dc2a8825fc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -206,7 +206,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public void onValueSelected(Entry e, Highlight h) { BarEntry entry = (BarEntry) e; Log.i("VAL SELECTED", 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 92812576fd..707c9d5e14 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 @@ -649,7 +649,7 @@ public void highlightValue(Highlight high, boolean callListener) { mSelectionListener.onNothingSelected(); else { // notify the listener - mSelectionListener.onValueSelected(e, high.getDataSetIndex(), high); + mSelectionListener.onValueSelected(e, high); } } // redraw the chart 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 6a9be2af34..82d2cb695d 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 @@ -201,7 +201,7 @@ public boolean equalTo(Highlight h) { @Override public String toString() { - return "Highlight, x: " + mX + "y: " + mY + ", dataSetIndex: " + mDataSetIndex + return "Highlight, x: " + mX + ", y: " + mY + ", dataSetIndex: " + mDataSetIndex + ", stackIndex (only stacked barentry): " + mStackIndex; } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java index 4c6da2e8c9..cc73f73802 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java @@ -6,21 +6,19 @@ /** * Listener for callbacks when selecting values inside the chart by * touch-gesture. - * + * * @author Philipp Jahoda */ public interface OnChartValueSelectedListener { /** * Called when a value has been selected inside the chart. - * - * @param e The selected Entry. - * @param dataSetIndex The index in the datasets array of the data object - * the Entrys DataSet is in. - * @param h the corresponding highlight object that contains information - * about the highlighted position + * + * @param e The selected Entry + * @param h The corresponding highlight object that contains information + * about the highlighted position */ - void onValueSelected(Entry e, int dataSetIndex, Highlight h); + void onValueSelected(Entry e, Highlight h); /** * Called when nothing has been selected or an "un-select" has been made. From 44d5641fb8e7eab038d2d6651e61cb72918b9d36 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 12 Jun 2016 16:20:52 +0200 Subject: [PATCH 224/606] Fix issue in selection callback --- .../java/com/github/mikephil/charting/data/PieData.java | 6 ++++++ 1 file changed, 6 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 801114943c..dc920bae50 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,7 @@ package com.github.mikephil.charting.data; +import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; import java.util.ArrayList; @@ -62,6 +63,11 @@ public IPieDataSet getDataSetByLabel(String label, boolean ignorecase) { : null : label.equals(mDataSets.get(0).getLabel()) ? mDataSets.get(0) : null; } + @Override + public Entry getEntryForHighlight(Highlight highlight) { + return getDataSet().getEntryForIndex((int) highlight.getX()); + } + /** * Returns the sum of all values in this PieData object. * From e853a80f782c8283874a7d164569a80b5469f998 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 12 Jun 2016 16:23:01 +0200 Subject: [PATCH 225/606] Unused method cleanup --- .../mikephil/charting/data/DataSet.java | 11 ---- .../data/realm/base/RealmBaseDataSet.java | 12 ----- .../interfaces/datasets/IDataSet.java | 11 ---- .../github/mikephil/charting/utils/Utils.java | 52 ------------------- 4 files changed, 86 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 0abdb49fdf..42571e95de 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 @@ -295,17 +295,6 @@ public int getEntryIndex(float xPos, Rounding rounding) { return high; } - @Override - public float getYValueForXValue(float xVal) { - - Entry e = getEntryForXPos(xVal); - - if (e != null && e.getX() == xVal) - return e.getY(); - else - return Float.NaN; - } - /** * Returns all Entry objects at the given xIndex. INFORMATION: This method * does calculations at runtime. Do not over-use in performance critical diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java index 7a55da224d..43927dd934 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java @@ -252,18 +252,6 @@ public int getEntryIndex(S e) { return mValues.indexOf(e); } - @Override - public float getYValueForXValue(float xVal) { - //return new DynamicRealmObject(results.where().greaterThanOrEqualTo(mXValuesField, xIndex).findFirst()) - // .getFloat(mYValuesField); - Entry e = getEntryForXPos(xVal); - - if (e != null && e.getX() == xVal) - return e.getY(); - else - return Float.NaN; - } - @Override public boolean addEntry(S 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 49a78d7e52..a9d60e870c 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 @@ -123,17 +123,6 @@ public interface IDataSet { */ int getEntryIndex(T e); - /** - * Returns the value of the Entry object at the given xPos. Returns - * Float.NaN if no value is at the given xPos. INFORMATION: This method - * does calculations at runtime. Do not over-use in performance critical - * situations. - * - * @param xPos - * @return - */ - float getYValueForXValue(float xPos); - /** * This method returns the actual 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 e375bd1534..ea249d95f2 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 @@ -19,10 +19,8 @@ import android.view.View; import android.view.ViewConfiguration; -import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.formatter.DefaultValueFormatter; import com.github.mikephil.charting.formatter.ValueFormatter; -import com.github.mikephil.charting.highlight.Highlight; import java.util.List; @@ -382,56 +380,6 @@ public static double nextUp(double d) { } } - /** - * Returns the index of the DataSet that contains the closest value on the - * y-axis. This is needed for highlighting. This will return -Integer.MAX_VALUE if failure. - * - * @param valsAtIndex all the values at a specific index - * @return - */ - public static int getClosestDataSetIndexByValue(List valsAtIndex, float value, - AxisDependency axis) { - - Highlight sel = getClosestSelectionDetailByValue(valsAtIndex, value, axis); - - if (sel == null) - return -Integer.MAX_VALUE; - - return sel.getDataSetIndex(); - } - - /** - * Returns the SelectionDetail of the DataSet that contains the closest value on the - * y-axis. - * - * @param valsAtIndex all the values at a specific index - * @return - */ - public static Highlight getClosestSelectionDetailByValue( - List valsAtIndex, - float value, - AxisDependency axis) { - - Highlight closest = null; - float distance = Float.MAX_VALUE; - - for (int i = 0; i < valsAtIndex.size(); i++) { - - Highlight sel = valsAtIndex.get(i); - - if (axis == null || sel.getAxis() == axis) { - - float cdistance = Math.abs(sel.getY() - value); - if (cdistance < distance) { - closest = sel; - distance = cdistance; - } - } - } - - return closest; - } - /** * If this component has no ValueFormatter or is only equipped with the * default one (no custom set), return true. From 8a37bbda380977443fb01043b642c2605eeb4fb7 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 13 Jun 2016 20:13:30 +0200 Subject: [PATCH 226/606] Work on highlight rendering and markerview --- .../mikephil/charting/charts/Chart.java | 2 +- .../charting/charts/HorizontalBarChart.java | 16 ++-- .../charting/highlight/Highlight.java | 39 ++++++++ .../charting/highlight/RadarHighlighter.java | 2 +- .../charting/renderer/BarChartRenderer.java | 94 ++++++------------- .../BarLineScatterCandleBubbleRenderer.java | 22 +++++ .../renderer/BubbleChartRenderer.java | 92 ++++++++---------- .../renderer/CandleStickChartRenderer.java | 40 +++----- .../renderer/HorizontalBarChartRenderer.java | 7 ++ .../charting/renderer/LineChartRenderer.java | 33 +++---- .../charting/renderer/RadarChartRenderer.java | 45 ++++++--- .../renderer/ScatterChartRenderer.java | 33 +++---- 12 files changed, 215 insertions(+), 210 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 707c9d5e14..b6c652a5b6 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 @@ -760,7 +760,7 @@ protected void drawMarkers(Canvas canvas) { * @return */ protected float[] getMarkerPosition(Highlight high) { - return new float[]{high.getXPx(), high.getYPx()}; + return new float[]{high.getDrawX(), high.getDrawY()}; } /** 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 a7a1cdf687..f95132769b 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 @@ -1,7 +1,6 @@ package com.github.mikephil.charting.charts; import android.content.Context; -import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.RectF; import android.util.AttributeSet; @@ -126,9 +125,14 @@ public void calculateOffsets() { @Override protected void prepareValuePxMatrix() { mRightAxisTransformer.prepareMatrixValuePx(mAxisRight.mAxisMinimum, mAxisRight.mAxisRange, mXAxis.mAxisRange, - mXAxis.mAxisMinimum); + mXAxis.mAxisMinimum); mLeftAxisTransformer.prepareMatrixValuePx(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisRange, mXAxis.mAxisRange, - mXAxis.mAxisMinimum); + mXAxis.mAxisMinimum); + } + + @Override + protected float[] getMarkerPosition(Highlight high) { + return new float[]{high.getDrawY(), high.getDrawX()}; } @Override @@ -181,7 +185,7 @@ public PointF getPosition(Entry e, AxisDependency axis) { public Highlight getHighlightByTouchPoint(float x, float y) { if (mData == null) { - if(mLogEnabled) + if (mLogEnabled) Log.e(LOG_TAG, "Can't select by touch. No data set."); return null; } else @@ -191,14 +195,14 @@ public Highlight getHighlightByTouchPoint(float x, float y) { @Override public float getLowestVisibleX() { PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), - mViewPortHandler.contentBottom()); + mViewPortHandler.contentBottom()); return (float) Math.max(mXAxis.mAxisMinimum, pos.y); } @Override public float getHighestVisibleX() { PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), - mViewPortHandler.contentTop()); + mViewPortHandler.contentTop()); return (float) Math.min(mXAxis.mAxisMaximum, pos.y); } } 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 82d2cb695d..f8989a4da7 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 @@ -55,6 +55,16 @@ public class Highlight { */ private YAxis.AxisDependency axis; + /** + * the x-position (pixels) on which this highlight object was last drawn + */ + private float mDrawX; + + /** + * the y-position (pixels) on which this highlight object was last drawn + */ + private float mDrawY; + public Highlight(float x, int dataSetIndex) { this.mX = x; this.mDataSetIndex = dataSetIndex; @@ -179,6 +189,35 @@ public YAxis.AxisDependency getAxis() { return axis; } + /** + * Sets the x- and y-position (pixels) where this highlight was last drawn. + * + * @param x + * @param y + */ + public void setDraw(float x, float y) { + this.mDrawX = x; + this.mDrawY = y; + } + + /** + * Returns the x-position in pixels where this highlight object was last drawn. + * + * @return + */ + public float getDrawX() { + return mDrawX; + } + + /** + * Returns the y-position in pixels where this highlight object was last drawn. + * + * @return + */ + public float getDrawY() { + return mDrawY; + } + /** * Returns true if this highlight object is equal to the other (compares * xIndex and dataSetIndex) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java index ab49232c89..c113d7d26d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java @@ -77,7 +77,7 @@ protected List getHighlightsAtIndex(int index) { y * factor * phaseY, sliceangle * index * phaseX + mChart.getRotationAngle()); - vals.add(new Highlight(entry.getX(), entry.getY(), p.x, p.y, i, dataSet.getAxisDependency())); + vals.add(new Highlight(index, entry.getY(), p.x, p.y, i, dataSet.getAxisDependency())); } return vals; 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 1698113de3..ae6e12d793 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,7 +19,7 @@ import java.util.List; -public class BarChartRenderer extends DataRenderer { +public class BarChartRenderer extends BarLineScatterCandleBubbleRenderer { protected BarDataProvider mChart; @@ -328,82 +328,48 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { for (Highlight high : indices) { - final int minDataSetIndex = high.getDataSetIndex() == -1 - ? 0 - : high.getDataSetIndex(); - final int maxDataSetIndex = high.getDataSetIndex() == -1 - ? barData.getDataSetCount() - : (high.getDataSetIndex() + 1); - if (maxDataSetIndex - minDataSetIndex < 1) continue; + IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex()); - for (int dataSetIndex = minDataSetIndex; - dataSetIndex < maxDataSetIndex; - dataSetIndex++) { + if (set == null || !set.isHighlightEnabled()) + continue; - IBarDataSet set = barData.getDataSetByIndex(dataSetIndex); + BarEntry e = set.getEntryForXPos(high.getX()); - if (set == null || !set.isHighlightEnabled()) - continue; - - Transformer trans = mChart.getTransformer(set.getAxisDependency()); + if (!isInBoundsX(e, set)) + continue; - mHighlightPaint.setColor(set.getHighLightColor()); - mHighlightPaint.setAlpha(set.getHighLightAlpha()); + Transformer trans = mChart.getTransformer(set.getAxisDependency()); - float x = high.getX(); + mHighlightPaint.setColor(set.getHighLightColor()); + mHighlightPaint.setAlpha(set.getHighLightAlpha()); - BarEntry e = set.getEntryForXPos(x); - float entryIndex = set.getEntryIndex(e); + boolean isStack = high.getStackIndex() < 0 ? false : true; - if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX()) - continue; + final float y1; + final float y2; - boolean isStack = high.getStackIndex() < 0 ? false : true; + if (isStack) { + y1 = high.getRange().from; + y2 = high.getRange().to; + } else { + y1 = e.getY(); + y2 = 0.f; + } - final float y1; - final float y2; + prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans); - if (isStack) { - y1 = high.getRange().from; - y2 = high.getRange().to; - } else { - y1 = e.getY(); - y2 = 0.f; - } + setHighlightDrawPos(high, mBarRect); - prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans); - - c.drawRect(mBarRect, mHighlightPaint); - -// if (mChart.isDrawHighlightArrowEnabled()) { -// -// mHighlightPaint.setAlpha(255); -// -// // distance between highlight arrow and bar -// float offsetY = mAnimator.getPhaseY() * 0.07f; -// -// float[] values = new float[9]; -// trans.getPixelToValueMatrix().getValues(values); -// final float xToYRel = Math.abs( -// values[Matrix.MSCALE_Y] / values[Matrix.MSCALE_X]); -// -// final float arrowWidth = barData.getBarWidth(); -// final float arrowWidthHalf = arrowWidth / 2f; -// final float arrowHeight = arrowWidth * xToYRel; -// -// final float yArrow = (y1 > -y2 ? y1 : y1) * mAnimator.getPhaseY(); -// -// Path arrow = new Path(); -// arrow.moveTo(e.getX() - arrowWidthHalf, yArrow + offsetY + arrowHeight); -// arrow.lineTo(e.getX(), yArrow + offsetY); -// arrow.lineTo(e.getX() + arrowWidthHalf, yArrow + offsetY + arrowHeight); -// -// trans.pathValueToPixel(arrow); -// c.drawPath(arrow, mHighlightPaint); -// } - } + c.drawRect(mBarRect, mHighlightPaint); } + } + /** + * Sets the drawing position of the highlight object based on the riven bar-rect. + * @param high + */ + protected void setHighlightDrawPos(Highlight high, RectF bar) { + high.setDraw(bar.centerX(), bar.top); } @Override 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 b21cbef0e4..60697eac7a 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 @@ -1,6 +1,7 @@ package com.github.mikephil.charting.renderer; import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; @@ -17,6 +18,27 @@ public BarLineScatterCandleBubbleRenderer(ChartAnimator animator, ViewPortHandle super(animator, viewPortHandler); } + /** + * Checks if the provided entry object is in bounds for drawing considering the current animation phase. + * + * @param e + * @param set + * @return + */ + protected boolean isInBoundsX(Entry e, IBarLineScatterCandleBubbleDataSet set) { + + if (e == null) + return false; + + float entryIndex = set.getEntryIndex(e); + + if (e == null || entryIndex >= set.getEntryCount() * mAnimator.getPhaseX()) { + return false; + } else { + return true; + } + } + /** * Calculates and returns the x-bounds for the given DataSet in terms of index in their values array. This * includes minimum and maximum visible x, as well as range. 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 7cf908ab8a..18884fd7ce 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 @@ -184,76 +184,62 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { for (Highlight high : indices) { - final int minDataSetIndex = high.getDataSetIndex() == -1 - ? 0 - : high.getDataSetIndex(); - final int maxDataSetIndex = high.getDataSetIndex() == -1 - ? bubbleData.getDataSetCount() - : (high.getDataSetIndex() + 1); - if (maxDataSetIndex - minDataSetIndex < 1) continue; + IBubbleDataSet set = bubbleData.getDataSetByIndex(high.getDataSetIndex()); - for (int dataSetIndex = minDataSetIndex; dataSetIndex < maxDataSetIndex; dataSetIndex++) { - - IBubbleDataSet dataSet = bubbleData.getDataSetByIndex(dataSetIndex); - - if (dataSet == null || !dataSet.isHighlightEnabled()) - continue; - - final BubbleEntry entry = dataSet.getEntryForXPos(high.getX()); + if (set == null || !set.isHighlightEnabled()) + continue; - if (entry == null) - continue; + final BubbleEntry entry = set.getEntryForXPos(high.getX()); - XBounds bounds = getXBounds(mChart, dataSet); + if (!isInBoundsX(entry, set)) + continue; - Transformer trans = mChart.getTransformer(dataSet.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 = dataSet.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); - float shapeHalf = getShapeSize(entry.getSize(), - dataSet.getMaxSize(), - referenceSize, - normalizeSize) / 2f; + high.setDraw(pointBuffer[0], pointBuffer[1]); - if (!mViewPortHandler.isInBoundsTop(pointBuffer[1] + shapeHalf) - || !mViewPortHandler.isInBoundsBottom(pointBuffer[1] - shapeHalf)) - continue; + float shapeHalf = getShapeSize(entry.getSize(), + set.getMaxSize(), + referenceSize, + normalizeSize) / 2f; - if (!mViewPortHandler.isInBoundsLeft(pointBuffer[0] + shapeHalf)) - continue; + if (!mViewPortHandler.isInBoundsTop(pointBuffer[1] + shapeHalf) + || !mViewPortHandler.isInBoundsBottom(pointBuffer[1] - shapeHalf)) + continue; - if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) - break; + if (!mViewPortHandler.isInBoundsLeft(pointBuffer[0] + shapeHalf)) + continue; - if (high.getX() < bounds.min || high.getX() > bounds.max) - continue; + if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) + break; - final int originalColor = dataSet.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(dataSet.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 9ced6efa05..294dcedf74 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 @@ -308,40 +308,26 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { for (Highlight high : indices) { - final int minDataSetIndex = high.getDataSetIndex() == -1 - ? 0 - : high.getDataSetIndex(); - final int maxDataSetIndex = high.getDataSetIndex() == -1 - ? candleData.getDataSetCount() - : (high.getDataSetIndex() + 1); - if (maxDataSetIndex - minDataSetIndex < 1) continue; + ICandleDataSet set = candleData.getDataSetByIndex(high.getDataSetIndex()); - for (int dataSetIndex = minDataSetIndex; - dataSetIndex < maxDataSetIndex; - dataSetIndex++) { - - float x = high.getX(); // get the - // x-position - - ICandleDataSet set = mChart.getCandleData().getDataSetByIndex(dataSetIndex); + if (set == null || !set.isHighlightEnabled()) + continue; - if (set == null || !set.isHighlightEnabled()) - continue; + CandleEntry e = set.getEntryForXPos(high.getX()); - CandleEntry e = set.getEntryForXPos(x); + if (!isInBoundsX(e, set)) + continue; - if (e == null) - continue; + float lowValue = e.getLow() * mAnimator.getPhaseY(); + float highValue = e.getHigh() * mAnimator.getPhaseY(); + float y = (lowValue + highValue) / 2f; - float lowValue = e.getLow() * mAnimator.getPhaseY(); - float highValue = e.getHigh() * mAnimator.getPhaseY(); - float y = (lowValue + highValue) / 2f; + PointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), y); - PointD px = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(x, y); + high.setDraw((float) pix.x, (float) pix.y); - // draw the lines - drawHighlightLines(c, (float) px.x, (float) px.y, set); - } + // draw the lines + drawHighlightLines(c, (float) pix.x, (float) pix.y, set); } } } 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 3952b90d90..66f3e96539 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 @@ -3,6 +3,7 @@ import android.graphics.Canvas; import android.graphics.Paint.Align; +import android.graphics.RectF; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.buffer.BarBuffer; @@ -10,6 +11,7 @@ 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.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; @@ -291,6 +293,11 @@ protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHa trans.rectToPixelPhaseHorizontal(mBarRect, mAnimator.getPhaseY()); } + @Override + protected void setHighlightDrawPos(Highlight high, RectF bar) { + high.setDraw(bar.centerY(), bar.right); + } + @Override protected boolean isDrawingValuesAllowed(ChartInterface chart) { return chart.getData().getEntryCount() < chart.getMaxVisibleCount() 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 ac0e57d2de..d6fe125cf1 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 @@ -622,34 +622,23 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { for (Highlight high : indices) { - final int minDataSetIndex = high.getDataSetIndex() == -1 - ? 0 - : high.getDataSetIndex(); - final int maxDataSetIndex = high.getDataSetIndex() == -1 - ? lineData.getDataSetCount() - : (high.getDataSetIndex() + 1); - if (maxDataSetIndex - minDataSetIndex < 1) continue; + ILineDataSet set = lineData.getDataSetByIndex(high.getDataSetIndex()); - for (int dataSetIndex = minDataSetIndex; - dataSetIndex < maxDataSetIndex; - dataSetIndex++) { - - ILineDataSet set = lineData.getDataSetByIndex(dataSetIndex); + if (set == null || !set.isHighlightEnabled()) + continue; - if (set == null || !set.isHighlightEnabled()) - continue; + Entry e = set.getEntryForXPos(high.getX()); - float x = high.getX(); - float y = high.getY() * mAnimator.getPhaseY(); + if (!isInBoundsX(e, set)) + continue; - if (x > mChart.getXChartMax() * mAnimator.getPhaseX()) - continue; + PointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), e.getY() * mAnimator + .getPhaseY()); - PointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(x, y); + high.setDraw((float) pix.x, (float) pix.y); - // draw the lines - drawHighlightLines(c, (float) pix.x, (float) pix.y, set); - } + // draw the lines + drawHighlightLines(c, (float) pix.x, (float) pix.y, 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 42a934f0ef..6029446bda 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 @@ -121,7 +121,7 @@ protected void drawDataSet(Canvas c, IRadarDataSet dataSet, int mostEntries) { surface.close(); - if(dataSet.isDrawFilledEnabled()) { + if (dataSet.isDrawFilledEnabled()) { final Drawable drawable = dataSet.getFillDrawable(); if (drawable != null) { @@ -176,7 +176,8 @@ public void drawValues(Canvas c) { (entry.getY() - mChart.getYChartMin()) * factor * phaseY, sliceangle * j * phaseX + mChart.getRotationAngle()); - drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, p.x, p.y - yoffset, dataSet.getValueTextColor(j)); + drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, p.x, p.y - yoffset, dataSet.getValueTextColor + (j)); } } } @@ -239,17 +240,33 @@ protected void drawWeb(Canvas c) { @Override public void drawHighlighted(Canvas c, Highlight[] indices) { - for (int i = 0; i < indices.length; i++) { + float sliceangle = mChart.getSliceAngle(); + + // calculate the factor that is needed for transforming the value to + // pixels + float factor = mChart.getFactor(); + + PointF center = mChart.getCenterOffsets(); - IRadarDataSet set = mChart.getData() - .getDataSetByIndex(indices[i] - .getDataSetIndex()); + RadarData radarData = mChart.getData(); + + for (Highlight high : indices) { + + IRadarDataSet set = radarData.getDataSetByIndex(high.getDataSetIndex()); if (set == null || !set.isHighlightEnabled()) continue; - Highlight high = indices[i]; - PointF p = new PointF(high.getXPx(), high.getYPx()); + Entry e = set.getEntryForXPos(high.getX()); + + if (!isInBoundsX(e, set)) + continue; + + PointF p = Utils.getPosition(center, + (e.getY() - mChart.getYChartMin()) * factor * mAnimator.getPhaseY(), + sliceangle * high.getX() * mAnimator.getPhaseX() + mChart.getRotationAngle()); + + high.setDraw(p.x, p.y); // draw the lines drawHighlightLines(c, p.x, p.y, set); @@ -280,12 +297,12 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } public void drawHighlightCircle(Canvas c, - PointF point, - float innerRadius, - float outerRadius, - int fillColor, - int strokeColor, - float strokeWidth) { + PointF point, + float innerRadius, + float outerRadius, + int fillColor, + int strokeColor, + float strokeWidth) { c.save(); outerRadius = Utils.convertDpToPixel(outerRadius); 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 94c881c82a..c4381aab3c 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 @@ -355,34 +355,23 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { for (Highlight high : indices) { - final int minDataSetIndex = high.getDataSetIndex() == -1 - ? 0 - : high.getDataSetIndex(); - final int maxDataSetIndex = high.getDataSetIndex() == -1 - ? scatterData.getDataSetCount() - : (high.getDataSetIndex() + 1); - if (maxDataSetIndex - minDataSetIndex < 1) continue; + IScatterDataSet set = scatterData.getDataSetByIndex(high.getDataSetIndex()); - for (int dataSetIndex = minDataSetIndex; - dataSetIndex < maxDataSetIndex; - dataSetIndex++) { + if (set == null || !set.isHighlightEnabled()) + continue; - IScatterDataSet set = scatterData.getDataSetByIndex(dataSetIndex); + Entry e = set.getEntryForXPos(high.getX()); - if (set == null || !set.isHighlightEnabled()) - continue; - - float x = high.getX(); - float y = high.getY() * mAnimator.getPhaseY(); + if (!isInBoundsX(e, set)) + continue; - if (x > mChart.getXChartMax() * mAnimator.getPhaseX()) - continue; + PointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), e.getY() * mAnimator + .getPhaseY()); - PointD px = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(x, y); + high.setDraw((float) pix.x, (float) pix.y); - // draw the lines - drawHighlightLines(c, (float) px.x, (float) px.y, set); - } + // draw the lines + drawHighlightLines(c, (float) pix.x, (float) pix.y, set); } } } From 5ff8f9609b28af541b074aa3c6bb18e9c4fdfa81 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 13 Jun 2016 23:19:51 +0200 Subject: [PATCH 227/606] More fixes related to MarkerView drawing --- .../mpchartexample/BubbleChartActivity.java | 2 +- .../mpchartexample/RadarChartActivitry.java | 10 ++--- .../mikephil/charting/charts/PieChart.java | 2 +- .../mikephil/charting/charts/RadarChart.java | 17 +++++-- .../mikephil/charting/data/PieEntry.java | 12 +++++ .../mikephil/charting/data/RadarData.java | 6 +++ .../mikephil/charting/data/RadarDataSet.java | 8 ++-- .../mikephil/charting/data/RadarEntry.java | 44 +++++++++++++++++++ .../realm/base/RealmLineRadarDataSet.java | 2 +- .../implementation/RealmLineDataSet.java | 3 +- .../implementation/RealmRadarDataSet.java | 4 +- .../charting/highlight/RadarHighlighter.java | 6 +-- .../interfaces/datasets/IRadarDataSet.java | 4 +- .../charting/renderer/RadarChartRenderer.java | 13 +++--- 14 files changed, 102 insertions(+), 31 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarEntry.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java index 7ffeef9aa6..aabd600026 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -68,7 +68,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setMaxVisibleValueCount(200); mChart.setPinchZoom(true); - mSeekBarX.setProgress(1); + mSeekBarX.setProgress(10); mSeekBarY.setProgress(50); Legend l = mChart.getLegend(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index 0d3cacf35a..3a97d4eed8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -17,9 +17,9 @@ 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.Entry; 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.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; @@ -210,20 +210,20 @@ public void setData() { float min = 20; int cnt = 5; - ArrayList yVals1 = new ArrayList(); - ArrayList yVals2 = new ArrayList(); + ArrayList yVals1 = new ArrayList(); + ArrayList yVals2 = new ArrayList(); // IMPORTANT: In a PieChart, no values (Entry) should have the same // xIndex (even if from different DataSets), since no values can be // drawn above each other. for (int i = 0; i < cnt; i++) { float val = (float) (Math.random() * mult) + min; - yVals1.add(new Entry(i, val)); + yVals1.add(new RadarEntry(val)); } for (int i = 0; i < cnt; i++) { float val = (float) (Math.random() * mult) + min; - yVals2.add(new Entry(i, val)); + yVals2.add(new RadarEntry(val)); } RadarDataSet set1 = new RadarDataSet(yVals1, "Last Week"); 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 f070da9988..e56ef96b2e 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 @@ -183,7 +183,7 @@ protected float[] getMarkerPosition(Highlight highlight) { float rotationAngle = getRotationAngle(); - int entryIndex = getData().getDataSet().getEntryIndex(highlight.getX(), DataSet.Rounding.CLOSEST); + int entryIndex = (int) highlight.getX(); // offset needed to center the drawn text in the slice float offset = mDrawAngles[entryIndex] / 2; 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 27c36fbad1..ab953d79f9 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 @@ -201,12 +201,21 @@ public int getIndexForAngle(float angle) { float sliceangle = getSliceAngle(); - for (int i = 0; i < mData.getMaxEntryCountSet().getEntryCount(); i++) { - if (sliceangle * (i + 1) - sliceangle / 2f > a) - return i; + int max = mData.getMaxEntryCountSet().getEntryCount(); + + int index = 0; + + for (int i = 0; i < max; i++) { + + float referenceAngle = sliceangle * (i + 1) - sliceangle / 2f; + + if (referenceAngle > a) { + index = i; + break; + } } - return 0; + return index; } /** 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 52853629fd..6ab31f140c 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 @@ -45,6 +45,18 @@ public void setLabel(String label) { this.label = label; } + @Deprecated + @Override + public void setX(float x) { + super.setX(x); + } + + @Deprecated + @Override + public float getX() { + return super.getX(); + } + public PieEntry copy() { PieEntry e = new PieEntry(getY(), label, getData()); return e; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java index 811414139a..0c1dbe5505 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.data; +import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; import java.util.ArrayList; @@ -49,4 +50,9 @@ public void setLabels(String... labels) { public List getLabels() { return mLabels; } + + @Override + public Entry getEntryForHighlight(Highlight highlight) { + return getDataSetByIndex(highlight.getDataSetIndex()).getEntryForIndex((int) highlight.getX()); + } } 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 baa81340e1..f18aa8c23a 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 @@ -9,7 +9,7 @@ import java.util.ArrayList; import java.util.List; -public class RadarDataSet extends LineRadarDataSet implements IRadarDataSet { +public class RadarDataSet extends LineRadarDataSet implements IRadarDataSet { /// flag indicating whether highlight circle should be drawn or not protected boolean mDrawHighlightCircleEnabled = false; @@ -25,7 +25,7 @@ public class RadarDataSet extends LineRadarDataSet implements IRadarDataS protected float mHighlightCircleOuterRadius = 4.0f; protected float mHighlightCircleStrokeWidth = 2.0f; - public RadarDataSet(List yVals, String label) { + public RadarDataSet(List yVals, String label) { super(yVals, label); } @@ -114,9 +114,9 @@ public void setHighlightCircleStrokeWidth(float strokeWidth) } @Override - public DataSet copy() { + public DataSet copy() { - List yVals = new ArrayList(); + List yVals = new ArrayList(); for (int i = 0; i < mValues.size(); i++) { yVals.add(mValues.get(i).copy()); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarEntry.java new file mode 100644 index 0000000000..02fdce7d32 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarEntry.java @@ -0,0 +1,44 @@ +package com.github.mikephil.charting.data; + +import android.annotation.SuppressLint; + +/** + * Created by philipp on 13/06/16. + */ +@SuppressLint("ParcelCreator") +public class RadarEntry extends Entry { + + public RadarEntry(float value) { + super(0f, value); + } + + public RadarEntry(float value, Object data) { + super(0f, value, data); + } + + /** + * This is the same as getY(). Returns the value of the RadarEntry. + * + * @return + */ + public float getValue() { + return getY(); + } + + public RadarEntry copy() { + RadarEntry e = new RadarEntry(getY(), getData()); + return e; + } + + @Deprecated + @Override + public void setX(float x) { + super.setX(x); + } + + @Deprecated + @Override + public float getX() { + return super.getX(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java index 31010f2489..a7b9cb29d8 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java @@ -14,7 +14,7 @@ /** * Created by Philipp Jahoda on 08/11/15. */ -public abstract class RealmLineRadarDataSet extends RealmLineScatterCandleRadarDataSet implements ILineRadarDataSet { +public abstract class RealmLineRadarDataSet extends RealmLineScatterCandleRadarDataSet implements ILineRadarDataSet { /** the color that is used for filling the line surface */ private int mFillColor = Color.rgb(140, 234, 255); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java index c985aee55c..5c4a58d15a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java @@ -4,6 +4,7 @@ import android.graphics.Color; import android.graphics.DashPathEffect; +import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.realm.base.RealmLineRadarDataSet; import com.github.mikephil.charting.formatter.DefaultFillFormatter; @@ -21,7 +22,7 @@ /** * Created by Philipp Jahoda on 21/10/15. */ -public class RealmLineDataSet extends RealmLineRadarDataSet implements ILineDataSet { +public class RealmLineDataSet extends RealmLineRadarDataSet implements ILineDataSet { /** Drawing mode for this line dataset **/ private LineDataSet.Mode mMode = LineDataSet.Mode.LINEAR; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java index 880e68586f..dac3a59c40 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java @@ -2,6 +2,8 @@ import android.graphics.Color; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.RadarEntry; import com.github.mikephil.charting.data.realm.base.RealmLineRadarDataSet; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -12,7 +14,7 @@ /** * Created by Philipp Jahoda on 07/11/15. */ -public class RealmRadarDataSet extends RealmLineRadarDataSet implements IRadarDataSet { +public class RealmRadarDataSet extends RealmLineRadarDataSet implements IRadarDataSet { /// flag indicating whether highlight circle should be drawn or not protected boolean mDrawHighlightCircleEnabled = false; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java index c113d7d26d..706dab07f4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java @@ -69,12 +69,8 @@ protected List getHighlightsAtIndex(int index) { float y = (entry.getY() - mChart.getYChartMin()); - if (Float.isNaN(y)) - continue; - PointF p = Utils.getPosition( - mChart.getCenterOffsets(), - y * factor * phaseY, + mChart.getCenterOffsets(), y * factor * phaseY, sliceangle * index * phaseX + mChart.getRotationAngle()); vals.add(new Highlight(index, entry.getY(), p.x, p.y, i, dataSet.getAxisDependency())); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java index 6d725df7d0..8af00d5376 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java @@ -1,11 +1,11 @@ package com.github.mikephil.charting.interfaces.datasets; -import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.RadarEntry; /** * Created by Philipp Jahoda on 03/11/15. */ -public interface IRadarDataSet extends ILineRadarDataSet { +public interface IRadarDataSet extends ILineRadarDataSet { /// flag indicating whether highlight circle should be drawn or not boolean isDrawHighlightCircleEnabled(); 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 6029446bda..90b9dcf225 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 @@ -10,8 +10,8 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.RadarData; +import com.github.mikephil.charting.data.RadarEntry; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; import com.github.mikephil.charting.utils.ColorTemplate; @@ -97,7 +97,7 @@ protected void drawDataSet(Canvas c, IRadarDataSet dataSet, int mostEntries) { mRenderPaint.setColor(dataSet.getColor(j)); - Entry e = dataSet.getEntryForIndex(j); + RadarEntry e = dataSet.getEntryForIndex(j); PointF p = Utils.getPosition( center, @@ -169,7 +169,7 @@ public void drawValues(Canvas c) { for (int j = 0; j < dataSet.getEntryCount(); j++) { - Entry entry = dataSet.getEntryForIndex(j); + RadarEntry entry = dataSet.getEntryForIndex(j); PointF p = Utils.getPosition( center, @@ -257,13 +257,14 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (set == null || !set.isHighlightEnabled()) continue; - Entry e = set.getEntryForXPos(high.getX()); + RadarEntry e = set.getEntryForIndex((int) high.getX()); if (!isInBoundsX(e, set)) continue; - PointF p = Utils.getPosition(center, - (e.getY() - mChart.getYChartMin()) * factor * mAnimator.getPhaseY(), + float y = (e.getY() - mChart.getYChartMin()); + + PointF p = Utils.getPosition(center, y * factor * mAnimator.getPhaseY(), sliceangle * high.getX() * mAnimator.getPhaseX() + mChart.getRotationAngle()); high.setDraw(p.x, p.y); From 09cfb2334bf54b98f38035d2e4507d52d1fa5097 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 14 Jun 2016 13:18:40 +0200 Subject: [PATCH 228/606] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6390228dae..4c784851b8 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Remember: *It's all about the looks.* [**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. -As an additional feature, this library allows cross-platform development between Android and iOS as an iOS version of this library is also available: [**ios-charts**](https://github.com/danielgindi/ios-charts) :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: 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). From a2e88ebeddd06aaa18042ef287021874035a1653 Mon Sep 17 00:00:00 2001 From: wajdi chamakhi Date: Wed, 15 Jun 2016 12:27:45 +0100 Subject: [PATCH 229/606] Author: wajdi chamakhi Date: Wed Jun 15 11:30:11 2016 +0100 Changing ScatterShape from ENUM to HashMap of String . Creating a provider which provides ShapeRenderer. Creating CustomShapeRenderers imlementing ShapeRender with different renderShape() method Replacing the Switch Statement by ShapeRenderer.renderShape(); => Better OOP implementation. Scalable easily. To add new Shape you only need to create a class implementing ShapeRenderer and override renderShape method and then add the class in the provider ScatterChart.registerShapeRenderer(String scatterShapeName, ShapeRenderer shapeRenderer). --- .../mpchartexample/ScatterChartActivity.java | 11 +- .../fragments/SimpleFragment.java | 5 +- .../realm/RealmDatabaseActivityScatter.java | 3 +- .../charting/charts/ScatterChart.java | 63 +++-- .../charting/data/ScatterDataSet.java | 8 +- .../implementation/RealmScatterDataSet.java | 10 +- .../interfaces/datasets/IScatterDataSet.java | 3 +- .../renderer/ScatterChartRenderer.java | 254 +----------------- .../ChevronDownShapeRenderer.java | 56 ++++ .../ShapeRenders/ChevronUpShapeRenderer.java | 55 ++++ .../ShapeRenders/CircleShapeRenderer.java | 75 ++++++ .../ShapeRenders/CrossShapeRenderer.java | 55 ++++ .../renderer/ShapeRenders/ScatterShape.java | 44 +++ .../renderer/ShapeRenders/ShapeRenderer.java | 22 ++ .../ShapeRenders/SquareShapeRenderer.java | 75 ++++++ .../ShapeRenders/TriangleShapeRenderer.java | 90 +++++++ .../renderer/ShapeRenders/XShapeRenderer.java | 57 ++++ 17 files changed, 598 insertions(+), 288 deletions(-) create mode 100644 MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ChevronDownShapeRenderer.java create mode 100644 MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ChevronUpShapeRenderer.java create mode 100644 MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/CircleShapeRenderer.java create mode 100644 MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/CrossShapeRenderer.java create mode 100644 MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ScatterShape.java create mode 100644 MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ShapeRenderer.java create mode 100644 MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/SquareShapeRenderer.java create mode 100644 MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/TriangleShapeRenderer.java create mode 100644 MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/XShapeRenderer.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index 95b3954fb8..03d98872d1 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.Color; import android.graphics.Typeface; import android.os.Bundle; import android.util.Log; @@ -13,7 +12,6 @@ import android.widget.TextView; import com.github.mikephil.charting.charts.ScatterChart; -import com.github.mikephil.charting.charts.ScatterChart.ScatterShape; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.components.XAxis; @@ -21,11 +19,10 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -191,15 +188,15 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // create a dataset and give it a type ScatterDataSet set1 = new ScatterDataSet(yVals1, "DS 1"); - set1.setScatterShape(ScatterShape.SQUARE); + set1.setScatterShape(ScatterShape.getScatterShapeNames().get(ScatterShape.SQUARE)); set1.setColor(ColorTemplate.COLORFUL_COLORS[0]); ScatterDataSet set2 = new ScatterDataSet(yVals2, "DS 2"); - set2.setScatterShape(ScatterShape.CIRCLE); + set2.setScatterShape(ScatterShape.getScatterShapeNames().get(ScatterShape.CIRCLE)); set2.setScatterShapeHoleColor(ColorTemplate.COLORFUL_COLORS[3]); set2.setScatterShapeHoleRadius(4f); set2.setColor(ColorTemplate.COLORFUL_COLORS[1]); ScatterDataSet set3 = new ScatterDataSet(yVals3, "DS 3"); - set3.setScatterShape(ScatterShape.CROSS); + set3.setScatterShape(ScatterShape.getScatterShapeNames().get(ScatterShape.CROSS)); set3.setColor(ColorTemplate.COLORFUL_COLORS[2]); set1.setScatterShapeSize(8f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java index 25d103e8ce..afb80fe802 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java @@ -8,8 +8,6 @@ import android.view.View; import android.view.ViewGroup; -import com.github.mikephil.charting.charts.ScatterChart; -import com.github.mikephil.charting.charts.ScatterChart.ScatterShape; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; @@ -24,6 +22,7 @@ import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.FileUtils; @@ -71,7 +70,7 @@ protected ScatterData generateScatterData(int dataSets, float range, int count) ArrayList sets = new ArrayList(); - ScatterShape[] shapes = ScatterChart.getAllPossibleShapes(); + String[] shapes = ScatterShape.getAllPossibleShapes(); for(int i = 0; i < dataSets; i++) { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java index b28bf137a8..470bb7f0e0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java @@ -8,6 +8,7 @@ import com.github.mikephil.charting.data.realm.implementation.RealmScatterData; import com.github.mikephil.charting.data.realm.implementation.RealmScatterDataSet; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.R; import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; @@ -57,7 +58,7 @@ private void setData() { set.setLabel("Realm ScatterDataSet"); set.setScatterShapeSize(9f); set.setColor(ColorTemplate.rgb("#CDDC39")); - set.setScatterShape(ScatterChart.ScatterShape.CIRCLE); + set.setScatterShape(ScatterShape.getScatterShapeNames().get(ScatterShape.CIRCLE)); ArrayList dataSets = new ArrayList(); dataSets.add(set); // add the dataset diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java index 292568a6b3..f13857e75a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java @@ -7,23 +7,63 @@ import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; import com.github.mikephil.charting.renderer.ScatterChartRenderer; +import com.github.mikephil.charting.renderer.ShapeRenders.ChevronDownShapeRenderer; +import com.github.mikephil.charting.renderer.ShapeRenders.ChevronUpShapeRenderer; +import com.github.mikephil.charting.renderer.ShapeRenders.CircleShapeRenderer; +import com.github.mikephil.charting.renderer.ShapeRenders.CrossShapeRenderer; +import com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape; +import com.github.mikephil.charting.renderer.ShapeRenders.ShapeRenderer; +import com.github.mikephil.charting.renderer.ShapeRenders.SquareShapeRenderer; +import com.github.mikephil.charting.renderer.ShapeRenders.TriangleShapeRenderer; +import com.github.mikephil.charting.renderer.ShapeRenders.XShapeRenderer; + +import java.util.HashMap; /** * The ScatterChart. Draws dots, triangles, squares and custom shapes into the * Chart-View. CIRCLE and SCQUARE offer the best performance, TRIANGLE has the * worst performance. - * + * * @author Philipp Jahoda */ public class ScatterChart extends BarLineChartBase implements ScatterDataProvider { + + /** + * Dictionary of ShapeRenderer which are responsible for drawing custom shapes. + * Can add to it your custom shapes. + * CustomShapeRenderer Implements ShapeRenderer{} + */ + private static HashMap shapeRendererList; + + public static void registerShapeRenderer(String scatterShapeName, ShapeRenderer shapeRenderer) { + if (shapeRendererList == null) { + shapeRendererList = new HashMap<>(); + } + shapeRendererList.put(scatterShapeName, shapeRenderer); + } + + public static ShapeRenderer getShapeRenderer(String scatterShapeName) { + if (shapeRendererList == null) { + initShapeRenderer(); + } + return shapeRendererList.get(scatterShapeName); + } + /** - * enum that defines the shape that is drawn where the values are + * Init ShapeRendererList */ - public enum ScatterShape { - SQUARE, CIRCLE, TRIANGLE, CROSS, X, + private static void initShapeRenderer() { + registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.SQUARE), new SquareShapeRenderer()); + registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.CIRCLE), new CircleShapeRenderer()); + registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.TRIANGLE), new TriangleShapeRenderer()); + registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.CROSS), new CrossShapeRenderer()); + registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.X), new XShapeRenderer()); + registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.CHEVRON_UP), new ChevronUpShapeRenderer()); + registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.CHEVRON_DOWN), new ChevronDownShapeRenderer()); } + public ScatterChart(Context context) { super(context); } @@ -36,6 +76,7 @@ public ScatterChart(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } + @Override protected void init() { super.init(); @@ -55,18 +96,10 @@ protected void calcMinMax() { mXAxis.mAxisRange = Math.abs(mXAxis.mAxisMaximum - mXAxis.mAxisMinimum); } - /** - * Returns all possible predefined ScatterShapes. - * - * @return - */ - public static ScatterShape[] getAllPossibleShapes() { - return new ScatterShape[] { - ScatterShape.SQUARE, ScatterShape.CIRCLE, ScatterShape.TRIANGLE, ScatterShape.CROSS - }; - } public ScatterData getScatterData() { return mData; - }; + } + + ; } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java index 199a20638b..3281fd0d54 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java @@ -1,8 +1,8 @@ package com.github.mikephil.charting.data; -import com.github.mikephil.charting.charts.ScatterChart.ScatterShape; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape; import com.github.mikephil.charting.utils.ColorTemplate; import java.util.ArrayList; @@ -19,7 +19,7 @@ public class ScatterDataSet extends LineScatterCandleRadarDataSet impleme * the type of shape that is set to be drawn where the values are at, * default ScatterShape.SQUARE */ - private ScatterShape mScatterShape = ScatterShape.SQUARE; + private String mScatterShape = ScatterShape.getScatterShapeNames().get(ScatterShape.SQUARE); /** * The radius of the hole in the shape (applies to Square, Circle and Triangle) @@ -84,12 +84,12 @@ public float getScatterShapeSize() { * * @param shape */ - public void setScatterShape(ScatterShape shape) { + public void setScatterShape(String shape) { mScatterShape = shape; } @Override - public ScatterShape getScatterShape() { + public String getScatterShape() { return mScatterShape; } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java index 88bbc01dc6..e15418852c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java @@ -1,13 +1,11 @@ package com.github.mikephil.charting.data.realm.implementation; -import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.realm.base.RealmLineScatterCandleRadarDataSet; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Utils; -import io.realm.DynamicRealmObject; import io.realm.RealmObject; import io.realm.RealmResults; @@ -25,7 +23,7 @@ public class RealmScatterDataSet extends RealmLineScatter * the type of shape that is set to be drawn where the values are at, * default ScatterShape.SQUARE */ - private ScatterChart.ScatterShape mScatterShape = ScatterChart.ScatterShape.SQUARE; + private String mScatterShape = ScatterShape.getScatterShapeNames().get(ScatterShape.SQUARE); /** * The radius of the hole in the shape (applies to Square, Circle and Triangle) @@ -89,12 +87,12 @@ public float getScatterShapeSize() { * * @param shape */ - public void setScatterShape(ScatterChart.ScatterShape shape) { + public void setScatterShape(String shape) { mScatterShape = shape; } @Override - public ScatterChart.ScatterShape getScatterShape() { + public String getScatterShape() { return mScatterShape; } diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java index d3a6d3400d..38742d65fb 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java @@ -1,6 +1,5 @@ package com.github.mikephil.charting.interfaces.datasets; -import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.data.Entry; /** @@ -20,7 +19,7 @@ public interface IScatterDataSet extends ILineScatterCandleRadarDataSet { * * @return */ - ScatterChart.ScatterShape getScatterShape(); + String getScatterShape(); /** * Returns radius of the hole in the shape diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java index 754b260a20..c54c962ec8 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -2,18 +2,15 @@ package com.github.mikephil.charting.renderer; import android.graphics.Canvas; -import android.graphics.Paint.Style; -import android.graphics.Path; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.buffer.ScatterBuffer; -import com.github.mikephil.charting.charts.ScatterChart.ScatterShape; +import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.ScatterData; 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.utils.ColorTemplate; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -65,14 +62,9 @@ protected void drawDataSet(Canvas c, IScatterDataSet dataSet) { float phaseY = mAnimator.getPhaseY(); final float shapeSize = Utils.convertDpToPixel(dataSet.getScatterShapeSize()); - final float shapeHalf = shapeSize / 2f; - final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); - final float shapeHoleSize = shapeHoleSizeHalf * 2.f; final int shapeHoleColor = dataSet.getScatterShapeHoleColor(); - final float shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.f; - final float shapeStrokeSizeHalf = shapeStrokeSize / 2.f; - ScatterShape shape = dataSet.getScatterShape(); + String shape = dataSet.getScatterShape(); ScatterBuffer buffer = mScatterBuffers[mChart.getScatterData().getIndexOfDataSet( dataSet)]; @@ -81,248 +73,10 @@ protected void drawDataSet(Canvas c, IScatterDataSet dataSet) { trans.pointValuesToPixel(buffer.buffer); - switch (shape) { - case SQUARE: + ScatterChart.getShapeRenderer(shape) + .renderShape(c, dataSet,mViewPortHandler, buffer, mRenderPaint, shapeHoleColor, shapeSize); - for (int i = 0; i < buffer.size(); i += 2) { - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - mRenderPaint.setColor(dataSet.getColor(i / 2)); - - if (shapeHoleSize > 0.0) { - mRenderPaint.setStyle(Style.STROKE); - mRenderPaint.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, - mRenderPaint); - - if (shapeHoleColor != ColorTemplate.COLOR_NONE) { - mRenderPaint.setStyle(Style.FILL); - - mRenderPaint.setColor(shapeHoleColor); - c.drawRect(buffer.buffer[i] - shapeHoleSizeHalf, - buffer.buffer[i + 1] - shapeHoleSizeHalf, - buffer.buffer[i] + shapeHoleSizeHalf, - buffer.buffer[i + 1] + shapeHoleSizeHalf, - mRenderPaint); - } - - } else { - mRenderPaint.setStyle(Style.FILL); - - c.drawRect(buffer.buffer[i] - shapeHalf, - buffer.buffer[i + 1] - shapeHalf, - buffer.buffer[i] + shapeHalf, - buffer.buffer[i + 1] + shapeHalf, - mRenderPaint); - } - } - - break; - - case CIRCLE: - - for (int i = 0; i < buffer.size(); i += 2) { - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - mRenderPaint.setColor(dataSet.getColor(i / 2)); - - if (shapeHoleSize > 0.0) { - mRenderPaint.setStyle(Style.STROKE); - mRenderPaint.setStrokeWidth(shapeStrokeSize); - - c.drawCircle( - buffer.buffer[i], - buffer.buffer[i + 1], - shapeHoleSizeHalf + shapeStrokeSizeHalf, - mRenderPaint); - - if (shapeHoleColor != ColorTemplate.COLOR_NONE) { - mRenderPaint.setStyle(Style.FILL); - - mRenderPaint.setColor(shapeHoleColor); - c.drawCircle( - buffer.buffer[i], - buffer.buffer[i + 1], - shapeHoleSizeHalf, - mRenderPaint); - } - } else { - mRenderPaint.setStyle(Style.FILL); - - c.drawCircle( - buffer.buffer[i], - buffer.buffer[i + 1], - shapeHalf, - mRenderPaint); - } - } - break; - - case TRIANGLE: - - mRenderPaint.setStyle(Style.FILL); - - // create a triangle path - Path tri = new Path(); - - for (int i = 0; i < buffer.size(); i += 2) { - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - mRenderPaint.setColor(dataSet.getColor(i / 2)); - - 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); - - if (shapeHoleSize > 0.0) { - tri.lineTo(buffer.buffer[i], buffer.buffer[i + 1] - shapeHalf); - - 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); - } - - tri.close(); - - c.drawPath(tri, mRenderPaint); - tri.reset(); - - if (shapeHoleSize > 0.0 && - shapeHoleColor != ColorTemplate.COLOR_NONE) { - - mRenderPaint.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, mRenderPaint); - tri.reset(); - } - } - break; - - case CROSS: - - mRenderPaint.setStyle(Style.STROKE); - mRenderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); - - for (int i = 0; i < buffer.size(); i += 2) { - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - mRenderPaint.setColor(dataSet.getColor(i / 2)); - - c.drawLine( - buffer.buffer[i] - shapeHalf, - buffer.buffer[i + 1], - buffer.buffer[i] + shapeHalf, - buffer.buffer[i + 1], - mRenderPaint); - c.drawLine( - buffer.buffer[i], - buffer.buffer[i + 1] - shapeHalf, - buffer.buffer[i], - buffer.buffer[i + 1] + shapeHalf, - mRenderPaint); - } - break; - - case X: - - mRenderPaint.setStyle(Style.STROKE); - mRenderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); - - for (int i = 0; i < buffer.size(); i += 2) { - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - mRenderPaint.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, - mRenderPaint); - c.drawLine( - buffer.buffer[i] + shapeHalf, - buffer.buffer[i + 1] - shapeHalf, - buffer.buffer[i] - shapeHalf, - buffer.buffer[i + 1] + shapeHalf, - mRenderPaint); - } - break; - - default: - break; - } - - // else { // draw the custom-shape - // - // Path customShape = dataSet.getCustomScatterShape(); - // - // for (int j = 0; j < entries.size() * mAnimator.getPhaseX(); j += 2) { - // - // Entry e = entries.get(j / 2); - // - // if (!fitsBounds(e.getXIndex(), mMinX, mMaxX)) - // continue; - // - // if (customShape == null) - // return; - // - // mRenderPaint.setColor(dataSet.getColor(j)); - // - // Path newPath = new Path(customShape); - // newPath.offset(e.getXIndex(), e.getVal()); - // - // // transform the provided custom path - // trans.pathValueToPixel(newPath); - // c.drawPath(newPath, mRenderPaint); - // } - // } } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ChevronDownShapeRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ChevronDownShapeRenderer.java new file mode 100644 index 0000000000..5ba5e00425 --- /dev/null +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ChevronDownShapeRenderer.java @@ -0,0 +1,56 @@ +package com.github.mikephil.charting.renderer.ShapeRenders; + +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; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class ChevronDownShapeRenderer implements ShapeRenderer { + + + @Override + public void renderShape( + Canvas c, IScatterDataSet dataSet, + ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final int shapeHoleColor, final float shapeSize) { + + final float shapeHalf = shapeSize / 2f; + + + mRenderPaint.setStyle(Paint.Style.STROKE); + mRenderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + + for (int i = 0; i < buffer.size(); i += 2) { + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + break; + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + continue; + + mRenderPaint.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], + mRenderPaint); + + c.drawLine( + buffer.buffer[i], + buffer.buffer[i + 1] + (2 * shapeHalf), + buffer.buffer[i] - (2 * shapeHalf), + buffer.buffer[i + 1], + mRenderPaint); + } + + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ChevronUpShapeRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ChevronUpShapeRenderer.java new file mode 100644 index 0000000000..3b4610282b --- /dev/null +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ChevronUpShapeRenderer.java @@ -0,0 +1,55 @@ +package com.github.mikephil.charting.renderer.ShapeRenders; + +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; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class ChevronUpShapeRenderer implements ShapeRenderer { + + + @Override + public void renderShape( + Canvas c, IScatterDataSet dataSet, + ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final int shapeHoleColor, final float shapeSize) { + final float shapeHalf = shapeSize / 2f; + + + mRenderPaint.setStyle(Paint.Style.STROKE); + mRenderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + + for (int i = 0; i < buffer.size(); i += 2) { + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + break; + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + continue; + + mRenderPaint.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], + mRenderPaint); + + c.drawLine( + buffer.buffer[i], + buffer.buffer[i + 1] - (2 * shapeHalf), + buffer.buffer[i] - (2 * shapeHalf), + buffer.buffer[i + 1], + mRenderPaint); + } + + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/CircleShapeRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/CircleShapeRenderer.java new file mode 100644 index 0000000000..44151db4c0 --- /dev/null +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/CircleShapeRenderer.java @@ -0,0 +1,75 @@ +package com.github.mikephil.charting.renderer.ShapeRenders; + +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; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class CircleShapeRenderer implements ShapeRenderer { + + + @Override + public void renderShape( + Canvas c, IScatterDataSet dataSet, + ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final int shapeHoleColor, final float shapeSize) { + + final float shapeHalf = shapeSize / 2f; + final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); + final float shapeHoleSize = shapeHoleSizeHalf * 2.f; + final float shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.f; + final float shapeStrokeSizeHalf = shapeStrokeSize / 2.f; + + + for (int i = 0; i < buffer.size(); i += 2) { + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + break; + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + continue; + + mRenderPaint.setColor(dataSet.getColor(i / 2)); + + if (shapeSize > 0.0) { + mRenderPaint.setStyle(Paint.Style.STROKE); + mRenderPaint.setStrokeWidth(shapeStrokeSize); + + c.drawCircle( + buffer.buffer[i], + buffer.buffer[i + 1], + shapeHoleSizeHalf + shapeStrokeSizeHalf, + mRenderPaint); + + if (shapeHoleColor != ColorTemplate.COLOR_NONE) { + mRenderPaint.setStyle(Paint.Style.FILL); + + mRenderPaint.setColor(shapeHoleColor); + c.drawCircle( + buffer.buffer[i], + buffer.buffer[i + 1], + shapeHoleSizeHalf, + mRenderPaint); + } + } else { + mRenderPaint.setStyle(Paint.Style.FILL); + + c.drawCircle( + buffer.buffer[i], + buffer.buffer[i + 1], + shapeHalf, + mRenderPaint); + } + } + + } + +} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/CrossShapeRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/CrossShapeRenderer.java new file mode 100644 index 0000000000..44eeaa7a5b --- /dev/null +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/CrossShapeRenderer.java @@ -0,0 +1,55 @@ +package com.github.mikephil.charting.renderer.ShapeRenders; + +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; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class CrossShapeRenderer implements ShapeRenderer { + + + @Override + public void renderShape( + Canvas c, IScatterDataSet dataSet, + ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final int shapeHoleColor, final float shapeSize) { + + final float shapeHalf = shapeSize / 2f; + + + mRenderPaint.setStyle(Paint.Style.STROKE); + mRenderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + + for (int i = 0; i < buffer.size(); i += 2) { + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + break; + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + continue; + + mRenderPaint.setColor(dataSet.getColor(i / 2)); + + c.drawLine( + buffer.buffer[i] - shapeHalf, + buffer.buffer[i + 1], + buffer.buffer[i] + shapeHalf, + buffer.buffer[i + 1], + mRenderPaint); + c.drawLine( + buffer.buffer[i], + buffer.buffer[i + 1] - shapeHalf, + buffer.buffer[i], + buffer.buffer[i + 1] + shapeHalf, + mRenderPaint); + } + + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ScatterShape.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ScatterShape.java new file mode 100644 index 0000000000..76943260d0 --- /dev/null +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ScatterShape.java @@ -0,0 +1,44 @@ +package com.github.mikephil.charting.renderer.ShapeRenders; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 10:12 + */ +public class ScatterShape { + + + public static final String SQUARE = "SQUARE"; + public static final String CIRCLE = "CIRCLE"; + public static final String TRIANGLE = "TRIANGLE"; + public static final String CROSS = "CROSS"; + public static final String X = "X"; + public static final String CHEVRON_UP = "CHEVRON_UP"; + public static final String CHEVRON_DOWN = "CHEVRON_DOWN"; + + private static Map scatterShapeNames; + + public static Map getScatterShapeNames() { + if (scatterShapeNames == null) { + scatterShapeNames = new HashMap<>(); + scatterShapeNames.put(SQUARE, SQUARE); + scatterShapeNames.put(CIRCLE, CIRCLE); + scatterShapeNames.put(TRIANGLE, TRIANGLE); + scatterShapeNames.put(CROSS, CROSS); + scatterShapeNames.put(X, X); + scatterShapeNames.put(CHEVRON_UP, CHEVRON_UP); + scatterShapeNames.put(CHEVRON_DOWN, CHEVRON_DOWN); + } + return scatterShapeNames; + } + + public static String[] getAllPossibleShapes() { + String[] possibleShapes = new String[getScatterShapeNames().size()]; + for (int i = 0; i < getScatterShapeNames().size(); i++) { + possibleShapes[i] = getScatterShapeNames().get(i); + } + return possibleShapes; + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ShapeRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ShapeRenderer.java new file mode 100644 index 0000000000..0c61eab11b --- /dev/null +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/ShapeRenderer.java @@ -0,0 +1,22 @@ +package com.github.mikephil.charting.renderer.ShapeRenders; + +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.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:07 + */ +public interface ShapeRenderer { + + void renderShape( + Canvas c, IScatterDataSet dataSet, + ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final int shapeHoleColor, final float shapeSize); + + + +} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/SquareShapeRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/SquareShapeRenderer.java new file mode 100644 index 0000000000..a801dd28e5 --- /dev/null +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/SquareShapeRenderer.java @@ -0,0 +1,75 @@ +package com.github.mikephil.charting.renderer.ShapeRenders; + +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; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class SquareShapeRenderer implements ShapeRenderer { + + + @Override + public void renderShape( + Canvas c, IScatterDataSet dataSet, + ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final int shapeHoleColor, final float shapeSize) { + + final float shapeHalf = shapeSize / 2f; + final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); + final float shapeHoleSize = shapeHoleSizeHalf * 2.f; + final float shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.f; + final float shapeStrokeSizeHalf = shapeStrokeSize / 2.f; + + for (int i = 0; i < buffer.size(); i += 2) { + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + break; + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + continue; + + mRenderPaint.setColor(dataSet.getColor(i / 2)); + + if (shapeSize > 0.0) { + mRenderPaint.setStyle(Paint.Style.STROKE); + mRenderPaint.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, + mRenderPaint); + + if (shapeHoleColor != ColorTemplate.COLOR_NONE) { + mRenderPaint.setStyle(Paint.Style.FILL); + + mRenderPaint.setColor(shapeHoleColor); + c.drawRect(buffer.buffer[i] - shapeHoleSizeHalf, + buffer.buffer[i + 1] - shapeHoleSizeHalf, + buffer.buffer[i] + shapeHoleSizeHalf, + buffer.buffer[i + 1] + shapeHoleSizeHalf, + mRenderPaint); + } + + } else { + mRenderPaint.setStyle(Paint.Style.FILL); + + c.drawRect(buffer.buffer[i] - shapeHalf, + buffer.buffer[i + 1] - shapeHalf, + buffer.buffer[i] + shapeHalf, + buffer.buffer[i + 1] + shapeHalf, + mRenderPaint); + } + } + } + + +} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/TriangleShapeRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/TriangleShapeRenderer.java new file mode 100644 index 0000000000..7f67a4b7ec --- /dev/null +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/TriangleShapeRenderer.java @@ -0,0 +1,90 @@ +package com.github.mikephil.charting.renderer.ShapeRenders; + +import android.graphics.Canvas; +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; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class TriangleShapeRenderer implements ShapeRenderer { + + + @Override + public void renderShape( + Canvas c, IScatterDataSet dataSet, + ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final int shapeHoleColor, final float shapeSize) { + + + final float shapeHalf = shapeSize / 2f; + final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); + final float shapeHoleSize = shapeHoleSizeHalf * 2.f; + final float shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.f; + + + mRenderPaint.setStyle(Paint.Style.FILL); + + // create a triangle path + Path tri = new Path(); + + for (int i = 0; i < buffer.size(); i += 2) { + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + break; + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + continue; + + mRenderPaint.setColor(dataSet.getColor(i / 2)); + + 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); + + if (shapeSize > 0.0) { + tri.lineTo(buffer.buffer[i], buffer.buffer[i + 1] - shapeHalf); + + 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); + } + + tri.close(); + + c.drawPath(tri, mRenderPaint); + tri.reset(); + + if (shapeSize > 0.0 && + shapeHoleColor != ColorTemplate.COLOR_NONE) { + + mRenderPaint.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, mRenderPaint); + tri.reset(); + } + } + + } + +} \ No newline at end of file diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/XShapeRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/XShapeRenderer.java new file mode 100644 index 0000000000..193483a413 --- /dev/null +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/ShapeRenders/XShapeRenderer.java @@ -0,0 +1,57 @@ +package com.github.mikephil.charting.renderer.ShapeRenders; + +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; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class XShapeRenderer implements ShapeRenderer { + + + @Override + public void renderShape( + Canvas c, IScatterDataSet dataSet, + ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final int shapeHoleColor, final float shapeSize) { + + final float shapeHalf = shapeSize / 2f; + + + + mRenderPaint.setStyle(Paint.Style.STROKE); + mRenderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + + for (int i = 0; i < buffer.size(); i += 2) { + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + break; + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + continue; + + mRenderPaint.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, + mRenderPaint); + c.drawLine( + buffer.buffer[i] + shapeHalf, + buffer.buffer[i + 1] - shapeHalf, + buffer.buffer[i] - shapeHalf, + buffer.buffer[i + 1] + shapeHalf, + mRenderPaint); + } + + } + +} \ No newline at end of file From 53e57db56205eb027cd135ab7cef2e05dd0fb978 Mon Sep 17 00:00:00 2001 From: wajdi chamakhi Date: Wed, 15 Jun 2016 14:20:18 +0100 Subject: [PATCH 230/606] Adding The Enum Patern Back --- .../mpchartexample/ScatterChartActivity.java | 4 +- .../charting/charts/ScatterChart.java | 40 +++++++++++++++---- .../charting/data/ScatterDataSet.java | 12 ++++++ .../implementation/RealmScatterDataSet.java | 12 ++++++ 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index 03d98872d1..4929eb069b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -188,10 +188,10 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // create a dataset and give it a type ScatterDataSet set1 = new ScatterDataSet(yVals1, "DS 1"); - set1.setScatterShape(ScatterShape.getScatterShapeNames().get(ScatterShape.SQUARE)); + set1.setScatterShape(ScatterChart.ScatterShape.SQUARE); set1.setColor(ColorTemplate.COLORFUL_COLORS[0]); ScatterDataSet set2 = new ScatterDataSet(yVals2, "DS 2"); - set2.setScatterShape(ScatterShape.getScatterShapeNames().get(ScatterShape.CIRCLE)); + set2.setScatterShape(ScatterChart.ScatterShape.CIRCLE); set2.setScatterShapeHoleColor(ColorTemplate.COLORFUL_COLORS[3]); set2.setScatterShapeHoleRadius(4f); set2.setColor(ColorTemplate.COLORFUL_COLORS[1]); diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java index f13857e75a..6645b4c322 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java @@ -11,7 +11,6 @@ import com.github.mikephil.charting.renderer.ShapeRenders.ChevronUpShapeRenderer; import com.github.mikephil.charting.renderer.ShapeRenders.CircleShapeRenderer; import com.github.mikephil.charting.renderer.ShapeRenders.CrossShapeRenderer; -import com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape; import com.github.mikephil.charting.renderer.ShapeRenders.ShapeRenderer; import com.github.mikephil.charting.renderer.ShapeRenders.SquareShapeRenderer; import com.github.mikephil.charting.renderer.ShapeRenders.TriangleShapeRenderer; @@ -54,16 +53,41 @@ public static ShapeRenderer getShapeRenderer(String scatterShapeName) { * Init ShapeRendererList */ private static void initShapeRenderer() { - registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.SQUARE), new SquareShapeRenderer()); - registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.CIRCLE), new CircleShapeRenderer()); - registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.TRIANGLE), new TriangleShapeRenderer()); - registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.CROSS), new CrossShapeRenderer()); - registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.X), new XShapeRenderer()); - registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.CHEVRON_UP), new ChevronUpShapeRenderer()); - registerShapeRenderer(ScatterShape.getScatterShapeNames().get(ScatterShape.CHEVRON_DOWN), new ChevronDownShapeRenderer()); + registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.SQUARE), new SquareShapeRenderer()); + registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.CIRCLE), new CircleShapeRenderer()); + registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.TRIANGLE), new TriangleShapeRenderer()); + registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.CROSS), new CrossShapeRenderer()); + registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.X), new XShapeRenderer()); + registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.CHEVRON_UP), new ChevronUpShapeRenderer()); + registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.CHEVRON_DOWN), new ChevronDownShapeRenderer()); } + /** + * enum that defines the shape that is drawn where the values are + */ + public enum ScatterShape { + SQUARE, CIRCLE, TRIANGLE, CROSS, X, + } + + /** + * Returns all possible predefined ScatterShapes. + * + * @return ScatterShape to array + */ + public static ScatterShape[] getAllPossibleShapes() { + return new ScatterShape[] { + ScatterShape.SQUARE, ScatterShape.CIRCLE, ScatterShape.TRIANGLE, ScatterShape.CROSS + }; + } + public ScatterChart(Context context) { super(context); } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java index 3281fd0d54..74a4d6ecd9 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.data; +import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape; import com.github.mikephil.charting.utils.ColorTemplate; @@ -79,6 +80,17 @@ public float getScatterShapeSize() { return mShapeSize; } + + /** + * Sets the shape that is drawn on the position where the values are at. + * + * @param shape + */ + public void setScatterShape(ScatterChart.ScatterShape shape) { + mScatterShape = shape.toString(); + } + + /** * Sets the shape that is drawn on the position where the values are at. * diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java index e15418852c..8aaa816539 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java +++ b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java @@ -1,5 +1,6 @@ package com.github.mikephil.charting.data.realm.implementation; +import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.realm.base.RealmLineScatterCandleRadarDataSet; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; @@ -80,6 +81,17 @@ public float getScatterShapeSize() { return mShapeSize; } + + /** + * Sets the shape that is drawn on the position where the values are at. + * + * @param shape + */ + public void setScatterShape(ScatterChart.ScatterShape shape) { + mScatterShape = shape.toString(); + } + + /** * Sets the shape that is drawn on the position where the values are at. If * "CUSTOM" is chosen, you need to call setCustomScatterShape(...) and From bda3ccee35d3fb3c20db255a8332a3eacfbc594a Mon Sep 17 00:00:00 2001 From: wajdi chamakhi Date: Wed, 15 Jun 2016 14:25:20 +0100 Subject: [PATCH 231/606] Init shapeRendererList optimisation --- .../mikephil/charting/charts/ScatterChart.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java index 6645b4c322..b0253a5606 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java @@ -37,7 +37,7 @@ public class ScatterChart extends BarLineChartBase implements Scatt public static void registerShapeRenderer(String scatterShapeName, ShapeRenderer shapeRenderer) { if (shapeRendererList == null) { - shapeRendererList = new HashMap<>(); + initShapeRenderer(); } shapeRendererList.put(scatterShapeName, shapeRenderer); } @@ -53,19 +53,20 @@ public static ShapeRenderer getShapeRenderer(String scatterShapeName) { * Init ShapeRendererList */ private static void initShapeRenderer() { - registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + shapeRendererList = new HashMap<>(); + shapeRendererList.put(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.SQUARE), new SquareShapeRenderer()); - registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + shapeRendererList.put(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.CIRCLE), new CircleShapeRenderer()); - registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + shapeRendererList.put(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.TRIANGLE), new TriangleShapeRenderer()); - registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + shapeRendererList.put(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.CROSS), new CrossShapeRenderer()); - registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + shapeRendererList.put(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.X), new XShapeRenderer()); - registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + shapeRendererList.put(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.CHEVRON_UP), new ChevronUpShapeRenderer()); - registerShapeRenderer(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() + shapeRendererList.put(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.getScatterShapeNames() .get(com.github.mikephil.charting.renderer.ShapeRenders.ScatterShape.CHEVRON_DOWN), new ChevronDownShapeRenderer()); } From 84160773ed0960303566da571e3cba0bc89a3908 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 15 Jun 2016 21:07:02 +0200 Subject: [PATCH 232/606] Fixes related to combinedchart #1853 --- MPChartExample/res/menu/combined.xml | 5 +- .../mpchartexample/CombinedChartActivity.java | 15 ++- .../mikephil/charting/charts/Chart.java | 18 +-- .../charting/charts/CombinedChart.java | 23 ---- .../mikephil/charting/data/ChartData.java | 12 +- .../mikephil/charting/data/CombinedData.java | 125 +++++++++++++++--- .../highlight/CombinedHighlighter.java | 3 +- 7 files changed, 139 insertions(+), 62 deletions(-) diff --git a/MPChartExample/res/menu/combined.xml b/MPChartExample/res/menu/combined.xml index 9b034d7f4f..7e37ba0d83 100644 --- a/MPChartExample/res/menu/combined.xml +++ b/MPChartExample/res/menu/combined.xml @@ -9,5 +9,8 @@ android:id="@+id/actionToggleBarValues" android:title="Toggle bar Values"> - + + \ No newline at end of file diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 237d7d6cfc..068491927f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -125,7 +125,7 @@ private LineData generateLineData() { set.setValueTextColor(Color.rgb(240, 238, 70)); set.setAxisDependency(YAxis.AxisDependency.LEFT); - + this.set1 = set; d.addDataSet(set); return d; @@ -209,6 +209,9 @@ protected CandleData generateCandleData() { return d; } + private LineDataSet set1; + private BubbleDataSet set2; + protected BubbleData generateBubbleData() { BubbleData bd = new BubbleData(); @@ -227,6 +230,7 @@ protected BubbleData generateBubbleData() { set.setValueTextColor(Color.WHITE); set.setHighlightCircleWidth(1.5f); set.setDrawValues(true); + this.set2 = set; bd.addDataSet(set); return bd; @@ -259,6 +263,15 @@ public boolean onOptionsItemSelected(MenuItem item) { mChart.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(); + break; + } } return 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 b6c652a5b6..42e4c880df 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 @@ -572,14 +572,14 @@ public void highlightValues(Highlight[] highs) { } /** - * Highlights the value at the given x-index in the given DataSet. Provide - * -1 as the x-index or dataSetIndex to undo all highlighting. + * Highlights the value at the given x-position in the given DataSet. Provide + * -1 as the dataSetIndex to undo all highlighting. This will trigger a callback to the OnChartValueSelectedListener. * - * @param xIndex + * @param x * @param dataSetIndex */ - public void highlightValue(int xIndex, int dataSetIndex) { - highlightValue(xIndex, dataSetIndex, true); + public void highlightValue(float x, int dataSetIndex) { + highlightValue(x, dataSetIndex, true); } /** @@ -656,14 +656,6 @@ public void highlightValue(Highlight high, boolean callListener) { invalidate(); } - /** - * Deprecated. Calls highlightValue(high, true) - */ - @Deprecated - public void highlightTouch(Highlight high) { - highlightValue(high, true); - } - /** * Returns the Highlight object (contains x-index and DataSet index) of the * selected value at the given touch point inside the Line-, Scatter-, or 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 710c82e4a5..49e42267a3 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 @@ -11,13 +11,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.interfaces.dataprovider.BarDataProvider; -import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; -import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider; -import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; -import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; -import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; import com.github.mikephil.charting.renderer.CombinedChartRenderer; /** @@ -28,11 +22,6 @@ */ public class CombinedChart extends BarLineChartBase implements CombinedDataProvider { -// /** -// * flag that enables or disables the highlighting arrow -// */ -// private boolean mDrawHighlightArrow = false; - /** * if set to true, all values are drawn above their bars, instead of below * their top @@ -77,9 +66,6 @@ protected void init() { // Old default behaviour setHighlightFullBarEnabled(true); - - // mRenderer = new CombinedChartRenderer(this, mAnimator, - // mViewPortHandler); } @Override @@ -142,15 +128,6 @@ public boolean isDrawValueAboveBarEnabled() { return mDrawValueAboveBar; } -// /** -// * set this to true to draw the highlightning arrow -// * -// * @param enabled -// */ -// public void setDrawHighlightArrow(boolean enabled) { -// mDrawHighlightArrow = enabled; -// } - /** * If set to true, all values are drawn above their bars, instead of below * their top. 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 0938fb1f11..fa66f4744e 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 @@ -139,7 +139,7 @@ public void calcMinMax() { mRightAxisMin = Float.MAX_VALUE; // left axis - T firstLeft = getFirstLeft(); + T firstLeft = getFirstLeft(mDataSets); if (firstLeft != null) { @@ -158,7 +158,7 @@ public void calcMinMax() { } // right axis - T firstRight = getFirstRight(); + T firstRight = getFirstRight(mDataSets); if (firstRight != null) { @@ -613,8 +613,8 @@ public int getIndexOfDataSet(T dataSet) { * * @return */ - public T getFirstLeft() { - for (T dataSet : mDataSets) { + protected T getFirstLeft(List sets) { + for (T dataSet : sets) { if (dataSet.getAxisDependency() == AxisDependency.LEFT) return dataSet; } @@ -627,8 +627,8 @@ public T getFirstLeft() { * * @return */ - public T getFirstRight() { - for (T dataSet : mDataSets) { + public T getFirstRight(List sets) { + for (T dataSet : sets) { if (dataSet.getAxisDependency() == AxisDependency.RIGHT) return dataSet; } 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 33cacc304a..37ab1e4bdb 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 @@ -1,6 +1,9 @@ package com.github.mikephil.charting.data; +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; @@ -11,7 +14,7 @@ /** * Data object that allows the combination of Line-, Bar-, Scatter-, Bubble- and * CandleData. Used in the CombinedChart class. - * + * * @author Philipp Jahoda */ public class CombinedData extends BarLineScatterCandleBubbleData> { @@ -28,34 +31,81 @@ public CombinedData() { public void setData(LineData data) { mLineData = data; - mDataSets.addAll(data.getDataSets()); init(); } public void setData(BarData data) { mBarData = data; - mDataSets.addAll(data.getDataSets()); init(); } public void setData(ScatterData data) { mScatterData = data; - mDataSets.addAll(data.getDataSets()); init(); } public void setData(CandleData data) { mCandleData = data; - mDataSets.addAll(data.getDataSets()); init(); } public void setData(BubbleData data) { mBubbleData = data; - mDataSets.addAll(data.getDataSets()); init(); } + @Override + public void calcMinMax() { + + mDataSets = new ArrayList<>(); + + mYMax = -Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; + + mLeftAxisMax = -Float.MAX_VALUE; + mLeftAxisMin = Float.MAX_VALUE; + mRightAxisMax = -Float.MAX_VALUE; + mRightAxisMin = Float.MAX_VALUE; + + List allData = getAllData(); + + for (ChartData data : allData) { + + data.calcMinMax(); + + List> sets = data.getDataSets(); + mDataSets.addAll(sets); + + if (data.getYMax() > mYMax) + mYMax = data.getYMax(); + + if (data.getYMin() < mYMin) + mYMin = data.getYMin(); + + if (data.getXMax() > mXMax) + mXMax = data.getXMax(); + + 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; + + } + + } + public BubbleData getBubbleData() { return mBubbleData; } @@ -78,26 +128,27 @@ public CandleData getCandleData() { /** * Returns all data objects in row: line-bar-scatter-candle-bubble if not null. + * * @return */ - public List getAllData() { + public List getAllData() { - List data = new ArrayList(); - if(mLineData != null) + List data = new ArrayList(); + if (mLineData != null) data.add(mLineData); - if(mBarData != null) + if (mBarData != null) data.add(mBarData); - if(mScatterData != null) + if (mScatterData != null) data.add(mScatterData); - if(mCandleData != null) + if (mCandleData != null) data.add(mCandleData); - if(mBubbleData != null) + if (mBubbleData != null) data.add(mBubbleData); return data; } - public ChartData getDataByIndex(int index) { + public BarLineScatterCandleBubbleData getDataByIndex(int index) { return getAllData().get(index); } @@ -126,7 +177,7 @@ public void notifyDataChanged() { @Override public Entry getEntryForHighlight(Highlight highlight) { - List dataObjects = getAllData(); + List dataObjects = getAllData(); if (highlight.getDataIndex() >= dataObjects.size()) return null; @@ -142,9 +193,9 @@ public Entry getEntryForHighlight(Highlight highlight) { List entries = data.getDataSetByIndex(highlight.getDataSetIndex()) .getEntriesForXPos(highlight.getX()); for (Object entry : entries) - if (((Entry)entry).getY() == highlight.getY() || + if (((Entry) entry).getY() == highlight.getY() || Float.isNaN(highlight.getY())) - return (Entry)entry; + return (Entry) entry; return null; } @@ -153,4 +204,44 @@ public Entry getEntryForHighlight(Highlight highlight) { public int getDataIndex(ChartData data) { return getAllData().indexOf(data); } + + @Override + public boolean removeDataSet(IBarLineScatterCandleBubbleDataSet d) { + + List datas = getAllData(); + + boolean success = false; + + for (ChartData data : datas) { + + success = data.removeDataSet(d); + + if (success) { + break; + } + } + + return success; + } + + @Deprecated + @Override + public boolean removeDataSet(int index) { + Log.e("MPAndroidChart", "removeDataSet(int index) not supported for CombinedData"); + return false; + } + + @Deprecated + @Override + public boolean removeEntry(Entry e, int dataSetIndex) { + Log.e("MPAndroidChart", "removeEntry(...) not supported for CombinedData"); + return false; + } + + @Deprecated + @Override + public boolean removeEntry(float xPos, int dataSetIndex) { + Log.e("MPAndroidChart", "removeEntry(...) not supported for CombinedData"); + return false; + } } 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 418d7a38e4..b8500fb092 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 @@ -1,6 +1,7 @@ package com.github.mikephil.charting.highlight; import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; @@ -32,7 +33,7 @@ protected List getHighlightsAtXPos(float xVal, float x, float y) { List vals = new ArrayList(); - List dataObjects = mChart.getCombinedData().getAllData(); + List dataObjects = mChart.getCombinedData().getAllData(); for (int i = 0; i < dataObjects.size(); i++) { From a3720feb0e714a8cbf6437cf6383c4d88089b403 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 15 Jun 2016 23:44:53 +0200 Subject: [PATCH 233/606] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4c784851b8..c6b0c386ff 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ 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). + ## 3rd party bindings From fb4590e6b002ee765e35e729b3619c235902b045 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 16 Jun 2016 10:07:12 +0200 Subject: [PATCH 234/606] Fix #1862 --- .../src/com/github/mikephil/charting/components/YAxis.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java index 0846a670a7..4b14b91c86 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java @@ -208,7 +208,7 @@ public boolean isGranularityEnabled() { * @param enabled */ public void setGranularityEnabled(boolean enabled) { - mGranularityEnabled = true; + mGranularityEnabled = enabled; } /** From 4c4c76b1c7213a2eed9bfa02881ae75b9c92ae8d Mon Sep 17 00:00:00 2001 From: wajdi chamakhi Date: Thu, 16 Jun 2016 09:23:23 +0100 Subject: [PATCH 235/606] remove Boldness --- .../charting/components/ComponentBase.java | 21 ------------------- .../charting/renderer/LegendRenderer.java | 2 -- .../charting/renderer/XAxisRenderer.java | 3 --- .../XAxisRendererHorizontalBarChart.java | 3 --- .../renderer/XAxisRendererRadarChart.java | 1 - .../charting/renderer/YAxisRenderer.java | 2 -- .../YAxisRendererHorizontalBarChart.java | 3 --- .../renderer/YAxisRendererRadarChart.java | 1 - 8 files changed, 36 deletions(-) diff --git a/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java b/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java index 5c0c596508..99e9c7a41b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java +++ b/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java @@ -43,10 +43,6 @@ public abstract class ComponentBase { */ protected int mTextColor = Color.BLACK; - /** - * the text style boldness {Default = false} - */ - protected boolean mTextBold = false; public ComponentBase() { @@ -135,23 +131,6 @@ public float getTextSize() { return mTextSize; } - /** - * set the text to be bold - * - * @param bold - */ - public void setTextBold(boolean bold) { - mTextBold = bold; - } - - /** - * returns the text boldness - * - * @return - */ - public boolean getTextBold() { - return mTextBold; - } /** * Sets the text color to use for the labels. Make sure to use diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java index a35653446e..f005a1f2ac 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -167,7 +167,6 @@ public void computeLegend(ChartData data) { mLegendLabelPaint.setTypeface(tf); mLegendLabelPaint.setTextSize(mLegend.getTextSize()); - mLegendLabelPaint.setFakeBoldText(mLegend.getTextBold()); mLegendLabelPaint.setColor(mLegend.getTextColor()); // calculate all dimensions of the mLegend @@ -185,7 +184,6 @@ public void renderLegend(Canvas c) { mLegendLabelPaint.setTypeface(tf); mLegendLabelPaint.setTextSize(mLegend.getTextSize()); - mLegendLabelPaint.setFakeBoldText(mLegend.getTextBold()); mLegendLabelPaint.setColor(mLegend.getTextColor()); float labelLineHeight = Utils.getLineHeight(mLegendLabelPaint); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java index a8b6056f81..9610d3b393 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -36,7 +36,6 @@ public void computeAxis(float xValMaximumLength, List xValues) { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - mAxisLabelPaint.setFakeBoldText(mXAxis.getTextBold()); StringBuilder widthText = new StringBuilder(); @@ -83,7 +82,6 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - mAxisLabelPaint.setFakeBoldText(mXAxis.getTextBold()); mAxisLabelPaint.setColor(mXAxis.getTextColor()); if (mXAxis.getPosition() == XAxisPosition.TOP) { @@ -295,7 +293,6 @@ public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position mLimitLinePaint.setColor(limitLine.getTextColor()); mLimitLinePaint.setStrokeWidth(0.5f); mLimitLinePaint.setTextSize(limitLine.getTextSize()); - mLimitLinePaint.setFakeBoldText(limitLine.getTextBold()); float xOffset = limitLine.getLineWidth() + limitLine.getXOffset(); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java index 0fe0f46ad8..e862263520 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java @@ -31,7 +31,6 @@ public void computeAxis(float xValAverageLength, List xValues) { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - mAxisLabelPaint.setFakeBoldText(mXAxis.getTextBold()); mXAxis.setValues(xValues); String longest = mXAxis.getLongestLabel(); @@ -62,7 +61,6 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - mAxisLabelPaint.setFakeBoldText(mXAxis.getTextBold()); mAxisLabelPaint.setColor(mXAxis.getTextColor()); if (mXAxis.getPosition() == XAxisPosition.TOP) { @@ -239,7 +237,6 @@ public void renderLimitLines(Canvas c) { mLimitLinePaint.setColor(l.getTextColor()); mLimitLinePaint.setStrokeWidth(0.5f); mLimitLinePaint.setTextSize(l.getTextSize()); - mLimitLinePaint.setFakeBoldText(l.getTextBold()); final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); float xOffset = Utils.convertDpToPixel(4f) + l.getXOffset(); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java index 228e818d83..50a2fc7dd4 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java @@ -30,7 +30,6 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - mAxisLabelPaint.setFakeBoldText(mXAxis.getTextBold()); mAxisLabelPaint.setColor(mXAxis.getTextColor()); float sliceangle = mChart.getSliceAngle(); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java index 9e37d71e00..fdae353e4b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -193,7 +193,6 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); - mAxisLabelPaint.setFakeBoldText(mYAxis.getTextBold()); mAxisLabelPaint.setColor(mYAxis.getTextColor()); float xoffset = mYAxis.getXOffset(); @@ -382,7 +381,6 @@ public void renderLimitLines(Canvas c) { mLimitLinePaint.setTypeface(l.getTypeface()); mLimitLinePaint.setStrokeWidth(0.5f); mLimitLinePaint.setTextSize(l.getTextSize()); - mLimitLinePaint.setFakeBoldText(l.getTextBold()); final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); float xOffset = Utils.convertDpToPixel(4f) + l.getXOffset(); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java index 098542f3fc..78a42f79b0 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java @@ -77,7 +77,6 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); - mAxisLabelPaint.setFakeBoldText(mYAxis.getTextBold()); mAxisLabelPaint.setColor(mYAxis.getTextColor()); mAxisLabelPaint.setTextAlign(Align.CENTER); @@ -140,7 +139,6 @@ protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, flo mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); - mAxisLabelPaint.setFakeBoldText(mYAxis.getTextBold()); mAxisLabelPaint.setColor(mYAxis.getTextColor()); for (int i = 0; i < mYAxis.mEntryCount; i++) { @@ -244,7 +242,6 @@ public void renderLimitLines(Canvas c) { mLimitLinePaint.setTypeface(l.getTypeface()); mLimitLinePaint.setStrokeWidth(0.5f); mLimitLinePaint.setTextSize(l.getTextSize()); - mLimitLinePaint.setFakeBoldText(l.getTextBold()); float xOffset = l.getLineWidth() + l.getXOffset(); float yOffset = Utils.convertDpToPixel(2f) + l.getYOffset(); diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java index 220a4735d2..66ddd3ceea 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -137,7 +137,6 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); - mAxisLabelPaint.setFakeBoldText(mYAxis.getTextBold()); mAxisLabelPaint.setColor(mYAxis.getTextColor()); PointF center = mChart.getCenterOffsets(); From 3aa4daae3201c2d997c07f7e5ccfe360c0a1e27f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 17 Jun 2016 21:53:30 +0200 Subject: [PATCH 236/606] Add feature allowing customization of dragTriggerDistance #1721 --- .../charting/charts/BarLineChartBase.java | 2 +- .../mikephil/charting/charts/Chart.java | 9 ++++ .../listener/BarLineChartTouchListener.java | 49 ++++++++++--------- 3 files changed, 37 insertions(+), 23 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 ceeccd2b1a..3e907e3deb 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 @@ -169,7 +169,7 @@ protected void init() { setHighlighter(new ChartHighlighter(this)); - mChartTouchListener = new BarLineChartTouchListener(this, mViewPortHandler.getMatrixTouch()); + mChartTouchListener = new BarLineChartTouchListener(this, mViewPortHandler.getMatrixTouch(), 3f); mGridBackgroundPaint = new Paint(); mGridBackgroundPaint.setStyle(Style.FILL); 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 7146ee60a3..b09a2705a3 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 @@ -684,6 +684,15 @@ public void setOnTouchListener(ChartTouchListener l) { this.mChartTouchListener = l; } + /** + * Returns an instance of the currently active touch listener. + * + * @return + */ + public ChartTouchListener getOnTouchListener() { + return mChartTouchListener; + } + /** * ################ ################ ################ ################ */ 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 02713af88a..620db7b45c 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 @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.listener; import android.annotation.SuppressLint; @@ -26,7 +25,8 @@ * * @author Philipp Jahoda */ -public class BarLineChartTouchListener extends ChartTouchListener>>> { +public class BarLineChartTouchListener extends ChartTouchListener>>> { /** * the original touch-matrix from the chart @@ -73,12 +73,20 @@ public class BarLineChartTouchListener extends ChartTouchListener>> chart, Matrix touchMatrix) { + /** + * Constructor with initialization parameters. + * + * @param chart instance of the chart + * @param touchMatrix the touch-matrix of the chart + * @param dragTriggerDistance the minimum movement distance that will be interpreted as a "drag" gesture in dp (3dp equals + * to about 9 pixels on a 5.5" FHD screen) + */ + public BarLineChartTouchListener(BarLineChartBase>> chart, Matrix touchMatrix, float dragTriggerDistance) { super(chart); this.mMatrix = touchMatrix; - // this equals to about 9 pixels on a 5.5" FHD screen - this.mDragTriggerDist = Utils.convertDpToPixel(3f); + this.mDragTriggerDist = Utils.convertDpToPixel(dragTriggerDistance); this.mMinScalePointerDistance = Utils.convertDpToPixel(3.5f); } @@ -207,7 +215,8 @@ public boolean onTouch(View v, MotionEvent event) { mDecelerationCurrentPoint = new PointF(event.getX(), event.getY()); mDecelerationVelocity = new PointF(velocityX, velocityY); - Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by Google + Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by + // Google } } @@ -513,6 +522,16 @@ public Matrix getMatrix() { return mMatrix; } + /** + * Sets the minimum distance that will be interpreted as a "drag" by the chart in dp. + * Default: 3dp + * + * @param dragTriggerDistance + */ + public void setDragTriggerDist(float dragTriggerDistance) { + this.mDragTriggerDist = Utils.convertDpToPixel(dragTriggerDistance); + } + @Override public boolean onDoubleTap(MotionEvent e) { @@ -573,21 +592,6 @@ public boolean onSingleTapUp(MotionEvent e) { return super.onSingleTapUp(e); } -// @Override -// public boolean onSingleTapConfirmed(MotionEvent e) { -// -// mLastGesture = ChartGesture.SINGLE_TAP; -// -// OnChartGestureListener l = mChart.getOnChartGestureListener(); -// -// if (l != null) { -// l.onChartSingleTapped(e); -// l.onChartGestureEnd(e, mLastGesture); -// } -// -// return super.onSingleTapConfirmed(e); -// } - @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { @@ -624,7 +628,8 @@ public void computeScroll() { mDecelerationCurrentPoint.x += distanceX; mDecelerationCurrentPoint.y += distanceY; - MotionEvent event = MotionEvent.obtain(currentTime, currentTime, MotionEvent.ACTION_MOVE, mDecelerationCurrentPoint.x, mDecelerationCurrentPoint.y, 0); + MotionEvent event = MotionEvent.obtain(currentTime, currentTime, MotionEvent.ACTION_MOVE, mDecelerationCurrentPoint.x, + mDecelerationCurrentPoint.y, 0); performDrag(event); event.recycle(); mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, false); From bb0ad3ac22cdfe1a25133b9979f018d271c86179 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 18 Jun 2016 00:33:26 +0200 Subject: [PATCH 237/606] Fix bug related to inverted axis & scrolling #1829 --- .../listener/BarLineChartTouchListener.java | 16 ++++++++++++---- 1 file changed, 12 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 620db7b45c..e82f3cd77c 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 @@ -296,8 +296,7 @@ private void performDrag(MotionEvent event) { float dX, dY; // check if axis is inverted - if (mChart.isAnyAxisInverted() && mClosestDataSetToTouch != null - && mChart.getAxis(mClosestDataSetToTouch.getAxisDependency()).isInverted()) { + if (inverted()) { // if there is an inverted horizontalbarchart if (mChart instanceof HorizontalBarChart) { @@ -498,8 +497,7 @@ public PointF getTrans(float x, float y) { float yTrans = 0f; // check if axis is inverted - if (mChart.isAnyAxisInverted() && mClosestDataSetToTouch != null - && mChart.isInverted(mClosestDataSetToTouch.getAxisDependency())) { + if (inverted()) { yTrans = -(y - vph.offsetTop()); } else { yTrans = -(mChart.getMeasuredHeight() - y - vph.offsetBottom()); @@ -508,6 +506,16 @@ public PointF getTrans(float x, float y) { return new PointF(xTrans, yTrans); } + /** + * Returns true if the current touch situation should be interpreted as inverted, false if not. + * + * @return + */ + private boolean inverted() { + return (mClosestDataSetToTouch == null && mChart.isAnyAxisInverted()) || (mClosestDataSetToTouch != null + && mChart.isInverted(mClosestDataSetToTouch.getAxisDependency())); + } + /** * ################ ################ ################ ################ */ From 735a9b9c98ab555373de99ccd3f420f27f470e47 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 18 Jun 2016 00:44:51 +0200 Subject: [PATCH 238/606] Fixes related to no data text #1749 --- .../com/github/mikephil/charting/charts/Chart.java | 14 ++++++-------- 1 file changed, 6 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 b09a2705a3..27223cd3e9 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 @@ -300,16 +300,13 @@ public void onAnimationUpdate(ValueAnimator animation) { */ public void setData(T data) { + mData = data; + mOffsetsCalculated = false; + if (data == null) { - Log.e(LOG_TAG, - "Cannot set data for chart. Provided data object is null."); return; } - // LET THE CHART KNOW THERE IS DATA - mOffsetsCalculated = false; - mData = data; - // calculate how many digits are needed calculateFormatter(data.getYMin(), data.getYMax()); @@ -331,13 +328,14 @@ public void setData(T data) { */ public void clear() { mData = null; + mOffsetsCalculated = false; mIndicesToHighlight = null; invalidate(); } /** - * Removes all DataSets (and thereby Entries) from the chart. Does not - * remove the x-values. Also refreshes the chart by calling invalidate(). + * Removes all DataSets (and thereby Entries) from the chart. Does not set the data object to null. Also refreshes the + * chart by calling invalidate(). */ public void clearValues() { mData.clearValues(); From f75ea845e0111f4388384eb8e5e859e78c9a6177 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 18 Jun 2016 00:56:04 +0200 Subject: [PATCH 239/606] Work on scatterdataset copy --- .../com/github/mikephil/charting/data/ScatterDataSet.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 4c9cbe8c6f..b07897d6c3 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 @@ -53,13 +53,16 @@ public DataSet copy() { } ScatterDataSet copied = new ScatterDataSet(yVals, getLabel()); + copied.mDrawValues = mDrawValues; + copied.mValueColors = mValueColors; copied.mColors = mColors; copied.mShapeSize = mShapeSize; copied.mScatterShape = mScatterShape; copied.mScatterShapeHoleRadius = mScatterShapeHoleRadius; copied.mScatterShapeHoleColor = mScatterShapeHoleColor; - //copied.mCustomScatterPath = mCustomScatterPath; + copied.mHighlightLineWidth = mHighlightLineWidth; copied.mHighLightColor = mHighLightColor; + copied.mHighlightDashPathEffect = mHighlightDashPathEffect; return copied; } From 8a6139b1fbe77f875a900158087128f691d8a2e9 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 18 Jun 2016 12:18:43 +0200 Subject: [PATCH 240/606] Fixes related to DefaultValueFormatter #1809 --- .../BarChartActivityMultiDataset.java | 2 +- .../mikephil/charting/charts/Chart.java | 16 +++++------ .../mikephil/charting/data/BaseDataSet.java | 7 ++++- .../formatter/AxisValueFormatter.java | 2 +- .../formatter/DefaultValueFormatter.java | 28 +++++++++++++++++-- .../interfaces/datasets/IDataSet.java | 7 +++++ .../github/mikephil/charting/utils/Utils.java | 18 ------------ 7 files changed, 49 insertions(+), 31 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java index f76fee3b8c..a75182bce8 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -195,7 +195,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { ArrayList yVals2 = new ArrayList(); ArrayList yVals3 = new ArrayList(); - float mult = mSeekBarY.getProgress() * 1000f; + float mult = mSeekBarY.getProgress() * 100000f; for (int i = startYear; i < endYear; i++) { float val = (float) (Math.random() * mult) + 3; 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 27223cd3e9..6b16ab3713 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 @@ -100,7 +100,7 @@ public abstract class Chart { */ ValueFormatter getValueFormatter(); + /** + * Returns true if the valueFormatter object of this DataSet is null. + * + * @return + */ + boolean needsFormatter(); + /** * Sets the color the value-labels of this DataSet should have. * 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 ea249d95f2..3da44084b6 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 @@ -19,9 +19,6 @@ import android.view.View; import android.view.ViewConfiguration; -import com.github.mikephil.charting.formatter.DefaultValueFormatter; -import com.github.mikephil.charting.formatter.ValueFormatter; - import java.util.List; /** @@ -380,21 +377,6 @@ public static double nextUp(double d) { } } - /** - * If this component has no ValueFormatter or is only equipped with the - * default one (no custom set), return true. - * - * @return - */ - public static boolean needsDefaultFormatter(ValueFormatter formatter) { - if (formatter == null) - return true; - if (formatter instanceof DefaultValueFormatter) - return true; - - return false; - } - /** * Calculates the position around a center point, depending on the distance * from the center, and the angle of the position around the center. From 005c166e0797bb00382252693d4087303eb72fc3 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 18 Jun 2016 12:24:56 +0200 Subject: [PATCH 241/606] Cleanup --- .../mpchartexample/custom/DayAxisValueFormatter.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java index 11e411a7c9..8e906c4921 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -32,10 +32,6 @@ public String getFormattedValue(float value, AxisBase axis) { String monthName = mMonths[month % mMonths.length]; String yearName = String.valueOf(year); - if (year == 2017) { - System.out.println(""); - } - if (chart.getVisibleXRange() > 30 * axis.getLabelCount()) { return monthName + " " + yearName; From 19265217bd1702e34bc4743ecce189648aec76e6 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 18 Jun 2016 16:09:22 +0200 Subject: [PATCH 242/606] Provide example for #1865 --- MPChartExample/AndroidManifest.xml | 1 + .../mpchartexample/FilledLineActivity.java | 150 ++++++++++++++++++ .../notimportant/MainActivity.java | 8 + 3 files changed, 159 insertions(+) create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index c0e6931a4c..e07a8bdfda 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -64,6 +64,7 @@ + diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java new file mode 100644 index 0000000000..ece33d8964 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java @@ -0,0 +1,150 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.graphics.Color; +import android.os.Bundle; +import android.view.WindowManager; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +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.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class FilledLineActivity extends DemoBase { + + private LineChart mChart; + private int mFillColor = Color.argb(150, 51, 181, 229); + + @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); + mChart.setBackgroundColor(Color.WHITE); + mChart.setGridBackgroundColor(mFillColor); + mChart.setDrawGridBackground(true); + + mChart.setDrawBorders(true); + + // no description text + mChart.setDescription(""); + + // if disabled, scaling can be done on x- and y-axis separately + mChart.setPinchZoom(false); + + Legend l = mChart.getLegend(); + l.setEnabled(false); + + XAxis xAxis = mChart.getXAxis(); + xAxis.setEnabled(false); + + YAxis leftAxis = mChart.getAxisLeft(); + leftAxis.setAxisMaxValue(900f); + leftAxis.setAxisMinValue(-250f); + leftAxis.setDrawAxisLine(false); + leftAxis.setDrawZeroLine(false); + leftAxis.setDrawGridLines(false); + + mChart.getAxisRight().setEnabled(false); + + // add data + setData(100, 60); + + mChart.invalidate(); + } + + private void setData(int count, float range) { + + ArrayList yVals1 = 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)); + } + + ArrayList yVals2 = 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)); + } + + 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(); + } else { + // create a dataset and give it a type + set1 = new LineDataSet(yVals1, "DataSet 1"); + + set1.setAxisDependency(YAxis.AxisDependency.LEFT); + set1.setColor(Color.rgb(255, 241, 46)); + set1.setDrawCircles(false); + set1.setLineWidth(2f); + set1.setCircleRadius(3f); + set1.setFillAlpha(255); + set1.setDrawFilled(true); + set1.setFillColor(Color.WHITE); + set1.setHighLightColor(Color.rgb(244, 117, 117)); + set1.setDrawCircleHole(false); + set1.setFillFormatter(new FillFormatter() { + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + return mChart.getAxisLeft().getAxisMinimum(); + } + }); + + // create a dataset and give it a type + set2 = new LineDataSet(yVals2, "DataSet 2"); + set2.setAxisDependency(YAxis.AxisDependency.LEFT); + set2.setColor(Color.rgb(255, 241, 46)); + set2.setDrawCircles(false); + set2.setLineWidth(2f); + set2.setCircleRadius(3f); + set2.setFillAlpha(255); + set2.setDrawFilled(true); + set2.setFillColor(Color.WHITE); + set2.setDrawCircleHole(false); + set2.setHighLightColor(Color.rgb(244, 117, 117)); + set2.setFillFormatter(new FillFormatter() { + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + return mChart.getAxisLeft().getAxisMaximum(); + } + }); + + ArrayList dataSets = new ArrayList(); + dataSets.add(set1); // add the datasets + dataSets.add(set2); + + // create a data object with the datasets + LineData data = new LineData(dataSets); + data.setDrawValues(false); + + // set data + mChart.setData(data); + } + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index 1cf582ce6d..a462f4517c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -24,6 +24,7 @@ import com.xxmassdeveloper.mpchartexample.CombinedChartActivity; import com.xxmassdeveloper.mpchartexample.CubicLineChartActivity; import com.xxmassdeveloper.mpchartexample.DynamicalAddingActivity; +import com.xxmassdeveloper.mpchartexample.FilledLineActivity; import com.xxmassdeveloper.mpchartexample.HorizontalBarChartActivity; import com.xxmassdeveloper.mpchartexample.InvertedLineChartActivity; import com.xxmassdeveloper.mpchartexample.LineChartActivity1; @@ -139,6 +140,9 @@ protected void onCreate(Bundle savedInstanceState) { "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); + objects.add(new ContentItem( + "Filled LineChart", + "This demonstrates how to fill an area between two LineDataSets.")); MyAdapter adapter = new MyAdapter(this, objects); @@ -274,6 +278,10 @@ public void onItemClick(AdapterView av, View v, int pos, long arg3) { i = new Intent(this, LineChartTime.class); startActivity(i); break; + case 30: + i = new Intent(this, FilledLineActivity.class); + startActivity(i); + break; } From 2dc7207f1348e0e46e39f85f983403ea271233e2 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 19 Jun 2016 21:19:08 +0200 Subject: [PATCH 243/606] Fix weird nullpointer #1404 --- .../github/mikephil/charting/highlight/ChartHighlighter.java | 1 + .../com/github/mikephil/charting/renderer/PieChartRenderer.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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 9674504d4b..8e9eeafb25 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 @@ -202,6 +202,7 @@ public Highlight getClosestHighlightByPixel(List closestValues, float * @return */ protected float getDistance(float x1, float y1, float x2, float y2) { + //return Math.abs(x1 - x2); return (float) Math.hypot(x1 - x2, y1 - y2); } 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 05c9d531ed..4391cd151c 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 @@ -590,7 +590,7 @@ public void drawExtras(Canvas c) { */ protected void drawHole(Canvas c) { - if (mChart.isDrawHoleEnabled()) { + if (mChart.isDrawHoleEnabled() && mBitmapCanvas != null) { float radius = mChart.getRadius(); float holeRadius = radius * (mChart.getHoleRadius() / 100); From 95bd8a580e562251b090f23acfc4a18d6c87b799 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 19 Jun 2016 21:35:23 +0200 Subject: [PATCH 244/606] Improvements regarding highlighting, issue #1335 --- .../mpchartexample/CubicLineChartActivity.java | 1 + .../java/com/github/mikephil/charting/charts/Chart.java | 4 ++-- .../mikephil/charting/highlight/BarHighlighter.java | 9 +++++---- .../mikephil/charting/highlight/ChartHighlighter.java | 5 +++-- .../mikephil/charting/highlight/CombinedHighlighter.java | 8 ++++---- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java index a775b5d8fb..2fc7d2b210 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -69,6 +69,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setPinchZoom(false); mChart.setDrawGridBackground(false); + mChart.setMaxHighlightDistance(300); XAxis x = mChart.getXAxis(); x.setEnabled(false); 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 6b16ab3713..58995395db 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 @@ -230,7 +230,7 @@ public void onAnimationUpdate(ValueAnimator animation) { // initialize the utils Utils.init(getContext()); - mMaxHighlightDistance = Utils.convertDpToPixel(70f); + mMaxHighlightDistance = Utils.convertDpToPixel(100f); mViewPortHandler = new ViewPortHandler(); @@ -496,7 +496,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: 70dp + * Default: 100dp * * @param distDp */ 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 9f39fc599f..a1efc8df88 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 @@ -18,14 +18,15 @@ public BarHighlighter(BarDataProvider chart) { @Override public Highlight getHighlight(float x, float y) { + Highlight high = super.getHighlight(x, y); - BarData barData = mChart.getBarData(); + if(high == null) { + return null; + } PointD pos = getValsForTouch(x, y); - Highlight high = getHighlightForX((float) pos.x, x, y); - if (high == null) - return null; + BarData barData = mChart.getBarData(); IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex()); if (set.isStacked()) { 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 8e9eeafb25..58ea3cbc67 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 @@ -128,8 +128,8 @@ protected List getHighlightsAtXPos(float xVal, float x, float y) { if (!dataSet.isHighlightEnabled()) continue; - vals.add(buildHighlight(dataSet, i, xVal, DataSet.Rounding.UP)); - vals.add(buildHighlight(dataSet, i, xVal, DataSet.Rounding.DOWN)); + vals.add(buildHighlight(dataSet, i, xVal, DataSet.Rounding.CLOSEST)); + //vals.add(buildHighlight(dataSet, i, xVal, DataSet.Rounding.DOWN)); } return vals; @@ -202,6 +202,7 @@ public Highlight getClosestHighlightByPixel(List closestValues, float * @return */ protected float getDistance(float x1, float y1, float x2, float y2) { + //return Math.abs(y1 - y2); //return Math.abs(x1 - x2); return (float) Math.hypot(x1 - x2, y1 - y2); } 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 b8500fb092..c9ba0b4e6d 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,13 @@ protected List getHighlightsAtXPos(float xVal, float x, float y) { if (!dataSet.isHighlightEnabled()) continue; - Highlight s1 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.UP); + Highlight s1 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.CLOSEST); s1.setDataIndex(i); vals.add(s1); - Highlight s2 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.DOWN); - s2.setDataIndex(i); - vals.add(s2); +// Highlight s2 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.DOWN); +// s2.setDataIndex(i); +// vals.add(s2); } } } From adfc9a5c77597152f5865be9836ea31d1c9f08a4 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 19 Jun 2016 21:42:38 +0200 Subject: [PATCH 245/606] Fix issue related to values not being drawn --- .../java/com/github/mikephil/charting/utils/Transformer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 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 6d16b2107e..e6e3bc17af 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 @@ -131,7 +131,7 @@ public float[] generateTransformedValuesScatter(IScatterDataSet data, */ public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY, int from, int to) { - final int count = (int) Math.ceil(to - from) * 2; // (int) Math.ceil((to - from) * phaseX) * 2; + final int count = (to - from + 1) * 2; // (int) Math.ceil((to - from) * phaseX) * 2; float[] valuePoints = new float[count]; @@ -189,7 +189,7 @@ public float[] generateTransformedValuesLine(ILineDataSet data, public float[] generateTransformedValuesCandle(ICandleDataSet data, float phaseX, float phaseY, int from, int to) { - final int count = (int) Math.ceil((to - from) * phaseX) * 2; + final int count = (int) ((to - from) * phaseX + 1) * 2; float[] valuePoints = new float[count]; From c18a2e270cc5d8cbdc9ca65e3863530d6e02b40e Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 19 Jun 2016 21:53:31 +0200 Subject: [PATCH 246/606] Improvements to scatter value rendering --- .../charting/highlight/ChartHighlighter.java | 9 ++++++++- .../charting/renderer/ScatterChartRenderer.java | 10 ++++++---- .../github/mikephil/charting/utils/Transformer.java | 12 +++++++----- 3 files changed, 21 insertions(+), 10 deletions(-) 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 58ea3cbc67..0deaea5ebf 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 @@ -59,6 +59,10 @@ protected Highlight getHighlightForX(float xVal, float x, float y) { List closestValues = getHighlightsAtXPos(xVal, x, y); + if(closestValues.isEmpty()) { + return null; + } + float leftAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.LEFT); float rightAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.RIGHT); @@ -128,7 +132,10 @@ protected List getHighlightsAtXPos(float xVal, float x, float y) { if (!dataSet.isHighlightEnabled()) continue; - vals.add(buildHighlight(dataSet, i, xVal, DataSet.Rounding.CLOSEST)); + Highlight high = buildHighlight(dataSet, i, xVal, DataSet.Rounding.CLOSEST); + + if(high != null) + vals.add(high); //vals.add(buildHighlight(dataSet, i, xVal, DataSet.Rounding.DOWN)); } 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 c4381aab3c..c0a07fe1f1 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 @@ -319,13 +319,15 @@ public void drawValues(Canvas c) { // apply the text-styling defined by the DataSet applyValueTextStyle(dataSet); + XBounds bounds = getXBounds(mChart, dataSet); + float[] positions = mChart.getTransformer(dataSet.getAxisDependency()) .generateTransformedValuesScatter(dataSet, - mAnimator.getPhaseY()); + mAnimator.getPhaseX(), mAnimator.getPhaseY(), bounds.min, bounds.max); float shapeSize = Utils.convertDpToPixel(dataSet.getScatterShapeSize()); - for (int j = 0; j < positions.length * mAnimator.getPhaseX(); j += 2) { + for (int j = 0; j < positions.length; j += 2) { if (!mViewPortHandler.isInBoundsRight(positions[j])) break; @@ -335,10 +337,10 @@ public void drawValues(Canvas c) { || !mViewPortHandler.isInBoundsY(positions[j + 1]))) continue; - Entry entry = dataSet.getEntryForIndex(j / 2); + Entry entry = dataSet.getEntryForIndex(j / 2 + bounds.min); drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, positions[j], - positions[j + 1] - shapeSize, dataSet.getValueTextColor(j / 2)); + positions[j + 1] - shapeSize, dataSet.getValueTextColor(j / 2 + bounds.min)); } } } 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 e6e3bc17af..392d35e53b 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 @@ -102,14 +102,16 @@ public void prepareMatrixOffset(boolean inverted) { * @param data * @return */ - public float[] generateTransformedValuesScatter(IScatterDataSet data, - float phaseY) { + public float[] generateTransformedValuesScatter(IScatterDataSet data, float phaseX, + float phaseY, int from, int to) { - float[] valuePoints = new float[data.getEntryCount() * 2]; + final int count = (int) ((to - from) * phaseX + 1) * 2; - for (int j = 0; j < valuePoints.length; j += 2) { + float[] valuePoints = new float[count]; - Entry e = data.getEntryForIndex(j / 2); + for (int j = 0; j < count; j += 2) { + + Entry e = data.getEntryForIndex(j / 2 + from); if (e != null) { valuePoints[j] = e.getX(); From 82d5fde6f2e8fc7ef1f90257de3671bac6d2d544 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 19 Jun 2016 23:51:25 +0200 Subject: [PATCH 247/606] Add feature that allows different styling for value and entry labels (former slice-text) --- .../mpchartexample/PieChartActivity.java | 7 ++- .../PiePolylineChartActivity.java | 2 +- .../mikephil/charting/charts/PieChart.java | 54 +++++++++++++++---- .../charting/renderer/PieChartRenderer.java | 53 ++++++++++++------ .../mikephil/charting/utils/Transformer.java | 2 - 5 files changed, 89 insertions(+), 29 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 1da4792f36..9027e5baa9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -100,6 +100,11 @@ protected void onCreate(Bundle savedInstanceState) { l.setXEntrySpace(7f); l.setYEntrySpace(0f); l.setYOffset(0f); + + // entry label styling + mChart.setEntryLabelColor(Color.WHITE); + mChart.setEntryLabelTypeface(mTfRegular); + mChart.setEntryLabelTextSize(12f); } @Override @@ -137,7 +142,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } case R.id.actionToggleXVals: { - mChart.setDrawSliceText(!mChart.isDrawSliceTextEnabled()); + mChart.setDrawEntryLabels(!mChart.isDrawEntryLabelsEnabled()); mChart.invalidate(); break; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index 1cbfd58638..cae2d2de89 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -141,7 +141,7 @@ public boolean onOptionsItemSelected(MenuItem item) { } case R.id.actionToggleXVals: { - mChart.setDrawSliceText(!mChart.isDrawSliceTextEnabled()); + mChart.setDrawEntryLabels(!mChart.isDrawEntryLabelsEnabled()); mChart.invalidate(); break; } 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 e56ef96b2e..4b2170ff95 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 @@ -10,8 +10,6 @@ import android.util.AttributeSet; import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.highlight.PieHighlighter; @@ -35,9 +33,9 @@ public class PieChart extends PieRadarChartBase { private RectF mCircleBox = new RectF(); /** - * flag indicating if the x-labels should be drawn or not + * flag indicating if entry labels should be drawn or not */ - private boolean mDrawXLabels = true; + private boolean mDrawEntryLabels = true; /** * array that holds the width of each pie-slice in degrees @@ -560,21 +558,59 @@ public void setTransparentCircleAlpha(int alpha) { } /** - * set this to true to draw the x-value text into the pie slices + * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class). + * Deprecated -> use setDrawEntryLabels(...) instead. * * @param enabled */ + @Deprecated public void setDrawSliceText(boolean enabled) { - mDrawXLabels = enabled; + mDrawEntryLabels = enabled; + } + + /** + * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class). + * + * @param enabled + */ + public void setDrawEntryLabels(boolean enabled) { + mDrawEntryLabels = enabled; } /** - * returns true if drawing x-values is enabled, false if not + * Returns true if drawing the entry labels is enabled, false if not. * * @return */ - public boolean isDrawSliceTextEnabled() { - return mDrawXLabels; + public boolean isDrawEntryLabelsEnabled() { + return mDrawEntryLabels; + } + + /** + * Sets the color the entry labels are drawn with. + * + * @param color + */ + public void setEntryLabelColor(int color) { + ((PieChartRenderer) mRenderer).getPaintEntryLabels().setColor(color); + } + + /** + * Sets a custom Typeface for the drawing of the entry labels. + * + * @param tf + */ + public void setEntryLabelTypeface(Typeface tf) { + ((PieChartRenderer) mRenderer).getPaintEntryLabels().setTypeface(tf); + } + + /** + * Sets the size of the entry labels in dp. Default: 13dp + * + * @param size + */ + public void setEntryLabelTextSize(float size) { + ((PieChartRenderer) mRenderer).getPaintEntryLabels().setTextSize(Utils.convertDpToPixel(size)); } /** 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 4391cd151c..2f8b127d20 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 @@ -18,7 +18,6 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.charts.PieChart; -import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; @@ -51,6 +50,11 @@ public class PieChartRenderer extends DataRenderer { */ private TextPaint mCenterTextPaint; + /** + * paint object used for drwing the slice-text + */ + private Paint mEntryLabelsPaint; + private StaticLayout mCenterTextLayout; private CharSequence mCenterTextLastValue; private RectF mCenterTextLastBounds = new RectF(); @@ -85,6 +89,11 @@ public PieChartRenderer(PieChart chart, ChartAnimator animator, mValuePaint.setColor(Color.WHITE); mValuePaint.setTextAlign(Align.CENTER); + mEntryLabelsPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mEntryLabelsPaint.setColor(Color.WHITE); + mEntryLabelsPaint.setTextAlign(Align.CENTER); + mEntryLabelsPaint.setTextSize(Utils.convertDpToPixel(13f)); + mValueLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mValueLinePaint.setStyle(Style.STROKE); } @@ -101,6 +110,10 @@ public TextPaint getPaintCenterText() { return mCenterTextPaint; } + public Paint getPaintEntryLabels() { + return mEntryLabelsPaint; + } + @Override public void initBuffers() { // TODO Auto-generated method stub @@ -392,7 +405,7 @@ public void drawValues(Canvas c) { float yValueSum = data.getYValueSum(); - boolean drawXVals = mChart.isDrawSliceTextEnabled(); + boolean drawEntryLabels = mChart.isDrawEntryLabelsEnabled(); float angle; int xIndex = 0; @@ -403,9 +416,9 @@ public void drawValues(Canvas c) { IPieDataSet dataSet = dataSets.get(i); - final boolean drawYVals = dataSet.isDrawValuesEnabled(); + final boolean drawValues = dataSet.isDrawValuesEnabled(); - if (!drawYVals && !drawXVals) + if (!drawValues && !drawEntryLabels) continue; final PieDataSet.ValuePosition xValuePosition = dataSet.getXValuePosition(); @@ -453,13 +466,13 @@ public void drawValues(Canvas c) { final float sliceXBase = (float) Math.cos(transformedAngle * Utils.FDEG2RAD); final float sliceYBase = (float) Math.sin(transformedAngle * Utils.FDEG2RAD); - final boolean drawXOutside = drawXVals && + final boolean drawXOutside = drawEntryLabels && xValuePosition == PieDataSet.ValuePosition.OUTSIDE_SLICE; - final boolean drawYOutside = drawYVals && + final boolean drawYOutside = drawValues && yValuePosition == PieDataSet.ValuePosition.OUTSIDE_SLICE; - final boolean drawXInside = drawXVals && + final boolean drawXInside = drawEntryLabels && xValuePosition == PieDataSet.ValuePosition.INSIDE_SLICE; - final boolean drawYInside = drawYVals && + final boolean drawYInside = drawValues && yValuePosition == PieDataSet.ValuePosition.INSIDE_SLICE; if (drawXOutside || drawYOutside) { @@ -523,14 +536,12 @@ public void drawValues(Canvas c) { dataSet.getValueTextColor(j)); if (j < data.getEntryCount() && entry.getLabel() != null) { - c.drawText(entry.getLabel(), labelPtx, labelPty + lineHeight, - mValuePaint); + drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight); } } else if (drawXOutside) { if (j < data.getEntryCount() && entry.getLabel() != null) { - mValuePaint.setColor(dataSet.getValueTextColor(j)); - c.drawText(entry.getLabel(), labelPtx, labelPty + lineHeight / 2.f, mValuePaint); + drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight / 2.f); } } else if (drawYOutside) { @@ -552,14 +563,12 @@ public void drawValues(Canvas c) { drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j)); if (j < data.getEntryCount() && entry.getLabel() != null) { - c.drawText(entry.getLabel(), x, y + lineHeight, - mValuePaint); + drawEntryLabel(c, entry.getLabel(), x, y + lineHeight); } } else if (drawXInside) { if (j < data.getEntryCount() && entry.getLabel() != null) { - mValuePaint.setColor(dataSet.getValueTextColor(j)); - c.drawText(entry.getLabel(), x, y + lineHeight / 2f, mValuePaint); + drawEntryLabel(c, entry.getLabel(), x, y + lineHeight / 2f); } } else if (drawYInside) { @@ -574,6 +583,18 @@ public void drawValues(Canvas c) { c.restore(); } + /** + * Draws an entry label at the specified position. + * + * @param c + * @param label + * @param x + * @param y + */ + protected void drawEntryLabel(Canvas c, String label, float x, float y) { + c.drawText(label, x, y, mEntryLabelsPaint); + } + @Override public void drawExtras(Canvas c) { // drawCircles(c); 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 392d35e53b..8604d56cbd 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 @@ -5,10 +5,8 @@ import android.graphics.Path; import android.graphics.RectF; -import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.CandleEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; From 72890afc106054ae5d63ebc9b819612d269facf9 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 20 Jun 2016 20:50:09 +0200 Subject: [PATCH 248/606] Minor fixes related to double tap highlight --- .../mpchartexample/CombinedChartActivity.java | 5 ----- .../charting/listener/BarLineChartTouchListener.java | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 068491927f..5c244de75b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -125,7 +125,6 @@ private LineData generateLineData() { set.setValueTextColor(Color.rgb(240, 238, 70)); set.setAxisDependency(YAxis.AxisDependency.LEFT); - this.set1 = set; d.addDataSet(set); return d; @@ -209,9 +208,6 @@ protected CandleData generateCandleData() { return d; } - private LineDataSet set1; - private BubbleDataSet set2; - protected BubbleData generateBubbleData() { BubbleData bd = new BubbleData(); @@ -230,7 +226,6 @@ protected BubbleData generateBubbleData() { set.setValueTextColor(Color.WHITE); set.setHighlightCircleWidth(1.5f); set.setDrawValues(true); - this.set2 = set; bd.addDataSet(set); return bd; 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 e82f3cd77c..0fe3ef94ac 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 @@ -552,7 +552,7 @@ public boolean onDoubleTap(MotionEvent e) { } // check if double-tap zooming is enabled - if (mChart.isDoubleTapToZoomEnabled()) { + if (mChart.isDoubleTapToZoomEnabled() && mChart.getData().getEntryCount() > 0) { PointF trans = getTrans(e.getX(), e.getY()); From ded930a2fa99a2e48d86704607d1e19aa60f12b7 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 21 Jun 2016 09:39:48 +0200 Subject: [PATCH 249/606] Work on viewport modifications for horizontal barchart (issue #1842) --- MPChartExample/build.gradle | 2 +- .../charting/charts/BarLineChartBase.java | 21 +++++++++---------- .../charting/charts/HorizontalBarChart.java | 20 ++++++++++++++++++ .../charting/utils/ViewPortHandler.java | 14 +++++++++++++ 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index dd8194aa12..13914819a6 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -57,5 +57,5 @@ dependencies { compile project(':MPChartLib') // remove this if you only imported the example project compile 'com.android.support:appcompat-v7:23.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.0' + //compile 'com.github.PhilJay:MPAndroidChart:v2.2.5' } 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 3e907e3deb..428530bf70 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 @@ -719,7 +719,7 @@ public void setScaleMinima(float scaleX, float scaleY) { /** * Sets the size of the area (range on the x-axis) that should be maximum * visible at once (no further zooming out allowed). If this is e.g. set to - * 10, no more than 10 values on the x-axis can be viewed at once without + * 10, no more than a range of 10 on the x-axis can be viewed at once without * scrolling. * * @param maxXRange The maximum visible range of x-values. @@ -732,7 +732,7 @@ public void setVisibleXRangeMaximum(float maxXRange) { /** * Sets the size of the area (range on the x-axis) that should be minimum * visible at once (no further zooming in allowed). If this is e.g. set to - * 10, no less than 10 values on the x-axis can be viewed at once without + * 10, no less than a range of 10 on the x-axis can be viewed at once without * scrolling. * * @param minXRange The minimum visible range of x-values. @@ -743,9 +743,8 @@ public void setVisibleXRangeMinimum(float minXRange) { } /** - * Limits the maximum and minimum value count that can be visible by - * pinching and zooming. e.g. minRange=10, maxRange=100 no less than 10 - * values and no more that 100 values can be viewed at once without + * Limits the maximum and minimum x range that can be visible by pinching and zooming. e.g. minRange=10, maxRange=100 the + * smallest range to be displayed at once is 10, and no more than a range of 100 values can be viewed at once without * scrolling * * @param minXRange @@ -770,14 +769,14 @@ public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { } /** - * Moves the left side of the current viewport to the specified x-index. + * Moves the left side of the current viewport to the specified x-position. * This also refreshes the chart by calling invalidate(). * - * @param xIndex + * @param xValue */ - public void moveViewToX(float xIndex) { + public void moveViewToX(float xValue) { - Runnable job = new MoveViewJob(mViewPortHandler, xIndex, 0f, + Runnable job = new MoveViewJob(mViewPortHandler, xValue, 0f, getTransformer(AxisDependency.LEFT), this); addViewportJob(job); @@ -802,7 +801,7 @@ public void moveViewToY(float yValue, AxisDependency axis) { /** * This will move the left side of the current viewport to the specified - * x value on the x-axis, and center the viewport to the specified y value on the y-axis. + * x-value on the x-axis, and center the viewport to the specified y value on the y-axis. * This also refreshes the chart by calling invalidate(). * * @param xValue @@ -820,7 +819,7 @@ public void moveViewTo(float xValue, float yValue, AxisDependency axis) { } /** - * This will move the left side of the current viewport to the specified x value + * This will move the left side of the current viewport to the specified x-value * and center the viewport to the y value animated. * This also refreshes the chart by calling invalidate(). * 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 f95132769b..0c378df0db 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 @@ -7,6 +7,7 @@ import android.util.Log; import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; @@ -205,4 +206,23 @@ public float getHighestVisibleX() { mViewPortHandler.contentTop()); return (float) Math.min(mXAxis.mAxisMaximum, pos.y); } + + @Override + public void setVisibleXRangeMaximum(float maxXRange) { + float xScale = mXAxis.mAxisRange / (maxXRange); + mViewPortHandler.setMinimumScaleY(xScale); + } + + @Override + public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { + float yScale = getDeltaY(axis) / maxYRange; + mViewPortHandler.setMinimumScaleX(yScale); + } + + @Override + public void setVisibleXRange(float minXRange, float maxXRange) { + float maxScale = mXAxis.mAxisRange / minXRange; + float minScale = mXAxis.mAxisRange / maxXRange; + mViewPortHandler.setMinMaxScaleY(minScale, maxScale); + } } 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 e7cfac56b3..d58f3f09ae 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 @@ -509,6 +509,20 @@ public void setMaximumScaleY(float yScale) { limitTransAndScale(mMatrixTouch, mContentRect); } + public void setMinMaxScaleY(float minScaleY, float maxScaleY) { + + if (minScaleY < 1f) + minScaleY = 1f; + + if (maxScaleY == 0.f) + maxScaleY = Float.MAX_VALUE; + + mMinScaleX = minScaleY; + mMaxScaleX = maxScaleY; + + limitTransAndScale(mMatrixTouch, mContentRect); + } + /** * Returns the charts-touch matrix used for translation and scale on touch. * From f83a7e3b26ddaf4b68ffacb59fe67c9e3b55fe2d Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Tue, 21 Jun 2016 11:11:49 +0200 Subject: [PATCH 250/606] Minor changes to highlighting --- .../java/com/github/mikephil/charting/charts/Chart.java | 2 +- .../github/mikephil/charting/highlight/Highlight.java | 9 +++++++-- 2 files changed, 8 insertions(+), 3 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 58995395db..e2d40d87c5 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 @@ -632,7 +632,7 @@ public void highlightValue(Highlight high, boolean callListener) { } else { if (this instanceof BarLineChartBase && ((BarLineChartBase) this).isHighlightFullBarEnabled()) - high = new Highlight(high.getX(), Float.NaN, Float.NaN, Float.NaN, -1, YAxis.AxisDependency.LEFT); + high = new Highlight(high.getX(), -1); // set the indices to highlight mIndicesToHighlight = new Highlight[]{ 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 f8989a4da7..021f80cce8 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 @@ -70,6 +70,11 @@ public Highlight(float x, int dataSetIndex) { this.mDataSetIndex = dataSetIndex; } + public Highlight(float x, int dataSetIndex, int stackIndex) { + this(x, dataSetIndex); + this.mStackIndex = stackIndex; + } + /** * constructor * @@ -77,7 +82,7 @@ public Highlight(float x, int dataSetIndex) { * @param y the y-value of the highlighted value * @param dataSetIndex the index of the DataSet the highlighted value belongs to */ - public Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, YAxis.AxisDependency axis) { + protected 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 +101,7 @@ public Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, YAxis * selected * @param range the range the selected stack-value is in */ - public Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, int stackIndex, Range range, + protected Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, int stackIndex, Range range, YAxis.AxisDependency axis) { this(x, y, xPx, yPx, dataSetIndex, axis); this.mStackIndex = stackIndex; From 6878220e2e798e8c4e0b20e39bb051633b9c96ea Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Wed, 22 Jun 2016 01:06:56 +0200 Subject: [PATCH 251/606] Improvements to chart highlighting --- .../mikephil/charting/charts/BarChart.java | 12 +++ .../mikephil/charting/data/BarEntry.java | 47 ++++++++++++ .../charting/highlight/BarHighlighter.java | 73 +++++++++---------- .../charting/highlight/ChartHighlighter.java | 1 - .../charting/highlight/Highlight.java | 19 +---- .../charting/renderer/BarChartRenderer.java | 10 ++- 6 files changed, 102 insertions(+), 60 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 fbcf5ccc0b..a8c166d5a5 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 @@ -183,6 +183,18 @@ public boolean isDrawBarShadowEnabled() { return mDrawBarShadow; } + /** + * Highlights the value at the given x-position in the given DataSet. Provide + * -1 as the dataSetIndex to undo all highlighting. + * + * @param x + * @param dataSetIndex + * @param stackIndex the index inside the stack - only relevant for stacked entries + */ + public void highlightValue(float x, int dataSetIndex, int stackIndex) { + highlightValue(new Highlight(x, dataSetIndex, stackIndex), false); + } + @Override public BarData getBarData() { return mData; 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 97358ae378..c884098d31 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 @@ -2,6 +2,8 @@ import android.annotation.SuppressLint; +import com.github.mikephil.charting.highlight.Range; + /** * Entry class for the BarChart. (especially stacked bars) * @@ -15,6 +17,11 @@ public class BarEntry extends Entry { */ private float[] mYVals; + /** + * the ranges for the individual stack values - automatically calculated + */ + private Range[] mRanges; + /** * the sum of all negative values this entry (if stacked) contains */ @@ -35,6 +42,7 @@ public BarEntry(float x, float[] vals) { super(x, calcSum(vals)); this.mYVals = vals; + calcRanges(); calcPosNegSum(); } @@ -59,6 +67,7 @@ public BarEntry(float x, float[] vals, String label) { super(x, calcSum(vals), label); this.mYVals = vals; + calcRanges(); calcPosNegSum(); } @@ -102,6 +111,7 @@ public void setVals(float[] vals) { setY(calcSum(vals)); mYVals = vals; calcPosNegSum(); + calcRanges(); } /** @@ -114,6 +124,15 @@ public float getY() { return super.getY(); } + /** + * Returns the ranges of the individual stack-entries. Will return null if this entry is not stacked. + * + * @return + */ + public Range[] getRanges() { + return mRanges; + } + /** * Returns true if this BarEntry is stacked (has a values array), false if not. * @@ -197,4 +216,32 @@ private static float calcSum(float[] vals) { return sum; } + + protected void calcRanges() { + + float[] values = getYVals(); + + if (values == null || values.length == 0) + return; + + mRanges = new Range[values.length]; + + float negRemain = -getNegativeSum(); + float posRemain = 0f; + + for (int i = 0; i < mRanges.length; i++) { + + float value = values[i]; + + if (value < 0) { + mRanges[i] = new Range(negRemain, negRemain + Math.abs(value)); + negRemain += Math.abs(value); + } else { + mRanges[i] = new Range(posRemain, posRemain + value); + posRemain += value; + } + } + } } + + 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 a1efc8df88..048db03299 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 @@ -61,14 +61,12 @@ public Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal if (entry.getYVals() == null) { return high; } else { - Range[] ranges = getRanges(entry); + Range[] ranges = entry.getRanges(); if (ranges.length > 0) { int stackIndex = getClosestStackIndex(ranges, yVal); - Range range = ranges[stackIndex]; - - PointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(high.getX(), range.to); + PointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(high.getX(), ranges[stackIndex].to); Highlight stackedHigh = new Highlight( entry.getX(), @@ -77,7 +75,6 @@ public Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal (float) pixels.y, high.getDataSetIndex(), stackIndex, - range, high.getAxis() ); @@ -116,39 +113,39 @@ protected int getClosestStackIndex(Range[] ranges, float value) { return (value > ranges[length].to) ? length : 0; } - /** - * Splits up the stack-values of the given bar-entry into Range objects. - * - * @param entry - * @return - */ - protected Range[] getRanges(BarEntry entry) { - - float[] values = entry.getYVals(); - - if (values == null || values.length == 0) - return new Range[0]; - - Range[] ranges = new Range[values.length]; - - float negRemain = -entry.getNegativeSum(); - float posRemain = 0f; - - for (int i = 0; i < ranges.length; i++) { - - float value = values[i]; - - if (value < 0) { - ranges[i] = new Range(negRemain, negRemain + Math.abs(value)); - negRemain += Math.abs(value); - } else { - ranges[i] = new Range(posRemain, posRemain + value); - posRemain += value; - } - } - - return ranges; - } +// /** +// * Splits up the stack-values of the given bar-entry into Range objects. +// * +// * @param entry +// * @return +// */ +// protected Range[] getRanges(BarEntry entry) { +// +// float[] values = entry.getYVals(); +// +// if (values == null || values.length == 0) +// return new Range[0]; +// +// Range[] ranges = new Range[values.length]; +// +// float negRemain = -entry.getNegativeSum(); +// float posRemain = 0f; +// +// for (int i = 0; i < ranges.length; i++) { +// +// float value = values[i]; +// +// if (value < 0) { +// ranges[i] = new Range(negRemain, negRemain + Math.abs(value)); +// negRemain += Math.abs(value); +// } else { +// ranges[i] = new Range(posRemain, posRemain + value); +// posRemain += value; +// } +// } +// +// return ranges; +// } @Override protected float getDistance(float x1, float y1, float x2, float y2) { 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 0deaea5ebf..ced7150c94 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 @@ -33,7 +33,6 @@ public Highlight getHighlight(float x, float y) { Highlight high = getHighlightForX(xVal, x, y); return high; } - /** * Returns the corresponding xPos for a given touch-position in pixels. * 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 021f80cce8..5599882ce7 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 @@ -45,11 +45,6 @@ public class Highlight { */ private int mStackIndex = -1; - /** - * the range of the bar that is selected (only for stacked-barchart) - */ - private Range mRange; - /** * the axis the highlighted value belongs to */ @@ -99,13 +94,10 @@ protected Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, YA * @param dataSetIndex the index of the DataSet the highlighted value belongs to * @param stackIndex references which value of a stacked-bar entry has been * selected - * @param range the range the selected stack-value is in */ - protected Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, int stackIndex, Range range, - YAxis.AxisDependency axis) { + protected 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; - this.mRange = range; } /** @@ -172,15 +164,6 @@ public int getStackIndex() { return mStackIndex; } - /** - * Returns the range of values the selected value of a stacked bar is in. (this is only relevant for stacked-barchart) - * - * @return - */ - public Range getRange() { - return mRange; - } - public boolean isStacked() { return mStackIndex >= 0; } 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 ae6e12d793..94bd9a47b9 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 @@ -11,6 +11,7 @@ import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.highlight.Range; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.utils.Transformer; @@ -343,14 +344,17 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { mHighlightPaint.setColor(set.getHighLightColor()); mHighlightPaint.setAlpha(set.getHighLightAlpha()); - boolean isStack = high.getStackIndex() < 0 ? false : true; + boolean isStack = (high.getStackIndex() >= 0 && e.isStacked()) ? true : false; final float y1; final float y2; if (isStack) { - y1 = high.getRange().from; - y2 = high.getRange().to; + + Range range = e.getRanges()[high.getStackIndex()]; + + y1 = range.from; + y2 = range.to; } else { y1 = e.getY(); y2 = 0.f; From cb0226b2312dcd69c16d4dc0825f02c72471a8d8 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 23 Jun 2016 15:11:19 +0200 Subject: [PATCH 252/606] Update CONTRIBUTING.md --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a6fb7cac57..2e4d5b866c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # How to contribute -Bug-fixes and features often come from users of the Charts framework, and improving 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 +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. @@ -46,4 +46,4 @@ If you are reporting a bug which can be observed visually, please add to your is 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. \ No newline at end of file +a ticket number. Even the default commit message the GitHub generates is fine with us. From 0eb69d246c567cdefe029a957c0a627504a95073 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 24 Jun 2016 23:01:05 +0200 Subject: [PATCH 253/606] Changes related to highlight full bar --- .../mpchartexample/CombinedChartActivity.java | 1 + .../mpchartexample/StackedBarActivity.java | 1 + .../StackedBarActivityNegative.java | 1 + .../mikephil/charting/charts/BarChart.java | 24 +++++++++++++++++++ .../charting/charts/BarLineChartBase.java | 22 ----------------- .../mikephil/charting/charts/Chart.java | 4 ---- .../charting/charts/CombinedChart.java | 24 +++++++++++++++++++ .../dataprovider/BarDataProvider.java | 1 + .../charting/renderer/BarChartRenderer.java | 2 +- 9 files changed, 53 insertions(+), 27 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java index 5c244de75b..519a15d219 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -55,6 +55,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setBackgroundColor(Color.WHITE); mChart.setDrawGridBackground(false); mChart.setDrawBarShadow(false); + mChart.setHighlightFullBarEnabled(false); // draw bars behind lines mChart.setDrawOrder(new DrawOrder[]{ diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java index 9b9767001c..5c562190bc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -69,6 +69,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawBarShadow(false); mChart.setDrawValueAboveBar(false); + mChart.setHighlightFullBarEnabled(false); // change the position of the y-labels YAxis leftAxis = mChart.getAxisLeft(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index dc2a8825fc..200c012771 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -56,6 +56,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawBarShadow(false); mChart.setDrawValueAboveBar(true); + mChart.setHighlightFullBarEnabled(false); mChart.getAxisLeft().setEnabled(false); mChart.getAxisRight().setAxisMaxValue(25f); 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 a8c166d5a5..fd38dea483 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 @@ -27,6 +27,11 @@ public class BarChart extends BarLineChartBase implements BarDataProvid */ private boolean mDrawHighlightArrow = false; + /** + * flag that indicates whether the highlight should be full-bar oriented, or single-value? + */ + protected boolean mHighlightFullBarEnabled = false; + /** * if set to true, all values are drawn above their bars, instead of below their top */ @@ -183,6 +188,25 @@ public boolean isDrawBarShadowEnabled() { return mDrawBarShadow; } + /** + * Set this to true to make the highlight operation full-bar oriented, + * false to make it highlight single values (relevant only for stacked). + * Default: false + * + * @param enabled + */ + public void setHighlightFullBarEnabled(boolean enabled) { + mHighlightFullBarEnabled = enabled; + } + + /** + * @return true the highlight operation is be full-bar oriented, false if single-value + */ + @Override + public boolean isHighlightFullBarEnabled() { + return mHighlightFullBarEnabled; + } + /** * Highlights the value at the given x-position in the given DataSet. Provide * -1 as the dataSetIndex to undo all highlighting. 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 428530bf70..478452d6ad 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 @@ -77,11 +77,6 @@ public abstract class BarLineChartBase implements Com */ private boolean mDrawValueAboveBar = true; + + /** + * flag that indicates whether the highlight should be full-bar oriented, or single-value? + */ + protected boolean mHighlightFullBarEnabled = false; + /** * if set to true, a grey area is drawn behind each bar that indicates the * maximum value @@ -149,6 +155,24 @@ public void setDrawBarShadow(boolean enabled) { mDrawBarShadow = enabled; } + /** + * Set this to true to make the highlight operation full-bar oriented, + * false to make it highlight single values (relevant only for stacked). + * + * @param enabled + */ + public void setHighlightFullBarEnabled(boolean enabled) { + mHighlightFullBarEnabled = enabled; + } + + /** + * @return true the highlight operation is be full-bar oriented, false if single-value + */ + @Override + public boolean isHighlightFullBarEnabled() { + return mHighlightFullBarEnabled; + } + /** * Returns the currently set draw order. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java index 631060d2d6..9dfee07f9c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java @@ -7,4 +7,5 @@ public interface BarDataProvider extends BarLineScatterCandleBubbleDataProvider BarData getBarData(); boolean isDrawBarShadowEnabled(); boolean isDrawValueAboveBarEnabled(); + boolean isHighlightFullBarEnabled(); } 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 94bd9a47b9..471ed7e558 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 @@ -349,7 +349,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { final float y1; final float y2; - if (isStack) { + if (isStack && !mChart.isHighlightFullBarEnabled()) { Range range = e.getRanges()[high.getStackIndex()]; From 5f8eb44d9f1f60099544ecc23a9ba2d2ffe5bccf Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 24 Jun 2016 23:10:03 +0200 Subject: [PATCH 254/606] Fixes related to highlight rendering --- .../github/mikephil/charting/data/BarEntry.java | 2 +- .../charting/renderer/BarChartRenderer.java | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 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 c884098d31..023a159750 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 @@ -234,7 +234,7 @@ protected void calcRanges() { float value = values[i]; if (value < 0) { - mRanges[i] = new Range(negRemain, negRemain + Math.abs(value)); + mRanges[i] = new Range(negRemain, negRemain + value); negRemain += Math.abs(value); } else { mRanges[i] = new Range(posRemain, posRemain + value); 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 471ed7e558..cffcc1c3da 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 @@ -349,12 +349,21 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { final float y1; final float y2; - if (isStack && !mChart.isHighlightFullBarEnabled()) { + if (isStack) { - Range range = e.getRanges()[high.getStackIndex()]; + if(mChart.isHighlightFullBarEnabled()) { + + y1 = e.getPositiveSum(); + y2 = -e.getNegativeSum(); + + } else { + + Range range = e.getRanges()[high.getStackIndex()]; + + y1 = range.from; + y2 = range.to; + } - y1 = range.from; - y2 = range.to; } else { y1 = e.getY(); y2 = 0.f; From f345d164ce4f6a6bceb40b78b1ca8f607ce8d8e6 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 25 Jun 2016 11:00:29 +0200 Subject: [PATCH 255/606] Fix issue related to horizontal dashed grid lines #1756 --- .../HorizontalBarChartActivity.java | 1 - .../mpchartexample/LineChartActivity1.java | 2 + .../mikephil/charting/charts/BarChart.java | 31 ++------------- .../charting/renderer/XAxisRenderer.java | 39 ++++++++++++------- .../XAxisRendererHorizontalBarChart.java | 29 +++----------- 5 files changed, 37 insertions(+), 65 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index e828d4dcf7..2d3018a48a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -79,7 +79,6 @@ protected void onCreate(Bundle savedInstanceState) { xl.setTypeface(mTfLight); xl.setDrawAxisLine(true); xl.setDrawGridLines(false); - xl.setGridLineWidth(0.3f); xl.setGranularity(10f); YAxis yl = mChart.getAxisLeft(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java index fd8c8f9e4f..ade229807e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -104,9 +104,11 @@ protected void onCreate(Bundle savedInstanceState) { llXAxis.setTextSize(10f); XAxis xAxis = mChart.getXAxis(); + xAxis.enableGridDashedLine(10f, 10f, 0f); //xAxis.setValueFormatter(new MyCustomXAxisValueFormatter()); //xAxis.addLimitLine(llXAxis); // add x-axis limit line + Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); LimitLine ll1 = new LimitLine(130f, "Upper Limit"); 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 fd38dea483..9b437c9028 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 @@ -8,7 +8,6 @@ import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.BarHighlighter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; @@ -22,11 +21,6 @@ */ public class BarChart extends BarLineChartBase implements BarDataProvider { - /** - * flag that enables or disables the highlighting arrow - */ - private boolean mDrawHighlightArrow = false; - /** * flag that indicates whether the highlight should be full-bar oriented, or single-value? */ @@ -133,24 +127,6 @@ public RectF getBarBounds(BarEntry e) { return bounds; } -// /** -// * set this to true to draw the highlightning arrow -// * -// * @param enabled -// */ -// public void setDrawHighlightArrow(boolean enabled) { -// mDrawHighlightArrow = enabled; -// } -// -// /** -// * returns true if drawing the highlighting arrow is enabled, false if not -// * -// * @return -// */ -// public boolean isDrawHighlightArrowEnabled() { -// return mDrawHighlightArrow; -// } - /** * If set to true, all values are drawn above their bars, instead of below their top. * @@ -189,8 +165,9 @@ public boolean isDrawBarShadowEnabled() { } /** - * Set this to true to make the highlight operation full-bar oriented, - * false to make it highlight single values (relevant only for stacked). + * Set this to true to make the highlight operation full-bar oriented, false to make it highlight single values (relevant + * only for stacked). If enabled, highlighting operations will highlight the whole bar, even if only a single stack entry + * was tapped. * Default: false * * @param enabled @@ -213,7 +190,7 @@ public boolean isHighlightFullBarEnabled() { * * @param x * @param dataSetIndex - * @param stackIndex the index inside the stack - only relevant for stacked entries + * @param stackIndex the index inside the stack - only relevant for stacked entries */ public void highlightValue(float x, int dataSetIndex, int stackIndex) { highlightValue(new Highlight(x, dataSetIndex, stackIndex), false); 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 dd17aec6c4..e0a2352cf5 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 @@ -33,6 +33,12 @@ public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer t mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); } + protected void setupGridPaint() { + mGridPaint.setColor(mXAxis.getGridColor()); + mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); + mGridPaint.setPathEffect(mXAxis.getGridDashPathEffect()); + } + @Override public void computeAxis(float min, float max, boolean inverted) { @@ -223,34 +229,39 @@ public void renderGridLines(Canvas c) { float[] positions = new float[mXAxis.mEntryCount * 2]; for (int i = 0; i < positions.length; i += 2) { - // only fill x values positions[i] = mXAxis.mEntries[i / 2]; + positions[i + 1] = mXAxis.mEntries[i / 2]; } mTrans.pointValuesToPixel(positions); - mGridPaint.setColor(mXAxis.getGridColor()); - mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); - mGridPaint.setPathEffect(mXAxis.getGridDashPathEffect()); + setupGridPaint(); Path gridLinePath = new Path(); for (int i = 0; i < positions.length; i += 2) { - float x = positions[i]; + drawGridLine(c, positions[i], positions[i + 1], gridLinePath); + } + } - if (x >= mViewPortHandler.offsetLeft() - && x <= mViewPortHandler.getChartWidth()) { + /** + * Draws the grid line at the specified position using the provided path. + * + * @param c + * @param x + * @param y + * @param gridLinePath + */ + protected void drawGridLine(Canvas c, float x, float y, Path gridLinePath) { - gridLinePath.moveTo(x, mViewPortHandler.contentBottom()); - gridLinePath.lineTo(x, mViewPortHandler.contentTop()); + gridLinePath.moveTo(x, mViewPortHandler.contentBottom()); + gridLinePath.lineTo(x, mViewPortHandler.contentTop()); - // draw a path because lines don't support dashing on lower android versions - c.drawPath(gridLinePath, mGridPaint); - } + // draw a path because lines don't support dashing on lower android versions + c.drawPath(gridLinePath, mGridPaint); - gridLinePath.reset(); - } + gridLinePath.reset(); } /** 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 9f070e8ed0..526e4ef7fd 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 @@ -152,32 +152,15 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { } @Override - public void renderGridLines(Canvas c) { + protected void drawGridLine(Canvas c, float x, float y, Path gridLinePath) { - if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) - return; - - mGridPaint.setColor(mXAxis.getGridColor()); - mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); - - float[] positions = new float[mXAxis.mEntryCount * 2]; - - for (int i = 0; i < positions.length; i += 2) { - positions[i + 1] = mXAxis.mEntries[i / 2]; - } - - mTrans.pointValuesToPixel(positions); - - for (int i = 0; i < positions.length; i += 2) { - - float y = positions[i + 1]; + gridLinePath.moveTo(mViewPortHandler.contentRight(), y); + gridLinePath.lineTo(mViewPortHandler.contentLeft(), y); - if (mViewPortHandler.isInBoundsY(y)) { + // draw a path because lines don't support dashing on lower android versions + c.drawPath(gridLinePath, mGridPaint); - c.drawLine(mViewPortHandler.contentLeft(), y, - mViewPortHandler.contentRight(), y, mGridPaint); - } - } + gridLinePath.reset(); } @Override From b5bfc3e2bd05c31a1b7ec18d3950a252cc86097c Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 26 Jun 2016 00:24:19 +0200 Subject: [PATCH 256/606] Fixes related to setting visible axis range for horizontal barchart --- .../HorizontalBarChartActivity.java | 3 +- .../charting/charts/BarLineChartBase.java | 30 +++++++++++++++-- .../charting/charts/HorizontalBarChart.java | 33 ++++++++++++++++--- .../charting/utils/ViewPortHandler.java | 4 +-- 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 2d3018a48a..411a0de200 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -111,7 +111,8 @@ protected void onCreate(Bundle savedInstanceState) { l.setFormSize(8f); l.setXEntrySpace(4f); - // mChart.setDrawLegend(false); +// mChart.setVisibleXRange(50, 10); +// mChart.moveViewToX(20); } @Override 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 478452d6ad..1004f7b659 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 @@ -746,8 +746,8 @@ public void setVisibleXRangeMinimum(float minXRange) { * @param maxXRange */ public void setVisibleXRange(float minXRange, float maxXRange) { - float maxScale = mXAxis.mAxisRange / minXRange; - float minScale = mXAxis.mAxisRange / maxXRange; + float minScale = mXAxis.mAxisRange / minXRange; + float maxScale = mXAxis.mAxisRange / maxXRange; mViewPortHandler.setMinMaxScaleX(minScale, maxScale); } @@ -756,13 +756,37 @@ public void setVisibleXRange(float minXRange, float maxXRange) { * visible at once. * * @param maxYRange the maximum visible range on the y-axis - * @param axis - the axis for which this limit should apply + * @param axis the axis for which this limit should apply */ public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { float yScale = getDeltaY(axis) / maxYRange; mViewPortHandler.setMinimumScaleY(yScale); } + /** + * Sets the size of the area (range on the y-axis) that should be minimum visible at once, no further zooming in possible. + * + * @param minYRange + * @param axis the axis for which this limit should apply + */ + public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) { + float yScale = getDeltaY(axis) / minYRange; + mViewPortHandler.setMaximumScaleY(yScale); + } + + /** + * Limits the maximum and minimum y range that can be visible by pinching and zooming. + * + * @param minYRange + * @param maxYRange + * @param axis + */ + public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis) { + float minScale = getDeltaY(axis) / minYRange; + float maxScale = getDeltaY(axis) / maxYRange; + mViewPortHandler.setMinMaxScaleY(minScale, maxScale); + } + /** * Moves the left side of the current viewport to the specified x-position. * This also refreshes the chart by calling invalidate(). 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 0c378df0db..56064ae2aa 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 @@ -7,13 +7,13 @@ import android.util.Log; import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.highlight.HorizontalBarHighlighter; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.jobs.MoveViewJob; import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer; import com.github.mikephil.charting.renderer.XAxisRendererHorizontalBarChart; import com.github.mikephil.charting.renderer.YAxisRendererHorizontalBarChart; @@ -207,12 +207,29 @@ public float getHighestVisibleX() { return (float) Math.min(mXAxis.mAxisMaximum, pos.y); } + /** + * ###### VIEWPORT METHODS BELOW THIS ###### + */ + @Override public void setVisibleXRangeMaximum(float maxXRange) { float xScale = mXAxis.mAxisRange / (maxXRange); mViewPortHandler.setMinimumScaleY(xScale); } + @Override + public void setVisibleXRangeMinimum(float minXRange) { + float xScale = mXAxis.mAxisRange / (minXRange); + mViewPortHandler.setMaximumScaleY(xScale); + } + + @Override + public void setVisibleXRange(float minXRange, float maxXRange) { + float minScale = mXAxis.mAxisRange / minXRange; + float maxScale = mXAxis.mAxisRange / maxXRange; + mViewPortHandler.setMinMaxScaleY(minScale, maxScale); + } + @Override public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { float yScale = getDeltaY(axis) / maxYRange; @@ -220,9 +237,15 @@ public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { } @Override - public void setVisibleXRange(float minXRange, float maxXRange) { - float maxScale = mXAxis.mAxisRange / minXRange; - float minScale = mXAxis.mAxisRange / maxXRange; - mViewPortHandler.setMinMaxScaleY(minScale, maxScale); + public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) { + float yScale = getDeltaY(axis) / minYRange; + mViewPortHandler.setMaximumScaleX(yScale); + } + + @Override + public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis) { + float minScale = getDeltaY(axis) / minYRange; + float maxScale = getDeltaY(axis) / maxYRange; + mViewPortHandler.setMinMaxScaleX(minScale, maxScale); } } 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 d58f3f09ae..5ee48a8eed 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 @@ -517,8 +517,8 @@ public void setMinMaxScaleY(float minScaleY, float maxScaleY) { if (maxScaleY == 0.f) maxScaleY = Float.MAX_VALUE; - mMinScaleX = minScaleY; - mMaxScaleX = maxScaleY; + mMinScaleY = minScaleY; + mMaxScaleY = maxScaleY; limitTransAndScale(mMatrixTouch, mContentRect); } From 3b0be740cd2fa10cc5f550fee72de17783f87722 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 26 Jun 2016 10:05:54 +0200 Subject: [PATCH 257/606] Add example for half-piechart --- MPChartExample/AndroidManifest.xml | 1 + .../res/layout/activity_piechart_half.xml | 11 ++ .../mpchartexample/HalfPieChartActivity.java | 134 ++++++++++++++++++ .../notimportant/MainActivity.java | 8 ++ .../mikephil/charting/charts/PieChart.java | 21 +++ .../charting/renderer/PieChartRenderer.java | 12 +- 6 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 MPChartExample/res/layout/activity_piechart_half.xml create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index e07a8bdfda..863e2ba381 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -65,6 +65,7 @@ + diff --git a/MPChartExample/res/layout/activity_piechart_half.xml b/MPChartExample/res/layout/activity_piechart_half.xml new file mode 100644 index 0000000000..52c62806b0 --- /dev/null +++ b/MPChartExample/res/layout/activity_piechart_half.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java new file mode 100644 index 0000000000..46967097f0 --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java @@ -0,0 +1,134 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.graphics.Color; +import android.graphics.Point; +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.Display; +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; +import com.github.mikephil.charting.formatter.PercentFormatter; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class HalfPieChartActivity extends DemoBase { + + 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_half); + + mChart = (PieChart) findViewById(R.id.chart1); + mChart.setBackgroundColor(Color.WHITE); + + moveOffScreen(); + + mChart.setUsePercentValues(true); + mChart.setDescription(""); + + mChart.setCenterTextTypeface(mTfLight); + mChart.setCenterText(generateCenterSpannableText()); + + mChart.setDrawHoleEnabled(true); + mChart.setHoleColor(Color.WHITE); + + mChart.setTransparentCircleColor(Color.WHITE); + mChart.setTransparentCircleAlpha(110); + + mChart.setHoleRadius(58f); + mChart.setTransparentCircleRadius(61f); + + mChart.setDrawCenterText(true); + + mChart.setRotationEnabled(false); + mChart.setHighlightPerTapEnabled(true); + + mChart.setMaxAngle(180f); // HALF CHART + mChart.setRotationAngle(180f); + mChart.setCenterTextOffset(0, -20); + + setData(4, 100); + + mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); + + Legend l = mChart.getLegend(); + l.setPosition(LegendPosition.ABOVE_CHART_CENTER); + l.setXEntrySpace(7f); + l.setYEntrySpace(0f); + l.setYOffset(0f); + + // entry label styling + mChart.setEntryLabelColor(Color.WHITE); + mChart.setEntryLabelTypeface(mTfRegular); + mChart.setEntryLabelTextSize(12f); + } + + private void setData(int count, float range) { + + 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])); + } + + PieDataSet dataSet = new PieDataSet(values, "Election Results"); + dataSet.setSliceSpace(3f); + dataSet.setSelectionShift(5f); + + dataSet.setColors(ColorTemplate.MATERIAL_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); + + mChart.invalidate(); + } + + private SpannableString generateCenterSpannableText() { + + SpannableString s = new SpannableString("MPAndroidChart\ndeveloped by Philipp Jahoda"); + s.setSpan(new RelativeSizeSpan(1.7f), 0, 14, 0); + s.setSpan(new StyleSpan(Typeface.NORMAL), 14, s.length() - 15, 0); + s.setSpan(new ForegroundColorSpan(Color.GRAY), 14, s.length() - 15, 0); + s.setSpan(new RelativeSizeSpan(.8f), 14, s.length() - 15, 0); + s.setSpan(new StyleSpan(Typeface.ITALIC), s.length() - 14, s.length(), 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), s.length() - 14, s.length(), 0); + return s; + } + + private void moveOffScreen() { + + Display display = getWindowManager().getDefaultDisplay(); + int height = display.getHeight(); // deprecated + + int offset = (int)(height * 0.65); /* or whatever */ + + RelativeLayout.LayoutParams rlParams = + (RelativeLayout.LayoutParams)mChart.getLayoutParams(); + rlParams.setMargins(0, 0, 0, -offset); + mChart.setLayoutParams(rlParams); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index a462f4517c..617e43c021 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -25,6 +25,7 @@ import com.xxmassdeveloper.mpchartexample.CubicLineChartActivity; import com.xxmassdeveloper.mpchartexample.DynamicalAddingActivity; import com.xxmassdeveloper.mpchartexample.FilledLineActivity; +import com.xxmassdeveloper.mpchartexample.HalfPieChartActivity; import com.xxmassdeveloper.mpchartexample.HorizontalBarChartActivity; import com.xxmassdeveloper.mpchartexample.InvertedLineChartActivity; import com.xxmassdeveloper.mpchartexample.LineChartActivity1; @@ -143,6 +144,9 @@ protected void onCreate(Bundle savedInstanceState) { 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.")); MyAdapter adapter = new MyAdapter(this, objects); @@ -282,6 +286,10 @@ public void onItemClick(AdapterView av, View v, int pos, long arg3) { i = new Intent(this, FilledLineActivity.class); startActivity(i); break; + case 31: + i = new Intent(this, HalfPieChartActivity.class); + startActivity(i); + break; } 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 4b2170ff95..232fe48ea8 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 @@ -72,6 +72,8 @@ public class PieChart extends PieRadarChartBase { */ private CharSequence mCenterText = ""; + private PointF mCenterTextOffset = new PointF(0, 0); + /** * indicates the size of the hole in the center of the piechart, default: * radius / 2 @@ -489,6 +491,25 @@ public void setCenterTextSizePixels(float sizePixels) { ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize(sizePixels); } + /** + * Sets the offset the center text should have from it's original position in dp. Default x = 0, y = 0 + * + * @param x + * @param y + */ + public void setCenterTextOffset(float x, float y) { + mCenterTextOffset = new PointF(Utils.convertDpToPixel(x), Utils.convertDpToPixel(y)); + } + + /** + * Returns the offset on the x- and y-axis the center text has in dp. + * + * @return + */ + public PointF getCenterTextOffset() { + return mCenterTextOffset; + } + /** * Sets the color of the center text of the PieChart. * 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 2f8b127d20..8eb5311ee9 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 @@ -656,16 +656,20 @@ protected void drawCenterText(Canvas c) { if (mChart.isDrawCenterTextEnabled() && centerText != null) { PointF center = mChart.getCenterCircleBox(); + PointF offset = mChart.getCenterTextOffset(); + + float x = center.x + offset.x; + float y = center.y + offset.y; float innerRadius = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled() ? mChart.getRadius() * (mChart.getHoleRadius() / 100f) : mChart.getRadius(); RectF holeRect = mRectBuffer[0]; - holeRect.left = center.x - innerRadius; - holeRect.top = center.y - innerRadius; - holeRect.right = center.x + innerRadius; - holeRect.bottom = center.y + innerRadius; + holeRect.left = x - innerRadius; + holeRect.top = y - innerRadius; + holeRect.right = x + innerRadius; + holeRect.bottom = y + innerRadius; RectF boundingRect = mRectBuffer[1]; boundingRect.set(holeRect); From 98e090ca19c73c263d1df46d668e557b579117dc Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 26 Jun 2016 10:30:16 +0200 Subject: [PATCH 258/606] Minor example adjustments --- .../mpchartexample/HalfPieChartActivity.java | 2 +- .../mpchartexample/PieChartActivity.java | 17 +++++++------ .../PiePolylineChartActivity.java | 17 +++++++------ .../mpchartexample/RadarChartActivitry.java | 24 +++++++++---------- 4 files changed, 28 insertions(+), 32 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java index 46967097f0..487da96b7e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java @@ -124,7 +124,7 @@ private void moveOffScreen() { Display display = getWindowManager().getDefaultDisplay(); int height = display.getHeight(); // deprecated - int offset = (int)(height * 0.65); /* or whatever */ + int offset = (int)(height * 0.65); /* percent to move */ RelativeLayout.LayoutParams rlParams = (RelativeLayout.LayoutParams)mChart.getLayoutParams(); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index 9027e5baa9..b77bbd1d18 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) { // add a selection listener mChart.setOnChartValueSelectedListener(this); - setData(3, 100); + setData(4, 100); mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); // mChart.spin(2000, 0, 360); @@ -179,7 +179,7 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 1)); + tvX.setText("" + (mSeekBarX.getProgress())); tvY.setText("" + (mSeekBarY.getProgress())); setData(mSeekBarX.getProgress(), mSeekBarY.getProgress()); @@ -189,16 +189,15 @@ private void setData(int count, float range) { float mult = range; - ArrayList values = new ArrayList(); + ArrayList entries = new ArrayList(); - // IMPORTANT: In a PieChart, no values (Entry) should have the same - // xIndex (even if from different DataSets), since no values can be - // drawn above each other. - for (int i = 0; i < count + 1; i++) { - values.add(new PieEntry((float) ((Math.random() * mult) + mult / 5), mParties[i % mParties.length])); + // 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])); } - PieDataSet dataSet = new PieDataSet(values, "Election Results"); + PieDataSet dataSet = new PieDataSet(entries, "Election Results"); dataSet.setSliceSpace(3f); dataSet.setSelectionShift(5f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java index cae2d2de89..6db7f80e29 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -96,7 +96,7 @@ protected void onCreate(Bundle savedInstanceState) { // add a selection listener mChart.setOnChartValueSelectedListener(this); - setData(3, 100); + setData(4, 100); mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); // mChart.spin(2000, 0, 360); @@ -173,7 +173,7 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 1)); + tvX.setText("" + (mSeekBarX.getProgress())); tvY.setText("" + (mSeekBarY.getProgress())); setData(mSeekBarX.getProgress(), mSeekBarY.getProgress()); @@ -183,16 +183,15 @@ private void setData(int count, float range) { float mult = range; - ArrayList yVals1 = new ArrayList(); + ArrayList entries = new ArrayList(); - // IMPORTANT: In a PieChart, no values (Entry) should have the same - // xIndex (even if from different DataSets), since no values can be - // drawn above each other. - for (int i = 0; i < count + 1; i++) { - yVals1.add(new PieEntry((float) (Math.random() * mult) + mult / 5, mParties[i % mParties.length])); + // 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])); } - PieDataSet dataSet = new PieDataSet(yVals1, "Election Results"); + PieDataSet dataSet = new PieDataSet(entries, "Election Results"); dataSet.setSliceSpace(3f); dataSet.setSelectionShift(5f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java index 3a97d4eed8..97b871a0da 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java @@ -77,6 +77,7 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setValueFormatter(new AxisValueFormatter() { private String[] mActivities = new String[]{"Burger", "Steak", "Salad", "Pasta", "Pizza"}; + @Override public String getFormattedValue(float value, AxisBase axis) { return mActivities[(int) value % mActivities.length]; @@ -210,23 +211,20 @@ public void setData() { float min = 20; int cnt = 5; - ArrayList yVals1 = new ArrayList(); - ArrayList yVals2 = new ArrayList(); + ArrayList entries1 = new ArrayList(); + ArrayList entries2 = new ArrayList(); - // IMPORTANT: In a PieChart, no values (Entry) should have the same - // xIndex (even if from different DataSets), since no values can be - // drawn above each other. + // 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 val = (float) (Math.random() * mult) + min; - yVals1.add(new RadarEntry(val)); - } + float val1 = (float) (Math.random() * mult) + min; + entries1.add(new RadarEntry(val1)); - for (int i = 0; i < cnt; i++) { - float val = (float) (Math.random() * mult) + min; - yVals2.add(new RadarEntry(val)); + float val2 = (float) (Math.random() * mult) + min; + entries2.add(new RadarEntry(val2)); } - RadarDataSet set1 = new RadarDataSet(yVals1, "Last Week"); + RadarDataSet set1 = new RadarDataSet(entries1, "Last Week"); set1.setColor(Color.rgb(103, 110, 129)); set1.setFillColor(Color.rgb(103, 110, 129)); set1.setDrawFilled(true); @@ -235,7 +233,7 @@ public void setData() { set1.setDrawHighlightCircleEnabled(true); set1.setDrawHighlightIndicators(false); - RadarDataSet set2 = new RadarDataSet(yVals2, "This Week"); + RadarDataSet set2 = new RadarDataSet(entries2, "This Week"); set2.setColor(Color.rgb(121, 162, 175)); set2.setFillColor(Color.rgb(121, 162, 175)); set2.setDrawFilled(true); From eb76d21fe053e370435b57c49e5793d68f18ba7d Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 26 Jun 2016 11:54:43 +0200 Subject: [PATCH 259/606] Cleanup --- .../HorizontalBarChartActivity.java | 3 -- .../charting/charts/BarLineChartBase.java | 54 +++++++++---------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 411a0de200..64b9be2ea5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -110,9 +110,6 @@ protected void onCreate(Bundle savedInstanceState) { l.setPosition(LegendPosition.BELOW_CHART_LEFT); l.setFormSize(8f); l.setXEntrySpace(4f); - -// mChart.setVisibleXRange(50, 10); -// mChart.moveViewToX(20); } @Override 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 1004f7b659..4f9c565d18 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 @@ -801,23 +801,6 @@ public void moveViewToX(float xValue) { addViewportJob(job); } - /** - * Centers the viewport to the specified y value on the y-axis. - * This also refreshes the chart by calling invalidate(). - * - * @param yValue - * @param axis - which axis should be used as a reference for the y-axis - */ - public void moveViewToY(float yValue, AxisDependency axis) { - - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - - Runnable job = new MoveViewJob(mViewPortHandler, 0f, yValue + valsInView / 2f, - getTransformer(axis), this); - - addViewportJob(job); - } - /** * This will move the left side of the current viewport to the specified * x-value on the x-axis, and center the viewport to the specified y value on the y-axis. @@ -829,9 +812,9 @@ public void moveViewToY(float yValue, AxisDependency axis) { */ public void moveViewTo(float xValue, float yValue, AxisDependency axis) { - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); + float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - Runnable job = new MoveViewJob(mViewPortHandler, xValue, yValue + valsInView / 2f, + Runnable job = new MoveViewJob(mViewPortHandler, xValue, yValue + yInView / 2f, getTransformer(axis), this); addViewportJob(job); @@ -854,9 +837,9 @@ public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, PointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); + float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - Runnable job = new AnimatedMoveViewJob(mViewPortHandler, xValue, yValue + valsInView / 2f, + Runnable job = new AnimatedMoveViewJob(mViewPortHandler, xValue, yValue + yInView / 2f, getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); addViewportJob(job); @@ -865,6 +848,23 @@ public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, } } + /** + * Centers the viewport to the specified y value on the y-axis. + * This also refreshes the chart by calling invalidate(). + * + * @param yValue + * @param axis - which axis should be used as a reference for the y-axis + */ + public void centerViewToY(float yValue, AxisDependency axis) { + + float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); + + Runnable job = new MoveViewJob(mViewPortHandler, 0f, yValue + valsInView / 2f, + getTransformer(axis), this); + + addViewportJob(job); + } + /** * This will move the center of the current viewport to the specified * x and y value. @@ -876,11 +876,11 @@ public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, */ public void centerViewTo(float xValue, float yValue, AxisDependency axis) { - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - float xsInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); + float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); + float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); Runnable job = new MoveViewJob(mViewPortHandler, - xValue - xsInView / 2f, yValue + valsInView / 2f, + xValue - xInView / 2f, yValue + yInView / 2f, getTransformer(axis), this); addViewportJob(job); @@ -902,11 +902,11 @@ public void centerViewToAnimated(float xValue, float yValue, AxisDependency axis PointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - float xsInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); + float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); + float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); Runnable job = new AnimatedMoveViewJob(mViewPortHandler, - xValue - xsInView / 2f, yValue + valsInView / 2f, + xValue - xInView / 2f, yValue + yInView / 2f, getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); addViewportJob(job); From f01b90668be552ec5139264cda72c0924a5451e3 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 26 Jun 2016 21:05:41 +0200 Subject: [PATCH 260/606] Work on horizontalbarchart --- .../HorizontalBarChartActivity.java | 2 ++ .../mikephil/charting/charts/Chart.java | 4 +-- .../charting/charts/HorizontalBarChart.java | 5 +++- .../utils/HorizontalViewPortHandler.java | 29 +++++++++++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/utils/HorizontalViewPortHandler.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 64b9be2ea5..6b891178bc 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -110,6 +110,8 @@ protected void onCreate(Bundle savedInstanceState) { l.setPosition(LegendPosition.BELOW_CHART_LEFT); l.setFormSize(8f); l.setXEntrySpace(4f); + + mChart.setVisibleXRange(10, 50); } @Override 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 69b2478449..ce5d42ea65 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 @@ -168,7 +168,7 @@ public abstract class Chart Date: Mon, 27 Jun 2016 12:29:49 +0200 Subject: [PATCH 261/606] Changes to custom scatter rendering --- .../HorizontalBarChartActivity.java | 2 - .../mpchartexample/ScatterChartActivity.java | 3 +- .../custom/CustomScatterShapeRenderer.java | 2 - .../charting/charts/CombinedChart.java | 15 ---- .../charting/charts/ScatterChart.java | 74 +------------------ .../charting/data/ScatterDataSet.java | 35 +++++---- .../implementation/RealmScatterDataSet.java | 36 ++++----- .../dataprovider/ScatterDataProvider.java | 17 ----- .../interfaces/datasets/IScatterDataSet.java | 13 ++-- .../OnChartValueSelectedListener.java | 2 +- .../renderer/ScatterChartRenderer.java | 9 +-- .../scatter/ChevronDownShapeRenderer.java | 18 ++--- .../scatter/ChevronUpShapeRenderer.java | 18 ++--- .../renderer/scatter/SquareShapeRenderer.java | 29 ++++---- .../scatter/TriangleShapeRenderer.java | 22 +++--- .../charting/utils/ShapeRendererHandler.java | 57 ++++++++++++++ 16 files changed, 151 insertions(+), 201 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/utils/ShapeRendererHandler.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 6b891178bc..64b9be2ea5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -110,8 +110,6 @@ protected void onCreate(Bundle savedInstanceState) { l.setPosition(LegendPosition.BELOW_CHART_LEFT); l.setFormSize(8f); l.setXEntrySpace(4f); - - mChart.setVisibleXRange(10, 50); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index 40547c4cd5..b288b162e9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -54,7 +54,6 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (ScatterChart) findViewById(R.id.chart1); mChart.setDescription(""); - mChart.addShapeRenderer(new CustomScatterShapeRenderer(), CustomScatterShapeRenderer.IDENTIFIER); mChart.setOnChartValueSelectedListener(this); @@ -188,7 +187,7 @@ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { set2.setScatterShapeHoleRadius(3f); set2.setColor(ColorTemplate.COLORFUL_COLORS[1]); ScatterDataSet set3 = new ScatterDataSet(yVals3, "DS 3"); - set3.setScatterShape(CustomScatterShapeRenderer.IDENTIFIER); + set3.setShapeRenderer(new CustomScatterShapeRenderer()); set3.setColor(ColorTemplate.COLORFUL_COLORS[2]); set1.setScatterShapeSize(8f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java index 15a9ed0b7d..25cd41b1a2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java @@ -14,8 +14,6 @@ */ public class CustomScatterShapeRenderer implements ShapeRenderer { - public static final String IDENTIFIER = "single_line"; - @Override public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint renderPaint, float shapeSize) { 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 d0754132cc..5f578c5d83 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 @@ -13,7 +13,6 @@ import com.github.mikephil.charting.highlight.CombinedHighlighter; import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider; import com.github.mikephil.charting.renderer.CombinedChartRenderer; -import com.github.mikephil.charting.renderer.scatter.ShapeRenderer; /** * This chart class allows the combination of lines, bars, scatter and candle @@ -23,8 +22,6 @@ */ public class CombinedChart extends BarLineChartBase implements CombinedDataProvider { - private ScatterChart.ShapeRendererHandler mShapeRendererHandler; - /** * if set to true, all values are drawn above their bars, instead of below * their top @@ -71,8 +68,6 @@ public CombinedChart(Context context, AttributeSet attrs, int defStyle) { protected void init() { super.init(); - mShapeRendererHandler = new ScatterChart.ShapeRendererHandler(); - setHighlighter(new CombinedHighlighter(this, this)); // Old default behaviour @@ -129,16 +124,6 @@ public BubbleData getBubbleData() { return mData.getBubbleData(); } - @Override - public void addShapeRenderer(ShapeRenderer shapeRenderer, String shapeIdentifier) { - mShapeRendererHandler.addShapeRenderer(shapeRenderer, shapeIdentifier); - } - - @Override - public ShapeRenderer getShapeRenderer(String shapeIdentifier) { - return mShapeRendererHandler.getShapeRenderer(shapeIdentifier); - } - @Override public boolean isDrawBarShadowEnabled() { return mDrawBarShadow; 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 48f4fe3708..cbaf0694b0 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 @@ -7,16 +7,6 @@ import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; import com.github.mikephil.charting.renderer.ScatterChartRenderer; -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.ShapeRenderer; -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; /** * The ScatterChart. Draws dots, triangles, squares and custom shapes into the @@ -27,9 +17,6 @@ */ public class ScatterChart extends BarLineChartBase implements ScatterDataProvider { - protected ShapeRendererHandler mShapeRendererHandler; - - public ScatterChart(Context context) { super(context); } @@ -47,8 +34,6 @@ public ScatterChart(Context context, AttributeSet attrs, int defStyle) { protected void init() { super.init(); - mShapeRendererHandler = new ShapeRendererHandler(); - mRenderer = new ScatterChartRenderer(this, mAnimator, mViewPortHandler); } @@ -57,17 +42,8 @@ public ScatterData getScatterData() { return mData; } - @Override - public void addShapeRenderer(ShapeRenderer shapeRenderer, String shapeIdentifier) { - mShapeRendererHandler.addShapeRenderer(shapeRenderer, shapeIdentifier); - } - - @Override - public ShapeRenderer getShapeRenderer(String shapeIdentifier) { - return mShapeRendererHandler.getShapeRenderer(shapeIdentifier); - } - public enum ScatterShape { + SQUARE("SQUARE"), CIRCLE("CIRCLE"), TRIANGLE("TRIANGLE"), CROSS("CROSS"), X("X"), CHEVRON_UP("CHEVRON_UP"), CHEVRON_DOWN("CHEVRON_DOWN"); @@ -86,52 +62,4 @@ public static ScatterShape[] getAllDefaultShapes() { return new ScatterShape[]{SQUARE, CIRCLE, TRIANGLE, CROSS, X, CHEVRON_UP, CHEVRON_DOWN}; } } - - - /** - * Handler class for all different ShapeRenderers. - */ - protected static class ShapeRendererHandler { - - /** - * Dictionary of ShapeRenderer which are responsible for drawing custom shapes. - * Can add to it your custom shapes. - * CustomShapeRenderer Implements ShapeRenderer{} - */ - protected HashMap shapeRendererList; - - public ShapeRendererHandler() { - initShapeRenderers(); - } - - /** - * Adds a new ShapeRenderer and the shapeIdentifier it is responsible for drawing. - * This shapeIdentifier should correspond to a DataSet with the same identifier. - * - * @param shapeRenderer - * @param shapeIdentifier - */ - public void addShapeRenderer(ShapeRenderer shapeRenderer, String shapeIdentifier) { - shapeRendererList.put(shapeIdentifier, shapeRenderer); - } - - public ShapeRenderer getShapeRenderer(String shapeIdentifier) { - return shapeRendererList.get(shapeIdentifier); - } - - /** - * Init default ShapeRenderers. - */ - protected void initShapeRenderers() { - shapeRendererList = new HashMap<>(); - - shapeRendererList.put(ScatterShape.SQUARE.toString(), new SquareShapeRenderer()); - shapeRendererList.put(ScatterShape.CIRCLE.toString(), new CircleShapeRenderer()); - shapeRendererList.put(ScatterShape.TRIANGLE.toString(), new TriangleShapeRenderer()); - shapeRendererList.put(ScatterShape.CROSS.toString(), new CrossShapeRenderer()); - shapeRendererList.put(ScatterShape.X.toString(), new XShapeRenderer()); - shapeRendererList.put(ScatterShape.CHEVRON_UP.toString(), new ChevronUpShapeRenderer()); - shapeRendererList.put(ScatterShape.CHEVRON_DOWN.toString(), new ChevronDownShapeRenderer()); - } - } } 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 1d07f20cbb..01f2933d3f 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,9 +1,14 @@ 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.SquareShapeRenderer; import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.ShapeRendererHandler; import java.util.ArrayList; import java.util.List; @@ -16,10 +21,9 @@ public class ScatterDataSet extends LineScatterCandleRadarDataSet impleme private float mShapeSize = 15f; /** - * the type of shape that is set to be drawn where the values are at, - * default ScatterShape.SQUARE + * Renderer responsible for rendering this DataSet, default: square */ - private String mScatterShape = ScatterChart.ScatterShape.SQUARE.toString(); + protected ShapeRenderer mShapeRenderer = new SquareShapeRenderer(); /** * The radius of the hole in the shape (applies to Square, Circle and Triangle) @@ -57,7 +61,7 @@ public DataSet copy() { copied.mValueColors = mValueColors; copied.mColors = mColors; copied.mShapeSize = mShapeSize; - copied.mScatterShape = mScatterShape; + copied.mShapeRenderer = mShapeRenderer; copied.mScatterShapeHoleRadius = mScatterShapeHoleRadius; copied.mScatterShapeHoleColor = mScatterShapeHoleColor; copied.mHighlightLineWidth = mHighlightLineWidth; @@ -82,30 +86,31 @@ public float getScatterShapeSize() { return mShapeSize; } - /** - * Sets the shapeIdentifier that this DataSet should be drawn with. - * Make sure the ScatterChart has a renderer capable of rendering the provided identifier. + * Sets the ScatterShape this DataSet should be drawn with. This will search for an available ShapeRenderer and set this + * renderer for the DataSet. * * @param shape */ public void setScatterShape(ScatterChart.ScatterShape shape) { - mScatterShape = shape.toString(); + + ShapeRendererHandler handler = new ShapeRendererHandler(); + mShapeRenderer = handler.getShapeRenderer(shape); } /** - * Sets the shapeIdentifier that this DataSet should be drawn with. - * Make sure the ScatterChart has a renderer capable of rendering the provided identifier. + * Sets a new ShapeRenderer responsible for drawing this DataSet. + * This can also be used to set a custom ShapeRenderer aside from the default ones. * - * @param shapeIdentifier + * @param shapeRenderer */ - public void setScatterShape(String shapeIdentifier) { - mScatterShape = shapeIdentifier; + public void setShapeRenderer(ShapeRenderer shapeRenderer) { + mShapeRenderer = shapeRenderer; } @Override - public String getScatterShape() { - return mScatterShape; + public ShapeRenderer getShapeRenderer() { + return mShapeRenderer; } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java index 9b1795b5f9..2c05c8d246 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java @@ -4,7 +4,10 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.realm.base.RealmLineScatterCandleRadarDataSet; import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.renderer.scatter.ShapeRenderer; +import com.github.mikephil.charting.renderer.scatter.SquareShapeRenderer; import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.ShapeRendererHandler; import io.realm.RealmObject; import io.realm.RealmResults; @@ -15,15 +18,14 @@ public class RealmScatterDataSet extends RealmLineScatterCandleRadarDataSet implements IScatterDataSet { /** - * the size the scattershape will have, in density pixels + * Renderer responsible for rendering this DataSet, default: square */ - private float mShapeSize = 10f; + protected ShapeRenderer mShapeRenderer = new SquareShapeRenderer(); /** - * the type of shape that is set to be drawn where the values are at, - * default ScatterShape.SQUARE + * the size the scattershape will have, in density pixels */ - private String mScatterShape = ScatterChart.ScatterShape.SQUARE.toString(); + private float mShapeSize = 10f; /** * The radius of the hole in the shape (applies to Square, Circle and Triangle) @@ -80,31 +82,31 @@ public float getScatterShapeSize() { return mShapeSize; } - /** - * Sets the shape that is drawn on the position where the values are at. + * Sets the ScatterShape this DataSet should be drawn with. This will search for an available ShapeRenderer and set this + * renderer for the DataSet. * * @param shape */ public void setScatterShape(ScatterChart.ScatterShape shape) { - mScatterShape = shape.toString(); - } + ShapeRendererHandler handler = new ShapeRendererHandler(); + mShapeRenderer = handler.getShapeRenderer(shape); + } /** - * Sets the shape that is drawn on the position where the values are at. If - * "CUSTOM" is chosen, you need to call setCustomScatterShape(...) and - * provide a path object that is drawn as the custom scattershape. + * Sets a new ShapeRenderer responsible for drawing this DataSet. + * This can also be used to set a custom ShapeRenderer aside from the default ones. * - * @param shape + * @param shapeRenderer */ - public void setScatterShape(String shape) { - mScatterShape = shape; + public void setShapeRenderer(ShapeRenderer shapeRenderer) { + mShapeRenderer = shapeRenderer; } @Override - public String getScatterShape() { - return mScatterShape; + public ShapeRenderer 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 2c43ec0491..e89110cdb9 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 @@ -6,21 +6,4 @@ public interface ScatterDataProvider extends BarLineScatterCandleBubbleDataProvider { ScatterData getScatterData(); - - /** - * Adds a new ShapeRenderer and the shapeIdentifier it is responsible for drawing. - * This shapeIdentifier should correspond to a DataSet with the same identifier. - * - * @param shapeRenderer - * @param shapeIdentifier - */ - void addShapeRenderer(ShapeRenderer shapeRenderer, String shapeIdentifier); - - /** - * Returns the corresponding ShapeRenderer for the given identifier. - * - * @param shapeIdentifier - * @return - */ - ShapeRenderer getShapeRenderer(String shapeIdentifier); } 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 38742d65fb..ac838a4d2e 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,6 +1,7 @@ package com.github.mikephil.charting.interfaces.datasets; import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.renderer.scatter.ShapeRenderer; /** * Created by philipp on 21/10/15. @@ -15,23 +16,23 @@ public interface IScatterDataSet extends ILineScatterCandleRadarDataSet { float getScatterShapeSize(); /** - * Returns all the different scattershapes the chart uses + * Returns radius of the hole in the shape * * @return */ - String getScatterShape(); + float getScatterShapeHoleRadius(); /** - * Returns radius of the hole in the shape + * Returns the color for the hole in the shape * * @return */ - float getScatterShapeHoleRadius(); + int getScatterShapeHoleColor(); /** - * Returns the color for the hole in the shape + * Returns the ShapeRenderer responsible for rendering this DataSet. * * @return */ - int getScatterShapeHoleColor(); + ShapeRenderer getShapeRenderer(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java index cc73f73802..7f50232b7e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java @@ -16,7 +16,7 @@ public interface OnChartValueSelectedListener { * * @param e The selected Entry * @param h The corresponding highlight object that contains information - * about the highlighted position + * about the highlighted position such as dataSetIndex, ... */ void onValueSelected(Entry e, Highlight h); 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 f46567ac3f..c9ffbaf3cc 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 @@ -24,8 +24,7 @@ public class ScatterChartRenderer extends LineScatterCandleRadarRenderer { protected ScatterBuffer[] mScatterBuffers; - public ScatterChartRenderer(ScatterDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { + public ScatterChartRenderer(ScatterDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { super(animator, viewPortHandler); mChart = chart; } @@ -64,15 +63,13 @@ protected void drawDataSet(Canvas c, IScatterDataSet dataSet) { final float shapeSize = Utils.convertDpToPixel(dataSet.getScatterShapeSize()); - ScatterBuffer buffer = mScatterBuffers[mChart.getScatterData().getIndexOfDataSet( - dataSet)]; + ScatterBuffer buffer = mScatterBuffers[mChart.getScatterData().getIndexOfDataSet(dataSet)]; buffer.setPhases(phaseX, phaseY); buffer.feed(dataSet); trans.pointValuesToPixel(buffer.buffer); - String shape = dataSet.getScatterShape(); - ShapeRenderer renderer = mChart.getShapeRenderer(shape); + ShapeRenderer renderer = dataSet.getShapeRenderer(); if (renderer != null) { renderer.renderShape(c, dataSet, mViewPortHandler, buffer, mRenderPaint, shapeSize); 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 75fcd0613f..8d4bfb6c85 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 @@ -18,37 +18,37 @@ public class ChevronDownShapeRenderer implements ShapeRenderer { @Override public void renderShape( Canvas c, IScatterDataSet dataSet, - ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final float shapeSize) { + ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint renderPaint, final float shapeSize) { final float shapeHalf = shapeSize / 2f; - mRenderPaint.setStyle(Paint.Style.STROKE); - mRenderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); for (int i = 0; i < buffer.size(); i += 2) { - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) break; - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) continue; - mRenderPaint.setColor(dataSet.getColor(i / 2)); + 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], - mRenderPaint); + renderPaint); c.drawLine( buffer.buffer[i], buffer.buffer[i + 1] + (2 * shapeHalf), buffer.buffer[i] - (2 * shapeHalf), buffer.buffer[i + 1], - mRenderPaint); + 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 3fc52fc210..459e307cf9 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 @@ -17,37 +17,37 @@ public class ChevronUpShapeRenderer implements ShapeRenderer { @Override public void renderShape(Canvas c, IScatterDataSet dataSet, - ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final float shapeSize) { + ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint renderPaint, final float shapeSize) { final float shapeHalf = shapeSize / 2f; - mRenderPaint.setStyle(Paint.Style.STROKE); - mRenderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); for (int i = 0; i < buffer.size(); i += 2) { - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) break; - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) continue; - mRenderPaint.setColor(dataSet.getColor(i / 2)); + 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], - mRenderPaint); + renderPaint); c.drawLine( buffer.buffer[i], buffer.buffer[i + 1] - (2 * shapeHalf), buffer.buffer[i] - (2 * shapeHalf), buffer.buffer[i + 1], - mRenderPaint); + 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 a728f2bdda..184e77a449 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 @@ -17,9 +17,8 @@ public class SquareShapeRenderer implements ShapeRenderer { @Override - public void renderShape( - Canvas c, IScatterDataSet dataSet, - ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final float shapeSize) { + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint + renderPaint, final float shapeSize) { final float shapeHalf = shapeSize / 2f; final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); @@ -31,44 +30,44 @@ public void renderShape( for (int i = 0; i < buffer.size(); i += 2) { - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) break; - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) continue; - mRenderPaint.setColor(dataSet.getColor(i / 2)); + renderPaint.setColor(dataSet.getColor(i / 2)); if (shapeSize > 0.0) { - mRenderPaint.setStyle(Paint.Style.STROKE); - mRenderPaint.setStrokeWidth(shapeStrokeSize); + 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, - mRenderPaint); + renderPaint); if (shapeHoleColor != ColorTemplate.COLOR_NONE) { - mRenderPaint.setStyle(Paint.Style.FILL); + renderPaint.setStyle(Paint.Style.FILL); - mRenderPaint.setColor(shapeHoleColor); + renderPaint.setColor(shapeHoleColor); c.drawRect(buffer.buffer[i] - shapeHoleSizeHalf, buffer.buffer[i + 1] - shapeHoleSizeHalf, buffer.buffer[i] + shapeHoleSizeHalf, buffer.buffer[i + 1] + shapeHoleSizeHalf, - mRenderPaint); + renderPaint); } } else { - mRenderPaint.setStyle(Paint.Style.FILL); + 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, - mRenderPaint); + 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 e03b441483..0b5a996bad 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 @@ -18,10 +18,8 @@ public class TriangleShapeRenderer implements ShapeRenderer { @Override - public void renderShape( - Canvas c, IScatterDataSet dataSet, - ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final float shapeSize) { - + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint + renderPaint, final float shapeSize) { final float shapeHalf = shapeSize / 2f; final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); @@ -30,21 +28,21 @@ public void renderShape( final int shapeHoleColor = dataSet.getScatterShapeHoleColor(); - mRenderPaint.setStyle(Paint.Style.FILL); + renderPaint.setStyle(Paint.Style.FILL); // create a triangle path Path tri = new Path(); for (int i = 0; i < buffer.size(); i += 2) { - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) break; - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) continue; - mRenderPaint.setColor(dataSet.getColor(i / 2)); + renderPaint.setColor(dataSet.getColor(i / 2)); tri.moveTo(buffer.buffer[i], buffer.buffer[i + 1] - shapeHalf); tri.lineTo(buffer.buffer[i] + shapeHalf, buffer.buffer[i + 1] + shapeHalf); @@ -65,13 +63,13 @@ public void renderShape( tri.close(); - c.drawPath(tri, mRenderPaint); + c.drawPath(tri, renderPaint); tri.reset(); if (shapeSize > 0.0 && shapeHoleColor != ColorTemplate.COLOR_NONE) { - mRenderPaint.setColor(shapeHoleColor); + renderPaint.setColor(shapeHoleColor); tri.moveTo(buffer.buffer[i], buffer.buffer[i + 1] - shapeHalf + shapeStrokeSize); @@ -81,7 +79,7 @@ public void renderShape( buffer.buffer[i + 1] + shapeHalf - shapeStrokeSize); tri.close(); - c.drawPath(tri, mRenderPaint); + c.drawPath(tri, renderPaint); tri.reset(); } } 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 new file mode 100644 index 0000000000..a482c44f7e --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ShapeRendererHandler.java @@ -0,0 +1,57 @@ +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.ShapeRenderer; +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. + */ +public final class ShapeRendererHandler { + + /** + * Dictionary of ShapeRenderer which are responsible for drawing custom shapes. + * Can add to it your custom shapes. + * CustomShapeRenderer Implements ShapeRenderer{} + */ + protected HashMap shapeRendererList; + + /** + * Constructor + */ + public ShapeRendererHandler() { + initShapeRenderers(); + } + /** + * Returns the corresponding ShapeRenderer for a given ScatterShape. + * + * @param shape + * @return + */ + public ShapeRenderer 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 1b4e9be07f459c340029e092a68013315030b490 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 27 Jun 2016 12:34:23 +0200 Subject: [PATCH 262/606] Code cleanup and documentation --- .../scatter/ChevronDownShapeRenderer.java | 5 ++-- .../scatter/ChevronUpShapeRenderer.java | 4 +-- .../renderer/scatter/CircleShapeRenderer.java | 30 +++++++++---------- .../renderer/scatter/CrossShapeRenderer.java | 21 +++++++------ .../renderer/scatter/ShapeRenderer.java | 10 +++---- .../renderer/scatter/XShapeRenderer.java | 21 +++++++------ .../charting/utils/ShapeRendererHandler.java | 2 ++ 7 files changed, 45 insertions(+), 48 deletions(-) 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 8d4bfb6c85..d293b28481 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 @@ -16,9 +16,8 @@ public class ChevronDownShapeRenderer implements ShapeRenderer { @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, ScatterBuffer buffer, Paint + renderPaint, final float shapeSize) { final float shapeHalf = shapeSize / 2f; 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 459e307cf9..4eb9db7942 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 @@ -16,8 +16,8 @@ public class ChevronUpShapeRenderer implements ShapeRenderer { @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, ScatterBuffer buffer, Paint + renderPaint, final float shapeSize) { final float shapeHalf = shapeSize / 2f; 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 4173a97c07..449bd9fe96 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 @@ -15,11 +15,9 @@ */ public class CircleShapeRenderer implements ShapeRenderer { - @Override - public void renderShape( - Canvas c, IScatterDataSet dataSet, - ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final float shapeSize) { + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint + renderPaint, final float shapeSize) { final float shapeHalf = shapeSize / 2f; final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); @@ -31,43 +29,43 @@ public void renderShape( for (int i = 0; i < buffer.size(); i += 2) { - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) break; - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) continue; - mRenderPaint.setColor(dataSet.getColor(i / 2)); + renderPaint.setColor(dataSet.getColor(i / 2)); if (shapeSize > 0.0) { - mRenderPaint.setStyle(Paint.Style.STROKE); - mRenderPaint.setStrokeWidth(shapeStrokeSize); + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(shapeStrokeSize); c.drawCircle( buffer.buffer[i], buffer.buffer[i + 1], shapeHoleSizeHalf + shapeStrokeSizeHalf, - mRenderPaint); + renderPaint); if (shapeHoleColor != ColorTemplate.COLOR_NONE) { - mRenderPaint.setStyle(Paint.Style.FILL); + renderPaint.setStyle(Paint.Style.FILL); - mRenderPaint.setColor(shapeHoleColor); + renderPaint.setColor(shapeHoleColor); c.drawCircle( buffer.buffer[i], buffer.buffer[i + 1], shapeHoleSizeHalf, - mRenderPaint); + renderPaint); } } else { - mRenderPaint.setStyle(Paint.Style.FILL); + renderPaint.setStyle(Paint.Style.FILL); c.drawCircle( buffer.buffer[i], buffer.buffer[i + 1], shapeHalf, - mRenderPaint); + 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 a50e6b55c8..45db656677 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 @@ -16,39 +16,38 @@ public class CrossShapeRenderer implements ShapeRenderer { @Override - public void renderShape( - Canvas c, IScatterDataSet dataSet, - ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final float shapeSize) { + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint + renderPaint, final float shapeSize) { final float shapeHalf = shapeSize / 2f; - mRenderPaint.setStyle(Paint.Style.STROKE); - mRenderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); for (int i = 0; i < buffer.size(); i += 2) { - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) break; - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) continue; - mRenderPaint.setColor(dataSet.getColor(i / 2)); + renderPaint.setColor(dataSet.getColor(i / 2)); c.drawLine( buffer.buffer[i] - shapeHalf, buffer.buffer[i + 1], buffer.buffer[i] + shapeHalf, buffer.buffer[i + 1], - mRenderPaint); + renderPaint); c.drawLine( buffer.buffer[i], buffer.buffer[i + 1] - shapeHalf, buffer.buffer[i], buffer.buffer[i + 1] + shapeHalf, - mRenderPaint); + renderPaint); } } 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/ShapeRenderer.java index c3808beb69..3fb23f7718 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/ShapeRenderer.java @@ -17,11 +17,11 @@ public interface ShapeRenderer { /** * Renders the provided ScatterDataSet with a shape. * - * @param c - * @param dataSet - * @param viewPortHandler - * @param buffer - * @param renderPaint + * @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 renderPaint Paint object used for styling and drawing * @param shapeSize */ void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint 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 1b4ba25586..2dbac6a974 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 @@ -16,38 +16,37 @@ public class XShapeRenderer implements ShapeRenderer { @Override - public void renderShape( - Canvas c, IScatterDataSet dataSet, - ViewPortHandler mViewPortHandler, ScatterBuffer buffer, Paint mRenderPaint, final float shapeSize) { + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint + renderPaint, final float shapeSize) { final float shapeHalf = shapeSize / 2f; - mRenderPaint.setStyle(Paint.Style.STROKE); - mRenderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); for (int i = 0; i < buffer.size(); i += 2) { - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) + if (!viewPortHandler.isInBoundsRight(buffer.buffer[i])) break; - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) + if (!viewPortHandler.isInBoundsLeft(buffer.buffer[i]) + || !viewPortHandler.isInBoundsY(buffer.buffer[i + 1])) continue; - mRenderPaint.setColor(dataSet.getColor(i / 2)); + 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, - mRenderPaint); + renderPaint); c.drawLine( buffer.buffer[i] + shapeHalf, buffer.buffer[i + 1] - shapeHalf, buffer.buffer[i] - shapeHalf, buffer.buffer[i + 1] + shapeHalf, - mRenderPaint); + renderPaint); } } 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 a482c44f7e..0cef75f151 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 @@ -14,6 +14,7 @@ /** * Created by Philipp Jahoda on 27/06/16. + * Class allowing to determine the corresponding ShapeRenderer for a given ScatterShape. */ public final class ShapeRendererHandler { @@ -30,6 +31,7 @@ public final class ShapeRendererHandler { public ShapeRendererHandler() { initShapeRenderers(); } + /** * Returns the corresponding ShapeRenderer for a given ScatterShape. * From f54631175ec2621cf00f1f1e81342da3f188912a Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 27 Jun 2016 17:20:50 +0200 Subject: [PATCH 263/606] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c6b0c386ff..f68b32c8c0 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ If you are having questions or problems, you should: - 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. From a233e389783ed2ec308bd4d0af372617981b0e2e Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Mon, 27 Jun 2016 23:47:32 +0200 Subject: [PATCH 264/606] Remove Realm related classes from project --- MPChartExample/AndroidManifest.xml | 2 +- MPChartExample/build.gradle | 7 +- .../realm/RealmBaseActivity.java | 22 +- .../realm/RealmDatabaseActivityBar.java | 2 +- .../realm/RealmDatabaseActivityBubble.java | 2 +- .../realm/RealmDatabaseActivityCandle.java | 2 +- .../RealmDatabaseActivityHorizontalBar.java | 2 +- .../realm/RealmDatabaseActivityLine.java | 2 +- .../realm/RealmDatabaseActivityPie.java | 2 +- .../realm/RealmDatabaseActivityRadar.java | 4 +- .../realm/RealmDatabaseActivityScatter.java | 2 +- .../realm/RealmMainActivity.java | 12 + .../realm/RealmWikiExample.java | 2 +- MPChartLib/build.gradle | 6 +- ...ealmBarLineScatterCandleBubbleDataSet.java | 49 --- .../data/realm/base/RealmBaseDataSet.java | 379 ------------------ .../realm/base/RealmLineRadarDataSet.java | 123 ------ .../RealmLineScatterCandleRadarDataSet.java | 123 ------ .../charting/data/realm/base/RealmUtils.java | 30 -- .../realm/implementation/RealmBarDataSet.java | 253 ------------ .../implementation/RealmBubbleDataSet.java | 129 ------ .../implementation/RealmCandleDataSet.java | 316 --------------- .../implementation/RealmLineDataSet.java | 371 ----------------- .../realm/implementation/RealmPieDataSet.java | 201 ---------- .../implementation/RealmRadarDataSet.java | 143 ------- .../implementation/RealmScatterDataSet.java | 139 ------- build.gradle | 2 +- settings.gradle | 2 + 28 files changed, 42 insertions(+), 2287 deletions(-) delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmUtils.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml index 863e2ba381..ab730cd2bd 100644 --- a/MPChartExample/AndroidManifest.xml +++ b/MPChartExample/AndroidManifest.xml @@ -5,7 +5,7 @@ android:versionName="3.0.0" > diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 13914819a6..0cebfdf441 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -1,10 +1,11 @@ apply plugin: 'com.android.application' +apply plugin: 'realm-android' android { compileSdkVersion 23 buildToolsVersion '23.0.2' defaultConfig { - minSdkVersion 8 + minSdkVersion 16 targetSdkVersion 23 versionCode 52 versionName '3.0.0' @@ -54,8 +55,8 @@ repositories { dependencies { //compile fileTree(dir: 'libs', include: ['*.jar']) - compile project(':MPChartLib') // remove this if you only imported the example project + compile project(':MPChartLib-Realm') compile 'com.android.support:appcompat-v7:23.1.1' - compile 'io.realm:realm-android:0.87.5' // dependency for realm-database API (http://realm.io) + //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/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java index 05aa742864..d4fef69576 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java @@ -83,15 +83,11 @@ protected void styleData(ChartData data) { protected void onResume() { super.onResume(); - RealmConfiguration config = new RealmConfiguration.Builder(this) - .name("myrealm.realm") - .build(); + // 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.deleteRealm(config); - - Realm.setDefaultConfiguration(config); - - mRealm = Realm.getInstance(config); + mRealm = Realm.getDefaultInstance(); } @Override @@ -104,7 +100,7 @@ protected void writeToDB(int objectCount) { mRealm.beginTransaction(); - mRealm.clear(RealmDemoData.class); + mRealm.delete(RealmDemoData.class); for (int i = 0; i < objectCount; i++) { @@ -121,7 +117,7 @@ protected void writeToDBStack(int objectCount) { mRealm.beginTransaction(); - mRealm.clear(RealmDemoData.class); + mRealm.delete(RealmDemoData.class); for (int i = 0; i < objectCount; i++) { @@ -140,7 +136,7 @@ protected void writeToDBCandle(int objectCount) { mRealm.beginTransaction(); - mRealm.clear(RealmDemoData.class); + mRealm.delete(RealmDemoData.class); for (int i = 0; i < objectCount; i++) { @@ -168,7 +164,7 @@ protected void writeToDBBubble(int objectCount) { mRealm.beginTransaction(); - mRealm.clear(RealmDemoData.class); + mRealm.delete(RealmDemoData.class); for (int i = 0; i < objectCount; i++) { @@ -186,7 +182,7 @@ protected void writeToDBPie() { mRealm.beginTransaction(); - mRealm.clear(RealmDemoData.class); + mRealm.delete(RealmDemoData.class); float value1 = 15f + (float) (Math.random() * 8f); float value2 = 15f + (float) (Math.random() * 8f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java index 54e4c55bfd..c87290050d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java @@ -47,7 +47,7 @@ protected void onResume() { private void setData() { - RealmResults result = mRealm.allObjects(RealmDemoData.class); + 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 diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java index c2c2003d2e..d0aa25b864 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java @@ -51,7 +51,7 @@ protected void onResume() { private void setData() { - RealmResults result = mRealm.allObjects(RealmDemoData.class); + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); RealmBubbleDataSet set = new RealmBubbleDataSet(result, "xValue", "yValue", "bubbleSize"); set.setLabel("Realm BubbleDataSet"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java index 1dd544fcce..a388df3741 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java @@ -51,7 +51,7 @@ protected void onResume() { private void setData() { - RealmResults result = mRealm.allObjects(RealmDemoData.class); + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); RealmCandleDataSet set = new RealmCandleDataSet(result, "xValue", "high", "low", "open", "close"); set.setLabel("Realm CandleDataSet"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java index 2c6081e40c..32d1234fe2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java @@ -51,7 +51,7 @@ protected void onResume() { private void setData() { - RealmResults result = mRealm.allObjects(RealmDemoData.class); + 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 diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java index 4e04cf09df..6d2396c22b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java @@ -52,7 +52,7 @@ protected void onResume() { private void setData() { - RealmResults result = mRealm.allObjects(RealmDemoData.class); + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); RealmLineDataSet set = new RealmLineDataSet(result, "xValue", "yValue"); set.setDrawCubic(false); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java index 8bcda98356..7b1eb675d9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java @@ -51,7 +51,7 @@ protected void onResume() { private void setData() { - RealmResults result = mRealm.allObjects(RealmDemoData.class); + 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 diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java index fe408bdc00..411f4b6ac9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java @@ -53,10 +53,10 @@ protected void onResume() { private void setData() { - RealmResults result = mRealm.allObjects(RealmDemoData.class); + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmRadarDataSet set = new RealmRadarDataSet(result, "xValue", "yValue"); // stacked entries + RealmRadarDataSet set = new RealmRadarDataSet(result, "yValue"); // stacked entries set.setLabel("Realm RadarDataSet"); set.setDrawFilled(true); set.setColor(ColorTemplate.rgb("#009688")); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java index d89545a949..14175ac73a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java @@ -51,7 +51,7 @@ protected void onResume() { private void setData() { - RealmResults result = mRealm.allObjects(RealmDemoData.class); + RealmResults result = mRealm.where(RealmDemoData.class).findAll(); RealmScatterDataSet set = new RealmScatterDataSet(result, "xValue", "yValue"); set.setLabel("Realm ScatterDataSet"); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java index fc1ca357e2..3198320272 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java @@ -17,6 +17,9 @@ import java.util.ArrayList; +import io.realm.Realm; +import io.realm.RealmConfiguration; + /** * Created by Philipp Jahoda on 07/12/15. */ @@ -52,6 +55,15 @@ protected void onCreate(Bundle savedInstanceState) { 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 diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java index c31e6b238c..7682bca957 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java @@ -81,7 +81,7 @@ protected void onResume() { private void setData() { // LINE-CHART - final RealmResults results = mRealm.allObjects(Score.class); + final RealmResults results = mRealm.where(Score.class).findAll(); AxisValueFormatter formatter = new AxisValueFormatter() { diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index a5b0ca19e3..f8f8455805 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -1,6 +1,6 @@ apply plugin: 'com.android.library' apply plugin: 'maven' -apply plugin: 'com.github.dcendents.android-maven' +//apply plugin: 'com.github.dcendents.android-maven' //apply plugin: 'realm-android' android { @@ -8,7 +8,7 @@ android { buildToolsVersion '23.0.2' // resourcePrefix 'mpcht' defaultConfig { - minSdkVersion 8 + minSdkVersion 9 targetSdkVersion 23 versionCode 3 versionName '3.0.0' @@ -36,7 +36,7 @@ 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 + //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" } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java deleted file mode 100644 index 55e33227c9..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.github.mikephil.charting.data.realm.base; - -import android.graphics.Color; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 08/11/15. - */ -public abstract class RealmBarLineScatterCandleBubbleDataSet extends RealmBaseDataSet implements IBarLineScatterCandleBubbleDataSet { - - /** default highlight color */ - protected int mHighLightColor = Color.rgb(255, 187, 115); - - public RealmBarLineScatterCandleBubbleDataSet(RealmResults results, String yValuesField) { - super(results, yValuesField); - } - - /** - * Constructor that takes the realm RealmResults, sorts & stores them. - * - * @param results - * @param xValuesField - * @param yValuesField - */ - public RealmBarLineScatterCandleBubbleDataSet(RealmResults results, String xValuesField, String yValuesField) { - super(results, xValuesField, yValuesField); - } - - /** - * 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) { - mHighLightColor = color; - } - - @Override - public int getHighLightColor() { - return mHighLightColor; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java deleted file mode 100644 index 43927dd934..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ /dev/null @@ -1,379 +0,0 @@ -package com.github.mikephil.charting.data.realm.base; - -import com.github.mikephil.charting.data.BaseDataSet; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; - -import java.util.ArrayList; -import java.util.List; - -import io.realm.DynamicRealmObject; -import io.realm.RealmObject; -import io.realm.RealmResults; -import io.realm.Sort; - -/** - * Created by Philipp Jahoda on 06/11/15. - */ -public abstract class RealmBaseDataSet extends BaseDataSet { - - /** - * a list of queried realm objects - */ - protected RealmResults results; - - /** - * a cached list of all data read from the database - */ - protected List mValues; - - /** - * maximum y-value in the value array - */ - protected float mYMax = -Float.MAX_VALUE; - - /** - * minimum y-value in the value array - */ - protected float mYMin = Float.MAX_VALUE; - - /** - * maximum x-value in the value array - */ - protected float mXMax = -Float.MAX_VALUE; - - /** - * minimum x-value in the value array - */ - protected float mXMin = Float.MAX_VALUE; - - /** - * fieldname of the column that contains the y-values of this dataset - */ - protected String mYValuesField; - - /** - * fieldname of the column that contains the x-values of this dataset - */ - protected String mXValuesField; - - public RealmBaseDataSet(RealmResults results, String yValuesField) { - this.results = results; - this.mYValuesField = yValuesField; - this.mValues = new ArrayList(); - - if (mXValuesField != null) - this.results.sort(mXValuesField, Sort.ASCENDING); - } - - /** - * Constructor that takes the realm RealmResults, sorts & stores them. - * - * @param results - * @param xValuesField - * @param yValuesField - */ - public RealmBaseDataSet(RealmResults results, String xValuesField, String yValuesField) { - this.results = results; - this.mYValuesField = yValuesField; - this.mXValuesField = xValuesField; - this.mValues = new ArrayList(); - - if (mXValuesField != null) - this.results.sort(mXValuesField, Sort.ASCENDING); - } - - /** - * Rebuilds the DataSet based on the given RealmResults. - */ - public void build(RealmResults results) { - - int xIndex = 0; - for (T object : results) { - mValues.add(buildEntryFromResultObject(object, xIndex++)); - } - } - - public S buildEntryFromResultObject(T realmObject, float x) { - DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - - return (S) new Entry(mXValuesField == null ? x : dynamicObject.getFloat(mXValuesField), dynamicObject.getFloat(mYValuesField)); - } - - @Override - public float getYMin() { - //return results.min(mYValuesField).floatValue(); - return mYMin; - } - - @Override - public float getYMax() { - //return results.max(mYValuesField).floatValue(); - return mYMax; - } - - @Override - public float getXMin() { - return mXMin; - } - - @Override - public float getXMax() { - return mXMax; - } - - @Override - public int getEntryCount() { - return mValues.size(); - } - - @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; - - for (S e : mValues) { - calcMinMax(e); - } - } - - /** - * Updates the min and max x and y value of this DataSet based on the given Entry. - * - * @param e - */ - protected void calcMinMax(S e) { - - if (e.getY() < mYMin) - mYMin = e.getY(); - - if (e.getY() > mYMax) - mYMax = e.getY(); - - if (e.getX() < mXMin) - mXMin = e.getX(); - - if (e.getX() > mXMax) - mXMax = e.getX(); - } - - @Override - public S getEntryForXPos(float xPos) { - //DynamicRealmObject o = new DynamicRealmObject(results.where().equalTo(mXValuesField, xIndex).findFirst()); - //return new Entry(o.getFloat(mYValuesField), o.getInt(mXValuesField)); - return getEntryForXPos(xPos, DataSet.Rounding.CLOSEST); - } - - @Override - public S getEntryForXPos(float xPos, DataSet.Rounding rounding) { - int index = getEntryIndex(xPos, rounding); - if (index > -1) - return mValues.get(index); - return null; - } - - @Override - public List getEntriesForXPos(float xVal) { - - List entries = new ArrayList<>(); - -// { -// T object = results.get(xVal); -// if (object != null) -// entries.add(buildEntryFromResultObject(object, xVal)); -// } else - - if (mXValuesField != null) { - RealmResults foundObjects = results.where().equalTo(mXValuesField, xVal).findAll(); - - for (T e : foundObjects) - entries.add(buildEntryFromResultObject(e, xVal)); - } - - return entries; - } - - @Override - public S getEntryForIndex(int index) { - //DynamicRealmObject o = new DynamicRealmObject(results.get(index)); - //return new Entry(o.getFloat(mYValuesField), o.getInt(mXValuesField)); - return mValues.get(index); - } - - @Override - public int getEntryIndex(float xPos, DataSet.Rounding rounding) { - - int low = 0; - int high = mValues.size() - 1; - int closest = -1; - - while (low <= high) { - int m = (high + low) / 2; - - if (xPos == mValues.get(m).getX()) { - while (m > 0 && mValues.get(m - 1).getX() == xPos) - m--; - - return m; - } - - if (xPos > mValues.get(m).getX()) - low = m + 1; - else - high = m - 1; - - closest = m; - } - - if (closest != -1) { - float closestXPos = mValues.get(closest).getX(); - if (rounding == DataSet.Rounding.UP) { - if (closestXPos < xPos && closest < mValues.size() - 1) { - ++closest; - } - } else if (rounding == DataSet.Rounding.DOWN) { - if (closestXPos > xPos && closest > 0) { - --closest; - } - } - } - - return closest; - } - - @Override - public int getEntryIndex(S e) { - return mValues.indexOf(e); - } - - @Override - public boolean addEntry(S e) { - - if (e == null) - return false; - - float val = e.getY(); - - if (mValues == null) { - mValues = new ArrayList(); - } - - calcMinMax(e); - - // add the entry - mValues.add(e); - return true; - } - - @Override - public boolean removeEntry(S e) { - - if (e == null) - return false; - - if (mValues == null) - return false; - - // remove the entry - boolean removed = mValues.remove(e); - - if (removed) { - calcMinMax(); - } - - return removed; - } - - @Override - public void addEntryOrdered(S e) { - - if (e == null) - return; - - float val = e.getY(); - - if (mValues == null) { - mValues = new ArrayList(); - } - - if (mValues.size() == 0) { - mYMax = val; - mYMin = val; - } else { - if (mYMax < val) - mYMax = val; - if (mYMin > val) - mYMin = val; - } - - if (mValues.size() > 0 && mValues.get(mValues.size() - 1).getX() > e.getX()) { - int closestIndex = getEntryIndex(e.getX(), DataSet.Rounding.UP); - mValues.add(closestIndex, e); - return; - } - - mValues.add(e); - } - - /** - * Returns the List of values that has been extracted from the RealmResults - * using the provided fieldnames. - * - * @return - */ - public List getValues() { - return mValues; - } - - @Override - public void clear() { - mValues.clear(); - notifyDataSetChanged(); - } - - public RealmResults getResults() { - return results; - } - - /** - * Returns the fieldname that represents the "y-values" in the realm-data. - * - * @return - */ - public String getYValuesField() { - return mYValuesField; - } - - /** - * Sets the field name that is used for getting the y-values out of the RealmResultSet. - * - * @param yValuesField - */ - public void setYValuesField(String yValuesField) { - this.mYValuesField = yValuesField; - } - - /** - * Returns the fieldname that represents the "x-values" in the realm-data. - * - * @return - */ - public String getXValuesField() { - return mXValuesField; - } - - /** - * Sets the field name that is used for getting the x-values out of the RealmResultSet. - * - * @param xValuesField - */ - public void setXValuesField(String xValuesField) { - this.mXValuesField = xValuesField; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java deleted file mode 100644 index a7b9cb29d8..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.github.mikephil.charting.data.realm.base; - -import android.graphics.Color; -import android.graphics.drawable.Drawable; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.ILineRadarDataSet; -import com.github.mikephil.charting.utils.Utils; - -import io.realm.DynamicRealmObject; -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 08/11/15. - */ -public abstract class RealmLineRadarDataSet extends RealmLineScatterCandleRadarDataSet implements ILineRadarDataSet { - - /** the color that is used for filling the line surface */ - private int mFillColor = Color.rgb(140, 234, 255); - - /** the drawable to be used for filling the line surface*/ - protected Drawable mFillDrawable; - - /** transparency used for filling line surface */ - private int mFillAlpha = 85; - - /** the width of the drawn data lines */ - private float mLineWidth = 2.5f; - - /** if true, the data will also be drawn filled */ - private boolean mDrawFilled = false; - - - public RealmLineRadarDataSet(RealmResults results, String yValuesField) { - super(results, yValuesField); - } - - /** - * Constructor that takes the realm RealmResults, sorts & stores them. - * - * @param results - * @param yValuesField - * @param xIndexField - */ - public RealmLineRadarDataSet(RealmResults results, String yValuesField, String xIndexField) { - super(results, yValuesField, xIndexField); - } - - @Override - public int getFillColor() { - return mFillColor; - } - - /** - * sets the color that is used for filling the line surface - * - * @param color - */ - public void setFillColor(int color) { - mFillColor = color; - mFillDrawable = null; - } - - @Override - public Drawable getFillDrawable() { - return mFillDrawable; - } - - /** - * Sets the drawable to be used to fill the area below the line. - * - * @param drawable - */ - public void setFillDrawable(Drawable drawable) { - this.mFillDrawable = drawable; - } - - @Override - public int getFillAlpha() { - return mFillAlpha; - } - - /** - * sets the alpha value (transparency) that is used for filling the line - * surface (0-255), default: 85 - * - * @param alpha - */ - public void setFillAlpha(int alpha) { - mFillAlpha = alpha; - } - - /** - * set the line width of the chart (min = 0.2f, max = 10f); default 1f NOTE: - * thinner line == better performance, thicker line == worse performance - * - * @param width - */ - public void setLineWidth(float width) { - - if (width < 0.2f) - width = 0.2f; - if (width > 10.0f) - width = 10.0f; - mLineWidth = Utils.convertDpToPixel(width); - } - - @Override - public float getLineWidth() { - return mLineWidth; - } - - @Override - public void setDrawFilled(boolean filled) { - mDrawFilled = filled; - } - - @Override - public boolean isDrawFilledEnabled() { - return mDrawFilled; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java deleted file mode 100644 index 1f0c4b064b..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.github.mikephil.charting.data.realm.base; - -import android.graphics.DashPathEffect; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.ILineScatterCandleRadarDataSet; -import com.github.mikephil.charting.utils.Utils; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 08/11/15. - */ -public abstract class RealmLineScatterCandleRadarDataSet extends RealmBarLineScatterCandleBubbleDataSet implements ILineScatterCandleRadarDataSet { - - protected boolean mDrawVerticalHighlightIndicator = true; - protected boolean mDrawHorizontalHighlightIndicator = true; - - /** the width of the highlight indicator lines */ - protected float mHighlightLineWidth = 0.5f; - - /** the path effect for dashed highlight-lines */ - protected DashPathEffect mHighlightDashPathEffect = null; - - public RealmLineScatterCandleRadarDataSet(RealmResults results, String yValuesField) { - super(results, yValuesField); - } - - /** - * Constructor that takes the realm RealmResults, sorts & stores them. - * - * @param results - * @param xValueField - * @param yValuesField - */ - public RealmLineScatterCandleRadarDataSet(RealmResults results, String xValueField, String yValuesField) { - super(results, xValueField, yValuesField); - } - - /** - * Enables / disables the horizontal highlight-indicator. If disabled, the indicator is not drawn. - * @param enabled - */ - public void setDrawHorizontalHighlightIndicator(boolean enabled) { - this.mDrawHorizontalHighlightIndicator = enabled; - } - - /** - * Enables / disables the vertical highlight-indicator. If disabled, the indicator is not drawn. - * @param enabled - */ - public void setDrawVerticalHighlightIndicator(boolean enabled) { - this.mDrawVerticalHighlightIndicator = enabled; - } - - /** - * Enables / disables both vertical and horizontal highlight-indicators. - * @param enabled - */ - public void setDrawHighlightIndicators(boolean enabled) { - setDrawVerticalHighlightIndicator(enabled); - setDrawHorizontalHighlightIndicator(enabled); - } - - @Override - public boolean isVerticalHighlightIndicatorEnabled() { - return mDrawVerticalHighlightIndicator; - } - - @Override - public boolean isHorizontalHighlightIndicatorEnabled() { - return mDrawHorizontalHighlightIndicator; - } - - /** - * Sets the width of the highlight line in dp. - * @param width - */ - public void setHighlightLineWidth(float width) { - mHighlightLineWidth = Utils.convertDpToPixel(width); - } - - @Override - public float getHighlightLineWidth() { - return mHighlightLineWidth; - } - - /** - * Enables the highlight-line to be drawn in dashed mode, e.g. like this "- - - - - -" - * - * @param lineLength the length of the line pieces - * @param spaceLength the length of space inbetween the line-pieces - * @param phase offset, in degrees (normally, use 0) - */ - public void enableDashedHighlightLine(float lineLength, float spaceLength, float phase) { - mHighlightDashPathEffect = new DashPathEffect(new float[] { - lineLength, spaceLength - }, phase); - } - - /** - * Disables the highlight-line to be drawn in dashed mode. - */ - public void disableDashedHighlightLine() { - mHighlightDashPathEffect = null; - } - - /** - * Returns true if the dashed-line effect is enabled for highlight lines, false if not. - * Default: disabled - * - * @return - */ - public boolean isDashedHighlightLineEnabled() { - return mHighlightDashPathEffect == null ? false : true; - } - - @Override - public DashPathEffect getDashPathEffectHighlight() { - return mHighlightDashPathEffect; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmUtils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmUtils.java deleted file mode 100644 index 6e7d0558bc..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/base/RealmUtils.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.github.mikephil.charting.data.realm.base; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public final class RealmUtils { - -// /** -// * Transforms the given Realm-ResultSet into a String array by using the provided xValuesField. -// * -// * @param result -// * @param xPositionField -// * @param xLabelField -// * @return -// */ -// public static List toXVals(RealmResults result, String xPositionField, String xLabelField) { -// -// List xVals = new ArrayList(); -// -// for (RealmObject object : result) { -// -// DynamicRealmObject dynamicObject = new DynamicRealmObject(object); -// -// XAxisValue val = new XAxisValue(dynamicObject.getDouble(xPositionField), dynamicObject.getString(xLabelField)); -// xVals.add(val); -// } -// -// return xVals; -// } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java deleted file mode 100644 index 13254d28c7..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java +++ /dev/null @@ -1,253 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import android.graphics.Color; - -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.realm.base.RealmBarLineScatterCandleBubbleDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; - -import io.realm.DynamicRealmObject; -import io.realm.RealmFieldType; -import io.realm.RealmList; -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 07/11/15. - */ -public class RealmBarDataSet extends RealmBarLineScatterCandleBubbleDataSet - implements IBarDataSet { - - private String mStackValueFieldName; - - /** - * the maximum number of bars that are stacked upon each other, this value - * is calculated from the Entries that are added to the DataSet - */ - private int mStackSize = 1; - - /** - * the color used for drawing the bar shadows - */ - private int mBarShadowColor = Color.rgb(215, 215, 215); - - private float mBarBorderWidth = 0.0f; - - private int mBarBorderColor = Color.BLACK; - - /** - * the alpha value used to draw the highlight indicator bar - */ - private int mHighLightAlpha = 120; - - /** - * array of labels used to describe the different values of the stacked bars - */ - private String[] mStackLabels = new String[]{ - "Stack" - }; - - public RealmBarDataSet(RealmResults results, String xValuesField, String yValuesField) { - super(results, xValuesField, yValuesField); - mHighLightColor = Color.rgb(0, 0, 0); - - build(this.results); - calcMinMax(); - } - - /** - * Constructor for supporting stacked values. - * - * @param results - * @param xValuesField - * @param yValuesField - * @param stackValueFieldName - */ - public RealmBarDataSet(RealmResults results, String xValuesField, String yValuesField, String - stackValueFieldName) { - super(results, xValuesField, yValuesField); - this.mStackValueFieldName = stackValueFieldName; - mHighLightColor = Color.rgb(0, 0, 0); - - build(this.results); - calcMinMax(); - } - - @Override - public void build(RealmResults results) { - - super.build(results); - - calcStackSize(); - } - - @Override - public BarEntry buildEntryFromResultObject(T realmObject, float x) { - DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - - if (dynamicObject.getFieldType(mYValuesField) == RealmFieldType.LIST) { - - RealmList list = dynamicObject.getList(mYValuesField); - float[] values = new float[list.size()]; - - int i = 0; - for (DynamicRealmObject o : list) { - values[i] = o.getFloat(mStackValueFieldName); - i++; - } - - return new BarEntry( - mXValuesField == null ? x : dynamicObject.getFloat(mXValuesField), values); - } else { - float value = dynamicObject.getFloat(mYValuesField); - return new BarEntry(mXValuesField == null ? x : dynamicObject.getFloat(mXValuesField), value); - } - } - - @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; - - for (BarEntry e : mValues) { - - if (e != null && !Float.isNaN(e.getY())) { - - if (e.getYVals() == null) { - - 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.getX() < mXMin) - mXMin = e.getX(); - - if (e.getX() > mXMax) - mXMax = e.getX(); - } - } - } - - private void calcStackSize() { - - for (int i = 0; i < mValues.size(); i++) { - - float[] vals = mValues.get(i).getYVals(); - - if (vals != null && vals.length > mStackSize) - mStackSize = vals.length; - } - } - - @Override - public int getStackSize() { - return mStackSize; - } - - @Override - public boolean isStacked() { - return mStackSize > 1 ? true : false; - } - - /** - * Sets the color used for drawing the bar-shadows. The bar shadows is a - * surface behind the bar that indicates the maximum value. Don't for get to - * use getResources().getColor(...) to set this. Or Color.rgb(...). - * - * @param color - */ - public void setBarShadowColor(int color) { - mBarShadowColor = color; - } - - @Override - public int getBarShadowColor() { - return mBarShadowColor; - } - - /** - * Sets the width used for drawing borders around the bars. - * If borderWidth == 0, no border will be drawn. - * - * @return - */ - public void setBarBorderWidth(float width) { - mBarBorderWidth = width; - } - - /** - * Returns the width used for drawing borders around the bars. - * If borderWidth == 0, no border will be drawn. - * - * @return - */ - @Override - public float getBarBorderWidth() { - return mBarBorderWidth; - } - - /** - * Sets the color drawing borders around the bars. - * - * @return - */ - public void setBarBorderColor(int color) { - mBarBorderColor = color; - } - - /** - * Returns the color drawing borders around the bars. - * - * @return - */ - @Override - public int getBarBorderColor() { - return mBarBorderColor; - } - - /** - * Set the alpha value (transparency) that is used for drawing the highlight - * indicator bar. min = 0 (fully transparent), max = 255 (fully opaque) - * - * @param alpha - */ - public void setHighLightAlpha(int alpha) { - mHighLightAlpha = alpha; - } - - @Override - public int getHighLightAlpha() { - return mHighLightAlpha; - } - - /** - * Sets labels for different values of bar-stacks, in case there are one. - * - * @param labels - */ - public void setStackLabels(String[] labels) { - mStackLabels = labels; - } - - @Override - public String[] getStackLabels() { - return mStackLabels; - } - -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java deleted file mode 100644 index 27c4b308e0..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.BubbleEntry; -import com.github.mikephil.charting.data.realm.base.RealmBarLineScatterCandleBubbleDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; -import com.github.mikephil.charting.utils.Utils; - -import io.realm.DynamicRealmObject; -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 07/11/15. - */ -public class RealmBubbleDataSet extends RealmBarLineScatterCandleBubbleDataSet implements IBubbleDataSet { - - private String mSizeField; - - protected float mMaxSize; - protected boolean mNormalizeSize = true; - - private float mHighlightCircleWidth = 2.5f; - - /** - * Constructor for creating a CandleDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - * @param sizeField the name of the field in your data object that represents the bubble size - */ - public RealmBubbleDataSet(RealmResults result, String yValuesField, String sizeField) { - super(result, yValuesField); - this.mSizeField = sizeField; - - build(this.results); - calcMinMax(); - } - - /** - * Constructor for creating a CandleDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - * @param xIndexField the name of the field in your data object that represents the x-index - * @param sizeField the name of the field in your data object that represents the bubble size - */ - public RealmBubbleDataSet(RealmResults result, String yValuesField, String xIndexField, String sizeField) { - super(result, yValuesField, xIndexField); - this.mSizeField = sizeField; - - build(this.results); - calcMinMax(); - } - - @Override - public BubbleEntry buildEntryFromResultObject(T realmObject, float x) { - DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - - return new BubbleEntry( - mXValuesField == null ? x : dynamicObject.getFloat(mXValuesField), - dynamicObject.getFloat(mYValuesField), - dynamicObject.getFloat(mSizeField)); - } - - @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; - - for (BubbleEntry e : mValues) { - - calcMinMax(e); - - final float size = e.getSize(); - - if (size > mMaxSize) { - mMaxSize = size; - } - } - } - - @Override - public float getMaxSize() { - return mMaxSize; - } - - @Override - public boolean isNormalizeSizeEnabled() { - return mNormalizeSize; - } - - public void setNormalizeSizeEnabled(boolean normalizeSize) { - mNormalizeSize = normalizeSize; - } - - @Override - public void setHighlightCircleWidth(float width) { - mHighlightCircleWidth = Utils.convertDpToPixel(width); - } - - @Override - public float getHighlightCircleWidth() { - return mHighlightCircleWidth; - } - - /** - * Sets the database fieldname for the bubble size. - * - * @param sizeField - */ - public void setSizeField(String sizeField) { - this.mSizeField = sizeField; - } - - /** - * Returns the database fieldname that stores bubble size. - * - * @return - */ - public String getSizeField() { - return mSizeField; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java deleted file mode 100644 index 1c08290656..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java +++ /dev/null @@ -1,316 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import android.graphics.Paint; - -import com.github.mikephil.charting.data.CandleEntry; -import com.github.mikephil.charting.data.realm.base.RealmLineScatterCandleRadarDataSet; -import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Utils; - -import io.realm.DynamicRealmObject; -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 07/11/15. - */ -public class RealmCandleDataSet extends RealmLineScatterCandleRadarDataSet - implements ICandleDataSet { - - private String mHighField; - private String mLowField; - private String mOpenField; - private String mCloseField; - - /** - * the width of the shadow of the candle - */ - private float mShadowWidth = 3f; - - /** - * should the candle bars show? - * when false, only "ticks" will show - *

- * - default: true - */ - private boolean mShowCandleBar = true; - - /** - * the space between the candle entries, default 0.1f (10%) - */ - private float mBarSpace = 0.1f; - - /** - * use candle color for the shadow - */ - private boolean mShadowColorSameAsCandle = false; - - /** - * paint style when open < close - * increasing candlesticks are traditionally hollow - */ - protected Paint.Style mIncreasingPaintStyle = Paint.Style.STROKE; - - /** - * paint style when open > close - * descreasing candlesticks are traditionally filled - */ - protected Paint.Style mDecreasingPaintStyle = Paint.Style.FILL; - - /** - * color for open == close - */ - protected int mNeutralColor = ColorTemplate.COLOR_NONE; - - /** - * color for open < close - */ - protected int mIncreasingColor = ColorTemplate.COLOR_NONE; - - /** - * color for open > close - */ - protected int mDecreasingColor = ColorTemplate.COLOR_NONE; - - /** - * shadow line color, set -1 for backward compatibility and uses default - * color - */ - protected int mShadowColor = ColorTemplate.COLOR_NONE; - - /** - * Constructor for creating a LineDataSet with realm data. - * - * @param result the queried results from the realm database - * @param highField the name of the field in your data object that represents the "high" value - * @param lowField the name of the field in your data object that represents the "low" value - * @param openField the name of the field in your data object that represents the "open" value - * @param closeField the name of the field in your data object that represents the "close" value - */ - public RealmCandleDataSet(RealmResults result, String highField, String lowField, String openField, String - closeField) { - super(result, null); - this.mHighField = highField; - this.mLowField = lowField; - this.mOpenField = openField; - this.mCloseField = closeField; - - build(this.results); - calcMinMax(); - } - - /** - * Constructor for creating a LineDataSet with realm data. - * - * @param result the queried results from the realm database - * @param xValueField the name of the field in your data object that represents the "x" value - * @param highField the name of the field in your data object that represents the "high" value - * @param lowField the name of the field in your data object that represents the "low" value - * @param openField the name of the field in your data object that represents the "open" value - * @param closeField the name of the field in your data object that represents the "close" value - */ - public RealmCandleDataSet(RealmResults result, String xValueField, String highField, String lowField, String openField, String - closeField) { - super(result, xValueField, null); - this.mHighField = highField; - this.mLowField = lowField; - this.mOpenField = openField; - this.mCloseField = closeField; - - build(this.results); - calcMinMax(); - } - - @Override - public CandleEntry buildEntryFromResultObject(T realmObject, float x) { - DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - - return new CandleEntry( - mXValuesField == null ? x : dynamicObject.getFloat(mXValuesField), - dynamicObject.getFloat(mHighField), - dynamicObject.getFloat(mLowField), - dynamicObject.getFloat(mOpenField), - dynamicObject.getFloat(mCloseField)); - } - - @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; - - for (CandleEntry e : mValues) { - - if (e.getLow() < mYMin) - mYMin = e.getLow(); - - if (e.getHigh() > mYMax) - mYMax = e.getHigh(); - - if (e.getX() < mXMin) - mXMin = e.getX(); - - if (e.getX() > mXMax) - mXMax = e.getX(); - } - } - - /** - * 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 - * - * @param space - */ - public void setBarSpace(float space) { - - if (space < 0f) - space = 0f; - if (space > 0.45f) - space = 0.45f; - - mBarSpace = space; - } - - @Override - public float getBarSpace() { - return mBarSpace; - } - - /** - * Sets the width of the candle-shadow-line in pixels. Default 3f. - * - * @param width - */ - public void setShadowWidth(float width) { - mShadowWidth = Utils.convertDpToPixel(width); - } - - @Override - public float getShadowWidth() { - return mShadowWidth; - } - - /** - * Sets whether the candle bars should show? - * - * @param showCandleBar - */ - public void setShowCandleBar(boolean showCandleBar) { - mShowCandleBar = showCandleBar; - } - - @Override - public boolean getShowCandleBar() { - return mShowCandleBar; - } - - - /** BELOW THIS COLOR HANDLING */ - - /** - * Sets the one and ONLY color that should be used for this DataSet when - * open == close. - * - * @param color - */ - public void setNeutralColor(int color) { - mNeutralColor = color; - } - - @Override - public int getNeutralColor() { - return mNeutralColor; - } - - /** - * Sets the one and ONLY color that should be used for this DataSet when - * open < close. - * - * @param color - */ - public void setIncreasingColor(int color) { - mIncreasingColor = color; - } - - @Override - public int getIncreasingColor() { - return mIncreasingColor; - } - - /** - * Sets the one and ONLY color that should be used for this DataSet when - * open > close. - * - * @param color - */ - public void setDecreasingColor(int color) { - mDecreasingColor = color; - } - - @Override - public int getDecreasingColor() { - return mDecreasingColor; - } - - @Override - public Paint.Style getIncreasingPaintStyle() { - return mIncreasingPaintStyle; - } - - /** - * Sets paint style when open < close - * - * @param paintStyle - */ - public void setIncreasingPaintStyle(Paint.Style paintStyle) { - this.mIncreasingPaintStyle = paintStyle; - } - - @Override - public Paint.Style getDecreasingPaintStyle() { - return mDecreasingPaintStyle; - } - - /** - * Sets paint style when open > close - * - * @param decreasingPaintStyle - */ - public void setDecreasingPaintStyle(Paint.Style decreasingPaintStyle) { - this.mDecreasingPaintStyle = decreasingPaintStyle; - } - - @Override - public int getShadowColor() { - return mShadowColor; - } - - /** - * Sets shadow color for all entries - * - * @param shadowColor - */ - public void setShadowColor(int shadowColor) { - this.mShadowColor = shadowColor; - } - - @Override - public boolean getShadowColorSameAsCandle() { - return mShadowColorSameAsCandle; - } - - /** - * Sets shadow color to be the same color as the candle color - * - * @param shadowColorSameAsCandle - */ - public void setShadowColorSameAsCandle(boolean shadowColorSameAsCandle) { - this.mShadowColorSameAsCandle = shadowColorSameAsCandle; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java deleted file mode 100644 index 5c4a58d15a..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java +++ /dev/null @@ -1,371 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import android.content.Context; -import android.graphics.Color; -import android.graphics.DashPathEffect; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.data.realm.base.RealmLineRadarDataSet; -import com.github.mikephil.charting.formatter.DefaultFillFormatter; -import com.github.mikephil.charting.formatter.FillFormatter; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmLineDataSet extends RealmLineRadarDataSet implements ILineDataSet { - - /** Drawing mode for this line dataset **/ - private LineDataSet.Mode mMode = LineDataSet.Mode.LINEAR; - - /** - * List representing all colors that are used for the circles - */ - private List mCircleColors = null; - - /** - * the color of the inner circles - */ - private int mCircleColorHole = Color.WHITE; - - /** - * the radius of the circle-shaped value indicators - */ - private float mCircleRadius = 8f; - - /** the hole radius of the circle-shaped value indicators */ - private float mCircleHoleRadius = 4f; - - /** - * sets the intensity of the cubic lines - */ - private float mCubicIntensity = 0.2f; - - /** - * the path effect of this DataSet that makes dashed lines possible - */ - private DashPathEffect mDashPathEffect = null; - - /** - * formatter for customizing the position of the fill-line - */ - private FillFormatter mFillFormatter = new DefaultFillFormatter(); - - /** - * if true, drawing circles is enabled - */ - private boolean mDrawCircles = true; - - private boolean mDrawCircleHole = true; - - /** - * Constructor for creating a LineDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - */ - public RealmLineDataSet(RealmResults result, String yValuesField) { - super(result, yValuesField); - mCircleColors = new ArrayList(); - - // default color - mCircleColors.add(Color.rgb(140, 234, 255)); - - build(this.results); - calcMinMax(); - } - - /** - * Constructor for creating a LineDataSet with realm data. - * - * @param result the queried results from the realm database - * @param xValuesField the name of the field in your data object that represents the x-axis value - * @param yValuesField the name of the field in your data object that represents the y-axis value - */ - public RealmLineDataSet(RealmResults result, String xValuesField, String yValuesField) { - super(result, xValuesField, yValuesField); - mCircleColors = new ArrayList(); - - // default color - mCircleColors.add(Color.rgb(140, 234, 255)); - - build(this.results); - calcMinMax(); - } - - /** - * Returns the drawing mode for this line dataset - * - * @return - */ - @Override - public LineDataSet.Mode getMode() { - return mMode; - } - - /** - * Returns the drawing mode for this line dataset - * - * @return - */ - public void setMode(LineDataSet.Mode mode) { - mMode = mode; - } - - /** - * Sets the intensity for cubic lines (if enabled). Max = 1f = very cubic, - * Min = 0.05f = low cubic effect, Default: 0.2f - * - * @param intensity - */ - public void setCubicIntensity(float intensity) { - - if (intensity > 1f) - intensity = 1f; - if (intensity < 0.05f) - intensity = 0.05f; - - mCubicIntensity = intensity; - } - - @Override - public float getCubicIntensity() { - return mCubicIntensity; - } - - /** - * sets the size (radius) of the circle shpaed value indicators, default - * size = 4f - * - * @param size - */ - public void setCircleSize(float size) { - mCircleRadius = Utils.convertDpToPixel(size); - } - - @Override - public float getCircleRadius() { - return mCircleRadius; - } - - /** - * sets the hole radius of the drawn circles. - * Default radius = 2f - * - * @param holeRadius - */ - public void setCircleHoleRadius(float holeRadius) { - mCircleHoleRadius = Utils.convertDpToPixel(holeRadius); - } - - @Override - public float getCircleHoleRadius() { - return mCircleHoleRadius; - } - - /** - * Enables the 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 enableDashedLine(float lineLength, float spaceLength, float phase) { - mDashPathEffect = new DashPathEffect(new float[]{ - lineLength, spaceLength - }, phase); - } - - /** - * Disables the line to be drawn in dashed mode. - */ - public void disableDashedLine() { - mDashPathEffect = null; - } - - @Override - public boolean isDashedLineEnabled() { - return mDashPathEffect == null ? false : true; - } - - @Override - public DashPathEffect getDashPathEffect() { - return mDashPathEffect; - } - - /** - * set this to true to enable the drawing of circle indicators for this - * DataSet, default true - * - * @param enabled - */ - public void setDrawCircles(boolean enabled) { - this.mDrawCircles = enabled; - } - - @Override - public boolean isDrawCirclesEnabled() { - return mDrawCircles; - } - - @Deprecated - public void setDrawCubic(boolean enabled) { - mMode = enabled ? LineDataSet.Mode.CUBIC_BEZIER : LineDataSet.Mode.LINEAR; - } - - @Deprecated - @Override - public boolean isDrawCubicEnabled() { - return mMode == LineDataSet.Mode.CUBIC_BEZIER; - } - - @Deprecated - public void setDrawStepped(boolean enabled) { - mMode = enabled ? LineDataSet.Mode.STEPPED : LineDataSet.Mode.LINEAR; - } - - @Deprecated - @Override - public boolean isDrawSteppedEnabled() { - return mMode == LineDataSet.Mode.STEPPED; - } - - /** ALL CODE BELOW RELATED TO CIRCLE-COLORS */ - - /** - * returns all colors specified for the circles - * - * @return - */ - public List getCircleColors() { - return mCircleColors; - } - - @Override - public int getCircleColor(int index) { - return mCircleColors.get(index % mCircleColors.size()); - } - - /** - * Sets the colors that should be used for the circles of this DataSet. - * Colors are reused as soon as the number of Entries the DataSet represents - * is higher than the size of the colors array. Make sure that the colors - * are already prepared (by calling getResources().getColor(...)) before - * adding them to the DataSet. - * - * @param colors - */ - public void setCircleColors(List colors) { - mCircleColors = colors; - } - - /** - * Sets the colors that should be used for the circles of this DataSet. - * Colors are reused as soon as the number of Entries the DataSet represents - * is higher than the size of the colors array. Make sure that the colors - * are already prepared (by calling getResources().getColor(...)) before - * adding them to the DataSet. - * - * @param colors - */ - public void setCircleColors(int[] colors) { - this.mCircleColors = ColorTemplate.createColors(colors); - } - - /** - * ets the colors that should be used for the circles of this DataSet. - * Colors are reused as soon as the number of Entries the DataSet represents - * is higher than the size of the colors array. You can use - * "new String[] { R.color.red, R.color.green, ... }" to provide colors for - * this method. Internally, the colors are resolved using - * getResources().getColor(...) - * - * @param colors - */ - public void setCircleColors(int[] colors, Context c) { - - List clrs = new ArrayList(); - - for (int color : colors) { - clrs.add(c.getResources().getColor(color)); - } - - mCircleColors = clrs; - } - - /** - * Sets the one and ONLY color that should be used for this DataSet. - * Internally, this recreates the colors array and adds the specified color. - * - * @param color - */ - public void setCircleColor(int color) { - resetCircleColors(); - mCircleColors.add(color); - } - - /** - * resets the circle-colors array and creates a new one - */ - public void resetCircleColors() { - mCircleColors = new ArrayList(); - } - - /** - * Sets the color of the inner circle of the line-circles. - * - * @param color - */ - public void setCircleColorHole(int color) { - mCircleColorHole = color; - } - - @Override - public int getCircleHoleColor() { - return mCircleColorHole; - } - - /** - * Set this to true to allow drawing a hole in each data circle. - * - * @param enabled - */ - public void setDrawCircleHole(boolean enabled) { - mDrawCircleHole = enabled; - } - - @Override - public boolean isDrawCircleHoleEnabled() { - return mDrawCircleHole; - } - - /** - * Sets a custom FillFormatter 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) { - - if (formatter == null) - mFillFormatter = new DefaultFillFormatter(); - else - mFillFormatter = formatter; - } - - @Override - public FillFormatter getFillFormatter() { - return mFillFormatter; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java deleted file mode 100644 index 985fc1d143..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.CandleEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.PieDataSet; -import com.github.mikephil.charting.data.PieEntry; -import com.github.mikephil.charting.data.realm.base.RealmBaseDataSet; -import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; -import com.github.mikephil.charting.utils.Utils; - -import io.realm.DynamicRealmObject; -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 07/11/15. - */ -public class RealmPieDataSet extends RealmBaseDataSet implements IPieDataSet { - - /** - * the space in pixels between the chart-slices, default 0f - */ - private float mSliceSpace = 0f; - - /** - * indicates the selection distance of a pie slice - */ - private float mShift = 18f; - - private PieDataSet.ValuePosition mXValuePosition = PieDataSet.ValuePosition.INSIDE_SLICE; - private PieDataSet.ValuePosition mYValuePosition = PieDataSet.ValuePosition.INSIDE_SLICE; - private int mValueLineColor = 0xff000000; - private float mValueLineWidth = 1.0f; - private float mValueLinePart1OffsetPercentage = 75.f; - private float mValueLinePart1Length = 0.3f; - private float mValueLinePart2Length = 0.4f; - private boolean mValueLineVariableLength = true; - - private String mLabelField; - - /** - * Constructor for creating a PieDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - */ - public RealmPieDataSet(RealmResults result, String yValuesField) { - super(result, yValuesField); - - build(this.results); - calcMinMax(); - } - - public RealmPieDataSet(RealmResults result, String yValuesField, String labelField) { - super(result, yValuesField); - this.mLabelField = labelField; - build(this.results); - calcMinMax(); - } - - @Override - public PieEntry buildEntryFromResultObject(T realmObject, float x) { - DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - - if(mLabelField == null) { - return new PieEntry(dynamicObject.getFloat(mYValuesField)); - } else { - return new PieEntry(dynamicObject.getFloat(mYValuesField), dynamicObject.getString(mLabelField)); - } - } - - /** - * Sets the space that is left out between the piechart-slices in dp. - * Default: 0 --> no space, maximum 20f - * - * @param spaceDp - */ - public void setSliceSpace(float spaceDp) { - - if (spaceDp > 20) - spaceDp = 20f; - if (spaceDp < 0) - spaceDp = 0f; - - mSliceSpace = Utils.convertDpToPixel(spaceDp); - } - - @Override - public float getSliceSpace() { - return mSliceSpace; - } - - /** - * 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) { - mShift = Utils.convertDpToPixel(shift); - } - - @Override - public float getSelectionShift() { - return mShift; - } - - @Override - public PieDataSet.ValuePosition getXValuePosition() - { - return mXValuePosition; - } - - public void setXValuePosition(PieDataSet.ValuePosition xValuePosition) - { - this.mXValuePosition = xValuePosition; - } - - @Override - public PieDataSet.ValuePosition getYValuePosition() - { - return mYValuePosition; - } - - public void setYValuePosition(PieDataSet.ValuePosition yValuePosition) - { - this.mYValuePosition = yValuePosition; - } - - /** When valuePosition is OutsideSlice, indicates line color */ - @Override - public int getValueLineColor() - { - return mValueLineColor; - } - - public void setValueLineColor(int valueLineColor) - { - this.mValueLineColor = valueLineColor; - } - - /** When valuePosition is OutsideSlice, indicates line width */ - @Override - public float getValueLineWidth() - { - return mValueLineWidth; - } - - public void setValueLineWidth(float valueLineWidth) - { - this.mValueLineWidth = valueLineWidth; - } - - /** When valuePosition is OutsideSlice, indicates offset as percentage out of the slice size */ - @Override - public float getValueLinePart1OffsetPercentage() - { - return mValueLinePart1OffsetPercentage; - } - - public void setValueLinePart1OffsetPercentage(float valueLinePart1OffsetPercentage) - { - this.mValueLinePart1OffsetPercentage = valueLinePart1OffsetPercentage; - } - - /** When valuePosition is OutsideSlice, indicates length of first half of the line */ - @Override - public float getValueLinePart1Length() - { - return mValueLinePart1Length; - } - - public void setValueLinePart1Length(float valueLinePart1Length) - { - this.mValueLinePart1Length = valueLinePart1Length; - } - - /** When valuePosition is OutsideSlice, indicates length of second half of the line */ - @Override - public float getValueLinePart2Length() - { - return mValueLinePart2Length; - } - - public void setValueLinePart2Length(float valueLinePart2Length) - { - this.mValueLinePart2Length = valueLinePart2Length; - } - - /** When valuePosition is OutsideSlice, this allows variable line length */ - @Override - public boolean isValueLineVariableLength() - { - return mValueLineVariableLength; - } - - public void setValueLineVariableLength(boolean valueLineVariableLength) - { - this.mValueLineVariableLength = valueLineVariableLength; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java deleted file mode 100644 index dac3a59c40..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import android.graphics.Color; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.RadarEntry; -import com.github.mikephil.charting.data.realm.base.RealmLineRadarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 07/11/15. - */ -public class RealmRadarDataSet extends RealmLineRadarDataSet implements IRadarDataSet { - - /// flag indicating whether highlight circle should be drawn or not - protected boolean mDrawHighlightCircleEnabled = false; - - protected int mHighlightCircleFillColor = Color.WHITE; - - /// The stroke color for highlight circle. - /// 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 float mHighlightCircleInnerRadius = 3.0f; - protected float mHighlightCircleOuterRadius = 4.0f; - protected float mHighlightCircleStrokeWidth = 2.0f; - - /** - * Constructor for creating a RadarDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - */ - public RealmRadarDataSet(RealmResults result, String yValuesField) { - super(result, yValuesField); - - build(this.results); - calcMinMax(); - } - - /** - * Constructor for creating a RadarDataSet with realm data. - * - * @param result the queried results from the realm database - * @param xValuesField the name of the field in your data object that represents the x value - * @param yValuesField the name of the field in your data object that represents the y value - */ - public RealmRadarDataSet(RealmResults result, String xValuesField, String yValuesField) { - super(result, xValuesField, yValuesField); - - build(this.results); - calcMinMax(); - } - - /// Returns true if highlight circle should be drawn, false if not - @Override - public boolean isDrawHighlightCircleEnabled() - { - return mDrawHighlightCircleEnabled; - } - - /// Sets whether highlight circle should be drawn or not - @Override - public void setDrawHighlightCircleEnabled(boolean enabled) - { - mDrawHighlightCircleEnabled = enabled; - } - - @Override - public int getHighlightCircleFillColor() - { - return mHighlightCircleFillColor; - } - - 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() - { - 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) - { - mHighlightCircleStrokeColor = color; - } - - @Override - public int getHighlightCircleStrokeAlpha() - { - return mHighlightCircleStrokeAlpha; - } - - public void setHighlightCircleStrokeAlpha(int alpha) - { - mHighlightCircleStrokeAlpha = alpha; - } - - @Override - public float getHighlightCircleInnerRadius() - { - return mHighlightCircleInnerRadius; - } - - public void setHighlightCircleInnerRadius(float radius) - { - mHighlightCircleInnerRadius = radius; - } - - @Override - public float getHighlightCircleOuterRadius() - { - return mHighlightCircleOuterRadius; - } - - public void setHighlightCircleOuterRadius(float radius) - { - mHighlightCircleOuterRadius = radius; - } - - @Override - public float getHighlightCircleStrokeWidth() - { - return mHighlightCircleStrokeWidth; - } - - public void setHighlightCircleStrokeWidth(float strokeWidth) - { - mHighlightCircleStrokeWidth = strokeWidth; - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java deleted file mode 100644 index 2c05c8d246..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.charts.ScatterChart; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.realm.base.RealmLineScatterCandleRadarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; -import com.github.mikephil.charting.renderer.scatter.ShapeRenderer; -import com.github.mikephil.charting.renderer.scatter.SquareShapeRenderer; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.ShapeRendererHandler; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 07/11/15. - */ -public class RealmScatterDataSet extends RealmLineScatterCandleRadarDataSet implements IScatterDataSet { - - /** - * Renderer responsible for rendering this DataSet, default: square - */ - protected ShapeRenderer mShapeRenderer = new SquareShapeRenderer(); - - /** - * the size the scattershape will have, in density pixels - */ - private float mShapeSize = 10f; - - /** - * The radius of the hole in the shape (applies to Square, Circle and Triangle) - * - default: 0.0 - */ - private float mScatterShapeHoleRadius = 0f; - - /** - * Color for the hole in the shape. - * Setting to `ColorTemplate.COLOR_NONE` will behave as transparent. - * - default: ColorTemplate.COLOR_NONE - */ - private int mScatterShapeHoleColor = ColorTemplate.COLOR_NONE; - - /** - * Constructor for creating a ScatterDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - */ - public RealmScatterDataSet(RealmResults result, String yValuesField) { - super(result, yValuesField); - - build(this.results); - calcMinMax(); - } - - /** - * Constructor for creating a ScatterDataSet with realm data. - * - * @param result the queried results from the realm database - * @param xValuesField the name of the field in your data object that represents the x value - * @param yValuesField the name of the field in your data object that represents the y value - */ - public RealmScatterDataSet(RealmResults result, String xValuesField, String yValuesField) { - super(result, xValuesField, yValuesField); - - build(this.results); - calcMinMax(); - } - - /** - * Sets the size in density pixels the drawn scattershape will have. This - * only applies for non custom shapes. - * - * @param size - */ - public void setScatterShapeSize(float size) { - mShapeSize = size; - } - - @Override - public float getScatterShapeSize() { - return mShapeSize; - } - - /** - * Sets the ScatterShape this DataSet should be drawn with. This will search for an available ShapeRenderer and set this - * renderer for the DataSet. - * - * @param shape - */ - public void setScatterShape(ScatterChart.ScatterShape shape) { - - ShapeRendererHandler handler = new ShapeRendererHandler(); - mShapeRenderer = handler.getShapeRenderer(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. - * - * @param shapeRenderer - */ - public void setShapeRenderer(ShapeRenderer shapeRenderer) { - mShapeRenderer = shapeRenderer; - } - - @Override - public ShapeRenderer getShapeRenderer() { - return mShapeRenderer; - } - - /** - * Sets the radius of the hole in the shape - * - * @param holeRadius - */ - public void setScatterShapeHoleRadius(float holeRadius) { - mScatterShapeHoleRadius = holeRadius; - } - - @Override - public float getScatterShapeHoleRadius() { - return mScatterShapeHoleRadius; - } - - /** - * Sets the color for the hole in the shape - * - * @param holeColor - */ - public void setScatterShapeHoleColor(int holeColor) { - mScatterShapeHoleColor = holeColor; - } - - @Override - public int getScatterShapeHoleColor() { - return mScatterShapeHoleColor; - } -} diff --git a/build.gradle b/build.gradle index f2bd5c0e14..0eb3f4e7bf 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - //classpath "io.realm:realm-gradle-plugin:1.0.0" + classpath "io.realm:realm-gradle-plugin:1.0.1" classpath 'com.android.tools.build:gradle:2.1.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' } diff --git a/settings.gradle b/settings.gradle index 6d793f8d10..484b8b3ecd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,7 @@ include 'MPChartLib' //include 'MPAndroidChart-Realm' include 'MPChartExample' +include ':MPChartLib-Realm' +project(':MPChartLib-Realm').projectDir = new File('../MPAndroidChart-Realm/MPChartLib-Realm') From cb6dabbdec6543b28a77d59c4f7c0bbcd9382153 Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Tue, 28 Jun 2016 12:42:05 -0700 Subject: [PATCH 265/606] Eliminate allocs - ObjectPool and Tests (#1892) --- .../mikephil/charting/utils/ObjectPool.java | 218 ++++++++++++++++ .../charting/test/ObjectPoolTest.java | 240 ++++++++++++++++++ 2 files changed, 458 insertions(+) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/utils/ObjectPool.java create mode 100644 MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ObjectPool.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ObjectPool.java new file mode 100644 index 0000000000..d1d54371f9 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ObjectPool.java @@ -0,0 +1,218 @@ +package com.github.mikephil.charting.utils; + +import java.util.List; + +/** + * An object pool for recycling of object instances extending Poolable. + * + * + * Cost/Benefit : + * Cost - The pool can only contain objects extending Poolable. + * Benefit - The pool can very quickly determine if an object is elligable for storage without iteration. + * Benefit - The pool can also know if an instance of Poolable is already stored in a different pool instance. + * Benefit - The pool can grow as needed, if it is empty + * Cost - However, refilling the pool when it is empty might incur a time cost with sufficiently large capacity. Set the replenishPercentage to a lower number if this is a concern. + * + * Created by Tony Patino on 6/20/16. + */ +public class ObjectPool { + + private static int ids = 0; + + private int poolId; + private int desiredCapacity; + private Object[] objects; + private int objectsPointer; + private T modelObject; + private float replenishPercentage; + + + /** + * Returns the id of the given pool instance. + * + * @return an integer ID belonging to this pool instance. + */ + public int getPoolId(){ + return poolId; + } + + /** + * Returns an ObjectPool instance, of a given starting capacity, that recycles instances of a given Poolable object. + * + * @param withCapacity A positive integer value. + * @param object An instance of the object that the pool should recycle. + * @return + */ + public static synchronized ObjectPool create(int withCapacity, Poolable object){ + ObjectPool result = new ObjectPool(withCapacity, object); + result.poolId = ids; + ids++; + + return result; + } + + private ObjectPool(int withCapacity, T object){ + if(withCapacity <= 0){ + throw new IllegalArgumentException("Object Pool must be instantiated with a capacity greater than 0!"); + } + this.desiredCapacity = withCapacity; + this.objects = new Object[this.desiredCapacity]; + this.objectsPointer = 0; + this.modelObject = object; + this.replenishPercentage = 1.0f; + this.refillPool(); + } + + /** + * Set the percentage of the pool to replenish on empty. Valid values are between + * 0.00f and 1.00f + * + * @param percentage a value between 0 and 1, representing the percentage of the pool to replenish. + */ + public void setReplenishPercentage(float percentage){ + float p = percentage; + if(p > 1){ + p = 1; + } + else if(p < 0f){ + p = 0f; + } + this.replenishPercentage = p; + } + + public float getReplenishPercentage(){ + return replenishPercentage; + } + + private void refillPool(){ + this.refillPool(this.replenishPercentage); + } + + private void refillPool(float percentage){ + int portionOfCapacity = (int) (desiredCapacity * percentage); + + if(portionOfCapacity < 1){ + portionOfCapacity = 1; + }else if(portionOfCapacity > desiredCapacity){ + portionOfCapacity = desiredCapacity; + } + + for(int i = 0 ; i < portionOfCapacity ; i++){ + this.objects[i] = modelObject.instantiate(); + } + objectsPointer = portionOfCapacity - 1; + } + + /** + * Returns an instance of Poolable. If get() is called with an empty pool, the pool will be + * replenished. If the pool capacity is sufficiently large, this could come at a performance + * cost. + * + * @return An instance of Poolable object T + */ + public synchronized T get(){ + + if(this.objectsPointer == -1 && this.replenishPercentage > 0.0f){ + this.refillPool(); + } + + T result = (T)objects[this.objectsPointer]; + result.currentOwnerId = Poolable.NO_OWNER; + this.objectsPointer--; + + return result; + } + + /** + * Recycle an instance of Poolable that this pool is capable of generating. + * The T instance passed must not already exist inside this or any other ObjectPool instance. + * + * @param object An object of type T to recycle + */ + public synchronized void recycle(T object){ + if(object.currentOwnerId != Poolable.NO_OWNER){ + if(object.currentOwnerId == this.poolId){ + throw new IllegalArgumentException("The object passed is already stored in this pool!"); + }else { + throw new IllegalArgumentException("The object to recycle already belongs to poolId " + object.currentOwnerId + ". Object cannot belong to two different pool instances simultaneously!"); + } + } + + this.objectsPointer++; + if(this.objectsPointer >= objects.length){ + this.resizePool(); + } + + object.currentOwnerId = this.poolId; + objects[this.objectsPointer] = object; + + } + + /** + * Recycle a List of Poolables that this pool is capable of generating. + * The T instances passed must not already exist inside this or any other ObjectPool instance. + * + * @param objects A list of objects of type T to recycle + */ + public synchronized void recycle(List objects){ + while(objects.size() + this.objectsPointer + 1 > this.desiredCapacity){ + this.resizePool(); + } + final int objectsListSize = objects.size(); + + // Not relying on recycle(T object) because this is more performant. + for(int i = 0 ; i < objectsListSize ; i++){ + T object = objects.get(i); + if(object.currentOwnerId != Poolable.NO_OWNER){ + if(object.currentOwnerId == this.poolId){ + throw new IllegalArgumentException("The object passed is already stored in this pool!"); + }else { + throw new IllegalArgumentException("The object to recycle already belongs to poolId " + object.currentOwnerId + ". Object cannot belong to two different pool instances simultaneously!"); + } + } + object.currentOwnerId = this.poolId; + this.objects[this.objectsPointer + 1 + i] = object; + } + this.objectsPointer += objectsListSize; + } + + private void resizePool() { + final int oldCapacity = this.desiredCapacity; + this.desiredCapacity *= 2; + Object[] temp = new Object[this.desiredCapacity]; + for(int i = 0 ; i < oldCapacity ; i++){ + temp[i] = this.objects[i]; + } + this.objects = temp; + } + + /** + * Returns the capacity of this object pool. Note : The pool will automatically resize + * to contain additional objects if the user tries to add more objects than the pool's + * capacity allows, but this comes at a performance cost. + * + * @return The capacity of the pool. + */ + public int getPoolCapacity(){ + return this.objects.length; + } + + /** + * Returns the number of objects remaining in the pool, for diagnostic purposes. + * + * @return The number of objects remaining in the pool. + */ + public int getPoolCount(){ + return this.objectsPointer + 1; + } + + + public static abstract class Poolable{ + + public static int NO_OWNER = -1; + int currentOwnerId = NO_OWNER; + + protected abstract Poolable instantiate(); + + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..e1dbe81be9 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java @@ -0,0 +1,240 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.utils.ObjectPool; + +import junit.framework.Assert; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by otheruser on 6/28/16. + */ +public class ObjectPoolTest { + + static class TestPoolable extends ObjectPool.Poolable{ + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(4, new TestPoolable(0,0)); + } + + public int foo = 0; + public int bar = 0; + + protected ObjectPool.Poolable instantiate(){ + return new TestPoolable(0,0); + } + + private TestPoolable(int foo, int bar){ + this.foo = foo; + this.bar = bar; + } + + public static TestPoolable getInstance(int foo, int bar){ + TestPoolable result = pool.get(); + result.foo = foo; + result.bar = bar; + return result; + } + + public static void recycleInstance(TestPoolable instance){ + pool.recycle(instance); + } + + public static void recycleInstances(List instances){ + pool.recycle(instances); + } + + public static ObjectPool getPool(){ + return pool; + } + + } + + @Test + public void testObjectPool(){ + + int poolCapacity = TestPoolable.getPool().getPoolCapacity(); + int poolCount = TestPoolable.getPool().getPoolCount(); + TestPoolable testPoolable; + ArrayList testPoolables = new ArrayList<>(); + + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(4, poolCount); + + testPoolable = TestPoolable.getInstance(6,7); + Assert.assertEquals(6, testPoolable.foo); + Assert.assertEquals(7, testPoolable.bar); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(3, poolCount); + + TestPoolable.recycleInstance(testPoolable); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(4, poolCount); + + + testPoolable = TestPoolable.getInstance(20,30); + Assert.assertEquals(20, testPoolable.foo); + Assert.assertEquals(30, testPoolable.bar); + + TestPoolable.recycleInstance(testPoolable); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(4, poolCount); + + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(1,2)); + testPoolables.add(TestPoolable.getInstance(3,5)); + testPoolables.add(TestPoolable.getInstance(6,8)); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(0, poolCount); + + + TestPoolable.recycleInstances(testPoolables); + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(4, poolCount); + + testPoolables.clear(); + + + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(1,2)); + testPoolables.add(TestPoolable.getInstance(3,5)); + testPoolables.add(TestPoolable.getInstance(6,8)); + testPoolables.add(TestPoolable.getInstance(8,9)); + Assert.assertEquals(12, testPoolables.get(0).foo); + Assert.assertEquals(24, testPoolables.get(0).bar); + Assert.assertEquals(1, testPoolables.get(1).foo); + Assert.assertEquals(2, testPoolables.get(1).bar); + Assert.assertEquals(3, testPoolables.get(2).foo); + Assert.assertEquals(5, testPoolables.get(2).bar); + Assert.assertEquals(6, testPoolables.get(3).foo); + Assert.assertEquals(8, testPoolables.get(3).bar); + Assert.assertEquals(8, testPoolables.get(4).foo); + Assert.assertEquals(9, testPoolables.get(4).bar); + + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(3, poolCount); + + TestPoolable.recycleInstances(testPoolables); + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(8, poolCapacity); + Assert.assertEquals(8, poolCount); + + testPoolables.clear(); + + + testPoolables.add(TestPoolable.getInstance(0,0)); + testPoolables.add(TestPoolable.getInstance(6,8)); + testPoolables.add(TestPoolable.getInstance(1,2)); + testPoolables.add(TestPoolable.getInstance(3,5)); + testPoolables.add(TestPoolable.getInstance(8,9)); + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(6,8)); + testPoolables.add(TestPoolable.getInstance(6,8)); + Assert.assertEquals(0, testPoolables.get(0).foo); + Assert.assertEquals(0, testPoolables.get(0).bar); + Assert.assertEquals(6, testPoolables.get(1).foo); + Assert.assertEquals(8, testPoolables.get(1).bar); + Assert.assertEquals(1, testPoolables.get(2).foo); + Assert.assertEquals(2, testPoolables.get(2).bar); + Assert.assertEquals(3, testPoolables.get(3).foo); + Assert.assertEquals(5, testPoolables.get(3).bar); + Assert.assertEquals(8, testPoolables.get(4).foo); + Assert.assertEquals(9, testPoolables.get(4).bar); + Assert.assertEquals(12, testPoolables.get(5).foo); + Assert.assertEquals(24, testPoolables.get(5).bar); + Assert.assertEquals(12, testPoolables.get(6).foo); + Assert.assertEquals(24, testPoolables.get(6).bar); + Assert.assertEquals(12, testPoolables.get(7).foo); + Assert.assertEquals(24, testPoolables.get(7).bar); + Assert.assertEquals(6, testPoolables.get(8).foo); + Assert.assertEquals(8, testPoolables.get(8).bar); + Assert.assertEquals(6, testPoolables.get(9).foo); + Assert.assertEquals(8, testPoolables.get(9).bar); + + for(TestPoolable p : testPoolables){ + TestPoolable.recycleInstance(p); + } + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(16, poolCount); + + testPoolable = TestPoolable.getInstance(9001,9001); + Assert.assertEquals(9001, testPoolable.foo); + Assert.assertEquals(9001, testPoolable.bar); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(15, poolCount); + + + TestPoolable.recycleInstance(testPoolable); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(16, poolCount); + + Exception e = null; + try{ + // expect an exception. + TestPoolable.recycleInstance(testPoolable); + }catch (Exception ex){ + e = ex; + }finally{ + Assert.assertEquals(e.getMessage(), true, e != null); + } + + testPoolables.clear(); + + TestPoolable.getPool().setReplenishPercentage(0.5f); + int i = 16; + while(i > 0){ + testPoolables.add(TestPoolable.getInstance(0,0)); + i--; + } + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(0, poolCount); + + testPoolables.add(TestPoolable.getInstance(0,0)); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(7, poolCount); + + + } + +} From d299546ebdfc0cb4d186f0d7ee9fb7564fa45fa7 Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Tue, 28 Jun 2016 13:54:10 -0700 Subject: [PATCH 266/606] Eliminate allocs - FSize pooling (#1892) Replace all "new FSize()" instantiations with FSize.getInstance() / FSize.recycleInstance() pairs. Smarter FSize array usage inside Legend. --- .../mikephil/charting/components/Legend.java | 58 ++++++++++++------ .../charting/renderer/XAxisRenderer.java | 2 + .../XAxisRendererHorizontalBarChart.java | 2 + .../github/mikephil/charting/utils/FSize.java | 41 +++++++++++-- .../github/mikephil/charting/utils/Utils.java | 61 ++++++++++++++++++- 5 files changed, 138 insertions(+), 26 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 77f1710f27..756cc40a13 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 @@ -649,7 +649,7 @@ public float getFormToTextSpace() { * sets the space between the form and the actual label/text, converts to dp * internally * - * @param mFormToTextSpace + * @param space */ public void setFormToTextSpace(float space) { this.mFormToTextSpace = Utils.convertDpToPixel(space); @@ -708,7 +708,7 @@ public float getFullWidth(Paint labelpaint) { /** * Calculates the full height of the drawn legend. * - * @param mLegendLabelPaint + * @param labelpaint * @return */ public float getFullHeight(Paint labelpaint) { @@ -815,6 +815,7 @@ public FSize[] getCalculatedLineSizes() { * * @param labelpaint */ + ArrayList calculatedLineSizesForCalculateDimensions = new ArrayList<>(); public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandler) { mTextWidthMax = getMaximumEntryWidth(labelpaint); @@ -882,9 +883,22 @@ else if (wasStacked) { float contentWidth = viewPortHandler.contentWidth() * mMaxSizePercent; // Prepare arrays for calculated layout - ArrayList calculatedLabelSizes = new ArrayList(labelCount); - ArrayList calculatedLabelBreakPoints = new ArrayList(labelCount); - ArrayList calculatedLineSizes = new ArrayList(); + + //ArrayList calculatedLabelBreakPoints = new ArrayList(labelCount); + + if(mCalculatedLabelSizes.length != labelCount){ + mCalculatedLabelSizes = new FSize[labelCount]; + } + int calculatedLabelSizesIndex = 0; + + if(mCalculatedLabelBreakPoints.length != labelCount){ + mCalculatedLabelBreakPoints = new Boolean[labelCount]; + } + int calculatedLabelBreakIndex = 0; + + ArrayList calculatedLineSizes = calculatedLineSizesForCalculateDimensions; + FSize.recycleInstances(calculatedLineSizes); + calculatedLineSizes.clear(); // Start calculating layout float maxLineWidth = 0.f; @@ -896,7 +910,8 @@ else if (wasStacked) { boolean drawingForm = mColors[i] != ColorTemplate.COLOR_SKIP; - calculatedLabelBreakPoints.add(false); + mCalculatedLabelBreakPoints[calculatedLabelBreakIndex] = false; + calculatedLabelBreakIndex++; if (stackedStartIndex == -1) { // we are not stacking, so required width is for this label @@ -910,14 +925,25 @@ else if (wasStacked) { // grouped forms have null labels if (mLabels[i] != null) { - - calculatedLabelSizes.add(Utils.calcTextSize(labelpaint, mLabels[i])); + if(mCalculatedLabelSizes[calculatedLabelSizesIndex] == null){ + mCalculatedLabelSizes[calculatedLabelSizesIndex] = Utils.calcTextSize(labelpaint, mLabels[i]); + }else{ + Utils.calcTextSize(labelpaint, mLabels[i], mCalculatedLabelSizes[calculatedLabelSizesIndex]); + } + FSize labelSize = mCalculatedLabelSizes[calculatedLabelSizesIndex]; + calculatedLabelSizesIndex++; requiredWidth += drawingForm ? mFormToTextSpace + mFormSize : 0.f; - requiredWidth += calculatedLabelSizes.get(i).width; + requiredWidth += labelSize.width; } else { - calculatedLabelSizes.add(new FSize(0.f, 0.f)); + if(mCalculatedLabelSizes[calculatedLabelSizesIndex] == null){ + mCalculatedLabelSizes[calculatedLabelSizesIndex] = FSize.getInstance(0.f, 0.f); + }else{ + mCalculatedLabelSizes[calculatedLabelSizesIndex].width = 0.f; + mCalculatedLabelSizes[calculatedLabelSizesIndex].height = 0.f; + } + calculatedLabelSizesIndex++; requiredWidth += drawingForm ? mFormSize : 0.f; if (stackedStartIndex == -1) { @@ -942,19 +968,19 @@ else if (wasStacked) { else { // It doesn't fit, we need to wrap a line // Add current line size to array - calculatedLineSizes.add(new FSize(currentLineWidth, labelLineHeight)); + calculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight)); maxLineWidth = Math.max(maxLineWidth, currentLineWidth); // Start a new line - calculatedLabelBreakPoints.set( + mCalculatedLabelBreakPoints[ stackedStartIndex > -1 ? stackedStartIndex - : i, true); + : i] = true; currentLineWidth = requiredWidth; } if (i == labelCount - 1) { // Add last line size to array - calculatedLineSizes.add(new FSize(currentLineWidth, labelLineHeight)); + calculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight)); maxLineWidth = Math.max(maxLineWidth, currentLineWidth); } } @@ -962,10 +988,6 @@ else if (wasStacked) { stackedStartIndex = mLabels[i] != null ? -1 : stackedStartIndex; } - mCalculatedLabelSizes = calculatedLabelSizes.toArray( - new FSize[calculatedLabelSizes.size()]); - mCalculatedLabelBreakPoints = calculatedLabelBreakPoints - .toArray(new Boolean[calculatedLabelBreakPoints.size()]); mCalculatedLineSizes = calculatedLineSizes .toArray(new FSize[calculatedLineSizes.size()]); 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 83c7e9cc3b..e86b38569b 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 @@ -92,6 +92,8 @@ protected void computeSize() { mXAxis.mLabelHeight = Math.round(labelHeight); mXAxis.mLabelRotatedWidth = Math.round(labelRotatedSize.width); mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height); + + FSize.recycleInstance(labelRotatedSize); } @Override 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 526e4ef7fd..b9a43a7454 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 @@ -76,6 +76,8 @@ protected void computeSize() { mXAxis.mLabelHeight = Math.round(labelHeight); mXAxis.mLabelRotatedWidth = (int)(labelRotatedSize.width + mXAxis.getXOffset() * 3.5f); mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height); + + FSize.recycleInstance(labelRotatedSize); } @Override 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 fa7ff4489a..bdd96775c2 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 @@ -1,16 +1,46 @@ package com.github.mikephil.charting.utils; +import java.util.List; + /** - * Immutable class for describing width and height dimensions in some arbitrary + * Class for describing width and height dimensions in some arbitrary * unit. Replacement for the android.Util.SizeF which is available only on API >= 21. */ -public final class FSize { +public final class FSize extends ObjectPool.Poolable{ + + // TODO : Encapsulate width & height + + public float width; + public float height; + + private static ObjectPool pool; - public final float width; - public final float height; + static { + pool = ObjectPool.create(500, new FSize(0,0)); + } + + + protected ObjectPool.Poolable instantiate(){ + return new FSize(0,0); + } - public FSize(final float width, final float height) { + public static FSize getInstance(final float width, final float height){ + FSize result = pool.get(); + result.width = width; + result.height = height; + return result; + } + + public static void recycleInstance(FSize instance){ + pool.recycle(instance); + } + + public static void recycleInstances(List instances){ + pool.recycle(instances); + } + + private FSize(final float width, final float height) { this.width = width; this.height = height; } @@ -42,4 +72,5 @@ public String toString() { public int hashCode() { return Float.floatToIntBits(width) ^ Float.floatToIntBits(height); } + } 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 3da44084b6..223ea6d8b3 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 @@ -173,20 +173,39 @@ public static float getLineSpacing(Paint paint) { } /** + * Returns a recyclable FSize instance. * calculates the approximate size of a text, depending on a demo text * avoid repeated calls (e.g. inside drawing methods) * * @param paint * @param demoText - * @return + * @return A Recyclable FSize instance */ public static FSize calcTextSize(Paint paint, String demoText) { + FSize result = FSize.getInstance(0,0); + calcTextSize(paint, demoText, result); + return result; + } + + /** + * calculates the approximate size of a text, depending on a demo text + * avoid repeated calls (e.g. inside drawing methods) + * + * @param paint + * @param demoText + * @param outputFSize An output variable, modified by the function. + */ + public static void calcTextSize(Paint paint, String demoText, FSize outputFSize) { + Rect r = new Rect(); paint.getTextBounds(demoText, 0, demoText.length(), r); - return new FSize(r.width(), r.height()); + outputFSize.width = r.width(); + outputFSize.height = r.height(); + } + /** * Math.pow(...) is very expensive, so avoid calling it and create it * yourself. @@ -494,6 +513,7 @@ public static void drawXAxisValue(Canvas c, String text, float x, float y, translateX -= rotatedSize.width * (anchor.x - 0.5f); translateY -= rotatedSize.height * (anchor.y - 0.5f); + FSize.recycleInstance(rotatedSize); } c.save(); @@ -564,6 +584,7 @@ public static void drawMultilineText(Canvas c, StaticLayout textLayout, translateX -= rotatedSize.width * (anchor.x - 0.5f); translateY -= rotatedSize.height * (anchor.y - 0.5f); + FSize.recycleInstance(rotatedSize); } c.save(); @@ -611,26 +632,60 @@ public static void drawMultilineText(Canvas c, String text, drawMultilineText(c, textLayout, x, y, paint, anchor, angleDegrees); } + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by degrees. + * + * @param rectangleSize + * @param degrees + * @return A Recyclable FSize instance + */ public static FSize getSizeOfRotatedRectangleByDegrees(FSize rectangleSize, float degrees) { final float radians = degrees * FDEG2RAD; return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height, radians); } + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by radians. + * + * @param rectangleSize + * @param radians + * @return A Recyclable FSize instance + */ public static FSize getSizeOfRotatedRectangleByRadians(FSize rectangleSize, float radians) { return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height, radians); } + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by degrees. + * + * @param rectangleWidth + * @param rectangleHeight + * @param degrees + * @return A Recyclable FSize instance + */ public static FSize getSizeOfRotatedRectangleByDegrees(float rectangleWidth, float rectangleHeight, float degrees) { final float radians = degrees * FDEG2RAD; return getSizeOfRotatedRectangleByRadians(rectangleWidth, rectangleHeight, radians); } + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by radians. + * + * @param rectangleWidth + * @param rectangleHeight + * @param radians + * @return A Recyclable FSize instance + */ public static FSize getSizeOfRotatedRectangleByRadians(float rectangleWidth, float rectangleHeight, float radians) { - return new FSize( + return FSize.getInstance( Math.abs(rectangleWidth * (float) Math.cos(radians)) + Math.abs(rectangleHeight * (float) Math.sin(radians)), Math.abs(rectangleWidth * (float) Math.sin(radians)) + Math.abs(rectangleHeight * From fcf26aa6adc44fff8d1700835690d400469ee84c Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Tue, 28 Jun 2016 14:26:27 -0700 Subject: [PATCH 267/606] Eliminate allocs - PointD pooling (#1892) Replace all "new PointD()" instantiations with PointD.getInstance() / PointD.recycleInstance() pairs. Helper methods overloaded to include void return / output PointD variable signatures. Old methods remain as convenience, with notations that they return recyclable instances. --- .../charting/charts/BarLineChartBase.java | 37 +++++++++++++++---- .../charting/charts/HorizontalBarChart.java | 16 +++++--- .../charting/highlight/BarHighlighter.java | 4 ++ .../charting/highlight/ChartHighlighter.java | 7 +++- .../highlight/HorizontalBarHighlighter.java | 2 + .../charting/renderer/AxisRenderer.java | 3 ++ .../charting/renderer/XAxisRenderer.java | 3 ++ .../XAxisRendererHorizontalBarChart.java | 3 ++ .../YAxisRendererHorizontalBarChart.java | 3 ++ .../github/mikephil/charting/utils/FSize.java | 2 +- .../mikephil/charting/utils/PointD.java | 33 +++++++++++++++-- .../mikephil/charting/utils/Transformer.java | 19 +++++++--- 12 files changed, 107 insertions(+), 25 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 4f9c565d18..2672bf3006 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 @@ -682,6 +682,8 @@ public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, floa xValue, yValue, (float) origin.x, (float) origin.y, duration); addViewportJob(job); + PointD.recycleInstance(origin); + } else { Log.e(LOG_TAG, "Unable to execute zoomAndCenterAnimated(...) on API level < 11"); } @@ -843,6 +845,8 @@ public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); addViewportJob(job); + + PointD.recycleInstance(bounds); } else { Log.e(LOG_TAG, "Unable to execute moveViewToAnimated(...) on API level < 11"); } @@ -910,6 +914,8 @@ public void centerViewToAnimated(float xValue, float yValue, AxisDependency axis getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); addViewportJob(job); + + PointD.recycleInstance(bounds); } else { Log.e(LOG_TAG, "Unable to execute centerViewToAnimated(...) on API level < 11"); } @@ -1183,6 +1189,7 @@ public void setKeepPositionOnRotation(boolean keepPositionOnRotation) { } /** + * Returns a recyclable PointD instance * Returns the x and y values in the chart at the given touch point * (encapsulated in a PointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to @@ -1193,10 +1200,17 @@ public void setKeepPositionOnRotation(boolean keepPositionOnRotation) { * @return */ public PointD getValuesByTouchPoint(float x, float y, AxisDependency axis) { - return getTransformer(axis).getValuesByTouchPoint(x, y); + PointD result = PointD.getInstance(0,0); + getValuesByTouchPoint(x,y,axis,result); + return result; + } + + public void getValuesByTouchPoint(float x, float y, AxisDependency axis, PointD outputPoint){ + getTransformer(axis).getValuesByTouchPoint(x, y, outputPoint); } /** + * Returns a recyclable PointD instance * Transforms the given chart values into pixels. This is the opposite * method to getValuesByTouchPoint(...). * @@ -1208,6 +1222,7 @@ public PointD getPixelsForValues(float x, float y, AxisDependency axis) { return getTransformer(axis).getPixelsForValues(x, y); } + PointD pointForGetYValueByTouchPoint = PointD.getInstance(0,0); /** * Returns y value at the given touch position (must not necessarily be * a value contained in one of the datasets) @@ -1217,7 +1232,9 @@ public PointD getPixelsForValues(float x, float y, AxisDependency axis) { * @return */ public float getYValueByTouchPoint(float x, float y, AxisDependency axis) { - return (float) getValuesByTouchPoint(x, y, axis).y; + getValuesByTouchPoint(x, y, axis, pointForGetYValueByTouchPoint); + float result = (float) pointForGetYValueByTouchPoint.y; + return result; } /** @@ -1250,6 +1267,7 @@ public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float return null; } + protected PointD posForGetLowestVisibleX = PointD.getInstance(0,0); /** * Returns the lowest x-index (value on the x-axis) that is still visible on * the chart. @@ -1258,11 +1276,13 @@ public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float */ @Override public float getLowestVisibleX() { - PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), - mViewPortHandler.contentBottom()); - return (float) Math.max(mXAxis.mAxisMinimum, pos.x); + getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), posForGetLowestVisibleX); + float result = (float) Math.max(mXAxis.mAxisMinimum, posForGetLowestVisibleX.x); + return result; } + protected PointD posForGetHighestVisibleX = PointD.getInstance(0,0); /** * Returns the highest x-index (value on the x-axis) that is still visible * on the chart. @@ -1271,9 +1291,10 @@ public float getLowestVisibleX() { */ @Override public float getHighestVisibleX() { - PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentRight(), - mViewPortHandler.contentBottom()); - return (float) Math.min(mXAxis.mAxisMaximum, pos.x); + getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentRight(), + mViewPortHandler.contentBottom(), posForGetHighestVisibleX); + float result = (float) Math.min(mXAxis.mAxisMaximum, posForGetHighestVisibleX.x); + return result; } /** 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 cae72464cc..1c04c22331 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 @@ -196,18 +196,22 @@ public Highlight getHighlightByTouchPoint(float x, float y) { return getHighlighter().getHighlight(y, x); // switch x and y } + protected PointD posForGetLowestVisibleX = PointD.getInstance(0,0); @Override public float getLowestVisibleX() { - PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), - mViewPortHandler.contentBottom()); - return (float) Math.max(mXAxis.mAxisMinimum, pos.y); + getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), posForGetLowestVisibleX); + float result = (float) Math.max(mXAxis.mAxisMinimum, posForGetLowestVisibleX.y); + return result; } + protected PointD posForGetHighestVisibleX = PointD.getInstance(0,0); @Override public float getHighestVisibleX() { - PointD pos = getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), - mViewPortHandler.contentTop()); - return (float) Math.min(mXAxis.mAxisMaximum, pos.y); + getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), + mViewPortHandler.contentTop(), posForGetHighestVisibleX); + float result = (float) Math.min(mXAxis.mAxisMaximum, posForGetHighestVisibleX.y); + return result; } /** 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 048db03299..c0b58947e1 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 @@ -37,6 +37,8 @@ public Highlight getHighlight(float x, float y) { (float) pos.y); } + PointD.recycleInstance(pos); + return high; } @@ -78,6 +80,8 @@ public Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal high.getAxis() ); + PointD.recycleInstance(pixels); + return stackedHigh; } } 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 ced7150c94..90d74b26e9 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 @@ -28,15 +28,20 @@ public ChartHighlighter(T chart) { @Override public Highlight getHighlight(float x, float y) { - float xVal = (float) getValsForTouch(x, y).x; + PointD pos = getValsForTouch(x, y); + float xVal = (float) pos.x; + PointD.recycleInstance(pos); Highlight high = getHighlightForX(xVal, x, y); return high; } + /** + * Returns a recyclable PointD instance. * Returns the corresponding xPos for a given touch-position in pixels. * * @param x + * @param y * @return */ protected PointD getValsForTouch(float x, float y) { 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 c9f89dbe2f..ce1220027e 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 @@ -37,6 +37,8 @@ public Highlight getHighlight(float x, float y) { (float) pos.x); } + PointD.recycleInstance(pos); + return high; } 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 e0d71b7a59..f1d4ff0981 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 @@ -133,6 +133,9 @@ public void computeAxis(float min, float max, boolean inverted) { min = (float) p1.y; max = (float) p2.y; } + + PointD.recycleInstance(p1); + PointD.recycleInstance(p2); } computeAxisValues(min, max); 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 e86b38569b..0e94f55e81 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 @@ -58,6 +58,9 @@ public void computeAxis(float min, float max, boolean inverted) { min = (float) p1.x; max = (float) p2.x; } + + PointD.recycleInstance(p1); + PointD.recycleInstance(p2); } computeAxisValues(min, max); 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 b9a43a7454..a40c75f1b4 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 @@ -49,6 +49,9 @@ public void computeAxis(float min, float max, boolean inverted) { min = (float) p1.y; max = (float) p2.y; } + + PointD.recycleInstance(p1); + PointD.recycleInstance(p2); } computeAxisValues(min, max); 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 c1f510a3cf..0f0e707850 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 @@ -51,6 +51,9 @@ public void computeAxis(float yMin, float yMax, boolean inverted) { yMin = (float) p2.x; yMax = (float) p1.x; } + + PointD.recycleInstance(p1); + PointD.recycleInstance(p2); } computeAxisValues(yMin, yMax); 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 bdd96775c2..89bf6a9a80 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 @@ -17,7 +17,7 @@ public final class FSize extends ObjectPool.Poolable{ private static ObjectPool pool; static { - pool = ObjectPool.create(500, new FSize(0,0)); + pool = ObjectPool.create(256, new FSize(0,0)); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java index 7335d771ab..ed5ae61e72 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java @@ -1,17 +1,44 @@ package com.github.mikephil.charting.utils; +import java.util.List; + /** * Point encapsulating two double values. * * @author Philipp Jahoda */ -public class PointD { +public class PointD extends ObjectPool.Poolable { + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(64, new PointD(0,0)); + } + + public static PointD getInstance(double x, double y){ + PointD result = pool.get(); + result.x = x; + result.y = y; + return result; + } + + public static void recycleInstance(PointD instance){ + pool.recycle(instance); + } + + public static void recycleInstances(List instances){ + pool.recycle(instances); + } public double x; public double y; - public PointD(double x, double y) { + protected ObjectPool.Poolable instantiate(){ + return new PointD(0,0); + } + + private PointD(double x, double y) { this.x = x; this.y = y; } @@ -22,4 +49,4 @@ public PointD(double x, double y) { public String toString() { return "PointD, x: " + x + ", y: " + y; } -} +} \ No newline at end of file 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 8604d56cbd..2134f5981c 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 @@ -355,7 +355,8 @@ public void pixelsToValue(float[] pixels) { float[] ptsBuffer = new float[2]; /** - * Returns the x and y values in the chart at the given touch point + * Returns a recyclable PointD instance. + * returns the x and y values in the chart at the given touch point * (encapsulated in a PointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to * getPixelsForValues(...). @@ -366,18 +367,24 @@ public void pixelsToValue(float[] pixels) { */ public PointD getValuesByTouchPoint(float x, float y) { + PointD result = PointD.getInstance(0,0); + getValuesByTouchPoint(x,y,result); + return result; + } + + public void getValuesByTouchPoint(float x, float y, PointD outputPoint){ + ptsBuffer[0] = x; ptsBuffer[1] = y; pixelsToValue(ptsBuffer); - double xTouchVal = ptsBuffer[0]; - double yTouchVal = ptsBuffer[1]; - - return new PointD(xTouchVal, yTouchVal); + outputPoint.x = ptsBuffer[0]; + outputPoint.y = ptsBuffer[1]; } /** + * Returns a recyclable PointD instance. * Returns the x and y coordinates (pixels) for a given x and y value in the chart. * * @param x @@ -394,7 +401,7 @@ public PointD getPixelsForValues(float x, float y) { double xPx = ptsBuffer[0]; double yPx = ptsBuffer[1]; - return new PointD(xPx, yPx); + return PointD.getInstance(xPx, yPx); } public Matrix getValueMatrix() { From 0f2e2133d33fa27dcf26ad193a952123b19b5788 Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Tue, 28 Jun 2016 14:48:57 -0700 Subject: [PATCH 268/606] Eliminate allocs - Legend arrays (#1892) In addition to creating a resized label sizes array, copy old FSize instances to the new array to cut down on the need to get instances from pool. Recycle to pool if the labels shrank. --- .../github/mikephil/charting/components/Legend.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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 756cc40a13..b66d93042e 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 @@ -887,7 +887,16 @@ else if (wasStacked) { //ArrayList calculatedLabelBreakPoints = new ArrayList(labelCount); if(mCalculatedLabelSizes.length != labelCount){ - mCalculatedLabelSizes = new FSize[labelCount]; + FSize[] temp = new FSize[labelCount]; + int count = mCalculatedLabelSizes.length; + for(int i = 0 ; i < count && i < labelCount ; i++){ + temp[i] = mCalculatedLabelSizes[i]; + } + while(count > labelCount){ + count--; + FSize.recycleInstance(mCalculatedLabelSizes[count]); + } + mCalculatedLabelSizes = temp; } int calculatedLabelSizesIndex = 0; From f86c1632a7b9d6eed6dbc8e2f1357c65b7b90359 Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Tue, 28 Jun 2016 15:48:48 -0700 Subject: [PATCH 269/606] Eliminate allocs - MPPointF replace PointF (#1892) In order to have a poolable alternative to PointF, this changeset introduces MPPointF. Convenience methods exist that accept output MPPointF variables for extra savings on pool access. Methods that return recyclable MPPointF instances are documented. --- .../mpchartexample/BarChartActivity.java | 5 +- .../HorizontalBarChartActivity.java | 5 +- .../charting/charts/BarLineChartBase.java | 14 +++- .../mikephil/charting/charts/Chart.java | 24 ++++-- .../charting/charts/HorizontalBarChart.java | 12 ++- .../mikephil/charting/charts/PieChart.java | 21 +++-- .../charting/charts/PieRadarChartBase.java | 30 +++++-- .../charting/highlight/RadarHighlighter.java | 8 +- .../dataprovider/ChartInterface.java | 5 +- .../listener/BarLineChartTouchListener.java | 41 +++++---- .../listener/PieRadarChartTouchListener.java | 3 +- .../charting/renderer/PieChartRenderer.java | 28 +++++-- .../charting/renderer/RadarChartRenderer.java | 78 +++++++++++------ .../charting/renderer/XAxisRenderer.java | 41 ++++----- .../XAxisRendererHorizontalBarChart.java | 42 ++++++---- .../renderer/XAxisRendererRadarChart.java | 17 ++-- .../renderer/YAxisRendererRadarChart.java | 24 ++++-- .../mikephil/charting/utils/MPPointF.java | 83 +++++++++++++++++++ .../github/mikephil/charting/utils/Utils.java | 18 ++-- .../charting/utils/ViewPortHandler.java | 4 +- 20 files changed, 357 insertions(+), 146 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index ce090da068..36dce1166e 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -33,6 +33,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.custom.DayAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -264,7 +265,7 @@ public void onValueSelected(Entry e, Highlight h) { return; RectF bounds = mChart.getBarBounds((BarEntry) e); - PointF position = mChart.getPosition(e, AxisDependency.LEFT); + MPPointF position = mChart.getPosition(e, AxisDependency.LEFT); Log.i("bounds", bounds.toString()); Log.i("position", position.toString()); @@ -272,6 +273,8 @@ public void onValueSelected(Entry e, Highlight h) { Log.i("x-index", "low: " + mChart.getLowestVisibleX() + ", high: " + mChart.getHighestVisibleX()); + + MPPointF.recycleInstance(position); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index 64b9be2ea5..a3ad974eff 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -27,6 +27,7 @@ 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; @@ -253,11 +254,13 @@ public void onValueSelected(Entry e, Highlight h) { return; RectF bounds = mChart.getBarBounds((BarEntry) e); - PointF position = mChart.getPosition(e, mChart.getData().getDataSetByIndex(h.getDataSetIndex()) + 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 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 2672bf3006..de0357d80c 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 @@ -32,6 +32,7 @@ import com.github.mikephil.charting.listener.OnDrawListener; import com.github.mikephil.charting.renderer.XAxisRenderer; import com.github.mikephil.charting.renderer.YAxisRenderer; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; @@ -594,11 +595,13 @@ public void computeScroll() { */ public void zoomIn() { - PointF center = mViewPortHandler.getContentCenter(); + MPPointF center = mViewPortHandler.getContentCenter(); Matrix save = mViewPortHandler.zoomIn(center.x, -center.y); mViewPortHandler.refresh(save, this, false); + MPPointF.recycleInstance(center); + // 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. @@ -611,11 +614,13 @@ public void zoomIn() { */ public void zoomOut() { - PointF center = mViewPortHandler.getContentCenter(); + MPPointF center = mViewPortHandler.getContentCenter(); Matrix save = mViewPortHandler.zoomOut(center.x, -center.y); mViewPortHandler.refresh(save, this, false); + MPPointF.recycleInstance(center); + // 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. @@ -1000,13 +1005,14 @@ public OnDrawListener getDrawListener() { } /** + * Returns a recyclable MPPointF instance. * Returns the position (in pixels) the provided Entry has inside the chart * view or null, if the provided Entry is null. * * @param e * @return */ - public PointF getPosition(Entry e, AxisDependency axis) { + public MPPointF getPosition(Entry e, AxisDependency axis) { if (e == null) return null; @@ -1017,7 +1023,7 @@ public PointF getPosition(Entry e, AxisDependency axis) { getTransformer(axis).pointValuesToPixel(vals); - return new PointF(vals[0], vals[1]); + return MPPointF.getInstance(vals[0], vals[1]); } /** 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 ce5d42ea65..8540e6032a 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 @@ -45,6 +45,7 @@ import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.renderer.DataRenderer; import com.github.mikephil.charting.renderer.LegendRenderer; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -450,7 +451,7 @@ protected void onDraw(Canvas canvas) { /** * the custom position of the description text */ - private PointF mDescriptionPosition; + private MPPointF mDescriptionPosition; /** * draws the description text in the bottom right corner of the chart @@ -1043,22 +1044,24 @@ public float getXRange() { } /** + * Returns a recyclable MPPointF instance. * Returns the center point of the chart (the whole View) in pixels. * * @return */ - public PointF getCenter() { - return new PointF(getWidth() / 2f, getHeight() / 2f); + public MPPointF getCenter() { + return MPPointF.getInstance(getWidth() / 2f, getHeight() / 2f); } /** + * Returns a recyclable MPPointF instance. * Returns the center of the chart taking offsets under consideration. * (returns the center of the content rectangle) * * @return */ @Override - public PointF getCenterOffsets() { + public MPPointF getCenterOffsets() { return mViewPortHandler.getContentCenter(); } @@ -1081,7 +1084,12 @@ public void setDescription(String desc) { * @param y - ycoordinate */ public void setDescriptionPosition(float x, float y) { - mDescriptionPosition = new PointF(x, y); + if(mDescriptionPosition == null){ + mDescriptionPosition = MPPointF.getInstance(x,y); + }else { + mDescriptionPosition.x = x; + mDescriptionPosition.y = y; + } } /** @@ -1484,8 +1492,12 @@ public void setHighlighter(ChartHighlighter highlighter) { mHighlighter = highlighter; } + /** + * Returns a recyclable MPPointF instance. + * @return + */ @Override - public PointF getCenterOfView() { + public MPPointF getCenterOfView() { return getCenter(); } 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 1c04c22331..4fbd2e2d41 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 @@ -17,6 +17,7 @@ import com.github.mikephil.charting.renderer.XAxisRendererHorizontalBarChart; import com.github.mikephil.charting.renderer.YAxisRendererHorizontalBarChart; import com.github.mikephil.charting.utils.HorizontalViewPortHandler; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.TransformerHorizontalBarChart; import com.github.mikephil.charting.utils.Utils; @@ -164,8 +165,15 @@ public RectF getBarBounds(BarEntry e) { return bounds; } + /** + * Returns a recyclable MPPointF instance. + * + * @param e + * @param axis + * @return + */ @Override - public PointF getPosition(Entry e, AxisDependency axis) { + public MPPointF getPosition(Entry e, AxisDependency axis) { if (e == null) return null; @@ -174,7 +182,7 @@ public PointF getPosition(Entry e, AxisDependency axis) { getTransformer(axis).pointValuesToPixel(vals); - return new PointF(vals[0], vals[1]); + return MPPointF.getInstance(vals[0], vals[1]); } /** 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 232fe48ea8..416110e9f9 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 @@ -15,6 +15,7 @@ import com.github.mikephil.charting.highlight.PieHighlighter; import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; import com.github.mikephil.charting.renderer.PieChartRenderer; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import java.util.List; @@ -72,7 +73,7 @@ public class PieChart extends PieRadarChartBase { */ private CharSequence mCenterText = ""; - private PointF mCenterTextOffset = new PointF(0, 0); + private MPPointF mCenterTextOffset = MPPointF.getInstance(0, 0); /** * indicates the size of the hole in the center of the piechart, default: @@ -150,7 +151,7 @@ public void calculateOffsets() { float diameter = getDiameter(); float radius = diameter / 2f; - PointF c = getCenterOffsets(); + MPPointF c = getCenterOffsets(); float shift = mData.getDataSet().getSelectionShift(); @@ -160,6 +161,8 @@ public void calculateOffsets() { c.y - radius + shift, c.x + radius - shift, c.y + radius - shift); + + MPPointF.recycleInstance(c); } @Override @@ -170,7 +173,7 @@ protected void calcMinMax() { @Override protected float[] getMarkerPosition(Highlight highlight) { - PointF center = getCenterCircleBox(); + MPPointF center = getCenterCircleBox(); float r = getRadius(); float off = r / 10f * 3.6f; @@ -196,6 +199,7 @@ protected float[] getMarkerPosition(Highlight highlight) { * Math.sin(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset) * mAnimator.getPhaseY())) + center.y); + MPPointF.recycleInstance(center); return new float[]{x, y}; } @@ -459,8 +463,8 @@ public RectF getCircleBox() { * * @return */ - public PointF getCenterCircleBox() { - return new PointF(mCircleBox.centerX(), mCircleBox.centerY()); + public MPPointF getCenterCircleBox() { + return MPPointF.getInstance(mCircleBox.centerX(), mCircleBox.centerY()); } /** @@ -498,7 +502,8 @@ public void setCenterTextSizePixels(float sizePixels) { * @param y */ public void setCenterTextOffset(float x, float y) { - mCenterTextOffset = new PointF(Utils.convertDpToPixel(x), Utils.convertDpToPixel(y)); + mCenterTextOffset.x = Utils.convertDpToPixel(x); + mCenterTextOffset.y = Utils.convertDpToPixel(y); } /** @@ -506,8 +511,8 @@ public void setCenterTextOffset(float x, float y) { * * @return */ - public PointF getCenterTextOffset() { - return mCenterTextOffset; + public MPPointF getCenterTextOffset() { + return MPPointF.getInstance(mCenterTextOffset.x, mCenterTextOffset.y); } /** 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 6a82bc13a8..2cf3da0b66 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 @@ -20,6 +20,7 @@ import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.PieRadarChartTouchListener; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; /** @@ -138,7 +139,7 @@ public void calculateOffsets() { float legendWidth = fullLegendWidth + spacing; float legendHeight = mLegend.mNeededHeight + mLegend.mTextHeightMax; - PointF c = getCenter(); + MPPointF center = getCenter(); float bottomX = mLegend.getHorizontalAlignment() == Legend.LegendHorizontalAlignment.RIGHT @@ -147,19 +148,22 @@ public void calculateOffsets() { float bottomY = legendHeight + 15.f; float distLegend = distanceToCenter(bottomX, bottomY); - PointF reference = getPosition(c, getRadius(), + MPPointF reference = getPosition(center, getRadius(), getAngleForPoint(bottomX, bottomY)); float distReference = distanceToCenter(reference.x, reference.y); float minOffset = Utils.convertDpToPixel(5f); - if (bottomY >= c.y && getHeight() - legendWidth > getWidth()) { + if (bottomY >= center.y && getHeight() - legendWidth > getWidth()) { xLegendOffset = legendWidth; } else if (distLegend < distReference) { float diff = distReference - distLegend; xLegendOffset = minOffset + diff; } + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(reference); } } @@ -258,7 +262,7 @@ public void calculateOffsets() { */ public float getAngleForPoint(float x, float y) { - PointF c = getCenterOffsets(); + MPPointF c = getCenterOffsets(); double tx = x - c.x, ty = y - c.y; double length = Math.sqrt(tx * tx + ty * ty); @@ -276,10 +280,13 @@ public float getAngleForPoint(float x, float y) { if (angle > 360f) angle = angle - 360f; + MPPointF.recycleInstance(c); + return angle; } /** + * Returns a recyclable MPPointF instance. * Calculates the position around a center point, depending on the distance * from the center, and the angle of the position around the center. * @@ -288,13 +295,18 @@ public float getAngleForPoint(float x, float y) { * @param angle in degrees, converted to radians internally * @return */ - public PointF getPosition(PointF center, float dist, float angle) { + public MPPointF getPosition(MPPointF center, float dist, float angle) { - PointF p = new PointF((float) (center.x + dist * Math.cos(Math.toRadians(angle))), - (float) (center.y + dist * Math.sin(Math.toRadians(angle)))); + MPPointF p = MPPointF.getInstance(0,0); + getPosition(center, dist, angle, p); return p; } + public void getPosition(MPPointF center, float dist, float angle, MPPointF outputPoint){ + outputPoint.x = (float) (center.x + dist * Math.cos(Math.toRadians(angle))); + outputPoint.y = (float) (center.y + dist * Math.sin(Math.toRadians(angle))); + } + /** * Returns the distance of a certain point on the chart to the center of the * chart. @@ -305,7 +317,7 @@ public PointF getPosition(PointF center, float dist, float angle) { */ public float distanceToCenter(float x, float y) { - PointF c = getCenterOffsets(); + MPPointF c = getCenterOffsets(); float dist = 0f; @@ -327,6 +339,8 @@ public float distanceToCenter(float x, float y) { // pythagoras dist = (float) Math.sqrt(Math.pow(xDist, 2.0) + Math.pow(yDist, 2.0)); + MPPointF.recycleInstance(c); + return dist; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java index 706dab07f4..c322980b89 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java @@ -5,6 +5,7 @@ import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; @@ -61,6 +62,7 @@ protected List getHighlightsAtIndex(int index) { float sliceangle = mChart.getSliceAngle(); float factor = mChart.getFactor(); + MPPointF pOut = MPPointF.getInstance(0,0); for (int i = 0; i < mChart.getData().getDataSetCount(); i++) { IDataSet dataSet = mChart.getData().getDataSetByIndex(i); @@ -69,11 +71,11 @@ protected List getHighlightsAtIndex(int index) { float y = (entry.getY() - mChart.getYChartMin()); - PointF p = Utils.getPosition( + Utils.getPosition( mChart.getCenterOffsets(), y * factor * phaseY, - sliceangle * index * phaseX + mChart.getRotationAngle()); + sliceangle * index * phaseX + mChart.getRotationAngle(), pOut); - vals.add(new Highlight(index, entry.getY(), p.x, p.y, i, dataSet.getAxisDependency())); + vals.add(new Highlight(index, entry.getY(), pOut.x, pOut.y, i, dataSet.getAxisDependency())); } return vals; 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 1083e34863..a4618554bb 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 @@ -5,6 +5,7 @@ import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; /** @@ -56,9 +57,9 @@ public interface ChartInterface { int getHeight(); - PointF getCenterOfView(); + MPPointF getCenterOfView(); - PointF getCenterOffsets(); + MPPointF getCenterOffsets(); RectF getContentRect(); 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 0fe3ef94ac..f1a8241dc4 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 @@ -16,6 +16,7 @@ import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; 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; @@ -41,12 +42,12 @@ public class BarLineChartTouchListener extends ChartTouchListener mMinScalePointerDistance) { // get the translation - PointF t = getTrans(mTouchPointCenter.x, mTouchPointCenter.y); + MPPointF t = getTrans(mTouchPointCenter.x, mTouchPointCenter.y); ViewPortHandler h = mChart.getViewPortHandler(); // take actions depending on the activated touch mode @@ -408,6 +414,8 @@ private void performZoom(MotionEvent event) { l.onChartScale(event, 1f, scaleY); } } + + MPPointF.recycleInstance(t); } } } @@ -439,10 +447,11 @@ private void performHighlightDrag(MotionEvent e) { * @param point * @param event */ - private static void midPoint(PointF point, MotionEvent event) { + private static void midPoint(MPPointF point, MotionEvent event) { float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); - point.set(x / 2f, y / 2f); + point.x = (x / 2f); + point.y = (y / 2f); } /** @@ -482,6 +491,7 @@ private static float getYDist(MotionEvent e) { } /** + * Returns a recyclable MPPointF instance. * returns the correct translation depending on the provided x and y touch * points * @@ -489,7 +499,7 @@ private static float getYDist(MotionEvent e) { * @param y * @return */ - public PointF getTrans(float x, float y) { + public MPPointF getTrans(float x, float y) { ViewPortHandler vph = mChart.getViewPortHandler(); @@ -503,7 +513,7 @@ public PointF getTrans(float x, float y) { yTrans = -(mChart.getMeasuredHeight() - y - vph.offsetBottom()); } - return new PointF(xTrans, yTrans); + return MPPointF.getInstance(xTrans, yTrans); } /** @@ -554,13 +564,15 @@ public boolean onDoubleTap(MotionEvent e) { // check if double-tap zooming is enabled if (mChart.isDoubleTapToZoomEnabled() && mChart.getData().getEntryCount() > 0) { - PointF trans = getTrans(e.getX(), e.getY()); + MPPointF trans = getTrans(e.getX(), e.getY()); mChart.zoom(mChart.isScaleXEnabled() ? 1.4f : 1f, mChart.isScaleYEnabled() ? 1.4f : 1f, trans.x, trans.y); if (mChart.isLogEnabled()) Log.i("BarlineChartTouch", "Double-Tap, Zooming In, x: " + trans.x + ", y: " + trans.y); + + MPPointF.recycleInstance(trans); } return super.onDoubleTap(e); @@ -615,7 +627,8 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve } public void stopDeceleration() { - mDecelerationVelocity = new PointF(0.f, 0.f); + mDecelerationVelocity.x = 0; + mDecelerationVelocity.y = 0; } public void computeScroll() { 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 c6d82d6193..527617472a 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 @@ -9,6 +9,7 @@ import com.github.mikephil.charting.charts.PieRadarChartBase; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; @@ -20,7 +21,7 @@ */ public class PieRadarChartTouchListener extends ChartTouchListener> { - private PointF mTouchStartPoint = new PointF(); + private MPPointF mTouchStartPoint = MPPointF.getInstance(0,0); /** * the angle where the dragging started 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 8eb5311ee9..2466078ce4 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 @@ -26,6 +26,7 @@ import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; 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.utils.ViewPortHandler; @@ -152,7 +153,7 @@ public void drawData(Canvas c) { private RectF mInnerRectBuffer = new RectF(); protected float calculateMinimumRadiusForSpacedSlice( - PointF center, + MPPointF center, float radius, float angle, float arcStartPointX, @@ -219,7 +220,7 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { final int entryCount = dataSet.getEntryCount(); final float[] drawAngles = mChart.getDrawAngles(); - final PointF center = mChart.getCenterCircleBox(); + final MPPointF center = mChart.getCenterCircleBox(); final float radius = mChart.getRadius(); final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled(); final float userInnerRadius = drawInnerArc @@ -375,12 +376,14 @@ protected void drawDataSet(Canvas c, IPieDataSet dataSet) { angle += sliceAngle * phaseX; } + + MPPointF.recycleInstance(center); } @Override public void drawValues(Canvas c) { - PointF center = mChart.getCenterCircleBox(); + MPPointF center = mChart.getCenterCircleBox(); // get whole the radius float radius = mChart.getRadius(); @@ -579,7 +582,7 @@ public void drawValues(Canvas c) { xIndex++; } } - + MPPointF.recycleInstance(center); c.restore(); } @@ -615,7 +618,7 @@ protected void drawHole(Canvas c) { float radius = mChart.getRadius(); float holeRadius = radius * (mChart.getHoleRadius() / 100); - PointF center = mChart.getCenterCircleBox(); + MPPointF center = mChart.getCenterCircleBox(); if (Color.alpha(mHolePaint.getColor()) > 0) { // draw the hole-circle @@ -642,6 +645,7 @@ protected void drawHole(Canvas c) { // reset alpha mTransparentCirclePaint.setAlpha(alpha); } + MPPointF.recycleInstance(center); } } @@ -655,8 +659,8 @@ protected void drawCenterText(Canvas c) { if (mChart.isDrawCenterTextEnabled() && centerText != null) { - PointF center = mChart.getCenterCircleBox(); - PointF offset = mChart.getCenterTextOffset(); + MPPointF center = mChart.getCenterCircleBox(); + MPPointF offset = mChart.getCenterTextOffset(); float x = center.x + offset.x; float y = center.y + offset.y; @@ -710,6 +714,9 @@ protected void drawCenterText(Canvas c) { mCenterTextLayout.draw(c); c.restore(); + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(offset); } } @@ -724,7 +731,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { float[] drawAngles = mChart.getDrawAngles(); float[] absoluteAngles = mChart.getAbsoluteAngles(); - final PointF center = mChart.getCenterCircleBox(); + final MPPointF center = mChart.getCenterCircleBox(); final float radius = mChart.getRadius(); final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled(); final float userInnerRadius = drawInnerArc @@ -901,6 +908,8 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint); } + + MPPointF.recycleInstance(center); } /** @@ -921,7 +930,7 @@ protected void drawRoundedSlices(Canvas c) { float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); - PointF center = mChart.getCenterCircleBox(); + MPPointF center = mChart.getCenterCircleBox(); float r = mChart.getRadius(); // calculate the radius of the "slice-circle" @@ -952,6 +961,7 @@ protected void drawRoundedSlices(Canvas c) { angle += sliceAngle * phaseX; } + MPPointF.recycleInstance(center); } /** 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 90b9dcf225..adeaac5851 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 @@ -15,6 +15,7 @@ import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; 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.utils.ViewPortHandler; @@ -87,8 +88,8 @@ protected void drawDataSet(Canvas c, IRadarDataSet dataSet, int mostEntries) { // pixels float factor = mChart.getFactor(); - PointF center = mChart.getCenterOffsets(); - + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); Path surface = new Path(); boolean hasMovedToPoint = false; @@ -99,19 +100,19 @@ protected void drawDataSet(Canvas c, IRadarDataSet dataSet, int mostEntries) { RadarEntry e = dataSet.getEntryForIndex(j); - PointF p = Utils.getPosition( + Utils.getPosition( center, (e.getY() - mChart.getYChartMin()) * factor * phaseY, - sliceangle * j * phaseX + mChart.getRotationAngle()); + sliceangle * j * phaseX + mChart.getRotationAngle(), pOut); - if (Float.isNaN(p.x)) + if (Float.isNaN(pOut.x)) continue; if (!hasMovedToPoint) { - surface.moveTo(p.x, p.y); + surface.moveTo(pOut.x, pOut.y); hasMovedToPoint = true; } else - surface.lineTo(p.x, p.y); + surface.lineTo(pOut.x, pOut.y); } if (dataSet.getEntryCount() > mostEntries) { @@ -139,6 +140,9 @@ protected void drawDataSet(Canvas c, IRadarDataSet dataSet, int mostEntries) { // draw the line (only if filled is disabled or alpha is below 255) if (!dataSet.isDrawFilledEnabled() || dataSet.getFillAlpha() < 255) c.drawPath(surface, mRenderPaint); + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); } @Override @@ -153,7 +157,8 @@ public void drawValues(Canvas c) { // pixels float factor = mChart.getFactor(); - PointF center = mChart.getCenterOffsets(); + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); float yoffset = Utils.convertDpToPixel(5f); @@ -171,15 +176,19 @@ public void drawValues(Canvas c) { RadarEntry entry = dataSet.getEntryForIndex(j); - PointF p = Utils.getPosition( - center, - (entry.getY() - mChart.getYChartMin()) * factor * phaseY, - sliceangle * j * phaseX + mChart.getRotationAngle()); + Utils.getPosition( + center, + (entry.getY() - mChart.getYChartMin()) * factor * phaseY, + sliceangle * j * phaseX + mChart.getRotationAngle(), + pOut); - drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, p.x, p.y - yoffset, dataSet.getValueTextColor + drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, pOut.x, pOut.y - yoffset, dataSet.getValueTextColor (j)); } } + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); } @Override @@ -196,7 +205,7 @@ protected void drawWeb(Canvas c) { float factor = mChart.getFactor(); float rotationangle = mChart.getRotationAngle(); - PointF center = mChart.getCenterOffsets(); + MPPointF center = mChart.getCenterOffsets(); // draw the web lines that come from the center mWebPaint.setStrokeWidth(mChart.getWebLineWidth()); @@ -206,15 +215,18 @@ protected void drawWeb(Canvas c) { final int xIncrements = 1 + mChart.getSkipWebLineCount(); int maxEntryCount = mChart.getData().getMaxEntryCountSet().getEntryCount(); + MPPointF p = MPPointF.getInstance(0,0); for (int i = 0; i < maxEntryCount; i += xIncrements) { - PointF p = Utils.getPosition( + Utils.getPosition( center, mChart.getYRange() * factor, - sliceangle * i + rotationangle); + sliceangle * i + rotationangle, + p); c.drawLine(center.x, center.y, p.x, p.y, mWebPaint); } + MPPointF.recycleInstance(p); // draw the inner-web mWebPaint.setStrokeWidth(mChart.getWebLineWidthInner()); @@ -223,18 +235,24 @@ protected void drawWeb(Canvas c) { int labelCount = mChart.getYAxis().mEntryCount; + MPPointF p1out = MPPointF.getInstance(0,0); + MPPointF p2out = MPPointF.getInstance(0,0); for (int j = 0; j < labelCount; j++) { for (int i = 0; i < mChart.getData().getEntryCount(); i++) { float r = (mChart.getYAxis().mEntries[j] - mChart.getYChartMin()) * factor; - PointF p1 = Utils.getPosition(center, r, sliceangle * i + rotationangle); - PointF p2 = Utils.getPosition(center, r, sliceangle * (i + 1) + rotationangle); + Utils.getPosition(center, r, sliceangle * i + rotationangle, p1out); + Utils.getPosition(center, r, sliceangle * (i + 1) + rotationangle, p2out); + + c.drawLine(p1out.x, p1out.y, p2out.x, p2out.y, mWebPaint); + - c.drawLine(p1.x, p1.y, p2.x, p2.y, mWebPaint); } } + MPPointF.recycleInstance(p1out); + MPPointF.recycleInstance(p2out); } @Override @@ -246,7 +264,8 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { // pixels float factor = mChart.getFactor(); - PointF center = mChart.getCenterOffsets(); + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); RadarData radarData = mChart.getData(); @@ -264,17 +283,19 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { float y = (e.getY() - mChart.getYChartMin()); - PointF p = Utils.getPosition(center, y * factor * mAnimator.getPhaseY(), - sliceangle * high.getX() * mAnimator.getPhaseX() + mChart.getRotationAngle()); + Utils.getPosition(center, + y * factor * mAnimator.getPhaseY(), + sliceangle * high.getX() * mAnimator.getPhaseX() + mChart.getRotationAngle(), + pOut); - high.setDraw(p.x, p.y); + high.setDraw(pOut.x, pOut.y); // draw the lines - drawHighlightLines(c, p.x, p.y, set); + drawHighlightLines(c, pOut.x, pOut.y, set); if (set.isDrawHighlightCircleEnabled()) { - if (!Float.isNaN(p.x) && !Float.isNaN(p.y)) { + if (!Float.isNaN(pOut.x) && !Float.isNaN(pOut.y)) { int strokeColor = set.getHighlightCircleStrokeColor(); if (strokeColor == ColorTemplate.COLOR_NONE) { @@ -286,7 +307,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } drawHighlightCircle(c, - p, + pOut, set.getHighlightCircleInnerRadius(), set.getHighlightCircleOuterRadius(), set.getHighlightCircleFillColor(), @@ -295,10 +316,13 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { } } } + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); } public void drawHighlightCircle(Canvas c, - PointF point, + MPPointF point, float innerRadius, float outerRadius, int fillColor, 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 0e94f55e81..46ccff7124 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 @@ -12,6 +12,7 @@ import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; @@ -111,32 +112,34 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); mAxisLabelPaint.setColor(mXAxis.getTextColor()); + MPPointF pointF = MPPointF.getInstance(0,0); if (mXAxis.getPosition() == XAxisPosition.TOP) { - - drawLabels(c, mViewPortHandler.contentTop() - yoffset, - new PointF(0.5f, 0.9f)); + pointF.x = 0.5f; + pointF.y = 0.9f; + drawLabels(c, mViewPortHandler.contentTop() - yoffset, pointF); } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { - - drawLabels(c, mViewPortHandler.contentTop() + yoffset + mXAxis.mLabelRotatedHeight, - new PointF(0.5f, 1.0f)); + pointF.x = 0.5f; + pointF.y = 1.0f; + drawLabels(c, mViewPortHandler.contentTop() + yoffset + mXAxis.mLabelRotatedHeight, pointF); } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - - drawLabels(c, mViewPortHandler.contentBottom() + yoffset, - new PointF(0.5f, 0.0f)); + pointF.x = 0.5f; + pointF.y = 0.0f; + drawLabels(c, mViewPortHandler.contentBottom() + yoffset, pointF); } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { - - drawLabels(c, mViewPortHandler.contentBottom() - yoffset - mXAxis.mLabelRotatedHeight, - new PointF(0.5f, 0.0f)); + pointF.x = 0.5f; + pointF.y = 0.0f; + drawLabels(c, mViewPortHandler.contentBottom() - yoffset - mXAxis.mLabelRotatedHeight, pointF); } else { // BOTH SIDED - - drawLabels(c, mViewPortHandler.contentTop() - yoffset, - new PointF(0.5f, 1.0f)); - drawLabels(c, mViewPortHandler.contentBottom() + yoffset, - new PointF(0.5f, 0.0f)); + pointF.x = 0.5f; + pointF.y = 1.0f; + drawLabels(c, mViewPortHandler.contentTop() - yoffset, pointF); + pointF.x = 0.5f; + pointF.y = 0.0f; + drawLabels(c, mViewPortHandler.contentBottom() + yoffset, pointF); } } @@ -171,7 +174,7 @@ public void renderAxisLine(Canvas c) { * * @param pos */ - protected void drawLabels(Canvas c, float pos, PointF anchor) { + protected void drawLabels(Canvas c, float pos, MPPointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); @@ -221,7 +224,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) { } } - protected void drawLabel(Canvas c, String formattedLabel, float x, float y, PointF anchor, float angleDegrees) { + protected void drawLabel(Canvas c, String formattedLabel, float x, float y, MPPointF anchor, float angleDegrees) { Utils.drawXAxisValue(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees); } 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 a40c75f1b4..c66ca164cc 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 @@ -12,6 +12,7 @@ import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; @@ -95,37 +96,42 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); mAxisLabelPaint.setColor(mXAxis.getTextColor()); - if (mXAxis.getPosition() == XAxisPosition.TOP) { + MPPointF pointF = MPPointF.getInstance(0,0); - drawLabels(c, mViewPortHandler.contentRight() + xoffset, - new PointF(0.0f, 0.5f)); + if (mXAxis.getPosition() == XAxisPosition.TOP) { + pointF.x = 0.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentRight() + xoffset, pointF); } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { - - drawLabels(c, mViewPortHandler.contentRight() - xoffset, - new PointF(1.0f, 0.5f)); + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentRight() - xoffset, pointF); } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - - drawLabels(c, mViewPortHandler.contentLeft() - xoffset, - new PointF(1.0f, 0.5f)); + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentLeft() - xoffset, pointF); } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { - - drawLabels(c, mViewPortHandler.contentLeft() + xoffset, - new PointF(0.0f, 0.5f)); + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentLeft() + xoffset, pointF); } else { // BOTH SIDED - - drawLabels(c, mViewPortHandler.contentRight() + xoffset, - new PointF(0.0f, 0.5f)); - drawLabels(c, mViewPortHandler.contentLeft() - xoffset, - new PointF(1.0f, 0.5f)); + pointF.x = 0.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentRight() + xoffset, pointF); + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentLeft() - xoffset, pointF); } + + MPPointF.recycleInstance(pointF); } @Override - protected void drawLabels(Canvas c, float pos, PointF anchor) { + protected void drawLabels(Canvas c, float pos, MPPointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); 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 6ccfb09a2a..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 @@ -6,6 +6,7 @@ import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -26,7 +27,7 @@ public void renderAxisLabels(Canvas c) { return; final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); - final PointF drawLabelAnchor = new PointF(0.5f, 0.25f); + final MPPointF drawLabelAnchor = MPPointF.getInstance(0.5f, 0.25f); mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); @@ -38,20 +39,24 @@ public void renderAxisLabels(Canvas c) { // pixels float factor = mChart.getFactor(); - PointF center = mChart.getCenterOffsets(); - + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); for (int i = 0; i < mChart.getData().getMaxEntryCountSet().getEntryCount(); i++) { String label = mXAxis.getValueFormatter().getFormattedValue(i, mXAxis); float angle = (sliceangle * i + mChart.getRotationAngle()) % 360f; - PointF p = Utils.getPosition(center, mChart.getYRange() * factor - + mXAxis.mLabelRotatedWidth / 2f, angle); + Utils.getPosition(center, mChart.getYRange() * factor + + mXAxis.mLabelRotatedWidth / 2f, angle, pOut); - drawLabel(c, label, p.x, p.y - mXAxis.mLabelRotatedHeight / 2.f, + drawLabel(c, label, pOut.x, pOut.y - mXAxis.mLabelRotatedHeight / 2.f, drawLabelAnchor, labelRotationAngleDegrees); } + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); + MPPointF.recycleInstance(drawLabelAnchor); } /** 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 05ac1ab2dd..1393aa2155 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 @@ -7,6 +7,7 @@ import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.components.LimitLine; import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -150,7 +151,8 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); mAxisLabelPaint.setColor(mYAxis.getTextColor()); - PointF center = mChart.getCenterOffsets(); + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); float factor = mChart.getFactor(); int labelCount = mYAxis.mEntryCount; @@ -162,12 +164,14 @@ public void renderAxisLabels(Canvas c) { float r = (mYAxis.mEntries[j] - mYAxis.mAxisMinimum) * factor; - PointF p = Utils.getPosition(center, r, mChart.getRotationAngle()); + Utils.getPosition(center, r, mChart.getRotationAngle(), pOut); String label = mYAxis.getFormattedLabel(j); - c.drawText(label, p.x + 10, p.y, mAxisLabelPaint); + c.drawText(label, pOut.x + 10, pOut.y, mAxisLabelPaint); } + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); } @Override @@ -184,8 +188,8 @@ public void renderLimitLines(Canvas c) { // pixels float factor = mChart.getFactor(); - PointF center = mChart.getCenterOffsets(); - + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); for (int i = 0; i < limitLines.size(); i++) { LimitLine l = limitLines.get(i); @@ -201,19 +205,21 @@ public void renderLimitLines(Canvas c) { Path limitPath = new Path(); + for (int j = 0; j < mChart.getData().getMaxEntryCountSet().getEntryCount(); j++) { - PointF p = Utils.getPosition(center, r, sliceangle * j + mChart.getRotationAngle()); + Utils.getPosition(center, r, sliceangle * j + mChart.getRotationAngle(), pOut); if (j == 0) - limitPath.moveTo(p.x, p.y); + limitPath.moveTo(pOut.x, pOut.y); else - limitPath.lineTo(p.x, p.y); + limitPath.lineTo(pOut.x, pOut.y); } - limitPath.close(); c.drawPath(limitPath, mLimitLinePaint); } + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); } } 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 new file mode 100644 index 0000000000..84f5850862 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java @@ -0,0 +1,83 @@ +package com.github.mikephil.charting.utils; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +/** + * Created by Tony Patino on 6/24/16. + */ +public class MPPointF extends ObjectPool.Poolable { + private static ObjectPool pool; + + public float x; + public float y; + + static { + pool = ObjectPool.create(32, new MPPointF(0,0)); + } + + private MPPointF(float x, float y){ + this.x = x; + this.y = y; + } + + public static MPPointF getInstance(float x, float y){ + MPPointF result = pool.get(); + result.x = x; + result.y = y; + return result; + } + + public static void recycleInstance(MPPointF instance){ + pool.recycle(instance); + } + + public static void recycleInstances(List instances){ + pool.recycle(instances); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + /** + * Return a new point from the data in the specified parcel. + */ + public MPPointF createFromParcel(Parcel in) { + MPPointF r = new MPPointF(0,0); + r.my_readFromParcel(in); + return r; + } + + /** + * Return an array of rectangles of the specified size. + */ + public MPPointF[] newArray(int size) { + return new MPPointF[size]; + } + }; + + /** + * Set the point's coordinates from the data stored in the specified + * parcel. To write a point to a parcel, call writeToParcel(). + * Provided to support older Android devices. + * + * @param in The parcel to read the point's coordinates from + */ + public void my_readFromParcel(Parcel in) { + x = in.readFloat(); + y = in.readFloat(); + } + + public float getX(){ + return this.x; + } + + public float getY(){ + return this.y; + } + + @Override + protected ObjectPool.Poolable instantiate() { + return new MPPointF(0,0); + } +} \ No newline at end of file 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 223ea6d8b3..5ba70d1d1e 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 @@ -397,6 +397,7 @@ public static double nextUp(double d) { } /** + * Returns a recyclable MPPointF instance. * Calculates the position around a center point, depending on the distance * from the center, and the angle of the position around the center. * @@ -405,13 +406,18 @@ public static double nextUp(double d) { * @param angle in degrees, converted to radians internally * @return */ - public static PointF getPosition(PointF center, float dist, float angle) { + public static MPPointF getPosition(MPPointF center, float dist, float angle) { - PointF p = new PointF((float) (center.x + dist * Math.cos(Math.toRadians(angle))), - (float) (center.y + dist * Math.sin(Math.toRadians(angle)))); + MPPointF p = MPPointF.getInstance(0,0); + getPosition(center, dist, angle, p); return p; } + public static void getPosition(MPPointF center, float dist, float angle, MPPointF outputPoint){ + outputPoint.x = (float) (center.x + dist * Math.cos(Math.toRadians(angle))); + outputPoint.y = (float) (center.y + dist * Math.sin(Math.toRadians(angle))); + } + public static void velocityTrackerPointerUpCleanUpIfNecessary(MotionEvent ev, VelocityTracker tracker) { @@ -475,7 +481,7 @@ public static float getNormalizedAngle(float angle) { public static void drawXAxisValue(Canvas c, String text, float x, float y, Paint paint, - PointF anchor, float angleDegrees) { + MPPointF anchor, float angleDegrees) { float drawOffsetX = 0.f; float drawOffsetY = 0.f; @@ -542,7 +548,7 @@ public static void drawXAxisValue(Canvas c, String text, float x, float y, public static void drawMultilineText(Canvas c, StaticLayout textLayout, float x, float y, TextPaint paint, - PointF anchor, float angleDegrees) { + MPPointF anchor, float angleDegrees) { float drawOffsetX = 0.f; float drawOffsetY = 0.f; @@ -620,7 +626,7 @@ public static void drawMultilineText(Canvas c, String text, float x, float y, TextPaint paint, FSize constrainedToSize, - PointF anchor, float angleDegrees) { + MPPointF anchor, float angleDegrees) { StaticLayout textLayout = new StaticLayout( text, 0, text.length(), 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 5ee48a8eed..88dce51460 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 @@ -162,8 +162,8 @@ public RectF getContentRect() { return mContentRect; } - public PointF getContentCenter() { - return new PointF(mContentRect.centerX(), mContentRect.centerY()); + public MPPointF getContentCenter() { + return MPPointF.getInstance(mContentRect.centerX(), mContentRect.centerY()); } public float getChartHeight() { From d946f3e3ff65f337d67ed93318da66e9becdee4d Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Tue, 28 Jun 2016 16:34:49 -0700 Subject: [PATCH 270/606] Eliminate allocs - float array buffers (#1892) Creating field level buffers for frequently instantiated float arrays. Cuts instantiations of many small objects. --- .../charting/charts/BarLineChartBase.java | 11 +++--- .../charting/charts/HorizontalBarChart.java | 5 ++- .../mikephil/charting/charts/PieChart.java | 20 ++++++++--- .../charting/renderer/LineChartRenderer.java | 8 +++-- .../charting/renderer/XAxisRenderer.java | 19 ++++++++-- .../XAxisRendererHorizontalBarChart.java | 14 +++++--- .../charting/renderer/YAxisRenderer.java | 11 ++++-- .../YAxisRendererHorizontalBarChart.java | 12 +++++-- .../mikephil/charting/utils/Transformer.java | 36 ++++++++++++++++--- .../charting/utils/ViewPortHandler.java | 6 +++- 10 files changed, 114 insertions(+), 28 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 de0357d80c..5a2d5c4f56 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 @@ -1004,6 +1004,7 @@ public OnDrawListener getDrawListener() { return mDrawListener; } + protected float[] mGetPositionBuffer = new float[2]; /** * Returns a recyclable MPPointF instance. * Returns the position (in pixels) the provided Entry has inside the chart @@ -1017,9 +1018,9 @@ public MPPointF getPosition(Entry e, AxisDependency axis) { if (e == null) return null; - float[] vals = new float[]{ - e.getX(), e.getY() - }; + float[] vals = mGetPositionBuffer; + vals[0] = e.getX(); + vals[1] = e.getY(); getTransformer(axis).pointValuesToPixel(vals); @@ -1535,11 +1536,13 @@ public Paint getPaint(int which) { return null; } + protected float[] mOnSizeChangedBuffer = new float[2]; @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { // Saving current position of chart. - float[] pts = new float[2]; + float[] pts = mOnSizeChangedBuffer; + pts[0] = pts[1] = 0; if (mKeepPositionOnRotation) { pts[0] = mViewPortHandler.contentLeft(); pts[1] = mViewPortHandler.contentTop(); 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 4fbd2e2d41..adba11e527 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 @@ -165,6 +165,7 @@ public RectF getBarBounds(BarEntry e) { return bounds; } + protected float[] mGetPositionBuffer = new float[2]; /** * Returns a recyclable MPPointF instance. * @@ -178,7 +179,9 @@ public MPPointF getPosition(Entry e, AxisDependency axis) { if (e == null) return null; - float[] vals = new float[]{e.getY(), e.getX()}; + float[] vals = mGetPositionBuffer; + vals[0] = e.getY(); + vals[1] = e.getX(); getTransformer(axis).pointValuesToPixel(vals); 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 416110e9f9..5dfd5263ff 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 @@ -41,12 +41,12 @@ public class PieChart extends PieRadarChartBase { /** * array that holds the width of each pie-slice in degrees */ - private float[] mDrawAngles; + private float[] mDrawAngles = new float[1]; /** * array that holds the absolute angle in degrees of each slice */ - private float[] mAbsoluteAngles; + private float[] mAbsoluteAngles = new float[1]; /** * if true, the white hole inside the chart will be drawn @@ -210,8 +210,20 @@ private void calcAngles() { int entryCount = mData.getEntryCount(); - mDrawAngles = new float[entryCount]; - mAbsoluteAngles = new float[entryCount]; + if(mDrawAngles.length != entryCount) { + mDrawAngles = new float[entryCount]; + }else{ + for(int i = 0 ; i < entryCount ; i++){ + mDrawAngles[i] = 0; + } + } + if(mAbsoluteAngles.length != entryCount) { + mAbsoluteAngles = new float[entryCount]; + }else{ + for(int i = 0 ; i < entryCount ; i++){ + mAbsoluteAngles[i] = 0; + } + } float yValueSum = mData.getYValueSum(); 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 d6fe125cf1..9571f56939 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 @@ -356,8 +356,8 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { } else { // only one color per dataset - if (mLineBuffer.length != Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 2) - mLineBuffer = new float[Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 2]; + if (mLineBuffer.length < Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 2) + mLineBuffer = new float[Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 4]; Entry e1, e2; @@ -527,6 +527,7 @@ public void drawExtras(Canvas c) { } private Path mCirclePathBuffer = new Path(); + private float[] circlesBuffer = new float[2]; protected void drawCircles(Canvas c) { @@ -534,7 +535,8 @@ protected void drawCircles(Canvas c) { float phaseY = mAnimator.getPhaseY(); - float[] circlesBuffer = new float[2]; + circlesBuffer[0] = 0; + circlesBuffer[1] = 0; List dataSets = mChart.getLineData().getDataSets(); 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 46ccff7124..e309065e0e 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 @@ -169,6 +169,7 @@ public void renderAxisLine(Canvas c) { } } + protected float[] mDrawLabelsBuffer = new float[2]; /** * draws the x-labels on the specified y-position * @@ -179,7 +180,10 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); - float[] positions = new float[mXAxis.mEntryCount * 2]; + if(mDrawLabelsBuffer.length != mAxis.mEntryCount * 2){ + mDrawLabelsBuffer = new float[mXAxis.mEntryCount * 2]; + } + float[] positions = mDrawLabelsBuffer; for (int i = 0; i < positions.length; i += 2) { @@ -189,6 +193,8 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { } else { positions[i] = mXAxis.mEntries[i / 2]; } + // init to 0 + positions[i+1] = 0; } mTrans.pointValuesToPixel(positions); @@ -228,13 +234,17 @@ protected void drawLabel(Canvas c, String formattedLabel, float x, float y, MPPo Utils.drawXAxisValue(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees); } + protected float[] mRenderGridLinesBuffer = new float[2]; @Override public void renderGridLines(Canvas c) { if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) return; - float[] positions = new float[mXAxis.mEntryCount * 2]; + if(mRenderGridLinesBuffer.length != mAxis.mEntryCount * 2){ + mRenderGridLinesBuffer = new float[mXAxis.mEntryCount * 2]; + } + float[] positions = mRenderGridLinesBuffer; for (int i = 0; i < positions.length; i += 2) { positions[i] = mXAxis.mEntries[i / 2]; @@ -272,6 +282,7 @@ protected void drawGridLine(Canvas c, float x, float y, Path gridLinePath) { gridLinePath.reset(); } + protected float[] mRenderLimitLinesBuffer = new float[2]; /** * Draws the LimitLines associated with this axis to the screen. * @@ -285,7 +296,9 @@ public void renderLimitLines(Canvas c) { if (limitLines == null || limitLines.size() <= 0) return; - float[] position = new float[2]; + float[] position = mRenderLimitLinesBuffer; + position[0] = 0; + position[1] = 0; for (int i = 0; i < limitLines.size(); i++) { 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 c66ca164cc..4a63c4daf8 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,7 +5,6 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Path; -import android.graphics.PointF; import com.github.mikephil.charting.charts.BarChart; import com.github.mikephil.charting.components.LimitLine; @@ -130,13 +129,17 @@ public void renderAxisLabels(Canvas c) { MPPointF.recycleInstance(pointF); } + protected float[] mDrawLabelsBuffer = new float[2]; @Override protected void drawLabels(Canvas c, float pos, MPPointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); - float[] positions = new float[mXAxis.mEntryCount * 2]; + if(mDrawLabelsBuffer.length != mAxis.mEntryCount * 2){ + mDrawLabelsBuffer = new float[mXAxis.mEntryCount * 2]; + } + float[] positions = mDrawLabelsBuffer; for (int i = 0; i < positions.length; i += 2) { @@ -200,7 +203,7 @@ public void renderAxisLine(Canvas c) { } } - /** + /** * Draws the LimitLines associated with this axis to the screen. * This is the standard YAxis renderer using the XAxis limit lines. * @@ -214,7 +217,10 @@ public void renderLimitLines(Canvas c) { if (limitLines == null || limitLines.size() <= 0) return; - float[] pts = new float[2]; + float[] pts = mRenderLimitLinesBuffer; + pts[0] = 0; + pts[1] = 0; + Path limitLinePath = new Path(); for (int i = 0; i < limitLines.size(); i++) { 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 f626ac752e..7b7285e3d4 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 @@ -171,6 +171,7 @@ protected Path linePath(Path p, int i, float[] positions) { return p; } + protected float[] mGetTransformedPositionsBuffer = new float[2]; /** * Transforms the values contained in the axis entries to screen pixels and returns them in form of a float array * of x- and y-coordinates. @@ -179,7 +180,10 @@ protected Path linePath(Path p, int i, float[] positions) { */ protected float[] getTransformedPositions() { - float[] positions = new float[mYAxis.mEntryCount * 2]; + if(mGetTransformedPositionsBuffer.length != mYAxis.mEntryCount * 2){ + mGetTransformedPositionsBuffer = new float[mYAxis.mEntryCount * 2]; + } + float[] positions = mGetTransformedPositionsBuffer; for (int i = 0; i < positions.length; i += 2) { // only fill y values, x values are not needed for y-labels @@ -210,6 +214,7 @@ protected void drawZeroLine(Canvas c) { c.drawPath(zeroLinePath, mZeroLinePaint); } + protected float[] mRenderLimitLinesBuffer = new float[2]; /** * Draws the LimitLines associated with this axis to the screen. * @@ -223,7 +228,9 @@ public void renderLimitLines(Canvas c) { if (limitLines == null || limitLines.size() <= 0) return; - float[] pts = new float[2]; + float[] pts = mRenderLimitLinesBuffer; + pts[0] = 0; + pts[1] = 0; Path limitLinePath = new Path(); for (int i = 0; i < limitLines.size(); i++) { 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 0f0e707850..08d7f9701e 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 @@ -150,7 +150,10 @@ protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, flo @Override protected float[] getTransformedPositions() { - float[] positions = new float[mYAxis.mEntryCount * 2]; + if(mGetTransformedPositionsBuffer.length != mYAxis.mEntryCount * 2) { + mGetTransformedPositionsBuffer = new float[mYAxis.mEntryCount * 2]; + } + float[] positions = mGetTransformedPositionsBuffer; for (int i = 0; i < positions.length; i += 2) { // only fill x values, y values are not needed for x-labels @@ -188,6 +191,7 @@ protected void drawZeroLine(Canvas c) { c.drawPath(zeroLinePath, mZeroLinePaint); } + protected float[] mRenderLimitLinesBuffer = new float[4]; /** * Draws the LimitLines associated with this axis to the screen. * This is the standard XAxis renderer using the YAxis limit lines. @@ -202,7 +206,11 @@ public void renderLimitLines(Canvas c) { if (limitLines == null || limitLines.size() <= 0) return; - float[] pts = new float[4]; + float[] pts = mRenderLimitLinesBuffer; + pts[0] = 0; + pts[1] = 0; + pts[2] = 0; + pts[3] = 0; Path limitLinePath = new Path(); for (int i = 0; i < limitLines.size(); i++) { 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 2134f5981c..1b07b75c57 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 @@ -93,6 +93,7 @@ public void prepareMatrixOffset(boolean inverted) { // mOffsetBottom); } + protected float[] valuePointsForGenerateTransformedValuesScatter = new float[1]; /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the SCATTERCHART. @@ -105,7 +106,10 @@ public float[] generateTransformedValuesScatter(IScatterDataSet data, float phas final int count = (int) ((to - from) * phaseX + 1) * 2; - float[] valuePoints = new float[count]; + if(valuePointsForGenerateTransformedValuesScatter.length != count){ + valuePointsForGenerateTransformedValuesScatter = new float[count]; + } + float[] valuePoints = valuePointsForGenerateTransformedValuesScatter; for (int j = 0; j < count; j += 2) { @@ -114,6 +118,9 @@ public float[] generateTransformedValuesScatter(IScatterDataSet data, float phas if (e != null) { valuePoints[j] = e.getX(); valuePoints[j + 1] = e.getY() * phaseY; + }else{ + valuePoints[j] = 0; + valuePoints[j + 1] = 0; } } @@ -122,6 +129,7 @@ public float[] generateTransformedValuesScatter(IScatterDataSet data, float phas return valuePoints; } + protected float[] valuePointsForGenerateTransformedValuesBubble = new float[1]; /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the BUBBLECHART. @@ -133,7 +141,10 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY final int count = (to - from + 1) * 2; // (int) Math.ceil((to - from) * phaseX) * 2; - float[] valuePoints = new float[count]; + if(valuePointsForGenerateTransformedValuesBubble.length != count){ + valuePointsForGenerateTransformedValuesBubble = new float[count]; + } + float[] valuePoints = valuePointsForGenerateTransformedValuesBubble; for (int j = 0; j < count; j += 2) { @@ -142,6 +153,9 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY if (e != null) { valuePoints[j] = e.getX(); valuePoints[j + 1] = e.getY() * phaseY; + }else{ + valuePoints[j] = 0; + valuePoints[j + 1] = 0; } } @@ -150,6 +164,7 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY return valuePoints; } + protected float[] valuePointsForGenerateTransformedValuesLine = new float[1]; /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the LINECHART. @@ -162,7 +177,10 @@ public float[] generateTransformedValuesLine(ILineDataSet data, final int count = (int) ((to - from) * phaseX + 1) * 2; - float[] valuePoints = new float[count]; + if(valuePointsForGenerateTransformedValuesLine.length != count){ + valuePointsForGenerateTransformedValuesLine = new float[count]; + } + float[] valuePoints = valuePointsForGenerateTransformedValuesLine; for (int j = 0; j < count; j += 2) { @@ -171,6 +189,9 @@ public float[] generateTransformedValuesLine(ILineDataSet data, if (e != null) { valuePoints[j] = e.getX(); valuePoints[j + 1] = e.getY() * phaseY; + }else{ + valuePoints[j] = 0; + valuePoints[j + 1] = 0; } } @@ -179,6 +200,7 @@ public float[] generateTransformedValuesLine(ILineDataSet data, return valuePoints; } + protected float[] valuePointsForGenerateTransformedValuesCandle = new float[1]; /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the CANDLESTICKCHART. @@ -191,7 +213,10 @@ public float[] generateTransformedValuesCandle(ICandleDataSet data, final int count = (int) ((to - from) * phaseX + 1) * 2; - float[] valuePoints = new float[count]; + if(valuePointsForGenerateTransformedValuesCandle.length != count){ + valuePointsForGenerateTransformedValuesCandle = new float[count]; + } + float[] valuePoints = valuePointsForGenerateTransformedValuesCandle; for (int j = 0; j < count; j += 2) { @@ -200,6 +225,9 @@ public float[] generateTransformedValuesCandle(ICandleDataSet data, if (e != null) { valuePoints[j] = e.getX(); valuePoints[j + 1] = e.getHigh() * phaseY; + }else{ + valuePoints[j] = 0; + valuePoints[j + 1] = 0; } } 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 88dce51460..e4efe5c871 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 @@ -291,6 +291,7 @@ public Matrix setZoom(float scaleX, float scaleY, float x, float y) { return save; } + protected float[] valsBufferForFitScreen = new float[9]; /** * Resets all zooming and dragging and makes the chart fit exactly it's * bounds. @@ -303,7 +304,10 @@ public Matrix fitScreen() { Matrix save = new Matrix(); save.set(mMatrixTouch); - float[] vals = new float[9]; + float[] vals = valsBufferForFitScreen; + for(int i = 0 ; i < 9 ; i++){ + vals[i] = 0; + } save.getValues(vals); From 56cfd2b42b67a23834ca68f8d1faae520010a88a Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Tue, 28 Jun 2016 17:22:48 -0700 Subject: [PATCH 271/606] Eliminate allocs - Matrix and Path buffers (#1892) Created buffers for short lived Matrix and Path objects, resetting them instead of instantiating new ones. Also created some matrix transform methods that accept output Matrix objects to facilitate caching. --- .../charting/charts/BarLineChartBase.java | 18 ++-- .../charting/jobs/AnimatedZoomJob.java | 6 +- .../mikephil/charting/jobs/ZoomJob.java | 6 +- .../charting/renderer/LineChartRenderer.java | 4 +- .../charting/renderer/PieChartRenderer.java | 4 +- .../charting/renderer/RadarChartRenderer.java | 8 +- .../charting/renderer/XAxisRenderer.java | 5 +- .../XAxisRendererHorizontalBarChart.java | 4 +- .../charting/renderer/YAxisRenderer.java | 12 ++- .../YAxisRendererHorizontalBarChart.java | 9 +- .../renderer/YAxisRendererRadarChart.java | 4 +- .../scatter/TriangleShapeRenderer.java | 4 +- .../mikephil/charting/utils/Transformer.java | 4 +- .../charting/utils/ViewPortHandler.java | 99 ++++++++++++------- 14 files changed, 129 insertions(+), 58 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 5a2d5c4f56..642c61e7c9 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 @@ -590,6 +590,7 @@ public void computeScroll() { * VIEWPORT */ + protected Matrix mZoomInMatrixBuffer = new Matrix(); /** * Zooms in by 1.4f, into the charts center. center. */ @@ -597,8 +598,8 @@ public void zoomIn() { MPPointF center = mViewPortHandler.getContentCenter(); - Matrix save = mViewPortHandler.zoomIn(center.x, -center.y); - mViewPortHandler.refresh(save, this, false); + mViewPortHandler.zoomIn(center.x, -center.y, mZoomInMatrixBuffer); + mViewPortHandler.refresh(mZoomInMatrixBuffer, this, false); MPPointF.recycleInstance(center); @@ -609,6 +610,7 @@ public void zoomIn() { postInvalidate(); } + protected Matrix mZoomOutMatrixBuffer = new Matrix(); /** * Zooms out by 0.7f, from the charts center. center. */ @@ -616,8 +618,8 @@ public void zoomOut() { MPPointF center = mViewPortHandler.getContentCenter(); - Matrix save = mViewPortHandler.zoomOut(center.x, -center.y); - mViewPortHandler.refresh(save, this, false); + mViewPortHandler.zoomOut(center.x, -center.y, mZoomOutMatrixBuffer); + mViewPortHandler.refresh(mZoomOutMatrixBuffer, this, false); MPPointF.recycleInstance(center); @@ -628,6 +630,7 @@ public void zoomOut() { postInvalidate(); } + protected Matrix mZoomMatrixBuffer = new Matrix(); /** * Zooms in or out by the given scale factor. x and y are the coordinates * (in pixels) of the zoom center. @@ -638,7 +641,8 @@ public void zoomOut() { * @param y */ public void zoom(float scaleX, float scaleY, float x, float y) { - Matrix save = mViewPortHandler.zoom(scaleX, scaleY, x, y); + Matrix save = mZoomMatrixBuffer; + mViewPortHandler.zoom(scaleX, scaleY, x, y, save); mViewPortHandler.refresh(save, this, false); // Range might have changed, which means that Y-axis labels @@ -694,12 +698,14 @@ public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, floa } } + protected Matrix mFitScreenMatrixBuffer = new Matrix(); /** * Resets all zooming and dragging and makes the chart fit exactly it's * bounds. */ public void fitScreen() { - Matrix save = mViewPortHandler.fitScreen(); + Matrix save = mFitScreenMatrixBuffer; + mViewPortHandler.fitScreen(save); mViewPortHandler.refresh(save, this, false); calculateOffsets(); 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 da22c92507..be61ce09f3 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 @@ -40,13 +40,15 @@ public AnimatedZoomJob(ViewPortHandler viewPortHandler, View v, Transformer tran this.xAxisRange = xAxisRange; } + protected Matrix mOnAnimationUpdateMatrixBuffer = new Matrix(); @Override public void onAnimationUpdate(ValueAnimator animation) { float scaleX = xOrigin + (xValue - xOrigin) * phase; float scaleY = yOrigin + (yValue - yOrigin) * phase; - Matrix save = mViewPortHandler.setZoom(scaleX, scaleY); + Matrix save = mOnAnimationUpdateMatrixBuffer; + mViewPortHandler.setZoom(scaleX, scaleY, save); mViewPortHandler.refresh(save, view, false); float valsInView = yAxis.mAxisRange / mViewPortHandler.getScaleY(); @@ -57,7 +59,7 @@ public void onAnimationUpdate(ValueAnimator animation) { mTrans.pointValuesToPixel(pts); - save = mViewPortHandler.translate(pts); + mViewPortHandler.translate(pts, save); mViewPortHandler.refresh(save, view, true); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java index d9d7e99354..94a8ea1e7b 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java @@ -27,10 +27,12 @@ public ZoomJob(ViewPortHandler viewPortHandler, float scaleX, float scaleY, floa this.axisDependency = axis; } + protected Matrix mRunMatrixBuffer = new Matrix(); @Override public void run() { - Matrix save = mViewPortHandler.zoom(scaleX, scaleY); + Matrix save = mRunMatrixBuffer; + mViewPortHandler.zoom(scaleX, scaleY, save); mViewPortHandler.refresh(save, view, false); float valsInView = ((BarLineChartBase) view).getDeltaY(axisDependency) / mViewPortHandler.getScaleY(); @@ -41,7 +43,7 @@ public void run() { mTrans.pointValuesToPixel(pts); - save = mViewPortHandler.translate(pts); + mViewPortHandler.translate(pts, save); mViewPortHandler.refresh(save, view, false); ((BarLineChartBase) view).calculateOffsets(); 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 9571f56939..49536a025b 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 @@ -431,6 +431,7 @@ protected void drawLinearFill(Canvas c, ILineDataSet dataSet, Transformer trans, } } + protected Path mGenerateFilledPathBuffer = new Path(); /** * Generates the path that is used for filled drawing. * @@ -443,7 +444,8 @@ private Path generateFilledPath(ILineDataSet dataSet, XBounds bounds) { float phaseY = mAnimator.getPhaseY(); final boolean isDrawSteppedEnabled = dataSet.getMode() == LineDataSet.Mode.STEPPED; - Path filled = new Path(); + Path filled = mGenerateFilledPathBuffer; + filled.reset(); Entry entry = dataSet.getEntryForIndex(bounds.min); filled.moveTo(entry.getX(), fillMin); 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 2466078ce4..f6f0c19b00 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 @@ -649,6 +649,7 @@ protected void drawHole(Canvas c) { } } + protected Path mDrawCenterTextPathBuffer = new Path(); /** * draws the description text in the center of the pie chart makes most * sense when center-hole is enabled @@ -705,7 +706,8 @@ protected void drawCenterText(Canvas c) { c.save(); if (Build.VERSION.SDK_INT >= 18) { - Path path = new Path(); + Path path = mDrawCenterTextPathBuffer; + path.reset(); path.addOval(holeRect, Path.Direction.CW); c.clipPath(path); } 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 adeaac5851..3c54aa7919 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 @@ -70,6 +70,7 @@ public void drawData(Canvas c) { } } + protected Path mDrawDataSetSurfacePathBuffer = new Path(); /** * Draws the RadarDataSet * @@ -90,7 +91,8 @@ protected void drawDataSet(Canvas c, IRadarDataSet dataSet, int mostEntries) { MPPointF center = mChart.getCenterOffsets(); MPPointF pOut = MPPointF.getInstance(0,0); - Path surface = new Path(); + Path surface = mDrawDataSetSurfacePathBuffer; + surface.reset(); boolean hasMovedToPoint = false; @@ -321,6 +323,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { MPPointF.recycleInstance(pOut); } + protected Path mDrawHighlightCirclePathBuffer = new Path(); public void drawHighlightCircle(Canvas c, MPPointF point, float innerRadius, @@ -334,7 +337,8 @@ public void drawHighlightCircle(Canvas c, innerRadius = Utils.convertDpToPixel(innerRadius); if (fillColor != ColorTemplate.COLOR_NONE) { - Path p = new Path(); + Path p = mDrawHighlightCirclePathBuffer; + p.reset(); p.addCircle(point.x, point.y, outerRadius, Path.Direction.CW); if (innerRadius > 0.f) { p.addCircle(point.x, point.y, innerRadius, Path.Direction.CCW); 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 e309065e0e..805bdadf8c 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 @@ -233,7 +233,7 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { protected void drawLabel(Canvas c, String formattedLabel, float x, float y, MPPointF anchor, float angleDegrees) { Utils.drawXAxisValue(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees); } - + protected Path mRenderGridLinesPath = new Path(); protected float[] mRenderGridLinesBuffer = new float[2]; @Override public void renderGridLines(Canvas c) { @@ -255,7 +255,8 @@ public void renderGridLines(Canvas c) { setupGridPaint(); - Path gridLinePath = new Path(); + Path gridLinePath = mRenderGridLinesPath; + gridLinePath.reset(); for (int i = 0; i < positions.length; i += 2) { 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 4a63c4daf8..e050af3c13 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 @@ -203,6 +203,7 @@ public void renderAxisLine(Canvas c) { } } + protected Path mRenderLimitLinesPathBuffer = new Path(); /** * Draws the LimitLines associated with this axis to the screen. * This is the standard YAxis renderer using the XAxis limit lines. @@ -221,7 +222,8 @@ public void renderLimitLines(Canvas c) { pts[0] = 0; pts[1] = 0; - Path limitLinePath = new Path(); + Path limitLinePath = mRenderLimitLinesPathBuffer; + limitLinePath.reset(); for (int i = 0; i < limitLines.size(); i++) { 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 7b7285e3d4..b550579d29 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 @@ -125,6 +125,7 @@ protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, flo } } + protected Path mRenderGridLinesPath = new Path(); @Override public void renderGridLines(Canvas c) { @@ -139,7 +140,8 @@ public void renderGridLines(Canvas c) { mGridPaint.setStrokeWidth(mYAxis.getGridLineWidth()); mGridPaint.setPathEffect(mYAxis.getGridDashPathEffect()); - Path gridLinePath = new Path(); + Path gridLinePath = mRenderGridLinesPath; + gridLinePath.reset(); // draw the grid for (int i = 0; i < positions.length; i += 2) { @@ -194,6 +196,7 @@ protected float[] getTransformedPositions() { return positions; } + protected Path mDrawZeroLinePath = new Path(); /** * Draws the zero line. */ @@ -205,7 +208,8 @@ protected void drawZeroLine(Canvas c) { mZeroLinePaint.setColor(mYAxis.getZeroLineColor()); mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth()); - Path zeroLinePath = new Path(); + Path zeroLinePath = mDrawZeroLinePath; + zeroLinePath.reset(); zeroLinePath.moveTo(mViewPortHandler.contentLeft(), (float) pos.y - 1); zeroLinePath.lineTo(mViewPortHandler.contentRight(), (float) pos.y - 1); @@ -214,6 +218,7 @@ protected void drawZeroLine(Canvas c) { c.drawPath(zeroLinePath, mZeroLinePaint); } + protected Path mRenderLimitLines = new Path(); protected float[] mRenderLimitLinesBuffer = new float[2]; /** * Draws the LimitLines associated with this axis to the screen. @@ -231,7 +236,8 @@ public void renderLimitLines(Canvas c) { float[] pts = mRenderLimitLinesBuffer; pts[0] = 0; pts[1] = 0; - Path limitLinePath = new Path(); + Path limitLinePath = mRenderLimitLines; + limitLinePath.reset(); for (int i = 0; i < limitLines.size(); i++) { 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 08d7f9701e..21e16ba325 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 @@ -173,6 +173,8 @@ protected Path linePath(Path p, int i, float[] positions) { return p; } + protected Path mDrawZeroLinePathBuffer = new Path(); + @Override protected void drawZeroLine(Canvas c) { @@ -182,7 +184,8 @@ protected void drawZeroLine(Canvas c) { mZeroLinePaint.setColor(mYAxis.getZeroLineColor()); mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth()); - Path zeroLinePath = new Path(); + Path zeroLinePath = mDrawZeroLinePathBuffer; + zeroLinePath.reset(); zeroLinePath.moveTo((float) pos.x - 1, mViewPortHandler.contentTop()); zeroLinePath.lineTo((float) pos.x - 1, mViewPortHandler.contentBottom()); @@ -191,6 +194,7 @@ protected void drawZeroLine(Canvas c) { c.drawPath(zeroLinePath, mZeroLinePaint); } + protected Path mRenderLimitLinesPathBuffer = new Path(); protected float[] mRenderLimitLinesBuffer = new float[4]; /** * Draws the LimitLines associated with this axis to the screen. @@ -211,7 +215,8 @@ public void renderLimitLines(Canvas c) { pts[1] = 0; pts[2] = 0; pts[3] = 0; - Path limitLinePath = new Path(); + Path limitLinePath = mRenderLimitLinesPathBuffer; + limitLinePath.reset(); for (int i = 0; i < limitLines.size(); i++) { 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 1393aa2155..9a5fd1cd44 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 @@ -174,6 +174,7 @@ public void renderAxisLabels(Canvas c) { MPPointF.recycleInstance(pOut); } + private Path mRenderLimitLinesPathBuffer = new Path(); @Override public void renderLimitLines(Canvas c) { @@ -203,7 +204,8 @@ public void renderLimitLines(Canvas c) { float r = (l.getLimit() - mChart.getYChartMin()) * factor; - Path limitPath = new Path(); + Path limitPath = mRenderLimitLinesPathBuffer; + limitPath.reset(); for (int j = 0; j < mChart.getData().getMaxEntryCountSet().getEntryCount(); j++) { 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 0b5a996bad..789297d170 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 @@ -16,6 +16,7 @@ */ public class TriangleShapeRenderer implements ShapeRenderer { + protected Path mTrianglePathBuffer = new Path(); @Override public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, ScatterBuffer buffer, Paint @@ -31,7 +32,8 @@ public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewP renderPaint.setStyle(Paint.Style.FILL); // create a triangle path - Path tri = new Path(); + Path tri = mTrianglePathBuffer; + tri.reset(); for (int i = 0; i < buffer.size(); i += 2) { 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 1b07b75c57..e842084936 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 @@ -356,6 +356,7 @@ public void rectValuesToPixel(List rects) { m.mapRect(rects.get(i)); } + protected Matrix mPixelsToValueMatrixBuffer = new Matrix(); /** * Transforms the given array of touch positions (pixels) (x, y, x, y, ...) * into values on the chart. @@ -364,7 +365,8 @@ public void rectValuesToPixel(List rects) { */ public void pixelsToValue(float[] pixels) { - Matrix tmp = new Matrix(); + Matrix tmp = mPixelsToValueMatrixBuffer; + tmp.reset(); // invert all matrixes to convert back to the original value mMatrixOffset.invert(tmp); 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 e4efe5c871..9aa8109054 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 @@ -198,13 +198,16 @@ public float getSmallestContentExtension() { public Matrix zoomIn(float x, float y) { Matrix save = new Matrix(); - save.set(mMatrixTouch); - - save.postScale(1.4f, 1.4f, x, y); - + zoomIn(x,y,save); return save; } + public void zoomIn(float x, float y, Matrix outputMatrix){ + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(1.4f, 1.4f, x, y); + } + /** * Zooms out by 0.7f, x and y are the coordinates (in pixels) of the zoom * center. @@ -212,13 +215,16 @@ public Matrix zoomIn(float x, float y) { public Matrix zoomOut(float x, float y) { Matrix save = new Matrix(); - save.set(mMatrixTouch); - - save.postScale(0.7f, 0.7f, x, y); - + zoomOut(x,y,save); return save; } + public void zoomOut(float x, float y, Matrix outputMatrix){ + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(0.7f, 0.7f, x, y); + } + /** * Post-scales by the specified scale factors. * @@ -229,13 +235,16 @@ public Matrix zoomOut(float x, float y) { public Matrix zoom(float scaleX, float scaleY) { Matrix save = new Matrix(); - save.set(mMatrixTouch); - - save.postScale(scaleX, scaleY); - + zoom(scaleX,scaleY,save); return save; } + public void zoom(float scaleX, float scaleY, Matrix outputMatrix){ + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(scaleX, scaleY); + } + /** * Post-scales by the specified scale factors. x and y is pivot. * @@ -248,13 +257,16 @@ public Matrix zoom(float scaleX, float scaleY) { public Matrix zoom(float scaleX, float scaleY, float x, float y) { Matrix save = new Matrix(); - save.set(mMatrixTouch); - - save.postScale(scaleX, scaleY, x, y); - + zoom(scaleX,scaleY,x,y,save); return save; } + public void zoom(float scaleX, float scaleY, float x, float y, Matrix outputMatrix){ + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(scaleX,scaleY,x,y); + } + /** * Sets the scale factor to the specified values. * @@ -265,13 +277,16 @@ public Matrix zoom(float scaleX, float scaleY, float x, float y) { public Matrix setZoom(float scaleX, float scaleY) { Matrix save = new Matrix(); - save.set(mMatrixTouch); - - save.setScale(scaleX, scaleY); - + setZoom(scaleX,scaleY,save); return save; } + public void setZoom(float scaleX, float scaleY, Matrix outputMatrix){ + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.setScale(scaleX,scaleY); + } + /** * Sets the scale factor to the specified values. x and y is pivot. * @@ -292,24 +307,34 @@ public Matrix setZoom(float scaleX, float scaleY, float x, float y) { } protected float[] valsBufferForFitScreen = new float[9]; + /** * Resets all zooming and dragging and makes the chart fit exactly it's * bounds. */ public Matrix fitScreen() { + Matrix save = new Matrix(); + fitScreen(save); + return save; + } + + /** + * Resets all zooming and dragging and makes the chart fit exactly it's + * bounds. Output Matrix is available for those who wish to cache the object. + */ + public void fitScreen(Matrix outputMatrix){ mMinScaleX = 1f; mMinScaleY = 1f; - Matrix save = new Matrix(); - save.set(mMatrixTouch); + outputMatrix.set(mMatrixTouch); float[] vals = valsBufferForFitScreen; for(int i = 0 ; i < 9 ; i++){ vals[i] = 0; } - save.getValues(vals); + outputMatrix.getValues(vals); // reset all translations and scaling vals[Matrix.MTRANS_X] = 0f; @@ -317,13 +342,11 @@ public Matrix fitScreen() { vals[Matrix.MSCALE_X] = 1f; vals[Matrix.MSCALE_Y] = 1f; - save.setValues(vals); - - return save; + outputMatrix.setValues(vals); } /** - * Post-translates to the specified points. + * Post-translates to the specified points. Less Performant. * * @param transformedPts * @return @@ -331,16 +354,25 @@ public Matrix fitScreen() { public Matrix translate(final float[] transformedPts) { Matrix save = new Matrix(); - save.set(mMatrixTouch); + translate(transformedPts, save); + return save; + } + /** + * Post-translates to the specified points. Output matrix allows for caching objects. + * + * @param transformedPts + * @return + */ + public void translate(final float[] transformedPts, Matrix outputMatrix){ + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); final float x = transformedPts[0] - offsetLeft(); final float y = transformedPts[1] - offsetTop(); - - save.postTranslate(-x, -y); - - return save; + outputMatrix.postTranslate(-x, -y); } + protected Matrix mCenterViewPortMatrixBuffer = new Matrix(); /** * Centers the viewport around the specified position (x-index and y-value) * in the chart. Centering the viewport outside the bounds of the chart is @@ -353,7 +385,8 @@ public Matrix translate(final float[] transformedPts) { */ public void centerViewPort(final float[] transformedPts, final View view) { - Matrix save = new Matrix(); + Matrix save = mCenterViewPortMatrixBuffer; + save.reset(); save.set(mMatrixTouch); final float x = transformedPts[0] - offsetLeft(); From c59b46affc1afdaed31b437caad47617324229be Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Tue, 28 Jun 2016 17:39:42 -0700 Subject: [PATCH 272/606] Eliminate allocs - Cache drawn circles as bitmaps (#1892) LineChartRenderer now caches drawn circles as bitmaps. ILineDataSet now has a method that returns the number of available colors in the set. --- .../mikephil/charting/data/LineDataSet.java | 4 + .../interfaces/datasets/ILineDataSet.java | 7 + .../charting/renderer/LineChartRenderer.java | 132 ++++++++++++++---- 3 files changed, 114 insertions(+), 29 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 d0d7cb3e82..2b802d9c28 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 @@ -252,6 +252,10 @@ public int getCircleColor(int index) { return mCircleColors.get(index % mCircleColors.size()); } + public int getCircleColorCount(){ + return mCircleColors.size(); + } + /** * Sets the colors that should be used for the circles of this DataSet. * Colors are reused as soon as the number of Entries the DataSet represents 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 6f62274b8e..bccf6984d5 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 @@ -52,6 +52,13 @@ public interface ILineDataSet extends ILineRadarDataSet { */ int getCircleColor(int index); + /** + * Returns the number of colors in this DataSet's circle-color array. + * + * @return + */ + int getCircleColorCount(); + /** * Returns true if drawing circles for this DataSet is enabled, false if not * 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 49536a025b..2c433fa15f 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 @@ -14,6 +14,7 @@ import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.PointD; @@ -21,10 +22,42 @@ import com.github.mikephil.charting.utils.ViewPortHandler; import java.lang.ref.WeakReference; +import java.util.HashMap; import java.util.List; public class LineChartRenderer extends LineRadarRenderer { + private class DataSetImageCache { + + + private Bitmap[] circleBitmaps; + private int[] circleColors; + + private void ensureCircleCache(int size){ + if(circleBitmaps == null){ + circleBitmaps = new Bitmap[size]; + }else if(circleBitmaps.length < size){ + Bitmap[] tmp = new Bitmap[size]; + for(int i = 0 ; i < circleBitmaps.length ; i++){ + tmp[i] = circleBitmaps[size]; + } + circleBitmaps = tmp; + } + + if(circleColors == null){ + circleColors = new int[size]; + }else if(circleColors.length < size){ + int[] tmp = new int[size]; + for(int i = 0 ; i < circleColors.length ; i++){ + tmp[i] = circleColors[size]; + } + circleColors = tmp; + } + } + + } + + protected LineDataProvider mChart; /** @@ -300,8 +333,8 @@ 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 * 2]; + if (mLineBuffer.length <= pointsPerEntryPair * 2) + mLineBuffer = new float[pointsPerEntryPair * 4]; for (int j = bounds.min; j <= bounds.range + bounds.min; j++) { @@ -529,23 +562,34 @@ public void drawExtras(Canvas c) { } private Path mCirclePathBuffer = new Path(); - private float[] circlesBuffer = new float[2]; - + private float[] mCirclesBuffer = new float[2]; + private HashMap mImageCaches = new HashMap<>(); protected void drawCircles(Canvas c) { mRenderPaint.setStyle(Paint.Style.FILL); float phaseY = mAnimator.getPhaseY(); + float[] circlesBuffer = mCirclesBuffer; circlesBuffer[0] = 0; circlesBuffer[1] = 0; List dataSets = mChart.getLineData().getDataSets(); - - for (int i = 0; i < dataSets.size(); i++) { + final int dataSetCount = dataSets.size(); + for (int i = 0; i < dataSetCount ; i++) { ILineDataSet dataSet = dataSets.get(i); + DataSetImageCache imageCache; + + if(mImageCaches.containsKey(dataSet)){ + imageCache = mImageCaches.get(dataSet); + }else{ + imageCache = new DataSetImageCache(); + mImageCaches.put(dataSet, imageCache); + } + imageCache.ensureCircleCache(dataSet.getCircleColorCount()); + if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled() || dataSet.getEntryCount() == 0) continue; @@ -555,6 +599,7 @@ protected void drawCircles(Canvas c) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); XBounds bounds = getXBounds(mChart, dataSet); + int entryCount = dataSet.getEntryCount(); float circleRadius = dataSet.getCircleRadius(); float circleHoleRadius = dataSet.getCircleHoleRadius(); @@ -564,8 +609,8 @@ protected void drawCircles(Canvas c) { boolean drawTransparentCircleHole = drawCircleHole && dataSet.getCircleHoleColor() == ColorTemplate.COLOR_NONE; - for (int j = bounds.min; j <= bounds.range + bounds.min; j++) { - + int boundsRangeCount = bounds.range + bounds.min; + for (int j = bounds.min; j <= boundsRangeCount; j++) { Entry e = dataSet.getEntryForIndex(j); if (e == null) break; @@ -584,37 +629,66 @@ protected void drawCircles(Canvas c) { !mViewPortHandler.isInBoundsY(circlesBuffer[1])) continue; - mRenderPaint.setColor(dataSet.getCircleColor(j)); - - if (drawTransparentCircleHole) { + final int circleColor = dataSet.getCircleColor(j); + mRenderPaint.setColor(circleColor); - // Begin path for circle with hole - mCirclePathBuffer.reset(); + Bitmap circleBitmap = null; - mCirclePathBuffer.addCircle(circlesBuffer[0], circlesBuffer[1], - circleRadius, - Path.Direction.CW); + final int dataSetColorCount = imageCache.circleColors.length; + int colorIndex; + for(colorIndex = 0 ; colorIndex < dataSetColorCount ; colorIndex++) { + int tempColor = imageCache.circleColors[colorIndex]; + Bitmap tempBitmap = imageCache.circleBitmaps[colorIndex]; + if(tempColor == circleColor) { + circleBitmap = tempBitmap; + break; + }else if(tempBitmap == null){ + break; + } + } - // Cut hole in path - mCirclePathBuffer.addCircle(circlesBuffer[0], circlesBuffer[1], - circleHoleRadius, - Path.Direction.CCW); - // Fill in-between - c.drawPath(mCirclePathBuffer, mRenderPaint); + if(circleBitmap == null){ + Bitmap.Config conf = Bitmap.Config.ARGB_8888; + circleBitmap = Bitmap.createBitmap((int)circleRadius * 2, (int)circleRadius * 2, conf); + Canvas canvas = new Canvas(circleBitmap); + imageCache.circleBitmaps[colorIndex] = circleBitmap; + imageCache.circleColors[colorIndex] = circleColor; - } else { + if(drawTransparentCircleHole){ + // Begin path for circle with hole + mCirclePathBuffer.reset(); - c.drawCircle(circlesBuffer[0], circlesBuffer[1], - circleRadius, - mRenderPaint); + mCirclePathBuffer.addCircle(circleRadius, circleRadius, + circleRadius, + Path.Direction.CW); - if (drawCircleHole) { - c.drawCircle(circlesBuffer[0], circlesBuffer[1], + // Cut hole in path + mCirclePathBuffer.addCircle(circleHoleRadius, circleHoleRadius, circleHoleRadius, - mCirclePaintInner); + Path.Direction.CCW); + + // Fill in-between + canvas.drawPath(mCirclePathBuffer, mRenderPaint); + }else{ + + canvas.drawCircle(circleRadius, circleRadius, + circleRadius, + mRenderPaint); + + if (drawCircleHole) { + canvas.drawCircle(circleRadius, circleRadius, + circleHoleRadius, + mCirclePaintInner); + } } } + + if(circleBitmap != null){ + + c.drawBitmap(circleBitmap, circlesBuffer[0] - circleRadius, circlesBuffer[1] - circleRadius, mRenderPaint); + + } } } } From bd45d73bc6fc6e24d5bf31fbf0072fc6fdd8e247 Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Tue, 28 Jun 2016 18:18:49 -0700 Subject: [PATCH 273/606] Eliminate allocs - Array Access (#1892) foreach(:) arrays on Android carry the cost that each iteration creates an implicit instantiation. for(;;) arrays are uglier but guarantee that no unwanted allocations occur while iterating. --- .../mikephil/charting/charts/Chart.java | 6 +- .../mikephil/charting/data/BarData.java | 5 +- .../mikephil/charting/data/BubbleData.java | 5 +- .../mikephil/charting/data/ChartData.java | 87 +++++++++++++++---- .../mikephil/charting/data/ScatterData.java | 6 +- .../renderer/BubbleChartRenderer.java | 7 +- .../renderer/CandleStickChartRenderer.java | 6 +- .../charting/renderer/LineChartRenderer.java | 5 +- .../charting/renderer/PieChartRenderer.java | 7 +- .../charting/renderer/RadarChartRenderer.java | 8 +- .../renderer/ScatterChartRenderer.java | 5 +- 11 files changed, 118 insertions(+), 29 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 8540e6032a..6807a57c08 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 @@ -306,7 +306,11 @@ public void setData(T data) { // calculate how many digits are needed setupDefaultFormatter(data.getYMin(), data.getYMax()); - for (IDataSet set : mData.getDataSets()) { + IDataSet set; + final List sets = mData.getDataSets(); + final int count = sets.size(); + for(int i = 0 ; i < count ; i++){ + set = (IDataSet)sets.get(i); 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 2cc22c8b72..609d480d32 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,7 +74,10 @@ public void groupBars(float fromX, float groupSpace, float barSpace) { float start = fromX; fromX += groupSpaceWidthHalf; - for (IBarDataSet set : mDataSets) { + IBarDataSet set; + final int setCountJ = mDataSets.size(); + for(int j = 0 ; j < setCountJ ; j++){ + set = mDataSets.get(j); 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 0ce345bc90..c7522599a9 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 @@ -28,7 +28,10 @@ public BubbleData(List dataSets) { * @param width */ public void setHighlightCircleWidth(float width) { - for (IBubbleDataSet set : mDataSets) { + IBubbleDataSet set; + final int setCount = mDataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = mDataSets.get(i); 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 fa66f4744e..0d243c6a1c 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 @@ -80,9 +80,12 @@ public ChartData(T... dataSets) { */ private List arrayToList(T[] array) { - List list = new ArrayList<>(); + int setsCount = array.length; + List list = new ArrayList<>(setsCount); - for (T set : array) { + T set = null; + for(int i = 0 ; i < setsCount ; i++){ + set = array[i]; list.add(set); } @@ -129,7 +132,10 @@ public void calcMinMax() { mXMax = -Float.MAX_VALUE; mXMin = Float.MAX_VALUE; - for (T set : mDataSets) { + T set; + int setCount = mDataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = mDataSets.get(i); calcMinMax(set); } @@ -146,7 +152,10 @@ public void calcMinMax() { mLeftAxisMax = firstLeft.getYMax(); mLeftAxisMin = firstLeft.getYMin(); - for (IDataSet dataSet : mDataSets) { + IDataSet dataSet; + int dataSetsCount = mDataSets.size(); + for(int i = 0 ; i < dataSetsCount ; i++){ + dataSet = mDataSets.get(i); if (dataSet.getAxisDependency() == AxisDependency.LEFT) { if (dataSet.getYMin() < mLeftAxisMin) mLeftAxisMin = dataSet.getYMin(); @@ -165,7 +174,10 @@ public void calcMinMax() { mRightAxisMax = firstRight.getYMax(); mRightAxisMin = firstRight.getYMin(); - for (IDataSet dataSet : mDataSets) { + IDataSet dataSet; + int dataSetsCount = mDataSets.size(); + for(int i = 0 ; i < dataSetsCount ; i++){ + dataSet = mDataSets.get(i); if (dataSet.getAxisDependency() == AxisDependency.RIGHT) { if (dataSet.getYMin() < mRightAxisMin) mRightAxisMin = dataSet.getYMin(); @@ -614,7 +626,10 @@ public int getIndexOfDataSet(T dataSet) { * @return */ protected T getFirstLeft(List sets) { - for (T dataSet : sets) { + T dataSet; + int setCount = sets.size(); + for(int i = 0 ; i < setCount ; i++){ + dataSet = sets.get(i); if (dataSet.getAxisDependency() == AxisDependency.LEFT) return dataSet; } @@ -628,7 +643,10 @@ protected T getFirstLeft(List sets) { * @return */ public T getFirstRight(List sets) { - for (T dataSet : sets) { + T dataSet; + int setCount = sets.size(); + for(int i = 0 ; i < setCount ; i++){ + dataSet = sets.get(i); if (dataSet.getAxisDependency() == AxisDependency.RIGHT) return dataSet; } @@ -644,7 +662,10 @@ public void setValueFormatter(ValueFormatter f) { if (f == null) return; else { - for (IDataSet set : mDataSets) { + IDataSet set; + final int setCount = mDataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = mDataSets.get(i); set.setValueFormatter(f); } } @@ -657,7 +678,10 @@ public void setValueFormatter(ValueFormatter f) { * @param color */ public void setValueTextColor(int color) { - for (IDataSet set : mDataSets) { + IDataSet set; + final int setCount = mDataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = mDataSets.get(i); set.setValueTextColor(color); } } @@ -669,7 +693,10 @@ public void setValueTextColor(int color) { * @param colors */ public void setValueTextColors(List colors) { - for (IDataSet set : mDataSets) { + IDataSet set; + final int setCount = mDataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = mDataSets.get(i); set.setValueTextColors(colors); } } @@ -681,7 +708,10 @@ public void setValueTextColors(List colors) { * @param tf */ public void setValueTypeface(Typeface tf) { - for (IDataSet set : mDataSets) { + IDataSet set; + final int setCount = mDataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = mDataSets.get(i); set.setValueTypeface(tf); } } @@ -693,7 +723,10 @@ public void setValueTypeface(Typeface tf) { * @param size */ public void setValueTextSize(float size) { - for (IDataSet set : mDataSets) { + IDataSet set; + final int setCount = mDataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = mDataSets.get(i); set.setValueTextSize(size); } } @@ -705,7 +738,10 @@ public void setValueTextSize(float size) { * @param enabled */ public void setDrawValues(boolean enabled) { - for (IDataSet set : mDataSets) { + IDataSet set; + final int setCount = mDataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = mDataSets.get(i); set.setDrawValues(enabled); } } @@ -716,7 +752,10 @@ public void setDrawValues(boolean enabled) { * be highlighted programmatically or by touch gesture. */ public void setHighlightEnabled(boolean enabled) { - for (IDataSet set : mDataSets) { + IDataSet set; + final int setCount = mDataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = mDataSets.get(i); set.setHighlightEnabled(enabled); } } @@ -728,7 +767,10 @@ public void setHighlightEnabled(boolean enabled) { * @return */ public boolean isHighlightEnabled() { - for (IDataSet set : mDataSets) { + IDataSet set; + final int setCount = mDataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = mDataSets.get(i); if (!set.isHighlightEnabled()) return false; } @@ -753,7 +795,10 @@ public void clearValues() { */ public boolean contains(T dataSet) { - for (T set : mDataSets) { + T set; + int setCount = mDataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = mDataSets.get(i); if (set.equals(dataSet)) return true; } @@ -770,7 +815,10 @@ public int getEntryCount() { int count = 0; - for (T set : mDataSets) { + T set; + int setCount = mDataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = mDataSets.get(i); count += set.getEntryCount(); } @@ -789,7 +837,10 @@ public T getMaxEntryCountSet() { T max = mDataSets.get(0); - for (T set : mDataSets) { + T set; + int setCount = mDataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = mDataSets.get(i); 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 fef13c20fd..f34c5ae3a3 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 @@ -29,7 +29,11 @@ public float getGreatestShapeSize() { float max = 0f; - for (IScatterDataSet set : mDataSets) { + + IScatterDataSet set; + final int count = mDataSets.size(); + for(int i = 0 ; i < count ; i++){ + set = mDataSets.get(i); 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 18884fd7ce..0712c0f84a 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 @@ -10,6 +10,7 @@ import com.github.mikephil.charting.data.BubbleEntry; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; @@ -46,7 +47,11 @@ public void drawData(Canvas c) { BubbleData bubbleData = mChart.getBubbleData(); - for (IBubbleDataSet set : bubbleData.getDataSets()) { + IBubbleDataSet set; + List dataSets = bubbleData.getDataSets(); + int setCount = dataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = dataSets.get(i); if (set.isVisible() && set.getEntryCount() > 0) 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 294dcedf74..9a1842221e 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,7 +44,11 @@ public void drawData(Canvas c) { CandleData candleData = mChart.getCandleData(); - for (ICandleDataSet set : candleData.getDataSets()) { + ICandleDataSet set; + List dataSets = candleData.getDataSets(); + int setCount = dataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = dataSets.get(i); if (set.isVisible() && set.getEntryCount() > 0) 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 2c433fa15f..e099af1b88 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 @@ -122,7 +122,10 @@ public void drawData(Canvas c) { LineData lineData = mChart.getLineData(); - for (ILineDataSet set : lineData.getDataSets()) { + ILineDataSet set; + int setCount = lineData.getDataSets().size(); + for(int i = 0 ; i < setCount ; i++){ + set = lineData.getDataSets().get(i); if (set.isVisible() && set.getEntryCount() > 0) 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 f6f0c19b00..354ea5d4ec 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,8 +142,11 @@ public void drawData(Canvas c) { PieData pieData = mChart.getData(); - for (IPieDataSet set : pieData.getDataSets()) { - + IPieDataSet set; + int setCount = pieData.getDataSets().size(); + List dataSet = pieData.getDataSets(); + for(int i = 0 ; i < setCount ; i++){ + set = dataSet.get(i); 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 3c54aa7919..c346c6f6a3 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 @@ -19,6 +19,8 @@ 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; @@ -62,7 +64,11 @@ public void drawData(Canvas c) { int mostEntries = radarData.getMaxEntryCountSet().getEntryCount(); - for (IRadarDataSet set : radarData.getDataSets()) { + IRadarDataSet set; + List dataSets = radarData.getDataSets(); + int setCount = dataSets.size(); + for(int i = 0 ; i < setCount ; i++){ + set = dataSets.get(i); if (set.isVisible() && set.getEntryCount() > 0) { 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 c9ffbaf3cc..026f975b57 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 @@ -47,7 +47,10 @@ public void drawData(Canvas c) { ScatterData scatterData = mChart.getScatterData(); - for (IScatterDataSet set : scatterData.getDataSets()) { + IScatterDataSet set; + int setCount = scatterData.getDataSets().size(); + for(int i = 0 ; i < setCount ; i++){ + set = scatterData.getDataSets().get(i); if (set.isVisible()) drawDataSet(c, set); From 0b72b5588b1770af9ddb756f20b4b4e7169ecf5b Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Wed, 29 Jun 2016 16:43:33 -0700 Subject: [PATCH 274/606] Eliminate allocs - Cache formatted Strings (#1892) ValueFormatter objects now rely on a FormattedStringCache to return already-formatted Strings. We might want to create primitive-enabled versions since all our values to format are float or double primitives, and the keys are also all primitives. This will eliminate auto-boxing penalties. --- .../BarChartPositiveNegative.java | 7 +-- .../mpchartexample/LineChartTime.java | 6 ++- .../StackedBarActivityNegative.java | 17 ++++--- .../custom/MyAxisValueFormatter.java | 6 ++- .../custom/MyCustomXAxisValueFormatter.java | 14 ++--- .../custom/MyValueFormatter.java | 7 +-- .../custom/RadarMarkerView.java | 6 ++- .../formatter/DefaultAxisValueFormatter.java | 12 +++-- .../formatter/DefaultValueFormatter.java | 11 ++-- .../formatter/FormattedStringCache.java | 51 +++++++++++++++++++ .../formatter/LargeValueFormatter.java | 12 +++-- .../charting/formatter/PercentFormatter.java | 14 +++-- .../formatter/StackedValueFormatter.java | 22 +++++--- 13 files changed, 136 insertions(+), 49 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FormattedStringCache.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index 1c22ae41c6..a080de8af6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -16,6 +16,7 @@ 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; @@ -164,15 +165,15 @@ public Data(float xValue, float yValue, String xAxisValue) { private class ValueFormatter implements com.github.mikephil.charting.formatter.ValueFormatter { - private DecimalFormat mFormat; + private FormattedStringCache mFormattedStringCache; public ValueFormatter() { - mFormat = new DecimalFormat("######.0"); + mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("######.0")); } @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormat.format(value); + return mFormattedStringCache.getFormattedString(value, dataSetIndex); } } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index 7d833625aa..c7893188a3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -21,6 +21,7 @@ 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; @@ -91,11 +92,12 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setGranularity(60000L); // one minute in millis xAxis.setValueFormatter(new AxisValueFormatter() { - private SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm"); + private FormattedStringCache mFormattedStringCache = new FormattedStringCache(new SimpleDateFormat("dd MMM HH:mm")); @Override public String getFormattedValue(float value, AxisBase axis) { - return mFormat.format(new Date((long) value)); + Long v = (long) value; + return mFormattedStringCache.getFormattedString(new Date(v), v); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index 200c012771..b1a54cbef1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -20,6 +20,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.FormattedStringCache; import com.github.mikephil.charting.formatter.ValueFormatter; import com.github.mikephil.charting.formatter.AxisValueFormatter; import com.github.mikephil.charting.highlight.Highlight; @@ -79,11 +80,12 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setGranularity(10f); xAxis.setValueFormatter(new AxisValueFormatter() { - private DecimalFormat format = new DecimalFormat("###"); + private FormattedStringCache format = new FormattedStringCache(new DecimalFormat("###")); @Override public String getFormattedValue(float value, AxisBase axis) { - return format.format(value) + "-" + format.format(value + 10); + Float key = value + 10; + return format.getFormattedString(value, value) + "-" + format.getFormattedString(value, key); } @Override @@ -222,22 +224,25 @@ public void onNothingSelected() { private class CustomFormatter implements ValueFormatter, AxisValueFormatter { - private DecimalFormat mFormat; + private FormattedStringCache mFormatValue; + private FormattedStringCache mFormatAxis; public CustomFormatter() { - mFormat = new DecimalFormat("###"); + mFormatValue = new FormattedStringCache<>(new DecimalFormat("###")); + mFormatAxis = new FormattedStringCache<>(new DecimalFormat("###")); } // data @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormat.format(Math.abs(value)) + "m"; + return mFormatValue.getFormattedString(value, dataSetIndex) + "m"; } // YAxis @Override public String getFormattedValue(float value, AxisBase axis) { - return mFormat.format(Math.abs(value)) + "m"; + Float v = Math.abs(value); + return mFormatAxis.getFormattedString(v,v) + "m"; } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java index ea50fd8fad..06197d7508 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java @@ -2,20 +2,22 @@ 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 mFormattedStringCache; public MyAxisValueFormatter() { - mFormat = new DecimalFormat("###,###,###,##0.0"); + mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0.0")); } @Override public String getFormattedValue(float value, AxisBase axis) { - return mFormat.format(value) + " $"; + return mFormattedStringCache.getFormattedString(value, value) + " $"; } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index 16d5752905..f2018bcdd4 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -2,6 +2,7 @@ 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; @@ -11,13 +12,13 @@ */ public class MyCustomXAxisValueFormatter implements AxisValueFormatter { - private DecimalFormat mFormat; + private FormattedStringCache mFormattedStringCache; private ViewPortHandler mViewPortHandler; public MyCustomXAxisValueFormatter(ViewPortHandler viewPortHandler) { mViewPortHandler = viewPortHandler; // maybe do something here or provide parameters in constructor - mFormat = new DecimalFormat("###,###,###,##0.0"); + mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0.0")); } @Override @@ -26,14 +27,15 @@ public String getFormattedValue(float value, AxisBase axis) { //Log.i("TRANS", "x: " + viewPortHandler.getTransX() + ", y: " + viewPortHandler.getTransY()); // e.g. adjust the x-axis values depending on scale / zoom level - if (mViewPortHandler.getScaleX() > 5) + final float xScale = mViewPortHandler.getScaleX(); + if (xScale > 5) return "4"; - else if (mViewPortHandler.getScaleX() > 3) + else if (xScale > 3) return "3"; - else if (mViewPortHandler.getScaleX() > 1) + else if (xScale > 1) return "2"; else - return mFormat.format(value); + return mFormattedStringCache.getFormattedString(value, value); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java index a374b60e7f..87cf28f4c3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java @@ -1,6 +1,7 @@ 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; @@ -8,14 +9,14 @@ public class MyValueFormatter implements ValueFormatter { - private DecimalFormat mFormat; + private FormattedStringCache mFormattedStringCache; public MyValueFormatter() { - mFormat = new DecimalFormat("###,###,###,##0.0"); + mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0.0")); } @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormat.format(value) + " $"; + return mFormattedStringCache.getFormattedString(value, dataSetIndex) + " $"; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java index b86e8ba603..b02212d89a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java @@ -8,6 +8,7 @@ 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.Utils; import com.xxmassdeveloper.mpchartexample.R; @@ -22,7 +23,7 @@ public class RadarMarkerView extends MarkerView { private TextView tvContent; - private DecimalFormat format = new DecimalFormat("##0"); + private FormattedStringCache mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("##0")); public RadarMarkerView(Context context, int layoutResource) { super(context, layoutResource); @@ -35,7 +36,8 @@ public RadarMarkerView(Context context, int layoutResource) { // content (user-interface) @Override public void refreshContent(Entry e, Highlight highlight) { - tvContent.setText(format.format(e.getY()) + " %"); + float value = e.getY(); + tvContent.setText(mFormattedStringCache.getFormattedString(value, value) + " %"); } @Override 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..3369e00693 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 { /** - * decimalformat for formatting + * FormattedStringFormat for formatting and caching */ - protected DecimalFormat mFormat; + protected FormattedStringCache mFormattedStringCache; /** * the number of decimal digits this formatter uses @@ -35,13 +35,15 @@ public DefaultAxisValueFormatter(int digits) { b.append("0"); } - mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); + mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0" + b.toString())); } @Override public String getFormattedValue(float value, AxisBase axis) { - // avoid memory allocations here (for performance) - return mFormat.format(value); + + // TODO: There should be a better way to do this. Floats are not the best keys... + return mFormattedStringCache.getFormattedString(value, 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 5d23d25f7e..039787113c 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 { /** - * DecimalFormat for formatting + * FormattedStringCache for formatting and caching. */ - protected DecimalFormat mFormat; + protected FormattedStringCache mFormattedStringCache; protected int mDecimalDigits; @@ -47,16 +47,15 @@ public void setup(int digits) { b.append("0"); } - mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); + mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0" + b.toString())); + } @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - // put more logic here ... - // avoid memory allocations here (for performance reasons) + return mFormattedStringCache.getFormattedString(value, dataSetIndex); - 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 new file mode 100644 index 0000000000..047af77571 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FormattedStringCache.java @@ -0,0 +1,51 @@ +package com.github.mikephil.charting.formatter; + +import java.text.Format; +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 { + + private Format mFormat; + private HashMap mCachedStringsHashMap = new HashMap<>(); + private HashMap mCachedValuesHashMap = new HashMap<>(); + + public Format getFormat(){ + return mFormat; + } + + public FormattedStringCache(Format format){ + this.mFormat = format; + } + + public String getFormattedString(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; + } + +} 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..5679c9a2aa 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,11 +23,16 @@ 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 mFormattedStringCache; + public LargeValueFormatter() { - mFormat = new DecimalFormat("###E00"); + mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###E00")); } /** @@ -77,7 +82,8 @@ public void setSuffix(String[] suff) { */ private String makePretty(double number) { - String r = mFormat.format(number); + // TODO : Should be better way to do this. Double isn't the best key... + String r = mFormattedStringCache.getFormattedString(number,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 209da1fa83..890eb681cc 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,10 +15,12 @@ */ public class PercentFormatter implements ValueFormatter, AxisValueFormatter { - protected DecimalFormat mFormat; + protected FormattedStringCache mFormattedStringCache; + protected FormattedStringCache mFormattedStringCacheAxis; public PercentFormatter() { - mFormat = new DecimalFormat("###,###,##0.0"); + mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,##0.0")); + mFormattedStringCacheAxis = new FormattedStringCache<>(new DecimalFormat("###,###,##0.0")); } /** @@ -27,19 +29,21 @@ public PercentFormatter() { * @param format */ public PercentFormatter(DecimalFormat format) { - this.mFormat = format; + mFormattedStringCache = new FormattedStringCache<>(format); + mFormattedStringCacheAxis = new FormattedStringCache<>(format); } // ValueFormatter @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormat.format(value) + " %"; + return mFormattedStringCache.getFormattedString(value, dataSetIndex) + " %"; } // AxisValueFormatter @Override public String getFormattedValue(float value, AxisBase axis) { - return mFormat.format(value) + " %"; + // TODO: Find a better way to do this. Float isn't the best key... + return mFormattedStringCacheAxis.getFormattedString(value, 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 b13dd8d907..97cc589528 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,6 +18,9 @@ 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 mFormattedStringCacheWholeStack; + private FormattedStringCache mFormattedStringCache; + /** * a string that should be appended behind the value @@ -44,12 +47,16 @@ public StackedValueFormatter(boolean drawWholeStack, String appendix, int decima b.append("0"); } - this.mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); + this.mFormattedStringCache = new FormattedStringCache(new DecimalFormat("###,###,###,##0" + b.toString())); + this.mFormattedStringCacheWholeStack = new FormattedStringCache(new DecimalFormat("###,###,###,##0" + b.toString())); } @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + FormattedStringCache chosenCache = mFormattedStringCache; + int chosenIndex = dataSetIndex; + float chosenValue = value; if (!mDrawWholeStack && entry instanceof BarEntry) { BarEntry barEntry = (BarEntry) entry; @@ -59,16 +66,19 @@ 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) { - - // return the "sum" across all stack values - return mFormat.format(barEntry.getY()) + mAppendix; + chosenCache = mFormattedStringCacheWholeStack; + chosenValue = barEntry.getY(); } else { - return ""; // return empty + chosenCache = null; } } } + if(chosenCache == null){ + return ""; + } + // return the "proposed" value - return mFormat.format(value) + mAppendix; + return chosenCache.getFormattedString(chosenValue, chosenIndex) + mAppendix; } } From d372fd305c600286709e90e49eb7b13881afdd6a Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Wed, 29 Jun 2016 17:05:07 -0700 Subject: [PATCH 275/606] Eliminate allocs - Rect and RectF buffers (#1892) Buffering temporary Rect and RectF instances. Created methods to modify RectF instances as performance alternatives to methods that create disposable RectF instances. --- .../mpchartexample/BarChartActivity.java | 5 ++- .../HorizontalBarChartActivity.java | 6 +++- .../mikephil/charting/charts/BarChart.java | 31 +++++++++++++++---- .../charting/charts/HorizontalBarChart.java | 12 ++++--- .../charting/renderer/PieChartRenderer.java | 4 ++- .../github/mikephil/charting/utils/Utils.java | 8 +++-- 6 files changed, 50 insertions(+), 16 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 36dce1166e..775c925bbf 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -3,6 +3,7 @@ import android.annotation.SuppressLint; import android.graphics.PointF; +import android.graphics.Rect; import android.graphics.RectF; import android.os.Bundle; import android.util.Log; @@ -257,6 +258,7 @@ private void setData(int count, float range) { } } + protected RectF mOnValueSelectedRectF = new RectF(); @SuppressLint("NewApi") @Override public void onValueSelected(Entry e, Highlight h) { @@ -264,7 +266,8 @@ public void onValueSelected(Entry e, Highlight h) { if (e == null) return; - RectF bounds = mChart.getBarBounds((BarEntry) e); + RectF bounds = mOnValueSelectedRectF; + mChart.getBarBounds((BarEntry) e, bounds); MPPointF position = mChart.getPosition(e, AxisDependency.LEFT); Log.i("bounds", bounds.toString()); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java index a3ad974eff..bab2e085a0 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -3,6 +3,7 @@ import android.annotation.SuppressLint; import android.graphics.PointF; +import android.graphics.Rect; import android.graphics.RectF; import android.os.Bundle; import android.util.Log; @@ -246,6 +247,7 @@ private void setData(int count, float range) { } } + protected RectF mOnValueSelectedRectF = new RectF(); @SuppressLint("NewApi") @Override public void onValueSelected(Entry e, Highlight h) { @@ -253,7 +255,9 @@ public void onValueSelected(Entry e, Highlight h) { if (e == null) return; - RectF bounds = mChart.getBarBounds((BarEntry) e); + RectF bounds = mOnValueSelectedRectF; + mChart.getBarBounds((BarEntry) e, bounds); + MPPointF position = mChart.getPosition(e, mChart.getData().getDataSetByIndex(h.getDataSetIndex()) .getAxisDependency()); 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 9b437c9028..05a0e1a1eb 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 @@ -98,17 +98,37 @@ public Highlight getHighlightByTouchPoint(float x, float y) { /** * Returns the bounding box of the specified Entry in the specified DataSet. Returns null if the Entry could not be - * found in the charts data. + * found in the charts data. Performance-intensive code should use void getBarBounds(BarEntry, RectF) instead. * * @param e * @return */ public RectF getBarBounds(BarEntry e) { + + RectF bounds = new RectF(); + getBarBounds(e, bounds); + + return bounds; + } + + /** + * The passed outputRect will be assigned the values of the bounding box of the specified Entry in the specified DataSet. + * The rect will be assigned Float.MIN_VALUE in all locations if the Entry could not be found in the charts data. + * + * @param e + * @return + */ + public void getBarBounds(BarEntry e, RectF outputRect){ + + RectF bounds = outputRect; + IBarDataSet set = mData.getDataSetForEntry(e); - if (set == null) - return null; + if (set == null) { + bounds.set(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE); + return; + } float y = e.getY(); float x = e.getX(); @@ -120,11 +140,10 @@ public RectF getBarBounds(BarEntry e) { float top = y >= 0 ? y : 0; float bottom = y <= 0 ? y : 0; - RectF bounds = new RectF(left, top, right, bottom); + bounds.set(left, top, right, bottom); - getTransformer(set.getAxisDependency()).rectValueToPixel(bounds); + getTransformer(set.getAxisDependency()).rectValueToPixel(outputRect); - return bounds; } /** 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 adba11e527..0c14db733a 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 @@ -141,12 +141,15 @@ protected float[] getMarkerPosition(Highlight high) { } @Override - public RectF getBarBounds(BarEntry e) { + public void getBarBounds(BarEntry e, RectF outputRect) { + RectF bounds = outputRect; IBarDataSet set = mData.getDataSetForEntry(e); - if (set == null) - return null; + if (set == null){ + outputRect.set(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE); + return; + } float y = e.getY(); float x = e.getX(); @@ -158,11 +161,10 @@ public RectF getBarBounds(BarEntry e) { float left = y >= 0 ? y : 0; float right = y <= 0 ? y : 0; - RectF bounds = new RectF(left, top, right, bottom); + bounds.set(left, top, right, bottom); getTransformer(set.getAxisDependency()).rectValueToPixel(bounds); - return bounds; } protected float[] mGetPositionBuffer = new float[2]; 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 354ea5d4ec..d479737c04 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 @@ -725,6 +725,7 @@ protected void drawCenterText(Canvas c) { } } + protected RectF mDrawHighlightedRectF = new RectF(); @Override public void drawHighlighted(Canvas c, Highlight[] indices) { @@ -743,7 +744,8 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { ? radius * (mChart.getHoleRadius() / 100.f) : 0.f; - final RectF highlightedCircleBox = new RectF(); + final RectF highlightedCircleBox = mDrawHighlightedRectF; + highlightedCircleBox.set(0,0,0,0); for (int i = 0; i < indices.length; i++) { 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 5ba70d1d1e..eab4b881ff 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 @@ -147,6 +147,7 @@ public static int calcTextWidth(Paint paint, String demoText) { return (int) paint.measureText(demoText); } + private static Rect mCalcTextHeightRect = new Rect(); /** * calculates the approximate height of a text, depending on a demo text * avoid repeated calls (e.g. inside drawing methods) @@ -157,7 +158,8 @@ public static int calcTextWidth(Paint paint, String demoText) { */ public static int calcTextHeight(Paint paint, String demoText) { - Rect r = new Rect(); + Rect r = mCalcTextHeightRect; + r.set(0,0,0,0); paint.getTextBounds(demoText, 0, demoText.length(), r); return r.height(); } @@ -188,6 +190,7 @@ public static FSize calcTextSize(Paint paint, String demoText) { return result; } + private static Rect mCalcTextSizeRect = new Rect(); /** * calculates the approximate size of a text, depending on a demo text * avoid repeated calls (e.g. inside drawing methods) @@ -198,7 +201,8 @@ public static FSize calcTextSize(Paint paint, String demoText) { */ public static void calcTextSize(Paint paint, String demoText, FSize outputFSize) { - Rect r = new Rect(); + Rect r = mCalcTextSizeRect; + r.set(0,0,0,0); paint.getTextBounds(demoText, 0, demoText.length(), r); outputFSize.width = r.width(); outputFSize.height = r.height(); From a04ad28cec0b21f95dbaefe5532dbe0b8c7b5c20 Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Wed, 29 Jun 2016 17:12:47 -0700 Subject: [PATCH 276/606] Eliminate allocs - Cache XBounds in Renderer (#1892) getXBounds creates a disposable XBounds instance on every call. Cache it and update the values with a set method. This means XBounds values are now mutable. --- .../BarLineScatterCandleBubbleRenderer.java | 15 ++++++++++++--- .../charting/renderer/LineChartRenderer.java | 8 +++++++- 2 files changed, 19 insertions(+), 4 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 60697eac7a..9028e94704 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 @@ -58,17 +58,17 @@ protected class XBounds { /** * minimum visible entry index */ - public final int min; + public int min; /** * maximum visible entry index */ - public final int max; + public int max; /** * range of visible entry indices */ - public final int range; + public int range; /** * Calculates the minimum and maximum x values as well as the range between them. @@ -77,7 +77,16 @@ protected class XBounds { * @param dataSet */ public XBounds(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet) { + this.set(chart, dataSet); + } + /** + * Calculates the minimum and maximum x values as well as the range between them. + * + * @param chart + * @param dataSet + */ + public void set(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet){ float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float low = chart.getLowestVisibleX(); 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 e099af1b88..9c92b9db52 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 @@ -302,6 +302,7 @@ protected void drawCubicFill(Canvas c, ILineDataSet dataSet, Path spline, Transf } private float[] mLineBuffer = new float[4]; + private XBounds xBoundsBuffer; /** * Draws a normal line. @@ -331,7 +332,12 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { canvas = c; } - XBounds bounds = getXBounds(mChart, dataSet); + if(xBoundsBuffer == null){ + xBoundsBuffer = getXBounds(mChart, dataSet); + }else{ + xBoundsBuffer.set(mChart, dataSet); + } + final XBounds bounds = xBoundsBuffer; // more than 1 color if (dataSet.getColors().size() > 1) { From 424ee02ce2088619dc1c792d1f162d33071de15e Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Wed, 29 Jun 2016 17:36:15 -0700 Subject: [PATCH 277/606] Eliminate allocs - String Caches now have Prim subtypes (#1892) By creating versions of the FormattedStringCache that don't require auto boxing, allocations are cut tremendously in this commit. --- .../BarChartPositiveNegative.java | 6 +- .../formatter/DefaultAxisValueFormatter.java | 6 +- .../formatter/FormattedStringCache.java | 80 ++++++++++++++++++- 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java index a080de8af6..efe51d2ffd 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -165,15 +165,15 @@ public Data(float xValue, float yValue, String xAxisValue) { private class ValueFormatter implements com.github.mikephil.charting.formatter.ValueFormatter { - private FormattedStringCache mFormattedStringCache; + private FormattedStringCache.PrimIntFloat mFormattedStringCache; public ValueFormatter() { - mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("######.0")); + mFormattedStringCache = new FormattedStringCache.PrimIntFloat(new DecimalFormat("######.0")); } @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormattedStringCache.getFormattedString(value, dataSetIndex); + return mFormattedStringCache.getFormattedValue(value, dataSetIndex); } } } 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 3369e00693..f55fc8175e 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 @@ -12,7 +12,7 @@ public class DefaultAxisValueFormatter implements AxisValueFormatter { /** * FormattedStringFormat for formatting and caching */ - protected FormattedStringCache mFormattedStringCache; + protected FormattedStringCache.PrimFloat mFormattedStringCache; /** * the number of decimal digits this formatter uses @@ -35,14 +35,14 @@ public DefaultAxisValueFormatter(int digits) { b.append("0"); } - mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0" + b.toString())); + mFormattedStringCache = new FormattedStringCache.PrimFloat(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.getFormattedString(value, value); + return mFormattedStringCache.getFormattedValue(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 index 047af77571..cb0508b86e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FormattedStringCache.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FormattedStringCache.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.formatter; import java.text.Format; +import java.util.ArrayList; import java.util.HashMap; /** @@ -17,7 +18,7 @@ */ public class FormattedStringCache { - private Format mFormat; + protected Format mFormat; private HashMap mCachedStringsHashMap = new HashMap<>(); private HashMap mCachedValuesHashMap = new HashMap<>(); @@ -48,4 +49,81 @@ public String getFormattedString(V value, K key){ return result; } + 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); + } + + } + + 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); + } + + } + + + } From 7fd18d2177477d2d61d271bfe2ba87f90860d36b Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Wed, 29 Jun 2016 17:48:31 -0700 Subject: [PATCH 278/606] Eliminate allocs - Buffer Paint.FontMetrics (#1892) paint.getFontMetrics() has a hidden allocation. paint.getFontMetrics(fontMetrics) is used internally inside of Paint, so caching and holding onto a metrics buffer helps reduce allocations. --- .../mikephil/charting/components/Legend.java | 9 +++++---- .../charting/renderer/LegendRenderer.java | 5 +++-- .../github/mikephil/charting/utils/Utils.java | 18 ++++++++++++++---- 3 files changed, 22 insertions(+), 10 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 b66d93042e..d1b69e2b16 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 @@ -808,6 +808,8 @@ public FSize[] getCalculatedLineSizes() { return mCalculatedLineSizes; } + protected Paint.FontMetrics fontMetricsForCalculateDimensions = new Paint.FontMetrics(); + ArrayList calculatedLineSizesForCalculateDimensions = new ArrayList<>(); /** * Calculates the dimensions of the Legend. This includes the maximum width * and height of a single entry, as well as the total width and height of @@ -815,7 +817,6 @@ public FSize[] getCalculatedLineSizes() { * * @param labelpaint */ - ArrayList calculatedLineSizesForCalculateDimensions = new ArrayList<>(); public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandler) { mTextWidthMax = getMaximumEntryWidth(labelpaint); @@ -825,7 +826,7 @@ public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandle case VERTICAL: { float maxWidth = 0f, maxHeight = 0f, width = 0f; - float labelLineHeight = Utils.getLineHeight(labelpaint); + float labelLineHeight = Utils.getLineHeight(labelpaint, fontMetricsForCalculateDimensions); final int count = mLabels.length; boolean wasStacked = false; @@ -878,8 +879,8 @@ else if (wasStacked) { case HORIZONTAL: { int labelCount = mLabels.length; - float labelLineHeight = Utils.getLineHeight(labelpaint); - float labelLineSpacing = Utils.getLineSpacing(labelpaint) + mYEntrySpace; + float labelLineHeight = Utils.getLineHeight(labelpaint, fontMetricsForCalculateDimensions); + float labelLineSpacing = Utils.getLineSpacing(labelpaint, fontMetricsForCalculateDimensions) + mYEntrySpace; float contentWidth = viewPortHandler.contentWidth() * mMaxSizePercent; // Prepare arrays for calculated layout 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 0add1004e0..4586d68e22 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 @@ -177,6 +177,7 @@ public void computeLegend(ChartData data) { mLegend.calculateDimensions(mLegendLabelPaint, mViewPortHandler); } + protected Paint.FontMetrics fontMetricsForRenderLegent = new Paint.FontMetrics(); public void renderLegend(Canvas c) { if (!mLegend.isEnabled()) @@ -190,8 +191,8 @@ public void renderLegend(Canvas c) { mLegendLabelPaint.setTextSize(mLegend.getTextSize()); mLegendLabelPaint.setColor(mLegend.getTextColor()); - float labelLineHeight = Utils.getLineHeight(mLegendLabelPaint); - float labelLineSpacing = Utils.getLineSpacing(mLegendLabelPaint) + mLegend.getYEntrySpace(); + float labelLineHeight = Utils.getLineHeight(mLegendLabelPaint, fontMetricsForRenderLegent); + float labelLineSpacing = Utils.getLineSpacing(mLegendLabelPaint, fontMetricsForRenderLegent) + mLegend.getYEntrySpace(); float formYOffset = labelLineHeight - Utils.calcTextHeight(mLegendLabelPaint, "ABC") / 2.f; String[] labels = mLegend.getLabels(); 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 eab4b881ff..8cf77e2de1 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 @@ -165,13 +165,23 @@ public static int calcTextHeight(Paint paint, String demoText) { } public static float getLineHeight(Paint paint) { - Paint.FontMetrics metrics = paint.getFontMetrics(); - return metrics.descent - metrics.ascent; + Paint.FontMetrics metrics = new Paint.FontMetrics(); + return getLineHeight(paint, metrics); + } + + public static float getLineHeight(Paint paint, Paint.FontMetrics fontMetrics){ + paint.getFontMetrics(fontMetrics); + return fontMetrics.descent - fontMetrics.ascent; } public static float getLineSpacing(Paint paint) { - Paint.FontMetrics metrics = paint.getFontMetrics(); - return metrics.ascent - metrics.top + metrics.bottom; + Paint.FontMetrics metrics = new Paint.FontMetrics(); + return getLineSpacing(paint, metrics); + } + + public static float getLineSpacing(Paint paint, Paint.FontMetrics fontMetrics){ + paint.getFontMetrics(fontMetrics); + return fontMetrics.ascent - fontMetrics.top + fontMetrics.bottom; } /** From 69f17b2a79de4f06321d154b6050e2a6fc3d2d14 Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Wed, 29 Jun 2016 18:30:21 -0700 Subject: [PATCH 279/606] Eliminate allocs - Buffer ArrayLists (#1892) Many short-lived ArrayLists were created in various methods, which could easily have been extracted to field level variable. This has been done, and the arrays cleared before use. --- .../RealtimeLineChartActivity.java | 17 ++++++++++------- .../github/mikephil/charting/charts/Chart.java | 18 ++++++++++++++++++ .../mikephil/charting/components/Legend.java | 2 +- .../mikephil/charting/data/BarDataSet.java | 4 +++- .../mikephil/charting/data/BaseDataSet.java | 11 +++++++++-- .../mikephil/charting/data/BubbleDataSet.java | 4 +++- .../mikephil/charting/data/CandleDataSet.java | 4 +++- .../mikephil/charting/data/CombinedData.java | 5 ++++- .../github/mikephil/charting/data/DataSet.java | 4 +++- .../mikephil/charting/data/LineDataSet.java | 18 ++++++++++++++---- .../charting/highlight/ChartHighlighter.java | 5 ++++- .../highlight/CombinedHighlighter.java | 5 ++++- .../charting/highlight/RadarHighlighter.java | 4 +++- .../renderer/CombinedChartRenderer.java | 12 +++++++++--- .../charting/renderer/LegendRenderer.java | 9 +++++++-- .../charting/renderer/LineChartRenderer.java | 10 ++++++++-- 16 files changed, 103 insertions(+), 29 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 5b8d3f6f3e..75675d1ac1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -176,19 +176,22 @@ private void feedMultiple() { if (thread != null) thread.interrupt(); + final Runnable runnable = new Runnable() { + + @Override + public void run() { + addEntry(); + } + }; + thread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { - runOnUiThread(new Runnable() { - - @Override - public void run() { - addEntry(); - } - }); + // Don't generate garbage runnables inside the loop. + runOnUiThread(runnable); try { Thread.sleep(25); 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 6807a57c08..69bdbd0a13 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 @@ -1442,6 +1442,24 @@ public List getEntriesAtIndex(int xIndex) { return vals; } + public void getEntriesAtIndex(int xIndex, List entriesOutput){ + + List vals = entriesOutput; + vals.clear(); + + for (int i = 0; i < mData.getDataSetCount(); i++) { + + IDataSet set = mData.getDataSetByIndex(i); + + Entry e = set.getEntryForXPos(xIndex); + + if (e != null) { + vals.add(e); + } + } + + } + /** * Returns the ChartData object that has been set for the chart. * 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 d1b69e2b16..b0b82bdc3e 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 @@ -809,7 +809,7 @@ public FSize[] getCalculatedLineSizes() { } protected Paint.FontMetrics fontMetricsForCalculateDimensions = new Paint.FontMetrics(); - ArrayList calculatedLineSizesForCalculateDimensions = new ArrayList<>(); + protected ArrayList calculatedLineSizesForCalculateDimensions = new ArrayList<>(); /** * Calculates the dimensions of the Legend. This includes the maximum width * and height of a single entry, as well as the total width and height of 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 2802ebc4c2..8e78de3397 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 @@ -51,10 +51,12 @@ public BarDataSet(List yVals, String label) { calcEntryCountIncludingStacks(yVals); } + protected List barEntriesForCopy = new ArrayList<>(); @Override public DataSet copy() { - List yVals = new ArrayList(); + List yVals = barEntriesForCopy; + yVals.clear(); for (int i = 0; i < mValues.size(); i++) { yVals.add(mValues.get(i).copy()); 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 19b06f9ce1..4f64c368f2 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 @@ -166,7 +166,11 @@ public void setColors(int[] colors) { */ public void setColors(int[] colors, Context c) { - List clrs = new ArrayList(); + if(mColors == null){ + mColors = new ArrayList<>(); + } + List clrs = mColors; + clrs.clear(); for (int color : colors) { clrs.add(c.getResources().getColor(color)); @@ -224,7 +228,10 @@ public void setColors(int[] colors, int alpha) { * Resets all colors of this DataSet and recreates the colors array. */ public void resetColors() { - mColors = new ArrayList(); + if(mColors == null) { + mColors = new ArrayList(); + } + mColors.clear(); } /** 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..3c2858fbf7 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 @@ -51,10 +51,12 @@ public void calcMinMax() { } } + protected ArrayList bubbleEntriesForCopy = new ArrayList<>(); @Override public DataSet copy() { - List yVals = new ArrayList(); + List yVals = bubbleEntriesForCopy; + yVals.clear(); for (int i = 0; i < mValues.size(); i++) { yVals.add(mValues.get(i).copy()); 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 5288d7e3a6..8af41c7a80 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 @@ -77,10 +77,12 @@ public CandleDataSet(List yVals, String label) { super(yVals, label); } + protected ArrayList candleEntriesForCopy = new ArrayList<>(); @Override public DataSet copy() { - List yVals = new ArrayList(); + List yVals = candleEntriesForCopy; + yVals.clear(); for (int i = 0; i < mValues.size(); i++) { yVals.add(((CandleEntry) mValues.get(i)).copy()); 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 37ab1e4bdb..2c650269b6 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 @@ -57,7 +57,10 @@ public void setData(BubbleData data) { @Override public void calcMinMax() { - mDataSets = new ArrayList<>(); + if(mDataSets == null){ + mDataSets = new ArrayList<>(); + } + mDataSets.clear(); mYMax = -Float.MAX_VALUE; mYMin = Float.MAX_VALUE; 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..84cf7c8bcb 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 @@ -295,6 +295,7 @@ public int getEntryIndex(float xPos, Rounding rounding) { return high; } + protected ArrayList entriesForGetEntriesForXPos = new ArrayList<>(2); /** * Returns all Entry objects at the given xIndex. INFORMATION: This method * does calculations at runtime. Do not over-use in performance critical @@ -306,7 +307,8 @@ public int getEntryIndex(float xPos, Rounding rounding) { @Override public List getEntriesForXPos(float xVal) { - List entries = new ArrayList(); + List entries = entriesForGetEntriesForXPos; + entries.clear(); int low = 0; int high = mValues.size() - 1; 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 2b802d9c28..0110ecd234 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 @@ -52,8 +52,11 @@ public LineDataSet(List yVals, String label) { // mCircleRadius = Utils.convertDpToPixel(4f); // mLineWidth = Utils.convertDpToPixel(1f); - mCircleColors = new ArrayList(); - + if(mCircleColors == null) { + mCircleColors = new ArrayList(); + } + mCircleColors.clear(); + // default colors // mColors.add(Color.rgb(192, 255, 140)); // mColors.add(Color.rgb(255, 247, 140)); @@ -294,7 +297,11 @@ public void setCircleColors(int[] colors) { */ public void setCircleColors(int[] colors, Context c) { - List clrs = new ArrayList(); + List clrs = mCircleColors; + if(clrs == null){ + clrs = new ArrayList<>(); + } + clrs.clear(); for (int color : colors) { clrs.add(c.getResources().getColor(color)); @@ -318,7 +325,10 @@ public void setCircleColor(int color) { * resets the circle-colors array and creates a new one */ public void resetCircleColors() { - mCircleColors = new ArrayList(); + if(mCircleColors == null) { + mCircleColors = new ArrayList(); + } + mCircleColors.clear(); } /** 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 90d74b26e9..4f96edfce4 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 @@ -110,6 +110,8 @@ protected float getHighlightPos(Highlight h) { return h.getYPx(); } + + protected ArrayList highlightsForGetHighlightsAtXPos = new ArrayList<>(2); /** * Returns a list of Highlight objects representing the entries closest to the given xVal. * The returned list contains two objects per DataSet (closest rounding up, closest rounding down). @@ -121,7 +123,8 @@ protected float getHighlightPos(Highlight h) { */ protected List getHighlightsAtXPos(float xVal, float x, float y) { - List vals = new ArrayList(); + List vals = highlightsForGetHighlightsAtXPos; + vals.clear(); BarLineScatterCandleBubbleData data = getData(); 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 c9ba0b4e6d..d948cedf7c 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,10 +28,13 @@ public CombinedHighlighter(CombinedDataProvider chart, BarDataProvider barChart) barHighlighter = barChart.getBarData() == null ? null : new BarHighlighter(barChart); } + + protected ArrayList highlightsForGetHighlightsAtXPos = new ArrayList<>(2); @Override protected List getHighlightsAtXPos(float xVal, float x, float y) { - List vals = new ArrayList(); + List vals = highlightsForGetHighlightsAtXPos; + vals.clear(); List dataObjects = mChart.getCombinedData().getAllData(); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java index c322980b89..cb0048c477 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java @@ -44,6 +44,7 @@ protected Highlight getClosestHighlight(int index, float x, float y) { return closest; } + protected ArrayList highlightsForGetHighlightsAtIndex = new ArrayList<>(2); /** * Returns an array of Highlight objects for the given index. The Highlight * objects give information about the value at the selected index and the @@ -55,7 +56,8 @@ protected Highlight getClosestHighlight(int index, float x, float y) { */ protected List getHighlightsAtIndex(int index) { - List vals = new ArrayList(); + List vals = highlightsForGetHighlightsAtIndex; + vals.clear(); float phaseX = mChart.getAnimator().getPhaseX(); float phaseY = mChart.getAnimator().getPhaseY(); 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 1f79868185..f6c671d1cf 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 @@ -33,6 +33,7 @@ public CombinedChartRenderer(CombinedChart chart, ChartAnimator animator, ViewPo createRenderers(chart, animator, viewPortHandler); } + protected ArrayList renderersForCreateRenderers = new ArrayList<>(4); /** * Creates the renderers needed for this combined-renderer in the required order. Also takes the DrawOrder into * consideration. @@ -43,7 +44,8 @@ public CombinedChartRenderer(CombinedChart chart, ChartAnimator animator, ViewPo */ protected void createRenderers(CombinedChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { - mRenderers = new ArrayList(); + mRenderers = renderersForCreateRenderers; + mRenderers.clear(); DrawOrder[] orders = chart.getDrawOrder(); @@ -102,6 +104,7 @@ public void drawExtras(Canvas c) { renderer.drawExtras(c); } + protected ArrayList highlightsforDrawHighlighted = new ArrayList<>(); @Override public void drawHighlighted(Canvas c, Highlight[] indices) { @@ -125,8 +128,11 @@ else if (renderer instanceof BubbleChartRenderer) int dataIndex = data == null ? -1 : ((CombinedData)chart.getData()).getAllData().indexOf(data); - ArrayList dataIndices = new ArrayList<>(); - for (Highlight h : indices) { + ArrayList dataIndices = highlightsforDrawHighlighted; + dataIndices.clear(); + Highlight h; + for(int i = 0 ; i < dataIndices.size() ; i++){ + h = dataIndices.get(i); if (h.getDataIndex() == dataIndex || h.getDataIndex() == -1) dataIndices.add(h); } 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 4586d68e22..fb8937a303 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 @@ -70,6 +70,8 @@ public Paint getFormPaint() { return mLegendFormPaint; } + protected ArrayList labelsForComputeLegend = new ArrayList<>(16); + protected ArrayList colorsForComputeLegend = new ArrayList<>(16); /** * Prepares the legend and calculates all needed forms, labels and colors. * @@ -79,8 +81,11 @@ public void computeLegend(ChartData data) { if (!mLegend.isLegendCustom()) { - List labels = new ArrayList(); - List colors = new ArrayList(); + ArrayList labels = labelsForComputeLegend; + ArrayList colors = colorsForComputeLegend; + + labels.clear(); + colors.clear(); // loop for building up the colors and labels used in the legend for (int i = 0; i < data.getDataSetCount(); i++) { 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 9c92b9db52..f44739dc8c 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 @@ -573,6 +573,7 @@ public void drawExtras(Canvas c) { private Path mCirclePathBuffer = new Path(); private float[] mCirclesBuffer = new float[2]; private HashMap mImageCaches = new HashMap<>(); + private XBounds mXBoundsBuffer; protected void drawCircles(Canvas c) { mRenderPaint.setStyle(Paint.Style.FILL); @@ -607,8 +608,13 @@ protected void drawCircles(Canvas c) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - XBounds bounds = getXBounds(mChart, dataSet); - int entryCount = dataSet.getEntryCount(); + if(mXBoundsBuffer == null) { + mXBoundsBuffer = getXBounds(mChart, dataSet); + }else{ + mXBoundsBuffer.set(mChart,dataSet); + } + + XBounds bounds = mXBoundsBuffer; float circleRadius = dataSet.getCircleRadius(); float circleHoleRadius = dataSet.getCircleHoleRadius(); From afdbd2c5f0fad23d255f8811ae756748717ff35a Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Thu, 30 Jun 2016 11:30:39 -0700 Subject: [PATCH 280/606] Eliminate allocs - Tests for StringCache (#1892) Created unit tests for the FormattedStringCache classes. Also, took this opportunity to create a FormattedStringCache.Generic static class to take over what was previously simply FormattedStringCache. Verified that all unit tests still pass. --- .../mpchartexample/LineChartTime.java | 4 +- .../StackedBarActivityNegative.java | 17 +- .../custom/MyAxisValueFormatter.java | 6 +- .../custom/MyCustomXAxisValueFormatter.java | 6 +- .../custom/MyValueFormatter.java | 6 +- .../custom/RadarMarkerView.java | 4 +- .../formatter/DefaultValueFormatter.java | 6 +- .../formatter/FormattedStringCache.java | 105 ++++- .../formatter/LargeValueFormatter.java | 6 +- .../charting/formatter/PercentFormatter.java | 16 +- .../formatter/StackedValueFormatter.java | 12 +- .../test/FormattedStringCacheTest.java | 433 ++++++++++++++++++ 12 files changed, 559 insertions(+), 62 deletions(-) create mode 100644 MPChartLib/src/test/java/com/github/mikephil/charting/test/FormattedStringCacheTest.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java index c7893188a3..77f9ecb834 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -92,12 +92,12 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setGranularity(60000L); // one minute in millis xAxis.setValueFormatter(new AxisValueFormatter() { - private FormattedStringCache mFormattedStringCache = new FormattedStringCache(new SimpleDateFormat("dd MMM HH:mm")); + private FormattedStringCache.Generic mFormattedStringCache = new FormattedStringCache.Generic<>(new SimpleDateFormat("dd MMM HH:mm")); @Override public String getFormattedValue(float value, AxisBase axis) { Long v = (long) value; - return mFormattedStringCache.getFormattedString(new Date(v), v); + return mFormattedStringCache.getFormattedValue(new Date(v), v); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java index b1a54cbef1..4f0e16f5fa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -80,12 +80,11 @@ protected void onCreate(Bundle savedInstanceState) { xAxis.setGranularity(10f); xAxis.setValueFormatter(new AxisValueFormatter() { - private FormattedStringCache format = new FormattedStringCache(new DecimalFormat("###")); + private FormattedStringCache.PrimFloat format = new FormattedStringCache.PrimFloat(new DecimalFormat("###")); @Override public String getFormattedValue(float value, AxisBase axis) { - Float key = value + 10; - return format.getFormattedString(value, value) + "-" + format.getFormattedString(value, key); + return format.getFormattedValue(value) + "-" + format.getFormattedValue(value+10); } @Override @@ -224,25 +223,25 @@ public void onNothingSelected() { private class CustomFormatter implements ValueFormatter, AxisValueFormatter { - private FormattedStringCache mFormatValue; - private FormattedStringCache mFormatAxis; + private FormattedStringCache.Generic mFormatValue; + private FormattedStringCache.PrimFloat mFormatAxis; public CustomFormatter() { - mFormatValue = new FormattedStringCache<>(new DecimalFormat("###")); - mFormatAxis = new FormattedStringCache<>(new DecimalFormat("###")); + mFormatValue = new FormattedStringCache.Generic<>(new DecimalFormat("###")); + mFormatAxis = new FormattedStringCache.PrimFloat(new DecimalFormat("###")); } // data @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormatValue.getFormattedString(value, dataSetIndex) + "m"; + return mFormatValue.getFormattedValue(value, dataSetIndex) + "m"; } // YAxis @Override public String getFormattedValue(float value, AxisBase axis) { Float v = Math.abs(value); - return mFormatAxis.getFormattedString(v,v) + "m"; + return mFormatAxis.getFormattedValue(v) + "m"; } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java index 06197d7508..2eeff56971 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java @@ -9,15 +9,15 @@ public class MyAxisValueFormatter implements AxisValueFormatter { private DecimalFormat mFormat; - private FormattedStringCache mFormattedStringCache; + private FormattedStringCache.PrimFloat mFormattedStringCache; public MyAxisValueFormatter() { - mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0.0")); + mFormattedStringCache = new FormattedStringCache.PrimFloat(new DecimalFormat("###,###,###,##0.0")); } @Override public String getFormattedValue(float value, AxisBase axis) { - return mFormattedStringCache.getFormattedString(value, value) + " $"; + return mFormattedStringCache.getFormattedValue(value) + " $"; } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java index f2018bcdd4..9b5f4c921c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -12,13 +12,13 @@ */ public class MyCustomXAxisValueFormatter implements AxisValueFormatter { - private FormattedStringCache mFormattedStringCache; + private FormattedStringCache.PrimFloat mFormattedStringCache; private ViewPortHandler mViewPortHandler; public MyCustomXAxisValueFormatter(ViewPortHandler viewPortHandler) { mViewPortHandler = viewPortHandler; // maybe do something here or provide parameters in constructor - mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0.0")); + mFormattedStringCache = new FormattedStringCache.PrimFloat(new DecimalFormat("###,###,###,##0.0")); } @Override @@ -35,7 +35,7 @@ else if (xScale > 3) else if (xScale > 1) return "2"; else - return mFormattedStringCache.getFormattedString(value, value); + return mFormattedStringCache.getFormattedValue(value); } @Override diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java index 87cf28f4c3..5ca05d92de 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java @@ -9,14 +9,14 @@ public class MyValueFormatter implements ValueFormatter { - private FormattedStringCache mFormattedStringCache; + private FormattedStringCache.Generic mFormattedStringCache; public MyValueFormatter() { - mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0.0")); + mFormattedStringCache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,###,##0.0")); } @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormattedStringCache.getFormattedString(value, dataSetIndex) + " $"; + return mFormattedStringCache.getFormattedValue(value, dataSetIndex) + " $"; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java index b02212d89a..61f7209e44 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java @@ -23,7 +23,7 @@ public class RadarMarkerView extends MarkerView { private TextView tvContent; - private FormattedStringCache mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("##0")); + private FormattedStringCache.PrimFloat mFormattedStringCache = new FormattedStringCache.PrimFloat(new DecimalFormat("##0")); public RadarMarkerView(Context context, int layoutResource) { super(context, layoutResource); @@ -37,7 +37,7 @@ public RadarMarkerView(Context context, int layoutResource) { @Override public void refreshContent(Entry e, Highlight highlight) { float value = e.getY(); - tvContent.setText(mFormattedStringCache.getFormattedString(value, value) + " %"); + tvContent.setText(mFormattedStringCache.getFormattedValue(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 039787113c..b6965435aa 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 @@ -17,7 +17,7 @@ public class DefaultValueFormatter implements ValueFormatter { /** * FormattedStringCache for formatting and caching. */ - protected FormattedStringCache mFormattedStringCache; + protected FormattedStringCache.Generic mFormattedStringCache; protected int mDecimalDigits; @@ -47,14 +47,14 @@ public void setup(int digits) { b.append("0"); } - mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,###,##0" + b.toString())); + mFormattedStringCache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,###,##0" + b.toString())); } @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormattedStringCache.getFormattedString(value, dataSetIndex); + return mFormattedStringCache.getFormattedValue(value, dataSetIndex); } 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 index cb0508b86e..c6b4ef3e6f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FormattedStringCache.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/FormattedStringCache.java @@ -16,11 +16,9 @@ * Possibly want to make some explicit primitive enabled caches, though they can be ugly. * */ -public class FormattedStringCache { +public class FormattedStringCache { protected Format mFormat; - private HashMap mCachedStringsHashMap = new HashMap<>(); - private HashMap mCachedValuesHashMap = new HashMap<>(); public Format getFormat(){ return mFormat; @@ -30,25 +28,53 @@ public FormattedStringCache(Format format){ this.mFormat = format; } - public String getFormattedString(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); - } + /** + * 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<>(); - // 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); + public Generic(Format format){ + super(format); } - String result = mCachedStringsHashMap.get(key); + 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); + } - return result; + // 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){ @@ -91,6 +117,9 @@ public String getFormattedValue(float value, int 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){ @@ -113,11 +142,47 @@ public String getFormattedValue(float value) { break; } } - if(!alreadyHasValue) { - cachedValues.add(value); - cachedStrings.add(mFormat.format(value)); - sIndex = cachedValues.size() - 1; - } + 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 5679c9a2aa..4e6e77a6bf 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 @@ -29,10 +29,10 @@ public class LargeValueFormatter implements ValueFormatter, AxisValueFormatter { /** * FormattedStringCache for formatting and caching. */ - protected FormattedStringCache mFormattedStringCache; + protected FormattedStringCache.PrimDouble mFormattedStringCache; public LargeValueFormatter() { - mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###E00")); + mFormattedStringCache = new FormattedStringCache.PrimDouble(new DecimalFormat("###E00")); } /** @@ -83,7 +83,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.getFormattedString(number,number); + String r = mFormattedStringCache.getFormattedValue(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 890eb681cc..1ad1ba1c0d 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,12 @@ */ public class PercentFormatter implements ValueFormatter, AxisValueFormatter { - protected FormattedStringCache mFormattedStringCache; - protected FormattedStringCache mFormattedStringCacheAxis; + protected FormattedStringCache.Generic mFormattedStringCache; + protected FormattedStringCache.PrimFloat mFormattedStringCacheAxis; public PercentFormatter() { - mFormattedStringCache = new FormattedStringCache<>(new DecimalFormat("###,###,##0.0")); - mFormattedStringCacheAxis = new FormattedStringCache<>(new DecimalFormat("###,###,##0.0")); + mFormattedStringCache = new FormattedStringCache.Generic<>(new DecimalFormat("###,###,##0.0")); + mFormattedStringCacheAxis = new FormattedStringCache.PrimFloat(new DecimalFormat("###,###,##0.0")); } /** @@ -29,21 +29,21 @@ public PercentFormatter() { * @param format */ public PercentFormatter(DecimalFormat format) { - mFormattedStringCache = new FormattedStringCache<>(format); - mFormattedStringCacheAxis = new FormattedStringCache<>(format); + mFormattedStringCache = new FormattedStringCache.Generic<>(format); + mFormattedStringCacheAxis = new FormattedStringCache.PrimFloat(format); } // ValueFormatter @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormattedStringCache.getFormattedString(value, dataSetIndex) + " %"; + return mFormattedStringCache.getFormattedValue(value, dataSetIndex) + " %"; } // 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.getFormattedString(value, value) + " %"; + return mFormattedStringCacheAxis.getFormattedValue(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 97cc589528..618b1b1fe4 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,8 @@ 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 mFormattedStringCacheWholeStack; - private FormattedStringCache mFormattedStringCache; + private FormattedStringCache.Generic mFormattedStringCacheWholeStack; + private FormattedStringCache.Generic mFormattedStringCache; /** @@ -47,14 +47,14 @@ public StackedValueFormatter(boolean drawWholeStack, String appendix, int decima b.append("0"); } - this.mFormattedStringCache = new FormattedStringCache(new DecimalFormat("###,###,###,##0" + b.toString())); - this.mFormattedStringCacheWholeStack = new FormattedStringCache(new DecimalFormat("###,###,###,##0" + b.toString())); + this.mFormattedStringCache = new FormattedStringCache.Generic(new DecimalFormat("###,###,###,##0" + b.toString())); + this.mFormattedStringCacheWholeStack = new FormattedStringCache.Generic(new DecimalFormat("###,###,###,##0" + b.toString())); } @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - FormattedStringCache chosenCache = mFormattedStringCache; + FormattedStringCache.Generic chosenCache = mFormattedStringCache; int chosenIndex = dataSetIndex; float chosenValue = value; if (!mDrawWholeStack && entry instanceof BarEntry) { @@ -79,6 +79,6 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View } // return the "proposed" value - return chosenCache.getFormattedString(chosenValue, chosenIndex) + mAppendix; + return chosenCache.getFormattedValue(chosenValue, chosenIndex) + 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 new file mode 100644 index 0000000000..bd33008ea6 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/FormattedStringCacheTest.java @@ -0,0 +1,433 @@ +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 b037f55d732e37d2a5c466ce72b4ff36777f6cd5 Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Thu, 30 Jun 2016 15:29:40 -0700 Subject: [PATCH 281/606] Eliminate allocs - XAxisRender array (#1892) Found an array that was instantiated frequently without need. Placed bounds on its instantiation. --- .../github/mikephil/charting/renderer/XAxisRenderer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 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 805bdadf8c..ef37a3a069 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 @@ -180,12 +180,12 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); - if(mDrawLabelsBuffer.length != mAxis.mEntryCount * 2){ + if(mDrawLabelsBuffer.length < mAxis.mEntryCount * 2){ mDrawLabelsBuffer = new float[mXAxis.mEntryCount * 2]; } float[] positions = mDrawLabelsBuffer; - for (int i = 0; i < positions.length; i += 2) { + for (int i = 0; i < positions.length && i < mXAxis.mEntries.length / 2 && i < mXAxis.mCenteredEntries.length / 2 ; i += 2) { // only fill x values if (centeringEnabled) { @@ -199,7 +199,7 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { mTrans.pointValuesToPixel(positions); - for (int i = 0; i < positions.length; i += 2) { + for (int i = 0; i < positions.length && i < mXAxis.mEntries.length / 2 && i < mXAxis.mCenteredEntries.length / 2 ; i += 2) { float x = positions[i]; From 5a18d0ef225ccf07acbc36cef259ffbdf31ed0ca Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Thu, 30 Jun 2016 15:30:45 -0700 Subject: [PATCH 282/606] Eliminate allocs - Adjust pool replenishing (#1892) Replenish fewer objects in utils pools when empty, in case the pool size grows large. --- .../src/main/java/com/github/mikephil/charting/utils/FSize.java | 1 + .../main/java/com/github/mikephil/charting/utils/MPPointF.java | 1 + .../src/main/java/com/github/mikephil/charting/utils/PointD.java | 1 + 3 files changed, 3 insertions(+) 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 89bf6a9a80..ecb8c2e312 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 @@ -18,6 +18,7 @@ public final class FSize extends ObjectPool.Poolable{ static { pool = ObjectPool.create(256, new FSize(0,0)); + pool.setReplenishPercentage(0.5f); } 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 84f5850862..f2e6c71fea 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 @@ -16,6 +16,7 @@ public class MPPointF extends ObjectPool.Poolable { static { pool = ObjectPool.create(32, new MPPointF(0,0)); + pool.setReplenishPercentage(0.5f); } private MPPointF(float x, float y){ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java index ed5ae61e72..c69c4da7d3 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java @@ -14,6 +14,7 @@ public class PointD extends ObjectPool.Poolable { static { pool = ObjectPool.create(64, new PointD(0,0)); + pool.setReplenishPercentage(0.5f); } public static PointD getInstance(double x, double y){ From 38fbefe66b5f91f6723818073d0acfc8b461aa2f Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Thu, 30 Jun 2016 15:33:02 -0700 Subject: [PATCH 283/606] Eliminate allocs - Move and Zoom Job Pools (#1892) Create object pools for the Zoom, move, and animation jobs. Their constructors remain public, to make this easier to roll back from if needed. --- .../charting/charts/BarLineChartBase.java | 18 +++---- .../charting/jobs/AnimatedMoveViewJob.java | 37 +++++++++++++++ .../charting/jobs/AnimatedViewPortJob.java | 47 ++++++++++++++++++- .../charting/jobs/AnimatedZoomJob.java | 31 ++++++++++++ .../mikephil/charting/jobs/MoveViewJob.java | 29 ++++++++++++ .../mikephil/charting/jobs/ViewPortJob.java | 4 +- .../mikephil/charting/jobs/ZoomJob.java | 32 +++++++++++++ 7 files changed, 187 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 642c61e7c9..f734a59e71 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 @@ -9,7 +9,6 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Style; -import android.graphics.PointF; import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; @@ -664,7 +663,7 @@ public void zoom(float scaleX, float scaleY, float x, float y) { */ public void zoomAndCenter(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis) { - Runnable job = new ZoomJob(mViewPortHandler, scaleX, scaleY, xValue, yValue, getTransformer(axis), axis, this); + Runnable job = ZoomJob.getInstance(mViewPortHandler, scaleX, scaleY, xValue, yValue, getTransformer(axis), axis, this); addViewportJob(job); } @@ -686,7 +685,7 @@ public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, floa PointD origin = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - Runnable job = new AnimatedZoomJob(mViewPortHandler, this, getTransformer(axis), getAxis(axis), mXAxis + 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); @@ -800,6 +799,7 @@ public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency ax mViewPortHandler.setMinMaxScaleY(minScale, maxScale); } + /** * Moves the left side of the current viewport to the specified x-position. * This also refreshes the chart by calling invalidate(). @@ -808,7 +808,7 @@ public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency ax */ public void moveViewToX(float xValue) { - Runnable job = new MoveViewJob(mViewPortHandler, xValue, 0f, + Runnable job = MoveViewJob.getInstance(mViewPortHandler, xValue, 0f, getTransformer(AxisDependency.LEFT), this); addViewportJob(job); @@ -827,7 +827,7 @@ public void moveViewTo(float xValue, float yValue, AxisDependency axis) { float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - Runnable job = new MoveViewJob(mViewPortHandler, xValue, yValue + yInView / 2f, + Runnable job = MoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f, getTransformer(axis), this); addViewportJob(job); @@ -852,7 +852,7 @@ public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - Runnable job = new AnimatedMoveViewJob(mViewPortHandler, xValue, yValue + yInView / 2f, + Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f, getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); addViewportJob(job); @@ -874,7 +874,7 @@ public void centerViewToY(float yValue, AxisDependency axis) { float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - Runnable job = new MoveViewJob(mViewPortHandler, 0f, yValue + valsInView / 2f, + Runnable job = MoveViewJob.getInstance(mViewPortHandler, 0f, yValue + valsInView / 2f, getTransformer(axis), this); addViewportJob(job); @@ -894,7 +894,7 @@ public void centerViewTo(float xValue, float yValue, AxisDependency axis) { float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); - Runnable job = new MoveViewJob(mViewPortHandler, + Runnable job = MoveViewJob.getInstance(mViewPortHandler, xValue - xInView / 2f, yValue + yInView / 2f, getTransformer(axis), this); @@ -920,7 +920,7 @@ public void centerViewToAnimated(float xValue, float yValue, AxisDependency axis float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); - Runnable job = new AnimatedMoveViewJob(mViewPortHandler, + Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, xValue - xInView / 2f, yValue + yInView / 2f, getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java index 128fdea36f..00bddc2427 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java @@ -5,14 +5,42 @@ import android.annotation.SuppressLint; import android.view.View; +import com.github.mikephil.charting.utils.ObjectPool; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.ViewPortHandler; /** * Created by Philipp Jahoda on 19/02/16. */ +@SuppressLint("NewApi") public class AnimatedMoveViewJob extends AnimatedViewPortJob { + private static ObjectPool pool; + + static { + pool = ObjectPool.create(4, new AnimatedMoveViewJob(null,0,0,null,null,0,0,0)); + pool.setReplenishPercentage(0.5f); + } + + public static AnimatedMoveViewJob getInstance(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v, float xOrigin, float yOrigin, long duration){ + AnimatedMoveViewJob result = pool.get(); + result.mViewPortHandler = viewPortHandler; + result.xValue = xValue; + result.yValue = yValue; + result.mTrans = trans; + result.view = v; + result.xOrigin = xOrigin; + result.yOrigin = yOrigin; + result.resetAnimator(); + result.animator.setDuration(duration); + return result; + } + + public static void recycleInstance(AnimatedMoveViewJob instance){ + pool.recycle(instance); + } + + public AnimatedMoveViewJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v, float xOrigin, float yOrigin, long duration) { super(viewPortHandler, xValue, yValue, trans, v, xOrigin, yOrigin, duration); } @@ -26,4 +54,13 @@ public void onAnimationUpdate(ValueAnimator animation) { mTrans.pointValuesToPixel(pts); mViewPortHandler.centerViewPort(pts, view); } + + public void recycleSelf(){ + recycleInstance(this); + } + + @Override + protected ObjectPool.Poolable instantiate() { + return new AnimatedMoveViewJob(null,0,0,null,null,0,0,0); + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java index ea17745ee8..f8b520a419 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java @@ -1,5 +1,6 @@ package com.github.mikephil.charting.jobs; +import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; @@ -12,7 +13,7 @@ * Created by Philipp Jahoda on 19/02/16. */ @SuppressLint("NewApi") -public abstract class AnimatedViewPortJob extends ViewPortJob implements ValueAnimator.AnimatorUpdateListener { +public abstract class AnimatedViewPortJob extends ViewPortJob implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener { protected ObjectAnimator animator; @@ -28,6 +29,7 @@ public AnimatedViewPortJob(ViewPortHandler viewPortHandler, float xValue, float animator = ObjectAnimator.ofFloat(this, "phase", 0f, 1f); animator.setDuration(duration); animator.addUpdateListener(this); + animator.addListener(this); } @SuppressLint("NewApi") @@ -51,4 +53,47 @@ public float getXOrigin() { public float getYOrigin() { return yOrigin; } + + public abstract void recycleSelf(); + + protected void resetAnimator(){ + animator.removeAllListeners(); + animator.removeAllUpdateListeners(); + animator.reverse(); + animator.addUpdateListener(this); + animator.addListener(this); + } + + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + try{ + recycleSelf(); + }catch (IllegalArgumentException e){ + // don't worry about it. + } + } + + @Override + public void onAnimationCancel(Animator animation) { + try{ + recycleSelf(); + }catch (IllegalArgumentException e){ + // don't worry about it. + } + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + + } } 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 be61ce09f3..0157e8fa76 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 @@ -8,6 +8,7 @@ import com.github.mikephil.charting.charts.BarLineChartBase; import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.utils.ObjectPool; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -17,6 +18,26 @@ @SuppressLint("NewApi") public class AnimatedZoomJob extends AnimatedViewPortJob implements Animator.AnimatorListener { + private static ObjectPool pool; + + static { + pool = ObjectPool.create(8, new AnimatedZoomJob(null,null,null,null,0,0,0,0,0,0,0,0,0,0)); + } + + public static AnimatedZoomJob getInstance(ViewPortHandler viewPortHandler, View v, Transformer trans, YAxis axis, float xAxisRange, float scaleX, float scaleY, float xOrigin, float yOrigin, float zoomCenterX, float zoomCenterY, float zoomOriginX, float zoomOriginY, long duration) { + AnimatedZoomJob result = pool.get(); + result.mViewPortHandler = viewPortHandler; + result.xValue = scaleX; + result.yValue = scaleY; + result.mTrans = trans; + result.view = v; + result.xOrigin = xOrigin; + result.yOrigin = yOrigin; + result.resetAnimator(); + result.animator.setDuration(duration); + return result; + } + protected float zoomOriginX; protected float zoomOriginY; @@ -79,8 +100,18 @@ public void onAnimationRepeat(Animator animation) { } + @Override + public void recycleSelf() { + + } + @Override public void onAnimationStart(Animator animation) { } + + @Override + protected ObjectPool.Poolable instantiate() { + return new AnimatedZoomJob(null,null,null,null,0,0,0,0,0,0,0,0,0,0); + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/MoveViewJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/MoveViewJob.java index e96aba8052..46b56b1347 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/MoveViewJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/MoveViewJob.java @@ -3,6 +3,7 @@ import android.view.View; +import com.github.mikephil.charting.utils.ObjectPool; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -11,6 +12,27 @@ */ public class MoveViewJob extends ViewPortJob { + private static ObjectPool pool; + + static { + pool = ObjectPool.create(2, new MoveViewJob(null,0,0,null,null)); + pool.setReplenishPercentage(0.5f); + } + + public static MoveViewJob getInstance(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v){ + MoveViewJob result = pool.get(); + result.mViewPortHandler = viewPortHandler; + result.xValue = xValue; + result.yValue = yValue; + result.mTrans = trans; + result.view = v; + return result; + } + + public static void recycleInstance(MoveViewJob instance){ + pool.recycle(instance); + } + public MoveViewJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v) { super(viewPortHandler, xValue, yValue, trans, v); } @@ -23,5 +45,12 @@ public void run() { mTrans.pointValuesToPixel(pts); mViewPortHandler.centerViewPort(pts, view); + + this.recycleInstance(this); + } + + @Override + protected ObjectPool.Poolable instantiate() { + return new MoveViewJob(mViewPortHandler, xValue, yValue, mTrans, view); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java index 912ab38431..c424e4b87a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java @@ -3,6 +3,7 @@ import android.view.View; +import com.github.mikephil.charting.utils.ObjectPool; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -15,7 +16,7 @@ * * @author Philipp Jahoda */ -public abstract class ViewPortJob implements Runnable { +public abstract class ViewPortJob extends ObjectPool.Poolable implements Runnable { protected float[] pts = new float[2]; @@ -33,6 +34,7 @@ public ViewPortJob(ViewPortHandler viewPortHandler, float xValue, float yValue, this.yValue = yValue; this.mTrans = trans; this.view = v; + } public float getXValue() { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java index 94a8ea1e7b..00b67df0fc 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java @@ -6,6 +6,7 @@ import com.github.mikephil.charting.charts.BarLineChartBase; import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.utils.ObjectPool; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -14,6 +15,30 @@ */ public class ZoomJob extends ViewPortJob { + private static ObjectPool pool; + + static { + pool = ObjectPool.create(1, new ZoomJob(null,0,0,0,0,null,null,null)); + pool.setReplenishPercentage(0.5f); + } + + public static ZoomJob getInstance(ViewPortHandler viewPortHandler, float scaleX, float scaleY, float xValue, float yValue, Transformer trans, YAxis.AxisDependency axis, View v) { + ZoomJob result = pool.get(); + result.xValue = xValue; + result.yValue = yValue; + result.scaleX = scaleX; + result.scaleY = scaleY; + result.mViewPortHandler = viewPortHandler; + result.mTrans = trans; + result.axisDependency = axis; + result.view = v; + return result; + } + + public static void recycleInstance(ZoomJob instance){ + pool.recycle(instance); + } + protected float scaleX; protected float scaleY; @@ -48,5 +73,12 @@ public void run() { ((BarLineChartBase) view).calculateOffsets(); view.postInvalidate(); + + recycleInstance(this); + } + + @Override + protected ObjectPool.Poolable instantiate() { + return new ZoomJob(null,0,0,0,0,null,null,null); } } From 37f24984879210651a6299021964049e4282d25b Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Thu, 30 Jun 2016 16:26:53 -0700 Subject: [PATCH 284/606] Eliminate allocs - MFPoint recycle (#1892) Noticed a couple of stray un-recycled MFPoint and FSize instances. These accounted for a bit of the remaining generated garbage. --- .../com/github/mikephil/charting/renderer/XAxisRenderer.java | 3 ++- 1 file changed, 2 insertions(+), 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 ef37a3a069..8a73bced5c 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,7 +6,6 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Path; -import android.graphics.PointF; import com.github.mikephil.charting.components.LimitLine; import com.github.mikephil.charting.components.XAxis; @@ -98,6 +97,7 @@ protected void computeSize() { mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height); FSize.recycleInstance(labelRotatedSize); + FSize.recycleInstance(labelSize); } @Override @@ -141,6 +141,7 @@ public void renderAxisLabels(Canvas c) { pointF.y = 0.0f; drawLabels(c, mViewPortHandler.contentBottom() + yoffset, pointF); } + MPPointF.recycleInstance(pointF); } @Override From 66094db76409e15546921d353d1c4454a8468feb Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Thu, 30 Jun 2016 16:28:15 -0700 Subject: [PATCH 285/606] Eliminate allocs - Copy arrays without new[] (#1892) Make sure we copy List<> into [] if the size is identical to the length, instead of instantiating a new []. --- .../mikephil/charting/components/Legend.java | 51 +++++++++++++++---- .../github/mikephil/charting/utils/Utils.java | 18 +++++-- 2 files changed, 56 insertions(+), 13 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 b0b82bdc3e..65824ac978 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 @@ -171,8 +171,8 @@ public Legend(List colors, List labels) { "colors array and labels array need to be of same size"); } - this.mColors = Utils.convertIntegers(colors); - this.mLabels = Utils.convertStrings(labels); + this.setComputedColors(colors); + this.setComputedLabels(labels); } /** @@ -180,7 +180,11 @@ public Legend(List colors, List labels) { * @param colors */ public void setComputedColors(List colors) { - mColors = Utils.convertIntegers(colors); + if(mColors != null && colors.size() == mColors.length){ + Utils.copyIntegers(colors, mColors); + }else { + mColors = Utils.convertIntegers(colors); + } } /** @@ -188,7 +192,11 @@ public void setComputedColors(List colors) { * @param labels */ public void setComputedLabels(List labels) { - mLabels = Utils.convertStrings(labels); + if(mLabels != null && mLabels.length == labels.size()){ + Utils.copyStrings(labels, mLabels); + }else { + mLabels = Utils.convertStrings(labels); + } } /** @@ -291,8 +299,17 @@ public String[] getExtraLabels() { * let the changes take effect) */ public void setExtra(List colors, List labels) { - this.mExtraColors = Utils.convertIntegers(colors); - this.mExtraLabels = Utils.convertStrings(labels); + if(mExtraColors != null && mExtraColors.length == colors.size()){ + Utils.copyIntegers(colors, mExtraColors); + }else { + this.mExtraColors = Utils.convertIntegers(colors); + } + + if(mExtraLabels != null && mExtraLabels.length == labels.size()){ + Utils.copyStrings(labels, mExtraLabels); + }else { + this.mExtraLabels = Utils.convertStrings(labels); + } } /** @@ -343,8 +360,8 @@ public void setCustom(List colors, List labels) { "colors array and labels array need to be of same size"); } - mColors = Utils.convertIntegers(colors); - mLabels = Utils.convertStrings(labels); + this.setComputedColors(colors); + this.setComputedLabels(labels); mIsLegendCustom = true; } @@ -791,6 +808,7 @@ public float getMaxSizePercent() { public void setMaxSizePercent(float maxSize) { mMaxSizePercent = maxSize; } + private boolean isCalculatedLineSizesArrayListResized = true; private FSize[] mCalculatedLabelSizes = new FSize[] {}; private Boolean[] mCalculatedLabelBreakPoints = new Boolean[] {}; @@ -805,6 +823,14 @@ public Boolean[] getCalculatedLabelBreakPoints() { } public FSize[] getCalculatedLineSizes() { + if(mCalculatedLineSizes == null || isCalculatedLineSizesArrayListResized){ + + mCalculatedLineSizes = calculatedLineSizesForCalculateDimensions + .toArray(new FSize[calculatedLineSizesForCalculateDimensions.size()]); + + isCalculatedLineSizesArrayListResized = false; + + } return mCalculatedLineSizes; } @@ -998,8 +1024,13 @@ else if (wasStacked) { stackedStartIndex = mLabels[i] != null ? -1 : stackedStartIndex; } - mCalculatedLineSizes = calculatedLineSizes - .toArray(new FSize[calculatedLineSizes.size()]); + if(calculatedLineSizes.size() != mCalculatedLineSizes.length) { + isCalculatedLineSizesArrayListResized = true; + }else{ + for(int i = 0 ; i < mCalculatedLineSizes.length ; i++){ + mCalculatedLineSizes[i] = calculatedLineSizes.get(i); + } + } mNeededWidth = maxLineWidth; mNeededHeight = labelLineHeight 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 8cf77e2de1..e599fc1db7 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 @@ -369,13 +369,18 @@ public static int[] convertIntegers(List integers) { int[] ret = new int[integers.size()]; - for (int i = 0; i < ret.length; i++) { - ret[i] = integers.get(i).intValue(); - } + copyIntegers(integers, ret); return ret; } + public static void copyIntegers(List from, int[] to){ + int count = to.length < from.size() ? to.length : from.size(); + for(int i = 0 ; i < count ; i++){ + to[i] = from.get(i); + } + } + /** * Converts the provided String List to a String array. * @@ -393,6 +398,13 @@ public static String[] convertStrings(List strings) { return ret; } + public static void copyStrings(List from, String[] to){ + int count = to.length < from.size() ? to.length : from.size(); + for(int i = 0 ; i < count ; i++){ + to[i] = from.get(i); + } + } + /** * Replacement for the Math.nextUp(...) method that is only available in * HONEYCOMB and higher. Dat's some seeeeek sheeet. From 523c7e5b216e062af5564afadfdf1fa662c3fd5f Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Thu, 30 Jun 2016 17:24:32 -0700 Subject: [PATCH 286/606] Render bug fix - circle bitmap sizes (#1892) Zooming in on the circle bitmaps, they were slightly small. Allocate a little more space in the circle's bitmap to correct this. --- .../charting/renderer/LineChartRenderer.java | 18 +++++++++++++----- 1 file changed, 13 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 f44739dc8c..20ffdd9db3 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 @@ -665,7 +665,7 @@ protected void drawCircles(Canvas c) { if(circleBitmap == null){ Bitmap.Config conf = Bitmap.Config.ARGB_8888; - circleBitmap = Bitmap.createBitmap((int)circleRadius * 2, (int)circleRadius * 2, conf); + circleBitmap = Bitmap.createBitmap((int)(circleRadius * 2.1), (int)(circleRadius * 2.1), conf); Canvas canvas = new Canvas(circleBitmap); imageCache.circleBitmaps[colorIndex] = circleBitmap; imageCache.circleColors[colorIndex] = circleColor; @@ -674,12 +674,16 @@ protected void drawCircles(Canvas c) { // Begin path for circle with hole mCirclePathBuffer.reset(); - mCirclePathBuffer.addCircle(circleRadius, circleRadius, + mCirclePathBuffer.addCircle( + circleRadius, + circleRadius, circleRadius, Path.Direction.CW); // Cut hole in path - mCirclePathBuffer.addCircle(circleHoleRadius, circleHoleRadius, + mCirclePathBuffer.addCircle( + circleRadius, + circleRadius, circleHoleRadius, Path.Direction.CCW); @@ -687,12 +691,16 @@ protected void drawCircles(Canvas c) { canvas.drawPath(mCirclePathBuffer, mRenderPaint); }else{ - canvas.drawCircle(circleRadius, circleRadius, + canvas.drawCircle( + circleRadius, + circleRadius, circleRadius, mRenderPaint); if (drawCircleHole) { - canvas.drawCircle(circleRadius, circleRadius, + canvas.drawCircle( + circleRadius, + circleRadius, circleHoleRadius, mCirclePaintInner); } From 12eee703a7e1e58a343356f742f6e26724f47aa6 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 1 Jul 2016 09:13:58 +0200 Subject: [PATCH 287/606] Fix issue related to highlight callbacks #745 --- .../mikephil/charting/charts/Chart.java | 23 +++++++++++++++---- 1 file changed, 18 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 ce5d42ea65..2ff5459c41 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 @@ -543,6 +543,20 @@ public boolean valuesToHighlight() { : true; } + /** + * Sets the last highlighted value for the touchlistener. + * + * @param highs + */ + protected void setLastHighlighted(Highlight[] highs) { + + if (highs == null || highs.length <= 0 || highs[0] == null) { + mChartTouchListener.setLastHighlighted(null); + } else { + mChartTouchListener.setLastHighlighted(highs[0]); + } + } + /** * 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 @@ -556,11 +570,7 @@ public void highlightValues(Highlight[] highs) { // set the indices to highlight mIndicesToHighlight = highs; - if (highs == null || highs.length <= 0 || highs[0] == null) { - mChartTouchListener.setLastHighlighted(null); - } else { - mChartTouchListener.setLastHighlighted(highs[0]); - } + setLastHighlighted(highs); // redraw the chart invalidate(); @@ -635,6 +645,8 @@ public void highlightValue(Highlight high, boolean callListener) { } } + setLastHighlighted(mIndicesToHighlight); + if (callListener && mSelectionListener != null) { if (!valuesToHighlight()) @@ -644,6 +656,7 @@ public void highlightValue(Highlight high, boolean callListener) { mSelectionListener.onValueSelected(e, high); } } + // redraw the chart invalidate(); } From efbea0a7ef24437f49559d738b4a1cf07a043a90 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 1 Jul 2016 17:30:12 +0200 Subject: [PATCH 288/606] Documentation --- MPChartExample/build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 0cebfdf441..8a6a75823d 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -55,7 +55,10 @@ repositories { dependencies { //compile fileTree(dir: 'libs', include: ['*.jar']) - compile project(':MPChartLib-Realm') + 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.0.0' + + //compile project(':MPChartLib') compile 'com.android.support:appcompat-v7:23.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' From 0597663392f9deac0e43f30e6badfece657674e7 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 1 Jul 2016 17:49:12 +0200 Subject: [PATCH 289/606] changes to build.gradle --- MPChartLib/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index f8f8455805..1d82f5f729 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -1,6 +1,6 @@ apply plugin: 'com.android.library' apply plugin: 'maven' -//apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'com.github.dcendents.android-maven' //apply plugin: 'realm-android' android { From 6a2e6e55288c81af3a98e903a7d48369960933d7 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 1 Jul 2016 18:12:42 +0200 Subject: [PATCH 290/606] Temporary remove of realm from example --- MPChartExample/build.gradle | 6 +- .../notimportant/MainActivity.java | 5 +- .../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 ----- settings.gradle | 4 +- 15 files changed, 7 insertions(+), 1132 deletions(-) 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 diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 8a6a75823d..4a17e8dfc5 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -55,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:v1.0.0' + //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:v0.9.9' - //compile project(':MPChartLib') + compile project(':MPChartLib') compile 'com.android.support:appcompat-v7:23.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/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index 617e43c021..df7c6e07c6 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; @@ -275,8 +274,8 @@ public void onItemClick(AdapterView av, View v, int pos, long arg3) { startActivity(i); break; case 28: - i = new Intent(this, RealmMainActivity.class); - startActivity(i); + //i = new Intent(this, RealmMainActivity.class); + //startActivity(i); break; case 29: i = new Intent(this, LineChartTime.class); 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/settings.gradle b/settings.gradle index 484b8b3ecd..32efe09de1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,7 @@ include 'MPChartLib' //include 'MPAndroidChart-Realm' include 'MPChartExample' -include ':MPChartLib-Realm' -project(':MPChartLib-Realm').projectDir = new File('../MPAndroidChart-Realm/MPChartLib-Realm') +//include ':MPChartLib-Realm' +//project(':MPChartLib-Realm').projectDir = new File('../MPAndroidChart-Realm/MPChartLib-Realm') From 0789628197d9b05756a83d179889d37bba2d8abc Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 1 Jul 2016 18:54:59 +0200 Subject: [PATCH 291/606] Add realm related example code --- MPChartExample/build.gradle | 2 +- .../notimportant/MainActivity.java | 5 +- .../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 +++++ MPChartLib/build.gradle | 2 +- 15 files changed, 1129 insertions(+), 4 deletions(-) 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/build.gradle b/MPChartExample/build.gradle index 4a17e8dfc5..b9dff17945 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:v0.9.9' + compile 'com.github.PhilJay:MPAndroidChart-Realm:v1.0.0@aar' compile project(':MPChartLib') compile 'com.android.support:appcompat-v7:23.1.1' diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index df7c6e07c6..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; @@ -274,8 +275,8 @@ public void onItemClick(AdapterView av, View v, int pos, long arg3) { startActivity(i); break; case 28: - //i = new Intent(this, RealmMainActivity.class); - //startActivity(i); + i = new Intent(this, RealmMainActivity.class); + startActivity(i); break; case 29: i = new Intent(this, LineChartTime.class); 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/MPChartLib/build.gradle b/MPChartLib/build.gradle index 1d82f5f729..f8f8455805 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -1,6 +1,6 @@ apply plugin: 'com.android.library' apply plugin: 'maven' -apply plugin: 'com.github.dcendents.android-maven' +//apply plugin: 'com.github.dcendents.android-maven' //apply plugin: 'realm-android' android { From d5df3ad795cb1fa45a24deb32e4f8bdc038c8414 Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Fri, 1 Jul 2016 13:16:22 -0700 Subject: [PATCH 292/606] Crash fix - Create small clip paths (#1895) With large data sets, the Path object created was sufficiently large as to cause an OutOfMemory error. This is resolved by only pathing a limited number of points on the chart at a time, then clearing the path and resuming. Stress testing with 1500 entries. --- .../res/layout/activity_linechart.xml | 4 +- .../mpchartexample/LineChartActivity1.java | 4 +- .../RealtimeLineChartActivity.java | 2 +- .../charting/renderer/LineChartRenderer.java | 86 +++++++++++++------ 4 files changed, 65 insertions(+), 31 deletions(-) diff --git a/MPChartExample/res/layout/activity_linechart.xml b/MPChartExample/res/layout/activity_linechart.xml index 0389e9e298..7cadd8dd65 100644 --- a/MPChartExample/res/layout/activity_linechart.xml +++ b/MPChartExample/res/layout/activity_linechart.xml @@ -18,7 +18,7 @@ android:layout_margin="8dp" android:layout_toLeftOf="@+id/tvYMax" android:layout_marginRight="5dp" - android:max="200" + android:max="150" android:paddingBottom="12dp" /> endingIndex ? endingIndex : currentEndIndex; + + if(currentStartIndex <= currentEndIndex) { + generateFilledPath(dataSet, currentStartIndex, currentEndIndex, filled); + + + + trans.pathValueToPixel(filled); + + final Drawable drawable = dataSet.getFillDrawable(); + if (drawable != null) { + + drawFilledPath(c, filled, drawable); + } else { + + drawFilledPath(c, filled, dataSet.getFillColor(), dataSet.getFillAlpha()); + } + } + + iterations++; + + }while(currentStartIndex <= currentEndIndex); - drawFilledPath(c, filled, dataSet.getFillColor(), dataSet.getFillAlpha()); - } } - protected Path mGenerateFilledPathBuffer = new Path(); /** - * Generates the path that is used for filled drawing. + * Generates a path that is used for filled drawing. + * + * @param dataSet The dataset from which to read the entries. + * @param startIndex The index from which to start reading the dataset + * @param endIndex The index from which to stop reading the dataset + * @param outputPath The path object that will be assigned the chart data. * - * @param dataSet * @return */ - private Path generateFilledPath(ILineDataSet dataSet, XBounds bounds) { + private void generateFilledPath(final ILineDataSet dataSet, final int startIndex, final int endIndex, final Path outputPath) { - float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart); - float phaseY = mAnimator.getPhaseY(); + final float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart); + final float phaseY = mAnimator.getPhaseY(); final boolean isDrawSteppedEnabled = dataSet.getMode() == LineDataSet.Mode.STEPPED; - Path filled = mGenerateFilledPathBuffer; + final Path filled = outputPath; filled.reset(); - Entry entry = dataSet.getEntryForIndex(bounds.min); + + final Entry entry = dataSet.getEntryForIndex(startIndex); filled.moveTo(entry.getX(), fillMin); filled.lineTo(entry.getX(), entry.getY() * phaseY); // create a new path - for (int x = bounds.min + 1; x <= bounds.range + bounds.min; x++) { - - Entry e = dataSet.getEntryForIndex(x); + Entry currentEntry = null; + Entry previousEntry = null; + for (int x = startIndex + 1 ; x <= endIndex ; x++) { - if (isDrawSteppedEnabled) { - final Entry ePrev = dataSet.getEntryForIndex(x - 1); - if (ePrev == null) continue; + currentEntry = dataSet.getEntryForIndex(x); - filled.lineTo(e.getX(), ePrev.getY() * phaseY); + if (isDrawSteppedEnabled && previousEntry != null) { + filled.lineTo(currentEntry.getX(), previousEntry.getY() * phaseY); } - filled.lineTo(e.getX(), e.getY() * phaseY); + filled.lineTo(currentEntry.getX(), currentEntry.getY() * phaseY); + + previousEntry = currentEntry; } // close up - filled.lineTo(dataSet.getEntryForIndex(bounds.range + bounds.min).getX(), fillMin); + if(currentEntry != null) { + filled.lineTo(currentEntry.getX(), fillMin); + } + filled.close(); - return filled; } @Override From 87758604d31270d19bbd55c4b34438ca0ed594da Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Fri, 1 Jul 2016 13:17:49 -0700 Subject: [PATCH 293/606] Revert "Crash fix - Create small clip paths (#1895)" This reverts commit d5df3ad795cb1fa45a24deb32e4f8bdc038c8414. --- .../res/layout/activity_linechart.xml | 4 +- .../mpchartexample/LineChartActivity1.java | 4 +- .../RealtimeLineChartActivity.java | 2 +- .../charting/renderer/LineChartRenderer.java | 86 ++++++------------- 4 files changed, 31 insertions(+), 65 deletions(-) diff --git a/MPChartExample/res/layout/activity_linechart.xml b/MPChartExample/res/layout/activity_linechart.xml index 7cadd8dd65..0389e9e298 100644 --- a/MPChartExample/res/layout/activity_linechart.xml +++ b/MPChartExample/res/layout/activity_linechart.xml @@ -18,7 +18,7 @@ android:layout_margin="8dp" android:layout_toLeftOf="@+id/tvYMax" android:layout_marginRight="5dp" - android:max="150" + android:max="200" android:paddingBottom="12dp" /> endingIndex ? endingIndex : currentEndIndex; - - if(currentStartIndex <= currentEndIndex) { - generateFilledPath(dataSet, currentStartIndex, currentEndIndex, filled); - + Path filled = generateFilledPath(dataSet, bounds); + trans.pathValueToPixel(filled); - trans.pathValueToPixel(filled); - - final Drawable drawable = dataSet.getFillDrawable(); - if (drawable != null) { - - drawFilledPath(c, filled, drawable); - } else { - - drawFilledPath(c, filled, dataSet.getFillColor(), dataSet.getFillAlpha()); - } - } - - iterations++; + final Drawable drawable = dataSet.getFillDrawable(); + if (drawable != null) { - }while(currentStartIndex <= currentEndIndex); + drawFilledPath(c, filled, drawable); + } else { + drawFilledPath(c, filled, dataSet.getFillColor(), dataSet.getFillAlpha()); + } } + protected Path mGenerateFilledPathBuffer = new Path(); /** - * Generates a path that is used for filled drawing. - * - * @param dataSet The dataset from which to read the entries. - * @param startIndex The index from which to start reading the dataset - * @param endIndex The index from which to stop reading the dataset - * @param outputPath The path object that will be assigned the chart data. + * Generates the path that is used for filled drawing. * + * @param dataSet * @return */ - private void generateFilledPath(final ILineDataSet dataSet, final int startIndex, final int endIndex, final Path outputPath) { + private Path generateFilledPath(ILineDataSet dataSet, XBounds bounds) { - final float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart); - final float phaseY = mAnimator.getPhaseY(); + float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart); + float phaseY = mAnimator.getPhaseY(); final boolean isDrawSteppedEnabled = dataSet.getMode() == LineDataSet.Mode.STEPPED; - final Path filled = outputPath; + Path filled = mGenerateFilledPathBuffer; filled.reset(); - - final Entry entry = dataSet.getEntryForIndex(startIndex); + Entry entry = dataSet.getEntryForIndex(bounds.min); filled.moveTo(entry.getX(), fillMin); filled.lineTo(entry.getX(), entry.getY() * phaseY); // create a new path - Entry currentEntry = null; - Entry previousEntry = null; - for (int x = startIndex + 1 ; x <= endIndex ; x++) { + for (int x = bounds.min + 1; x <= bounds.range + bounds.min; x++) { - currentEntry = dataSet.getEntryForIndex(x); + Entry e = dataSet.getEntryForIndex(x); - if (isDrawSteppedEnabled && previousEntry != null) { - filled.lineTo(currentEntry.getX(), previousEntry.getY() * phaseY); - } + if (isDrawSteppedEnabled) { + final Entry ePrev = dataSet.getEntryForIndex(x - 1); + if (ePrev == null) continue; - filled.lineTo(currentEntry.getX(), currentEntry.getY() * phaseY); + filled.lineTo(e.getX(), ePrev.getY() * phaseY); + } - previousEntry = currentEntry; + filled.lineTo(e.getX(), e.getY() * phaseY); } // close up - if(currentEntry != null) { - filled.lineTo(currentEntry.getX(), fillMin); - } - + filled.lineTo(dataSet.getEntryForIndex(bounds.range + bounds.min).getX(), fillMin); filled.close(); + return filled; } @Override From b5da8dcdb585ad94fefd837d07da638ca40aaa24 Mon Sep 17 00:00:00 2001 From: Tony Patino Date: Fri, 1 Jul 2016 13:19:37 -0700 Subject: [PATCH 294/606] Crash fix - Create small clip paths (#1895) With large data sets, the Path object created was sufficiently large as to cause an OutOfMemory error. This is resolved by only pathing a limited number of points on the chart at a time, then clearing the path and resuming. Stress testing with 1500 entries. --- .../res/layout/activity_linechart.xml | 4 +- .../mpchartexample/LineChartActivity1.java | 4 +- .../charting/renderer/LineChartRenderer.java | 86 +++++++++++++------ 3 files changed, 64 insertions(+), 30 deletions(-) diff --git a/MPChartExample/res/layout/activity_linechart.xml b/MPChartExample/res/layout/activity_linechart.xml index 0389e9e298..7cadd8dd65 100644 --- a/MPChartExample/res/layout/activity_linechart.xml +++ b/MPChartExample/res/layout/activity_linechart.xml @@ -18,7 +18,7 @@ android:layout_margin="8dp" android:layout_toLeftOf="@+id/tvYMax" android:layout_marginRight="5dp" - android:max="200" + android:max="150" android:paddingBottom="12dp" /> endingIndex ? endingIndex : currentEndIndex; + + if(currentStartIndex <= currentEndIndex) { + generateFilledPath(dataSet, currentStartIndex, currentEndIndex, filled); + + + + trans.pathValueToPixel(filled); + + final Drawable drawable = dataSet.getFillDrawable(); + if (drawable != null) { + + drawFilledPath(c, filled, drawable); + } else { + + drawFilledPath(c, filled, dataSet.getFillColor(), dataSet.getFillAlpha()); + } + } + + iterations++; + + }while(currentStartIndex <= currentEndIndex); - drawFilledPath(c, filled, dataSet.getFillColor(), dataSet.getFillAlpha()); - } } - protected Path mGenerateFilledPathBuffer = new Path(); /** - * Generates the path that is used for filled drawing. + * Generates a path that is used for filled drawing. + * + * @param dataSet The dataset from which to read the entries. + * @param startIndex The index from which to start reading the dataset + * @param endIndex The index from which to stop reading the dataset + * @param outputPath The path object that will be assigned the chart data. * - * @param dataSet * @return */ - private Path generateFilledPath(ILineDataSet dataSet, XBounds bounds) { + private void generateFilledPath(final ILineDataSet dataSet, final int startIndex, final int endIndex, final Path outputPath) { - float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart); - float phaseY = mAnimator.getPhaseY(); + final float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart); + final float phaseY = mAnimator.getPhaseY(); final boolean isDrawSteppedEnabled = dataSet.getMode() == LineDataSet.Mode.STEPPED; - Path filled = mGenerateFilledPathBuffer; + final Path filled = outputPath; filled.reset(); - Entry entry = dataSet.getEntryForIndex(bounds.min); + + final Entry entry = dataSet.getEntryForIndex(startIndex); filled.moveTo(entry.getX(), fillMin); filled.lineTo(entry.getX(), entry.getY() * phaseY); // create a new path - for (int x = bounds.min + 1; x <= bounds.range + bounds.min; x++) { - - Entry e = dataSet.getEntryForIndex(x); + Entry currentEntry = null; + Entry previousEntry = null; + for (int x = startIndex + 1 ; x <= endIndex ; x++) { - if (isDrawSteppedEnabled) { - final Entry ePrev = dataSet.getEntryForIndex(x - 1); - if (ePrev == null) continue; + currentEntry = dataSet.getEntryForIndex(x); - filled.lineTo(e.getX(), ePrev.getY() * phaseY); + if (isDrawSteppedEnabled && previousEntry != null) { + filled.lineTo(currentEntry.getX(), previousEntry.getY() * phaseY); } - filled.lineTo(e.getX(), e.getY() * phaseY); + filled.lineTo(currentEntry.getX(), currentEntry.getY() * phaseY); + + previousEntry = currentEntry; } // close up - filled.lineTo(dataSet.getEntryForIndex(bounds.range + bounds.min).getX(), fillMin); + if(currentEntry != null) { + filled.lineTo(currentEntry.getX(), fillMin); + } + filled.close(); - return filled; } @Override From 43fa1e6cecf9b194be7dc56e4a01df9f6ea3f22b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 2 Jul 2016 12:29:50 +0200 Subject: [PATCH 295/606] Merge #1947 --- .../mpchartexample/LineChartActivity2.java | 1 + .../mikephil/charting/charts/BarChart.java | 2 - .../charting/charts/BarLineChartBase.java | 4 + .../mikephil/charting/charts/Chart.java | 45 ------ .../charting/charts/HorizontalBarChart.java | 4 - .../mikephil/charting/data/BarDataSet.java | 3 +- .../mikephil/charting/data/BaseDataSet.java | 8 +- .../mikephil/charting/data/BubbleData.java | 8 +- .../mikephil/charting/data/BubbleDataSet.java | 4 +- .../mikephil/charting/data/CandleDataSet.java | 5 +- .../mikephil/charting/data/ChartData.java | 123 ++++++--------- .../mikephil/charting/data/CombinedData.java | 8 +- .../mikephil/charting/data/DataSet.java | 4 +- .../mikephil/charting/data/LineDataSet.java | 1 + .../mikephil/charting/data/ScatterData.java | 10 +- .../charting/highlight/ChartHighlighter.java | 16 +- .../highlight/CombinedHighlighter.java | 12 +- .../highlight/PieRadarHighlighter.java | 11 +- .../charting/highlight/RadarHighlighter.java | 12 +- .../BarLineScatterCandleBubbleRenderer.java | 24 +-- .../renderer/BubbleChartRenderer.java | 15 +- .../renderer/CandleStickChartRenderer.java | 12 +- .../renderer/CombinedChartRenderer.java | 19 +-- .../charting/renderer/LineChartRenderer.java | 147 ++++++++---------- .../renderer/ScatterChartRenderer.java | 8 +- .../charting/renderer/XAxisRenderer.java | 12 +- .../XAxisRendererHorizontalBarChart.java | 6 +- 27 files changed, 191 insertions(+), 333 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index afdf3eadd7..1508aea66b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -58,6 +58,7 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (LineChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); + mChart.setLogEnabled(true); // no description text mChart.setDescription(""); 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 05a0e1a1eb..f3d39b97f5 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 @@ -105,7 +105,6 @@ public Highlight getHighlightByTouchPoint(float x, float y) { */ public RectF getBarBounds(BarEntry e) { - RectF bounds = new RectF(); getBarBounds(e, bounds); @@ -143,7 +142,6 @@ public void getBarBounds(BarEntry e, RectF outputRect){ bounds.set(left, top, right, bottom); getTransformer(set.getAxisDependency()).rectValueToPixel(outputRect); - } /** 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 f734a59e71..33de770eb3 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 @@ -1280,7 +1280,9 @@ public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float return null; } + /** buffer for storing lowest visible x point */ protected PointD posForGetLowestVisibleX = PointD.getInstance(0,0); + /** * Returns the lowest x-index (value on the x-axis) that is still visible on * the chart. @@ -1295,7 +1297,9 @@ public float getLowestVisibleX() { return result; } + /** buffer for storing highest visible x point */ protected PointD posForGetHighestVisibleX = PointD.getInstance(0,0); + /** * Returns the highest x-index (value on the x-axis) that is still visible * on the chart. 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 9709488bc1..c7ae8a57ff 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 @@ -12,7 +12,6 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; -import android.graphics.PointF; import android.graphics.RectF; import android.graphics.Typeface; import android.graphics.drawable.Drawable; @@ -1429,50 +1428,6 @@ public void setDrawMarkerViews(boolean enabled) { mDrawMarkerViews = enabled; } - /** - * Get all Entry objects at the given index across all DataSets. - * INFORMATION: This method does calculations at runtime. Do not over-use in - * performance critical situations. - * - * @param xIndex - * @return - */ - public List getEntriesAtIndex(int xIndex) { - - List vals = new ArrayList(); - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - IDataSet set = mData.getDataSetByIndex(i); - - Entry e = set.getEntryForXPos(xIndex); - - if (e != null) { - vals.add(e); - } - } - - return vals; - } - - public void getEntriesAtIndex(int xIndex, List entriesOutput){ - - List vals = entriesOutput; - vals.clear(); - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - IDataSet set = mData.getDataSetByIndex(i); - - Entry e = set.getEntryForXPos(xIndex); - - if (e != null) { - vals.add(e); - } - } - - } - /** * Returns the ChartData object that has been set for the chart. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java index 0c14db733a..806717a620 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 @@ -1,7 +1,6 @@ package com.github.mikephil.charting.charts; import android.content.Context; -import android.graphics.PointF; import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; @@ -18,7 +17,6 @@ import com.github.mikephil.charting.renderer.YAxisRendererHorizontalBarChart; import com.github.mikephil.charting.utils.HorizontalViewPortHandler; import com.github.mikephil.charting.utils.MPPointF; -import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.TransformerHorizontalBarChart; import com.github.mikephil.charting.utils.Utils; @@ -209,7 +207,6 @@ public Highlight getHighlightByTouchPoint(float x, float y) { return getHighlighter().getHighlight(y, x); // switch x and y } - protected PointD posForGetLowestVisibleX = PointD.getInstance(0,0); @Override public float getLowestVisibleX() { getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), @@ -218,7 +215,6 @@ public float getLowestVisibleX() { return result; } - protected PointD posForGetHighestVisibleX = PointD.getInstance(0,0); @Override public float getHighestVisibleX() { getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), 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 8e78de3397..63b5222198 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 @@ -51,11 +51,10 @@ public BarDataSet(List yVals, String label) { calcEntryCountIncludingStacks(yVals); } - protected List barEntriesForCopy = new ArrayList<>(); @Override public DataSet copy() { - List yVals = barEntriesForCopy; + List yVals = new ArrayList(); yVals.clear(); for (int i = 0; i < mValues.size(); 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 4f64c368f2..4c9f8e13e4 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 @@ -169,14 +169,12 @@ public void setColors(int[] colors, Context c) { if(mColors == null){ mColors = new ArrayList<>(); } - List clrs = mColors; - clrs.clear(); + + mColors.clear(); for (int color : colors) { - clrs.add(c.getResources().getColor(color)); + mColors.add(c.getResources().getColor(color)); } - - mColors = clrs; } /** 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 c7522599a9..bf1be94a86 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 @@ -3,7 +3,6 @@ import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; -import java.util.ArrayList; import java.util.List; public class BubbleData extends BarLineScatterCandleBubbleData { @@ -28,11 +27,8 @@ public BubbleData(List dataSets) { * @param width */ public void setHighlightCircleWidth(float width) { - IBubbleDataSet set; - final int setCount = mDataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = mDataSets.get(i); - set.setHighlightCircleWidth(width); + for(int i = 0 ; i < mDataSets.size() ; i++){ + mDataSets.get(i).setHighlightCircleWidth(width); } } } 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 3c2858fbf7..ec288123e2 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 @@ -51,12 +51,10 @@ public void calcMinMax() { } } - protected ArrayList bubbleEntriesForCopy = new ArrayList<>(); @Override public DataSet copy() { - List yVals = bubbleEntriesForCopy; - yVals.clear(); + List yVals = new ArrayList(); for (int i = 0; i < mValues.size(); i++) { yVals.add(mValues.get(i).copy()); 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 8af41c7a80..139a1bad42 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 @@ -77,15 +77,14 @@ public CandleDataSet(List yVals, String label) { super(yVals, label); } - protected ArrayList candleEntriesForCopy = new ArrayList<>(); @Override public DataSet copy() { - List yVals = candleEntriesForCopy; + List yVals = new ArrayList(); yVals.clear(); for (int i = 0; i < mValues.size(); i++) { - yVals.add(((CandleEntry) mValues.get(i)).copy()); + yVals.add(mValues.get(i).copy()); } CandleDataSet copied = new CandleDataSet(yVals, getLabel()); 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 0d243c6a1c..a8496b3bfb 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 @@ -10,7 +10,6 @@ import com.github.mikephil.charting.interfaces.datasets.IDataSet; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** @@ -80,12 +79,10 @@ public ChartData(T... dataSets) { */ private List arrayToList(T[] array) { - int setsCount = array.length; - List list = new ArrayList<>(setsCount); + List list = new ArrayList(); - T set = null; - for(int i = 0 ; i < setsCount ; i++){ - set = array[i]; + for (int i = 0; i < array.length; i++) { + T set = array[i]; list.add(set); } @@ -132,10 +129,8 @@ public void calcMinMax() { mXMax = -Float.MAX_VALUE; mXMin = Float.MAX_VALUE; - T set; - int setCount = mDataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = mDataSets.get(i); + for (int i = 0; i < mDataSets.size(); i++) { + T set = mDataSets.get(i); calcMinMax(set); } @@ -152,10 +147,9 @@ public void calcMinMax() { mLeftAxisMax = firstLeft.getYMax(); mLeftAxisMin = firstLeft.getYMin(); - IDataSet dataSet; - int dataSetsCount = mDataSets.size(); - for(int i = 0 ; i < dataSetsCount ; i++){ - dataSet = mDataSets.get(i); + for (int i = 0; i < mDataSets.size(); i++) { + + T dataSet = mDataSets.get(i); if (dataSet.getAxisDependency() == AxisDependency.LEFT) { if (dataSet.getYMin() < mLeftAxisMin) mLeftAxisMin = dataSet.getYMin(); @@ -174,10 +168,9 @@ public void calcMinMax() { mRightAxisMax = firstRight.getYMax(); mRightAxisMin = firstRight.getYMin(); - IDataSet dataSet; - int dataSetsCount = mDataSets.size(); - for(int i = 0 ; i < dataSetsCount ; i++){ - dataSet = mDataSets.get(i); + for (int i = 0; i < mDataSets.size(); i++) { + + T dataSet = mDataSets.get(i); if (dataSet.getAxisDependency() == AxisDependency.RIGHT) { if (dataSet.getYMin() < mRightAxisMin) mRightAxisMin = dataSet.getYMin(); @@ -321,7 +314,7 @@ protected int getDataSetIndexByLabel(List dataSets, String label, * * @return */ - protected String[] getDataSetLabels() { + public String[] getDataSetLabels() { String[] types = new String[mDataSets.size()]; @@ -626,10 +619,9 @@ public int getIndexOfDataSet(T dataSet) { * @return */ protected T getFirstLeft(List sets) { - T dataSet; - int setCount = sets.size(); - for(int i = 0 ; i < setCount ; i++){ - dataSet = sets.get(i); + + for (int i = 0; i < sets.size(); i++) { + T dataSet = sets.get(i); if (dataSet.getAxisDependency() == AxisDependency.LEFT) return dataSet; } @@ -643,10 +635,9 @@ protected T getFirstLeft(List sets) { * @return */ public T getFirstRight(List sets) { - T dataSet; - int setCount = sets.size(); - for(int i = 0 ; i < setCount ; i++){ - dataSet = sets.get(i); + + for (int i = 0; i < sets.size(); i++) { + T dataSet = sets.get(i); if (dataSet.getAxisDependency() == AxisDependency.RIGHT) return dataSet; } @@ -662,10 +653,9 @@ public void setValueFormatter(ValueFormatter f) { if (f == null) return; else { - IDataSet set; - final int setCount = mDataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = mDataSets.get(i); + + for (int i = 0; i < mDataSets.size(); i++) { + T set = mDataSets.get(i); set.setValueFormatter(f); } } @@ -678,10 +668,9 @@ public void setValueFormatter(ValueFormatter f) { * @param color */ public void setValueTextColor(int color) { - IDataSet set; - final int setCount = mDataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = mDataSets.get(i); + + for (int i = 0; i < mDataSets.size(); i++) { + T set = mDataSets.get(i); set.setValueTextColor(color); } } @@ -693,10 +682,9 @@ public void setValueTextColor(int color) { * @param colors */ public void setValueTextColors(List colors) { - IDataSet set; - final int setCount = mDataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = mDataSets.get(i); + + for (int i = 0; i < mDataSets.size(); i++) { + T set = mDataSets.get(i); set.setValueTextColors(colors); } } @@ -708,10 +696,9 @@ public void setValueTextColors(List colors) { * @param tf */ public void setValueTypeface(Typeface tf) { - IDataSet set; - final int setCount = mDataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = mDataSets.get(i); + + for (int i = 0; i < mDataSets.size(); i++) { + T set = mDataSets.get(i); set.setValueTypeface(tf); } } @@ -723,10 +710,9 @@ public void setValueTypeface(Typeface tf) { * @param size */ public void setValueTextSize(float size) { - IDataSet set; - final int setCount = mDataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = mDataSets.get(i); + + for (int i = 0; i < mDataSets.size(); i++) { + T set = mDataSets.get(i); set.setValueTextSize(size); } } @@ -738,10 +724,9 @@ public void setValueTextSize(float size) { * @param enabled */ public void setDrawValues(boolean enabled) { - IDataSet set; - final int setCount = mDataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = mDataSets.get(i); + + for (int i = 0; i < mDataSets.size(); i++) { + T set = mDataSets.get(i); set.setDrawValues(enabled); } } @@ -752,10 +737,9 @@ public void setDrawValues(boolean enabled) { * be highlighted programmatically or by touch gesture. */ public void setHighlightEnabled(boolean enabled) { - IDataSet set; - final int setCount = mDataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = mDataSets.get(i); + + for (int i = 0; i < mDataSets.size(); i++) { + T set = mDataSets.get(i); set.setHighlightEnabled(enabled); } } @@ -767,10 +751,9 @@ public void setHighlightEnabled(boolean enabled) { * @return */ public boolean isHighlightEnabled() { - IDataSet set; - final int setCount = mDataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = mDataSets.get(i); + + for (int i = 0; i < mDataSets.size(); i++) { + T set = mDataSets.get(i); if (!set.isHighlightEnabled()) return false; } @@ -782,7 +765,9 @@ public boolean isHighlightEnabled() { * forget to invalidate the chart after this. */ public void clearValues() { - mDataSets.clear(); + if (mDataSets != null) { + mDataSets.clear(); + } notifyDataChanged(); } @@ -795,10 +780,8 @@ public void clearValues() { */ public boolean contains(T dataSet) { - T set; - int setCount = mDataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = mDataSets.get(i); + for (int i = 0; i < mDataSets.size(); i++) { + T set = mDataSets.get(i); if (set.equals(dataSet)) return true; } @@ -815,10 +798,8 @@ public int getEntryCount() { int count = 0; - T set; - int setCount = mDataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = mDataSets.get(i); + for (int i = 0; i < mDataSets.size(); i++) { + T set = mDataSets.get(i); count += set.getEntryCount(); } @@ -837,10 +818,8 @@ public T getMaxEntryCountSet() { T max = mDataSets.get(0); - T set; - int setCount = mDataSets.size(); - for(int i = 0 ; i < setCount ; i++){ - set = mDataSets.get(i); + for (int i = 0; i < mDataSets.size(); i++) { + T set = mDataSets.get(i); if (set.getEntryCount() > max.getEntryCount()) max = 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 2c650269b6..0b6b94437a 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 @@ -193,12 +193,12 @@ public Entry getEntryForHighlight(Highlight highlight) { // 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()) + List entries = data.getDataSetByIndex(highlight.getDataSetIndex()) .getEntriesForXPos(highlight.getX()); - for (Object entry : entries) - if (((Entry) entry).getY() == highlight.getY() || + for (Entry entry : entries) + if (entry.getY() == highlight.getY() || Float.isNaN(highlight.getY())) - return (Entry) entry; + return entry; return null; } 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 84cf7c8bcb..42571e95de 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 @@ -295,7 +295,6 @@ public int getEntryIndex(float xPos, Rounding rounding) { return high; } - protected ArrayList entriesForGetEntriesForXPos = new ArrayList<>(2); /** * Returns all Entry objects at the given xIndex. INFORMATION: This method * does calculations at runtime. Do not over-use in performance critical @@ -307,8 +306,7 @@ public int getEntryIndex(float xPos, Rounding rounding) { @Override public List getEntriesForXPos(float xVal) { - List entries = entriesForGetEntriesForXPos; - entries.clear(); + List entries = new ArrayList(); int low = 0; int high = mValues.size() - 1; 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 0110ecd234..c568ab0671 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 @@ -255,6 +255,7 @@ public int getCircleColor(int index) { return mCircleColors.get(index % mCircleColors.size()); } + @Override public int getCircleColorCount(){ return mCircleColors.size(); } 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 f34c5ae3a3..930eb04198 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 @@ -3,7 +3,6 @@ import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; -import java.util.ArrayList; import java.util.List; public class ScatterData extends BarLineScatterCandleBubbleData { @@ -22,18 +21,15 @@ public ScatterData(IScatterDataSet... dataSets) { /** * Returns the maximum shape-size across all DataSets. - * + * * @return */ public float getGreatestShapeSize() { float max = 0f; - - IScatterDataSet set; - final int count = mDataSets.size(); - for(int i = 0 ; i < count ; i++){ - set = mDataSets.get(i); + for (int i = 0; i < mDataSets.size(); i++) { + IScatterDataSet set = mDataSets.get(i); float size = set.getScatterShapeSize(); if (size > max) 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 4f96edfce4..529ba8894a 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 @@ -21,6 +21,11 @@ public class ChartHighlighter */ protected T mChart; + /** + * buffer for storing previously highlighted values + */ + protected List mHighlightBuffer = new ArrayList(); + public ChartHighlighter(T chart) { this.mChart = chart; } @@ -110,8 +115,6 @@ protected float getHighlightPos(Highlight h) { return h.getYPx(); } - - protected ArrayList highlightsForGetHighlightsAtXPos = new ArrayList<>(2); /** * Returns a list of Highlight objects representing the entries closest to the given xVal. * The returned list contains two objects per DataSet (closest rounding up, closest rounding down). @@ -123,13 +126,12 @@ protected float getHighlightPos(Highlight h) { */ protected List getHighlightsAtXPos(float xVal, float x, float y) { - List vals = highlightsForGetHighlightsAtXPos; - vals.clear(); + mHighlightBuffer.clear(); BarLineScatterCandleBubbleData data = getData(); if (data == null) - return vals; + return mHighlightBuffer; for (int i = 0, dataSetCount = data.getDataSetCount(); i < dataSetCount; i++) { @@ -142,11 +144,11 @@ protected List getHighlightsAtXPos(float xVal, float x, float y) { Highlight high = buildHighlight(dataSet, i, xVal, DataSet.Rounding.CLOSEST); if(high != null) - vals.add(high); + mHighlightBuffer.add(high); //vals.add(buildHighlight(dataSet, i, xVal, DataSet.Rounding.DOWN)); } - return vals; + return mHighlightBuffer; } /** 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 d948cedf7c..eaff38c27c 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 @@ -8,7 +8,6 @@ import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider; import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import java.util.ArrayList; import java.util.List; /** @@ -28,13 +27,10 @@ public CombinedHighlighter(CombinedDataProvider chart, BarDataProvider barChart) barHighlighter = barChart.getBarData() == null ? null : new BarHighlighter(barChart); } - - protected ArrayList highlightsForGetHighlightsAtXPos = new ArrayList<>(2); @Override protected List getHighlightsAtXPos(float xVal, float x, float y) { - List vals = highlightsForGetHighlightsAtXPos; - vals.clear(); + mHighlightBuffer.clear(); List dataObjects = mChart.getCombinedData().getAllData(); @@ -48,7 +44,7 @@ protected List getHighlightsAtXPos(float xVal, float x, float y) { if (high != null) { high.setDataIndex(i); - vals.add(high); + mHighlightBuffer.add(high); } } else { @@ -62,7 +58,7 @@ protected List getHighlightsAtXPos(float xVal, float x, float y) { Highlight s1 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.CLOSEST); s1.setDataIndex(i); - vals.add(s1); + mHighlightBuffer.add(s1); // Highlight s2 = buildHighlight(dataSet, j, xVal, DataSet.Rounding.DOWN); // s2.setDataIndex(i); @@ -71,7 +67,7 @@ protected List getHighlightsAtXPos(float xVal, float x, float y) { } } - return vals; + return mHighlightBuffer; } // protected Highlight getClosest(float x, float y, Highlight... highs) { 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 029212c84e..9c067de808 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 @@ -1,13 +1,7 @@ package com.github.mikephil.charting.highlight; -import android.graphics.PointF; - import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.charts.PieRadarChartBase; -import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; import java.util.List; @@ -19,6 +13,11 @@ public abstract class PieRadarHighlighter implement protected T mChart; + /** + * buffer for storing previously highlighted values + */ + protected List mHighlightBuffer = new ArrayList(); + public PieRadarHighlighter(T chart) { this.mChart = chart; } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java index cb0048c477..3c4f6d03ac 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java @@ -1,14 +1,11 @@ package com.github.mikephil.charting.highlight; -import android.graphics.PointF; - import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; -import java.util.ArrayList; import java.util.List; /** @@ -43,8 +40,6 @@ protected Highlight getClosestHighlight(int index, float x, float y) { return closest; } - - protected ArrayList highlightsForGetHighlightsAtIndex = new ArrayList<>(2); /** * Returns an array of Highlight objects for the given index. The Highlight * objects give information about the value at the selected index and the @@ -56,8 +51,7 @@ protected Highlight getClosestHighlight(int index, float x, float y) { */ protected List getHighlightsAtIndex(int index) { - List vals = highlightsForGetHighlightsAtIndex; - vals.clear(); + mHighlightBuffer.clear(); float phaseX = mChart.getAnimator().getPhaseX(); float phaseY = mChart.getAnimator().getPhaseY(); @@ -77,9 +71,9 @@ protected List getHighlightsAtIndex(int index) { mChart.getCenterOffsets(), y * factor * phaseY, sliceangle * index * phaseX + mChart.getRotationAngle(), pOut); - vals.add(new Highlight(index, entry.getY(), pOut.x, pOut.y, i, dataSet.getAxisDependency())); + mHighlightBuffer.add(new Highlight(index, entry.getY(), pOut.x, pOut.y, i, dataSet.getAxisDependency())); } - return vals; + return mHighlightBuffer; } } 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 9028e94704..aa400caa61 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 @@ -1,7 +1,6 @@ package com.github.mikephil.charting.renderer; import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.data.DataSet; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; @@ -13,6 +12,8 @@ */ public abstract class BarLineScatterCandleBubbleRenderer extends DataRenderer { + /** buffer for storing the current minimum and maximum visible x */ + protected XBounds mXBounds = new XBounds(); public BarLineScatterCandleBubbleRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { super(animator, viewPortHandler); @@ -39,17 +40,6 @@ protected boolean isInBoundsX(Entry e, IBarLineScatterCandleBubbleDataSet set) { } } - /** - * Calculates and returns the x-bounds for the given DataSet in terms of index in their values array. This - * includes minimum and maximum visible x, as well as range. - * - * @param dataSet - * @return - */ - protected XBounds getXBounds(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet) { - return new XBounds(chart, dataSet); - } - /** * Class representing the bounds of the current viewport in terms of indices in the values array of a DataSet. */ @@ -70,16 +60,6 @@ protected class XBounds { */ public int range; - /** - * Calculates the minimum and maximum x values as well as the range between them. - * - * @param chart - * @param dataSet - */ - public XBounds(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet) { - this.set(chart, dataSet); - } - /** * Calculates the minimum and maximum x values as well as the range between them. * 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 0712c0f84a..6ff540cc7c 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 @@ -10,7 +10,6 @@ import com.github.mikephil.charting.data.BubbleEntry; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; @@ -50,7 +49,7 @@ public void drawData(Canvas c) { IBubbleDataSet set; List dataSets = bubbleData.getDataSets(); int setCount = dataSets.size(); - for(int i = 0 ; i < setCount ; i++){ + for (int i = 0; i < setCount; i++) { set = dataSets.get(i); if (set.isVisible() && set.getEntryCount() > 0) @@ -74,7 +73,7 @@ protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { float phaseY = mAnimator.getPhaseY(); - XBounds bounds = getXBounds(mChart, dataSet); + mXBounds.set(mChart, dataSet); sizeBuffer[0] = 0f; sizeBuffer[2] = 1f; @@ -88,7 +87,7 @@ protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { final float maxBubbleHeight = Math.abs(mViewPortHandler.contentBottom() - mViewPortHandler.contentTop()); final float referenceSize = Math.min(maxBubbleHeight, maxBubbleWidth); - for (int j = bounds.min; j <= bounds.range + bounds.min; j++) { + for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) { final BubbleEntry entry = dataSet.getEntryForIndex(j); @@ -143,16 +142,16 @@ public void drawValues(Canvas c) { final float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); final float phaseY = mAnimator.getPhaseY(); - XBounds bounds = getXBounds(mChart, dataSet); + mXBounds.set(mChart, dataSet); final float[] positions = mChart.getTransformer(dataSet.getAxisDependency()) - .generateTransformedValuesBubble(dataSet, phaseY, bounds.min, bounds.max); + .generateTransformedValuesBubble(dataSet, phaseY, mXBounds.min, mXBounds.max); final float alpha = phaseX == 1 ? phaseY : phaseX; for (int j = 0; j < positions.length; j += 2) { - int valueTextColor = dataSet.getValueTextColor(j / 2 + bounds.min); + int valueTextColor = dataSet.getValueTextColor(j / 2 + mXBounds.min); valueTextColor = Color.argb(Math.round(255.f * alpha), Color.red(valueTextColor), Color.green(valueTextColor), Color.blue(valueTextColor)); @@ -165,7 +164,7 @@ public void drawValues(Canvas c) { if ((!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y))) continue; - BubbleEntry entry = dataSet.getEntryForIndex(j / 2 + bounds.min); + BubbleEntry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); drawValue(c, dataSet.getValueFormatter(), entry.getSize(), entry, i, x, y + (0.5f * lineHeight), valueTextColor); 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 9a1842221e..3b99a074f5 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 @@ -47,7 +47,7 @@ public void drawData(Canvas c) { ICandleDataSet set; List dataSets = candleData.getDataSets(); int setCount = dataSets.size(); - for(int i = 0 ; i < setCount ; i++){ + for (int i = 0; i < setCount; i++) { set = dataSets.get(i); if (set.isVisible() && set.getEntryCount() > 0) @@ -64,12 +64,12 @@ protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { float barSpace = dataSet.getBarSpace(); boolean showCandleBar = dataSet.getShowCandleBar(); - XBounds bounds = getXBounds(mChart, dataSet); + mXBounds.set(mChart, dataSet); mRenderPaint.setStrokeWidth(dataSet.getShadowWidth()); // draw the body - for (int j = bounds.min; j <= bounds.range + bounds.min; j++) { + for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) { // get the entry CandleEntry e = dataSet.getEntryForIndex(j); @@ -274,10 +274,10 @@ public void drawValues(Canvas c) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - XBounds bounds = getXBounds(mChart, dataSet); + mXBounds.set(mChart, dataSet); float[] positions = trans.generateTransformedValuesCandle( - dataSet, mAnimator.getPhaseX(), mAnimator.getPhaseY(), bounds.min, bounds.max); + dataSet, mAnimator.getPhaseX(), mAnimator.getPhaseY(), mXBounds.min, mXBounds.max); float yOffset = Utils.convertDpToPixel(5f); @@ -292,7 +292,7 @@ public void drawValues(Canvas c) { if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) continue; - CandleEntry entry = dataSet.getEntryForIndex(j / 2 + bounds.min); + CandleEntry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); drawValue(c, dataSet.getValueFormatter(), entry.getHigh(), entry, i, x, y - yOffset, dataSet .getValueTextColor(j / 2)); 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 f6c671d1cf..142dff4346 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 @@ -23,7 +23,7 @@ public class CombinedChartRenderer extends DataRenderer { /** * all rederers for the different kinds of data this combined-renderer can draw */ - protected List mRenderers; + protected List mRenderers = new ArrayList(5); protected WeakReference mChart; @@ -33,7 +33,6 @@ public CombinedChartRenderer(CombinedChart chart, ChartAnimator animator, ViewPo createRenderers(chart, animator, viewPortHandler); } - protected ArrayList renderersForCreateRenderers = new ArrayList<>(4); /** * Creates the renderers needed for this combined-renderer in the required order. Also takes the DrawOrder into * consideration. @@ -44,7 +43,6 @@ public CombinedChartRenderer(CombinedChart chart, ChartAnimator animator, ViewPo */ protected void createRenderers(CombinedChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { - mRenderers = renderersForCreateRenderers; mRenderers.clear(); DrawOrder[] orders = chart.getDrawOrder(); @@ -104,7 +102,8 @@ public void drawExtras(Canvas c) { renderer.drawExtras(c); } - protected ArrayList highlightsforDrawHighlighted = new ArrayList<>(); + protected List mHighlightBuffer = new ArrayList(); + @Override public void drawHighlighted(Canvas c, Highlight[] indices) { @@ -128,16 +127,14 @@ else if (renderer instanceof BubbleChartRenderer) int dataIndex = data == null ? -1 : ((CombinedData)chart.getData()).getAllData().indexOf(data); - ArrayList dataIndices = highlightsforDrawHighlighted; - dataIndices.clear(); - Highlight h; - for(int i = 0 ; i < dataIndices.size() ; i++){ - h = dataIndices.get(i); + mHighlightBuffer.clear(); + + for (Highlight h : indices) { if (h.getDataIndex() == dataIndex || h.getDataIndex() == -1) - dataIndices.add(h); + mHighlightBuffer.add(h); } - renderer.drawHighlighted(c, dataIndices.toArray(new Highlight[dataIndices.size()])); + renderer.drawHighlighted(c, mHighlightBuffer.toArray(new Highlight[mHighlightBuffer.size()])); } } 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 f6cbccfefd..87f273ff0a 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 @@ -29,26 +29,25 @@ public class LineChartRenderer extends LineRadarRenderer { private class DataSetImageCache { - private Bitmap[] circleBitmaps; private int[] circleColors; - private void ensureCircleCache(int size){ - if(circleBitmaps == null){ + private void ensureCircleCache(int size) { + if (circleBitmaps == null) { circleBitmaps = new Bitmap[size]; - }else if(circleBitmaps.length < size){ + } else if (circleBitmaps.length < size) { Bitmap[] tmp = new Bitmap[size]; - for(int i = 0 ; i < circleBitmaps.length ; i++){ + for (int i = 0; i < circleBitmaps.length; i++) { tmp[i] = circleBitmaps[size]; } circleBitmaps = tmp; } - if(circleColors == null){ + if (circleColors == null) { circleColors = new int[size]; - }else if(circleColors.length < size){ + } else if (circleColors.length < size) { int[] tmp = new int[size]; - for(int i = 0 ; i < circleColors.length ; i++){ + for (int i = 0; i < circleColors.length; i++) { tmp[i] = circleColors[size]; } circleColors = tmp; @@ -124,7 +123,7 @@ public void drawData(Canvas c) { ILineDataSet set; int setCount = lineData.getDataSets().size(); - for(int i = 0 ; i < setCount ; i++){ + for (int i = 0; i < setCount; i++) { set = lineData.getDataSets().get(i); if (set.isVisible() && set.getEntryCount() > 0) @@ -167,19 +166,19 @@ protected void drawHorizontalBezier(ILineDataSet dataSet) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - XBounds bounds = getXBounds(mChart, dataSet); + mXBounds.set(mChart, dataSet); cubicPath.reset(); - if (bounds.range >= 1) { + if (mXBounds.range >= 1) { - Entry prev = dataSet.getEntryForIndex(bounds.min); + Entry prev = dataSet.getEntryForIndex(mXBounds.min); Entry cur = prev; // let the spline start cubicPath.moveTo(cur.getX(), cur.getY() * phaseY); - for (int j = bounds.min + 1; j <= bounds.range + bounds.min; j++) { + for (int j = mXBounds.min + 1; j <= mXBounds.range + mXBounds.min; j++) { prev = dataSet.getEntryForIndex(j - 1); cur = dataSet.getEntryForIndex(j); @@ -200,7 +199,7 @@ protected void drawHorizontalBezier(ILineDataSet dataSet) { cubicFillPath.reset(); cubicFillPath.addPath(cubicPath); // create a new path, this is bad for performance - drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, bounds); + drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, mXBounds); } mRenderPaint.setColor(dataSet.getColor()); @@ -221,33 +220,33 @@ protected void drawCubicBezier(ILineDataSet dataSet) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - XBounds bounds = getXBounds(mChart, dataSet); + mXBounds.set(mChart, dataSet); float intensity = dataSet.getCubicIntensity(); cubicPath.reset(); - if (bounds.range >= 1) { + if (mXBounds.range >= 1) { float prevDx = 0f; float prevDy = 0f; float curDx = 0f; float curDy = 0f; - Entry prevPrev = dataSet.getEntryForIndex(bounds.min); + Entry prevPrev = dataSet.getEntryForIndex(mXBounds.min); Entry prev = prevPrev; Entry cur = prev; - Entry next = dataSet.getEntryForIndex(bounds.min + 1); + Entry next = dataSet.getEntryForIndex(mXBounds.min + 1); // let the spline start cubicPath.moveTo(cur.getX(), cur.getY() * phaseY); - for (int j = bounds.min + 1; j <= bounds.range + bounds.min; j++) { + 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 = bounds.max > j + 1 ? dataSet.getEntryForIndex(j + 1) : cur; + next = mXBounds.max > j + 1 ? dataSet.getEntryForIndex(j + 1) : cur; prevDx = (cur.getX() - prevPrev.getX()) * intensity; prevDy = (cur.getY() - prevPrev.getY()) * intensity; @@ -266,7 +265,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, bounds); + drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, mXBounds); } mRenderPaint.setColor(dataSet.getColor()); @@ -302,7 +301,6 @@ protected void drawCubicFill(Canvas c, ILineDataSet dataSet, Path spline, Transf } private float[] mLineBuffer = new float[4]; - private XBounds xBoundsBuffer; /** * Draws a normal line. @@ -332,12 +330,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { canvas = c; } - if(xBoundsBuffer == null){ - xBoundsBuffer = getXBounds(mChart, dataSet); - }else{ - xBoundsBuffer.set(mChart, dataSet); - } - final XBounds bounds = xBoundsBuffer; + mXBounds.set(mChart, dataSet); // more than 1 color if (dataSet.getColors().size() > 1) { @@ -345,7 +338,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { if (mLineBuffer.length <= pointsPerEntryPair * 2) mLineBuffer = new float[pointsPerEntryPair * 4]; - for (int j = bounds.min; j <= bounds.range + bounds.min; j++) { + for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) { Entry e = dataSet.getEntryForIndex(j); if (e == null) continue; @@ -353,7 +346,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { mLineBuffer[0] = e.getX(); mLineBuffer[1] = e.getY() * phaseY; - if (j < bounds.max) { + if (j < mXBounds.max) { e = dataSet.getEntryForIndex(j + 1); @@ -403,12 +396,12 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { Entry e1, e2; - e1 = dataSet.getEntryForIndex(bounds.min); + e1 = dataSet.getEntryForIndex(mXBounds.min); if (e1 != null) { int j = 0; - for (int x = bounds.min; x <= bounds.range + bounds.min; x++) { + for (int x = mXBounds.min; x <= mXBounds.range + mXBounds.min; x++) { e1 = dataSet.getEntryForIndex(x == 0 ? 0 : (x - 1)); e2 = dataSet.getEntryForIndex(x); @@ -432,7 +425,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { if (j > 0) { trans.pointValuesToPixel(mLineBuffer); - final int size = Math.max((bounds.range + 1) * pointsPerEntryPair, pointsPerEntryPair) * 2; + final int size = Math.max((mXBounds.range + 1) * pointsPerEntryPair, pointsPerEntryPair) * 2; mRenderPaint.setColor(dataSet.getColor()); @@ -445,7 +438,7 @@ protected void drawLinear(Canvas c, ILineDataSet dataSet) { // if drawing filled is enabled if (dataSet.isDrawFilledEnabled() && entryCount > 0) { - drawLinearFill(c, dataSet, trans, bounds); + drawLinearFill(c, dataSet, trans, mXBounds); } } @@ -472,16 +465,15 @@ protected void drawLinearFill(Canvas c, ILineDataSet dataSet, Transformer trans, int iterations = 0; // Doing this iteratively in order to avoid OutOfMemory errors that can happen on large bounds sets. - do{ + do { currentStartIndex = startingIndex + (iterations * indexInterval); currentEndIndex = currentStartIndex + indexInterval; currentEndIndex = currentEndIndex > endingIndex ? endingIndex : currentEndIndex; - if(currentStartIndex <= currentEndIndex) { + if (currentStartIndex <= currentEndIndex) { generateFilledPath(dataSet, currentStartIndex, currentEndIndex, filled); - trans.pathValueToPixel(filled); final Drawable drawable = dataSet.getFillDrawable(); @@ -496,18 +488,17 @@ protected void drawLinearFill(Canvas c, ILineDataSet dataSet, Transformer trans, iterations++; - }while(currentStartIndex <= currentEndIndex); + } while (currentStartIndex <= currentEndIndex); } /** * Generates a path that is used for filled drawing. * - * @param dataSet The dataset from which to read the entries. + * @param dataSet The dataset from which to read the entries. * @param startIndex The index from which to start reading the dataset - * @param endIndex The index from which to stop reading the dataset + * @param endIndex The index from which to stop reading the dataset * @param outputPath The path object that will be assigned the chart data. - * * @return */ private void generateFilledPath(final ILineDataSet dataSet, final int startIndex, final int endIndex, final Path outputPath) { @@ -527,7 +518,7 @@ private void generateFilledPath(final ILineDataSet dataSet, final int startIndex // create a new path Entry currentEntry = null; Entry previousEntry = null; - for (int x = startIndex + 1 ; x <= endIndex ; x++) { + for (int x = startIndex + 1; x <= endIndex; x++) { currentEntry = dataSet.getEntryForIndex(x); @@ -541,7 +532,7 @@ private void generateFilledPath(final ILineDataSet dataSet, final int startIndex } // close up - if(currentEntry != null) { + if (currentEntry != null) { filled.lineTo(currentEntry.getX(), fillMin); } @@ -574,10 +565,10 @@ public void drawValues(Canvas c) { if (!dataSet.isDrawCirclesEnabled()) valOffset = valOffset / 2; - XBounds bounds = getXBounds(mChart, dataSet); + mXBounds.set(mChart, dataSet); float[] positions = trans.generateTransformedValuesLine(dataSet, mAnimator.getPhaseX(), mAnimator - .getPhaseY(), bounds.min, bounds.max); + .getPhaseY(), mXBounds.min, mXBounds.max); for (int j = 0; j < positions.length; j += 2) { @@ -590,7 +581,7 @@ public void drawValues(Canvas c) { if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) continue; - Entry entry = dataSet.getEntryForIndex(j / 2 + bounds.min); + Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, y - valOffset, dataSet.getValueTextColor(j / 2)); @@ -607,28 +598,27 @@ public void drawExtras(Canvas c) { private Path mCirclePathBuffer = new Path(); private float[] mCirclesBuffer = new float[2]; private HashMap mImageCaches = new HashMap<>(); - private XBounds mXBoundsBuffer; + protected void drawCircles(Canvas c) { mRenderPaint.setStyle(Paint.Style.FILL); float phaseY = mAnimator.getPhaseY(); - float[] circlesBuffer = mCirclesBuffer; - circlesBuffer[0] = 0; - circlesBuffer[1] = 0; + mCirclesBuffer[0] = 0; + mCirclesBuffer[1] = 0; List dataSets = mChart.getLineData().getDataSets(); - final int dataSetCount = dataSets.size(); - for (int i = 0; i < dataSetCount ; i++) { + + for (int i = 0; i < dataSets.size(); i++) { ILineDataSet dataSet = dataSets.get(i); DataSetImageCache imageCache; - if(mImageCaches.containsKey(dataSet)){ + if (mImageCaches.containsKey(dataSet)) { imageCache = mImageCaches.get(dataSet); - }else{ + } else { imageCache = new DataSetImageCache(); mImageCaches.put(dataSet, imageCache); } @@ -642,13 +632,7 @@ protected void drawCircles(Canvas c) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - if(mXBoundsBuffer == null) { - mXBoundsBuffer = getXBounds(mChart, dataSet); - }else{ - mXBoundsBuffer.set(mChart,dataSet); - } - - XBounds bounds = mXBoundsBuffer; + mXBounds.set(mChart, dataSet); float circleRadius = dataSet.getCircleRadius(); float circleHoleRadius = dataSet.getCircleHoleRadius(); @@ -658,24 +642,26 @@ protected void drawCircles(Canvas c) { boolean drawTransparentCircleHole = drawCircleHole && dataSet.getCircleHoleColor() == ColorTemplate.COLOR_NONE; - int boundsRangeCount = bounds.range + bounds.min; - for (int j = bounds.min; j <= boundsRangeCount; j++) { + int boundsRangeCount = mXBounds.range + mXBounds.min; + + for (int j = mXBounds.min; j <= boundsRangeCount; j++) { + Entry e = dataSet.getEntryForIndex(j); if (e == null) break; - circlesBuffer[0] = e.getX(); - circlesBuffer[1] = e.getY() * phaseY; + mCirclesBuffer[0] = e.getX(); + mCirclesBuffer[1] = e.getY() * phaseY; - trans.pointValuesToPixel(circlesBuffer); + trans.pointValuesToPixel(mCirclesBuffer); - if (!mViewPortHandler.isInBoundsRight(circlesBuffer[0])) + if (!mViewPortHandler.isInBoundsRight(mCirclesBuffer[0])) break; // make sure the circles don't do shitty things outside // bounds - if (!mViewPortHandler.isInBoundsLeft(circlesBuffer[0]) || - !mViewPortHandler.isInBoundsY(circlesBuffer[1])) + if (!mViewPortHandler.isInBoundsLeft(mCirclesBuffer[0]) || + !mViewPortHandler.isInBoundsY(mCirclesBuffer[1])) continue; final int circleColor = dataSet.getCircleColor(j); @@ -683,28 +669,27 @@ protected void drawCircles(Canvas c) { Bitmap circleBitmap = null; - final int dataSetColorCount = imageCache.circleColors.length; int colorIndex; - for(colorIndex = 0 ; colorIndex < dataSetColorCount ; colorIndex++) { + + for (colorIndex = 0; colorIndex < imageCache.circleColors.length; colorIndex++) { int tempColor = imageCache.circleColors[colorIndex]; Bitmap tempBitmap = imageCache.circleBitmaps[colorIndex]; - if(tempColor == circleColor) { + if (tempColor == circleColor) { circleBitmap = tempBitmap; break; - }else if(tempBitmap == null){ + } else if (tempBitmap == null) { break; } } - - if(circleBitmap == null){ + if (circleBitmap == null) { Bitmap.Config conf = Bitmap.Config.ARGB_8888; - circleBitmap = Bitmap.createBitmap((int)(circleRadius * 2.1), (int)(circleRadius * 2.1), conf); + circleBitmap = Bitmap.createBitmap((int) (circleRadius * 2.1), (int) (circleRadius * 2.1), conf); Canvas canvas = new Canvas(circleBitmap); imageCache.circleBitmaps[colorIndex] = circleBitmap; imageCache.circleColors[colorIndex] = circleColor; - if(drawTransparentCircleHole){ + if (drawTransparentCircleHole) { // Begin path for circle with hole mCirclePathBuffer.reset(); @@ -723,7 +708,7 @@ protected void drawCircles(Canvas c) { // Fill in-between canvas.drawPath(mCirclePathBuffer, mRenderPaint); - }else{ + } else { canvas.drawCircle( circleRadius, @@ -741,10 +726,8 @@ protected void drawCircles(Canvas c) { } } - if(circleBitmap != null){ - - c.drawBitmap(circleBitmap, circlesBuffer[0] - circleRadius, circlesBuffer[1] - circleRadius, mRenderPaint); - + if (circleBitmap != null) { + c.drawBitmap(circleBitmap, mCirclesBuffer[0] - circleRadius, mCirclesBuffer[1] - circleRadius, mRenderPaint); } } } 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 026f975b57..9439cbfd6f 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 @@ -100,11 +100,11 @@ public void drawValues(Canvas c) { // apply the text-styling defined by the DataSet applyValueTextStyle(dataSet); - XBounds bounds = getXBounds(mChart, dataSet); + mXBounds.set(mChart, dataSet); float[] positions = mChart.getTransformer(dataSet.getAxisDependency()) .generateTransformedValuesScatter(dataSet, - mAnimator.getPhaseX(), mAnimator.getPhaseY(), bounds.min, bounds.max); + mAnimator.getPhaseX(), mAnimator.getPhaseY(), mXBounds.min, mXBounds.max); float shapeSize = Utils.convertDpToPixel(dataSet.getScatterShapeSize()); @@ -118,10 +118,10 @@ public void drawValues(Canvas c) { || !mViewPortHandler.isInBoundsY(positions[j + 1]))) continue; - Entry entry = dataSet.getEntryForIndex(j / 2 + bounds.min); + 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 + bounds.min)); + positions[j + 1] - shapeSize, dataSet.getValueTextColor(j / 2 + mXBounds.min)); } } } 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 8a73bced5c..d112cf92de 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 @@ -170,7 +170,6 @@ public void renderAxisLine(Canvas c) { } } - protected float[] mDrawLabelsBuffer = new float[2]; /** * draws the x-labels on the specified y-position * @@ -181,12 +180,9 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); - if(mDrawLabelsBuffer.length < mAxis.mEntryCount * 2){ - mDrawLabelsBuffer = new float[mXAxis.mEntryCount * 2]; - } - float[] positions = mDrawLabelsBuffer; + float[] positions = new float[mXAxis.mEntryCount * 2]; - for (int i = 0; i < positions.length && i < mXAxis.mEntries.length / 2 && i < mXAxis.mCenteredEntries.length / 2 ; i += 2) { + for (int i = 0; i < positions.length; i += 2) { // only fill x values if (centeringEnabled) { @@ -194,13 +190,11 @@ protected void drawLabels(Canvas c, float pos, MPPointF anchor) { } else { positions[i] = mXAxis.mEntries[i / 2]; } - // init to 0 - positions[i+1] = 0; } mTrans.pointValuesToPixel(positions); - for (int i = 0; i < positions.length && i < mXAxis.mEntries.length / 2 && i < mXAxis.mCenteredEntries.length / 2 ; i += 2) { + for (int i = 0; i < positions.length; i += 2) { float x = positions[i]; 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 e050af3c13..68b280df2d 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 @@ -129,17 +129,13 @@ public void renderAxisLabels(Canvas c) { MPPointF.recycleInstance(pointF); } - protected float[] mDrawLabelsBuffer = new float[2]; @Override protected void drawLabels(Canvas c, float pos, MPPointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); - if(mDrawLabelsBuffer.length != mAxis.mEntryCount * 2){ - mDrawLabelsBuffer = new float[mXAxis.mEntryCount * 2]; - } - float[] positions = mDrawLabelsBuffer; + float[] positions = new float[mXAxis.mEntryCount * 2]; for (int i = 0; i < positions.length; i += 2) { From 6d21817c8aed43dae6deb1bf7bb835518ecbe2f6 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 2 Jul 2016 12:36:16 +0200 Subject: [PATCH 296/606] Rename PointD to MPPointD --- .../charting/charts/BarLineChartBase.java | 34 +++++++++---------- .../charting/highlight/BarHighlighter.java | 10 +++--- .../charting/highlight/ChartHighlighter.java | 14 ++++---- .../highlight/HorizontalBarHighlighter.java | 8 ++--- .../charting/renderer/AxisRenderer.java | 10 +++--- .../renderer/CandleStickChartRenderer.java | 4 +-- .../charting/renderer/LineChartRenderer.java | 4 +-- .../renderer/ScatterChartRenderer.java | 4 +-- .../charting/renderer/XAxisRenderer.java | 10 +++--- .../XAxisRendererHorizontalBarChart.java | 10 +++--- .../charting/renderer/YAxisRenderer.java | 4 +-- .../YAxisRendererHorizontalBarChart.java | 12 +++---- .../utils/{PointD.java => MPPointD.java} | 20 +++++------ .../mikephil/charting/utils/MPPointF.java | 1 + .../mikephil/charting/utils/Transformer.java | 16 ++++----- 15 files changed, 81 insertions(+), 80 deletions(-) rename MPChartLib/src/main/java/com/github/mikephil/charting/utils/{PointD.java => MPPointD.java} (55%) 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 33de770eb3..a794c6a5ef 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 @@ -31,8 +31,8 @@ import com.github.mikephil.charting.listener.OnDrawListener; import com.github.mikephil.charting.renderer.XAxisRenderer; import com.github.mikephil.charting.renderer.YAxisRenderer; +import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.MPPointF; -import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; @@ -683,14 +683,14 @@ public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, floa if (android.os.Build.VERSION.SDK_INT >= 11) { - PointD 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); - PointD.recycleInstance(origin); + MPPointD.recycleInstance(origin); } else { Log.e(LOG_TAG, "Unable to execute zoomAndCenterAnimated(...) on API level < 11"); @@ -848,7 +848,7 @@ public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, if (android.os.Build.VERSION.SDK_INT >= 11) { - PointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); + MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); @@ -857,7 +857,7 @@ public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, addViewportJob(job); - PointD.recycleInstance(bounds); + MPPointD.recycleInstance(bounds); } else { Log.e(LOG_TAG, "Unable to execute moveViewToAnimated(...) on API level < 11"); } @@ -915,7 +915,7 @@ public void centerViewToAnimated(float xValue, float yValue, AxisDependency axis if (android.os.Build.VERSION.SDK_INT >= 11) { - PointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); + MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); @@ -926,7 +926,7 @@ public void centerViewToAnimated(float xValue, float yValue, AxisDependency axis addViewportJob(job); - PointD.recycleInstance(bounds); + MPPointD.recycleInstance(bounds); } else { Log.e(LOG_TAG, "Unable to execute centerViewToAnimated(...) on API level < 11"); } @@ -1202,9 +1202,9 @@ public void setKeepPositionOnRotation(boolean keepPositionOnRotation) { } /** - * Returns a recyclable PointD instance + * Returns a recyclable MPPointD instance * Returns the x and y values in the chart at the given touch point - * (encapsulated in a PointD). This method transforms pixel coordinates to + * (encapsulated in a MPPointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to * getPixelsForValues(...). * @@ -1212,18 +1212,18 @@ public void setKeepPositionOnRotation(boolean keepPositionOnRotation) { * @param y * @return */ - public PointD getValuesByTouchPoint(float x, float y, AxisDependency axis) { - PointD result = PointD.getInstance(0,0); + public MPPointD getValuesByTouchPoint(float x, float y, AxisDependency axis) { + MPPointD result = MPPointD.getInstance(0,0); getValuesByTouchPoint(x,y,axis,result); return result; } - public void getValuesByTouchPoint(float x, float y, AxisDependency axis, PointD outputPoint){ + public void getValuesByTouchPoint(float x, float y, AxisDependency axis, MPPointD outputPoint){ getTransformer(axis).getValuesByTouchPoint(x, y, outputPoint); } /** - * Returns a recyclable PointD instance + * Returns a recyclable MPPointD instance * Transforms the given chart values into pixels. This is the opposite * method to getValuesByTouchPoint(...). * @@ -1231,11 +1231,11 @@ public void getValuesByTouchPoint(float x, float y, AxisDependency axis, PointD * @param y * @return */ - public PointD getPixelsForValues(float x, float y, AxisDependency axis) { + public MPPointD getPixelsForValues(float x, float y, AxisDependency axis) { return getTransformer(axis).getPixelsForValues(x, y); } - PointD pointForGetYValueByTouchPoint = PointD.getInstance(0,0); + MPPointD pointForGetYValueByTouchPoint = MPPointD.getInstance(0,0); /** * Returns y value at the given touch position (must not necessarily be * a value contained in one of the datasets) @@ -1281,7 +1281,7 @@ public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float } /** buffer for storing lowest visible x point */ - protected PointD posForGetLowestVisibleX = PointD.getInstance(0,0); + protected MPPointD posForGetLowestVisibleX = MPPointD.getInstance(0,0); /** * Returns the lowest x-index (value on the x-axis) that is still visible on @@ -1298,7 +1298,7 @@ public float getLowestVisibleX() { } /** buffer for storing highest visible x point */ - protected PointD posForGetHighestVisibleX = PointD.getInstance(0,0); + protected MPPointD posForGetHighestVisibleX = MPPointD.getInstance(0,0); /** * Returns the highest x-index (value on the x-axis) that is still visible 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 c0b58947e1..1adad89de1 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 @@ -5,7 +5,7 @@ import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.utils.PointD; +import com.github.mikephil.charting.utils.MPPointD; /** * Created by Philipp Jahoda on 22/07/15. @@ -24,7 +24,7 @@ public Highlight getHighlight(float x, float y) { return null; } - PointD pos = getValsForTouch(x, y); + MPPointD pos = getValsForTouch(x, y); BarData barData = mChart.getBarData(); @@ -37,7 +37,7 @@ public Highlight getHighlight(float x, float y) { (float) pos.y); } - PointD.recycleInstance(pos); + MPPointD.recycleInstance(pos); return high; } @@ -68,7 +68,7 @@ public Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal if (ranges.length > 0) { int stackIndex = getClosestStackIndex(ranges, yVal); - PointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(high.getX(), ranges[stackIndex].to); + MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(high.getX(), ranges[stackIndex].to); Highlight stackedHigh = new Highlight( entry.getX(), @@ -80,7 +80,7 @@ public Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal high.getAxis() ); - PointD.recycleInstance(pixels); + MPPointD.recycleInstance(pixels); return stackedHigh; } 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 529ba8894a..c3aa4560d8 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 @@ -6,7 +6,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.utils.PointD; +import com.github.mikephil.charting.utils.MPPointD; import java.util.ArrayList; import java.util.List; @@ -33,26 +33,26 @@ public ChartHighlighter(T chart) { @Override public Highlight getHighlight(float x, float y) { - PointD pos = getValsForTouch(x, y); + MPPointD pos = getValsForTouch(x, y); float xVal = (float) pos.x; - PointD.recycleInstance(pos); + MPPointD.recycleInstance(pos); Highlight high = getHighlightForX(xVal, x, y); return high; } /** - * Returns a recyclable PointD instance. + * Returns a recyclable MPPointD instance. * Returns the corresponding xPos for a given touch-position in pixels. * * @param x * @param y * @return */ - protected PointD getValsForTouch(float x, float y) { + protected MPPointD getValsForTouch(float x, float y) { // take any transformer to determine the x-axis value - PointD pos = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(x, y); + MPPointD pos = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(x, y); return pos; } @@ -167,7 +167,7 @@ protected Highlight buildHighlight(IDataSet set, int dataSetIndex, float xVal, D if (e == null) return null; - PointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), e.getY()); + MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(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 ce1220027e..626868c3de 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 @@ -6,7 +6,7 @@ import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.utils.PointD; +import com.github.mikephil.charting.utils.MPPointD; /** * Created by Philipp Jahoda on 22/07/15. @@ -22,7 +22,7 @@ public Highlight getHighlight(float x, float y) { BarData barData = mChart.getBarData(); - PointD pos = getValsForTouch(y, x); + MPPointD pos = getValsForTouch(y, x); Highlight high = getHighlightForX((float) pos.y, y, x); if (high == null) @@ -37,7 +37,7 @@ public Highlight getHighlight(float x, float y) { (float) pos.x); } - PointD.recycleInstance(pos); + MPPointD.recycleInstance(pos); return high; } @@ -47,7 +47,7 @@ protected Highlight buildHighlight(IDataSet set, int dataSetIndex, float xVal, D final Entry e = set.getEntryForXPos(xVal, rounding); - PointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getY(), e.getX()); + MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(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/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java index f1d4ff0981..1b9c82364a 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 @@ -7,7 +7,7 @@ import android.graphics.Paint.Style; import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.utils.PointD; +import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -121,8 +121,8 @@ public void computeAxis(float min, float max, boolean inverted) { // zoom / contentrect bounds) if (mViewPortHandler != null && mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { - PointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); - PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); + MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); + MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); if (!inverted) { @@ -134,8 +134,8 @@ public void computeAxis(float min, float max, boolean inverted) { max = (float) p2.y; } - PointD.recycleInstance(p1); - PointD.recycleInstance(p2); + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); } computeAxisValues(min, max); 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 3b99a074f5..2aa11482f0 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 @@ -11,7 +11,7 @@ import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.PointD; +import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -326,7 +326,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { float highValue = e.getHigh() * mAnimator.getPhaseY(); float y = (lowValue + highValue) / 2f; - PointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), y); + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(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 87f273ff0a..d478024808 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 @@ -17,7 +17,7 @@ import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.PointD; +import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -750,7 +750,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (!isInBoundsX(e, set)) continue; - PointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), e.getY() * mAnimator + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(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 9439cbfd6f..1d45c4974d 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 @@ -11,7 +11,7 @@ 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.utils.PointD; +import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -148,7 +148,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (!isInBoundsX(e, set)) continue; - PointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(e.getX(), e.getY() * mAnimator + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelsForValues(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/XAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java index d112cf92de..1973aa3a47 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 @@ -11,8 +11,8 @@ import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.MPPointF; -import com.github.mikephil.charting.utils.PointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -46,8 +46,8 @@ public void computeAxis(float min, float max, boolean inverted) { // zoom / contentrect bounds) if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { - PointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); - PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), mViewPortHandler.contentTop()); + MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); + MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), mViewPortHandler.contentTop()); if (inverted) { @@ -59,8 +59,8 @@ public void computeAxis(float min, float max, boolean inverted) { max = (float) p2.x; } - PointD.recycleInstance(p1); - PointD.recycleInstance(p2); + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); } computeAxisValues(min, max); 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 68b280df2d..02192f5a47 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 @@ -12,7 +12,7 @@ import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.utils.FSize; import com.github.mikephil.charting.utils.MPPointF; -import com.github.mikephil.charting.utils.PointD; +import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -37,8 +37,8 @@ public void computeAxis(float min, float max, boolean inverted) { // zoom / contentrect bounds) if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { - PointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); - PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); + MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); + MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); if (inverted) { @@ -50,8 +50,8 @@ public void computeAxis(float min, float max, boolean inverted) { max = (float) p2.y; } - PointD.recycleInstance(p1); - PointD.recycleInstance(p2); + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); } computeAxisValues(min, max); 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 b550579d29..71cfbf324e 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 @@ -10,7 +10,7 @@ import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; -import com.github.mikephil.charting.utils.PointD; +import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -203,7 +203,7 @@ protected float[] getTransformedPositions() { protected void drawZeroLine(Canvas c) { // draw zero line - PointD pos = mTrans.getPixelsForValues(0f, 0f); + MPPointD pos = mTrans.getPixelsForValues(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 21e16ba325..3f9f36a965 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 @@ -10,7 +10,7 @@ import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; -import com.github.mikephil.charting.utils.PointD; +import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -39,9 +39,9 @@ public void computeAxis(float yMin, float yMax, boolean inverted) { // zoom / contentrect bounds) if (mViewPortHandler.contentHeight() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { - PointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), + MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); - PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), + MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), mViewPortHandler.contentTop()); if (!inverted) { @@ -52,8 +52,8 @@ public void computeAxis(float yMin, float yMax, boolean inverted) { yMax = (float) p1.x; } - PointD.recycleInstance(p1); - PointD.recycleInstance(p2); + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); } computeAxisValues(yMin, yMax); @@ -179,7 +179,7 @@ protected Path linePath(Path p, int i, float[] positions) { protected void drawZeroLine(Canvas c) { // draw zero line - PointD pos = mTrans.getPixelsForValues(0f, 0f); + MPPointD pos = mTrans.getPixelsForValues(0f, 0f); mZeroLinePaint.setColor(mYAxis.getZeroLineColor()); mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth()); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointD.java similarity index 55% rename from MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointD.java index c69c4da7d3..f6220a72e9 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/PointD.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointD.java @@ -8,27 +8,27 @@ * * @author Philipp Jahoda */ -public class PointD extends ObjectPool.Poolable { +public class MPPointD extends ObjectPool.Poolable { - private static ObjectPool pool; + private static ObjectPool pool; static { - pool = ObjectPool.create(64, new PointD(0,0)); + pool = ObjectPool.create(64, new MPPointD(0,0)); pool.setReplenishPercentage(0.5f); } - public static PointD getInstance(double x, double y){ - PointD result = pool.get(); + public static MPPointD getInstance(double x, double y){ + MPPointD result = pool.get(); result.x = x; result.y = y; return result; } - public static void recycleInstance(PointD instance){ + public static void recycleInstance(MPPointD instance){ pool.recycle(instance); } - public static void recycleInstances(List instances){ + public static void recycleInstances(List instances){ pool.recycle(instances); } @@ -36,10 +36,10 @@ public static void recycleInstances(List instances){ public double y; protected ObjectPool.Poolable instantiate(){ - return new PointD(0,0); + return new MPPointD(0,0); } - private PointD(double x, double y) { + private MPPointD(double x, double y) { this.x = x; this.y = y; } @@ -48,6 +48,6 @@ private PointD(double x, double y) { * returns a string representation of the object */ public String toString() { - return "PointD, x: " + x + ", y: " + y; + return "MPPointD, x: " + x + ", y: " + y; } } \ No newline at end of file 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 f2e6c71fea..2e3fc58934 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 @@ -9,6 +9,7 @@ * Created by Tony Patino on 6/24/16. */ public class MPPointF extends ObjectPool.Poolable { + private static ObjectPool pool; public float x; 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 e842084936..be6d4cefe8 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 @@ -385,9 +385,9 @@ public void pixelsToValue(float[] pixels) { float[] ptsBuffer = new float[2]; /** - * Returns a recyclable PointD instance. + * Returns a recyclable MPPointD instance. * returns the x and y values in the chart at the given touch point - * (encapsulated in a PointD). This method transforms pixel coordinates to + * (encapsulated in a MPPointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to * getPixelsForValues(...). * @@ -395,14 +395,14 @@ public void pixelsToValue(float[] pixels) { * @param y * @return */ - public PointD getValuesByTouchPoint(float x, float y) { + public MPPointD getValuesByTouchPoint(float x, float y) { - PointD result = PointD.getInstance(0,0); + MPPointD result = MPPointD.getInstance(0,0); getValuesByTouchPoint(x,y,result); return result; } - public void getValuesByTouchPoint(float x, float y, PointD outputPoint){ + public void getValuesByTouchPoint(float x, float y, MPPointD outputPoint){ ptsBuffer[0] = x; ptsBuffer[1] = y; @@ -414,14 +414,14 @@ public void getValuesByTouchPoint(float x, float y, PointD outputPoint){ } /** - * Returns a recyclable PointD instance. + * Returns a recyclable MPPointD instance. * Returns the x and y coordinates (pixels) for a given x and y value in the chart. * * @param x * @param y * @return */ - public PointD getPixelsForValues(float x, float y) { + public MPPointD getPixelsForValues(float x, float y) { ptsBuffer[0] = x; ptsBuffer[1] = y; @@ -431,7 +431,7 @@ public PointD getPixelsForValues(float x, float y) { double xPx = ptsBuffer[0]; double yPx = ptsBuffer[1]; - return PointD.getInstance(xPx, yPx); + return MPPointD.getInstance(xPx, yPx); } public Matrix getValueMatrix() { From 94a67ccea00bebd7e82ad73e2e52aa68a12c999e Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 2 Jul 2016 21:05:00 +0200 Subject: [PATCH 297/606] Fixes & improvements related to circle cache --- MPChartExample/build.gradle | 2 +- .../mpchartexample/LineChartActivity2.java | 19 +- .../mikephil/charting/data/LineDataSet.java | 82 +++--- .../charting/renderer/LineChartRenderer.java | 233 ++++++++++-------- 4 files changed, 186 insertions(+), 150 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index b9dff17945..f0ed2a5a33 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.0.0@aar' + compile 'com.github.PhilJay:MPAndroidChart-Realm:v1.0.2@aar' compile project(':MPChartLib') compile 'com.android.support:appcompat-v7:23.1.1' diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 1508aea66b..738e08814a 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -193,7 +193,7 @@ public boolean onOptionsItemSelected(MenuItem item) { LineDataSet set = (LineDataSet) iSet; set.setMode(set.getMode() == LineDataSet.Mode.CUBIC_BEZIER ? LineDataSet.Mode.LINEAR - : LineDataSet.Mode.CUBIC_BEZIER); + : LineDataSet.Mode.CUBIC_BEZIER); } mChart.invalidate(); break; @@ -207,7 +207,7 @@ public boolean onOptionsItemSelected(MenuItem item) { LineDataSet set = (LineDataSet) iSet; set.setMode(set.getMode() == LineDataSet.Mode.STEPPED ? LineDataSet.Mode.LINEAR - : LineDataSet.Mode.STEPPED); + : LineDataSet.Mode.STEPPED); } mChart.invalidate(); break; @@ -221,7 +221,7 @@ public boolean onOptionsItemSelected(MenuItem item) { LineDataSet set = (LineDataSet) iSet; set.setMode(set.getMode() == LineDataSet.Mode.HORIZONTAL_BEZIER ? LineDataSet.Mode.LINEAR - : LineDataSet.Mode.HORIZONTAL_BEZIER); + : LineDataSet.Mode.HORIZONTAL_BEZIER); } mChart.invalidate(); break; @@ -306,8 +306,8 @@ private void setData(int count, float range) { if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { - set1 = (LineDataSet)mChart.getData().getDataSetByIndex(0); - set2 = (LineDataSet)mChart.getData().getDataSetByIndex(1); + set1 = (LineDataSet) mChart.getData().getDataSetByIndex(0); + set2 = (LineDataSet) mChart.getData().getDataSetByIndex(1); set1.setValues(yVals1); set2.setValues(yVals2); mChart.getData().notifyDataChanged(); @@ -361,9 +361,12 @@ private void setData(int count, float range) { public void onValueSelected(Entry e, Highlight h) { Log.i("Entry selected", e.toString()); - mChart.centerViewToAnimated(e.getX(), e.getY(), mChart.getData().getDataSetByIndex(h.getDataSetIndex()).getAxisDependency(), 500); - //mChart.zoomAndCenterAnimated(2.5f, 2.5f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex).getAxisDependency(), 1000); - //mChart.zoomAndCenterAnimated(1.8f, 1.8f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex).getAxisDependency(), 1000); + mChart.centerViewToAnimated(e.getX(), e.getY(), mChart.getData().getDataSetByIndex(h.getDataSetIndex()) + .getAxisDependency(), 500); + //mChart.zoomAndCenterAnimated(2.5f, 2.5f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex) + // .getAxisDependency(), 1000); + //mChart.zoomAndCenterAnimated(1.8f, 1.8f, e.getX(), e.getY(), mChart.getData().getDataSetByIndex(dataSetIndex) + // .getAxisDependency(), 1000); } @Override 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 c568ab0671..0d7147e5dd 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 @@ -5,10 +5,10 @@ import android.graphics.Color; import android.graphics.DashPathEffect; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.formatter.DefaultFillFormatter; import com.github.mikephil.charting.formatter.FillFormatter; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; @@ -16,31 +16,49 @@ public class LineDataSet extends LineRadarDataSet implements ILineDataSet { - /** Drawing mode for this line dataset **/ + /** + * Drawing mode for this line dataset + **/ private LineDataSet.Mode mMode = Mode.LINEAR; - /** List representing all colors that are used for the circles */ + /** + * List representing all colors that are used for the circles + */ private List mCircleColors = null; - /** the color of the inner circles */ + /** + * the color of the inner circles + */ private int mCircleColorHole = Color.WHITE; - /** the radius of the circle-shaped value indicators */ + /** + * the radius of the circle-shaped value indicators + */ private float mCircleRadius = 8f; - /** the hole radius of the circle-shaped value indicators */ + /** + * the hole radius of the circle-shaped value indicators + */ private float mCircleHoleRadius = 4f; - /** sets the intensity of the cubic lines */ + /** + * sets the intensity of the cubic lines + */ private float mCubicIntensity = 0.2f; - /** the path effect of this DataSet that makes dashed lines possible */ + /** + * the path effect of this DataSet that makes dashed lines possible + */ private DashPathEffect mDashPathEffect = null; - /** formatter for customizing the position of the fill-line */ + /** + * formatter for customizing the position of the fill-line + */ private FillFormatter mFillFormatter = new DefaultFillFormatter(); - /** if true, drawing circles is enabled */ + /** + * if true, drawing circles is enabled + */ private boolean mDrawCircles = true; private boolean mDrawCircleHole = true; @@ -52,11 +70,11 @@ public LineDataSet(List yVals, String label) { // mCircleRadius = Utils.convertDpToPixel(4f); // mLineWidth = Utils.convertDpToPixel(1f); - if(mCircleColors == null) { + if (mCircleColors == null) { mCircleColors = new ArrayList(); } mCircleColors.clear(); - + // default colors // mColors.add(Color.rgb(192, 255, 140)); // mColors.add(Color.rgb(255, 247, 140)); @@ -108,7 +126,7 @@ public void setMode(LineDataSet.Mode mode) { /** * Sets the intensity for cubic lines (if enabled). Max = 1f = very cubic, * Min = 0.05f = low cubic effect, Default: 0.2f - * + * * @param intensity */ public void setCubicIntensity(float intensity) { @@ -160,7 +178,7 @@ public float getCircleHoleRadius() { /** * sets the size (radius) of the circle shpaed value indicators, * default size = 4f - * + *

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 324/606] 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 325/606] 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 326/606] 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 327/606] 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 328/606] 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 329/606] 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 330/606] `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 331/606] 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 332/606] 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 333/606] 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 334/606] 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 335/606] 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 336/606] 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 337/606] 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 338/606] 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 339/606] 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 340/606] 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 341/606] 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 342/606] 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 343/606] 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 344/606] 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 345/606] 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 346/606] 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 347/606] 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 348/606] 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 349/606] 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 350/606] 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 351/606] 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 352/606] 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 353/606] 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 354/606] 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 355/606] 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 356/606] 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 357/606] 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 358/606] 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 359/606] 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 360/606] 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 361/606] 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 362/606] 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 363/606] 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 364/606] 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 365/606] 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 366/606] 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 367/606] 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 368/606] 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 369/606] 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 370/606] 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 371/606] 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 372/606] 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 373/606] 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 374/606] 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 375/606] 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 376/606] 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 377/606] 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 378/606] 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 379/606] 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 380/606] 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 381/606] 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 382/606] 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 383/606] 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 384/606] 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 385/606] 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 386/606] 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 387/606] 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 388/606] 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 389/606] 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 390/606] 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 391/606] 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 392/606] 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 393/606] 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 394/606] 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 395/606] 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 396/606] 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 397/606] 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 398/606] 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 399/606] 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 400/606] 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 401/606] 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 402/606] 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 403/606] 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 404/606] 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 405/606] 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 406/606] [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 407/606] 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 408/606] 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 409/606] 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 410/606] 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 411/606] 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 412/606] 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 413/606] 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 414/606] 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 415/606] 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 416/606] 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 417/606] 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 418/606] 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 419/606] 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 420/606] 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 421/606] 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 422/606] 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 423/606] 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 424/606] 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 425/606] 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 426/606] 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 427/606] 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 428/606] 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 429/606] 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 430/606] 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 431/606] 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 432/606] 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 433/606] 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 434/606] 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 435/606] 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 436/606] 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 437/606] 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 438/606] 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 439/606] 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 440/606] 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 441/606] 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 442/606] 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 443/606] 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 444/606] 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 445/606] 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 446/606] 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 447/606] 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 448/606] 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 449/606] 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 450/606] 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 451/606] 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 452/606] 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 453/606] 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 454/606] 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 455/606] 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 456/606] 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 457/606] 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 458/606] 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 459/606] 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 460/606] 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 461/606] 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 462/606] 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 463/606] 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 464/606] 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 465/606] 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 466/606] 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 467/606] 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 468/606] 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 469/606] 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 470/606] 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 471/606] 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 472/606] 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 473/606] 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 474/606] 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 475/606] 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 476/606] 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 477/606] 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 478/606] 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 479/606] 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 480/606] 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 481/606] 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 482/606] 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 483/606] 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 484/606] 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 485/606] 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 486/606] 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 487/606] 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 488/606] 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 489/606] 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 490/606] 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 zNtg``nnX+s@Q?$`KjQB5Hy~tq(#Gz zk8jmUJj)}glssm`Pr4fZDV zV&YjP{W(Z1+z4_EfOUwtz+C~0d*BG>q%mHhD6e338NP^7hNtXtQUG8D%8d56wNFKwExc(40!$$?X(zX+M|a&e}f3hF?YCd8^D*ryOY+Q5j9(OSw@oe@S8OD7ab^r z`gNIYxIAZ3c^_8rJkyTHxGIMu!qIvOsMQw>|$$Z5eBw1i^HjP>PjDypg$=+li&TAeyIUW4;W2| zWMFF_9K(Ba3HoVL65m9OPcc=)myUZoHXwuO9s@N74aAHB^Z;@9Yo3CgDu~-*7IDI* zSf2&M6Frv>fw}(V|)F_KFY8*4J@Dzj=!3jbE7?;ib!#f-!h?_VWSu)k^A8Tlv);mtXZ$Kl=-X)s?^bAAk5u|MkDOo6Vz-KTNZ1yHQ^~ zw=lWuk{fS(`#1jjFVt4cG>~ve9M6-(#IxqPw(GR(mG}JOZ`M3{;p~dwnSC7paC4$Gt;Tz?4%Bl!!#oG2-N*|0L|hsL>)uLreskh=W1dHa-Q5 zH^L)+aAq_=T=r5PF$EG6RAUEoddtmnl%2^|**80|9lR$f`(f6WSfOci83z(znw+h0 z!j!|k)51Uxe+h638s(_s$2h@`PAK9}o6OI-um#q4Sl0At#q*y6f01%RtZi^~#WQ`u z!ssz#hWDv##Pt>_dy(@@Mqo{f_AFg!Xpt=|TXRx2sWBulJx@Wk0%=Uo4KkrkSwXqj zCFX<8!M&?JpWH&8q5Ngo{2EV%>4Vzku1Am4=c2UI@*=L!h`w2bOodEGBcI5uL9R{) z+}b0=&|+&7`5dW{ICWxt6pawQai>Je)P5L@7CAYrOlBsuZ7`pLGLc*(B2;)&uqnPi zC@qVwT5M-=J1Mr_H&EcNav%)$}Sa9tw>HQZYUTn^l)0XOH4!tyYzXXuJa3q?Q2xDK9^Q?SD!^Xf%B z4{OkiP?3RJV6tR4Ayy#@W6wuDcm?)Ak;N9*8Q-=; zlEX_2v#n-B5XFn&D*@oymSLHuD2F>vb{Fw>qJ6}u&C0$T-t_u+-EsQJ)8G8!XMgpN z|GZSlug;$u9GjRsaqN-%zVo_UZu^-}{>Ep1^FL|r*4Xs+&HP%auz?}ByMOSSH@x{f zfB)BGlT%mTdiyhTtMl^gs zIgVvG4llsVV@4tof6H5LLr8e!(MJ~+7A_JqN&(HdP@eJpKmL8zrk;-(_}F?`%m`a# zr5WESFc*gz9lfb{wTFJby)l?N-k@XuJ^%3?Z=ZY5<=7vi<_oxs1+E*p^+qP=T1`ylJ!?33WnO-PLq+zsaf>40ICegi+F~v>> z7Cjg@tB@Li)qT|A7~gV$HXK>?WDMfOf0Qx?=(Uy?mVGUmsf+J5sY`wDE=v7|q|>1o z+Yk~q;K`|qIwJ|!nW3vS*aYux%B|1~_BdwiebUC*HA~EGE_4AL0KFjjNJv^PyW-GI z&*4~0<&l3Fl&HFrmX?`GKb5T}+I4S*Y1P89uu^vuZk5q=X%R*?p$q8|#_OGMCXigr zD23%{sdfiss39oqs(XzXA?h zz%9Y3O^i&M0%gScX2{c)?SYf99d{`NIu9n|K@oBxGFMnR4431l;724#*Oq6KWibyA z4X^15V)eQUVQd_hYmn4P194R+N|d@m9Q{&w^%3^)y)qGriJKrwM5fRUk(M*PMCX{C zcWv+E#WNX07kJuYX&aH)qB)h7F+7Y=Yo^A0YM}QX|5ai}#}E9q#SDzsnAc}}2E}l+ zvazD6Eezm@8KH3Kh8u1e8y&q6%)rp5tF1S^??ZzV(~sQwPcugkh2wDu=!+Ftcd##K z+ecxVPSMQ!KJkCaa`;<+{ikb7^RYzg!kLOkMoXz&Pk&ec;LNc@dT=IrQ6iQb7qAiA zvcj?0o~y1M-g&tYNzEU9^3i|!$|wHt&$iZ<3(NDDU41P+@$G;3o15SAt`GdDPk;8e zJ|PIgPyO7-zxS}mXfQXJbs;B`t7cLhGXw_+hw^8m`c+}VLN7;SyH{IV{qX}MC&>W<89zf z;G@B8SCbs2*aw9{W;L5AhuyZVbGjwzp0EnNI><;tILeCnta%hhR&hRZPAnad;HVnjVD zT!)wjVVc+?5f>@Ugco39X#4X1H$D{=TFr-|>H!&8YR;W%`OyrN5FHUO^U&8OtpT;e zu&tLY}6t1yJopeMA|HCdEmx_VkZ(A+m;?Fh80SPf#A!MSVTK+yC& zg5RDVC?Jr>Xa$dG&Fei2L-w;rr{d>gh!6B@=L*nMgl-J_OaO1l7sCOTWT89{lP6(9 z20cf7GGY`lqpfHlA_+4Ci(dygBp(PfBH_bsix3eaz^)ThsXmhureX`1B#u|}aoYCk zY?7ATgi!Tes?tt@4c%-}ONpOO_uQjjoPWk&{Ka2fb=6g`h#4>V4qxEIacl`gbt21& zA-p$i+p2DE7^Wf1GGfN~h4BnzC$b>i`jKBB+_w9Hum0I%cYQsX?d}*frLsBKag;{= zMfWWj0U=#vsa)6FKk>VwB;Wm4pFMZxm=unFKPq_VL)8y%!8CM^7p}kUUDv+#J)i%* zUuiY!WT8w|k(y-{f0q<-_0lT0E5++jaRBZ~I^}D(4HOC!Ty- zZMP*!GA&CKgsZN&y5;IiBPC3b+*1J9$%6D&@SKIQpo_0$?$j-Nw)E>^mj2jA|o=;hXjByw|-2xj`sIpq+(Mcv@xu z$btRP`XAx)A7}hH)f3v}9aA+zMEa65q#m7mA28Ru)~C4q>0Trg8LP?&9_;YNTTGZF z3s%+)Z?<{L0Ab5u8m_~zj>qCFJ&UPB0yDYBUT1$W0Vk+Ho~jD~p@Wt|f0o zffz>BKu_3Cs4!sNhgw3#{6U&2_t@9yMTa=!fx$^kdPRj?4 zTo+~t3In7WAgJgBsv)>P8L5nk|AIa`3!^d8(JN}e4HCmX32|Z?W*k!g7{#|B=|Qx> ztBE@t0|igcJd9ewjtPLk~TUy^e1^1$~Ly%`6W~9^USHO&p zy!Q7;oL58<*S0XJXXOaR@~K3^_1t=KgKQ1F*Ij$f`1tsRU`D&8jEs&=UH|sX!077H zho8Fp>vAM!nwHjTz3&%(xn8L}asOSo!_Qg-e*boe**HLV|G1{tR8-N=HMeu zMG*w){9r`K*7QY*6h5zOYB&Ld@O^*x=lkFEt{dL>kw?Dv zg=Ejb;La;gJoKFd_kL3jN48&ieRAr`TD8_vlzP2J{P3=E>FKw09o#f&!XWW4xK ztKA=EHo8XUbxM5j@Xb9xyRobLg!MH}*@$SYthpgaXZpN+C{66~=&Vbv`{o)CV-S6B z+y5sppAv{Kmq%B;^cHlr$Pj>uDYHwYTe&Kv=V81~66EF(Y<5ho@zA@WbQyDGAVx$i zJex5+grP3g31hg#L>@ETQCNyjwdgxMFGs2W95U|_<&Y|bExjS=oWy4wN!5Lef_H-O zam)EOV>~J-C*ryygyL%Q>N2O$!ZEI2+zH2d?T4t@?t69;IZ*&ov6HJ^#NRQT`uL>y)nix7;MB78(fF2&K z$=wU1pIG8Y&DNL0`M>Qg0g`C?By3AT?Fu-8r}_x&MS`^to}Pi77@ey`FE%0)nGla5 z;xBrGjCo5Q&><2}ojRHEj$7bQk%j`T0rDof&NTi#3L7zKQb6;h)yh>M5eEHoIGBOr zG7Qeaq)##NNPVDhtuI-y8|>W1)RT>U4L{0}jw~t%g_IA9t>%Dwo`hKdi}iMPcf9H8o@wt*`Kp(H54c>xuLKZ$7py=aW_N44%YI*ee`bD(a4U6< zFC=qY*7eW6^7NtSo_3=$Y;hhEMko@&$X%(|Ur#ysQO$Zi7z*vY@y_<~oii^#F3H~A zx85^*?7)#{e^4sq@q(-kPt||CGV_{GXx3{Rwr=a)dEM-4nWLSNn0)k^M{d6VH%sXx z(&y-|tK*ZW4nF$8z~;+#-uAJD!#`wYzrTAVeg4RyC%%t&dGC#PLU{1V!9%DaxYiOJ zkv@2TdEtu7_uhQ-jn91Vt12Z<#xto@R+e6yr+JS!qDZU2fJ(bo?GBl#_xHxRO{Sh^sa3};a;1ttZ-?^4bkB_eI7XVS*>=eU84bH# z1Hl8$5ENA4tE7wA6NTIr)Jt9L3UGGG3KWcxX1g3R+znBz&Vz|?EJRgi*ro~&mo{ku zgB^!)nP!fq6|3QO7w9m4QRKFa|07lHPk1LzQDv}I+O!#(x493P{BP*Q_38)2iro-d zo#WY>$>$BL$S`$FH(A5Sm$^Wu5v!|Wj7yJt7MuW0DYm83wCOYqc}ef7ngJWM(-7GU z3w~k)70N=`A?_MV%dU|(h&!y)zhQCczs!rns{Uk+jsR zK%-j0K3-}J-nYZi0F*NjU4;H7Ef03g^>im`+AgjJk7W1EHwI_|^kjagpdd0q7viTUv#4Iv&4^c#f?sLmyrTnT8XcSEGbBDqHb*|FAvsjG>UVDR`%AQ)`?N3WYArTzU;>tfo@UrqVk! ziXn3;pV4##EYTMX%N}AzHmcPn~nNfyx9UWIus1-+_bJO95{0H z*pGhnyxZe`!%x7&P@B!6bsIkRx!*l1TiNFDO~Oe*eYAQKU?^R*ki_Z@umgsdH3YInUB;&US~qLT}U^pE~!# zQ;%sXKIE`@$DV2hSs+oXR1pOQQnq9w_SUPF-B(`Swe{-B<($A!>$?M!ho0MW&!>tj zOQ6+CrWlUJ+C6xWs&$Z2xjU@5TGlpi7&-UC4^AC?@v>X)ZHUq7^XE`r(65f~rc_Ns zL2=c-tFFKPy8RD-r(x4)=aQLB7Lolu<&4+Y+OEydSX-M~8w>x|wDCT0##`$QvNqKu ztnlt~Mvb;&w&(ta|5H@T*A=f+J%7D$|HgYJcgd&f|3aoFx?PPV-L;~0^xAO*CIwt| z%56DxgO!ENM*nq-%Cjyw=O~V->Mq%7u0rQY*pegVwuM1hq`=8TR3+1RLWr>4kVU9P z5J`}nNBS0cD{4ojAa?7$=p;ssj|vly-xAxKIyJ!SV$-C-$xg+o3NIF(`TdQGnC zUX$l|&afSd9NYoRV^q`B_kMjdg_kEf^{0kHo{aFq?Vz55xQj1 zqfo7p!MlJ__Ifxs7M%%PSD|}s^DtX@A+!K}`(c+!W>vkk0VYZiz;K(q?qJ8Ezcm+~ z%|NUSJ|OhdaFeb*;Rr+lxPIb4sMxJEJ3iHA@P>&gh`&sR`Vr%Nnat+HpuXsZ$~5#P zp=$%oUJuW%RV5{$2bp`!J=N=4Zkn!aBQ|A>oOZ@kl6gkOC6B||YvJ@V^dMuk3TiX0 zd_Iksehvm+fwA3W<_BH^N(9vpl^C7E@O#P{UmFD5Aa}C|38NR5)=mj(eQ| zC9*2&Fw>wlOKb?TAFk$La5^@)ig*xQ7 zu1~1->c9TOpC=aPkk$6xeB0UMM^@uY_=|?2OR}`#vg@|raBtWnynOKZD~FHn+Ovl) zCyqSz{X#zH_V`(WZ&s`S^-un`S*yJC_&;Y>;&|1Rrb7RCMgmPU#az0*yJzPO_nPju z09_Y2`r!HHfxiCRZoYQz)RC|J`Twn{DxTVJ|LI?zI`s1XCm!**y?A|AmzP+M^M}I7 zDE&iYq0yZ)$vkE0mv38dHu9^QD)lb(x0+V8^zX36^0aA%%BV64C0VBKM@YvXlbY&A!);!6kkHi-B zz6n?_fwm1!_R|Xn!rWQO|J@&F}qBzqMR-mX?#0V{5H7 zs4K6$VsL11`sE+EqJ0A++qJ3Ja7H$pEfflpB)uz~ae**?s+^JZk{|H5#CMf5h|X|4zk-~htOml1 zbxZ!2)w>4oSs0V2D-ZkFvd)u+C-QDn4YXC?u$2^gVT?QbX`STNHNK=>OMXKxKXhI z+kS#h{A1TSYhBiE!ySfTwu4GBw5if!&GP6D*XJxx!uUzpR3fuOFuJ$HWPzv{Z>0nz z;?N#kB>Dpa;P9kbT9*J}7(;lrH2_2FhKwJYxC3H+%W2USg5q8{jGU2%wm9^1phu)! zc%&f3lv3RP{K}OJwP6$iNg@TP7#IVP7>3y*1Zv>L-}2JcSR~6@=9&EX8Ccg3Nr?>Y zAPyf$4uUkhxE?M#_I{ND1zi=PrsuD&GxGYPYQ?|6hGpdyWam&W!1w9_!6^}gP2UC&THzfpkDNPYI*>^ z;OTk1d1x?gc@h?iHir@>?>Pj%^=g74o)_V1NvrsAv9OsdQOfqge&g*TX zY}<;ob&FH?88C;$AG&1v*i9}Km_*QG6!|}Y==fCBeovB) z>>U~&-E+Mm#kOu5d-=e@L_C3#d+UbLzW#2_u(GqK|M_qK-}PH}3}1bh%j3K9iapiz z^56ci|DIiqOR{$ju*!w}_RFul>ZaRs75&U&ddKGV-Q8Ue{^R!$CvUmq_S)+F_y6X9 z6?6H`d#||ZSAP4{^N+s#$Uoivng6SMc2sTaYBOm!tK3{z7vBzx(?z-jrK*0NhAMQ^k zR%A)~2_pTNGq!Hs+}YJJHahy$lh2$wedg6FfY-p+ds%CXoN-|u`2P<$;|Ir86*ep_&!mOe#%%0N9O9P`=OP)2S2{B&V9D_T{l%CLp^d3seo@BCNbWu@}36e zwp9k9-?z0S-Kg5n(CkT4e2IL+I?gN%Ps5l>9FrQu5SPgsdayy3(cAF+0#L4l)EX;A z_t43G{7W9DuIXag@*t$LmZds^PD*lX4(T<^1x8BrCrL9iJOkzg+i=NTO?xDL3)6JA z&=V5fmN8Cn%!JADCVAD* zOD;;Eae7L&KVp}HOvMbab{ox_W+aw3lPtGvYC1nfjZ8YjUZ}K8{ahA7ZE*>Dve1Tl zXcT6fB&Npr-3;%}2R~b6+b#V8F8}TDGT)QXyYLzhk(fPY0M{g$e}_j3a43ZJ6lv%q z6S?iy;5;{|dGPqbREe;V6_7ZR+Hn}5-6SQh=ZV6k)~3LYz*3+!GqL5sK}uJnP*~#( z{Nz~}iBOq7cii1yXWdpaEQu;CY|dqbrSCxo8;rYnG=_`bIl@4To+toAgEhL^wkfuiU*AIpa(`KU1dHjSqK* z-A{k>Z;}hM3@;$|5jbnfolQG;_wKlUVrrpS$crKwSV@}HG!4>G7T5&l*$#9GK!)^+uAsn$TQ4_{^==^ee|BeAlh!!%NFCRtW0Tm9Hml&4=t<| z+!D9Wei1;tPd}Jw}5CpDTtz*D`&xh_T7RoQZ_;S78_{c}^bIbBq zzxs7i5H6nM-6|k9@e(0>dA;79yS6|3qvz-67w~W1pxXF7=8V_$fv<7EThPV@l6ZaT z)c;YOK|BiW@Gmq5KV_{9bsn#3p1+^_M8^a5FCaT6X*bx{5=&?GHZc(kX5Uq*~tn`?R)WCg}QM$ z9Q8VNMG}0s)LkD`%rlH~NYqY?=5-K3M95c$KtvceL`xm6NdDMeZ9Z zok{`LR)8VmqW!A+?;r$}KA@ zO$G5BDU9w$o#Afj7`#--UNOjY!b%Fd5X7gT!vYv3gOTx-?rV!ESx&{%%d3H>#DkC?^Wxry7i@_XKUqx%jH_bGCclDwc6Ryan}cL z9vSF9@WaO!^TzUOrn|4VD=43T@v)N!4|oHCwQtF1Qa9f7v0wPaXQt+s9)0rpvC)zB z8`eGZ)H92VOGBe$eKGIip{M6&Chz;Ne>jyWZ$aW~Ydbd^mnC^*Xz<9PSH^lmm0~`d zS}9sms?abr1+SH&G;6g+G#c^w{h4$cFFRi9wR+#tv0+VBr)5ByFzBNz;P@Pi-3|3CNqb5m2(m&zFzW-k4Va>g6>fjbwk zO&P6TxHsVp`~km+V=uL*Pxsl84>krrW33FgA1f-}|FQhJZ9C3hBOI?D^?KBbn^=>E zF~r8mQ4$*rWow48Y<5>@f21ovG(HDsfn71DU^odqgD{^VX4fRX)>@5n64YCx?%Hd_ zsLt?!S_M~~czWx-knM#fjLYxo8bTw3<9w`S{JU>s5ZzR3g-_+ z0&FSJRh}%ZvujkFo#Md2INZOh%3Nus_eYVvxD0t29XIW{swVGjYJQI2W5U--HLzW$ z`MS;;#COEzDRYLYfyeEt>$I?{b*QEom9pMIwds`tVyTvOD>>29*)jz1^l5T1jVNT2 zKy0;SXrfX{k}-VzCYV`KFCSi-S@YF!*p#YxyG>k7pWF1Uyhtp*U9fciu z8HZu^G;DT}IelCX0;nx&Ai9YsW3vXd1(KKetk_xLJ>ayHl4Zk9rn=H5$#c{wpbI1= zUWh^-FA+`{hS(HJtl}unyfYkQlYiV1dcs1q)!%s^_ywKu26a=YGyeLo z0D?e$zw?!vVo)s?kRP`otocW^-1Lw?JYH`#pDyLIR;%iC4UQO^^@pQ^>@HX9s-j$s zXlrXzSMAz5(9<=sTzF+R&rs0Q*Sl-H_tZ;IpE~>s$pNs8g>ts7{f1xq+zSU!AY<{b zYxc&kYfYn|ZQH_#yRDQ&Z1B{;%FZ=vAGOP2Kw8U%BTqzcDkr@W4NQFW~n_ zB4HF6<#MUJZ>T%$UOl}(u{3|{XFk9Gl@p)*_=j+}{V%=J(b?YL-&f9OcW)jmx7ywEVA}AEPswtFcyKrnkzP1wnW-pOQ7iN7;vWihh$W z{dcA_0(abTM=a&3XMYl{jpB+s=s7v&MxoGs8tn>&y!qEwc$DVn#Uw`81a zn2A5y5UpmzY`&!XD}v8IQi+Xb=mDGAXRF^5EPQ(VEq>XckJwO0tfj4GI%`B3$#n6W zqPohej~BGIK!%B00;f->hMg=GF>^J_GwTfFRSyxE&NN`HZqmtu~?KqG4_#*5>*|Y)9ZH99V@Jzr44CIGkHVwqEz1W(S-5@>EwhXEl zV^?7Ck2;yrjd#ZOfaW13xkx1RI+#IKu}FT=EdVu4`oMFoS->_@8XgSlfGgk?KZWq! zvEU5oz#+%CdozAdrC9TRt$FJ?bF95(<$~wp2dx*0NwxbV zY>7Z#B!^TPff=&W$P1(F^rA8_<@86P6d}#t?lQPhNvtQibPW%H2XoE5HV&O`h{TT4B^=Dk11OL)r{$;jMR&9iVpTNz<-F*0? z;1WcJsugoev)M8@!jphtq+Rs*6;*9E8f#>85!Ty0$Mkf>g3*q=&Ls*BJXnt7KJ(en zmJ(Cn|GPh*J$I&SV5q&b8>PhhYwtdE60bBn$}Xr#a0%+CGrp?_NA4E(xk zsc`dspZL&c|J(fH%2)p3@9`kl(!Uz%EpP7$duod(S65c{{KBW6-+$n9zy52})6>sB z`+T#h$g;G3>-rs=)@70_xpX34P*cU4p*G)oSjdIm7kHP9c;A*Y@c*JHj*N`BJ#sw0 zoG%oRfNj%$CpmB=j7%njoPh%LLO$bXjx+v)Yf~x?4 zo#92ZZv7p~{0EqKkslR2>PC zD^4Z|$p}>(NtovYLfO@>QZA>Smzp`r!#9|K`C@G!tLdA#3BO%YDS_Q$GFzivW zuYjDvMC@|H$0kUWGYung=nX(A2*ntg@Ig#%Tg6~~$Z7+m`@>m+o&c0bebar{#`zJW zc}%K5OU`8u2Iir680NOXStOhDuudaAE6YuAqf9A4BtvEqvH}^Q$|fM#1q~Nju;Qyk zE>f=6dXWiq6Zjo4Ns~5gR5nx;8l8}Jk^FOAYndlzAQpn+K6nwY*ebNm!{91(c%k0S zEq5|WTce+Gu02njA{CPiP%>%qrNrn^A~Tfi)&i6qWQ+Ka&X^+z`pDJ{m?(yNVL1;` zhlMbgol?ioJHzde9)d-@N9SQM3WX2A3wT3kVOVeZR`H<4p&ySY3RxsA{L2~A&KV4~ zJS!8>iBHZD@uc!ot#)w+F&0>SoQGU<+oY4Ficd6E)g=JyFsfBEJ$WibQaJ=;V(cI`Z7p`)<4E-ur)T;@F{8g`J+BwOSDW7VoLu>d6R& z!|S>NPkj5!h|)KH=$FF7+mD@{u{7oNNR7OO$I#Q$efK>deBj&vlt?7p-oV&U4_%5s z{LR0Mbo4kaVb!cvueci#V>m*Oj5`-g8x@~ib!x=vb8+Wdl=JcpNe-F5dJIPl6R ze)(e=%2V1vnykgIm#KK%Dmzz&lS94_~bNW3Pclht-j92$*E%fmMzou{A z`E1tm*UB_dzaC z+NTVetx@h;#T$0=vfRM9yPyWErnd|Mm(*PEJI@z2iBguS&K0%2vxTa&_6VNN2u#b+ zM3|_N5fRQw7;u2v42v8wPztHw@C_Hq}ajC!WIhN;wCE=ge}3rR%`X@adA6|Q1<>%8plY=j0&xnzi0 zG2B7L_M^QB)X5VU?S~RNv1*Xmlu0)ii=w{>kw0Z%!j=ABe)7>!9I9p4;)31 zJqsI@mUl>wEOtQxVoB&kZg7LrBxA<;eyFUFUTeA4`{sn8=_cd5X%QL`DAvJmlF6@C zhg8Rs?nu-YDw)3CvqYxqmac~9qf|Z((M4yV2tgD9JqR4o`<#N&4EP;jgHZQF zg-lc;wM>;DRFMvO;`sqOh%Hm8ST`h)Gwjyrrxl0>m@+CGyhKQ!coBk7D3Z=ixiyPv z8ZXW{*w6q8c_IS2A{APKKEct4saf17>2#F9D^ZOARaYE2>qIb~sDRW9t3G0p!nTIj zI=J%|`c&EWnhq!84P_18#K3^BTXK!^QY;+}ul!LU`nY0WlFsboEmY{_WK9gV)~k39)1BrB@Dn zT;l8HN$X-bZnvwqw|9Pi0hNJi+FQ168t)E0{=nbOPoBq95JXXH)b`!=i#LD#R~Hr* zzxUnmqk6aiqrB)EAM76M^p(ohg^DvfHQm?OgUDGfm+@aYmfgI07lcx*Q_C`64yMT!bf%PdXw4>^`mb6A>QvP+A{Na;${~pTf?Uq+iH~D9$(Ep*l-F! zR~;?S*A#cdkzIls94|5(Ep=XGlcrGAE2gg|y1nk2PpYW4z~n54q415)s4?PfNV)m~ ziE+=gR^a(|qgHlPMO!tcd1^4@L|e{CWLc>-Y!}vMU=)Z0yW@xQDH!X3+{VCE=av;N zsvGCI<%u2)_VeV#x%DtP4pSuvoFYqhBA)U*^y7_Ph43tNG+W7xcBr&Li3jC0bfbJh z&ZrSnbTvvUQ!TmGaIOGf2EGZL4~z~d_(pSlZ;6b1D)$gmcb`epRGAHMP9~B?P{5spj(O+_K?U~-kUFUP zNf`G~^)YAZ80^rAv|8wZ6asw$dhi(5!vreA)*_p1wgy14l#wNtC(Lg4v}TG+4yh1B z{pY1>j>DG0kL*$hH!2uZZ}^J|=*G*q9?rMM#5xKkx;TaBb%SA>mJ6RKm31K%*&Xl) zeC(;JW?qWU_{KNBv3vLKi*?3p{HETPGZ60m0e`)iZIp|sIULL8MKRpjS1MQ5QoC>X zO9(u)w~ayR=-6w;wY5fb-_=)TCSF=Rb!gpXAL!b;Z+dbvnMlwy{kDVwYmQGey>s_Y zJw2B=ceG>O?xqktbMmCu<9YK%tylkrYSn{zyuPu~A&*mk?3;fpdOX!qiKnUEx81kr zrhAgf#G{WqD!HT!--Xv`bYy7zmJR82_SmV3M!m5%e-JnPD8$W;8#df>%gyIb9kR5> z#7r{2nq{b;_Ryzy$QigHXRP7%C2>YJn_c4!e0VN!##_+F`^g!v@6XT~GRgC;^BG!8 zXIv_0ta+h-wBGS)Yo(+6e3fy3W%|Rz;l-Qe7uXd`Rd|r-CAx2_I=TlAwFfdq@TDsq zRmkO8ki{OL*!5P?(# zxiB{03CG9iX{o!(4V$(`uP$~JQUTsF5W)y1vQ4VnrtWsAZD1Vd>?bH5qfdrCvAPxE zq}Q{=63M`MhU>y!P21121+$j-RCSLS)ckD~VO)bfKrF)#0pvj8#fBxbb&ID}lV|M} z+9TF$mS?6pk}}(TRC!dMX_vBO2ttQZE4q<{5b_qN{)E$o_W@BG@v)WQ#lT<)T z7UR&MvwV#vb4R*`M7`Vf`)tQkMUCXZ-?7g4<~P5&#u=}#n|gJMk9X@} zyF?o+6>TY9ZB)zrnf(g;Q(m+jb;Jhi`nZ2cn-d}GVb9V5H0$19ZO)0V?vZ6x<(=>#EFcOC zh<*;Wp!Xkx?Iqv?Q0;r!_`?srx}~yZra2<}^)TBUP$vavjj^OJ7 zWUwBjDa)awj*;N%lWu(YMOM-Bf=8~KE;ggZa`h-@8=ZVID&+}KcVoC7P*NgenpMg- z>kK0(BZpxiTnCRsZ2nA)mx~aj$$g-qHB6$jYh`5jtXHlTPX?za`m;o2`0He_-T57G zvI@Taa1~0E4KRTc1ceI=bP6I_@Zw#<2c!&4JE>l^Y9R51Bxkb8U?yv1Wrf=kkg%>_) zTny5E5a&p7b4?^iVzOZQg>oF)p4BeHbKC|qJfTTcQn?a@WoUFk;ykQdgtpCa7QZwH zG1NmSCEFl{U%dkD1v>13dWb4y?C6v;Y!F++Y!f`lVHhj;iEt^j(di2K588X%ARU3M zPAbRnbI4L#XNxoN8!@UgY_93JXx3yMv!Tch)72hGYccls#Lg!x<|VC7y`D2Jb!`d} z#N(EuvGzu_SjeVikDrq~^;RJbylEEq4LCzp)c%oC*(maQ#qq?F6|0obu;WavUp);>D+l7Z5>nXn5$xo33A;U+_xYi-%6k&c$6W@#22?pThQj zamJg`#_KtQ`mv+%&u%{B-?cWS1?=#h&7n^_D?@F^Yh~$MD<2;E(BgHG7uhFFeTB;# z;U%ZH3;{&U_Gmh1M^^Hk7{>bON_SV%wMjMW413;8WV><@2}5BOI?BYws3J0o-&?jV zdb!#ghaQZ$5ttDB@T42yLO_f_ehYP`Q&`2w-Qj?ttdbI3HQF(%?S_O#g4U*|r^bB5 zhGDRur<^B&wjJHgrwZ+O&%<2N-KgsvXKZ8TO$v1!%rxDs%Bsb40TaasH)_B*0BV#! zWvRTw`YbcWHWCs|Ig-&-ssb+s-vAj{5VqP1BurIpu4vxF`e4;9G=& z5(H5CY=Kh@-X+SZINmt)qUOL;Fk3cFQOLODruXt%Xt>xs<(fRuzXELvL?qCLVR96v z@N*8q9+W2Q;oK=0i$RGcek8#(v5xn%;2>_cX2-f4!23aOgKBFst{;D;14@2K(?As< zx(p!>8kfPuFlnKvm%&>H87Ulbe-CviMrYGhY|%uW!Sr%*PbcC(tt|7e)Hj{9);Yw; z)`cXu9%fND)LYA6Zt?^f9AyN=l4Y}}we{05I6z!UIJ`{=?)XvOmWAS3*oZ7X)GAL% zxBM2vM2lpQpH=~b-=@qoErwk-dU94+B6C4X`25boaFv+aR{E)h7?Vyy*I{e-pfisL zkDt&gKMSEC+sIAsWRIvik5Z6wIe0lJ$2Kc$rR+9XJ|q`aR`~sJ+f%xBN%J$1GxqG+ zlaBunr7VBVGO-Q|qe_}X6R z>BEPoQmLq_`g6``9cyd0Yn1eXNA5RQBkglFUH;bM-GkRJT^~KbzGA7XLeU7|dtR~w zTd9*#Z$9O;E!O&Lj>kjShGVnAk&;uSn7Em##d1#A3zZDCF9vYiyYT!bYuaVorlM=5jWqDVVuj%Fyx-pN??>P!PPyuX$Gkf5W0^)Jltbni) zCeOe+yfYG5OVD;2dQAe&bq^7WdJ{y9p-o`xq{OyC6Ss436pFpD?15?uVv{hclU1^F zeX#5T9XSE_$7{t?`X*)~$d^`}jzzWK4Yi^6dG}BQWl3r#dZ4&#K^ z&{awdM+(7xM6uV~X}s-Ogd&mqK6LlXFCT1^sjwX)}d*^dwW+QM}kpJg95)#HNR;B#y=+2|bMrrX9chRPSG0|f|a3>O+K zMX$=bswx};by+YdPItIP(yR~&K{3>_ma2dsL-GXlXuuE_fm#M4P158c`l0v%c(I3G zF@21M$65hZCzYcHaEMSAsqiWKHkbK7HDM<>U!?4B(A0=yXDM@@VHArES|C%p%~7Ki zw_C42BU33yv@sm8tuRe*1Lh_Ru4vTJUy}_E+;DwEPD%r-&krYd7U{rq>Bsb9$KBq#r;R|Eu3K7I*g_M3(n3l;BVYs_W zZMUbn1;cuTDNY2Jpz~i~OAM@Q;LHX%Ujpw5*ii$|2AEiZE^@oc+Eg?N?GEXWt4L65 zfRF{B0j`$6Ix!-YNeK)qk&$*fMdpEX_z6nO?V}HpF~}p$XvEgZg;%Vfjk{b*-07Kb z40b`Hr#<1?s!|hB297ul9Dlz9r5QTEd>TjTSiXh4u-={Zi*Uf2vV=O z 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 f+_wPbM>_2+KkZRqt6BQh00Yd(%& Date: Tue, 19 Dec 2017 14:14:54 +0100 Subject: [PATCH 491/606] 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 492/606] 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 493/606] 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 494/606] 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 495/606] 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 496/606] 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 497/606] 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 498/606] 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 499/606] 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 500/606] 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 501/606] 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 502/606] 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 503/606] 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 504/606] 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 505/606] 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 506/606] 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 507/606] 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 508/606] 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 509/606] 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 510/606] 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 511/606] 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 512/606] 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 513/606] 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 514/606] 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 515/606] 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 516/606] 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 517/606] 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 518/606] 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 519/606] 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 520/606] 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 521/606] 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 522/606] 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 523/606] - 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 524/606] 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 525/606] 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 526/606] 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 527/606] 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 528/606] 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 529/606] 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 530/606] 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 531/606] 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 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 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 532/606] 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 533/606] 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 534/606] 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 535/606] 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 536/606] 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 537/606] 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 538/606] 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 539/606] 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 540/606] 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 541/606] 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 542/606] 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 543/606] 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 544/606] 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 545/606] 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 546/606] 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 547/606] 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 548/606] 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 549/606] 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 550/606] 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 551/606] 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 552/606] 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 553/606] 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 554/606] 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 555/606] 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 556/606] 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 557/606] 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 558/606] 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 559/606] 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 560/606] 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 561/606] 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 562/606] 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 563/606] 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 564/606] 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 565/606] 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 566/606] 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 567/606] 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 568/606] 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 569/606] 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 570/606] 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 571/606] 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 572/606] 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 573/606] 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 574/606] 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 575/606] 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 576/606] 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 577/606] 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 578/606] 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 579/606] 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 580/606] 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 581/606] 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 582/606] 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 583/606] 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 584/606] 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 585/606] 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 586/606] 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 587/606] 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 588/606] 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 589/606] 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 590/606] 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 591/606] 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 592/606] 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 593/606] 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 594/606] 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 595/606] 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 596/606] 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 597/606] 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 598/606] 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 599/606] 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 600/606] 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 601/606] 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 602/606] 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 603/606] 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 604/606] 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 605/606] 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 606/606] 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).
  • * This method is deprecated because of unclarity. Use setCircleRadius instead. * * @param size @@ -171,9 +189,7 @@ public void setCircleSize(float size) { } /** - * * This function is deprecated because of unclarity. Use getCircleRadius instead. - * */ @Deprecated public float getCircleSize() { @@ -184,13 +200,13 @@ public float getCircleSize() { * Enables the 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 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) + * @param phase offset, in degrees (normally, use 0) */ public void enableDashedLine(float lineLength, float spaceLength, float phase) { - mDashPathEffect = new DashPathEffect(new float[] { + mDashPathEffect = new DashPathEffect(new float[]{ lineLength, spaceLength }, phase); } @@ -215,7 +231,7 @@ public DashPathEffect getDashPathEffect() { /** * set this to true to enable the drawing of circle indicators for this * DataSet, default true - * + * * @param enabled */ public void setDrawCircles(boolean enabled) { @@ -243,7 +259,7 @@ public boolean isDrawSteppedEnabled() { /** * returns all colors specified for the circles - * + * * @return */ public List getCircleColors() { @@ -252,11 +268,11 @@ public List getCircleColors() { @Override public int getCircleColor(int index) { - return mCircleColors.get(index % mCircleColors.size()); + return mCircleColors.get(index); } @Override - public int getCircleColorCount(){ + public int getCircleColorCount() { return mCircleColors.size(); } @@ -266,7 +282,7 @@ public int getCircleColorCount(){ * is higher than the size of the colors array. Make sure that the colors * are already prepared (by calling getResources().getColor(...)) before * adding them to the DataSet. - * + * * @param colors */ public void setCircleColors(List colors) { @@ -279,7 +295,7 @@ public void setCircleColors(List colors) { * is higher than the size of the colors array. Make sure that the colors * are already prepared (by calling getResources().getColor(...)) before * adding them to the DataSet. - * + * * @param colors */ public void setCircleColors(int[] colors) { @@ -293,13 +309,13 @@ public void setCircleColors(int[] colors) { * "new String[] { R.color.red, R.color.green, ... }" to provide colors for * this method. Internally, the colors are resolved using * getResources().getColor(...) - * + * * @param colors */ public void setCircleColors(int[] colors, Context c) { List clrs = mCircleColors; - if(clrs == null){ + if (clrs == null) { clrs = new ArrayList<>(); } clrs.clear(); @@ -314,7 +330,7 @@ public void setCircleColors(int[] colors, Context c) { /** * Sets the one and ONLY color that should be used for this DataSet. * Internally, this recreates the colors array and adds the specified color. - * + * * @param color */ public void setCircleColor(int color) { @@ -326,7 +342,7 @@ public void setCircleColor(int color) { * resets the circle-colors array and creates a new one */ public void resetCircleColors() { - if(mCircleColors == null) { + if (mCircleColors == null) { mCircleColors = new ArrayList(); } mCircleColors.clear(); @@ -334,7 +350,7 @@ public void resetCircleColors() { /** * Sets the color of the inner circle of the line-circles. - * + * * @param color */ public void setCircleColorHole(int color) { @@ -348,7 +364,7 @@ public int getCircleHoleColor() { /** * Set this to true to allow drawing a hole in each data circle. - * + * * @param enabled */ public void setDrawCircleHole(boolean enabled) { 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 d478024808..aac9440111 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 @@ -27,36 +27,6 @@ public class LineChartRenderer extends LineRadarRenderer { - private class DataSetImageCache { - - private Bitmap[] circleBitmaps; - private int[] circleColors; - - private void ensureCircleCache(int size) { - if (circleBitmaps == null) { - circleBitmaps = new Bitmap[size]; - } else if (circleBitmaps.length < size) { - Bitmap[] tmp = new Bitmap[size]; - for (int i = 0; i < circleBitmaps.length; i++) { - tmp[i] = circleBitmaps[size]; - } - circleBitmaps = tmp; - } - - if (circleColors == null) { - circleColors = new int[size]; - } else if (circleColors.length < size) { - int[] tmp = new int[size]; - for (int i = 0; i < circleColors.length; i++) { - tmp[i] = circleColors[size]; - } - circleColors = tmp; - } - } - - } - - protected LineDataProvider mChart; /** @@ -95,9 +65,7 @@ public LineChartRenderer(LineDataProvider chart, ChartAnimator animator, } @Override - public void initBuffers() { - - } + public void initBuffers() { } @Override public void drawData(Canvas c) { @@ -595,10 +563,16 @@ public void drawExtras(Canvas c) { drawCircles(c); } - private Path mCirclePathBuffer = new Path(); - private float[] mCirclesBuffer = new float[2]; + /** + * cache for the circle bitmaps of all datasets + */ private HashMap mImageCaches = new HashMap<>(); + /** + * buffer for drawing the circles + */ + private float[] mCirclesBuffer = new float[2]; + protected void drawCircles(Canvas c) { mRenderPaint.setStyle(Paint.Style.FILL); @@ -614,16 +588,6 @@ protected void drawCircles(Canvas c) { ILineDataSet dataSet = dataSets.get(i); - DataSetImageCache imageCache; - - if (mImageCaches.containsKey(dataSet)) { - imageCache = mImageCaches.get(dataSet); - } else { - imageCache = new DataSetImageCache(); - mImageCaches.put(dataSet, imageCache); - } - imageCache.ensureCircleCache(dataSet.getCircleColorCount()); - if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled() || dataSet.getEntryCount() == 0) continue; @@ -642,6 +606,22 @@ protected void drawCircles(Canvas c) { boolean drawTransparentCircleHole = drawCircleHole && dataSet.getCircleHoleColor() == ColorTemplate.COLOR_NONE; + DataSetImageCache imageCache; + + if (mImageCaches.containsKey(dataSet)) { + imageCache = mImageCaches.get(dataSet); + } else { + imageCache = new DataSetImageCache(); + mImageCaches.put(dataSet, imageCache); + } + + boolean changeRequired = imageCache.init(dataSet); + + // only fill the cache with new bitmaps if a change is required + if (changeRequired) { + imageCache.fill(dataSet, drawCircleHole, drawTransparentCircleHole); + } + int boundsRangeCount = mXBounds.range + mXBounds.min; for (int j = mXBounds.min; j <= boundsRangeCount; j++) { @@ -658,73 +638,11 @@ protected void drawCircles(Canvas c) { if (!mViewPortHandler.isInBoundsRight(mCirclesBuffer[0])) break; - // make sure the circles don't do shitty things outside - // bounds if (!mViewPortHandler.isInBoundsLeft(mCirclesBuffer[0]) || !mViewPortHandler.isInBoundsY(mCirclesBuffer[1])) continue; - final int circleColor = dataSet.getCircleColor(j); - mRenderPaint.setColor(circleColor); - - Bitmap circleBitmap = null; - - int colorIndex; - - for (colorIndex = 0; colorIndex < imageCache.circleColors.length; colorIndex++) { - int tempColor = imageCache.circleColors[colorIndex]; - Bitmap tempBitmap = imageCache.circleBitmaps[colorIndex]; - if (tempColor == circleColor) { - circleBitmap = tempBitmap; - break; - } else if (tempBitmap == null) { - break; - } - } - - if (circleBitmap == null) { - Bitmap.Config conf = Bitmap.Config.ARGB_8888; - circleBitmap = Bitmap.createBitmap((int) (circleRadius * 2.1), (int) (circleRadius * 2.1), conf); - Canvas canvas = new Canvas(circleBitmap); - imageCache.circleBitmaps[colorIndex] = circleBitmap; - imageCache.circleColors[colorIndex] = circleColor; - - if (drawTransparentCircleHole) { - // Begin path for circle with hole - mCirclePathBuffer.reset(); - - mCirclePathBuffer.addCircle( - circleRadius, - circleRadius, - circleRadius, - Path.Direction.CW); - - // Cut hole in path - mCirclePathBuffer.addCircle( - circleRadius, - circleRadius, - circleHoleRadius, - Path.Direction.CCW); - - // Fill in-between - canvas.drawPath(mCirclePathBuffer, mRenderPaint); - } else { - - canvas.drawCircle( - circleRadius, - circleRadius, - circleRadius, - mRenderPaint); - - if (drawCircleHole) { - canvas.drawCircle( - circleRadius, - circleRadius, - circleHoleRadius, - mCirclePaintInner); - } - } - } + Bitmap circleBitmap = imageCache.getBitmap(j); if (circleBitmap != null) { c.drawBitmap(circleBitmap, mCirclesBuffer[0] - circleRadius, mCirclesBuffer[1] - circleRadius, mRenderPaint); @@ -795,4 +713,103 @@ public void releaseBitmap() { mDrawBitmap = null; } } + + private class DataSetImageCache { + + private Path mCirclePathBuffer = new Path(); + + private Bitmap[] circleBitmaps; + + /** + * Sets up the cache, returns true if a change of cache was required. + * + * @param set + * @return + */ + protected boolean init(ILineDataSet set) { + + int size = set.getCircleColorCount(); + boolean changeRequired = false; + + if (circleBitmaps == null) { + circleBitmaps = new Bitmap[size]; + changeRequired = true; + } else if (circleBitmaps.length != size) { + circleBitmaps = new Bitmap[size]; + changeRequired = true; + } + + return changeRequired; + } + + /** + * Fills the cache with bitmaps for the given dataset. + * + * @param set + * @param drawCircleHole + * @param drawTransparentCircleHole + */ + protected void fill(ILineDataSet set, boolean drawCircleHole, boolean drawTransparentCircleHole) { + + int colorCount = set.getCircleColorCount(); + float circleRadius = set.getCircleRadius(); + float circleHoleRadius = set.getCircleHoleRadius(); + + for (int i = 0; i < colorCount; i++) { + + Bitmap.Config conf = Bitmap.Config.ARGB_4444; + Bitmap circleBitmap = Bitmap.createBitmap((int) (circleRadius * 2.1), (int) (circleRadius * 2.1), conf); + + Canvas canvas = new Canvas(circleBitmap); + circleBitmaps[i] = circleBitmap; + mRenderPaint.setColor(set.getCircleColor(i)); + + if (drawTransparentCircleHole) { + // Begin path for circle with hole + mCirclePathBuffer.reset(); + + mCirclePathBuffer.addCircle( + circleRadius, + circleRadius, + circleRadius, + Path.Direction.CW); + + // Cut hole in path + mCirclePathBuffer.addCircle( + circleRadius, + circleRadius, + circleHoleRadius, + Path.Direction.CCW); + + // Fill in-between + canvas.drawPath(mCirclePathBuffer, mRenderPaint); + } else { + + canvas.drawCircle( + circleRadius, + circleRadius, + circleRadius, + mRenderPaint); + + if (drawCircleHole) { + canvas.drawCircle( + circleRadius, + circleRadius, + circleHoleRadius, + mCirclePaintInner); + } + } + } + } + + /** + * Returns the cached Bitmap at the given index. + * + * @param index + * @return + */ + protected Bitmap getBitmap(int index) { + return circleBitmaps[index % circleBitmaps.length]; + } + } } From 360cc24b971d68529ca0d0686e7e5bff19ac540e Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Jul 2016 00:11:48 +0200 Subject: [PATCH 298/606] Cleanup --- .../com/xxmassdeveloper/mpchartexample/BarChartActivity.java | 1 + .../mikephil/charting/formatter/DefaultValueFormatter.java | 2 -- .../mikephil/charting/formatter/StackedValueFormatter.java | 2 +- .../com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java | 3 +-- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index 775c925bbf..eb17690354 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -259,6 +259,7 @@ private void setData(int count, float range) { } protected RectF mOnValueSelectedRectF = new RectF(); + @SuppressLint("NewApi") @Override public void onValueSelected(Entry e, Highlight h) { 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 b6965435aa..8d169b1405 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 @@ -53,9 +53,7 @@ public void setup(int digits) { @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormattedStringCache.getFormattedValue(value, dataSetIndex); - } /** 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 618b1b1fe4..bc020d5637 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 @@ -21,7 +21,6 @@ public class StackedValueFormatter implements ValueFormatter { private FormattedStringCache.Generic mFormattedStringCacheWholeStack; private FormattedStringCache.Generic mFormattedStringCache; - /** * a string that should be appended behind the value */ @@ -57,6 +56,7 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View FormattedStringCache.Generic chosenCache = mFormattedStringCache; int chosenIndex = dataSetIndex; float chosenValue = value; + if (!mDrawWholeStack && entry instanceof BarEntry) { BarEntry barEntry = (BarEntry) entry; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java index 00bddc2427..8f953a06aa 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java @@ -1,6 +1,5 @@ package com.github.mikephil.charting.jobs; -import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.view.View; @@ -31,7 +30,7 @@ public static AnimatedMoveViewJob getInstance(ViewPortHandler viewPortHandler, f result.view = v; result.xOrigin = xOrigin; result.yOrigin = yOrigin; - result.resetAnimator(); + //result.resetAnimator(); result.animator.setDuration(duration); return result; } From 3f0f3a82c27b139470db884e62fdfee86b68c204 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Jul 2016 00:23:56 +0200 Subject: [PATCH 299/606] Improve setColors methods, cleanup --- .../mpchartexample/LineChartActivity2.java | 1 - .../mikephil/charting/data/BaseDataSet.java | 2 +- .../mikephil/charting/data/LineDataSet.java | 2 +- .../github/mikephil/charting/utils/FSize.java | 1 - .../mikephil/charting/utils/Transformer.java | 34 +++++++++---------- .../utils/TransformerHorizontalBarChart.java | 2 +- .../github/mikephil/charting/utils/Utils.java | 27 --------------- 7 files changed, 19 insertions(+), 50 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java index 738e08814a..81097debea 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -58,7 +58,6 @@ protected void onCreate(Bundle savedInstanceState) { mChart = (LineChart) findViewById(R.id.chart1); mChart.setOnChartValueSelectedListener(this); - mChart.setLogEnabled(true); // no description text mChart.setDescription(""); 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 4c9f8e13e4..28dd77ceb4 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 @@ -150,7 +150,7 @@ public void setColors(List colors) { * * @param colors */ - public void setColors(int[] colors) { + public void setColors(int... colors) { this.mColors = ColorTemplate.createColors(colors); } 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 0d7147e5dd..2f1af59d1d 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 @@ -298,7 +298,7 @@ public void setCircleColors(List colors) { * * @param colors */ - public void setCircleColors(int[] colors) { + public void setCircleColors(int... colors) { this.mCircleColors = ColorTemplate.createColors(colors); } 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 ecb8c2e312..d4ff8974d4 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 @@ -73,5 +73,4 @@ public String toString() { public int hashCode() { return Float.floatToIntBits(width) ^ Float.floatToIntBits(height); } - } 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 be6d4cefe8..d9a57c3f82 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 @@ -84,16 +84,10 @@ public void prepareMatrixOffset(boolean inverted) { .setTranslate(mViewPortHandler.offsetLeft(), -mViewPortHandler.offsetTop()); mMatrixOffset.postScale(1.0f, -1.0f); } - - // mMatrixOffset.set(offset); - - // mMatrixOffset.reset(); - // - // mMatrixOffset.postTranslate(mOffsetLeft, getHeight() - - // mOffsetBottom); } protected float[] valuePointsForGenerateTransformedValuesScatter = new float[1]; + /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the SCATTERCHART. @@ -106,7 +100,7 @@ public float[] generateTransformedValuesScatter(IScatterDataSet data, float phas final int count = (int) ((to - from) * phaseX + 1) * 2; - if(valuePointsForGenerateTransformedValuesScatter.length != count){ + if (valuePointsForGenerateTransformedValuesScatter.length != count) { valuePointsForGenerateTransformedValuesScatter = new float[count]; } float[] valuePoints = valuePointsForGenerateTransformedValuesScatter; @@ -118,7 +112,7 @@ public float[] generateTransformedValuesScatter(IScatterDataSet data, float phas if (e != null) { valuePoints[j] = e.getX(); valuePoints[j + 1] = e.getY() * phaseY; - }else{ + } else { valuePoints[j] = 0; valuePoints[j + 1] = 0; } @@ -130,6 +124,7 @@ public float[] generateTransformedValuesScatter(IScatterDataSet data, float phas } protected float[] valuePointsForGenerateTransformedValuesBubble = new float[1]; + /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the BUBBLECHART. @@ -141,7 +136,7 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY final int count = (to - from + 1) * 2; // (int) Math.ceil((to - from) * phaseX) * 2; - if(valuePointsForGenerateTransformedValuesBubble.length != count){ + if (valuePointsForGenerateTransformedValuesBubble.length != count) { valuePointsForGenerateTransformedValuesBubble = new float[count]; } float[] valuePoints = valuePointsForGenerateTransformedValuesBubble; @@ -153,7 +148,7 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY if (e != null) { valuePoints[j] = e.getX(); valuePoints[j + 1] = e.getY() * phaseY; - }else{ + } else { valuePoints[j] = 0; valuePoints[j + 1] = 0; } @@ -165,6 +160,7 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY } protected float[] valuePointsForGenerateTransformedValuesLine = new float[1]; + /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the LINECHART. @@ -177,7 +173,7 @@ public float[] generateTransformedValuesLine(ILineDataSet data, final int count = (int) ((to - from) * phaseX + 1) * 2; - if(valuePointsForGenerateTransformedValuesLine.length != count){ + if (valuePointsForGenerateTransformedValuesLine.length != count) { valuePointsForGenerateTransformedValuesLine = new float[count]; } float[] valuePoints = valuePointsForGenerateTransformedValuesLine; @@ -189,7 +185,7 @@ public float[] generateTransformedValuesLine(ILineDataSet data, if (e != null) { valuePoints[j] = e.getX(); valuePoints[j + 1] = e.getY() * phaseY; - }else{ + } else { valuePoints[j] = 0; valuePoints[j + 1] = 0; } @@ -201,6 +197,7 @@ public float[] generateTransformedValuesLine(ILineDataSet data, } protected float[] valuePointsForGenerateTransformedValuesCandle = new float[1]; + /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the CANDLESTICKCHART. @@ -213,7 +210,7 @@ public float[] generateTransformedValuesCandle(ICandleDataSet data, final int count = (int) ((to - from) * phaseX + 1) * 2; - if(valuePointsForGenerateTransformedValuesCandle.length != count){ + if (valuePointsForGenerateTransformedValuesCandle.length != count) { valuePointsForGenerateTransformedValuesCandle = new float[count]; } float[] valuePoints = valuePointsForGenerateTransformedValuesCandle; @@ -225,7 +222,7 @@ public float[] generateTransformedValuesCandle(ICandleDataSet data, if (e != null) { valuePoints[j] = e.getX(); valuePoints[j + 1] = e.getHigh() * phaseY; - }else{ + } else { valuePoints[j] = 0; valuePoints[j + 1] = 0; } @@ -357,6 +354,7 @@ public void rectValuesToPixel(List rects) { } protected Matrix mPixelsToValueMatrixBuffer = new Matrix(); + /** * Transforms the given array of touch positions (pixels) (x, y, x, y, ...) * into values on the chart. @@ -397,12 +395,12 @@ public void pixelsToValue(float[] pixels) { */ public MPPointD getValuesByTouchPoint(float x, float y) { - MPPointD result = MPPointD.getInstance(0,0); - getValuesByTouchPoint(x,y,result); + MPPointD result = MPPointD.getInstance(0, 0); + getValuesByTouchPoint(x, y, result); return result; } - public void getValuesByTouchPoint(float x, float y, MPPointD outputPoint){ + public void getValuesByTouchPoint(float x, float y, MPPointD outputPoint) { ptsBuffer[0] = x; ptsBuffer[1] = y; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java index aee132cca2..05fa82ae29 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java @@ -15,7 +15,7 @@ public TransformerHorizontalBarChart(ViewPortHandler viewPortHandler) { /** * Prepares the matrix that contains all offsets. * - * @param chart + * @param inverted */ public void prepareMatrixOffset(boolean inverted) { 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 e599fc1db7..0799679114 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 @@ -6,7 +6,6 @@ import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; import android.text.Layout; @@ -728,30 +727,4 @@ public static FSize getSizeOfRotatedRectangleByRadians(float rectangleWidth, flo public static int getSDKInt() { return android.os.Build.VERSION.SDK_INT; } - - /** - * Calculates the granularity (minimum axis interval) based on axis range and labelcount. - * Default granularity is 1/10th of interval. - * - * @param range - * @param labelCount - * @return - */ - public static double granularity(float range, int labelCount) { - - // Find out how much spacing (in y value space) between axis values - double rawInterval = range / labelCount; - double interval = Utils.roundToNextSignificant(rawInterval); - - // Normalize interval - double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10 - (interval))); - int intervalSigDigit = (int) (interval / intervalMagnitude); - - if (intervalSigDigit > 5) { - interval = Math.floor(10 * intervalMagnitude); - } - - return interval * 0.1; // granularity is 1/10th of interval - } } From 08b2d550496214aae9e4b6a01bb683dc43329edd Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Jul 2016 00:26:11 +0200 Subject: [PATCH 300/606] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 256000ca76..10f104b7f0 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,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/v2.2.5/javadoc/) + - 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/) - 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) @@ -91,7 +91,7 @@ Features - 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 - Gradle support - - Plotting data directly from [**Realm.io**](https://realm.io) mobile database + - Plotting data directly from [**Realm.io**](https://realm.io) mobile database: [MPAndroidChart-Realm](https://github.com/PhilJay/MPAndroidChart-Realm) Usage ----- @@ -107,7 +107,7 @@ repositories { } dependencies { - compile 'com.github.PhilJay:MPAndroidChart:v2.2.5' + compile 'com.github.PhilJay:MPAndroidChart:v3.0.0-beta1' } ``` @@ -122,7 +122,7 @@ dependencies { com.github.PhilJay MPAndroidChart - v2.2.5 + v3.0.0-beta1 ``` @@ -142,7 +142,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/v2.2.5/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-beta1/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 a4ac12aebff0809dc01863c771445cc046a046f8 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Jul 2016 01:17:15 +0200 Subject: [PATCH 301/606] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 10f104b7f0..01a321ba2b 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ Features - 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 - Gradle support - - Plotting data directly from [**Realm.io**](https://realm.io) mobile database: [MPAndroidChart-Realm](https://github.com/PhilJay/MPAndroidChart-Realm) + - Plotting data directly from [**Realm.io**](https://realm.io) mobile database: [MPAndroidChart-Realm](https://github.com/PhilJay/MPAndroidChart-Realm) :zap: Usage ----- From 83a4bf90755a56476158713da6674d4011f804f5 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Jul 2016 09:50:34 +0200 Subject: [PATCH 302/606] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01a321ba2b..842d720470 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ Features - 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 - Gradle support - - Plotting data directly from [**Realm.io**](https://realm.io) mobile database: [MPAndroidChart-Realm](https://github.com/PhilJay/MPAndroidChart-Realm) :zap: + - Plotting data directly from [**Realm.io**](https://realm.io) mobile database: [**MPAndroidChart-Realm**](https://github.com/PhilJay/MPAndroidChart-Realm) :zap: Usage ----- From bad43a5e1319c26ac68db62dc765f8ba176a8b42 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Jul 2016 11:48:04 +0200 Subject: [PATCH 303/606] Move example to realm v1.1.0 --- 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 f0ed2a5a33..9605392372 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.0.2@aar' + compile 'com.github.PhilJay:MPAndroidChart-Realm:v1.1.0@aar' compile project(':MPChartLib') compile 'com.android.support:appcompat-v7:23.1.1' diff --git a/build.gradle b/build.gradle index 0eb3f4e7bf..3dfff3b287 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath "io.realm:realm-gradle-plugin:1.0.1" + 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 1a39ac9b1215f2512762f0ce34695ae4aa6587c8 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Jul 2016 14:49:47 +0200 Subject: [PATCH 304/606] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 842d720470..eac1681bfa 100644 --- a/README.md +++ b/README.md @@ -216,4 +216,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), [ph1lb4](https://github.com/ph1lb4) 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) and [jitpack.io](https://github.com/jitpack-io) for their contributions to this project. From db70835cbc7003e920f23abb3bb0b079e2977d4b Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Jul 2016 14:53:58 +0200 Subject: [PATCH 305/606] Update README.md --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index eac1681bfa..ef80f3a05e 100644 --- a/README.md +++ b/README.md @@ -100,14 +100,20 @@ In order to use the library, there are 4 different options: **1. Gradle dependency** (recommended) - - Add the following to your `build.gradle`: + - Add the following to your project level `build.gradle`: + - ```gradle -repositories { - maven { url "https://jitpack.io" } +allprojects { + repositories { + maven { url "https://jitpack.io" } + } } - +``` + - Add this to your app `build.gradle`: + +```gradle dependencies { - compile 'com.github.PhilJay:MPAndroidChart:v3.0.0-beta1' + compile 'com.github.PhilJay:MPAndroidChart:v3.0.0-beta1' } ``` From 75ba4f69fc1a745eb511f67a41fc48cd37fee39f Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Jul 2016 14:54:43 +0200 Subject: [PATCH 306/606] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ef80f3a05e..fe395d5c34 100644 --- a/README.md +++ b/README.md @@ -100,16 +100,16 @@ In order to use the library, there are 4 different options: **1. Gradle dependency** (recommended) - - Add the following to your project level `build.gradle`: - - - ```gradle + - Add the following to your project level `build.gradle`: + +```gradle allprojects { repositories { maven { url "https://jitpack.io" } } } ``` - - Add this to your app `build.gradle`: + - Add this to your app `build.gradle`: ```gradle dependencies { From e2cbafa049fd160267b277bce71a9e5e7b424273 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Jul 2016 20:22:56 +0200 Subject: [PATCH 307/606] Code cleanup & new zoom method --- .../charting/charts/BarLineChartBase.java | 86 ++++++++++++------- .../charting/charts/HorizontalBarChart.java | 8 +- .../mikephil/charting/jobs/ZoomJob.java | 21 +++-- .../charting/utils/ViewPortHandler.java | 33 ++++--- 4 files changed, 88 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 a794c6a5ef..cc6cca64ff 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 @@ -590,6 +590,7 @@ public void computeScroll() { */ protected Matrix mZoomInMatrixBuffer = new Matrix(); + /** * Zooms in by 1.4f, into the charts center. center. */ @@ -610,6 +611,7 @@ public void zoomIn() { } protected Matrix mZoomOutMatrixBuffer = new Matrix(); + /** * Zooms out by 0.7f, from the charts center. center. */ @@ -630,6 +632,7 @@ public void zoomOut() { } protected Matrix mZoomMatrixBuffer = new Matrix(); + /** * Zooms in or out by the given scale factor. x and y are the coordinates * (in pixels) of the zoom center. @@ -641,7 +644,7 @@ public void zoomOut() { */ public void zoom(float scaleX, float scaleY, float x, float y) { Matrix save = mZoomMatrixBuffer; - mViewPortHandler.zoom(scaleX, scaleY, x, y, save); + mViewPortHandler.zoom(scaleX, scaleY, x, -y, save); mViewPortHandler.refresh(save, this, false); // Range might have changed, which means that Y-axis labels @@ -653,7 +656,7 @@ public void zoom(float scaleX, float scaleY, float x, float y) { /** * Zooms in or out by the given scale factor. - * x and y are the values (NOT PIXELS) which to zoom to or from (the values of the zoom center). + * x and y are the values (NOT PIXELS) of the zoom center.. * * @param scaleX * @param scaleY @@ -661,12 +664,27 @@ public void zoom(float scaleX, float scaleY, float x, float y) { * @param yValue * @param axis the axis relative to which the zoom should take place */ - public void zoomAndCenter(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis) { + public void zoom(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis) { Runnable job = ZoomJob.getInstance(mViewPortHandler, scaleX, scaleY, xValue, yValue, getTransformer(axis), axis, this); addViewportJob(job); } + /** + * Zooms to the center of the chart with the given scale factor. + * + * @param scaleX + * @param scaleY + */ + public void zoomCenter(float scaleX, float scaleY) { + + MPPointF center = getCenterOffsets(); + + Matrix save = mZoomMatrixBuffer; + mViewPortHandler.zoom(scaleX, scaleY, center.x, -center.y, save); + mViewPortHandler.refresh(save, this, false); + } + /** * Zooms by the specified scale factor to the specified values on the specified axis. * @@ -686,7 +704,7 @@ public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, floa 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(), + .mAxisRange, scaleX, scaleY, mViewPortHandler.getScaleX(), mViewPortHandler.getScaleY(), xValue, yValue, (float) origin.x, (float) origin.y, duration); addViewportJob(job); @@ -698,6 +716,7 @@ public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, floa } protected Matrix mFitScreenMatrixBuffer = new Matrix(); + /** * Resets all zooming and dragging and makes the chart fit exactly it's * bounds. @@ -771,7 +790,7 @@ public void setVisibleXRange(float minXRange, float maxXRange) { * @param axis the axis for which this limit should apply */ public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { - float yScale = getDeltaY(axis) / maxYRange; + float yScale = getAxisRange(axis) / maxYRange; mViewPortHandler.setMinimumScaleY(yScale); } @@ -782,7 +801,7 @@ public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { * @param axis the axis for which this limit should apply */ public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) { - float yScale = getDeltaY(axis) / minYRange; + float yScale = getAxisRange(axis) / minYRange; mViewPortHandler.setMaximumScaleY(yScale); } @@ -794,8 +813,8 @@ public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) { * @param axis */ public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis) { - float minScale = getDeltaY(axis) / minYRange; - float maxScale = getDeltaY(axis) / maxYRange; + float minScale = getAxisRange(axis) / minYRange; + float maxScale = getAxisRange(axis) / maxYRange; mViewPortHandler.setMinMaxScaleY(minScale, maxScale); } @@ -825,7 +844,7 @@ public void moveViewToX(float xValue) { */ public void moveViewTo(float xValue, float yValue, AxisDependency axis) { - float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); Runnable job = MoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f, getTransformer(axis), this); @@ -850,7 +869,7 @@ public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - float yInView = getDeltaY(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); @@ -872,7 +891,7 @@ public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, */ public void centerViewToY(float yValue, AxisDependency axis) { - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); + float valsInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); Runnable job = MoveViewJob.getInstance(mViewPortHandler, 0f, yValue + valsInView / 2f, getTransformer(axis), this); @@ -891,7 +910,7 @@ public void centerViewToY(float yValue, AxisDependency axis) { */ public void centerViewTo(float xValue, float yValue, AxisDependency axis) { - float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); Runnable job = MoveViewJob.getInstance(mViewPortHandler, @@ -917,7 +936,7 @@ public void centerViewToAnimated(float xValue, float yValue, AxisDependency axis MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - float yInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, @@ -980,12 +999,12 @@ public void resetViewPortOffsets() { /** CODE BELOW IS GETTERS AND SETTERS */ /** - * Returns the delta-y value (y-value range) of the specified axis. + * Returns the range of the specified axis. * * @param axis * @return */ - public float getDeltaY(AxisDependency axis) { + protected float getAxisRange(AxisDependency axis) { if (axis == AxisDependency.LEFT) return mAxisLeft.mAxisRange; else @@ -1011,6 +1030,7 @@ public OnDrawListener getDrawListener() { } protected float[] mGetPositionBuffer = new float[2]; + /** * Returns a recyclable MPPointF instance. * Returns the position (in pixels) the provided Entry has inside the chart @@ -1213,12 +1233,12 @@ public void setKeepPositionOnRotation(boolean keepPositionOnRotation) { * @return */ public MPPointD getValuesByTouchPoint(float x, float y, AxisDependency axis) { - MPPointD result = MPPointD.getInstance(0,0); - getValuesByTouchPoint(x,y,axis,result); + MPPointD result = MPPointD.getInstance(0, 0); + getValuesByTouchPoint(x, y, axis, result); return result; } - public void getValuesByTouchPoint(float x, float y, AxisDependency axis, MPPointD outputPoint){ + public void getValuesByTouchPoint(float x, float y, AxisDependency axis, MPPointD outputPoint) { getTransformer(axis).getValuesByTouchPoint(x, y, outputPoint); } @@ -1235,7 +1255,8 @@ public MPPointD getPixelsForValues(float x, float y, AxisDependency axis) { return getTransformer(axis).getPixelsForValues(x, y); } - MPPointD pointForGetYValueByTouchPoint = MPPointD.getInstance(0,0); + MPPointD pointForGetYValueByTouchPoint = MPPointD.getInstance(0, 0); + /** * Returns y value at the given touch position (must not necessarily be * a value contained in one of the datasets) @@ -1280,8 +1301,10 @@ public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float return null; } - /** buffer for storing lowest visible x point */ - protected MPPointD posForGetLowestVisibleX = MPPointD.getInstance(0,0); + /** + * buffer for storing lowest visible x point + */ + protected MPPointD posForGetLowestVisibleX = MPPointD.getInstance(0, 0); /** * Returns the lowest x-index (value on the x-axis) that is still visible on @@ -1297,8 +1320,10 @@ public float getLowestVisibleX() { return result; } - /** buffer for storing highest visible x point */ - protected MPPointD posForGetHighestVisibleX = MPPointD.getInstance(0,0); + /** + * buffer for storing highest visible x point + */ + protected MPPointD posForGetHighestVisibleX = MPPointD.getInstance(0, 0); /** * Returns the highest x-index (value on the x-axis) that is still visible @@ -1547,16 +1572,17 @@ public Paint getPaint(int which) { } protected float[] mOnSizeChangedBuffer = new float[2]; + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { // Saving current position of chart. - float[] pts = mOnSizeChangedBuffer; - pts[0] = pts[1] = 0; + mOnSizeChangedBuffer[0] = mOnSizeChangedBuffer[1] = 0; + if (mKeepPositionOnRotation) { - pts[0] = mViewPortHandler.contentLeft(); - pts[1] = mViewPortHandler.contentTop(); - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); + mOnSizeChangedBuffer[0] = mViewPortHandler.contentLeft(); + mOnSizeChangedBuffer[1] = mViewPortHandler.contentTop(); + getTransformer(AxisDependency.LEFT).pixelsToValue(mOnSizeChangedBuffer); } //Superclass transforms chart. @@ -1565,8 +1591,8 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (mKeepPositionOnRotation) { //Restoring old position of chart. - getTransformer(AxisDependency.LEFT).pointValuesToPixel(pts); - mViewPortHandler.centerViewPort(pts, this); + getTransformer(AxisDependency.LEFT).pointValuesToPixel(mOnSizeChangedBuffer); + mViewPortHandler.centerViewPort(mOnSizeChangedBuffer, this); } else { mViewPortHandler.refresh(mViewPortHandler.getMatrixTouch(), this, true); } 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 806717a620..49047da63f 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 @@ -248,20 +248,20 @@ public void setVisibleXRange(float minXRange, float maxXRange) { @Override public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { - float yScale = getDeltaY(axis) / maxYRange; + float yScale = getAxisRange(axis) / maxYRange; mViewPortHandler.setMinimumScaleX(yScale); } @Override public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) { - float yScale = getDeltaY(axis) / minYRange; + float yScale = getAxisRange(axis) / minYRange; mViewPortHandler.setMaximumScaleX(yScale); } @Override public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis) { - float minScale = getDeltaY(axis) / minYRange; - float maxScale = getDeltaY(axis) / maxYRange; + float minScale = getAxisRange(axis) / minYRange; + float maxScale = getAxisRange(axis) / maxYRange; mViewPortHandler.setMinMaxScaleX(minScale, maxScale); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java index 00b67df0fc..c39586ca87 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java @@ -18,11 +18,12 @@ public class ZoomJob extends ViewPortJob { private static ObjectPool pool; static { - pool = ObjectPool.create(1, new ZoomJob(null,0,0,0,0,null,null,null)); + pool = ObjectPool.create(1, new ZoomJob(null, 0, 0, 0, 0, null, null, null)); pool.setReplenishPercentage(0.5f); } - public static ZoomJob getInstance(ViewPortHandler viewPortHandler, float scaleX, float scaleY, float xValue, float yValue, Transformer trans, YAxis.AxisDependency axis, View v) { + public static ZoomJob getInstance(ViewPortHandler viewPortHandler, float scaleX, float scaleY, float xValue, float yValue, + Transformer trans, YAxis.AxisDependency axis, View v) { ZoomJob result = pool.get(); result.xValue = xValue; result.yValue = yValue; @@ -35,7 +36,7 @@ public static ZoomJob getInstance(ViewPortHandler viewPortHandler, float scaleX, return result; } - public static void recycleInstance(ZoomJob instance){ + public static void recycleInstance(ZoomJob instance) { pool.recycle(instance); } @@ -44,7 +45,8 @@ public static void recycleInstance(ZoomJob instance){ protected YAxis.AxisDependency axisDependency; - public ZoomJob(ViewPortHandler viewPortHandler, float scaleX, float scaleY, float xValue, float yValue, Transformer trans, YAxis.AxisDependency axis, View v) { + public ZoomJob(ViewPortHandler viewPortHandler, float scaleX, float scaleY, float xValue, float yValue, Transformer trans, + YAxis.AxisDependency axis, View v) { super(viewPortHandler, xValue, yValue, trans, v); this.scaleX = scaleX; @@ -53,6 +55,7 @@ public ZoomJob(ViewPortHandler viewPortHandler, float scaleX, float scaleY, floa } protected Matrix mRunMatrixBuffer = new Matrix(); + @Override public void run() { @@ -60,11 +63,11 @@ public void run() { mViewPortHandler.zoom(scaleX, scaleY, save); mViewPortHandler.refresh(save, view, false); - float valsInView = ((BarLineChartBase) view).getDeltaY(axisDependency) / mViewPortHandler.getScaleY(); - float xsInView = ((BarLineChartBase) view).getXAxis().mAxisRange / mViewPortHandler.getScaleX(); + float yValsInView = ((BarLineChartBase) view).getAxis(axisDependency).mAxisRange / mViewPortHandler.getScaleY(); + float xValsInView = ((BarLineChartBase) view).getXAxis().mAxisRange / mViewPortHandler.getScaleX(); - pts[0] = xValue - xsInView / 2f; - pts[1] = yValue + valsInView / 2f; + pts[0] = xValue - xValsInView / 2f; + pts[1] = yValue + yValsInView / 2f; mTrans.pointValuesToPixel(pts); @@ -79,6 +82,6 @@ public void run() { @Override protected ObjectPool.Poolable instantiate() { - return new ZoomJob(null,0,0,0,0,null,null,null); + return new ZoomJob(null, 0, 0, 0, 0, null, null, null); } } 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 9aa8109054..eedd190456 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 @@ -2,9 +2,7 @@ package com.github.mikephil.charting.utils; import android.graphics.Matrix; -import android.graphics.PointF; import android.graphics.RectF; -import android.util.Log; import android.view.View; /** @@ -198,11 +196,11 @@ public float getSmallestContentExtension() { public Matrix zoomIn(float x, float y) { Matrix save = new Matrix(); - zoomIn(x,y,save); + zoomIn(x, y, save); return save; } - public void zoomIn(float x, float y, Matrix outputMatrix){ + public void zoomIn(float x, float y, Matrix outputMatrix) { outputMatrix.reset(); outputMatrix.set(mMatrixTouch); outputMatrix.postScale(1.4f, 1.4f, x, y); @@ -215,11 +213,11 @@ public void zoomIn(float x, float y, Matrix outputMatrix){ public Matrix zoomOut(float x, float y) { Matrix save = new Matrix(); - zoomOut(x,y,save); + zoomOut(x, y, save); return save; } - public void zoomOut(float x, float y, Matrix outputMatrix){ + public void zoomOut(float x, float y, Matrix outputMatrix) { outputMatrix.reset(); outputMatrix.set(mMatrixTouch); outputMatrix.postScale(0.7f, 0.7f, x, y); @@ -235,11 +233,11 @@ public void zoomOut(float x, float y, Matrix outputMatrix){ public Matrix zoom(float scaleX, float scaleY) { Matrix save = new Matrix(); - zoom(scaleX,scaleY,save); + zoom(scaleX, scaleY, save); return save; } - public void zoom(float scaleX, float scaleY, Matrix outputMatrix){ + public void zoom(float scaleX, float scaleY, Matrix outputMatrix) { outputMatrix.reset(); outputMatrix.set(mMatrixTouch); outputMatrix.postScale(scaleX, scaleY); @@ -257,14 +255,14 @@ public void zoom(float scaleX, float scaleY, Matrix outputMatrix){ public Matrix zoom(float scaleX, float scaleY, float x, float y) { Matrix save = new Matrix(); - zoom(scaleX,scaleY,x,y,save); + zoom(scaleX, scaleY, x, y, save); return save; } - public void zoom(float scaleX, float scaleY, float x, float y, Matrix outputMatrix){ + public void zoom(float scaleX, float scaleY, float x, float y, Matrix outputMatrix) { outputMatrix.reset(); outputMatrix.set(mMatrixTouch); - outputMatrix.postScale(scaleX,scaleY,x,y); + outputMatrix.postScale(scaleX, scaleY, x, y); } /** @@ -277,14 +275,14 @@ public void zoom(float scaleX, float scaleY, float x, float y, Matrix outputMatr public Matrix setZoom(float scaleX, float scaleY) { Matrix save = new Matrix(); - setZoom(scaleX,scaleY,save); + setZoom(scaleX, scaleY, save); return save; } - public void setZoom(float scaleX, float scaleY, Matrix outputMatrix){ + public void setZoom(float scaleX, float scaleY, Matrix outputMatrix) { outputMatrix.reset(); outputMatrix.set(mMatrixTouch); - outputMatrix.setScale(scaleX,scaleY); + outputMatrix.setScale(scaleX, scaleY); } /** @@ -323,14 +321,14 @@ public Matrix fitScreen() { * Resets all zooming and dragging and makes the chart fit exactly it's * bounds. Output Matrix is available for those who wish to cache the object. */ - public void fitScreen(Matrix outputMatrix){ + public void fitScreen(Matrix outputMatrix) { mMinScaleX = 1f; mMinScaleY = 1f; outputMatrix.set(mMatrixTouch); float[] vals = valsBufferForFitScreen; - for(int i = 0 ; i < 9 ; i++){ + for (int i = 0; i < 9; i++) { vals[i] = 0; } @@ -364,7 +362,7 @@ public Matrix translate(final float[] transformedPts) { * @param transformedPts * @return */ - public void translate(final float[] transformedPts, Matrix outputMatrix){ + public void translate(final float[] transformedPts, Matrix outputMatrix) { outputMatrix.reset(); outputMatrix.set(mMatrixTouch); final float x = transformedPts[0] - offsetLeft(); @@ -373,6 +371,7 @@ public void translate(final float[] transformedPts, Matrix outputMatrix){ } protected Matrix mCenterViewPortMatrixBuffer = new Matrix(); + /** * Centers the viewport around the specified position (x-index and y-value) * in the chart. Centering the viewport outside the bounds of the chart is From 16738ef4809197fb84e73adf771e9fd7490a6c90 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sun, 3 Jul 2016 20:38:12 +0200 Subject: [PATCH 308/606] Import cleanup --- .../mikephil/charting/charts/BarChart.java | 2 +- .../charting/charts/BarLineChartBase.java | 44 ++----------------- .../mikephil/charting/charts/BubbleChart.java | 1 - .../charting/charts/CandleStickChart.java | 3 +- .../mikephil/charting/charts/Chart.java | 14 +++--- .../charting/charts/HorizontalBarChart.java | 3 +- .../mikephil/charting/charts/LineChart.java | 6 +-- .../mikephil/charting/charts/PieChart.java | 13 +++--- .../charting/charts/PieRadarChartBase.java | 6 +-- .../mikephil/charting/charts/RadarChart.java | 19 -------- .../charting/charts/ScatterChart.java | 4 ++ 11 files changed, 30 insertions(+), 85 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 f3d39b97f5..6f3692c2f3 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 @@ -118,7 +118,7 @@ public RectF getBarBounds(BarEntry e) { * @param e * @return */ - public void getBarBounds(BarEntry e, RectF outputRect){ + public void getBarBounds(BarEntry e, RectF outputRect) { RectF bounds = outputRect; 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 cc6cca64ff..a208e73837 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 @@ -511,25 +511,6 @@ public void calculateOffsets() { prepareValuePxMatrix(); } -// @Override -// protected float[] getMarkerPosition(Highlight high) { -// return new float[] { high.getXPx(), high.getYPx() }; -// -// int dataSetIndex = highlight.getDataSetIndex(); -// float xPos = e.getX(); -// float yPos = e.getY() * mAnimator.getPhaseY(); -// -// // position of the marker depends on selected value index and value -// float[] pts = new float[]{ -// xPos, yPos -// }; -// -// getTransformer(mData.getDataSetByIndex(dataSetIndex).getAxisDependency()) -// .pointValuesToPixel(pts); -// -// return pts; -// } - /** * draws the grid background */ @@ -1044,13 +1025,12 @@ public MPPointF getPosition(Entry e, AxisDependency axis) { if (e == null) return null; - float[] vals = mGetPositionBuffer; - vals[0] = e.getX(); - vals[1] = e.getY(); + mGetPositionBuffer[0] = e.getX(); + mGetPositionBuffer[1] = e.getY(); - getTransformer(axis).pointValuesToPixel(vals); + getTransformer(axis).pointValuesToPixel(mGetPositionBuffer); - return MPPointF.getInstance(vals[0], vals[1]); + return MPPointF.getInstance(mGetPositionBuffer[0], mGetPositionBuffer[1]); } /** @@ -1255,22 +1235,6 @@ public MPPointD getPixelsForValues(float x, float y, AxisDependency axis) { return getTransformer(axis).getPixelsForValues(x, y); } - MPPointD pointForGetYValueByTouchPoint = MPPointD.getInstance(0, 0); - - /** - * Returns y value at the given touch position (must not necessarily be - * a value contained in one of the datasets) - * - * @param x - * @param y - * @return - */ - public float getYValueByTouchPoint(float x, float y, AxisDependency axis) { - getValuesByTouchPoint(x, y, axis, pointForGetYValueByTouchPoint); - float result = (float) pointForGetYValueByTouchPoint.y; - return result; - } - /** * returns the Entry object displayed at the touched position of the chart * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java index 58532d2084..23dac5780f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java @@ -6,7 +6,6 @@ import com.github.mikephil.charting.data.BubbleData; import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; -import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; import com.github.mikephil.charting.renderer.BubbleChartRenderer; /** 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 029a2bb490..61dff4bb61 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 @@ -10,7 +10,7 @@ /** * Financial chart type that draws candle-sticks (OHCL chart). - * + * * @author Philipp Jahoda */ public class CandleStickChart extends BarLineChartBase implements CandleDataProvider { @@ -32,7 +32,6 @@ protected void init() { super.init(); mRenderer = new CandleStickChartRenderer(this, mAnimator, mViewPortHandler); - mXAxis.mAxisMinimum = -0.5f; } @Override 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 c7ae8a57ff..c4fc594cc0 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 @@ -308,8 +308,8 @@ public void setData(T data) { 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 (int i = 0; i < count; i++) { + set = (IDataSet) sets.get(i); if (set.needsFormatter() || set.getValueFormatter() == mDefaultFormatter) set.setValueFormatter(mDefaultFormatter); } @@ -486,7 +486,7 @@ protected void drawDescription(Canvas c) { protected Highlight[] mIndicesToHighlight; /** - * The maximum distance in screen pixels away from an entry causing it to highlight. + * The maximum distance in dp away from an entry causing it to highlight. */ protected float mMaxHighlightDistance = 0f; @@ -1100,9 +1100,9 @@ public void setDescription(String desc) { * @param y - ycoordinate */ public void setDescriptionPosition(float x, float y) { - if(mDescriptionPosition == null){ - mDescriptionPosition = MPPointF.getInstance(x,y); - }else { + if (mDescriptionPosition == null) { + mDescriptionPosition = MPPointF.getInstance(x, y); + } else { mDescriptionPosition.x = x; mDescriptionPosition.y = y; } @@ -1478,12 +1478,12 @@ public Highlighter getHighlighter() { * @param highlighter */ public void setHighlighter(ChartHighlighter highlighter) { - mHighlighter = highlighter; } /** * Returns a recyclable MPPointF instance. + * * @return */ @Override 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 49047da63f..e4ec309d9f 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 @@ -144,7 +144,7 @@ public void getBarBounds(BarEntry e, RectF outputRect) { RectF bounds = outputRect; IBarDataSet set = mData.getDataSetForEntry(e); - if (set == null){ + if (set == null) { outputRect.set(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE); return; } @@ -166,6 +166,7 @@ public void getBarBounds(BarEntry e, RectF outputRect) { } protected float[] mGetPositionBuffer = new float[2]; + /** * Returns a recyclable MPPointF instance. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java index c96926017f..aa7afc4c85 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java @@ -10,7 +10,7 @@ /** * Chart that draws lines, surfaces, circles, ... - * + * * @author Philipp Jahoda */ public class LineChart extends BarLineChartBase implements LineDataProvider { @@ -33,7 +33,7 @@ protected void init() { mRenderer = new LineChartRenderer(this, mAnimator, mViewPortHandler); } - + @Override public LineData getLineData() { return mData; @@ -42,7 +42,7 @@ public LineData getLineData() { @Override protected void onDetachedFromWindow() { // releases the bitmap in the renderer to avoid oom error - if(mRenderer != null && mRenderer instanceof LineChartRenderer) { + if (mRenderer != null && mRenderer instanceof LineChartRenderer) { ((LineChartRenderer) mRenderer).releaseBitmap(); } super.onDetachedFromWindow(); 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 5dfd5263ff..bad10dbbb4 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 @@ -4,7 +4,6 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.PointF; import android.graphics.RectF; import android.graphics.Typeface; import android.util.AttributeSet; @@ -210,17 +209,17 @@ private void calcAngles() { int entryCount = mData.getEntryCount(); - if(mDrawAngles.length != entryCount) { + if (mDrawAngles.length != entryCount) { mDrawAngles = new float[entryCount]; - }else{ - for(int i = 0 ; i < entryCount ; i++){ + } else { + for (int i = 0; i < entryCount; i++) { mDrawAngles[i] = 0; } } - if(mAbsoluteAngles.length != entryCount) { + if (mAbsoluteAngles.length != entryCount) { mAbsoluteAngles = new float[entryCount]; - }else{ - for(int i = 0 ; i < entryCount ; i++){ + } else { + for (int i = 0; i < entryCount; i++) { mAbsoluteAngles[i] = 0; } } 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 2cf3da0b66..637be47cab 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 @@ -6,7 +6,6 @@ import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.SuppressLint; import android.content.Context; -import android.graphics.PointF; import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; @@ -17,7 +16,6 @@ import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.PieRadarChartTouchListener; import com.github.mikephil.charting.utils.MPPointF; @@ -297,12 +295,12 @@ public float getAngleForPoint(float x, float y) { */ public MPPointF getPosition(MPPointF center, float dist, float angle) { - MPPointF p = MPPointF.getInstance(0,0); + MPPointF p = MPPointF.getInstance(0, 0); getPosition(center, dist, angle, p); return p; } - public void getPosition(MPPointF center, float dist, float angle, MPPointF outputPoint){ + public void getPosition(MPPointF center, float dist, float angle, MPPointF outputPoint) { outputPoint.x = (float) (center.x + dist * Math.cos(Math.toRadians(angle))); outputPoint.y = (float) (center.y + dist * Math.sin(Math.toRadians(angle))); } 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 ab953d79f9..1cc1bd6d60 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 @@ -4,15 +4,12 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.PointF; import android.graphics.RectF; import android.util.AttributeSet; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.RadarData; -import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.highlight.RadarHighlighter; import com.github.mikephil.charting.renderer.RadarChartRenderer; import com.github.mikephil.charting.renderer.XAxisRendererRadarChart; @@ -106,22 +103,6 @@ protected void calcMinMax() { mXAxis.calculate(0, mData.getMaxEntryCountSet().getEntryCount()); } -// @Override -// protected float[] getMarkerPosition(Highlight highlight) { -// return null; -// -//// float angle = getSliceAngle() * e.getX() * mAnimator.getPhaseX() + getRotationAngle(); -//// float val = e.getY() * getFactor() * mAnimator.getPhaseY(); -//// PointF c = getCenterOffsets(); -//// -//// PointF p = new PointF((float) (c.x + val * Math.cos(Math.toRadians(angle))), -//// (float) (c.y + val * Math.sin(Math.toRadians(angle)))); -//// -//// return new float[]{ -//// p.x, p.y -//// }; -// } - @Override public void notifyDataSetChanged() { if (mData == null) 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 cbaf0694b0..0f68457b55 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 @@ -42,6 +42,10 @@ public ScatterData getScatterData() { return mData; } + /** + * Predefined ScatterShapes that allow the specification of a shape a ScatterDataSet should be drawn with. + * If a ScatterShape is specified for a ScatterDataSet, the required renderer is set. + */ public enum ScatterShape { SQUARE("SQUARE"), CIRCLE("CIRCLE"), TRIANGLE("TRIANGLE"), CROSS("CROSS"), X("X"), CHEVRON_UP("CHEVRON_UP"), From 89104c28587bc3f67b375675a02fc3948b1904d0 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 7 Jul 2016 11:34:08 +0200 Subject: [PATCH 309/606] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fe395d5c34..03d657b560 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,14 @@ Donations 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). -## 3rd party bindings +## Xamarin Xamarin (by @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 2fa4a094e79d6a15d9df41de7491d00528a1e8e2 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 7 Jul 2016 11:36:01 +0200 Subject: [PATCH 310/606] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 03d657b560..518240d305 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ If you just want to be nice, you can check out my [**Amazon-Wishlist**]( https:/ ## Xamarin -Xamarin (by @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/). +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 From 9ccf58feab2c1e52dfa25beff4878763d12b38b9 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Thu, 7 Jul 2016 16:48:59 +0200 Subject: [PATCH 311/606] Fixes related to legend rendering (issue #1969) --- .../mpchartexample/PieChartActivity.java | 8 +- .../mpchartexample/ScatterChartActivity.java | 1 + .../mikephil/charting/components/Legend.java | 310 ++++++------------ .../charting/renderer/LegendRenderer.java | 80 ++--- .../github/mikephil/charting/utils/Utils.java | 8 +- 5 files changed, 157 insertions(+), 250 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java index b77bbd1d18..f514110d59 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -52,12 +52,9 @@ protected void onCreate(Bundle savedInstanceState) { mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - + mSeekBarX.setProgress(4); mSeekBarY.setProgress(10); - mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarY.setOnSeekBarChangeListener(this); - mChart = (PieChart) findViewById(R.id.chart1); mChart.setUsePercentValues(true); mChart.setDescription(""); @@ -95,6 +92,9 @@ protected void onCreate(Bundle savedInstanceState) { mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); // mChart.spin(2000, 0, 360); + mSeekBarX.setOnSeekBarChangeListener(this); + mSeekBarY.setOnSeekBarChangeListener(this); + Legend l = mChart.getLegend(); l.setPosition(LegendPosition.RIGHT_OF_CHART); l.setXEntrySpace(7f); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java index b288b162e9..fbcea85d2f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -74,6 +74,7 @@ protected void onCreate(Bundle savedInstanceState) { Legend l = mChart.getLegend(); l.setPosition(LegendPosition.RIGHT_OF_CHART); l.setTypeface(mTfLight); + l.setXOffset(5f); YAxis yl = mChart.getAxisLeft(); yl.setTypeface(mTfLight); 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 65824ac978..b12a39b394 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 @@ -15,13 +15,14 @@ * Class representing the legend of the chart. The legend will contain one entry * per color and DataSet. Multiple colors in one DataSet are grouped together. * The legend object is NOT available before setting data to the chart. - * + * * @author Philipp Jahoda */ public class Legend extends ComponentBase { /** - * This property is deprecated - Use `position`, `horizontalAlignment`, `verticalAlignment`, `orientation`, `drawInside`, `direction`. + * This property is deprecated - Use `position`, `horizontalAlignment`, `verticalAlignment`, `orientation`, `drawInside`, + * `direction`. */ public enum LegendPosition { RIGHT_OF_CHART, RIGHT_OF_CHART_CENTER, RIGHT_OF_CHART_INSIDE, @@ -35,18 +36,15 @@ public enum LegendForm { SQUARE, CIRCLE, LINE } - public enum LegendHorizontalAlignment - { + public enum LegendHorizontalAlignment { LEFT, CENTER, RIGHT } - public enum LegendVerticalAlignment - { + public enum LegendVerticalAlignment { TOP, CENTER, BOTTOM } - public enum LegendOrientation - { + public enum LegendOrientation { HORIZONTAL, VERTICAL } @@ -60,7 +58,9 @@ public enum LegendDirection { */ private int[] mColors; - /** the legend text array. a null label will start a group. */ + /** + * the legend text array. a null label will start a group. + */ private String[] mLabels; /** @@ -86,13 +86,19 @@ public enum LegendDirection { private LegendOrientation mOrientation = LegendOrientation.HORIZONTAL; private boolean mDrawInside = false; - /** the text direction for the legend */ + /** + * the text direction for the legend + */ private LegendDirection mDirection = LegendDirection.LEFT_TO_RIGHT; - /** the shape/form the legend colors are drawn in */ + /** + * the shape/form the legend colors are drawn in + */ private LegendForm mShape = LegendForm.SQUARE; - /** the size of the legend forms/shapes */ + /** + * the size of the legend forms/shapes + */ private float mFormSize = 8f; /** @@ -112,13 +118,19 @@ public enum LegendDirection { */ private float mFormToTextSpace = 5f; - /** the space that should be left between stacked forms */ + /** + * the space that should be left between stacked forms + */ private float mStackSpace = 3f; - /** the maximum relative size out of the whole chart view in percent */ + /** + * the maximum relative size out of the whole chart view in percent + */ private float mMaxSizePercent = 0.95f; - /** default constructor */ + /** + * default constructor + */ public Legend() { mFormSize = Utils.convertDpToPixel(8f); @@ -133,7 +145,7 @@ public Legend() { /** * Constructor. Provide colors and labels for the legend. - * + * * @param colors * @param labels */ @@ -155,7 +167,7 @@ public Legend(int[] colors, String[] labels) { /** * Constructor. Provide colors and labels for the legend. - * + * * @param colors * @param labels */ @@ -171,30 +183,32 @@ public Legend(List colors, List labels) { "colors array and labels array need to be of same size"); } - this.setComputedColors(colors); - this.setComputedLabels(labels); + this.mColors = Utils.convertIntegers(colors); + this.mLabels = Utils.convertStrings(labels); } /** * This method sets the automatically computed colors for the legend. Use setCustom(...) to set custom colors. + * * @param colors */ public void setComputedColors(List colors) { - if(mColors != null && colors.size() == mColors.length){ + if (mColors != null && colors.size() == mColors.length) { Utils.copyIntegers(colors, mColors); - }else { + } else { mColors = Utils.convertIntegers(colors); } } /** * 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()){ + if (mLabels != null && mLabels.length == labels.size()) { Utils.copyStrings(labels, mLabels); - }else { + } else { mLabels = Utils.convertStrings(labels); } } @@ -202,7 +216,7 @@ public void setComputedLabels(List labels) { /** * returns the maximum length in pixels across all legend labels + formsize * + formtotextspace - * + * * @param p the paint object used for rendering the text * @return */ @@ -226,7 +240,7 @@ public float getMaximumEntryWidth(Paint p) { /** * returns the maximum height in pixels across all legend labels - * + * * @param p the paint object used for rendering the text * @return */ @@ -250,7 +264,7 @@ public float getMaximumEntryHeight(Paint p) { /** * returns all the colors the legend uses - * + * * @return */ public int[] getColors() { @@ -259,7 +273,7 @@ public int[] getColors() { /** * returns all the labels the legend uses - * + * * @return */ public String[] getLabels() { @@ -268,7 +282,7 @@ public String[] getLabels() { /** * Returns the legend-label at the given index. - * + * * @param index * @return */ @@ -299,15 +313,15 @@ public String[] getExtraLabels() { * let the changes take effect) */ public void setExtra(List colors, List labels) { - if(mExtraColors != null && mExtraColors.length == colors.size()){ + if (mExtraColors != null && mExtraColors.length == colors.size()) { Utils.copyIntegers(colors, mExtraColors); - }else { + } else { this.mExtraColors = Utils.convertIntegers(colors); } - if(mExtraLabels != null && mExtraLabels.length == labels.size()){ + if (mExtraLabels != null && mExtraLabels.length == labels.size()) { Utils.copyStrings(labels, mExtraLabels); - }else { + } else { this.mExtraLabels = Utils.convertStrings(labels); } } @@ -360,8 +374,8 @@ public void setCustom(List colors, List labels) { "colors array and labels array need to be of same size"); } - this.setComputedColors(colors); - this.setComputedLabels(labels); + mColors = Utils.convertIntegers(colors); + mLabels = Utils.convertStrings(labels); mIsLegendCustom = true; } @@ -376,7 +390,7 @@ public void resetCustom() { /** * @return true if a custom legend labels and colors has been set default - * false (automatic legend) + * false (automatic legend) */ public boolean isLegendCustom() { return mIsLegendCustom; @@ -384,7 +398,7 @@ public boolean isLegendCustom() { /** * returns the position of the legend relative to the chart - * + * * @return */ public LegendPosition getPosition() { @@ -393,8 +407,7 @@ public LegendPosition getPosition() { && mHorizontalAlignment == LegendHorizontalAlignment.CENTER && mVerticalAlignment == LegendVerticalAlignment.CENTER) { return LegendPosition.PIECHART_CENTER; - } - else if (mOrientation == LegendOrientation.HORIZONTAL) { + } else if (mOrientation == LegendOrientation.HORIZONTAL) { if (mVerticalAlignment == LegendVerticalAlignment.TOP) return mHorizontalAlignment == LegendHorizontalAlignment.LEFT ? LegendPosition.ABOVE_CHART_LEFT @@ -407,8 +420,7 @@ else if (mOrientation == LegendOrientation.HORIZONTAL) { : (mHorizontalAlignment == LegendHorizontalAlignment.RIGHT ? LegendPosition.BELOW_CHART_RIGHT : LegendPosition.BELOW_CHART_CENTER); - } - else { + } else { if (mHorizontalAlignment == LegendHorizontalAlignment.LEFT) return mVerticalAlignment == LegendVerticalAlignment.TOP && mDrawInside ? LegendPosition.LEFT_OF_CHART_INSIDE @@ -426,7 +438,7 @@ else if (mOrientation == LegendOrientation.HORIZONTAL) { /** * sets the position of the legend relative to the whole chart - * + * * @param newValue */ public void setPosition(LegendPosition newValue) { @@ -579,7 +591,7 @@ public void setDirection(LegendDirection pos) { /** * returns the current form/shape that is set for the legend - * + * * @return */ public LegendForm getForm() { @@ -588,7 +600,7 @@ public LegendForm getForm() { /** * sets the form/shape of the legend forms - * + * * @param shape */ public void setForm(LegendForm shape) { @@ -598,7 +610,7 @@ public void setForm(LegendForm shape) { /** * sets the size in pixels of the legend forms, this is internally converted * in dp, default 8f - * + * * @param size */ public void setFormSize(float size) { @@ -607,7 +619,7 @@ public void setFormSize(float size) { /** * returns the size in dp of the legend forms - * + * * @return */ public float getFormSize() { @@ -617,7 +629,7 @@ public float getFormSize() { /** * returns the space between the legend entries on a horizontal axis in * pixels - * + * * @return */ public float getXEntrySpace() { @@ -627,7 +639,7 @@ public float getXEntrySpace() { /** * sets the space between the legend entries on a horizontal axis in pixels, * converts to dp internally - * + * * @param space */ public void setXEntrySpace(float space) { @@ -636,7 +648,7 @@ public void setXEntrySpace(float space) { /** * returns the space between the legend entries on a vertical axis in pixels - * + * * @return */ public float getYEntrySpace() { @@ -646,7 +658,7 @@ public float getYEntrySpace() { /** * sets the space between the legend entries on a vertical axis in pixels, * converts to dp internally - * + * * @param space */ public void setYEntrySpace(float space) { @@ -655,7 +667,7 @@ public void setYEntrySpace(float space) { /** * returns the space between the form and the actual label/text - * + * * @return */ public float getFormToTextSpace() { @@ -665,7 +677,7 @@ public float getFormToTextSpace() { /** * sets the space between the form and the actual label/text, converts to dp * internally - * + * * @param space */ public void setFormToTextSpace(float space) { @@ -674,7 +686,7 @@ public void setFormToTextSpace(float space) { /** * returns the space that is left out between stacked forms (with no label) - * + * * @return */ public float getStackSpace() { @@ -683,7 +695,7 @@ public float getStackSpace() { /** * sets the space that is left out between stacked forms (with no label) - * + * * @param space */ public void setStackSpace(float space) { @@ -691,73 +703,22 @@ public void setStackSpace(float space) { } /** - * calculates the full width the fully drawn legend will use in pixels - * - * @return + * the total width of the legend (needed width space) */ - public float getFullWidth(Paint labelpaint) { - - float width = 0f; - - for (int i = 0; i < mLabels.length; i++) { - - // grouped forms have null labels - if (mLabels[i] != null) { - - // make a step to the left - if (mColors[i] != ColorTemplate.COLOR_SKIP) - width += mFormSize + mFormToTextSpace; - - width += Utils.calcTextWidth(labelpaint, mLabels[i]); - - if (i < mLabels.length - 1) - width += mXEntrySpace; - } else { - width += mFormSize; - if (i < mLabels.length - 1) - width += mStackSpace; - } - } - - return width; - } + public float mNeededWidth = 0f; /** - * Calculates the full height of the drawn legend. - * - * @param labelpaint - * @return + * the total height of the legend (needed height space) */ - public float getFullHeight(Paint labelpaint) { - - float height = 0f; - - for (int i = 0; i < mLabels.length; i++) { - - // grouped forms have null labels - if (mLabels[i] != null) { - - height += Utils.calcTextHeight(labelpaint, mLabels[i]); - - if (i < mLabels.length - 1) - height += mYEntrySpace; - } - } - - return height; - } - - /** the total width of the legend (needed width space) */ - public float mNeededWidth = 0f; - - /** the total height of the legend (needed height space) */ public float mNeededHeight = 0f; public float mTextHeightMax = 0f; public float mTextWidthMax = 0f; - /** flag that indicates if word wrapping is enabled */ + /** + * flag that indicates if word wrapping is enabled + */ private boolean mWordWrapEnabled = false; /** @@ -766,7 +727,7 @@ public float getFullHeight(Paint labelpaint) { * wrapping a legend takes a toll on performance. / you may want to set * maxSizePercent when word wrapping, to set the point where the text wraps. * / default: false - * + * * @param enabled */ public void setWordWrapEnabled(boolean enabled) { @@ -776,7 +737,7 @@ public void setWordWrapEnabled(boolean enabled) { /** * If this is set, then word wrapping the legend is enabled. This means the * legend will not be cut off if too long. - * + * * @return */ public boolean isWordWrapEnabled() { @@ -790,7 +751,7 @@ public boolean isWordWrapEnabled() { * affects the height of the legend. / If the legend is the center of the * piechart, then this defines the size of the rectangular bounds out of the * size of the "hole". / default: 0.95f (95%) - * + * * @return */ public float getMaxSizePercent() { @@ -802,45 +763,34 @@ public float getMaxSizePercent() { * the legend is to the right/left of the chart, then this affects the width * of the legend. / If the legend is to the top/bottom of the chart, then * this affects the height of the legend. / default: 0.95f (95%) - * + * * @param maxSize */ public void setMaxSizePercent(float maxSize) { mMaxSizePercent = maxSize; } - private boolean isCalculatedLineSizesArrayListResized = true; - private FSize[] mCalculatedLabelSizes = new FSize[] {}; - private Boolean[] mCalculatedLabelBreakPoints = new Boolean[] {}; - private FSize[] mCalculatedLineSizes = new FSize[] {}; + private List mCalculatedLabelSizes = new ArrayList<>(16); + private List mCalculatedLabelBreakPoints = new ArrayList<>(16); + private List mCalculatedLineSizes = new ArrayList<>(16); - public FSize[] getCalculatedLabelSizes() { + public List getCalculatedLabelSizes() { return mCalculatedLabelSizes; } - public Boolean[] getCalculatedLabelBreakPoints() { + public List getCalculatedLabelBreakPoints() { return mCalculatedLabelBreakPoints; } - public FSize[] getCalculatedLineSizes() { - if(mCalculatedLineSizes == null || isCalculatedLineSizesArrayListResized){ - - mCalculatedLineSizes = calculatedLineSizesForCalculateDimensions - .toArray(new FSize[calculatedLineSizesForCalculateDimensions.size()]); - - isCalculatedLineSizesArrayListResized = false; - - } + public List getCalculatedLineSizes() { return mCalculatedLineSizes; } - protected Paint.FontMetrics fontMetricsForCalculateDimensions = new Paint.FontMetrics(); - protected ArrayList calculatedLineSizesForCalculateDimensions = new ArrayList<>(); /** * Calculates the dimensions of the Legend. This includes the maximum width * and height of a single entry, as well as the total width and height of * the Legend. - * + * * @param labelpaint */ public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandler) { @@ -852,7 +802,7 @@ public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandle case VERTICAL: { float maxWidth = 0f, maxHeight = 0f, width = 0f; - float labelLineHeight = Utils.getLineHeight(labelpaint, fontMetricsForCalculateDimensions); + float labelLineHeight = Utils.getLineHeight(labelpaint); final int count = mLabels.length; boolean wasStacked = false; @@ -886,8 +836,7 @@ else if (wasStacked) { if (i < count - 1) maxHeight += labelLineHeight + mYEntrySpace; - } - else { + } else { wasStacked = true; width += mFormSize; if (i < count - 1) @@ -905,81 +854,44 @@ else if (wasStacked) { case HORIZONTAL: { int labelCount = mLabels.length; - float labelLineHeight = Utils.getLineHeight(labelpaint, fontMetricsForCalculateDimensions); - float labelLineSpacing = Utils.getLineSpacing(labelpaint, fontMetricsForCalculateDimensions) + mYEntrySpace; + float labelLineHeight = Utils.getLineHeight(labelpaint); + float labelLineSpacing = Utils.getLineSpacing(labelpaint) + mYEntrySpace; float contentWidth = viewPortHandler.contentWidth() * mMaxSizePercent; - // Prepare arrays for calculated layout - - //ArrayList calculatedLabelBreakPoints = new ArrayList(labelCount); - - if(mCalculatedLabelSizes.length != labelCount){ - FSize[] temp = new FSize[labelCount]; - int count = mCalculatedLabelSizes.length; - for(int i = 0 ; i < count && i < labelCount ; i++){ - temp[i] = mCalculatedLabelSizes[i]; - } - while(count > labelCount){ - count--; - FSize.recycleInstance(mCalculatedLabelSizes[count]); - } - mCalculatedLabelSizes = temp; - } - int calculatedLabelSizesIndex = 0; - - if(mCalculatedLabelBreakPoints.length != labelCount){ - mCalculatedLabelBreakPoints = new Boolean[labelCount]; - } - int calculatedLabelBreakIndex = 0; - - ArrayList calculatedLineSizes = calculatedLineSizesForCalculateDimensions; - FSize.recycleInstances(calculatedLineSizes); - calculatedLineSizes.clear(); - // Start calculating layout float maxLineWidth = 0.f; float currentLineWidth = 0.f; float requiredWidth = 0.f; int stackedStartIndex = -1; + mCalculatedLabelBreakPoints.clear(); + mCalculatedLabelSizes.clear(); + mCalculatedLineSizes.clear(); + for (int i = 0; i < labelCount; i++) { boolean drawingForm = mColors[i] != ColorTemplate.COLOR_SKIP; - mCalculatedLabelBreakPoints[calculatedLabelBreakIndex] = false; - calculatedLabelBreakIndex++; + mCalculatedLabelBreakPoints.add(false); if (stackedStartIndex == -1) { // we are not stacking, so required width is for this label // only requiredWidth = 0.f; - } - else { + } else { // add the spacing appropriate for stacked labels/forms requiredWidth += mStackSpace; } // grouped forms have null labels if (mLabels[i] != null) { - if(mCalculatedLabelSizes[calculatedLabelSizesIndex] == null){ - mCalculatedLabelSizes[calculatedLabelSizesIndex] = Utils.calcTextSize(labelpaint, mLabels[i]); - }else{ - Utils.calcTextSize(labelpaint, mLabels[i], mCalculatedLabelSizes[calculatedLabelSizesIndex]); - } - FSize labelSize = mCalculatedLabelSizes[calculatedLabelSizesIndex]; - calculatedLabelSizesIndex++; + + mCalculatedLabelSizes.add(Utils.calcTextSize(labelpaint, mLabels[i])); requiredWidth += drawingForm ? mFormToTextSpace + mFormSize : 0.f; - requiredWidth += labelSize.width; - } - else { + requiredWidth += mCalculatedLabelSizes.get(i).width; + } else { - if(mCalculatedLabelSizes[calculatedLabelSizesIndex] == null){ - mCalculatedLabelSizes[calculatedLabelSizesIndex] = FSize.getInstance(0.f, 0.f); - }else{ - mCalculatedLabelSizes[calculatedLabelSizesIndex].width = 0.f; - mCalculatedLabelSizes[calculatedLabelSizesIndex].height = 0.f; - } - calculatedLabelSizesIndex++; + mCalculatedLabelSizes.add(FSize.getInstance(0.f, 0.f)); requiredWidth += drawingForm ? mFormSize : 0.f; if (stackedStartIndex == -1) { @@ -1000,23 +912,22 @@ else if (wasStacked) { requiredSpacing + requiredWidth)) { // Expand current line currentLineWidth += requiredSpacing + requiredWidth; - } - else { // It doesn't fit, we need to wrap a line + } else { // It doesn't fit, we need to wrap a line // Add current line size to array - calculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight)); + mCalculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight)); maxLineWidth = Math.max(maxLineWidth, currentLineWidth); // Start a new line - mCalculatedLabelBreakPoints[ + mCalculatedLabelBreakPoints.set( stackedStartIndex > -1 ? stackedStartIndex - : i] = true; + : i, true); currentLineWidth = requiredWidth; } if (i == labelCount - 1) { // Add last line size to array - calculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight)); + mCalculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight)); maxLineWidth = Math.max(maxLineWidth, currentLineWidth); } } @@ -1024,24 +935,19 @@ else if (wasStacked) { stackedStartIndex = mLabels[i] != null ? -1 : stackedStartIndex; } - if(calculatedLineSizes.size() != mCalculatedLineSizes.length) { - isCalculatedLineSizesArrayListResized = true; - }else{ - for(int i = 0 ; i < mCalculatedLineSizes.length ; i++){ - mCalculatedLineSizes[i] = calculatedLineSizes.get(i); - } - } - mNeededWidth = maxLineWidth; mNeededHeight = labelLineHeight - * (float) (mCalculatedLineSizes.length) + * (float) (mCalculatedLineSizes.size()) + labelLineSpacing * - (float) (mCalculatedLineSizes.length == 0 + (float) (mCalculatedLineSizes.size() == 0 ? 0 - : (mCalculatedLineSizes.length - 1)); + : (mCalculatedLineSizes.size() - 1)); break; } } + + mNeededHeight += mYOffset; + mNeededWidth += mXOffset; } } 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 fb8937a303..f621afcf35 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 @@ -70,8 +70,10 @@ public Paint getFormPaint() { return mLegendFormPaint; } - protected ArrayList labelsForComputeLegend = new ArrayList<>(16); - protected ArrayList colorsForComputeLegend = new ArrayList<>(16); + + protected List computedLabels = new ArrayList<>(16); + protected List computedColors = new ArrayList<>(16); + /** * Prepares the legend and calculates all needed forms, labels and colors. * @@ -81,11 +83,8 @@ public void computeLegend(ChartData data) { if (!mLegend.isLegendCustom()) { - ArrayList labels = labelsForComputeLegend; - ArrayList colors = colorsForComputeLegend; - - labels.clear(); - colors.clear(); + computedLabels.clear(); + computedColors.clear(); // loop for building up the colors and labels used in the legend for (int i = 0; i < data.getDataSetCount(); i++) { @@ -103,14 +102,14 @@ public void computeLegend(ChartData data) { for (int j = 0; j < clrs.size() && j < bds.getStackSize(); j++) { - labels.add(sLabels[j % sLabels.length]); - colors.add(clrs.get(j)); + computedLabels.add(sLabels[j % sLabels.length]); + computedColors.add(clrs.get(j)); } if (bds.getLabel() != null) { // add the legend description label - colors.add(ColorTemplate.COLOR_SKIP); - labels.add(bds.getLabel()); + computedColors.add(ColorTemplate.COLOR_SKIP); + computedLabels.add(bds.getLabel()); } } else if (dataSet instanceof IPieDataSet) { @@ -119,27 +118,27 @@ public void computeLegend(ChartData data) { for (int j = 0; j < clrs.size() && j < entryCount; j++) { - labels.add(pds.getEntryForIndex(j).getLabel()); - colors.add(clrs.get(j)); + computedLabels.add(pds.getEntryForIndex(j).getLabel()); + computedColors.add(clrs.get(j)); } if (pds.getLabel() != null) { // add the legend description label - colors.add(ColorTemplate.COLOR_SKIP); - labels.add(pds.getLabel()); + computedColors.add(ColorTemplate.COLOR_SKIP); + computedLabels.add(pds.getLabel()); } } else if (dataSet instanceof ICandleDataSet && ((ICandleDataSet) dataSet).getDecreasingColor() != ColorTemplate.COLOR_NONE) { int decreasingColor = ((ICandleDataSet) dataSet).getDecreasingColor(); - colors.add(decreasingColor); + computedColors.add(decreasingColor); int increasingColor = ((ICandleDataSet) dataSet).getIncreasingColor(); - colors.add(increasingColor); + computedColors.add(increasingColor); - labels.add(null); - labels.add(dataSet.getLabel()); + computedLabels.add(null); + computedLabels.add(dataSet.getLabel()); } else { // all others @@ -148,26 +147,26 @@ public void computeLegend(ChartData data) { // if multiple colors are set for a DataSet, group them if (j < clrs.size() - 1 && j < entryCount - 1) { - labels.add(null); + computedLabels.add(null); } else { // add label to the last entry String label = data.getDataSetByIndex(i).getLabel(); - labels.add(label); + computedLabels.add(label); } - colors.add(clrs.get(j)); + computedColors.add(clrs.get(j)); } } } if (mLegend.getExtraColors() != null && mLegend.getExtraLabels() != null) { for (int color : mLegend.getExtraColors()) - colors.add(color); - Collections.addAll(labels, mLegend.getExtraLabels()); + computedColors.add(color); + Collections.addAll(computedLabels, mLegend.getExtraLabels()); } - mLegend.setComputedColors(colors); - mLegend.setComputedLabels(labels); + mLegend.setComputedColors(computedColors); + mLegend.setComputedLabels(computedLabels); } Typeface tf = mLegend.getTypeface(); @@ -182,7 +181,8 @@ public void computeLegend(ChartData data) { mLegend.calculateDimensions(mLegendLabelPaint, mViewPortHandler); } - protected Paint.FontMetrics fontMetricsForRenderLegent = new Paint.FontMetrics(); + protected Paint.FontMetrics legendFontMetrics = new Paint.FontMetrics(); + public void renderLegend(Canvas c) { if (!mLegend.isEnabled()) @@ -196,8 +196,8 @@ public void renderLegend(Canvas c) { mLegendLabelPaint.setTextSize(mLegend.getTextSize()); mLegendLabelPaint.setColor(mLegend.getTextColor()); - float labelLineHeight = Utils.getLineHeight(mLegendLabelPaint, fontMetricsForRenderLegent); - float labelLineSpacing = Utils.getLineSpacing(mLegendLabelPaint, fontMetricsForRenderLegent) + mLegend.getYEntrySpace(); + float labelLineHeight = Utils.getLineHeight(mLegendLabelPaint, legendFontMetrics); + float labelLineSpacing = Utils.getLineSpacing(mLegendLabelPaint, legendFontMetrics) + mLegend.getYEntrySpace(); float formYOffset = labelLineHeight - Utils.calcTextHeight(mLegendLabelPaint, "ABC") / 2.f; String[] labels = mLegend.getLabels(); @@ -269,11 +269,11 @@ public void renderLegend(Canvas c) { switch (orientation) { case HORIZONTAL: { - FSize[] calculatedLineSizes = mLegend.getCalculatedLineSizes(); - FSize[] calculatedLabelSizes = mLegend.getCalculatedLabelSizes(); - Boolean[] calculatedLabelBreakPoints = mLegend.getCalculatedLabelBreakPoints(); + List calculatedLineSizes = mLegend.getCalculatedLineSizes(); + List calculatedLabelSizes = mLegend.getCalculatedLabelSizes(); + List calculatedLabelBreakPoints = mLegend.getCalculatedLabelBreakPoints(); - float posX = originPosX; + float posX = originPosX + xoffset; float posY = 0.f; switch (verticalAlignment) { @@ -293,17 +293,17 @@ public void renderLegend(Canvas c) { int lineIndex = 0; for (int i = 0, count = labels.length; i < count; i++) { - if (i < calculatedLabelBreakPoints.length && calculatedLabelBreakPoints[i]) { + if (i < calculatedLabelBreakPoints.size() && calculatedLabelBreakPoints.get(i)) { posX = originPosX; posY += labelLineHeight + labelLineSpacing; } if (posX == originPosX && horizontalAlignment == Legend.LegendHorizontalAlignment.CENTER && - lineIndex < calculatedLineSizes.length) { + lineIndex < calculatedLineSizes.size()) { posX += (direction == Legend.LegendDirection.RIGHT_TO_LEFT - ? calculatedLineSizes[lineIndex].width - : -calculatedLineSizes[lineIndex].width) / 2.f; + ? calculatedLineSizes.get(lineIndex).width + : -calculatedLineSizes.get(lineIndex).width) / 2.f; lineIndex++; } @@ -326,12 +326,12 @@ public void renderLegend(Canvas c) { formToTextSpace; if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - posX -= calculatedLabelSizes[i].width; + posX -= calculatedLabelSizes.get(i).width; drawLabel(c, posX, posY + labelLineHeight, labels[i]); if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - posX += calculatedLabelSizes[i].width; + posX += calculatedLabelSizes.get(i).width; posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -xEntrySpace : xEntrySpace; } else @@ -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; + float posX = originPosX + xoffset; if (drawingForm) { if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) 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 0799679114..bee377dcf9 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 @@ -163,9 +163,10 @@ public static int calcTextHeight(Paint paint, String demoText) { return r.height(); } + private static Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); + public static float getLineHeight(Paint paint) { - Paint.FontMetrics metrics = new Paint.FontMetrics(); - return getLineHeight(paint, metrics); + return getLineHeight(paint, mFontMetrics); } public static float getLineHeight(Paint paint, Paint.FontMetrics fontMetrics){ @@ -174,8 +175,7 @@ public static float getLineHeight(Paint paint, Paint.FontMetrics fontMetrics){ } public static float getLineSpacing(Paint paint) { - Paint.FontMetrics metrics = new Paint.FontMetrics(); - return getLineSpacing(paint, metrics); + return getLineSpacing(paint, mFontMetrics); } public static float getLineSpacing(Paint paint, Paint.FontMetrics fontMetrics){ From 8c67ec19f73b01c059f65c6631356680717ac693 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Fri, 8 Jul 2016 19:15:52 +0200 Subject: [PATCH 312/606] Fix #1978 --- .../charting/renderer/BarChartRenderer.java | 4 ++-- .../BarLineScatterCandleBubbleRenderer.java | 17 +++++++++++++++-- .../charting/renderer/BubbleChartRenderer.java | 4 ++-- .../renderer/CandleStickChartRenderer.java | 4 ++-- .../renderer/HorizontalBarChartRenderer.java | 2 +- .../charting/renderer/LineChartRenderer.java | 4 ++-- .../charting/renderer/RadarChartRenderer.java | 5 ++--- .../charting/renderer/ScatterChartRenderer.java | 2 +- 8 files changed, 27 insertions(+), 15 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 cffcc1c3da..3260d4c470 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 @@ -74,7 +74,7 @@ public void drawData(Canvas c) { IBarDataSet set = barData.getDataSetByIndex(i); - if (set.isVisible() && set.getEntryCount() > 0) { + if (set.isVisible()) { drawDataSet(c, set, i); } } @@ -195,7 +195,7 @@ public void drawValues(Canvas c) { IBarDataSet dataSet = dataSets.get(i); - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) + if (!shouldDrawValues(dataSet)) continue; // apply the text-styling defined by the DataSet 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 aa400caa61..8f2447d960 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 @@ -5,6 +5,7 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.utils.ViewPortHandler; /** @@ -12,13 +13,25 @@ */ public abstract class BarLineScatterCandleBubbleRenderer extends DataRenderer { - /** buffer for storing the current minimum and maximum visible x */ + /** + * buffer for storing the current minimum and maximum visible x + */ protected XBounds mXBounds = new XBounds(); public BarLineScatterCandleBubbleRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { super(animator, viewPortHandler); } + /** + * Returns true if the DataSet values should be drawn, false if not. + * + * @param set + * @return + */ + protected boolean shouldDrawValues(IDataSet set) { + return set.isVisible() && set.isDrawValuesEnabled(); + } + /** * Checks if the provided entry object is in bounds for drawing considering the current animation phase. * @@ -66,7 +79,7 @@ protected class XBounds { * @param chart * @param dataSet */ - public void set(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet){ + public void set(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet) { float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); float low = chart.getLowestVisibleX(); 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 6ff540cc7c..458a46f884 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 @@ -52,7 +52,7 @@ public void drawData(Canvas c) { for (int i = 0; i < setCount; i++) { set = dataSets.get(i); - if (set.isVisible() && set.getEntryCount() > 0) + if (set.isVisible()) drawDataSet(c, set); } } @@ -133,7 +133,7 @@ public void drawValues(Canvas c) { IBubbleDataSet dataSet = dataSets.get(i); - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) + if (!shouldDrawValues(dataSet)) 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 2aa11482f0..734b4301e5 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 @@ -50,7 +50,7 @@ public void drawData(Canvas c) { for (int i = 0; i < setCount; i++) { set = dataSets.get(i); - if (set.isVisible() && set.getEntryCount() > 0) + if (set.isVisible()) drawDataSet(c, set); } } @@ -266,7 +266,7 @@ public void drawValues(Canvas c) { ICandleDataSet dataSet = dataSets.get(i); - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) + if (!shouldDrawValues(dataSet)) continue; // apply the text-styling defined by the DataSet 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 66f3e96539..2162a28b06 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 @@ -116,7 +116,7 @@ public void drawValues(Canvas c) { IBarDataSet dataSet = dataSets.get(i); - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) + if (!shouldDrawValues(dataSet)) continue; boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); 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 aac9440111..48c7a4be3c 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 @@ -94,7 +94,7 @@ public void drawData(Canvas c) { for (int i = 0; i < setCount; i++) { set = lineData.getDataSets().get(i); - if (set.isVisible() && set.getEntryCount() > 0) + if (set.isVisible()) drawDataSet(c, set); } @@ -519,7 +519,7 @@ public void drawValues(Canvas c) { ILineDataSet dataSet = dataSets.get(i); - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) + if (!shouldDrawValues(dataSet)) continue; // apply the text-styling defined by the DataSet 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 c346c6f6a3..4d0a8d9b0f 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 @@ -5,7 +5,6 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; -import android.graphics.PointF; import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; @@ -70,7 +69,7 @@ public void drawData(Canvas c) { for(int i = 0 ; i < setCount ; i++){ set = dataSets.get(i); - if (set.isVisible() && set.getEntryCount() > 0) { + if (set.isVisible()) { drawDataSet(c, set, mostEntries); } } @@ -174,7 +173,7 @@ public void drawValues(Canvas c) { IRadarDataSet dataSet = mChart.getData().getDataSetByIndex(i); - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) + if (!shouldDrawValues(dataSet)) 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 1d45c4974d..484bcba551 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 @@ -94,7 +94,7 @@ public void drawValues(Canvas c) { IScatterDataSet dataSet = dataSets.get(i); - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) + if (!shouldDrawValues(dataSet)) continue; // apply the text-styling defined by the DataSet From d7e659a1fac34f1d187d2ac259a415302ba2cadf Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 9 Jul 2016 15:42:17 +0200 Subject: [PATCH 313/606] Change max-highlight-distance default to 500dp --- .../main/java/com/github/mikephil/charting/charts/Chart.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c4fc594cc0..f6596aaccc 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 @@ -229,7 +229,7 @@ public void onAnimationUpdate(ValueAnimator animation) { // initialize the utils Utils.init(getContext()); - mMaxHighlightDistance = Utils.convertDpToPixel(100f); + mMaxHighlightDistance = Utils.convertDpToPixel(500f); mLegend = new Legend(); From 4d5ce149dd6cbb9c72975d479588c7d7d075d867 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sat, 16 Jul 2016 15:17:54 +0200 Subject: [PATCH 314/606] Ignore ds_store files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 31581642ab..a340b1d6b3 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,5 @@ build/ # maven target/ + +.DS_Store From 37c4eef38eb707e359dc5cec6383007fe9bed1a5 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sat, 16 Jul 2016 15:41:37 +0200 Subject: [PATCH 315/606] Update build tools --- MPChartExample/build.gradle | 2 +- MPChartLib/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 9605392372..c0d4745dcc 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'realm-android' android { compileSdkVersion 23 - buildToolsVersion '23.0.2' + buildToolsVersion '23.0.3' defaultConfig { minSdkVersion 16 targetSdkVersion 23 diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index f8f8455805..b3e89b84fa 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -5,7 +5,7 @@ apply plugin: 'maven' android { compileSdkVersion 23 - buildToolsVersion '23.0.2' + buildToolsVersion '23.0.3' // resourcePrefix 'mpcht' defaultConfig { minSdkVersion 9 From ef575decbb572a71b8055d8742172be868826d78 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 16 Jul 2016 15:43:26 +0200 Subject: [PATCH 316/606] Remove old screenshot --- screenshots/simpledesign_barchart1.png | Bin 179237 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 screenshots/simpledesign_barchart1.png diff --git a/screenshots/simpledesign_barchart1.png b/screenshots/simpledesign_barchart1.png deleted file mode 100644 index e1c3f14155cc6c6a9b337a8e709a537efbb9b2aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 179237 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െ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 Date: Sat, 16 Jul 2016 22:51:08 +0200 Subject: [PATCH 317/606] Create new XYMarkerView (#2029) --- .../mpchartexample/custom/XYMarkerView.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java new file mode 100644 index 0000000000..772927b02c --- /dev/null +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java @@ -0,0 +1,7 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +/** + * Created by philipp on 16/07/16. + */ +public class XYMarkerView { +} From 3ea1db57abb08780d6c0182480c3eda5a09050a9 Mon Sep 17 00:00:00 2001 From: Philipp Jahoda Date: Sat, 16 Jul 2016 22:53:01 +0200 Subject: [PATCH 318/606] Create example for XYMarkerView (#2029) --- .../mpchartexample/BarChartActivity.java | 7 ++- .../custom/DayAxisValueFormatter.java | 2 +- .../mpchartexample/custom/XYMarkerView.java | 53 ++++++++++++++++++- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java index eb17690354..b51735f1e6 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -37,6 +37,7 @@ import com.github.mikephil.charting.utils.MPPointF; import com.xxmassdeveloper.mpchartexample.custom.DayAxisValueFormatter; import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; +import com.xxmassdeveloper.mpchartexample.custom.XYMarkerView; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -79,13 +80,15 @@ protected void onCreate(Bundle savedInstanceState) { mChart.setDrawGridBackground(false); // mChart.setDrawYLabels(false); + AxisValueFormatter xAxisFormatter = new DayAxisValueFormatter(mChart); + XAxis xAxis = mChart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); xAxis.setTypeface(mTfLight); xAxis.setDrawGridLines(false); xAxis.setGranularity(1f); // only intervals of 1 day xAxis.setLabelCount(7); - xAxis.setValueFormatter(new DayAxisValueFormatter(mChart)); + xAxis.setValueFormatter(xAxisFormatter); AxisValueFormatter custom = new MyAxisValueFormatter(); @@ -116,6 +119,8 @@ protected void onCreate(Bundle savedInstanceState) { // l.setCustom(ColorTemplate.VORDIPLOM_COLORS, new String[] { "abc", // "def", "ghj", "ikl", "mno" }); + mChart.setMarkerView(new XYMarkerView(this, xAxisFormatter)); + setData(12, 50); // setting data diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java index 8e906c4921..d989104e48 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -32,7 +32,7 @@ public String getFormattedValue(float value, AxisBase axis) { String monthName = mMonths[month % mMonths.length]; String yearName = String.valueOf(year); - if (chart.getVisibleXRange() > 30 * axis.getLabelCount()) { + if (chart.getVisibleXRange() > 30 * 6) { return monthName + " " + yearName; } else { diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java index 772927b02c..6bfa313c77 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java +++ b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java @@ -1,7 +1,56 @@ + package com.xxmassdeveloper.mpchartexample.custom; +import android.content.Context; +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.highlight.Highlight; +import com.github.mikephil.charting.utils.Utils; +import com.xxmassdeveloper.mpchartexample.R; + +import java.text.DecimalFormat; + /** - * Created by philipp on 16/07/16. + * Custom implementation of the MarkerView. + * + * @author Philipp Jahoda */ -public class XYMarkerView { +public class XYMarkerView extends MarkerView { + + private TextView tvContent; + private AxisValueFormatter xAxisValueFormatter; + + private DecimalFormat format; + + public XYMarkerView(Context context, AxisValueFormatter xAxisValueFormatter) { + super(context, R.layout.custom_marker_view); + + this.xAxisValueFormatter = xAxisValueFormatter; + tvContent = (TextView) findViewById(R.id.tvContent); + format = new DecimalFormat("###.0"); + } + + // callbacks everytime 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())); + } + + @Override + public int getXOffset(float xpos) { + // this will center the marker-view horizontally + return -(getWidth() / 2); + } + + @Override + public int getYOffset(float ypos) { + // this will cause the marker-view to be above the selected value + return -getHeight(); + } } From a91de26a5291471d9e705d0836771636bc2725b8 Mon Sep 17 00:00:00 2001 From: Martin Guillon Date: Sun, 7 Aug 2016 10:17:18 +0200 Subject: [PATCH 319/606] 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 320/606] 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 321/606] 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 322/606] 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 zcmaHTbzD>7`?jD6f`|d4q>4xh7)+$W072;(wV@(XQo?9PC>Tfx64D(TFhXiDLR55g zj8UUUY`_S|fB}1tzxVz9_5JexgMQ%b?0L?8pF6Jmx}FaPdRm<9eC$V#9O2Y{^uXxI z5thv(N0`~!jsw4Wmx~Jr{$cVo(zf7CS+oBao;tj+^ zs~l9jf4R%p9ePjjfo-lTR=KKfZsTxS<1%2O6{aj ztulj*Y96n+rk}A~(?@&Q%=G3sXC)uV>4%CChmoOTT-sROE)+0K#oJIe%o{nuW!8?lPysv29!(HjgSc*XvBY1uJzCJ#b!#Cr zcocYmFWs7U3S34if0i2O>+mRH>0Na*AEioO6R|gIXMYZ!>!naRWE-Cf2d;&t2dvhY zz0%FQVc#uKd$2rG1%a0s7e=Rhb}I}47OLB^fstvG!IPRXd@dZk(q0WH%W6b=;Km>C zQO81yt`yKY!CUQ>>$B|*jfDKiytOXT;mX>6Ot^DU_>P^@YTfK|=k1;+i+}}#habyg zUSN$S#Z04p$U|M-s`OXJP0>kB2PEf!gYbnFB&z8(vgo?na(UZ0tFhGW+H&%k_7SoicA%^5z{d))T{n1qBTls zW~`bSt2XUzbU@OfRL--^XX{pK#$HW-58wJN5@U|>r=HCLb}m*H-59vD;jn)X|LUXF zRfJ6p9|)U3=fCsHoeoxBnTeFGIj48G^^5HO-!A#dIz@WU!$|8VKY~^EP#bW-tc3bn zhraJWkG7H58r@m21Y-Vom16<2OUxD)7OOLJVH`5$fNKr^-P_yji3u2GpOAX`7ZtqU z0X%Ur-s`4E7|o1*)INQZ;mzB{$$zJ_0wWWac-`W;mT=HFz}4+#hDR{YKFC}(g0leJ*Shm`t>Sg`Z^PUWqS*-Zeygih9Q@ z36HJ7Y<%0u3wwZGyE_$fbh*wprmD;8PJ?nOd#+>vySro&w;ubI;+s!6EhgdgQbE7D z&o#h*!ojnC?Cj!hrREjiO%L4arjIA}jMloj(J9TrPdH0aPm5(rc#gS@)s*hf3Ny+9 zzZglrY5RotH$-nS4r!>vX->h=G8)9e;pIUMTi)B#?;328t)`Qgi5K&I9r0uuy{QRY`%gJ9D%?G>A*nh%3;VIvfaO&ZFZ&v~kxOy$QWC>9VY=0G z%43Ab;9&QFPFV?M6)G3Y@co`BZ8g8~uyNA46!j|`c#YZ;IRurz=gVM_H6`&zyTyw!EV1EJiHK%1Y56`SQQ$<63z1c1sP8 zmND-*y)bxF!q=LzIymT(t9WWXvfPrSlBwiVMlR3aYxG@zP9`+?eAhFSjKtMHHJP<$vzbY7jxdzL}Pqgy97EKh5A3+Mk`!<4SYtk@mCn| zo9|aPe(}3KXp9I2Zh)=mE-#@eN ze?_a2is%eAuBRe{Hp{ynrhP`1je9)|sgPJ| z-gIS#Wldxv#x@#H{n60-!B=|j^RFm=twtfi`osWU<1@04_|}=fouT9?^%v@Az6XzV zR13-G6@=h#i_CxmH5(&x-s3%GW~is5eWQ|Y-fV2qodYsZbRr5GdkdYBSbTl8H~an% ztD{xY#TikRXfUSbOjDY!!;XGZF6hgUrLb&gYd&-sSp& zGBpd}onB4b!|%w0SWIgCbN8-HJQ4*9`D3E4F!mPIL=ZkdbatbJZ@qZ*qv6H}U*2&A zvvnEwD-(;{;CX*uVTD9L4Lxu~KyLY!iJzX)c8Db3(BAj*_e5u40`Fz$K-ePO!M0=Z zt$BN^RR50xzAd3=P=z)>8VpXz%zJ!1;TxrR24&wU#bv5xrh+FE&KqmMjAhL)F7@SV z&2=kLI;nIDWh)j?J&uJZ>)=zmC!VuwHmXWy}kzYFI<^_|Hn61&sUH4Nw z8E;t{^+eCO{eb)+n+hB5cvErUhV6&jw8=?57)L1u=Jla7`KCy_TN9}i8cRq>f*OFd zKHb9yr7OXreOve5lZexEAhY_Q?lgtljlWA#{_n~zV~$r?cla?eohX7Mwv+HuYBR8i z*LZ#C0zUb!q!mUFaF#lub;XmK+!VHJsL(qzpwkZlUrNf&D6LDC|M&yaQ{2gk_rvtP zIfKeoe*lYFQ+-3{Qk$rOr6S{z*TF%VCd$ko+F5vvj~;y|&Bvtcmz&Ws!IkZtFks-N z`#2|Kry?%tAta`&VYRWNtYJ$LuXLp@!*a8c!vjA`XtrT2UP1NVkac)5!4jcD9W++( zf94=s{W3rUZXAiW{JlR18Lyjo#1~eu6cBcQ;aij?j`ie)Da(=I?E}`}+XuSOm^f4a z%H%+Le5r%I+)5-S;Z?`!a}sU{pZ)S@ZQ~B{Yvq$99J1l^R}yF8&?_|wH@!5uzQNls zw%4W)I{Jb^uT~0DU0;_H59F>|7ExjgaDB0UE5^A-jaf~l))lw#v@7V_LV2G;q@*xV z_muA%DnoenUa&coi<1}#;kuy!bJMkn$wMAPEv=#T!rpNTyRb<%%4UcV_28Kq(EcyBCgVp+IabNtIcgI0jA-N9*k>)C2 z6J$+%qLWNm4N(eIg8??YJJ<85(}O-5BB+1W)S|12Z1O(wl|1o-|7du;{}K{h=#O_K z_-gN1Q5|p)uRto?OxE!%Gl>(`SeVfwnFN|K*%6%pW&W7(qYL7VO$I)^)$$S7M=W5l zUQwM}9okV7^tl0jYlFcRtXUbcLv#6ANCK%l`_}=X-^E0NOH70I2uO@{37g_KGF20St)05Cs zok6M+OZPR3cJ%lbt+^|u?}T_2rAdw7$R#!VjKkj{>Bl#lhx*=2{hXGA>E95hkG0cj z2dt|;ll?l$};3;Q%Ce8)`Itw((IvdcuK8eqXO-x>GS2GVZIgZ2ePi z0`8{*JLc7wHT5Yhimw1EtO_#t`4~Ikj4y$;jm+vk%ZYrhOI=IzSSxVMV}I@F>XD9u-!=6+$*4O{YnvTBZ6MSeUM8uT0=8z#rt%MobqZr= z(aB~%=rVM&1+Ms)jZkOIKk`95jRjiT{aRDR$#Qs}FJAcgMQDpZn}?ck%k;1#%K?dUMK;bYc;z$Nti3R}?Q^ z?HAwCgY~J1+xS}0GhkA%zN(Pp_?x@jT3Ri5t$s7&%v*@n4s($9`|BO`?|On?usNeoCZS~160YiLX)o zcT(hMCW#C_knLpZ6F)uL%Qelc2NcTGg0AjS0a;nHrQy!qi4F72&c8i{4U_MDjjeu; zM&Q|@Ay470sfj5nyDBpvt;D#d7OOi9g0u8cYA;vf{G*|y8a=|WymCKRYS=Qn9zv`s zj-5^LzGsB$?(bX$<}J%I_H-F1lQSkU#HanYx^jhOi?0+-Eq+XpnkoHGmbjYr>#Ez` zat`ZVH!I%Bb(uy;Mh3mq+3*xiXrmV2jNJr+=nhxHmv){haIA@+VxUG2$7^bwS&UOD z?-M7C8PdPn2{+!cJ0Z$`>R2t&W8}Pns_8*5=h9w1Yt`*@@)sm1oeaMw&`9wI4&`5G z`&lZn+Fep*$1OyOYNnS!h4u5BgFcs&y)Nf`HnY@k9expyebuG43KohdL{qVeAsZ<9 zgg0cCLZ5=Ytk*Z8O~d6LZyMg4K}}Dqm>F3V2ckU&H1UxZB$34LjQTOPsLv-_6&Jd)bvHn`GpvtRqe(c^lS~=LlB~cPzBYLKExm z;M!gX^(f|(*H(;Gc1-Uea}%+FYD;LZr1K3wmXoJYZOz5fS$p|(iK6R+Zl2y|GwAS* zdc}QQiRl2O<7b1=AT@0@xPW|zp_IL|Vrp;DV!CrD*+kR1hADyQG`K-tmh{B6S>SU% z2K{hSW?OyNUM!V`4u{iB#SDy2%0LAzI)b;?S;4GkZ|rmYi#G~1k&?2K#kom*FL1r6 z0Lt5#Z2m9E=#q&YxT-h*we3vBtMHqf9QbDSkW+FcgM)@=6Du2x3Q2} zeM#W*!fi~!k!HSBxu73GG{S<+Z_^WloV>UT`9i%%R|QX(Ivl56q*MfZp!ntEPOq_i znf%KuE8(`wiI`S@>CO-N`!=9MsuvOI2xIHjZIui9ATtH(UrR``@sLNG9{tLE#7{IR zhMU`z%O0mD$C417oq(^oE=yS_VPXpQtOqwwd+5~oKPR{_KxQbHVrF}XTM2J2i>8`5fV&Irj&db4wb zEw@iwGJaMXk4B^xDVtqfS^$-zCWAqI;`;uyuQ%M9Jb2ZO3NPp?<)qF?W=0g&4AwXe z<4l6svq+lVB*%POU_*i1}Pd$o``ylDP_307Mx<) zj9*&I*+cr*kZ`2Ha|aV|dp<0LOU2TM#=--Xx1b+cOdO44F6}2r`PqF+hyPltpNL{V zPVF^7bw53#6CPPG+1Hqxl4~RVcA~4%8g&)o9w7e|3u0~dcX4Rg-U}ArV0)?51uZ~JiM<0V=9Y0N8KHaRp`|~?JkQk0#13{dC#)D{er`3)is@=sdXb^& zF0^qwlNaJv@S-Oez(rkGwG#fISvgLGzF_$hgByFKkV}L}+_6lMvak^OOFP|fi9x1lk>yMFq zx^elFp>LviYtJbD6IsgplwihKRF;uFFQp*Ath=9xnyI77ukx3!%4UO2@UndN0es_b_T z^eyr2$!80zbJo{)q{iPg)4vlOKrJr<*Jd;*#JP?aaqu@6HZ{vaLL6i~sn>P9j5$}T zTrp_ZCS?}HBepp$^&PE*>yR5#u}fUqo3w0YklhBikFmb#!FS$YqfkG{K+@usCOBqy z_1bOrMQ3v{!1K=chKKM)%`*HsPCQoi#~MG{W2Pse0{(C`Od-t5m^GFngHho7N6|hx6C>z6WYXz zg6qbrW~Uu+BnF{ZrY>rgm>VGz#XYz;4>!Q^bDZ0}(CXs%N&QObdFW{qPW_Y_i9mf2 zY}DWJ@8hl2A3mO+b~kB-deWn2I~G)1-4jLr5sDn*YVA5H6bqGyxE4fe9HJ4wh>)N3 zA7HtUTi3+wYyW%N^2NBt{15Th(0dmq%s3xO#wR?7kc$tp_fP=}MVl>dk#J9yc#z!( z|0&jZtPQTplxt}42U=ojZMqF&8?t(I8vw-XPMt`ZFdAz|#Iw@cZI1;q5v2+33%Hvo zbij7;1m6>c&EMwIW^$;ol#K+#NktH{^rAx*J$FQ-aoUCZd{Efr~xL<5bi1~Bq(D&2v0Ic+PRIS!;Ak;|+vG zX838IupHsT!E#l%HwHAQqw-?$wAEA>zdz0tJfg6WHY@2deLQT9AFuG`vDn&0@97=J zv9rWy5MP0E2Gc*Vzg}S#N7m)NhJJ6bQ=+U)?-=o>nt~g2z~vi-I^(+Xgt>yV^#CBc zlFn$F<#XccH9;8>Zamq%GM~U*Ze&qL>E@K5h;>GPYpk*Nb7B)0Mm9z!d9?;ToRnIQ zQDeB%dM@5|&*C@CX^D1SE7_m|@VmQ;@*BGfzPQ1$MF7nA7SWMQpWN(GqmVALAN+&( zyrGoMaTee0VLmu2fLiqKGd7U$+mISRfe9FUU#M9}NeDBzdhBW}IK-N%Gd>_~SrJq8 z#I(Ky9m@QW^r85S7yy>P_S3_tz9FtJ45<0spR(^xUfL+((=x3c+M7Y}I|~KsDxK}5 zlP)7ZmjdtXoU{AiET78Nz@53%??tmJ%^9LPDc;jxjP*P?OArHyG}GCljWmA=S!m_3 z9>ovM@%s2?xwKT%hP?otEG|O|{T1_3 z8zDAi6=5xPkCvR#PbrwI2HSY%K506bcLuAOeVqV?W#EI|F^L{Tch=4ZNbzAVLQc53 z+nAFnC1q~N$_*yiJeX~G^CU24F__T)^5vZzHFx!M!BVE&PgK|@=_)d!H(p<11b5C$ zw+Qye@%#1(8H$t;4M54q%G)FAyTJ(C#T3Ia%08mh|D9 zm=z-=SdBS4v6)`rx!eDTP^OTZOoH-obS3+LPp|0(_szRV^WEGyhwJhdPs3SG6xL0bH*$ zXO{#yuX?@bjQ9;E!ti4|WY|}18~g~An8T+78)!J#r#JMOz{|CmdXu33e>iu7_N(zO zF)Fn888I?}a%m7hb9vvP%`!-XW)iD#k%npLRhB29hdl}mWwE;CcDAe1Kq8B%a!xrz z?Z$`K%p-6HPcdb$O>(yftExN~x?P(kBSE)xcxTZU`g~fNFZFw)W6e}%fuR^okbRd* zS&%7uBbJYTqn2X8JBFo6wu68gh71fCQ*$TIjwiMQo9BLS5(L&rU1*$M!v^TEuNH;s z@U}>z_Mhe_E~$!=?i%F3=4n{(=9j+9@&W6{X$MZX!YJ_Had3F&eRq($XtHnRc?((A zkA^v~NT&Q%=@o{}il5-xkN_qpiiHlACZJfvJD}YzFZgQm)I=o~chrVh8%H`%63BI~ zO?9gu`8V;Up$`iC2xzqwEWX&dASH~3-rB5Cwxp|h^kSXC! z(C%Lkc~-CjVuuX0HbL7ZRo$MGmujeQruomAFUcjnIF>v*J+QMW8r z=62I2+xbAQb9`sGPm2Cq!AM|X-{R=G05O`MHMXhw&tM-K=Y3m;+8a)pvmNlo&qIYV$7G_;Cx-232KMxntfMxE~(BYLj#P z8twznvSI(<7wlN%y7rDz+F%xA7=4xHxPVN^^YV;pv zsh##9vH5~HWN(Yn;0cU9&El!^=;Ou&3(OKvqlpnJ5LAv(&2esUH>yWKyZO2bqn|mQ zUL%W6!`gaarG=^KVsyciZatfoVp)x`uiyr=UlcQb=pgP35JAetp4m1r1~+UfKD{}m zD$e(t%M}^G1k|2|cc_?R?`4m6&BA44f_Xag49KV$ZiqCK1_zr#lt=c2mG8uKWT?Y8 zUY96VJ{W8@711!cCS_Kbw;R5Z!@M<}I_p%=$L3m3JdeMTwP7{DMW1An=DHU3sb2+w zFh!jY{M!Cg8~_h5tUsR1jxQPhspMn2oB_;sd9Ar7-W!I86}(!rI@&gdjO`1r^Y`55 zUAELxd(F31cGlCiTTNRPeVj`<(dH0Wrx2oskYF>P<$XVWev(jS#g4wXTHm z)1BrayRHs(fh&sDko(F0gwLnnOO+qpF{ck=9?1#R0W)10brjx2Q!4ozMU{?*X822| zeTe~HKp&sa8IvsqB$;)Y-)-&Fej~iYpLgGSnb?~iE%twb`25FhZx9ZWgTh99dHc|< zRn1ZUV!?ZT?Pc(iU)QO(a6Ya2qwgZx$V$0H6lN=h99nqXGGXaJ{C;rIfv!fV;Pn{M zf~SoImMU0|uo_@0f0@Lm*P5r|Jt?AkMLA z4ZU`P>;7i&b$ZX2>M>^ zVVzKmPWqs^<5<@?`5v(wrl})sQ!x%PXve;@EZai^9EnBmKSfLTtf`-T2lhm(xP~+? ziDyl(O2(LSnNb4Oij{}DQmU8M!3#LM&*bOi8A*UZj8De`X%2;R9 z-)($bUC8})w0SS4Rt6;!E6fy?W0s~d@UF-+B-13=4AVw^UG~>U);{UcnNZpc>l>#c<1!8b1D zwU7o2Pj{i0EMDBF`+NNo@~876-Kpq+orEOMsK9=&)9Bg{5oeqk70gA``|ARsD?A1k zCH(Iv04~#N&Mq(q`LL=JdWDnUOra!#VXh4z1sD|69S+5*o)aZ{bS2m{LxX%oHXo2H zPlmoYHuxC2c^x?rS>R$M_+3B6RSUR26scM04v+&9`0EiIuR+X386_KFPF<2qVWrA; z;S^#Em64C7Sr%jrpko)VD3^3>?y-xP-+ncG;~yF0iE)$wk*c>9k2Oy~@tKyMlAX@8 zBVEe38tI3Bfq-fdIR^G5NJYui>8t^10} zX7Q2!pJz^l`ZE8Sb7TqdI+OjZnG{vRKjBpNz;xq_7!E0&Jm`7f52BWV__p-j+B2la zVd6U+fM{z==Lhi*qg@}xi!beMD7HTst14OM$SV|e^#S+H3d6TsS)9D?D-b@dDo}n! zB7B4X_GztZ)}|}MuuS=p+yvSK3>hFO`Qjb&Cmyg%I)3>gbI0qP5#cT^#ktI546z3u zLGe!(FQm(uaEJ7h;r+Hm`-IsPx`J{J)+Xj(4o~o$IM($6t4I{w@0MCD)--^4iC_Ac z<##roo`2H$A& zDxg~(=vylg{c9}A-=1iQq(_p3tn2d3pj~9bHEbIY;$)F0w~j7JrkWP|?hum*CI+Nq zr!)AB?vIToxwYQQpdoE(CSs|% zMru|V9~Hz}rKy8-oz{mH?1siETBdvTYq#o-cbyBMl89vluV!%w-SO`?tYud|>i7|s zdD9H%6xHVFEV`NlS{!7wG@s>ksh2a`zAJ;c%9;JznpWC>5QZuA6i5@3kjQFG1NXZ% zY^3nr$7SX1mTK-wOcJ~XbXOJi{A80&CJow}63GW*0JNI@7jFZHd9Xo*B@42r9NqI0 zR(Z1N3`p=kwX9F4f(v(hz9*AHx{XLu0|-B^nY*r^Psgf5f$@K?vnq>aN@J|j@BdV8A^T5;>Gk67I;i*~aei?xP+sL+Z0gN)h?V|2$Vd%Z|iU$S-uqd7_gud+=dR#Cj9Jl`OM|m&hZE zFK~O|qoTdUTuR4^3`OY9D>gMwRLOt*Jo?R(wFcyRNmq7~vl7oW^ab#msr*sn@SA~# z+~n=X#P7_|w3~eoOC@1^TW}yIpd&e1HhJ>!Dnf(m`5Yh3wRx|x41wgJ32G z-{?g>0jL$3&W#_dUAC(qd0(AYh`=uZ1HG%*Hg_g~ zGvOVg3IWaD6VOA8<15cYQRsgZG45|P{?A{;5@=Mx-)?>gidD{J^qSwAa`mw<@@I)$ z`}4?~rW!&-Ef(0IlufH2NOc8Zfz`K|)EX>YkJ=3&6&G5=hno2`@-<}=1|(59wkg() z>Ui&!(fSQ2nZ##koUzDfFex0RZg{cml8O6+$7OFe@9D7n)(?9LCWsd^>2PXeGfD4@ z$jU?w&;~M)s5|!(rGzDei>V4<_wsEQ=~+jbHon2-c&-~| z=v`Y%R@8zF9+Q0)HU5={^$_K`XHtx-cuHym7@GO_Z>UVYCH1l!U~j^ zlTeCg+dk?YgYH>7uGLWo&rSEdt_y;hO#ORB`Nu7}Huai?UlPL!iv)!VRlQ!JwGE-S;(p zKH^^~`fDm1b;}&v&9jmVCfC}rU?YG!4K)M4n{W>{_u?oCtvN{hiL%<>G51q6?7x{% zXexx9m~DSNlWw0AR8jJZLp-<`o*gX)eESAU(YZL=RNF2GYn1#y`)?ZRFK zxp|cqQ0_P8ZCK%xu?><8ZPCX-H2Slve3$<)x;xXc*C(#7vZp)4p#Pl@|38HH(46y^ zl5GZO1h@|8%?Gld?(7k}fE4+!l=y#Kd4oabW1EqdX|tCPy(CtjDQsuwm@Vw^^?tMj z_GB38seJe)8xra_)H0uzL?6!3H{>|oDeL4B{qFEfR9V>O7tVdcojLTuh&utReJI_% zeu$O;cv!y+kF@RuhE*sYrWH8C{7~AUsff$GKf3l=+wqR^M8`z4{_ank8i(l*#3h)C z*n}#CDbmu?a{XAmv1)QNP#__VYoqfEZ$8S-3zWDUv&Zqm&b~eho1fId`1LCEKX=Jp zss8e1L4ILjL-RdV?gp(agJV}dzqd2#ys`iIMzg{D%?Zby4PxoDwg^AeZ$7p!PnmeG z==?N|{qmGgkMlpgKqsUHVE;>th~yqiJ5d;-zQmKIgr<$`g!cB+Z@!){mWfSBu-g^+ zFNPO2R9~-MUS5v#bxJ$OoUaKOw|J-MuHj+U4K?Apl$?pkRHpF%$147x8L|f10ONv$ zdBi^c=f3~1Vj~s-xS}yNej(^kcm2W#7&m`Dcjm#N4q#*otQG;^XmmStwMHTaO@nC? zhp)uo2=f^?;UxdyX8+(3mhxt$#vlRGzNvUfP(`FKu*lgzYlr!c!8I1{PR4IKy_G&N zE2zsGpd|iYGgMF|rV^tvu>57EC&}k9_x^FZXc3cmewMo8_u_T3_B74 zF#4nIv@R{xc78lWD075a^x@QM1Epp_Uyblj3j;xWRS2p0o6}YaE;xj#_xx9TpcWZ; zHCE#yO7!wsRL=05zo^96+jLo*Y6U)k4qS=M*x8wRZg;2!!cPCjVk`gQ$65P8bMlP7 z+JLfq@#29`(98$1Q9xIstMa!aNtTfP3Z&Tx@E*#ZJs&QAwgt#57U(JouAz(V*rSKu zy#GqMUB`KTMXw@`Iix*&_E(f=x_^SCX$hc~QGhZ4g-0n+JeZ-NDyg^ZKDrE-w|bA* z@s0sY>6CV0PkX%9rz2SDgjy>!LI#Ki4`qrrRY?}8Znv;y^}?%?0C zmk*}n4w4+b;|iWXJaiLbr_Iq`|1+!vICQc|n`U@_gGtO5m+E$lYs1Q&0O0Fa+J)hA zEZC~5DuOC7e-f0}?FnYkZRmjLWZVXr|E`dbs2Us+tQAxB;?G$!9+m0*FDuc zKxMS*zl>y>23Q$)K<7URDD+duWjpz){kPS24L1*q&FWd=$5i*$?#n;at4l)rk1#em zWVe7ZSwnL;2k$7L>&huKDYI6f`2(_v5~pfJAD%k(kM6`jkH9wx|HhM91-)YbNf}&8 z0=lhL7;WPX0rwWBK1V<9x@Ou$0O5C-8e{Kv&&1AHfOT*~UcfxLyVVB1lkW^GGOx&K z4jK;zW^q2XO{?l9spl8xS(GS^p5s)D!xJtaGrGdXduY;l_Soe5aE0wZY1+r*Yar9^ zER8oM0kl=KVk1%7E(mP%PYc%7hlF}KfKD%uzp^)OQA^{y^J=l6%i^2EXOKhhrN+C{ zo}jx$UWLiW~q{O}?=)9$T)*XPjSkZ*nCl(!Ez}%>V*ja%*5IDyRt0sBszF$VaXW#+ExH zA+~xa@*-FW<7O4>-=EZ1-_rur5w(tG*_+);JsG}xAC%v*J9K9OJpKtFTB#}lq96hx zg7>=l({o~a--7qaHbX!Mr?mff;J((h#9^sk9l_>+<|CBcL2;9RRaQU;8tFic^IUgU zTN56y)nsyLU?)l)vxx?9{DUb~Is2xJc5+b}Rm!9&);3SQQ;;$h=@BsRe2)>lH!Hja z#FmBz_DtB2MIuy2r9G8R*vgYvA~twBoHxbN0KiXI%^dr3!XdB!h|1KV_?hQwi6Vgl z8HeXPXeb~tY^bLIss>l9npY#!@C5g8|(af+ofZEd;uY`0DDE9knQA_PiC~T3fCom9I3Lm0)ivq&jQRd zb*En6ax5nHEk4ZmK=P!0e$~pF5^*Q%;3SITGtFm?y zJ|>-c%TCArOw6GPN+&d=prC$rl>BOMdwpvGp*EXQ;Xf`G?{(XfuxKXVjr*cBou?ky zWeL*tTyTbn>Pj?w%>EM42*4c76!GeMlIc;*q^~Ic&8D^Q95t>%-9fTeHJzmafCR-7 zi0I$^vh(X&_kff$GWRDw*JmG>YQ*tDPZ_%VcR-&Yr?{R-rUJ>NA>*JGrupf@vp1|< zclO7!@3R8CT^N5i4Cnym71iardMiM@&6*sITcgf0y{LA;rQn@hVs*hnha2X4sEioQ zBb2JTK&t?#HlyFeIi%wRKV-}SfRn>Ofo_K<$tU#ZFVcn_7tdr?n~3&s6p9 zR#>P@#W@ABPd1#U*BmAL?lb}9U@@d!Ge&4-^?`EW zw!2Rir24r7I^&&?|8G|e)hqyzSd_xZL#t&iE~DQ(?qBw&lI#GXs`uuZ{}Cf>{W*7N zN{W-ps#|-*rMg&vx_IYRG8I5~sjjquZnufXk?y9PD#s_cEYhfS^~Cq8tqZZT(7Dx@cdA2Z_M}T+9VXjCb6pS{9?c zV2z3}(l8*QU3BdZz)BR>wKOTV2*U;qy$LXU{jbmqh1@jJW_<8C&-3e#6qAK*$C722 z=Fa5*Ba!+7Tl=!0LU8No|Ghj{Qgf%%O~%+EL#KQ{}JGE&smxt z0}r=d$YGslIxIl6oZ=Agg#*ZzqWxkV-ob#Cl6CZp+(6GhSAgMTi!? z!yJ6q7XE0UC@^$c>(Zo+{^pJ>1U*=wm+k~<@TS_#+Z<}dI%YCPwBC)I--r5^qp~sI z?+H21J)p`7hpcZ_HtXPd8dKdAMofuU`1H*7rTn|J0@Pv{2#s=(Ek%qPIlFCXSNKpCxm zNkxUA87<%l$i>Bnsw8@!!U37~GTtUwcRNCkvxkW;9P2K4{>q_+Ku8PY7`t3@@ISZD zKHe6=NuNB!9J7j{L5(TVM(opNIUD|F#b` z3kUC2a9+IiLr;Oo4QNLVb3C74JbW)~$ByOH^t}1svjDKHLSli*K>G;5xJg79enJY> z*neIY`s!cO|2M6VR$9Fq0HB*LLnRj8-%p4Qw}c+`6toAbKVT^E%Nj)ve{^*wy1{Xv zAh`pkYFQw}xO$k&G9AO_sHgB=kGZ<}%M4JI&$dU^3KedZ10tt|`aeM2T>%29w|qmX zL3Ws6;9BcY_WgIsV_j(q1OK$|Ky2%-4(v#j-U6yVtH|@x%YZ_#oDr{J{`&Rn>Nqw@A01Toqls7VWy)Y}Kn{1&iHkR7FQ zGg|k8S}VXjubcAzk$PYcKr=(LUxbDh!c+l&tjc>cA$n%IjZ5ttPy&smx-~87=~LYH zcd2)twj5EKc`s7pghTjc+-coY{$S4ZA7=kwMB~OWAkVtUV*bVn`Y&d00R>Cx!^rb} zS^MMlo-06}ESI*b@inFg7#~fTk%M7PoO{bdqXdCpWoyY9*zecI;lv1Z8P=wCj4WM9Zmr&R9;QMropC@I_ zg_ENelWn6bfTIq^048NA&>@&L!}s61f$IPGo?Q(KKRn=Fl`Rk4dEM~0|M5!f8?FjK zIDHj-No^uymqu{|bmwJ`PC_hgK-=%krSdnn6w)x{l2hzFIK~*Zjj8^J#}j$2-9x*+ z-fpj~tjuYEls^rH0IjvFWaeTdgQ31jqb&`z18wp&bUm{tRlY~aZ|;n|bo5qG3lr-w zAi#A2u#0KPod2pnft7jZfE}pt0L9L~icncgxcBxXc=qtTcIAx08U{c&ytmYPYMgM5 zsq5Fia^Gdl#p}NUuo3S`3~eNoRq%ofTW=+BqC_uzZ(S<@05Xs1y zgH*n2Wlg(I&b?V0B@od-;1mEC@3mG|tCf^$g09E8(kDItf9tx3e!Xo!TGQ#WGl6pD zrZ48kpr`y&Yg}?d16Ll=2TfGiM}@t5HGlx9K-&WtaMk}B9AF+CGXh|aIqfE~s!(=W ztE!h#95prpE7e1F9=5dq(SL{T7aJF%C#bC%jP))4z@YV+pLc?Gy_^Z?YC97wo6N_Z zy9Xn`tkZc9Pb9-PLn-rcd#l=4UDffxfeKhX<@E>ncqnEGW;+ao@UQ;mQcExEfMX7x z1I-+^FCL<@Y~PdOBB=UfUQo@M|JsS;Pj9D zAb@jJ1Kh}(BG$*@Z$nx_Nd#=5cS^VJ-%$H}t>Zu9=l^#4?ABS{>7!)}fxhkRXfEJ{ z3GP9BW-$TfjO zV}b#Toc`XYi??61RPbEDXJ-C2_Z(-9FX%bJ+RQUI`OPnWDWY=PZq$t4>kZ`L_e%Qk zwF}ej|27=>JRitt4H03!o0NC8hL?f?FwL7>T5B19P4|1`tZJb>+?wzFVey%bJ0iqC z7r@|pwgCLooir+mFx7{_D_p z^&zt%1vHCq?uMP7kjfVyQfWZJrG^%vv;qbEs~_*r{HsyN0OgRhvY>%GVa+yd=h>OV zn^tZ_i0hTHdSm-kKuSB-=;!&}_9w|vc;b?XNZV+Q3z>PVB5ePw#aCBs1`r-ZM+ek~ z{6{IkVGyqX9W1EXP`#Z2pffImi-rYDf#0=589;wWOwm_s%+S0N2)t^m^O%N}8mmsn zi0@;^GS78#Ijvnd)F8bqa)NrO3Cwc+LPnKguI7kr^WIs`3_2@bnTB>ORkLYg_%{aA z#@y6un*qiA!lFg$lA4|{JN)^zvAk1G!bVog*O zBm@QN25D5nAVj1jrKKhyF>204Dd`RY1xe`|45f1*2*OB7N!h5u7{4=pZ1?>4y?*ui z!|R&kz4Jcj+;Q%B-S=6U%}4hE^tT!CIV~&y!S`vmH9orMF)2Xa*m`&>ad@NsjqGP;E)yVE3X6xAMZhlfcndRYf*WWjtVs8%oVCm4I59r}zsyl3qpNF-fb8*ym*eQ_yw!CwxooQ$Y4_NJm~ za*%jqTsg7Mmt7+)S$i7oKt|UhJ|6l|RLk8NQ(&$%Y1cbZu{~!};?-wh0Mb?pD3LXL zp2d9BxmZuAusZ(*blIfL`=U9qVu8^SSAj{xG!IXdD~QAU}B6Xez6De5zkK!!GG zH#pu2p+3k`sD4x7f9iHb4al(2Y`i{9X2NpY5GJ%B^Eu13ot?brBxeFLMNhnR$Yz`F z%H@*MjVpn7X~=CX@St)58Ea&?706CE@dJrE8%kAFWK}csGaZnUGGPiQ@915fC1qhZ zS%=Bk(4^O2(pkeuaTebQv@M=!f%%|OqTGEvllR(l$!q-2n56E^^%Vl|d_ZP> zid7Qy1WNK1E+&G8I(8tVm`iFh2$G}Rl9Cus?QGk}3@(wHSvN$==79-n+ZcTM=o9(= znfen>%6E<*`12(Arhv})QVc#Q-4RjdGDB+2xB}4ob^V{%ncybClgMA0!%XVPnAGeh z^#}W8wA>E|!F^J`;ft+OFH5`$MJ_%fH5trC5#3h_U<;izmaU>DCVONqkzZ)2CW2xv z3fRg*pnGGS!JQ6;l^Pl~9pKj`+xyb;6=h^sHtiS#h9a$(!GY@R&B!G89F^2scc zFY)5?5wUtAFsICl2i-GBo@F;EBclBP?R@DEa@{&Ph83Bh$Vuz@`5h#To8xQ<`+v;l z2#I{u3UAhwcFLN79(ma_x^Msz#jH7u+O7GFxPbJs9;je&>*@l(JpVzmdE^ySyud>glc*&NLAVQA#_esSQ( zy8|Di=*5+YzOGjqSzt4X%>-Yvr-ED*!+aD>bgaOMK+vp0S>E&E|oFVn}7QGpoMHPHw#-?rsx9vJn-z z#4c(O)P(~+bOG6Dj6*$xB?*7+Jo}*opdpPxP1`GfkYffpX){vgH;*pz6s^9Ci%WJ- zu-Ccmo0*xJX4;n0=euXj z0U(ADvybC`SMon>o2&0gQD78k770KNR{g7K_=N@3f;W&?yMAY#AJ6^yNqh(RVb0)U z^r(#b4U~69hp$Y~LGu3e!3K`2iEjvgetSu10g}iJM2fnd*-|V7vFCh{N!vxcNY>Q? zHp-#hq#!wh0P^>JcRhA~Aa&rYUf)NG<#jGef+Trl!9W9mhKrg9+9ta|F zYA_R@UD%%s3K;u6#^`L|#Iehk#v*Q>oiYUNare0_I}U@`M%nN5=GkEm+z3f$Y9) zLnHwJnNh*ui9b|Qw-=6$Z}RLkE#EN>({t&a&+0w+ z{Wr`aJ0gW-MPywEMSikeFdSFgFxl^1?3*nt$|yS%+^0f*TH-}gLno|O<`eQ8lS_gM z9(CFOAHEwFP&%a8M1?$!o+^O%n)@KxG3rsb4^$Sh9V zo^adiW#G*b*=XPVzrH%rkIL`!AewRks&U-INMYH9cX@x;z($7t^<86UYfk|$FmhO& z`|ne_IJ|3N;lctmIsO@zr@a<)o|XQhxxrm=T=P16%f?@oTcSL*)zip=Ltc+gZ;UmC z&32B??okH(f>6(6CC@K=l)xh+C3)Ka@yP!NM@jTV%spZFL#N73Q_1JG<`qSmTfQ9h z;PY`kvFLIwJ9bsOgPSLdd8I+-i_kZv0k#!fe}gUhNe6u4mNSPCsPGuCb!2b9YsK9= zK__x;8ER~HH z+KWKF01zgc(QSBphI;a& z_c=yGqIP{|Ao=tfgxq(zlNZ*57q2B&-N)}4BlT_X1|E~-*s}|>0P$8l8YnwB4YE|d zy1gqP{dWpxo%uWO{k%zs&U`i|g0AfLmhrHcA~xMEq|7F8_)V@$ROpw9Sbbn#ms*qa z-$vA5@9piiSI@*R7seAqO?X_X*Oq{@S%JU4V+Tn^)wmX?;-wFUZ#56l-JK`a8emk;I3E&l`B@!|%_ zp)c#01F*Mw_c4HYk-ilPN(_2Gp7x!~yTX?bVA>cEcE1HiDx2RT8v2cQnk0sO55w8*+EOP=nCyfY*(1H!7Pqooy2s!O!zJTdUOJK>O$ z8}J-lq-U$2X>w{i_;1q_h30Cgir1>j;LS8l@7`Ojetf|74WasgWdFN&owFo|0Kl`m zm%9_rIwX-U2v`Ax>jRTw8stHfJDq^k(U8o2&==9L!rfjX7LS|6+}-|DG*`&Oq7T0oMULkgoLO1so9*qX>XRE4hHkA3QXM01z@pA%g4k7HB`; zYgasA0XhK`lYs0Ii@#d|Kg0l$$MVeZ8Px3G6fs5*T44@4TRe|h@Sb}8Ty$#WTz8qK zO^>f3!WA@`jE+wQ$z&3ShSJC-kY3no*!~VC*Hj5p+S^tdUJM-?Y|Y&Q7`KTVb<}@I z$Pc;PL5gPN(7#JhMcQAK1VB!)tbo9jL?Q@T&1DLq^SoAjd)yOxFh*t=!QxWJwPMsO-4pIj0PvhTDX~^(IB0BSTcg36sl8yBU0;DpLS=19twA%Hy>FLJ zd>SeXqb^PS;j|$Hd7hQ_+7j>Nqlu$ClAD(#d1A$A7hUw8m-QOPwX7@{o~<{|`FR7b zCQ|HRY=Hs*`CA&~@I7kV{t^cO{JTM_fq3hz`%`j5PI)`jEOnDu(d)t5x~D*q9%p5~ zP;0xuqnTx+^3QBAOi{)7&b)8F1&89DFVm@~H7%q`#$^^x}~KPuRDBNKaMH2W>ZcP)ju z#`(EtPkyUSOP=N@*6}NH{>&oN9&v`#ZW`B)PR&{1oaq*|c-xID?M*h`f?jqUP(eXK z6GkU+`GZ!n#XcKQn$a>&K6Udtjv{0$r$Q1Dk-~YP&db7br^U|O$IcymH8 zVOqaaY4SBY`s?HxQq#@^r=5Y+P>MjV%kR+dWqF#k(=QrzP^IDVBcGDoWR@78d;Bex zgrQYO4_o&#kM)&)%#4#aYSrbvcwtQf^vvGrc(sI{RBJwx zz;p%H9dkJCRC)!-%UGX{FwIV|~POy;F{mwbzCJNv-u&(e`SJthTA`C;ZHwESH zgUoVww{8FV!`@gqw$BP1JEaNx8cGJEP`ws0VV#5U8{!j@rizCE9<`eO^7#Hu!mh#v zuLX~0fTO=rxts;{|0Er}b)vXewt=DZt+MiOgMNF>y0Wo;2)=g}R9)xwJ5L&n(UNQfJY71q`_EQ0N?|`9ch_QCM88KPgrgOB|Qqc z8o&L0gPQa&bN}|5-Alz)CG5D6(FZ8$=TfVQli{ls3tr;{>NX}w9~fIHK6R_4UfNzd zS9;|`a!ZUDv|>FoQq=B-I5@7F0E}Ow*bB647_a5?cIX8`teK*WQ@`)izwg@Qq_VLaRuYfn@(2aOt7U6;kIha8)EnrTsMwTi9NRNtrf%Lln3YQGuTsyUB+ z41W;g-v_17Fh>pDm#k%j{QU`p_4Y=B^}g!A(Gv`jxNFBLoX73d52t=6^iPwx%EKIG zlyx)mS5El;z*6E}Gw%cQhhyabB&+c=N>bg=#eoi53IoWqmW4g#VTb;l zk1e4ypX90BXjMWp$N%yeGe65~pimA`?zT;->hg~rn*DW2i3fIQ)r#^3e*W-y#bq!w z(oXR~!Fdnw2^M}&ieP+oF?!jJu)}jY0QKthFQdz$sr4axYtMZWRtVuM6FcXR#5jA_ z;M|>Xwnw&8NV5+MNJcj_6xGj@f(^{9uweUf#zY8#bu^jM^TT)BpJ z*(pHM({353|KuxvZqi!vQkR`r0NvQH_2m7HbIvg4k4D_Bs-wO*0YyGP{he4peQ1|A zZVKg4xgi2!Zhs0$$aVSU$v><}3xD$8mv72Q z@}c0F!S>@PzdV_>`{G1#k{KNt_HS1J!}EUB6CtxYD+M`0)REJjzs&i^N)fz(Yg{2i z>3<^=*gZKeK+mi*QICH7}0%;UA0bZ6%%Tlq*FK;n6ZLL+4f*G02yHqp2}7Ws2r56S0+Z?A@8%<7IH3 z@yFWXf>{TOiX%h?Lh$BV93C=Cf~bfUU%S6>eET^-@BJnRHq$NS&$}z0F*{>Q(~wG| zvdGi$pN%*J#O>czz`M=ZXA5P{d*+faL;$JB9{aNt)Q@CB0M$=Tk5dJ$q z?M=IDM!Y+(Gr;m!21dLA9KaOo^@{!PEWn=#w)3AdQ)l15bJ$KLIYIdCl^5~fU--+1 zs=I)H4@?ET=lMGa9XkQG>;KNhM@pPE=%D$1a#gRXxC8g+94}H0G=*E%AR`$eR=Mau4({-= zUGs?x^j=D4onw!^eO?^g8q*AJ{SylQfC9y{O(6fOvtM%dbsm7Jh687^@C2xj8-MpkOoL+jla zO=OsINRZev*NMr26|uoPrj2IAkF~zd$1P*kLGvD?HgNfeBS7#*EO<^zj)PDNALxE# zsQcRZ&BgO@l4)rNz_hpVvz9V?hU<9|`}-<8n0^P_x z*ygu3JQaLn1d~$D+MI2*h=9UpFypaKZO5Z>-o*kA6Pf(D_2cXc}u&rH#M*BS)Z!#9=tJaa(`@O+cC0qaWgEVzGobVXV4fb)lVbpXD@$mM=y(%INGsx#mC=>n8OJFhO+=5xc5>zw6){@WOg1CQ! z9aB+aX-o4vp^YOc>qMfq@)I^QkxMW5cotx2k0w(_T%>@wYP|kdASD|>l3>wx^wLS} z+QH~i0CvSkcK7tvY|(@aVER&Crb8OWCMzL&Jzey1yAc*}{I#Y9lQ3k#Ts5;VzA)%Y0K0P7fFC1bQiG? zeP}5@-IW_P^CmhsE<?<{|bZ$eWn{~#OgA`@g1oF9T82rL44iylM^!6%l zt-qjUbSiN0;M_d1OSG-M|4$;0p5*6kBmh?u-?9#_Wy?lRh~#i+yv*G4`~(>Mbtb6w zKH=0NU>>3($^k^4(=ygUu@`jEtxIMKKli)N{a0=8)H2o&#)EoME|TylB?WZ>J#l@% z#8jdq02D<4$nC2RGjhJzHU!;v^to5t1ul~r-CZSdw2|PcMbx=63ZED6J{=WLhW(ck zs!F6RQp4;)fvVRSeoZ4a8`YFm)R)c;)bYFt7QaDF%S#dp=YbV{!y<5P=Y%sx^B1q> z$H)k8S{>PZnRC)NUK^h)1s8fw^KPm@-8BZ*Gh-!P?MtWAGw6!Y39=}CAfEd$A|wq| zmAISRHz1QCwC&DwCVYg05KA4A*3saD&L6(4HKjy&OO9@i`WGF-zY`%_Q(&n`FxiWX zT7YJuG;6!=ZWB=P~PX2V3R_agYi-twy8Z6#+vrdIFIg=`M-PDsm~7lgAO zZ2fH~%h=i%;de2G@izoWJiU}FCqrkU#RP@t*p>xu8uro@-T$#{(i47~RhpYRwaRK_ zC?dfPj@6sDGg$v>I&G&gJpyc}jL1*qjPu99^^n|a@BAlse)+~Y6P(JXM})`(8V*jq z2A}uS}Dd>}00La1=OR{3Y-IW+sE5Aaa2*6R6p;L}q%4)nIxI zJBuZLqs_O?;@1MUTkcb`TzW^x*!ppgC293jaD+(4I_iMI%$t;JY-*qS`m+8A2(;&b z*KL+f+KO$b7=Y+F!a2k?El;JC=dV{9&h%XzgBHO!BxRFx%5RSTvEKs+!Ac*MIUizf zPXPD#hGJu4F5^Fga`RH8zt=IV@;QSh%vP#Dto08Y`5yV~6O*$IjjhaqhzSsehCb( zYOP00W_4Bav7?Fv z(3-`u;pF2`aOZUDEGR=6PeVzhw?vC%c#eGJ3%$Zynqsz5OKYTW0qzmaj(g2dM$-8k zC`O}O;8V{ZH_2|{WFuGu&VE$4U{COL1ig0D#itVQH^mGQCL96$SSEd7_usV`+2DxP-GI?)m=4N=6w zP1#LduNdZ<+`XrKby9QTsYQ;(|GCmp)kPavyLzBO*FgCGEyKq(D4k z%YBaXr?)e==Tm5W47ODy1in6=`!N1+=b(LbIQ-o(ny#-}2%N?)gELldFPLhP62@d{NS2X91@U6v>#9@9SzGr2@_MA(%p;tPc@2p^}f zb!Xp#iP8k@8KdpWc@pF13VXR_{xRZS6(yCjo1kpS91~%9S2Jkz&`fQ#_Er$)xQS7r zd#?sc9>x+d69b$6mu22s;<0XZDc^YQCkrc)0a5b2K#EZAw$vLJ)Dn2p+?GOPl&@9G z!aHE#!?3bSqBDX@WdgJn%0!OrgPsAmX=&qIEM+uAd(A?+i(5kfVirhn925%+-%Ir= zHzTm*McnzjRq|BII)btbq3{jNkRb@vFoorKFK3v87*0Z(j1HN2%fVw-2&zjHlMHy0 zr-+|-Md?*y!*W6yfFveDoXOef$|NbiEen)MVJvvA8f@Ct67*NK^i73=CgHls{FW3uITO9!A#BDoMU8c;3~=#{Ow z1y4)y)qbbuD9dDcnL?5D2H0UTqwr@^jE1)u<(XY7 z1#WZ`P%ws-pC9st0_;b(NNWv&#NVWtlJlINU7=W3+KYj}hwL8AI1521;@r!HrHLwh6Lu?^4k4 ziN46@CwBS8Mg-Z?(jiIX4KlB&N(9-g1M7_4vVULbln1bTD~BFR{MEMEPH~?XgyE+= zbhdt$*&oj)E&!{2!<%;cXASogH|{M15#Ypyo15RX$ZuwIB`q*KHw@DL`RO-5J;FZ) z?ArU4kCy}gKK{BPFq;=#pYQ+aH$Nr^{Pq7W{(qGJ8|VIy?f)n9zo^mg`saTN@BhKt z-q>1pc^EXh@B+Dw@iLV`5)|l*<3UgEc)Wk6owkuca_ojS=UvCaC4U^Q{SHb|GG6Yw zNH+&qz2u-eXxEVte97xuY^EJUVPlG83~);<7Ryg9w!dsU@Dk$W?#^X*;%B>s?R?^7 z2p$wyaezWy7_6s!P!j;0Gz@XkJ)!JUBUE3|R^Vy@6WB+Z1f90Cqkeg{NA;8+1e$+~ zNY`C5cHo}80$HF-9p5)*0iS_yt-?{fK}MfGYb309 zIfC-f@O-pPc|W)*Hq1Kb`xO7-;J?M}@j~O@FAz6DsEX-PMP?ukPdl!W5a=~)O0qHe z%IC{$418&k!#l>mxN9K&HsBKWeVCs<+|dT7dO5LM<`{)^LJM#*HG4s7DaIO_-zqu| zaIba}{)&IIX3ALkVUFn3ljZ-IW>MBLx8oL)2&u(z;v{_V^%YXG1MM-0yG%^h|3>Mb zOoeTTL7HdfGZkJYzUyj-GHrn<>%Fg>xDj!km|P$7R3-E=0rzKiX>cgHJ_}| zO2s+_-0l__NK}->+1`GBLG+yeP4fb*gc+Tkt5}6%rOe>5691^;IUc6|saszqnX={X^q_FKt>bsweV z3=Qi!Y9Y{=R#mnti)`Cc73N1fjqg;68>;ixGc_%lXe89(mSg19Y@v?p()LhoKLY2< z{DP?(J5CJv7?34W&XJTI9@+2q`M`njrhRutJC!xw>|H4xNZYvm%HI^4;T@>d#`V@I*wj(4>XXjn#NGl92QO@adr=@aK?Cl>6Uq@5e-Nugn@Et#oUIfHw~kL6w@ z#M!2vRkK%`3xZjLAvsNDZM*P>8+x5vP63{2BD0D(_}v)XMCsu4Bb<#D+f`FpNBt-0 z;=++AmCG{}tcIGC)>-J@AdVS^LW#VOT3Ujj>2olU7|`mOmxQaFZp#BXXvb=QF=3UJ zo;ZgBN03=oi9X<0)EZM@W6k|+*Rb8z2Ptvy_b&U;x16qg0tjMRdq=f>S^LnfOg*w|%BY5fLIsYE$Ce9>JzFIe6{4ZEeK^-!P8nG0xA$3G ztew2`!}83T{n)r(*3Qtpb@Hv>FtDH0Hl2>WHqin#ZBsrr?olOBx)0f*`%x-M>Qm52 zBe;CI{#yKWWPiBjgmh~C0=CGX$+Fk26ea~bS0nAu-tkVJ8I4|fdQ3VhlD7UK`{T)K zlxyyQeAcbbqLHZj2LcgD!$zF1lqi=et&i#L@o4C}g+p|a5r<89_Qre04eTSg0L_k7 zhL5XmA>ww-V++K57OZ=29ABYH(R1FYa<^F~kaQ4fwnsm4s>r$@GRbuF#))Xb!qC#2 zJZ#naz3-)}C(lqI4A zbI!e(I%HI>H5-X-3bLH+!8CbNstuM7%?d~@PiQFp-DIF#S0Eh)^^6|2pn;QzV$xBO zt+ZjFrEm3G4{D9?;A@aw*8H2!0Ebr9Fx8T$ARIIkG$-Lu0RGpg6m+p`!j3mLlj4h@ z0ieG3qOVbCa-d9^6iMHMH16l&IR_07(p*U$2WLdfGOcCxIoTzl-mQQCi@-~TNr!^7Zjs5=U5vzKu>uj0ULqh z&hcusX?_leS&_m&qVALZAPi-^YUkZ8Sa-@o9|zVaQ0vo}f3=oF6YGl@Kn z4fV4m&za;kv*v`YxEMLXn24VrH-nBzL-Q;xKla?%m{Mlx`GK%v0FE}tj>W?{C#xc7 zeuOX)`>2!$+9@l#WIzf%SYQw!P3$>(3|tKW;6)cGF_k)x#qh7gx!)MpMy_#!C`8$8)Sf=^g#S@ZmKDA``E$`U;nO3MYB;&RFYRubK9LGk4j z;%H~^yBtbba)^r_QzqkmGy)yce{ z>#GrBhFCj+WRcmG5a$&OnskSOYLXGBou#OC#k+D&2+)*ruFRzNX$+@Et;0yoS>9N! z4mRqVw}b`ya_D;j9=;NtC+E#lIBT z`-HNtGhO{7J`tV6DE1XKzf+f423*uOJv5sBXx|}b{s$C=lWz+rn=V{TYW_iDcSVp_ zp6mN3eN|Ea^~DM8`^QK?NyIr?H`^EY7mt@Xn|}aHsph!q!FysfP(qtg^+?4JC}lf6 z#f7+*K%l9P!zGZKt=VoXpd4SS;$_>Juj@aHSj(S}OVV)4kPvk($WCu!27|HiyYH96 ze#p9y#|xYL|)-A_YBx*?vcPV8E6zvQQmRa~Y)DP?}eRo}Uo zXm~H%(BQFXy){T!SIQ+j4J$4W44LTtFpi9UC?APN!CM6CTb^zb{M33VN1Vw$pen7%l_ZB0YHuKwG$+j0?F{N zwE4MvoSDDuiS1Bgs+eb@=#Eij__9|(71#RG%JRr8SoEx%oXN{78axX^Q9REkD`ETl z6C;Ea(Q)*`KVy>fQaAG1yEMxI$?g2=6&uh9B+gT!8==OQ*UtKJw;o-w`K zFMdNcy;Jzwktg2u4;9b;gj>0D(Ks>$PhZB|d)4EyOVIl-GK`>tSgnZnWZInqMbJa`gUTUvDzrm0j{ZG4ejPj9SlZwzZF)t^`P78?@`1GaS!0KcqE0du^*BGT{p+8-Jz*%}W=NQ<~NF zx6Ck8!nFGg7nXgtA>1h(_7m`zy<(XX)Q<<$&v@dJe0j@W3(kjqC(bs+Jk?%vL6k>- z_gA?o<9iM6;PB=s`j}CAdJ%2EC1&V?9yudsThwc@AtR`R^@aX!N>4#RJdv>%BKj3` za_6$~5?_~M@UqeghnhaO2XzhUqx<^{JBm^?FgK-t$ljOJ5r7^q;9CgYFy%{R4%#w4 zkmTR=GNvYTn#yT_x4)F4`-$^^b`0fKNuYSt0o(20H}}^MRseK76spAf>$l&Bn5qP6 z^wm`a&-ZVB`=A2GkHK1zt85t{MLUTyuXp^m@NX1IdsM}Hhnzsw);5{$0`6aMC({h#W2F7al0>qhL_~$3h^6i&j$BG6R3bFByKcK(=PV)ae@%amHW&APw zsz*DHj-3MMn%82G5@YGYBX+Ucv??i3Py@CxQ4_35>xJ?UE9L8wgb#fdw>qecHLNezGwI zfjn5zisFWEyyV!o%19Ha3I|6*-y#zk8 zL(2DGX3LX}ujGi>ivEw(sU9p-oA zy5(p9y0lp%Nq!ub7|9V)p%M~>_A8Q%DX{Fsu{esuMiakg zK+D*!;*BxSZS?pZxV%-l-Bsj?wQma2-`mnxACT{dNEE-s^OvUsS(W^Yyz*bR0_&^# zWP5WX!ON6(EO(~U*`?N8-F4N5bL-^d~|mL!EbNzdueo2M~it+^lVHFct^S4XAMQ`Pwy85*JuyXcyHWTMW6JEXC6G zM<^)isK@+iar9!)*Qe3PE$xicr2<=GB(Q~@r~@OF+@C9AbmnW zBN&_WocYqIeX=YpC=sZzop2a| zrc93&%W^6rj_Ct_&cn_`CcQh%vi52=tC7%%gOPk&Ob+}=5c{?SzXs?`zBf#s$YGdo zu83<;Fpx14-YDfaQI~InyFB>9C7UPPXBr90%)I^$=<+@@GqxAm;8XlREWoj0**u-lZI2c2o|hwK{D%f7(+64h($0ebVldI;+&5G zOLUlWw1I}@J$40y_~Ob+RXwdanxMH-X(qo0yXIvJ3StZdqa@6wEpMudGP?id-Rwh1 z)WvjdQJ2lq7E@NNr))8+sY=Z%i;ItymP=sWmQUI&TDjdzngv}gcHJ=!<=3!0F0kCR zyS`QK>h9}QV-BB+A+QroO5+nA+(SaJ{n!bgePL3JhIHzsz08xXT2Gbi{ z_`6arlY)~<*PDb)nP=h|f_g?^+_4aK4wzK)ikKGXG}4=Q+S?$tvdntuSgC}Gf;=x@ zULIRFe#4hYQF_xSLot;tYof=>-Hf$Yfh<&f-EMn5Y%dhiC=$ma6XAIE&O6~`Xm;dN54dDPTtdQYkm?=A0r{29AMjKY@! zmv$x!HUHKYQ*SnR&T$6;{uhGpKH7K-ATP*E;wCt;)MM_h#EIHl_CvB@9&kulueV7> z-A?Bi#Z6PX0+EbCK|0r`k)5_lJMC~um2u9h{&+@=aa1~$Fipei%6*8d5>n{n5lDiM z&Y_^jP$qd{PFep)%I#Y=@@%$_Yig|4SJ;qP0$&8a(kDU@DQ4mzpDjr<>ffZ<@oX}o z@bqNk>e6E2zU0v`({2U&ZVr#_12307`rSVC^5%AbHNHu@A0oolop{!LLG&t?d=@!OqlNN1P4$o|Ne;CI|Nr zJ_XnJGzqM33LsCh1Xh<4?0~9gR1d;V^)Kq!taid^7kJmR+U&LU)5nYg-zv@9wCO`bi8x=ge z0^1^JcSe0k3st^8hIQP~;|Qa9AR@4c$Z9eEQW!dQ9HBfmCLqffEpsf0D*FW5w*#tZ z%hZuMd(0m%UckvR*}P^~Lj`GVf-8)ovze zI26o(bgK~fcnLDsf6LXj#nnUp1so4st?*$0;!N=Kc9l1b)S75LWzF_k^txkj)b$4H z@z_;b2p%SR`uxfEr&YzKNrcVDRV(L4zDlgC^@T=4n1D*9+N6>&C(N&5^+J5AR>Y>s zDd@_Ysf&QcNSILPSM@t}n_an^mD$>=ADSTXdA^edIS@`-cr!!)ZO2Nq4{#f<;s z7%r`pDlM&~*!+Ic9Y6K?=wfvJAp95uIzp;@$WX)(`Z+kQW%OEdaKese*7&PJZxZU< z7>)Aan63iNz<4HBafAG8jk*LuwrEX}oAHy<2N!Ya68FE@aK>*b+X7Bc?$Yz#Cc+tZ zU_tqTFqfsGDQ|{h9*f#^{#*!T`SSSv+?7-C$;h*l8=kGoAtq946ujxLpu*AYlBKbJ zv?9?LzrgvIQsl;2Z~-66kmb`UKFAnuntIrX3$$iG&?qfwNO6-)*XQGT-gJvl$QE7D z6T*o$-?x3i3C~OI@W-8WYmDT_-0QDlKYHG(@q!rn>l}8ll{efr=_q@!BhAc_FSXHk zP5CVoU63eD-CaRG-|?pK1QHgs*#oIp7*lZWu3$}4Z$wXSuzDX4!i^Evl;yL9P}|gZ z+g@Z2;lz7rc?3k@S_2wZU5LD*oOogjKB3?0UK+e*N*EuXt}O`4;2P{+X=gue(wi0@ zlW?jWohdNa9FJW7v~#XE=QIQ+gUM|1uhW(vgUzB@u$y5s6|&&4O3urj8R9vLywLiV ziGH+4X>5JQZWY;4q+IeoTv^cK&>|H{yF@KD@m9`DMM|A zF$vDoXuC>TiQxK;Fi6622Ix|7eR%CYI<5qEm~xIbQJOO!?jNu$j>bb^nYXk!#eG-1 zqRya?;67x2asLohv}Yx|pE22Hox%;3AhPf-1(UlLyCgbQ0?N!G+TCC-O}j=jtovwd z-0Y1iXnggxxf%(wOa_W<;>3dvYVwA+zbW^yn(J&Rm#p5g3no&s>~yDF`zok;v~it2 zgiL&n4<6EbnGMDHWVJsY=`jaq8Dw&ht9f*J{3D@1no6&@P4}=$_XI|Cfsmsy3=v?{zaDs z(*_-}n=qq_N9ko-G}Wa6AsN%@yM~Ui_IevTo4+RsGku&S-0@BYMl&lT?2WQ~+`%9t z)$9N_7x@ZoS6Yj=UCa8a4_?i0%~76hQRsL?eYL**@*<0(QWJ4e2&3w5c1KwjbwtDR z(43fa61Im5#trH)-NyJn(lKll?N9V%@=m9^9_$rG&v9 z>r3-H9ktabLEXqFfpvR9qFL28YT>f8^W(fjg$VzcLvt{ec&3!X(=&NIsh`XmX&yMS zV(*Kn9VGI8%!R-pUw$G7ry^Xjoo^M1iFFONd?&@HJaalS+kaj8a@+}ZgZ;-CqBIS3 zE#0iU80a0KaYfVVF(#)XN{wpDS+ac^_!tG%@mnmFG(oOZ&@^PR#l3L>?;AA*J`vpo znu>t|h}lsn*OA5O1ZPRb7QeK~L+@F)?z0);s2@+G=aD+zE;U>%junOG_Sa7w3Zktn zqrXkb{y_w?#+VVbm98ZASc)YhSaePLQ{Do)3vgwwS-m;H;L4@&9wOv5~vH`3j8 z!`45&4@<7b)X%(v;2AS{T$O5Nl?W>G_SHs;!dzj?U8)+da*BzuqCHz7K2kgT4+TAW zO}IDw)<^M$BX_H5x6T#DWNgXeCyB+}v}V)cM*(jHo}4$fqU2l{>D#Y8OT?_` z#W`dHi|`&)sXW6nG?W#(kHLwnxxpz>3f_cEnpEg#bhse@!c%f5wj`#$)KYQGWfJzT zF1$&g!bLp8hD+4Z6B$t)6l3Z_a-^n0<-ALl^%_HFEynAtJXLG#Aw50HB~E^m3c75P z61sw~Ufb2ELr?){*;)9M4G%3g`B>1N?)V~f$ull2q(u!Aj~N|u z(5Vq(5&X27;v){645aB?nHJlLxC!{E$S4yl_Pj_BKHi7mNq9G915TgPY(i!_PcMqQ zTn3vrh$h&!ddD5hh1SrpI`%awcd8@Xw#1vjG|m1{B-A2DvS zZVXuIF-gD3y?L|*R)AB>zW;eG4I`1XvZ*imsj2U2^de}AlsC!BH?x`9B_<(jK;vZg z4p}s|Qh1HCIVVnZD5rD|m2T&=C}mh7X>zkBjC6A7lxSAerI)#MqYPRSX10obXysGR zy-?2C+XbsIFUVe>$26Hm2}`klM6t+dzRGlOoq05=VD^sqJbnE{3oJhY<#!dxn!K?g za!ddlPxnl!$m|u8iRt*pC_2Q1KhS84{o~eVuKU<}g>}IQAaQBfajyDoS^uY=P+A1F=$G^Gg}MmC^Asl zs1%5&%7)612_zMb8I=>bSuwwJnIGa`Rj+VXECgb;V^F;l=NWi{ zHfVD1){RcB_l{x30Z%cw!@*E&9g1I<~<@K7fG7-3x#?~+MjK3hSR7oUPV zZlR9m;8RhslfjpWv0F31=*A_TOoeH-Qc^3_R24M1F{OBU=p-vdWnnNY}~20Qjv!C?L@L7i#$0w9S2k23y;+rcK2*}zEWQA zJhnE}bTOnSP(hD(=84Kn{JSaPLfWV>UBTRl<_~4p)R&{F3T!tv(#x8LU6Sp@;cS)Z zSG*M`JpFl9g{TtdFiKtFJqopxtc)6%gQNHt7u!~!lfpN&^M#GbogwLrj)+vHtqA%4 zBS8zbs3#4$+7oEAt9EZWfcva9ln`~MDdwy<(;{%u9Orl-QNl(?)je!|(w)brjx`M} zXRrhSg}nodTq*O(l$A*`e03t9(Wg$5W$>`WRe9B|a-Ufzwn(}#cBSf%KK+$WEc*l3 zI(_uFCRqeI;zgZUro;Sow@ghDuz~9>}fmO>bTKiqXq#6OI^r-?MBV zcR(f;&5Ru3txv74WBX1Yb%AX&A00>dwZckRjaHTPaIV3pj%np@fy-S%N z(T4>pUZ_l!p;^rIK^me>TcE7iikRj>4#0;3XC^&v+Fb})><@@#M-}uZwcUZ~x2*VS z1PnND&b-$DuW01+(F%Ld}^(B zF&bC#@|=ggr6J|G@K*5~;V*4x62&MvpS*f(;v@(&Fj(;qLT=l%CK(g1rf2!wZD=;x z6%D+1BE^8kIo7!)YMq&$qdzvGaC^?(F%Jg8?{H$PrwfY}E1s+tp)q^Q9irb`Ut!bd zxYP>~HyUizHz@K8(zh&9$+l0U^Tu0w7?x;7w3>{qLW;GuyZ5Hr81nZRCZf(vJI295 zB+yD>m_wi=3=);nxH=727&BPH50J z1Ff!uC`V4fj-L2t;Mj^oj`$Y5(vGJO1HqyqF(VXX?gjb$;N}hEntvc+u!X=8SCS$m zs57H!1@S~nNU`E|l##0MqWk1Yb?Iy1;GOenl7ev)RBJ|Av2HQT3%+SQkChaPDqa^o zZ(K9-Z*y|Kf9q&K(WC=h>;A3u48A8pgjg%BmB2bCrRxDPS4_@s;D|o`L^~F`FhYML zI!vlK5OX8)0W`*KRll0YO7)SjfrCw28?lyq? zGgu9ebfk^d!VKF3mQ~B^>!hO2wD9c-@N?6W&v5OSpxqHQ+%iWrt2qgQJ}F^inK7gR zkMD4@U$BF0iLq>WT$8u2kAvV?4^x;z8YsK`I*tqG#ysg)3k(SCX=ck!WS4I_F-qaI zp4*}=Zowc~7T>#AW?5sAJZcJyqNkapg+-M#n-}EGjphA612D#HghBZ$m&3aif-s-Y<)9V1?E%LEhJgGzwH+k!SSXsAzez zfA*-rZm&e zQ7Eh6O;;OvbE2|U{LzltdrR*-yp7&Vp?pVrl%&{AmaV+llxD(|3&tbp4=7#lj&kj1 zqRD+83_r41?b9UTNaSJ_M5F>%W?pbaOODyVQ8?-(f|t92yeu%KWN4eZ-uUUb6 z>OkF~LX7L!9NprYK&B8I0|)*g=)PyeREoQpkpSYXW_dCpE7}SK=NbsaQeli%UiN94 z3RX$eDAb<4v%G>>`G4((rx}*BPaLe7wx0Y`HZ4((>959z=^HQp6qn_9c@p38 Date: Sun, 7 Aug 2016 22:52:40 +0200 Subject: [PATCH 323/606] 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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