Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 114 additions & 69 deletions gauge/src/main/java/de/nitri/gauge/Gauge.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,31 @@
*/
public class Gauge extends View {

//Create a default nick logic behaviour
private IGaugeNick gaugeNick = new IGaugeNick() {
@Override
public boolean shouldDrawMajorNick(int nick, float value) {
return (nick % majorNickInterval == 0);
}

@Override
public boolean shouldDrawHalfNick(int nick, float value) {
if (minorTicInterval > 0) {
return nick % minorTicInterval == 0;
}
return false;
}

@Override
public String getLabelString(int nick, float value) {
if (shouldDrawMajorNick(nick, value)) {
return String.valueOf(Math.round(value));
} else {
return null;
}
}
};

private Paint needlePaint;
private Path needlePath;
private Paint needleScrewPaint;
Expand All @@ -49,8 +74,10 @@ public class Gauge extends View {
private Paint scalePaint;
private RectF scaleRect;

private int totalNicks = 120; // on a full circle
private float degreesPerNick = totalNicks / 360;
private int totalNicks = 120;
private float startAngle = 10;
private float endAngle = 350;
private float degreesPerNick = (endAngle - startAngle) / totalNicks;
private float valuePerNick = 10;
private float minValue = 0;
private float maxValue = 1000;
Expand All @@ -64,10 +91,10 @@ public class Gauge extends View {

private float needleStep;

private float centerValue;
private float labelRadius;

private int majorNickInterval = 10;
private int minorTicInterval = -1;

private int deltaTimeInterval = 5;
private float needleStepFactor = 3f;
Expand All @@ -77,6 +104,7 @@ public class Gauge extends View {
private long lastMoveTime;
private boolean needleShadow = true;
private int faceColor;
private int rimColor;
private int scaleColor;
private int needleColor;
private Paint upperTextPaint;
Expand Down Expand Up @@ -108,22 +136,22 @@ public Gauge(Context context, AttributeSet attrs) {
public Gauge(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
applyAttrs(context, attrs);
initValues();
initPaint();
}

private void applyAttrs(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Gauge, 0, 0);
totalNicks = a.getInt(R.styleable.Gauge_totalNicks, totalNicks);
degreesPerNick = 360.0f / totalNicks;
valuePerNick = a.getFloat(R.styleable.Gauge_valuePerNick, valuePerNick);
majorNickInterval = a.getInt(R.styleable.Gauge_majorNickInterval, 10);
startAngle = a.getFloat(R.styleable.Gauge_startAngle, startAngle);
endAngle = a.getFloat(R.styleable.Gauge_endAngle, endAngle);
majorNickInterval = a.getInt(R.styleable.Gauge_majorNickInterval, majorNickInterval);
minValue = a.getFloat(R.styleable.Gauge_minValue, minValue);
maxValue = a.getFloat(R.styleable.Gauge_maxValue, maxValue);
intScale = a.getBoolean(R.styleable.Gauge_intScale, intScale);
initialValue = a.getFloat(R.styleable.Gauge_initialValue, initialValue);
requestedLabelTextSize = a.getFloat(R.styleable.Gauge_labelTextSize, requestedLabelTextSize);
faceColor = a.getColor(R.styleable.Gauge_faceColor, Color.argb(0xff, 0xff, 0xff, 0xff));
rimColor = a.getColor(R.styleable.Gauge_rimColor, Color.argb(0x4f, 0x33, 0x36, 0x33));
scaleColor = a.getColor(R.styleable.Gauge_scaleColor, 0x9f004d0f);
needleColor = a.getColor(R.styleable.Gauge_needleColor, Color.RED);
needleShadow = a.getBoolean(R.styleable.Gauge_needleShadow, needleShadow);
Expand All @@ -134,14 +162,25 @@ private void applyAttrs(Context context, AttributeSet attrs) {
requestedLowerTextSize = a.getFloat(R.styleable.Gauge_lowerTextSize, 0);
a.recycle();

initValues();
validate();
}

private void initValues() {
degreesPerNick = (endAngle - startAngle) / totalNicks;
valuePerNick = (maxValue - minValue) / totalNicks;
needleStep = needleStepFactor * valuePerDegree();
centerValue = (minValue + maxValue) / 2;
needleValue = value = initialValue;

// if major nick interval is a multiple of 2, 3, or 5 then draw minor nicks.
if (majorNickInterval % 2 == 0) {
minorTicInterval = majorNickInterval/2;
} else if (majorNickInterval % 3 == 0) {
minorTicInterval = majorNickInterval/3;
} else if (majorNickInterval % 5 == 0) {
minorTicInterval = majorNickInterval/5;
}

int widthPixels = getResources().getDisplayMetrics().widthPixels;
textScaleFactor = (float) widthPixels / (float) REF_MAX_PORTRAIT_CANVAS_SIZE;

Expand All @@ -160,12 +199,13 @@ private void initPaint() {
// http://mindtherobot.com/blog/272/android-custom-ui-making-a-vintage-thermometer/

rimPaint = new Paint();
rimPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
rimPaint.setAntiAlias(true);
rimPaint.setColor(rimColor);

rimCirclePaint = new Paint();
rimCirclePaint.setAntiAlias(true);
rimCirclePaint.setStyle(Paint.Style.STROKE);
rimCirclePaint.setColor(Color.argb(0x4f, 0x33, 0x36, 0x33));
rimCirclePaint.setColor(rimColor);
rimCirclePaint.setStrokeWidth(0.005f);

facePaint = new Paint();
Expand Down Expand Up @@ -216,7 +256,6 @@ protected void onDraw(Canvas canvas) {
drawRim(canvas);
drawFace(canvas);
drawScale(canvas);
drawLabels(canvas);
drawTexts(canvas);
canvas.rotate(scaleToCanvasDegrees(valueToDegrees(needleValue)), canvasCenterX, canvasCenterY);
canvas.drawPath(needlePath, needlePaint);
Expand Down Expand Up @@ -258,50 +297,37 @@ private void drawFace(Canvas canvas) {
}

private void drawScale(Canvas canvas) {

canvas.save();
for (int i = 0; i < totalNicks; ++i) {
float y1 = scaleRect.top;
float y2 = y1 + (0.020f * canvasHeight);
float y3 = y1 + (0.060f * canvasHeight);
float y4 = y1 + (0.030f * canvasHeight);

float value = nickToValue(i);

if (value >= minValue && value <= maxValue) {
canvas.drawLine(0.5f * canvasWidth, y1, 0.5f * canvasWidth, y2, scalePaint);

if (i % majorNickInterval == 0) {
canvas.drawLine(0.5f * canvasWidth, y1, 0.5f * canvasWidth, y3, scalePaint);
}

if (i % (majorNickInterval / 2) == 0) {
canvas.drawLine(0.5f * canvasWidth, y1, 0.5f * canvasWidth, y4, scalePaint);
}
float y1 = scaleRect.top;
float y2 = y1 + (0.020f * canvasHeight);
float y3 = y1 + (0.060f * canvasHeight);
float y4 = y1 + (0.030f * canvasHeight);

for (int i = 0; i <= totalNicks; ++i) {
canvas.save();
canvas.rotate(i * degreesPerNick + 180 + startAngle, 0.5f * canvasWidth, 0.5f * canvasHeight);
canvas.drawLine(0.5f * canvasWidth, y1, 0.5f * canvasWidth, y2, scalePaint);

if (gaugeNick.shouldDrawMajorNick(i, i * valuePerNick)) {
canvas.drawLine(0.5f * canvasWidth, y1, 0.5f * canvasWidth, y3, scalePaint);
}

canvas.rotate(degreesPerNick, 0.5f * canvasWidth, 0.5f * canvasHeight);
}
canvas.restore();
}

private void drawLabels(Canvas canvas) {
for (int i = 0; i < totalNicks; i += majorNickInterval) {
float value = nickToValue(i);
if (value >= minValue && value <= maxValue) {
float scaleAngle = i * degreesPerNick;
float scaleAngleRads = (float) Math.toRadians(scaleAngle);
//Log.d(TAG, "i = " + i + ", angle = " + scaleAngle + ", value = " + value);
float deltaX = labelRadius * (float) Math.sin(scaleAngleRads);
float deltaY = labelRadius * (float) Math.cos(scaleAngleRads);
String valueLabel;
if (intScale) {
valueLabel = String.valueOf((int) value);
} else {
valueLabel = String.valueOf(value);
}
drawTextCentered(valueLabel, canvasCenterX + deltaX, canvasCenterY - deltaY, labelPaint, canvas);
if (gaugeNick.shouldDrawHalfNick(i, i * valuePerNick)) {
canvas.drawLine(0.5f * canvasWidth, y1, 0.5f * canvasWidth, y4, scalePaint);
}
canvas.restore();
drawLabels(canvas, i);
}
}

private void drawLabels(Canvas canvas, int i) {
String valueLabel = gaugeNick.getLabelString(i, i * valuePerNick);
if (valueLabel != null) {
float scaleAngle = (i * degreesPerNick) + 180 + startAngle;
float scaleAngleRads = (float) Math.toRadians(scaleAngle);
//Log.d(TAG, "i = " + i + ", angle = " + scaleAngle + ", value = " + value);
float deltaX = labelRadius * (float) Math.sin(scaleAngleRads);
float deltaY = labelRadius * (float) Math.cos(scaleAngleRads);
drawTextCentered(valueLabel, canvasCenterX + deltaX, canvasCenterY - deltaY, labelPaint, canvas);
}
}

Expand Down Expand Up @@ -440,14 +466,9 @@ protected void onRestoreInstanceState(Parcelable state) {
}
}

private float nickToValue(int nick) {
float rawValue = ((nick < totalNicks / 2) ? nick : (nick - totalNicks)) * valuePerNick;
return rawValue + centerValue;
}

private float valueToDegrees(float value) {
// these are scale degrees, 0 is on top
return ((value - centerValue) / valuePerNick) * degreesPerNick;
float angle = (value/valuePerNick * degreesPerNick);
return angle <= endAngle ? angle + 180 + startAngle : endAngle + 180; // +180 because we need to flip the top/bottom
}

private float valuePerDegree() {
Expand Down Expand Up @@ -606,25 +627,36 @@ public void setMaxValue(float value) {
}

/**
* Set the total amount of nicks on a full 360 degree scale. Should be a multiple of majorNickInterval.
* Set the starting angle for the values. The origin is the bottom (6h) position.
*
* @param nicks number of nicks
* @param value start angle
*/
public void setTotalNicks(int nicks) {
totalNicks = nicks;
degreesPerNick = 360.0f / totalNicks;
public void setStartAngle(float value) {
startAngle = value;
initValues();
validate();
invalidate();
}

/**
* Set the end angle for the values. The origin is the bottom (6h) position.
*
* @param value end angle
*/
public void setEndAngle(float value) {
endAngle = value;
initValues();
validate();
invalidate();
}

/**
* Set the value (interval) per nick.
* Set the total amount of nicks on a full 360 degree scale. Should be a multiple of majorNickInterval.
*
* @param value value per nick
* @param nicks number of nicks
*/
public void setValuePerNick(float value) {
valuePerNick = value;
public void setTotalNicks(int nicks) {
totalNicks = nicks;
initValues();
validate();
invalidate();
Expand All @@ -637,6 +669,19 @@ public void setValuePerNick(float value) {
*/
public void setMajorNickInterval(int interval) {
majorNickInterval = interval;
initValues();
validate();
invalidate();
}

/**
* Set the class to handle when to draw a nick.
*
* @param nickHandler major nick interval
*/
public void setNickHandler(IGaugeNick nickHandler) {
this.gaugeNick = nickHandler;
initValues();
validate();
invalidate();
}
Expand Down Expand Up @@ -671,4 +716,4 @@ private static Spanned fromHtml(String html) {
return result;
}

}
}
7 changes: 7 additions & 0 deletions gauge/src/main/java/de/nitri/gauge/IGaugeNick.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package de.nitri.gauge;

public interface IGaugeNick {
boolean shouldDrawMajorNick(int nick, float value);
boolean shouldDrawHalfNick(int nick, float value);
String getLabelString(int nick, float value);
}
10 changes: 6 additions & 4 deletions gauge/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@

<declare-styleable name="Gauge">
<attr name="totalNicks" format="integer" />
<attr name="valuePerNick" format="float" />
<attr name="startAngle" format="float" />
<attr name="endAngle" format="float" />
<attr name="majorNickInterval" format="integer" />
<attr name="minValue" format="float" />
<attr name="maxValue" format="float" />
<attr name="intScale" format="boolean" />
<attr name="initialValue" format="float" />
<attr name="labelTextSize" format="float" />
<attr name="faceColor" format="integer" />
<attr name="scaleColor" format="integer" />
<attr name="faceColor" format="color" />
<attr name="rimColor" format="color" />
<attr name="scaleColor" format="color" />
<attr name="needleShadow" format="boolean" />
<attr name="needleColor" format="integer" />
<attr name="needleColor" format="color" />
<attr name="textSize" format="float" />
<attr name="upperText" format="string" />
<attr name="lowerText" format="string" />
Expand Down