Skip to content

Commit a4759fb

Browse files
committed
- Added AdvancedLineAndPointRenderer and ECGExample to demonstrate its capabilities.
- Additional safety checks added to Plot and Redrawer to avoid potential memory leaks.
1 parent 0a2c4d5 commit a4759fb

9 files changed

Lines changed: 448 additions & 24 deletions

File tree

androidplot-core/src/main/java/com/androidplot/Plot.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
* Base class for all Plot implementations.
4545
*/
4646
public abstract class Plot<SeriesType extends Series, FormatterType extends Formatter, RendererType extends SeriesRenderer>
47-
extends View implements Resizable{
47+
extends View implements Resizable {
4848
private static final String TAG = Plot.class.getName();
4949
private static final String XML_ATTR_PREFIX = "androidplot";
5050
private static final String BASE_PACKAGE = "com.androidplot.";
@@ -167,7 +167,7 @@ public enum RenderMode {
167167
* Any rendering that utilizes a buffer from this class should synchronize rendering on the instance of this class
168168
* that is being used.
169169
*/
170-
private class BufferedCanvas {
170+
private static class BufferedCanvas {
171171
private volatile Bitmap bgBuffer; // all drawing is done on this buffer.
172172
private volatile Bitmap fgBuffer;
173173
private Canvas canvas = new Canvas();
@@ -192,6 +192,14 @@ public synchronized void resize(int h, int w) {
192192
}
193193
}
194194

195+
public void recycle() {
196+
bgBuffer.recycle();
197+
bgBuffer = null;
198+
199+
fgBuffer.recycle();
200+
fgBuffer = null;
201+
System.gc();
202+
}
195203

196204
/**
197205
* Get a Canvas for drawing. Actual drawing should be synchronized on the instance
@@ -368,6 +376,7 @@ public void run() {
368376
}
369377
}
370378
}
379+
pingPong.recycle();
371380
}
372381
});
373382
}

androidplot-core/src/main/java/com/androidplot/util/Redrawer.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,37 +19,48 @@
1919
import android.util.Log;
2020
import com.androidplot.Plot;
2121

22+
import java.lang.ref.WeakReference;
2223
import java.util.ArrayList;
2324
import java.util.Arrays;
2425
import java.util.List;
2526

2627
/**
2728
* Utility class for invoking Plot.redraw() on a background thread
28-
* at a set frequency.
29+
* at a set frequency. Callers should be sure to create a separate thread from which to use this class.
2930
*/
3031
public class Redrawer implements Runnable {
3132

3233
private static final int ONE_SECOND_MS = 1000;
3334

3435
private static final String TAG = Redrawer.class.getName();
3536

36-
private List<Plot> plots;
37+
private List<WeakReference<Plot>> plots;
3738
private long sleepTime;
39+
40+
// used to temporarily pause rendering without disposing of the run thread
3841
private boolean keepRunning;
42+
43+
// when set to false, run thread will be allowed to exit the main run loop
3944
private boolean keepAlive;
4045

46+
private Thread thread;
47+
4148
/**
4249
*
4350
* @param plots List of Plot instances to be redrawn
4451
* @param maxRefreshRate Desired frequency at which to redraw plots.
4552
* @param startImmediately If true, invokes run() immediately after construction.
4653
*/
4754
public Redrawer(List<Plot> plots, float maxRefreshRate, boolean startImmediately) {
48-
this.plots = plots;
55+
this.plots = new ArrayList<>();
56+
for(Plot plot : plots) {
57+
this.plots.add(new WeakReference<>(plot));
58+
}
4959
setMaxRefreshRate(maxRefreshRate);
50-
new Thread(this).start();
60+
thread = new Thread(this);
61+
thread.start();
5162
if(startImmediately) {
52-
run();
63+
start();
5364
}
5465
}
5566

@@ -97,8 +108,8 @@ public void run() {
97108
// TODO: record start and end timestamps and
98109
// TODO: calculate sleepTime from that, in order to more accurately
99110
// TODO: meet desired refresh rate.
100-
for(Plot plot : plots) {
101-
plot.redraw();
111+
for(WeakReference<Plot> plotRef : plots) {
112+
plotRef.get().redraw();
102113
}
103114
synchronized (this) {
104115
wait(sleepTime);
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Copyright 2016 AndroidPlot.com
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.androidplot.xy;
18+
19+
import android.graphics.*;
20+
import com.androidplot.exception.PlotRenderException;
21+
import com.androidplot.ui.RenderStack;
22+
import com.androidplot.ui.SeriesRenderer;
23+
import com.androidplot.util.ValPixConverter;
24+
import java.util.ArrayList;
25+
26+
/**
27+
* This is an experimental (but stable) implementation of an {@link XYSeriesRenderer} that provides instrumentation
28+
* allowing advanced behaviors like dynamically coloring / styling individual segments of a series, etc. This class
29+
* may be removed or renamed in future releases.
30+
* Currently has the following constraints:
31+
* - Interpolation is not supported
32+
* - Only draws lines; no points or fill
33+
* - Draws series lines using simple Canvas.drawLine(...) invocations.
34+
* @since 0.9.9
35+
*/
36+
public class AdvancedLineAndPointRenderer extends XYSeriesRenderer<XYSeries, AdvancedLineAndPointRenderer.Formatter> {
37+
38+
private int latestIndex;
39+
40+
public AdvancedLineAndPointRenderer(XYPlot plot) {
41+
super(plot);
42+
}
43+
44+
@Override
45+
protected void onRender(Canvas canvas, RectF plotArea, XYSeries series, Formatter formatter, RenderStack stack) throws PlotRenderException {
46+
PointF thisPoint;
47+
PointF lastPoint = null;
48+
ArrayList<PointF> points = new ArrayList<>(series.size());
49+
for (int i = 0; i < series.size(); i++) {
50+
Number y = series.getY(i);
51+
Number x = series.getX(i);
52+
53+
if (y != null && x != null) {
54+
thisPoint = ValPixConverter.valToPix(
55+
x, y,
56+
plotArea,
57+
getPlot().getCalculatedMinX(),
58+
getPlot().getCalculatedMaxX(),
59+
getPlot().getCalculatedMinY(),
60+
getPlot().getCalculatedMaxY());
61+
points.add(thisPoint);
62+
} else {
63+
thisPoint = null;
64+
}
65+
66+
// don't need to do any of this if the line isnt going to be drawn:
67+
if(formatter.getLinePaint() != null) {
68+
if (thisPoint != null && lastPoint != null) {
69+
canvas.drawLine(lastPoint.x, lastPoint.y, thisPoint.x, thisPoint.y, formatter.getLinePaint(i, latestIndex, series.size()));
70+
}
71+
}
72+
lastPoint = thisPoint;
73+
}
74+
}
75+
76+
@Override
77+
protected void doDrawLegendIcon(Canvas canvas, RectF rect, Formatter formatter) {
78+
if(formatter.getLinePaint() != null) {
79+
canvas.drawLine(rect.left, rect.bottom, rect.right, rect.top, formatter.getLinePaint());
80+
}
81+
}
82+
83+
public void setLatestIndex(int latestIndex) {
84+
this.latestIndex = latestIndex;
85+
}
86+
87+
88+
/**
89+
* Formatter designed to work in tandem with {@link AdvancedLineAndPointRenderer}.
90+
* @since 0.9.9
91+
*/
92+
public static class Formatter extends XYSeriesFormatter<XYRegionFormatter> {
93+
94+
private Paint linePaint;
95+
96+
{
97+
linePaint = new Paint();
98+
linePaint.setStrokeWidth(3);
99+
linePaint.setColor(Color.RED);
100+
}
101+
102+
@Override
103+
public Class<? extends SeriesRenderer> getRendererClass() {
104+
return AdvancedLineAndPointRenderer.class;
105+
}
106+
107+
@Override
108+
public SeriesRenderer getRendererInstance(XYPlot plot) {
109+
return new AdvancedLineAndPointRenderer(plot);
110+
}
111+
112+
public Paint getLinePaint() {
113+
return linePaint;
114+
}
115+
116+
/**
117+
* By default, simply returns the line paint as-is. May be overridden to provide custom behavior based
118+
* on input params.
119+
* @param thisIndex
120+
* @param latestIndex
121+
* @param seriesSize
122+
* @return
123+
*/
124+
public Paint getLinePaint(int thisIndex, int latestIndex, int seriesSize) {
125+
return getLinePaint();
126+
}
127+
128+
129+
public void setLinePaint(Paint linePaint) {
130+
this.linePaint = linePaint;
131+
}
132+
}
133+
}

androidplot-core/src/main/java/com/androidplot/xy/XYSeriesFormatter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public abstract class XYSeriesFormatter<XYRegionFormatterType extends XYRegionFo
2424
ZHash<RectRegion, XYRegionFormatterType> regions;
2525

2626
{
27-
regions = new ZHash<RectRegion, XYRegionFormatterType>();
27+
regions = new ZHash<>();
2828
}
2929

3030
public void addRegion(RectRegion region, XYRegionFormatterType regionFormatter) {

demoapp/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
<activity android:name=".TimeSeriesActivity"/>
9999
<activity android:name=".DualScaleXYPlotExampleActivity"/>
100100
<activity android:name=".XYPlotWithBgImgActivity"/>
101+
<activity android:name=".ECGExample"/>
101102

102103
<!-- receiver for demo app widget -->
103104
<receiver

0 commit comments

Comments
 (0)