.
+ * Copyright 2016 Jonas Gehring
+ *
+ * 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.
*/
package com.jjoe64.graphview.series;
@@ -23,6 +20,8 @@
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
+import androidx.core.view.ViewCompat;
+import android.view.animation.AccelerateInterpolator;
import com.jjoe64.graphview.GraphView;
@@ -35,6 +34,8 @@
* @author jjoe64
*/
public class LineGraphSeries extends BaseSeries {
+ private static final long ANIMATION_DURATION = 333;
+
/**
* wrapped styles regarding the line
*/
@@ -84,6 +85,8 @@ private final class Styles {
*/
private Styles mStyles;
+ private Paint mSelectionPaint;
+
/**
* internal paint object
*/
@@ -110,6 +113,39 @@ private final class Styles {
*/
private Paint mCustomPaint;
+ /**
+ * rendering is animated
+ */
+ private boolean mAnimated;
+
+ /**
+ * last animated value
+ */
+ private double mLastAnimatedValue = Double.NaN;
+
+ /**
+ * time of animation start
+ */
+ private long mAnimationStart;
+
+ /**
+ * animation interpolator
+ */
+ private AccelerateInterpolator mAnimationInterpolator;
+
+ /**
+ * number of animation frame to avoid lagging
+ */
+ private int mAnimationStartFrameNo;
+
+ /**
+ * flag whether the line should be drawn as a path
+ * or with single drawLine commands (more performance)
+ * By default we use drawLine because it has much more peformance.
+ * For some styling reasons it can make sense to draw as path.
+ */
+ private boolean mDrawAsPath = false;
+
/**
* creates a series without data
*/
@@ -120,7 +156,8 @@ public LineGraphSeries() {
/**
* creates a series with data
*
- * @param data data points
+ * @param data data points
+ * important: array has to be sorted from lowest x-value to the highest
*/
public LineGraphSeries(E[] data) {
super(data);
@@ -138,8 +175,14 @@ protected void init() {
mPaint.setStyle(Paint.Style.STROKE);
mPaintBackground = new Paint();
+ mSelectionPaint = new Paint();
+ mSelectionPaint.setColor(Color.argb(80, 0, 0, 0));
+ mSelectionPaint.setStyle(Paint.Style.FILL);
+
mPathBackground = new Path();
mPath = new Path();
+
+ mAnimationInterpolator = new AccelerateInterpolator(2f);
}
/**
@@ -161,8 +204,8 @@ public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) {
double maxY;
double minY;
if (isSecondScale) {
- maxY = graphView.getSecondScale().getMaxY();
- minY = graphView.getSecondScale().getMinY();
+ maxY = graphView.getSecondScale().getMaxY(false);
+ minY = graphView.getSecondScale().getMinY(false);
} else {
maxY = graphView.getViewport().getMaxY(false);
minY = graphView.getViewport().getMinY(false);
@@ -186,6 +229,8 @@ public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) {
paint = mPaint;
}
+ mPath.reset();
+
if (mStyles.drawBackground) {
mPathBackground.reset();
}
@@ -200,9 +245,20 @@ public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) {
lastEndY = 0;
lastEndX = 0;
+
+ // needed to end the path for background
double lastUsedEndX = 0;
- float firstX = 0;
- int i=0;
+ double lastUsedEndY = 0;
+ float firstX = -1;
+ float firstY = -1;
+ float lastRenderedX = Float.NaN;
+ int i = 0;
+ float lastAnimationReferenceX = graphLeft;
+
+ boolean sameXSkip = false;
+ float minYOnSameX = 0f;
+ float maxYOnSameX = 0f;
+
while (values.hasNext()) {
E value = values.next();
@@ -210,7 +266,8 @@ public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) {
double ratY = valY / diffY;
double y = graphHeight * ratY;
- double valX = value.getX() - minX;
+ double valueX = value.getX();
+ double valX = valueX - minX;
double ratX = valX / diffX;
double x = graphWidth * ratX;
@@ -219,80 +276,248 @@ public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) {
if (i > 0) {
// overdraw
+ boolean isOverdrawY = false;
+ boolean isOverdrawEndPoint = false;
+ boolean skipDraw = false;
+
if (x > graphWidth) { // end right
- double b = ((graphWidth - lastEndX) * (y - lastEndY)/(x - lastEndX));
- y = lastEndY+b;
+ double b = ((graphWidth - lastEndX) * (y - lastEndY) / (x - lastEndX));
+ y = lastEndY + b;
x = graphWidth;
+ isOverdrawEndPoint = true;
}
if (y < 0) { // end bottom
- double b = ((0 - lastEndY) * (x - lastEndX)/(y - lastEndY));
- x = lastEndX+b;
+ // skip when previous and this point is out of bound
+ if (lastEndY < 0) {
+ skipDraw = true;
+ } else {
+ double b = ((0 - lastEndY) * (x - lastEndX) / (y - lastEndY));
+ x = lastEndX + b;
+ }
y = 0;
+ isOverdrawY = isOverdrawEndPoint = true;
}
if (y > graphHeight) { // end top
- double b = ((graphHeight - lastEndY) * (x - lastEndX)/(y - lastEndY));
- x = lastEndX+b;
+ // skip when previous and this point is out of bound
+ if (lastEndY > graphHeight) {
+ skipDraw = true;
+ } else {
+ double b = ((graphHeight - lastEndY) * (x - lastEndX) / (y - lastEndY));
+ x = lastEndX + b;
+ }
y = graphHeight;
- }
- if (lastEndY < 0) { // start bottom
- double b = ((0 - y) * (x - lastEndX)/(lastEndY - y));
- lastEndX = x-b;
- lastEndY = 0;
+ isOverdrawY = isOverdrawEndPoint = true;
}
if (lastEndX < 0) { // start left
- double b = ((0 - x) * (y - lastEndY)/(lastEndX - x));
- lastEndY = y-b;
+ double b = ((0 - x) * (y - lastEndY) / (lastEndX - x));
+ lastEndY = y - b;
lastEndX = 0;
}
+
+ // we need to save the X before it will be corrected when overdraw y
+ float orgStartX = (float) lastEndX + (graphLeft + 1);
+
+ if (lastEndY < 0) { // start bottom
+ if (!skipDraw) {
+ double b = ((0 - y) * (x - lastEndX) / (lastEndY - y));
+ lastEndX = x - b;
+ }
+ lastEndY = 0;
+ isOverdrawY = true;
+ }
if (lastEndY > graphHeight) { // start top
- double b = ((graphHeight - y) * (x - lastEndX)/(lastEndY - y));
- lastEndX = x-b;
+ // skip when previous and this point is out of bound
+ if (!skipDraw) {
+ double b = ((graphHeight - y) * (x - lastEndX) / (lastEndY - y));
+ lastEndX = x - b;
+ }
lastEndY = graphHeight;
+ isOverdrawY = true;
}
float startX = (float) lastEndX + (graphLeft + 1);
float startY = (float) (graphTop - lastEndY) + graphHeight;
float endX = (float) x + (graphLeft + 1);
float endY = (float) (graphTop - y) + graphHeight;
+ float startXAnimated = startX;
+ float endXAnimated = endX;
+
+ if (endX < startX) {
+ // dont draw from right to left
+ skipDraw = true;
+ }
+
+ // NaN can happen when previous and current value is out of y bounds
+ if (!skipDraw && !Float.isNaN(startY) && !Float.isNaN(endY)) {
+ // animation
+ if (mAnimated) {
+ if ((Double.isNaN(mLastAnimatedValue) || mLastAnimatedValue < valueX)) {
+ long currentTime = System.currentTimeMillis();
+ if (mAnimationStart == 0) {
+ // start animation
+ mAnimationStart = currentTime;
+ mAnimationStartFrameNo = 0;
+ } else {
+ // anti-lag: wait a few frames
+ if (mAnimationStartFrameNo < 15) {
+ // second time
+ mAnimationStart = currentTime;
+ mAnimationStartFrameNo++;
+ }
+ }
+ float timeFactor = (float) (currentTime - mAnimationStart) / ANIMATION_DURATION;
+ float factor = mAnimationInterpolator.getInterpolation(timeFactor);
+ if (timeFactor <= 1.0) {
+ startXAnimated = (startX - lastAnimationReferenceX) * factor + lastAnimationReferenceX;
+ startXAnimated = Math.max(startXAnimated, lastAnimationReferenceX);
+ endXAnimated = (endX - lastAnimationReferenceX) * factor + lastAnimationReferenceX;
+ ViewCompat.postInvalidateOnAnimation(graphView);
+ } else {
+ // animation finished
+ mLastAnimatedValue = valueX;
+ }
+ } else {
+ lastAnimationReferenceX = endX;
+ }
+ }
+
+ // draw data point
+ if (!isOverdrawEndPoint) {
+ if (mStyles.drawDataPoints) {
+ // draw first datapoint
+ Paint.Style prevStyle = paint.getStyle();
+ paint.setStyle(Paint.Style.FILL);
+ canvas.drawCircle(endXAnimated, endY, mStyles.dataPointsRadius, paint);
+ paint.setStyle(prevStyle);
+ }
+ registerDataPoint(endX, endY, value);
+ }
+
+ if (mDrawAsPath) {
+ mPath.moveTo(startXAnimated, startY);
+ }
+ // performance opt.
+ if (Float.isNaN(lastRenderedX) || Math.abs(endX - lastRenderedX) > .3f) {
+ if (mDrawAsPath) {
+ mPath.lineTo(endXAnimated, endY);
+ } else {
+ // draw vertical lines that were skipped
+ if (sameXSkip) {
+ sameXSkip = false;
+ renderLine(canvas, new float[]{lastRenderedX, minYOnSameX, lastRenderedX, maxYOnSameX}, paint);
+ }
+ renderLine(canvas, new float[]{startXAnimated, startY, endXAnimated, endY}, paint);
+ }
+ lastRenderedX = endX;
+ } else {
+ // rendering on same x position
+ // save min+max y position and draw it as line
+ if (sameXSkip) {
+ minYOnSameX = Math.min(minYOnSameX, endY);
+ maxYOnSameX = Math.max(maxYOnSameX, endY);
+ } else {
+ // first
+ sameXSkip = true;
+ minYOnSameX = Math.min(startY, endY);
+ maxYOnSameX = Math.max(startY, endY);
+ }
+ }
- // draw data point
- if (mStyles.drawDataPoints) {
- //fix: last value was not drawn. Draw here now the end values
- canvas.drawCircle(endX, endY, mStyles.dataPointsRadius, paint);
}
- registerDataPoint(endX, endY, value);
- mPath.reset();
- mPath.moveTo(startX, startY);
- mPath.lineTo(endX, endY);
- canvas.drawPath(mPath, paint);
if (mStyles.drawBackground) {
- if (i==1) {
- firstX = startX;
- mPathBackground.moveTo(startX, startY);
+ if (isOverdrawY) {
+ // start draw original x
+ if (firstX == -1) {
+ firstX = orgStartX;
+ firstY = startY;
+ mPathBackground.moveTo(orgStartX, startY);
+ }
+ // from original start to new start
+ mPathBackground.lineTo(startXAnimated, startY);
+ }
+ if (firstX == -1) {
+ firstX = startXAnimated;
+ firstY = startY;
+ mPathBackground.moveTo(startXAnimated, startY);
}
- mPathBackground.lineTo(endX, endY);
+ mPathBackground.lineTo(startXAnimated, startY);
+ mPathBackground.lineTo(endXAnimated, endY);
}
- lastUsedEndX = endX;
+
+ lastUsedEndX = endXAnimated;
+ lastUsedEndY = endY;
} else if (mStyles.drawDataPoints) {
//fix: last value not drawn as datapoint. Draw first point here, and then on every step the end values (above)
float first_X = (float) x + (graphLeft + 1);
float first_Y = (float) (graphTop - y) + graphHeight;
- //TODO canvas.drawCircle(first_X, first_Y, dataPointsRadius, mPaint);
+
+ if (first_X >= graphLeft && first_Y <= (graphTop + graphHeight)) {
+ if (mAnimated && (Double.isNaN(mLastAnimatedValue) || mLastAnimatedValue < valueX)) {
+ long currentTime = System.currentTimeMillis();
+ if (mAnimationStart == 0) {
+ // start animation
+ mAnimationStart = currentTime;
+ }
+ float timeFactor = (float) (currentTime - mAnimationStart) / ANIMATION_DURATION;
+ float factor = mAnimationInterpolator.getInterpolation(timeFactor);
+ if (timeFactor <= 1.0) {
+ first_X = (first_X - lastAnimationReferenceX) * factor + lastAnimationReferenceX;
+ ViewCompat.postInvalidateOnAnimation(graphView);
+ } else {
+ // animation finished
+ mLastAnimatedValue = valueX;
+ }
+ }
+
+
+ Paint.Style prevStyle = paint.getStyle();
+ paint.setStyle(Paint.Style.FILL);
+ canvas.drawCircle(first_X, first_Y, mStyles.dataPointsRadius, paint);
+ paint.setStyle(prevStyle);
+ registerDataPoint(first_X, first_Y, value);
+ }
}
lastEndY = orgY;
lastEndX = orgX;
i++;
}
- if (mStyles.drawBackground) {
+ if (mDrawAsPath) {
+ // draw at the end
+ canvas.drawPath(mPath, paint);
+ }
+
+ if (mStyles.drawBackground && firstX != -1) {
// end / close path
- mPathBackground.lineTo((float) lastUsedEndX, graphHeight + graphTop);
+ if (lastUsedEndY != graphHeight + graphTop) {
+ // dont draw line to same point, otherwise the path is completely broken
+ mPathBackground.lineTo((float) lastUsedEndX, graphHeight + graphTop);
+ }
mPathBackground.lineTo(firstX, graphHeight + graphTop);
- mPathBackground.close();
+ if (firstY != graphHeight + graphTop) {
+ // dont draw line to same point, otherwise the path is completely broken
+ mPathBackground.lineTo(firstX, firstY);
+ }
+ //mPathBackground.close();
canvas.drawPath(mPathBackground, mPaintBackground);
}
+ }
+ /**
+ * just a wrapper to draw lines on canvas
+ *
+ * @param canvas
+ * @param pts
+ * @param paint
+ */
+ private void renderLine(Canvas canvas, float[] pts, Paint paint) {
+ if (pts.length == 4 && pts[0] == pts[2] && pts[1] == pts[3]) {
+ // avoid zero length lines, to makes troubles on some devices
+ // see https://github.com/appsthatmatter/GraphView/issues/499
+ return;
+ }
+ canvas.drawLines(pts, paint);
}
/**
@@ -380,7 +605,7 @@ public void setDataPointsRadius(float dataPointsRadius) {
}
/**
- * @return the background color for the filling under
+ * @return the background color for the filling under
* the line.
* @see #setDrawBackground(boolean)
*/
@@ -406,4 +631,84 @@ public void setBackgroundColor(int backgroundColor) {
public void setCustomPaint(Paint customPaint) {
this.mCustomPaint = customPaint;
}
+
+ /**
+ * @param animated activate the animated rendering
+ */
+ public void setAnimated(boolean animated) {
+ this.mAnimated = animated;
+ }
+
+ /**
+ * flag whether the line should be drawn as a path
+ * or with single drawLine commands (more performance)
+ * By default we use drawLine because it has much more peformance.
+ * For some styling reasons it can make sense to draw as path.
+ */
+ public boolean isDrawAsPath() {
+ return mDrawAsPath;
+ }
+
+ /**
+ * flag whether the line should be drawn as a path
+ * or with single drawLine commands (more performance)
+ * By default we use drawLine because it has much more peformance.
+ * For some styling reasons it can make sense to draw as path.
+ *
+ * @param mDrawAsPath true to draw as path
+ */
+ public void setDrawAsPath(boolean mDrawAsPath) {
+ this.mDrawAsPath = mDrawAsPath;
+ }
+
+ /**
+ *
+ * @param dataPoint values the values must be in the correct order!
+ * x-value has to be ASC. First the lowest x value and at least the highest x value.
+ * @param scrollToEnd true => graphview will scroll to the end (maxX)
+ * @param maxDataPoints if max data count is reached, the oldest data
+ * value will be lost to avoid memory leaks
+ * @param silent set true to avoid rerender the graph
+ */
+ public void appendData(E dataPoint, boolean scrollToEnd, int maxDataPoints, boolean silent) {
+ if (!isAnimationActive()) {
+ mAnimationStart = 0;
+ }
+ super.appendData(dataPoint, scrollToEnd, maxDataPoints, silent);
+ }
+
+ /**
+ * @return currently animation is active
+ */
+ private boolean isAnimationActive() {
+ if (mAnimated) {
+ long curr = System.currentTimeMillis();
+ return curr - mAnimationStart <= ANIMATION_DURATION;
+ }
+ return false;
+ }
+
+ @Override
+ public void drawSelection(GraphView graphView, Canvas canvas, boolean b, DataPointInterface value) {
+ double spanX = graphView.getViewport().getMaxX(false) - graphView.getViewport().getMinX(false);
+ double spanXPixel = graphView.getGraphContentWidth();
+
+ double spanY = graphView.getViewport().getMaxY(false) - graphView.getViewport().getMinY(false);
+ double spanYPixel = graphView.getGraphContentHeight();
+
+ double pointX = (value.getX() - graphView.getViewport().getMinX(false)) * spanXPixel / spanX;
+ pointX += graphView.getGraphContentLeft();
+
+ double pointY = (value.getY() - graphView.getViewport().getMinY(false)) * spanYPixel / spanY;
+ pointY = graphView.getGraphContentTop() + spanYPixel - pointY;
+
+ // border
+ canvas.drawCircle((float) pointX, (float) pointY, 30f, mSelectionPaint);
+
+ // fill
+ Paint.Style prevStyle = mPaint.getStyle();
+ mPaint.setStyle(Paint.Style.FILL);
+ canvas.drawCircle((float) pointX, (float) pointY, 23f, mPaint);
+ mPaint.setStyle(prevStyle);
+ }
}
diff --git a/src/main/java/com/jjoe64/graphview/series/OnDataPointTapListener.java b/src/main/java/com/jjoe64/graphview/series/OnDataPointTapListener.java
index 748a1122e..e846e329e 100644
--- a/src/main/java/com/jjoe64/graphview/series/OnDataPointTapListener.java
+++ b/src/main/java/com/jjoe64/graphview/series/OnDataPointTapListener.java
@@ -1,21 +1,18 @@
/**
* GraphView
- * Copyright (C) 2014 Jonas Gehring
+ * Copyright 2016 Jonas Gehring
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License,
- * with the "Linking Exception", which can be found at the license.txt
- * file in this program.
+ * 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
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * You should have received a copy of the GNU General Public License
- * with the "Linking Exception" along with this program; if not,
- * write to the author Jonas Gehring .
+ * 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.
*/
package com.jjoe64.graphview.series;
diff --git a/src/main/java/com/jjoe64/graphview/series/PointsGraphSeries.java b/src/main/java/com/jjoe64/graphview/series/PointsGraphSeries.java
index c57d476d7..2ec74a526 100644
--- a/src/main/java/com/jjoe64/graphview/series/PointsGraphSeries.java
+++ b/src/main/java/com/jjoe64/graphview/series/PointsGraphSeries.java
@@ -1,21 +1,18 @@
/**
* GraphView
- * Copyright (C) 2014 Jonas Gehring
+ * Copyright 2016 Jonas Gehring
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License,
- * with the "Linking Exception", which can be found at the license.txt
- * file in this program.
+ * 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
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * You should have received a copy of the GNU General Public License
- * with the "Linking Exception" along with this program; if not,
- * write to the author Jonas Gehring .
+ * 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.
*/
package com.jjoe64.graphview.series;
@@ -44,7 +41,7 @@ public class PointsGraphSeries extends BaseSeries<
public static interface CustomShape {
/**
* called when drawing a single data point.
- * use the x and y coordinates to render your
+ * use the x and y coordinates to draw your
* drawing at this point.
*
* @param canvas canvas to draw on
@@ -58,9 +55,9 @@ public static interface CustomShape {
}
/**
- * choose a predefined shape to render for
+ * choose a predefined shape to draw for
* each data point.
- * You can also render a custom drawing via {@link com.jjoe64.graphview.series.PointsGraphSeries.CustomShape}
+ * You can also draw a custom drawing via {@link com.jjoe64.graphview.series.PointsGraphSeries.CustomShape}
*/
public enum Shape {
/**
@@ -158,8 +155,8 @@ public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) {
double maxY;
double minY;
if (isSecondScale) {
- maxY = graphView.getSecondScale().getMaxY();
- minY = graphView.getSecondScale().getMinY();
+ maxY = graphView.getSecondScale().getMaxY(false);
+ minY = graphView.getSecondScale().getMinY(false);
} else {
maxY = graphView.getViewport().getMaxY(false);
minY = graphView.getViewport().getMinY(false);
@@ -211,7 +208,11 @@ public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) {
if (y > graphHeight) { // end top
overdraw = true;
}
-
+ /* Fix a bug that continue to show the DOT after Y axis */
+ if(x < 0) {
+ overdraw = true;
+ }
+
float endX = (float) x + (graphLeft + 1);
float endY = (float) (graphTop - y) + graphHeight;
registerDataPoint(endX, endY, value);
@@ -239,7 +240,7 @@ public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) {
}
/**
- * helper to render triangle
+ * helper to draw triangle
*
* @param point array with 3 coordinates
* @param canvas canvas to draw on
@@ -301,7 +302,7 @@ public void setShape(Shape s) {
}
/**
- * Use a custom handler to render your own
+ * Use a custom handler to draw your own
* drawing for each data point.
*
* @param shape handler to use a custom drawing
@@ -309,4 +310,9 @@ public void setShape(Shape s) {
public void setCustomShape(CustomShape shape) {
mCustomShape = shape;
}
+
+ @Override
+ public void drawSelection(GraphView mGraphView, Canvas canvas, boolean b, DataPointInterface value) {
+ // TODO
+ }
}
diff --git a/src/main/java/com/jjoe64/graphview/series/Series.java b/src/main/java/com/jjoe64/graphview/series/Series.java
index dce32eb78..95e82a7e1 100644
--- a/src/main/java/com/jjoe64/graphview/series/Series.java
+++ b/src/main/java/com/jjoe64/graphview/series/Series.java
@@ -1,21 +1,18 @@
/**
* GraphView
- * Copyright (C) 2014 Jonas Gehring
+ * Copyright 2016 Jonas Gehring
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License,
- * with the "Linking Exception", which can be found at the license.txt
- * file in this program.
+ * 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
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * You should have received a copy of the GNU General Public License
- * with the "Linking Exception" along with this program; if not,
- * write to the author Jonas Gehring .
+ * 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.
*/
package com.jjoe64.graphview.series;
@@ -122,4 +119,11 @@ public interface Series {
* @return whether there are data points
*/
boolean isEmpty();
+
+ /**
+ * clear reference to view and activity
+ *
+ * @param graphView
+ */
+ void clearReference(GraphView graphView);
}
diff --git a/zooming.gif b/zooming.gif
new file mode 100644
index 000000000..2b3dc215c
Binary files /dev/null and b/zooming.gif differ