Skip to content

Commit 45417fd

Browse files
committed
Cleanup of Candlestick related code.
1 parent 18d64c8 commit 45417fd

15 files changed

Lines changed: 347 additions & 319 deletions

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,7 @@ public enum RenderMode {
138138
private final Object renderSynch = new Object();
139139

140140
private HashMap<Class<? extends RendererType>, RendererType> renderers;
141-
142-
//private ArrayList<SeriesAndFormatter<SeriesType, FormatterType>> seriesRegistry;
143141
private SeriesRegistry<SeriesType, FormatterType> seriesRegistry;
144-
145142
private final ArrayList<PlotListener> listeners;
146143

147144
private Thread renderThread;
@@ -520,6 +517,23 @@ protected void notifyListenersAfterDraw(Canvas canvas) {
520517
}
521518
}
522519

520+
/**
521+
* Convenience method to add a multiple series at once using the same formatter.
522+
* If a problem is encountered, the method immediately returns false and the plot
523+
* will contain whatever series were added before the failure.
524+
* @param formatter
525+
* @param series
526+
* @return True if all series were successfully added, false otherwise.
527+
*/
528+
public synchronized boolean addSeries(FormatterType formatter, SeriesType... series) {
529+
for(SeriesType s : series) {
530+
if(!addSeries(s, formatter)) {
531+
return false;
532+
}
533+
}
534+
return true;
535+
}
536+
523537
/**
524538
* Add a new Series to the Plot.
525539
* @param series

androidplot-core/src/main/java/com/androidplot/candlestick/CandlestickFormatter.java

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,31 @@
2525
import com.androidplot.xy.XYSeriesFormatter;
2626

2727
/**
28-
* Format for drawing a value in a {@link CandlestickSeries}.
28+
* Format for drawing a value using {@link CandlestickRenderer}.
2929
*/
3030
public class CandlestickFormatter extends XYSeriesFormatter<XYRegionFormatter> {
3131

32-
private Paint wickPaint = getDefaultStrokePaint(1.5f, Color.GREEN);
33-
private Paint bodyFillPaint = getDefaultFillPaint(Color.YELLOW);
34-
private Paint bodyStrokePaint = getDefaultStrokePaint(1.5f, Color.GREEN);
35-
private Paint upperCapPaint = getDefaultStrokePaint(1.5f, Color.GREEN);
36-
private Paint lowerCapPaint = getDefaultStrokePaint(1.5f, Color.GREEN);
32+
private static final float DEFAULT_WIDTH_PIX = PixelUtils.dpToPix(10);
33+
private static final float DEFAULT_STROKE_PIX = PixelUtils.dpToPix(2);
3734

38-
private float bodyWidth = PixelUtils.dpToPix(10f);
39-
private float upperCapWidth = PixelUtils.dpToPix(10f);
40-
private float lowerCapWidth = PixelUtils.dpToPix(10f);
35+
private Paint wickPaint;
36+
private Paint risingBodyFillPaint;
37+
private Paint fallingBodyFillPaint;
38+
private Paint risingBodyStrokePaint;
39+
private Paint fallingBodyStrokePaint;
40+
private Paint upperCapPaint;
41+
private Paint lowerCapPaint;
42+
43+
private float bodyWidth = DEFAULT_WIDTH_PIX;
44+
private float upperCapWidth = DEFAULT_WIDTH_PIX;
45+
private float lowerCapWidth = DEFAULT_WIDTH_PIX;
46+
47+
private BodyStyle bodyStyle;
48+
49+
public enum BodyStyle {
50+
Square,
51+
Triangle
52+
}
4153

4254
protected static Paint getDefaultFillPaint(int color) {
4355
Paint p = new Paint();
@@ -46,14 +58,39 @@ protected static Paint getDefaultFillPaint(int color) {
4658
return p;
4759
}
4860

49-
protected static Paint getDefaultStrokePaint(float strokeDp, int color) {
61+
protected static Paint getDefaultStrokePaint(int color) {
5062
Paint p = new Paint();
5163
p.setStyle(Paint.Style.STROKE);
52-
p.setStrokeWidth(PixelUtils.dpToPix(strokeDp));
64+
p.setStrokeWidth(DEFAULT_STROKE_PIX);
5365
p.setColor(color);
66+
p.setAntiAlias(true);
5467
return p;
5568
}
5669

70+
public CandlestickFormatter() {
71+
this(getDefaultStrokePaint(Color.YELLOW),
72+
getDefaultFillPaint(Color.GREEN),
73+
getDefaultFillPaint(Color.RED),
74+
getDefaultStrokePaint(Color.GREEN),
75+
getDefaultStrokePaint(Color.RED),
76+
getDefaultStrokePaint(Color.YELLOW),
77+
getDefaultStrokePaint(Color.YELLOW),
78+
BodyStyle.Square);
79+
}
80+
81+
public CandlestickFormatter(Paint wickPaint, Paint risingBodyFillPaint, Paint fallingBodyFillPaint,
82+
Paint risingBodyStrokePaint, Paint fallingBodyStrokePaint,
83+
Paint upperCapPaint, Paint lowerCapPaint, BodyStyle bodyStyle) {
84+
setWickPaint(wickPaint);
85+
setRisingBodyFillPaint(risingBodyFillPaint);
86+
setFallingBodyFillPaint(fallingBodyFillPaint);
87+
setRisingBodyStrokePaint(risingBodyStrokePaint);
88+
setFallingBodyStrokePaint(fallingBodyStrokePaint);
89+
setUpperCapPaint(upperCapPaint);
90+
setLowerCapPaint(lowerCapPaint);
91+
setBodyStyle(bodyStyle);
92+
}
93+
5794
@Override
5895
public Class<? extends SeriesRenderer> getRendererClass() {
5996
return CandlestickRenderer.class;
@@ -72,20 +109,20 @@ public void setWickPaint(Paint wickPaint) {
72109
this.wickPaint = wickPaint;
73110
}
74111

75-
public Paint getBodyFillPaint() {
76-
return bodyFillPaint;
112+
public Paint getRisingBodyFillPaint() {
113+
return risingBodyFillPaint;
77114
}
78115

79-
public void setBodyFillPaint(Paint bodyFillPaint) {
80-
this.bodyFillPaint = bodyFillPaint;
116+
public void setRisingBodyFillPaint(Paint risingBodyFillPaint) {
117+
this.risingBodyFillPaint = risingBodyFillPaint;
81118
}
82119

83-
public Paint getBodyStrokePaint() {
84-
return bodyStrokePaint;
120+
public Paint getRisingBodyStrokePaint() {
121+
return risingBodyStrokePaint;
85122
}
86123

87-
public void setBodyStrokePaint(Paint bodyStrokePaint) {
88-
this.bodyStrokePaint = bodyStrokePaint;
124+
public void setRisingBodyStrokePaint(Paint risingBodyStrokePaint) {
125+
this.risingBodyStrokePaint = risingBodyStrokePaint;
89126
}
90127

91128
public Paint getUpperCapPaint() {
@@ -127,4 +164,28 @@ public float getUpperCapWidth() {
127164
public void setUpperCapWidth(float upperCapWidth) {
128165
this.upperCapWidth = upperCapWidth;
129166
}
167+
168+
public Paint getFallingBodyFillPaint() {
169+
return fallingBodyFillPaint;
170+
}
171+
172+
public void setFallingBodyFillPaint(Paint fallingBodyFillPaint) {
173+
this.fallingBodyFillPaint = fallingBodyFillPaint;
174+
}
175+
176+
public Paint getFallingBodyStrokePaint() {
177+
return fallingBodyStrokePaint;
178+
}
179+
180+
public void setFallingBodyStrokePaint(Paint fallingBodyStrokePaint) {
181+
this.fallingBodyStrokePaint = fallingBodyStrokePaint;
182+
}
183+
184+
public BodyStyle getBodyStyle() {
185+
return bodyStyle;
186+
}
187+
188+
public void setBodyStyle(BodyStyle bodyStyle) {
189+
this.bodyStyle = bodyStyle;
190+
}
130191
}

androidplot-core/src/main/java/com/androidplot/candlestick/CandlestickSeries.java renamed to androidplot-core/src/main/java/com/androidplot/candlestick/CandlestickMaker.java

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,26 @@
1616

1717
package com.androidplot.candlestick;
1818

19+
import com.androidplot.xy.XYPlot;
1920
import com.androidplot.xy.XYSeries;
2021

2122
/**
22-
* Series data for a candlestick chart.
23-
* Variables:
24-
* X - x value
25-
* Y - max value
26-
* Z - min value
27-
* A - open value
28-
* B - close value
29-
* For a description of candlestick charts see: https://en.wikipedia.org/wiki/Candlestick_chart
23+
* Helper utility to simplify the creation of of candlestick charts
3024
*/
31-
public interface CandlestickSeries extends XYSeries {
25+
public abstract class CandlestickMaker {
3226

33-
Number getA(int index);
34-
35-
Number getB(int index);
36-
37-
Number getZ(int index);
27+
/**
28+
* Adds a candlestick chart to the specified plot using the specified
29+
* high, low, open and close values.
30+
* @param plot
31+
* @param formatter
32+
* @param openVals
33+
* @param closeVals
34+
* @param highVals
35+
* @param lowVals
36+
*/
37+
public static void make(XYPlot plot, CandlestickFormatter formatter,
38+
XYSeries openVals, XYSeries closeVals, XYSeries highVals, XYSeries lowVals) {
39+
plot.addSeries(formatter, highVals, lowVals, openVals, closeVals);
40+
}
3841
}

androidplot-core/src/main/java/com/androidplot/candlestick/CandlestickRenderer.java

Lines changed: 77 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,86 +16,119 @@
1616

1717
package com.androidplot.candlestick;
1818

19-
import android.graphics.Canvas;
20-
import android.graphics.PointF;
21-
import android.graphics.RectF;
22-
import com.androidplot.exception.PlotRenderException;
19+
import android.graphics.*;
2320
import com.androidplot.ui.RenderStack;
21+
import com.androidplot.ui.SeriesAndFormatter;
2422
import com.androidplot.util.ValPixConverter;
23+
import com.androidplot.xy.ComplexRenderer;
2524
import com.androidplot.xy.XYPlot;
26-
import com.androidplot.xy.XYSeriesRenderer;
25+
import com.androidplot.xy.XYSeries;
26+
27+
import java.util.List;
2728

2829
/**
29-
* Renders {@link CandlestickSeries} data into an {@link com.androidplot.xy.XYPlot}.
30+
* Renders a group of {@link com.androidplot.xy.XYSeries} as a candlestick chart
31+
* into an {@link com.androidplot.xy.XYPlot}.
32+
*
33+
* Constraints:
34+
* - Exactly four series must be added using the same {@link CandlestickFormatter}.
35+
* - Each of the four series has the same x(i) value.
36+
* - Expects that series are added in the order of:
37+
* high, low, open, close
38+
*
39+
* {@link CandlestickMaker} simplified methods for setting up a candlestick chart.
3040
*/
31-
public class CandlestickRenderer<FormatterType extends CandlestickFormatter> extends XYSeriesRenderer<CandlestickSeries, FormatterType> {
41+
public class CandlestickRenderer<FormatterType extends CandlestickFormatter> extends ComplexRenderer<FormatterType> {
42+
43+
private static final int HIGH_INDEX = 0;
44+
private static final int LOW_INDEX = 1;
45+
private static final int OPEN_INDEX = 2;
46+
private static final int CLOSE_INDEX = 3;
3247

3348
public CandlestickRenderer(XYPlot plot) {
3449
super(plot);
3550
}
3651

52+
3753
@Override
38-
public void onRender(Canvas canvas, RectF plotArea, CandlestickSeries series, FormatterType formatter, RenderStack stack)
39-
throws PlotRenderException {
40-
for(int i = 0; i < series.size(); i++) {
41-
Number y = series.getY(i);
42-
Number x = series.getX(i);
43-
Number z = series.getZ(i);
44-
Number a = series.getA(i);
45-
Number b = series.getB(i);
46-
drawValue(canvas, plotArea, formatter, x, y, z, a, b);
54+
public void onRender(Canvas canvas, RectF plotArea, List<SeriesAndFormatter<XYSeries,
55+
? extends FormatterType>> sfList, int seriesSize, RenderStack stack) {
56+
57+
for(int i = 0; i < seriesSize; i++) {
58+
59+
// x-val for all series should be identical so just grab x from the first series:
60+
Number x = sfList.get(HIGH_INDEX).getSeries().getX(i);
61+
62+
Number high = sfList.get(HIGH_INDEX).getSeries().getY(i);
63+
Number low = sfList.get(LOW_INDEX).getSeries().getY(i);
64+
Number open = sfList.get(OPEN_INDEX).getSeries().getY(i);
65+
Number close = sfList.get(CLOSE_INDEX).getSeries().getY(i);
66+
drawValue(canvas, plotArea, sfList.get(0).getFormatter(), x, high, low, open, close);
4767
}
4868
}
4969

5070
protected void drawValue(Canvas canvas, RectF plotArea, FormatterType formatter,
51-
Number x, Number y, Number z, Number a, Number b) {
52-
final PointF yPix = ValPixConverter.valToPix(
53-
x, y,
71+
Number x, Number high, Number low, Number open, Number close) {
72+
final PointF highPix = ValPixConverter.valToPix(
73+
x, high,
5474
plotArea,
5575
getPlot().getCalculatedMinX(),
5676
getPlot().getCalculatedMaxX(),
5777
getPlot().getCalculatedMinY(),
5878
getPlot().getCalculatedMaxY());
5979

60-
final PointF zPix = ValPixConverter.valToPix(
61-
x, z,
80+
final PointF lowPix = ValPixConverter.valToPix(
81+
x, low,
6282
plotArea,
6383
getPlot().getCalculatedMinX(),
6484
getPlot().getCalculatedMaxX(),
6585
getPlot().getCalculatedMinY(),
6686
getPlot().getCalculatedMaxY());
6787

68-
final PointF aPix = ValPixConverter.valToPix(
69-
x, a,
88+
final PointF openPix = ValPixConverter.valToPix(
89+
x, open,
7090
plotArea,
7191
getPlot().getCalculatedMinX(),
7292
getPlot().getCalculatedMaxX(),
7393
getPlot().getCalculatedMinY(),
7494
getPlot().getCalculatedMaxY());
7595

76-
final PointF bPix = ValPixConverter.valToPix(
77-
x, b,
96+
final PointF closePix = ValPixConverter.valToPix(
97+
x, close,
7898
plotArea,
7999
getPlot().getCalculatedMinX(),
80100
getPlot().getCalculatedMaxX(),
81101
getPlot().getCalculatedMinY(),
82102
getPlot().getCalculatedMaxY());
83103

84-
drawWick(canvas, zPix, yPix, formatter);
85-
drawBody(canvas, bPix, aPix, formatter);
86-
drawUpperCap(canvas, yPix, formatter);
87-
drawLowerCap(canvas, zPix, formatter);
104+
drawWick(canvas, highPix, lowPix, formatter);
105+
drawBody(canvas, openPix, closePix, formatter);
106+
drawUpperCap(canvas, highPix, formatter);
107+
drawLowerCap(canvas, lowPix, formatter);
88108
}
89109

90110
protected void drawWick(Canvas canvas, PointF min, PointF max, FormatterType formatter) {
91111
canvas.drawLine(min.x, min.y, max.x, max.y, formatter.getWickPaint());
92112
}
93113

94-
protected void drawBody(Canvas canvas, PointF min, PointF max, FormatterType formatter) {
114+
protected void drawBody(Canvas canvas, PointF open, PointF close, FormatterType formatter) {
95115
final float halfWidth = formatter.getBodyWidth() / 2;
96-
final RectF rect = new RectF(min.x - halfWidth, min.y, max.x + halfWidth, max.y);
97-
canvas.drawRect(rect, formatter.getBodyFillPaint());
98-
canvas.drawRect(rect, formatter.getBodyStrokePaint());
116+
final RectF rect = new RectF(open.x - halfWidth, open.y, close.x + halfWidth, close.y);
117+
118+
Paint bodyFillPaint = open.y >= close.y ?
119+
formatter.getRisingBodyFillPaint() : formatter.getFallingBodyFillPaint();
120+
121+
Paint bodyStrokePaint = open.y >= close.y ?
122+
formatter.getRisingBodyStrokePaint() : formatter.getFallingBodyStrokePaint();
123+
124+
switch(formatter.getBodyStyle()) {
125+
case Square:
126+
canvas.drawRect(rect, bodyFillPaint);
127+
canvas.drawRect(rect, bodyStrokePaint);
128+
break;
129+
case Triangle:
130+
drawTriangle(canvas, rect, bodyFillPaint, bodyStrokePaint);
131+
}
99132
}
100133

101134
protected void drawUpperCap(Canvas canvas, PointF val, FormatterType formatter) {
@@ -112,4 +145,15 @@ protected void drawLowerCap(Canvas canvas, PointF val, FormatterType formatter)
112145
protected void doDrawLegendIcon(Canvas canvas, RectF rect, FormatterType formatter) {
113146
// TODO
114147
}
148+
149+
protected void drawTriangle(Canvas canvas, RectF rect,
150+
Paint fillPaint, Paint strokePaint) {
151+
Path path = new Path();
152+
path.moveTo(rect.centerX(), rect.bottom);
153+
path.lineTo(rect.left,rect.top);
154+
path.lineTo(rect.right, rect.top);
155+
path.close();
156+
canvas.drawPath(path, fillPaint);
157+
canvas.drawPath(path, strokePaint);
158+
}
115159
}

0 commit comments

Comments
 (0)