package com.boardgamegeek.ui.widget; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.support.annotation.ColorInt; import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import com.boardgamegeek.R; import com.boardgamegeek.util.MathUtils; import java.text.DecimalFormat; public class ScoreGraphView extends View { private static final DecimalFormat SCORE_FORMAT = new DecimalFormat("0"); private static final int SCORE_STROKE_WIDTH = 2; private Paint barPaint; private Paint scorePaint; private Paint textPaint; private float scoreRadius; private float smallTickHeight; private float largeTickHeight; private int lowScoreColor; private int averageScoreColor; private int averageWinScoreColor; private int highScoreColor; private double lowScore; private double averageScore; private double averageWinScore; private double highScore; private double personalLowScore; private double personalAverageScore; private double personalAverageWinScore; private double personalHighScore; private boolean hasPersonalScores; public ScoreGraphView(Context context) { super(context); init(context); } public ScoreGraphView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } private void init(Context context) { barPaint = new Paint(Paint.ANTI_ALIAS_FLAG); barPaint.setColor(Color.BLACK); barPaint.setStrokeWidth(1); scorePaint = new Paint(Paint.ANTI_ALIAS_FLAG); scorePaint.setStrokeWidth(SCORE_STROKE_WIDTH); textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); textPaint.setColor(ContextCompat.getColor(getContext(), R.color.secondary_text)); textPaint.setTextSize(8f * getContext().getResources().getDisplayMetrics().scaledDensity); scoreRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6, context.getResources().getDisplayMetrics()); smallTickHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, context.getResources().getDisplayMetrics()); largeTickHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6, context.getResources().getDisplayMetrics()); lowScoreColor = ContextCompat.getColor(getContext(), R.color.score_low); averageScoreColor = ContextCompat.getColor(getContext(), R.color.score_average); averageWinScoreColor = ContextCompat.getColor(getContext(), R.color.score_average_win); highScoreColor = ContextCompat.getColor(getContext(), R.color.score_high); } public void setLowScore(double lowScore) { this.lowScore = lowScore; } public void setAverageScore(double averageScore) { this.averageScore = averageScore; } public void setAverageWinScore(double averageWinScore) { this.averageWinScore = averageWinScore; } public void setHighScore(double highScore) { this.highScore = highScore; } public void setPersonalLowScore(double lowScore) { this.personalLowScore = lowScore; this.hasPersonalScores = true; } public void setPersonalAverageScore(double averageScore) { this.personalAverageScore = averageScore; this.hasPersonalScores = true; } public void setPersonalAverageWinScore(double averageWinScore) { this.personalAverageWinScore = averageWinScore; this.hasPersonalScores = true; } public void setPersonalHighScore(double highScore) { this.personalHighScore = highScore; this.hasPersonalScores = true; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); final float y = canvas.getHeight() / 2; final float left = scoreRadius + SCORE_STROKE_WIDTH; final float right = canvas.getWidth() - scoreRadius - SCORE_STROKE_WIDTH; canvas.drawLine(left, y, right, y, barPaint); // ticks int tickSpacing; final double scoreSpread = highScore - lowScore; if (scoreSpread <= 20) { tickSpacing = 1; } else if (scoreSpread <= 50) { tickSpacing = 5; } else if (scoreSpread <= 200) { tickSpacing = 10; } else if (scoreSpread <= 500) { tickSpacing = 20; } else { tickSpacing = MathUtils.significantDigits((int) (Math.ceil(scoreSpread / 100) * 10), 2); } double tickScore = Math.ceil(lowScore / tickSpacing) * tickSpacing; while (tickScore <= highScore) { float x = (float) ((tickScore - lowScore) / (scoreSpread) * (right - left) + left); final float tickHeight; if ((tickScore % (5 * tickSpacing)) == 0) { tickHeight = largeTickHeight; final String label = SCORE_FORMAT.format(tickScore); float labelWidth = textPaint.measureText(label); float labelLeft = MathUtils.constrain(x - labelWidth / 2, 0, canvas.getWidth() - labelWidth); canvas.drawText(label, labelLeft, canvas.getHeight(), textPaint); } else { tickHeight = smallTickHeight; } canvas.drawLine(x, y - tickHeight, x, y + tickHeight, barPaint); tickScore += tickSpacing; } // score dots scorePaint.setStyle(hasPersonalScores() ? Style.STROKE : Style.FILL); drawScore(canvas, y, left, right, lowScore, lowScoreColor); drawScore(canvas, y, left, right, highScore, highScoreColor); drawScore(canvas, y, left, right, averageScore, averageScoreColor); drawScore(canvas, y, left, right, averageWinScore, averageWinScoreColor); if (hasPersonalScores()) { scorePaint.setStyle(Style.FILL); drawScore(canvas, y, left, right, personalLowScore, lowScoreColor); drawScore(canvas, y, left, right, personalHighScore, highScoreColor); drawScore(canvas, y, left, right, personalAverageScore, averageScoreColor); drawScore(canvas, y, left, right, personalAverageWinScore, averageWinScoreColor); } } private void drawScore(Canvas canvas, float y, float left, float right, double score, @ColorInt int color) { scorePaint.setColor(color); float x = (float) ((score - lowScore) / (highScore - lowScore) * (right - left) + left); canvas.drawCircle(x, y, scoreRadius, scorePaint); } private boolean hasPersonalScores() { return hasPersonalScores; } }