/* 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 bar chart graph with a shaded area. * Layout parameters are read in from XML. When specifying the data to be * plotted, the x-values give the left and right edges' positions, with the y * value giving the height of the bar. The first y value is ignored as there is * one more side edge than top edge. Bars can have different colours to one * another by use of setBarColours(). */ public class BarGraphView 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(); protected float barBorder; protected int[][] mBarColours = {{0xFF007ba1}}; protected float mBarNumSpacing; protected boolean drawBarNumbers; public BarGraphView(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); mBarNumSpacing = attributes.getAttributeFloatValue(customSchemaLocation, "graph_bar_number_spacing", 10.0f); drawBarNumbers = attributes.getAttributeBooleanValue(customSchemaLocation, "graph_draw_bar_numbers", false); barBorder = attributes.getAttributeFloatValue(customSchemaLocation, "graph_bar_border", 10.0f); 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); } /** * Sets the colours of the bars. If there are more colours than bars, then * the extra colours are ignored. If there are fewer colours than bars, then * the extra bars default to a blue colour. * * @param colours An integer array specifying the colours of each bar in order */ public void setBarColours(int[][] colours) { mBarColours = colours; } public void setDrawBarNumbers(boolean value) { drawBarNumbers = value; } @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 } canvas.save(); canvas.clipRect(mLeftPadding, mTopPadding - mLinePaint.getStrokeWidth(), mLeftPadding + graphWidth, mTopPadding + graphHeight - mAxesPaint.getStrokeWidth() / 2); for (int i = 0; i < mData[set][0].length - 1; i++) { float[] coords = calcCoordinates(set, i); if (Float.isNaN(mData[set][1][i + 1])) { } else if (Float.isNaN(mData[set][1][i]) && Float.isNaN(mData[set][1][i + 1]) == false) { } else { mLinePath = new Path(); mGradientPath = new Path(); drawAscendingSection(canvas, coords); gradientShader = new LinearGradient(0, 0, 0, graphHeight, 0xCCffffff, 0x11ffffff, Shader.TileMode.CLAMP); compositeShader = new ComposeShader(bandShader, gradientShader, PorterDuff.Mode.MULTIPLY); mAscendingPaint.setShader(compositeShader); try { int barColour = mBarColours[set][i]; mLinePaint.setColor(barColour); int[] barBitmapColours = {barColour, barColour, barColour, 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) { try { mLinePaint.setColor(mDataSetColours[set]); bandShader = new BitmapShader(mBitmap, 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 e2) { } } canvas.drawPath(mGradientPath, mBackgroundPaint); canvas.drawPath(mGradientPath, mAscendingPaint); canvas.drawPath(mLinePath, mLinePaint); if (drawBarNumbers) { canvas.drawText(Float.toString(mData[set][1][i + 1]), coords[0] + (coords[2] - coords[0] - mAxisLabelPaint.measureText(Float.toString(mData[set][1][i + 1]))) / 2, coords[3] - mBarNumSpacing, mAxisLabelPaint); } } } canvas.restore(); } } @Override protected void drawAscendingSection(Canvas canvas, float[] coords) { mLinePath.moveTo(coords[0] + barBorder, mTopPadding + graphHeight); mLinePath.lineTo(coords[0] + barBorder, coords[3]); mLinePath.lineTo(coords[2] - barBorder, coords[3]); mLinePath.lineTo(coords[2] - barBorder, mTopPadding + graphHeight); mGradientPath.moveTo(coords[0] + barBorder, mTopPadding + graphHeight); mGradientPath.lineTo(coords[0] + barBorder, coords[3]); mGradientPath.lineTo(coords[2] - barBorder, coords[3]); mGradientPath.lineTo(coords[2] - barBorder, mTopPadding + graphHeight); } @Override protected void drawDescendingSection(Canvas canvas, float[] coords) { drawAscendingSection(canvas, coords); } @Override protected void drawGridlines(Canvas canvas) { Path gridlinePath = new Path(); for (int i = 0; i < mYAxisLabelPositions.length; i++) { gridlinePath.moveTo(mLeftPadding, mTopPadding + (1.00f - mYAxisLabelPositions[i]) * graphHeight); gridlinePath.lineTo(mLeftPadding + graphWidth, mTopPadding + (1.00f - mYAxisLabelPositions[i]) * graphHeight); } canvas.drawPath(gridlinePath, mGridlinesPaint); } @Override protected void drawAxes(Canvas canvas) { float textHoriOffset; float textVertOffset = -(2 * mAxesPaint.getTextSize() - mAxesPaint.getFontSpacing()) / 2; Path axesPath = new Path(); axesPath.moveTo(mLeftPadding, mTopPadding - mGridlinesPaint.getStrokeWidth() / 2); axesPath.lineTo(mLeftPadding, mTopPadding + graphHeight); axesPath.lineTo(mLeftPadding + graphWidth + mGridlinesPaint.getStrokeWidth() / 2, mTopPadding + graphHeight); mAxesPaint.setStyle(Paint.Style.STROKE); mAxesPaint.setAntiAlias(false); canvas.drawPath(axesPath, mAxesPaint); mAxesPaint.setAntiAlias(mAxesPaintAntiAlias); mAxesPaint.setStyle(Paint.Style.FILL); for (int i = 0; i < mYAxisLabels.length; i++) { textHoriOffset = mAxesPaint.measureText(mYAxisLabels[i]) + mGridlineLabelHoriSpacing; canvas.drawText(mYAxisLabels[i], mLeftPadding - textHoriOffset, mTopPadding - textVertOffset + (1.00f - mYAxisLabelPositions[i]) * graphHeight, mAxesPaint); } textVertOffset = (2 * mAxesPaint.getTextSize() - mAxesPaint.getFontSpacing()) + mGridlineLabelVertSpacing; for (int i = 0; i < mXAxisLabels.length; i++) { textHoriOffset = mAxesPaint.measureText(mXAxisLabels[i]) / 2; canvas.drawText(mXAxisLabels[i], mLeftPadding - textHoriOffset + (mXAxisLabelPositions[i] - mMinX) * graphWidth / mDataXWidth, mTopPadding + graphHeight + textVertOffset, mAxesPaint); } } }