KnobView UI refresh

New style (simpler, light theme). New interaction gesture (simple linear
movement rather than emulation of rotary motion).
master
Raph Levien 11 years ago
parent 117f0557b0
commit facbb3cc94
  1. 198
      android/src/com/levien/synthesizer/android/widgets/knob/KnobView.java

@ -23,11 +23,8 @@ import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Paint.Align; import android.graphics.Paint.Align;
import android.graphics.Paint.Style; import android.graphics.Paint.Style;
import android.graphics.RadialGradient;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.RectF; import android.graphics.RectF;
import android.graphics.Shader.TileMode;
import android.graphics.SweepGradient;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
@ -55,15 +52,17 @@ public class KnobView extends View {
a.recycle(); a.recycle();
// Set up the drawing structures. // Set up the drawing structures.
knobPaint_ = new Paint(); paint_ = new Paint();
knobPaint_.setAntiAlias(true); paint_.setAntiAlias(true);
knobPaint_.setColor(Color.WHITE); paint_.setColor(Color.WHITE);
float density = getResources().getDisplayMetrics().density; float density = getResources().getDisplayMetrics().density;
knobPaint_.setStrokeWidth(2.0f * density); sensitivity_ = .01f * (float)(max_ - min_) / density; // TODO: should be configurable
arcWidth_ = 8.0f * density; strokeWidth_ = 1.0f * density;
rect_ = new Rect(); rect_ = new Rect();
rectF_ = new RectF(); rectF_ = new RectF();
textRect_ = new Rect(); innerRectF_ = new RectF();
textHeight_ = 18f * density; // probably should be set by XML parameter
paint_.setTextSize(textHeight_);
// The listener has to be set later. // The listener has to be set later.
listener_ = null; listener_ = null;
@ -81,36 +80,17 @@ public class KnobView extends View {
switch (action) { switch (action) {
case MotionEvent.ACTION_DOWN: { case MotionEvent.ACTION_DOWN: {
// Just record the current finger position. // Just record the current finger position.
xyAtTouch_ = event.getX() - event.getY();
valueAtTouch_ = knobValue_;
getDrawingRect(rect_); getDrawingRect(rect_);
previousX_ = event.getX() - rect_.centerX();
previousY_ = event.getY() - rect_.centerY();
currentTouchAngle_ = knobValue_ * 2 * Math.PI * 0.8 + (Math.PI / 5.0);
diffAngle_ = 0;
break; break;
} }
case MotionEvent.ACTION_MOVE: { case MotionEvent.ACTION_MOVE: {
getDrawingRect(rect_); float xyDelta = event.getX() - event.getY() - xyAtTouch_;
knobValue_ = valueAtTouch_ + sensitivity_ * xyDelta;
// Compare the previous angle of the finger position (relative to the center of the control) knobValue_ = Math.min(knobValue_, max_);
// to the new angle, and update the value accordingly. knobValue_ = Math.max(knobValue_, min_);
currentTouchAngle_ = knobValue_ * 2 * Math.PI * 0.8 + (Math.PI / 5.0);
currentX_ = event.getX() - rect_.centerX();
currentY_ = event.getY() - rect_.centerY();
diffAngle_ = Math.atan2(currentY_, currentX_) - Math.atan2(previousY_, previousX_);
if (diffAngle_ > Math.PI) {
diffAngle_ -= Math.PI * 2;
} else if (diffAngle_ < -Math.PI) {
diffAngle_ += Math.PI * 2;
}
currentTouchAngle_ += diffAngle_;
knobValue_ = currentTouchAngle_ / (2.0 * Math.PI);
if (knobValue_ < 0.1) knobValue_ = 0.1;
if (knobValue_ > 0.9) knobValue_ = 0.9;
knobValue_ -= 0.1;
knobValue_ /= 0.8;
previousX_ = currentX_;
previousY_ = currentY_;
// Notify listener and redraw. // Notify listener and redraw.
if (listener_ != null) { if (listener_ != null) {
@ -194,99 +174,53 @@ public class KnobView extends View {
rectF_.right = center + rectF_.height() / 2; rectF_.right = center + rectF_.height() / 2;
} }
// Draw outer white glow. float border = textHeight_ + rectF_.width() * 0.05f;
int[] radialGradientColors = {Color.WHITE, Color.WHITE, 0x00000000};
float[] radialGradientPositions = {0.0f, 0.87f, 1.0f};
radialGradient_ = new RadialGradient(rect_.exactCenterX(),
rect_.exactCenterY(),
Math.min(rect_.width(), rect_.height()) / 2,
radialGradientColors,
radialGradientPositions,
TileMode.CLAMP);
knobPaint_.setShader(radialGradient_);
canvas.drawCircle(rect_.exactCenterX(),
rect_.exactCenterY(),
Math.min(rect_.width(), rect_.height()) / 2,
knobPaint_);
// Draw outer gauge.
final int fullDark = Color.BLACK;
final int guageStartColor = 0xff202050;
final int guageEndColor = 0xff4040A0;
final int adjustedStartColor = Color.argb(
0xFF,
(int)(0.1875 * Color.red(guageStartColor) + (1.0 - 0.1875) * Color.red(guageEndColor)),
(int)(0.1875 * Color.green(guageStartColor) + (1.0 - 0.1875) * Color.green(guageEndColor)),
(int)(0.1875 * Color.blue(guageStartColor) + (1.0 - 0.1875) * Color.blue(guageEndColor)));
int[] sweepGradientColors = {
adjustedStartColor,
guageEndColor,
fullDark,
fullDark,
guageStartColor,
adjustedStartColor};
float[] sweepGradientPositions = { 0.0f, 0.16f, 0.16f, 0.35f, 0.35f, 1.0f };
sweepGradient_ = new SweepGradient(rect_.exactCenterX(),
rect_.exactCenterY(),
sweepGradientColors,
sweepGradientPositions);
knobPaint_.setShader(sweepGradient_);
canvas.drawCircle(rect_.exactCenterX(),
rect_.exactCenterY(),
Math.min(rect_.width(), rect_.height()) / 2.4f,
knobPaint_);
// Draw inner gauge.
knobPaint_.setShader(null);
knobPaint_.setStyle(Style.FILL);
knobPaint_.setColor(Color.BLACK);
canvas.drawCircle(rect_.exactCenterX(),
rect_.exactCenterY(),
Math.min(rect_.width(), rect_.height()) / 4,
knobPaint_);
// Draw inner white glow.
int[] innerRadialGradientColors = { 0x00000000, 0x00000000, Color.WHITE };
float[] innerRadialGradientPositions = { 0.0f, 0.6f, 1.0f };
innerRadialGradient_ = new RadialGradient(rect_.exactCenterX(),
rect_.exactCenterY(),
Math.min(rect_.width(), rect_.height()) / 4f,
innerRadialGradientColors,
innerRadialGradientPositions,
TileMode.CLAMP);
knobPaint_.setShader(innerRadialGradient_);
canvas.drawCircle(rect_.exactCenterX(),
rect_.exactCenterY(),
Math.min(rect_.width(),rect_.height()) / 4,
knobPaint_);
// Draw indicator. // Draw indicator.
knobPaint_.setShader(null);
knobPaint_.setColor(Color.BLACK); innerRectF_.set(rectF_);
knobPaint_.setStyle(Style.STROKE); float inner = border;
canvas.drawArc(rectF_, innerRectF_.inset(inner, inner);
(float)(knobValue_ * 360 * 0.8 + 90 - arcWidth_ / 2 + 36), paint_.setShader(null);
(float)arcWidth_, paint_.setColor(0xffcccccc);
paint_.setStyle(Style.STROKE);
paint_.setStrokeWidth(rectF_.width() * 0.1f);
float startAngle = startAngle_ + 90f;
float range = 360f - 2 * startAngle_ - arcWidth_;
float sweepAngle = (float)((knobValue_ - min_) / (max_ - min_))* range;
canvas.drawArc(innerRectF_,
startAngle,
sweepAngle + 0.5f * arcWidth_,
false, false,
knobPaint_); paint_);
paint_.setColor(Color.BLACK);
canvas.drawArc(innerRectF_,
startAngle + sweepAngle,
arcWidth_,
false,
paint_);
// Draw outer circle.
paint_.setColor(Color.BLACK);
paint_.setStyle(Paint.Style.STROKE);
paint_.setStrokeWidth(strokeWidth_);
canvas.drawCircle(rect_.exactCenterX(),
rect_.exactCenterY(),
Math.min(rect_.width(), rect_.height()) * 0.45f - border,
paint_);
// Draw text. // Draw text.
String knobValueString = String.format("%.2f", getValue()); String knobValueString = String.format("%.2f", getValue());
Typeface typeface = Typeface.DEFAULT_BOLD; Typeface typeface = Typeface.DEFAULT_BOLD;
knobPaint_.setColor(Color.WHITE); paint_.setColor(Color.BLACK);
knobPaint_.setTypeface(typeface); paint_.setTypeface(typeface);
knobPaint_.setTextAlign(Align.CENTER); paint_.setTextAlign(Align.CENTER);
knobPaint_.setTextSize(rectF_.width() / 8); paint_.setSubpixelText(true);
knobPaint_.setSubpixelText(true); paint_.setStyle(Style.FILL);
knobPaint_.setStyle(Style.FILL);
knobPaint_.getTextBounds(knobValueString, 0, knobValueString.length(), textRect_);
canvas.drawText(knobValueString, canvas.drawText(knobValueString,
rect_.centerX(), rect_.centerX(),
rect_.centerY() + textRect_.height() / 2, rect_.top + 0.8f * textHeight_,
knobPaint_); paint_);
} }
/** /**
@ -372,23 +306,21 @@ public class KnobView extends View {
private double max_; private double max_;
// Structures used in drawing that we don't want to reallocate every time we draw. // Structures used in drawing that we don't want to reallocate every time we draw.
private Paint knobPaint_; private Paint paint_;
private Rect rect_; private Rect rect_;
private Rect textRect_;
private RectF rectF_; private RectF rectF_;
private SweepGradient sweepGradient_; private RectF innerRectF_;
private RadialGradient radialGradient_;
private RadialGradient innerRadialGradient_; // Appearance and behavior parameters
private final float textHeight_;
private float arcWidth_; private final float arcWidth_ = 4.0f;
private final float startAngle_ = 36f; // relative to bottom
// Position of the finger relative to the knob. private final float strokeWidth_;
private double previousX_ = 0; private float sensitivity_;
private double previousY_ = 0;
private double currentX_ = 0; // State of drag gesture
private double currentY_ = 0; private float xyAtTouch_;
private double currentTouchAngle_ = 0; private double valueAtTouch_;
private double diffAngle_ = 0;
// Object listening for events when the knob's value changes. // Object listening for events when the knob's value changes.
private KnobListener listener_; private KnobListener listener_;

Loading…
Cancel
Save