/*******************************************************************************
* Copyright 2013-2015 alladin-IT GmbH
*
* 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 at.alladin.rmbt.android.graphview;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import at.alladin.openrmbt.android.R;
import at.alladin.rmbt.android.util.net.NetworkUtil.MinMax;
import at.alladin.rmbt.util.NumberUtil;
/**
*
* @author lb
*
*/
public class CustomizableGraphView extends View implements GraphView {
public static class PositionedGraphLabel extends GraphView.GraphLabel {
private int x;
private int y;
public PositionedGraphLabel(String text, String color, int x, int y) {
super(text, color);
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public String toString() {
return "PositionedGraphLabel [x=" + x + ", y=" + y + ", color="
+ color + ", text=" + text + "]";
}
}
private boolean recycled;
private List<GraphService> graphs = new ArrayList<GraphService>();
private int width;
private int height;
private float scale = 1f;
private Paint bitmapPaint;
private int graphWidth;
private int graphHeight;
private float graphStrokeWidth;
private Bitmap genBackgroundBitmap;
private Bitmap gridBitmap;
private int gridCells = 7;
private float gridRows = 4;
private float gridX;
private float gridY;
private float graphX;
private float graphY;
private String title;
private List<GraphView.GraphLabel> labelHMinList = new ArrayList<GraphView.GraphLabel>();
private List<GraphView.GraphLabel> labelHMaxList = new ArrayList<GraphView.GraphLabel>();
private List<GraphView.GraphLabel> labelVMinList = new ArrayList<GraphView.GraphLabel>();
private List<GraphView.GraphLabel> labelVMaxList = new ArrayList<GraphView.GraphLabel>();
private List<GraphView.GraphLabel> labelRowList = new ArrayList<GraphView.GraphLabel>();
private List<GraphView.GraphLabel> labelInfoVerticalList = new ArrayList<GraphView.GraphLabel>();
private List<PositionedGraphLabel> labelList = new ArrayList<CustomizableGraphView.PositionedGraphLabel>();
private static String DEFAULT_H_LABEL_COLOR = "#C8ffffff";
private static String DEFAULT_V_LABEL_COLOR = "#C800f940";
private String labelInfoHorizontal;
private int internalPaddingTop;
private boolean showLog10Lines = true;
/**
* following parameters can be set in an xml layout file:
* <ul>
* <li>labelh - x-axis label</li>
* <li>labelh_min - x-axis min value</li>
* <li>labelh_max - x-axis max value</li>
* <li>labelv - y-axis label</li>
* <li>labelv_min - y-axis min value</li>
* <li>labelv_max - y-axis max value</li>
* <li>grid_cells - number of cells</li>
* <li>grid_rows - number of rows</li>
* <li>show_log10_lines - draws log10 grid lines for each row<li>
* </ul>
* @param context
* @param attrs
* @param defStyle
*/
public CustomizableGraphView(final Context context, final AttributeSet attrs, final int defStyle)
{
super(context, attrs, defStyle);
String labelHMin;
String labelHMax;
String labelVMin;
String labelVMax;
labelInfoHorizontal = getAttributeValue(attrs, "labelh", "->");
final String labelInfoVertical = getAttributeValue(attrs, "labelv", null);
if (labelInfoVertical != null) {
labelInfoVerticalList.add(new GraphView.GraphLabel(labelInfoVertical, DEFAULT_V_LABEL_COLOR));
}
labelHMin = getAttributeValue(attrs, "labelh_min", "0");
labelHMinList.add(new GraphView.GraphLabel(labelHMin, DEFAULT_H_LABEL_COLOR));
labelHMax = getAttributeValue(attrs, "labelh_max", "10");
labelHMaxList.add(new GraphView.GraphLabel(labelHMax, DEFAULT_H_LABEL_COLOR));
labelVMin = getAttributeValue(attrs, "labelv_min", null);
if (labelVMin != null) {
labelVMinList.add(new GraphView.GraphLabel(labelVMin, DEFAULT_V_LABEL_COLOR));
}
labelVMax = getAttributeValue(attrs, "labelv_max", null);
if (labelVMax != null) {
labelVMaxList.add(new GraphView.GraphLabel(labelVMax, DEFAULT_V_LABEL_COLOR));
}
this.gridCells = Integer.valueOf(getAttributeValue(attrs, "grid_cells", "7"));
this.gridRows = Integer.valueOf(getAttributeValue(attrs, "grid_rows", "4"));
this.showLog10Lines = Boolean.valueOf(getAttributeValue(attrs, "show_log10_lines", "true"));
title = getAttributeValue(attrs, "title", null);
repaint(context);
}
final int relW = 593;
final int relH = 237;
public void repaint(final Context context) {
final Resources res = context.getResources();
bitmapPaint = new Paint();
bitmapPaint.setFilterBitmap(true);
internalPaddingTop = 0;
final Bitmap backgroundBitmap = BitmapFactory.decodeResource(res, R.drawable.test_box_small);
width = backgroundBitmap.getWidth();
height = backgroundBitmap.getHeight();
if (genBackgroundBitmap != null) {
genBackgroundBitmap.recycle();
genBackgroundBitmap = null;
}
genBackgroundBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(genBackgroundBitmap);
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setTextSize(coordFH(15, relH));
for (int i = 0; i < labelHMinList.size(); i++) {
GraphView.GraphLabel label = labelHMinList.get(i);
paint.setColor(Color.parseColor(label.getColor()));
paint.setTextAlign(Align.LEFT);
canvas.drawText(label.getText(), coordFW(88 + i * 60, relW), coordFH(220, relH), paint);
}
for (int i = 0; i < labelHMaxList.size(); i++) {
GraphView.GraphLabel label = labelHMaxList.get(i);
paint.setColor(Color.parseColor(label.getColor()));
paint.setTextAlign(Align.RIGHT);
canvas.drawText(label.getText(), coordFW(567 - i * 60, relW), coordFH(220, relH), paint);
}
paint.setTextAlign(Align.CENTER);
canvas.drawText(labelInfoHorizontal, coordFW(326, relW), coordFH(220, relH), paint);
paint.setTextAlign(Align.LEFT);
paint.setColor(Color.parseColor("#C800f940"));
final int vLabelStartY = (labelInfoVerticalList.size() % 2 == 0 ? 140 : 130) - (20 * labelInfoVerticalList.size());
for (int i = 0; i < labelInfoVerticalList.size(); i++) {
final GraphView.GraphLabel label = labelInfoVerticalList.get(i);
paint.setColor(Color.parseColor(label.getColor()));
canvas.drawText(String.format("– %s", label.getText()), coordFW(2, relW), coordFH(vLabelStartY + i * 20, relH), paint);
}
for (int i = 0; i < labelList.size(); i++) {
final PositionedGraphLabel label = labelList.get(i);
paint.setColor(Color.parseColor(label.getColor()));
paint.setTextAlign(Align.LEFT);
canvas.drawText(label.getText(), label.getX(), label.getY(), paint);
}
paint.setTextAlign(Align.RIGHT);
for (int i = 0; i < labelVMinList.size(); i++) {
final GraphView.GraphLabel label = labelVMinList.get(i);
paint.setColor(Color.parseColor(label.getColor()));
canvas.drawText(label.getText(), coordFW(72, relW), coordFH(190 - i * 20, relH), paint);
}
for (int i = 0; i < labelVMaxList.size(); i++) {
final GraphView.GraphLabel label = labelVMaxList.get(i);
paint.setColor(Color.parseColor(label.getColor()));
canvas.drawText(label.getText(), coordFW(72, relW), coordFH(38 + i * 20, relH), paint);
}
paint.setColor(Color.parseColor("#C8f8a000"));
if (title != null) {
paint.setTextAlign(Align.LEFT);
paint.setTextSize(coordFH(18, relH));
//paint.setUnderlineText(true);
paint.setShadowLayer(0.5f, 2, 2, Color.parseColor("#FF000000"));
canvas.drawText(title, coordFW(112, relW), coordFH(38, relH), paint);
}
// gridBitmap = getBitmap(res, R.drawable.test_grid);
gridBitmap = generateGridBitmap();
gridX = coordFW(55, relW);
gridY = coordFH(16, relH);
graphX = coordFW(80, relW);
graphY = coordFH(16, relH);
graphWidth = coordW(493, relW);
graphHeight = coordH(183, relH);
graphStrokeWidth = coordFW(4, relW);
}
public CustomizableGraphView(final Context context, final AttributeSet attrs)
{
this(context, attrs, 0);
}
public CustomizableGraphView(final Context context)
{
this(context, null, 0);
}
protected Bitmap generateGridBitmap() {
return generateGridBitmap(null);
}
protected Bitmap generateGridBitmap(Canvas canvas) {
int x = 20;
int y = 0;
int h = 180;
int w = 520;
float startX = coordFW(x, relW);
float endX = coordFW(w, relW);
float startY = coordFH(y, relW);
float endY = coordFH(h, relH);
int partH = Math.round((float)(h-y) / (float)gridRows);
int partW = Math.round((float)(w-x) / (float)gridCells);
Bitmap grid = Bitmap.createBitmap(width, height, Config.ARGB_8888);
Canvas gridCanvas = new Canvas(grid);
Paint p = new Paint();
p.setColor(Color.WHITE);
p.setAlpha(100);
p.setStyle(Style.STROKE);
final Paint paintNames = new Paint();
paintNames.setAntiAlias(true);
paintNames.setTypeface(Typeface.DEFAULT_BOLD);
paintNames.setTextSize(coordFH(15, relH));
paintNames.setTextAlign(Align.RIGHT);
gridCanvas.drawRect(startX, startY, endX, endY, p);
for (int cy = 0; cy <= gridRows; cy++) {
final int posY = Math.max(h - partH * cy, 0);
gridCanvas.drawLine((cy == 0 || cy == gridRows ? 0 : coordFW(x - 5, relW)),
coordFH(posY, relH), endX, coordFH(posY, relH), p);
if (showLog10Lines) {
p.setAlpha(30);
for (int logy = 1; logy < 10; logy++) {
final int log = (int) (Math.log10(logy) * partH);
gridCanvas.drawLine(coordFW(x, relW), coordFH(h - (partH * cy) - log, relH), endX, coordFH(h - (partH * cy) - log, relH), p);
}
p.setAlpha(100);
}
}
if (!NumberUtil.isInteger(gridRows)) {
gridCanvas.drawLine(0, coordFH(0, relH), endX, coordFH(0, relH), p);
}
if (labelRowList != null && canvas != null) {
for (int i = 0; i < labelRowList.size(); i++) {
final GraphView.GraphLabel label = labelRowList.get(i);
paintNames.setColor(Color.parseColor(label.getColor()));
canvas.drawText(label.getText(), coordFW((int) (gridX - 3), relW), coordFH((int) ((gridY + h) - partH * i + 7), relH), paintNames);
}
}
for (int cx = 1; cx < gridCells; cx++) {
final float posX = coordFW(partW * cx, relW) + startX;
gridCanvas.drawLine(posX, startY, posX, endY, p);
}
return grid;
}
public void updateGrid(int cells, float rows) {
this.gridCells = cells;
this.gridRows = rows;
}
protected float coordFW(final int x, final int y)
{
return (float) x / y * width;
}
protected float coordFH(final int x, final int y)
{
return (float) x / y * height;
}
protected int coordW(final int x, final int y)
{
return Math.round((float) x / y * width);
}
protected int coordH(final int x, final int y)
{
return Math.round((float) x / y * height);
}
public int getGraphWidth()
{
return graphWidth;
}
public int getGraphHeight()
{
return graphHeight;
}
public float getGraphStrokeWidth()
{
return graphStrokeWidth;
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)
{
final int paddingH = getPaddingLeft() + getPaddingRight();
final int paddingW = getPaddingTop() + getPaddingBottom();
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int w = MeasureSpec.getSize(widthMeasureSpec);
final int newW = width + paddingH;
switch (MeasureSpec.getMode(widthMeasureSpec))
{
case MeasureSpec.AT_MOST:
if (newW < w)
w = newW;
break;
case MeasureSpec.EXACTLY:
break;
case MeasureSpec.UNSPECIFIED:
w = newW;
break;
}
scale = (float) (w - getPaddingLeft() - getPaddingRight()) / width;
int h = MeasureSpec.getSize(heightMeasureSpec);
final int newH = Math.round(height * scale) + paddingW;
switch (MeasureSpec.getMode(heightMeasureSpec))
{
case MeasureSpec.AT_MOST:
if (newH < h)
h = newH;
break;
case MeasureSpec.EXACTLY:
break;
case MeasureSpec.UNSPECIFIED:
h = newH;
break;
}
setMeasuredDimension(w, h);
}
@Override
protected void onDraw(final Canvas canvas)
{
try {
if (recycled)
return;
repaint(getContext());
final int canvasSave = canvas.save();
canvas.translate(getPaddingLeft(), getPaddingTop());
canvas.translate(0, internalPaddingTop);
canvas.scale(scale, scale);
if (!genBackgroundBitmap.isRecycled()) {
canvas.drawBitmap(genBackgroundBitmap, 0, 0, bitmapPaint);
}
gridBitmap = null;
gridBitmap = generateGridBitmap(canvas);
canvas.drawBitmap(gridBitmap, gridX, gridY, bitmapPaint);
final int canvasSave2 = canvas.save();
canvas.translate(graphX, graphY);
for (final GraphService graph : graphs)
graph.draw(canvas);
canvas.restoreToCount(canvasSave2);
// if (!reflectionBitmap.isRecycled()) {
// canvas.drawBitmap(reflectionBitmap, reflectionX, reflectionY, bitmapPaint);
// }
canvas.restoreToCount(canvasSave);
}
catch (Exception e) {
e.printStackTrace();
}
}
public void addGraph(final GraphService graph)
{
graphs.add(graph);
}
public void recycle()
{
Log.d("GraphView", "recycling");
recycled = true;
genBackgroundBitmap.recycle();
genBackgroundBitmap = null;
gridBitmap.recycle();
gridBitmap = null;
}
public String getAttributeValue(AttributeSet attr, String attrName, String defaultValue) {
String value;
int resId = attr.getAttributeResourceValue(null, attrName, -1);
if (resId < 0) {
value = attr.getAttributeValue(null, attrName);
}
else {
value = getContext().getResources().getString(resId);
}
return (value != null ? value : defaultValue);
}
/**
*
* @return
*/
public List<GraphService> getGraphs() {
return graphs;
}
public int getGraphSize() {
return (graphs == null ? 0 : graphs.size());
}
public final static int LABELLIST_ALL = 0;
public final static int LABELLIST_HORIZONTAL_MIN = 1;
public final static int LABELLIST_HORIZONTAL_MAX = 2;
public final static int LABELLIST_VERTICAL_MIN = 3;
public final static int LABELLIST_VERTICAL_MAX = 4;
public void clearLabels(int labelList) {
switch (labelList) {
case LABELLIST_ALL:
getLabelHMaxList().clear();
getLabelHMinList().clear();
getLabelVMaxList().clear();
getLabelVMinList().clear();
break;
case LABELLIST_HORIZONTAL_MAX:
getLabelHMaxList().clear();
break;
case LABELLIST_HORIZONTAL_MIN:
getLabelHMinList().clear();
break;
case LABELLIST_VERTICAL_MAX:
getLabelVMaxList().clear();
break;
case LABELLIST_VERTICAL_MIN:
getLabelVMinList().clear();
break;
}
}
public void addLabelHMin(String labelHMin) {
addLabelHMin(labelHMin, DEFAULT_H_LABEL_COLOR);
}
public void addLabelHMin(String labelHMin, String color) {
this.labelHMinList.add(new GraphView.GraphLabel(labelHMin, color));
}
public void addLabelHMax(String labelHMax) {
addLabelHMax(labelHMax, DEFAULT_H_LABEL_COLOR);
}
public void addLabelHMax(String labelHMax, String color) {
this.labelHMaxList.add(new GraphView.GraphLabel(labelHMax, color));
}
public void addLabelVMin(String labelVMin) {
addLabelVMin(labelVMin, DEFAULT_V_LABEL_COLOR);
}
public void addLabelVMin(String labelVMin, String color) {
this.labelVMinList.add(new GraphView.GraphLabel(labelVMin, color));
}
public void addLabelVMax(String labelVMax) {
addLabelVMax(labelVMax, DEFAULT_V_LABEL_COLOR);
}
public void addLabelVMax(String labelVMax, String color) {
this.labelVMaxList.add(new GraphView.GraphLabel(labelVMax, color));
}
public String getLabelInfoHorizontal() {
return labelInfoHorizontal;
}
public void setLabelInfoHorizontal(String labelInfoHorizontal) {
this.labelInfoHorizontal = labelInfoHorizontal;
}
public List<GraphView.GraphLabel> getLabelInfoVerticalList() {
return labelInfoVerticalList;
}
public void setLabelInfoVerticalList(List<GraphView.GraphLabel> labelInfoVerticalList) {
this.labelInfoVerticalList = labelInfoVerticalList;
}
public List<GraphView.GraphLabel> getLabelHMinList() {
return labelHMinList;
}
public void setLabelHMinList(List<GraphView.GraphLabel> labelHMinList) {
this.labelHMinList = labelHMinList;
}
public List<GraphView.GraphLabel> getLabelHMaxList() {
return labelHMaxList;
}
public void setLabelHMaxList(List<GraphView.GraphLabel> labelHMaxList) {
this.labelHMaxList = labelHMaxList;
}
public List<GraphView.GraphLabel> getLabelVMinList() {
return labelVMinList;
}
public void setLabelVMinList(List<GraphView.GraphLabel> labelVMinList) {
this.labelVMinList = labelVMinList;
}
public List<GraphView.GraphLabel> getLabelVMaxList() {
return labelVMaxList;
}
public void setLabelVMaxList(List<GraphView.GraphLabel> labelVMaxList) {
this.labelVMaxList = labelVMaxList;
}
public List<PositionedGraphLabel> getLabelList() {
return labelList;
}
public void setLabelList(List<PositionedGraphLabel> labelList) {
this.labelList = labelList;
}
public void addLabel(float x, float y, String text, String color) {
PositionedGraphLabel label = createPositionedGraphLabel(x, y, text, color);
labelList.add(label);
}
public PositionedGraphLabel createPositionedGraphLabel(float x, float y, String text, String color) {
final float relW = 593;
final float relH = 220;
final int posx = (int) coordFW((int) (83f + x * 500f) , (int) relW);
final int posy = (int) coordFH((int) (220f - y * 207f), (int) relH);
return new PositionedGraphLabel(text, color, posx, posy);
}
@Override
public void setSignalRange(int min, int max) {
labelVMaxList.add(new GraphView.GraphLabel(String.valueOf(max), "#C8f8a000"));
labelVMinList.add(new GraphView.GraphLabel(String.valueOf(min), "#C8f8a000"));
}
@Override
public void removeSignalRange() {
labelVMaxList.remove(labelVMaxList.size()-1);
labelVMinList.remove(labelVMinList.size()-1);
}
@Override
public MinMax<Integer> getSignalRange() {
try {
if (labelVMinList.size() >= 1 && labelVMaxList.size() >= 1) {
Integer min = Integer.valueOf(labelVMinList.get(labelVMinList.size()-1).getText());
Integer max = Integer.valueOf(labelVMaxList.get(labelVMaxList.size()-1).getText());
return new MinMax<Integer>(min, max);
}
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public List<GraphLabel> getRowLinesLabelList() {
return labelRowList;
}
@Override
public void setRowLinesLabelList(List<GraphLabel> rowLabelList) {
labelRowList = rowLabelList;
}
}