/** * GraphView * 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; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import com.jjoe64.graphview.GraphView; import java.util.Iterator; /** * Series that plots the data as points. * The points can be different shapes or a * complete custom drawing. * * @author jjoe64 */ public class PointsGraphSeries<E extends DataPointInterface> extends BaseSeries<E> { /** * interface to implement a custom * drawing for the data points. */ public static interface CustomShape { /** * called when drawing a single data point. * use the x and y coordinates to draw your * drawing at this point. * * @param canvas canvas to draw on * @param paint internal paint object. this has the correct color. * But you can use your own paint. * @param x x-coordinate the point has to be drawn to * @param y y-coordinate the point has to be drawn to * @param dataPoint the related data point */ void draw(Canvas canvas, Paint paint, float x, float y, DataPointInterface dataPoint); } /** * choose a predefined shape to draw for * each data point. * You can also draw a custom drawing via {@link com.jjoe64.graphview.series.PointsGraphSeries.CustomShape} */ public enum Shape { /** * draws a point / circle */ POINT, /** * draws a triangle */ TRIANGLE, /** * draws a rectangle */ RECTANGLE } /** * wrapped styles for this series */ private final class Styles { /** * this is used for the size of the shape that * will be drawn. * This is useless if you are using a custom shape. */ float size; /** * the shape that will be drawn for each point. */ Shape shape; } /** * wrapped styles */ private Styles mStyles; /** * internal paint object */ private Paint mPaint; /** * handler to use a custom drawing */ private CustomShape mCustomShape; /** * creates the series without data */ public PointsGraphSeries() { init(); } /** * creates the series with data * * @param data datapoints */ public PointsGraphSeries(E[] data) { super(data); init(); } /** * inits the internal objects * set the defaults */ protected void init() { mStyles = new Styles(); mStyles.size = 20f; mPaint = new Paint(); mPaint.setStrokeCap(Paint.Cap.ROUND); setShape(Shape.POINT); } /** * plot the data to the viewport * * @param graphView graphview * @param canvas canvas to draw on * @param isSecondScale whether it is the second scale */ @Override public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) { resetDataPoints(); // get data double maxX = graphView.getViewport().getMaxX(false); double minX = graphView.getViewport().getMinX(false); double maxY; double minY; if (isSecondScale) { maxY = graphView.getSecondScale().getMaxY(false); minY = graphView.getSecondScale().getMinY(false); } else { maxY = graphView.getViewport().getMaxY(false); minY = graphView.getViewport().getMinY(false); } Iterator<E> values = getValues(minX, maxX); // draw background double lastEndY = 0; double lastEndX = 0; // draw data mPaint.setColor(getColor()); double diffY = maxY - minY; double diffX = maxX - minX; float graphHeight = graphView.getGraphContentHeight(); float graphWidth = graphView.getGraphContentWidth(); float graphLeft = graphView.getGraphContentLeft(); float graphTop = graphView.getGraphContentTop(); lastEndY = 0; lastEndX = 0; float firstX = 0; int i=0; while (values.hasNext()) { E value = values.next(); double valY = value.getY() - minY; double ratY = valY / diffY; double y = graphHeight * ratY; double valX = value.getX() - minX; double ratX = valX / diffX; double x = graphWidth * ratX; double orgX = x; double orgY = y; // overdraw boolean overdraw = false; if (x > graphWidth) { // end right overdraw = true; } if (y < 0) { // end bottom overdraw = true; } 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); // draw data point if (!overdraw) { if (mCustomShape != null) { mCustomShape.draw(canvas, mPaint, endX, endY, value); } else if (mStyles.shape == Shape.POINT) { canvas.drawCircle(endX, endY, mStyles.size, mPaint); } else if (mStyles.shape == Shape.RECTANGLE) { canvas.drawRect(endX-mStyles.size, endY-mStyles.size, endX+mStyles.size, endY+mStyles.size, mPaint); } else if (mStyles.shape == Shape.TRIANGLE) { Point[] points = new Point[3]; points[0] = new Point((int)endX, (int)(endY-getSize())); points[1] = new Point((int)(endX+getSize()), (int)(endY+getSize()*0.67)); points[2] = new Point((int)(endX-getSize()), (int)(endY+getSize()*0.67)); drawArrows(points, canvas, mPaint); } } i++; } } /** * helper to draw triangle * * @param point array with 3 coordinates * @param canvas canvas to draw on * @param paint paint object */ private void drawArrows(Point[] point, Canvas canvas, Paint paint) { float [] points = new float[8]; points[0] = point[0].x; points[1] = point[0].y; points[2] = point[1].x; points[3] = point[1].y; points[4] = point[2].x; points[5] = point[2].y; points[6] = point[0].x; points[7] = point[0].y; canvas.drawVertices(Canvas.VertexMode.TRIANGLES, 8, points, 0, null, 0, null, 0, null, 0, 0, paint); Path path = new Path(); path.moveTo(point[0].x , point[0].y); path.lineTo(point[1].x,point[1].y); path.lineTo(point[2].x,point[2].y); canvas.drawPath(path,paint); } /** * This is used for the size of the shape that * will be drawn. * This is useless if you are using a custom shape. * * @return the size of the shape */ public float getSize() { return mStyles.size; } /** * This is used for the size of the shape that * will be drawn. * This is useless if you are using a custom shape. * * @param radius the size of the shape */ public void setSize(float radius) { mStyles.size = radius; } /** * @return the shape that will be drawn for each point */ public Shape getShape() { return mStyles.shape; } /** * @param s the shape that will be drawn for each point */ public void setShape(Shape s) { mStyles.shape = s; } /** * Use a custom handler to draw your own * drawing for each data point. * * @param shape handler to use a custom drawing */ public void setCustomShape(CustomShape shape) { mCustomShape = shape; } @Override public void drawSelection(GraphView mGraphView, Canvas canvas, boolean b, DataPointInterface value) { // TODO } }