/* 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 uk.ac.cam.cl.dtg.snowdon;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ComposeShader;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.Shader;
import android.util.AttributeSet;
/**
* Inherits from GraphView and draws a graph with a shaded area under a line.
* Layout parameters are read in from XML.
*/
public class AreaGraphView extends GraphView {
protected Paint mLinePaint = new Paint();
protected int[] bitmapColours;
protected Bitmap mBitmap;
protected Shader bandShader;
protected Shader gradientShader;
protected Shader compositeShader;
protected Path mLinePath = new Path();
protected Path mGradientPath = new Path();
/**
* Creates a new ShaderAreaGraphView and reads in layout parameters from
* XML.
*
* @param context
* @param attributes
*/
public AreaGraphView(Context context, AttributeSet attributes) {
super(context, attributes);
mLinePaint.setColor(attributes.getAttributeIntValue(customSchemaLocation, "graph_line_colour", 0xFF007ba1));
mLinePaint.setStrokeWidth(attributes.getAttributeFloatValue(customSchemaLocation, "graph_line_width", 3.0f));
mLinePaint.setAntiAlias(attributes.getAttributeBooleanValue(customSchemaLocation, "graph_line_anti_alias", true));
mLinePaint.setStyle(Paint.Style.STROKE);
// Ensure you can see the gridlines behind the graph area, but only just
mBackgroundPaint.setAlpha(200);
String bitmapColoursString = attributes.getAttributeValue(customSchemaLocation, "graph_shaded_area_colours");
if (bitmapColoursString == null)
bitmapColoursString = "FF007ba1; FF007ba1; FF007ba1; 00007ba1; 00007ba1; 00007ba1";
String[] bitmapColoursStringArray = bitmapColoursString.split("; ");
int[] bitmapColoursIntArray = new int[bitmapColoursStringArray.length];
for (int i = 0; i < bitmapColoursIntArray.length; i++) {
bitmapColoursIntArray[i] = (int) (Long.parseLong(bitmapColoursStringArray[i], 16) & 0xffffffffL);
}
bitmapColours = bitmapColoursIntArray;
mBitmap = Bitmap.createBitmap(bitmapColours, 0, 1, 1, 6, Bitmap.Config.ARGB_8888);
bandShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.REPEAT);
}
@Override
protected void drawPlot(Canvas canvas) {
for (int set = 0; set < mData.length; set++) {
try {
int setColour = mDataSetColours[set];
mLinePaint.setColor(setColour);
int[] barBitmapColours = {setColour, setColour, setColour, 00000000, 00000000, 00000000};
Bitmap barShaderBitmap = Bitmap.createBitmap(barBitmapColours, 0, 1, 1, 6, Bitmap.Config.ARGB_8888);
bandShader = new BitmapShader(barShaderBitmap, Shader.TileMode.CLAMP, Shader.TileMode.REPEAT);
gradientShader = new LinearGradient(0, 0, 0, graphHeight, 0xCCffffff, 0x11ffffff, Shader.TileMode.CLAMP);
compositeShader = new ComposeShader(bandShader, gradientShader, PorterDuff.Mode.MULTIPLY);
mAscendingPaint.setShader(compositeShader);
} catch (IndexOutOfBoundsException e) {
// Just carry on using the last colour
}
float[] startCoordinates = calcCoordinates(set, 0);
canvas.save();
canvas.clipRect(mLeftPadding + mLinePaint.getStrokeWidth() / 2 - 0.5f, mTopPadding - mLinePaint.getStrokeWidth(), mLeftPadding + graphWidth, mTopPadding + graphHeight - mAxesPaint.getStrokeWidth() / 2);
mLinePath = new Path();
mGradientPath = new Path();
mLinePath.moveTo(startCoordinates[0], startCoordinates[1]);
mGradientPath.moveTo(startCoordinates[0], startCoordinates[1]);
for (int i = 0; i < mData[set][0].length - 1; i++) {
float[] coords = calcCoordinates(set, i);
if (Float.isNaN(mData[set][1][i + 1])) {
mLinePath.moveTo(coords[0], mTopPadding + graphHeight);
mGradientPath.lineTo(coords[0], mTopPadding + graphHeight);
} else if (Float.isNaN(mData[set][1][i]) && Float.isNaN(mData[set][1][i + 1]) == false) {
mLinePath.moveTo(coords[2], coords[3]);
mGradientPath.lineTo(coords[2], mTopPadding + graphHeight);
mGradientPath.lineTo(coords[2], coords[3]);
} else if (coords[3] < coords[1]) { // If the data is increasing, draw in the ascending style...
drawAscendingSection(canvas, coords);
} else { // ...otherwise, draw in the descending style
drawDescendingSection(canvas, coords);
}
}
float[] endCoordinates = calcCoordinates(set, mData[set][0].length - 2);
mGradientPath.lineTo(endCoordinates[2], mTopPadding + graphHeight);
mGradientPath.lineTo(startCoordinates[0], mTopPadding + graphHeight);
mGradientPath.lineTo(startCoordinates[0], startCoordinates[1]);
gradientShader = new LinearGradient(0, 0, 0, graphHeight, 0xCCffffff, 0x11ffffff, Shader.TileMode.CLAMP);
compositeShader = new ComposeShader(bandShader, gradientShader, PorterDuff.Mode.MULTIPLY);
mAscendingPaint.setShader(compositeShader);
canvas.drawPath(mGradientPath, mBackgroundPaint);
canvas.drawPath(mGradientPath, mAscendingPaint);
canvas.drawPath(mLinePath, mLinePaint);
canvas.restore();
}
}
@Override
protected void drawAscendingSection(Canvas canvas, float[] coords) {
mLinePath.lineTo(coords[2], coords[3]);
mGradientPath.lineTo(coords[2], coords[3]);
}
@Override
protected void drawDescendingSection(Canvas canvas, float[] coords) {
drawAscendingSection(canvas, coords);
}
}