package com.example.rangegraph; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.FontMetrics; import android.graphics.Paint.Style; import android.util.AttributeSet; import android.util.Log; import android.view.View; /** * Draw a Range Graph to show if the current value is in range or not * Example AsciiArt:<pre> * | T | * | T | * 140 | M | * | M | * 120 | B | * | B | * </pre> * The region T will be red if the value is too high, blank otherwise * The region M will be red if the value is out of range, green otherwise * The region B will be red if the value is too low, green otherwise * @author Ian Darwin */ public class RangeGraph extends View { private static final String TAG = "RangeGraph"; // State private int mMin = 0, mMax = 100; private int mValue = 50; // Graphics protected int colorNeutral = Color.BLACK; protected int colorOutOfRange = Color.RED; protected int colorInRange = Color.GREEN; private int mFontSize = 16; private Paint mPaint; // Layout private int mWidth = 200; private int mHeight = 300; public RangeGraph(Context context) { super(context); commonSetup(); } /** * @param context * @param attrs */ public RangeGraph(Context context, AttributeSet attrs) { super(context, attrs); // Then allow overrides from XML TypedArray a = context.getTheme().obtainStyledAttributes( attrs, R.styleable.RangeGraph, 0, 0); try { colorNeutral = a.getColor(R.styleable.RangeGraph_colorNeutral, Color.BLACK); colorInRange = a.getColor(R.styleable.RangeGraph_colorInRange, Color.GREEN); colorOutOfRange = a.getColor(R.styleable.RangeGraph_colorOutOfRange, Color.RED); mMin = a.getInteger(R.styleable.RangeGraph_minimum, 0); mMax = a.getInteger(R.styleable.RangeGraph_maximum, 100); } finally { a.recycle(); } commonSetup(); } private void commonSetup() { mPaint = new Paint(); mPaint.setColor(colorNeutral); // Scale the desired text size to match screen density mPaint.setTextSize(mFontSize * getResources().getDisplayMetrics().density); mPaint.setStrokeWidth(2f); setPadding(5, 5, 5, 5); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(mWidth, mHeight); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // Label it int oneThirdHeight = getPaddingTop() + (2*mHeight/3); int twoThirdsHeight = getPaddingTop() + (mHeight/3); String stringMax = Integer.toString(mMax); String stringMin = Integer.toString(mMin); String stringValue = Integer.toString(mValue); FontMetrics fm = mPaint.getFontMetrics(); final float fontHeight = fm.ascent + fm.descent; canvas.drawText(stringMax, getPaddingLeft(), twoThirdsHeight - fontHeight/2, mPaint); canvas.drawText(stringMin, getPaddingLeft(), oneThirdHeight - (fontHeight/2), mPaint); // Draw the bar outline, at 1/2 and 2/3 of the width int top = getPaddingTop(); int bot = mHeight - getPaddingBottom(); int leftSide = (int) (mWidth*0.40f); int rightSide = (int) (mWidth*0.60f); Style oldStyle = mPaint.getStyle(); mPaint.setStyle(Paint.Style.STROKE); canvas.drawRect(leftSide, top, rightSide, bot, mPaint); mPaint.setStyle(oldStyle); // Now draw the bar graph. // The distance from min to max fills the middle third of the graph int oneThirdValue = mMax - mMin; int valueRange = oneThirdValue * 3; int valueAtBottom = mMin - oneThirdValue; int visibleValue = Math.max(0, mValue - valueAtBottom); int barHeight = Math.min(mHeight, (int) (mHeight * (1f * visibleValue / valueRange))); // First put in three tick marks before changing the color canvas.drawLine(leftSide-20, oneThirdHeight, leftSide, oneThirdHeight, mPaint); canvas.drawLine(leftSide-20, twoThirdsHeight, leftSide, twoThirdsHeight, mPaint); canvas.drawLine(rightSide, mHeight - barHeight, rightSide + 20, mHeight - barHeight, mPaint); mPaint.setColor(isInRange() ? colorInRange : colorOutOfRange); Log.d(TAG, String.format("drawRect(%d %d %d %d)", leftSide, mHeight - barHeight, rightSide, bot)); canvas.drawRect(leftSide, mHeight - barHeight, rightSide, bot, mPaint); // Draw the actual reading beside the bar top mPaint.setColor(isInRange() ? colorNeutral : colorOutOfRange); canvas.drawText(stringValue, mWidth*0.65f, mHeight - barHeight - (fontHeight/2), mPaint); } public boolean isInRange() { return mValue >= mMin && mValue <= mMax; } // Simple accessors public int getMin() { return mMin; } public void setMin(int min) { this.mMin = min; } public int getMax() { return mMax; } public void setMax(int max) { this.mMax = max; } public int getValue() { return mValue; } public void setValue(int value) { this.mValue = value; } public int getColorOutOfRange() { return colorOutOfRange; } public void setColorOutOfRange(int colorOutOfRange) { this.colorOutOfRange = colorOutOfRange; } public int getColorInRange() { return colorInRange; } public void setColorInRange(int colorInRange) { this.colorInRange = colorInRange; } public int getColorNeutral() { return colorNeutral; } public void setColorNeutral(int colorNeutral) { this.colorNeutral = colorNeutral; } }