Add ScrollStripView

This view is a scrollable and zoomable strip. It will be used for
scrolling the keyboard.
master
Raph Levien 11 years ago
parent 1c6027bc3b
commit 5073a65ad1
  1. 10
      android/res/layout/piano2.xml
  2. 4
      android/src/com/levien/synthesizer/android/ui/PianoActivity2.java
  3. 22
      android/src/com/levien/synthesizer/android/widgets/keyboard/KeyboardView.java
  4. 192
      android/src/com/levien/synthesizer/android/widgets/keyboard/ScrollStripView.java

@ -84,6 +84,16 @@
--> -->
</LinearLayout> </LinearLayout>
</TableRow> </TableRow>
<TableRow
>
<com.levien.synthesizer.android.widgets.keyboard.ScrollStripView
android:id="@+id/scrollstrip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_span="6"
/>
</TableRow>
<TableRow> <TableRow>
<com.levien.synthesizer.android.widgets.keyboard.KeyboardView <com.levien.synthesizer.android.widgets.keyboard.KeyboardView
android:id="@+id/piano" android:id="@+id/piano"

@ -44,9 +44,9 @@ import com.levien.synthesizer.R;
import com.levien.synthesizer.android.service.SynthesizerService; import com.levien.synthesizer.android.service.SynthesizerService;
import com.levien.synthesizer.android.widgets.keyboard.KeyboardSpec; import com.levien.synthesizer.android.widgets.keyboard.KeyboardSpec;
import com.levien.synthesizer.android.widgets.keyboard.KeyboardView; import com.levien.synthesizer.android.widgets.keyboard.KeyboardView;
import com.levien.synthesizer.android.widgets.keyboard.ScrollStripView;
import com.levien.synthesizer.android.widgets.knob.KnobListener; import com.levien.synthesizer.android.widgets.knob.KnobListener;
import com.levien.synthesizer.android.widgets.knob.KnobView; import com.levien.synthesizer.android.widgets.knob.KnobView;
import com.levien.synthesizer.android.widgets.piano.PianoView;
import com.levien.synthesizer.core.midi.MidiAdapter; import com.levien.synthesizer.core.midi.MidiAdapter;
import com.levien.synthesizer.core.midi.MidiListener; import com.levien.synthesizer.core.midi.MidiListener;
@ -67,6 +67,8 @@ public class PianoActivity2 extends Activity {
//piano_ = (PianoView)findViewById(R.id.piano); //piano_ = (PianoView)findViewById(R.id.piano);
keyboard_ = (KeyboardView)findViewById(R.id.piano); keyboard_ = (KeyboardView)findViewById(R.id.piano);
keyboard_.setKeyboardSpec(KeyboardSpec.make3Layer()); keyboard_.setKeyboardSpec(KeyboardSpec.make3Layer());
ScrollStripView scrollStrip_ = (ScrollStripView)findViewById(R.id.scrollstrip);
scrollStrip_.bindKeyboard(keyboard_);
cutoffKnob_ = (KnobView)findViewById(R.id.cutoffKnob); cutoffKnob_ = (KnobView)findViewById(R.id.cutoffKnob);
resonanceKnob_ = (KnobView)findViewById(R.id.resonanceKnob); resonanceKnob_ = (KnobView)findViewById(R.id.resonanceKnob);
overdriveKnob_ = (KnobView)findViewById(R.id.overdriveKnob); overdriveKnob_ = (KnobView)findViewById(R.id.overdriveKnob);

@ -32,8 +32,8 @@ import com.levien.synthesizer.core.midi.MidiListener;
public class KeyboardView extends View { public class KeyboardView extends View {
public KeyboardView(Context context, AttributeSet attrs) { public KeyboardView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
nKeys_ = 25; // TODO: make configurable nKeys_ = 108; // TODO: make configurable
firstKey_ = 48; firstKey_ = 0;
noteStatus_ = new byte[128]; noteStatus_ = new byte[128];
noteForFinger_ = new int[FINGERS]; noteForFinger_ = new int[FINGERS];
for (int i = 0; i < FINGERS; i++) { for (int i = 0; i < FINGERS; i++) {
@ -49,11 +49,13 @@ public class KeyboardView extends View {
strokeWidth_ = 1.0f * density; strokeWidth_ = 1.0f * density;
paint_.setStrokeWidth(strokeWidth_); paint_.setStrokeWidth(strokeWidth_);
keyboardSpec_ = KeyboardSpec.make2Layer(); keyboardSpec_ = KeyboardSpec.make2Layer();
offset_ = 0.0f;
zoom_ = 1.0f;
} }
public void setKeyboardSpec(KeyboardSpec keyboardSpec) { public void setKeyboardSpec(KeyboardSpec keyboardSpec) {
keyboardSpec_ = keyboardSpec; keyboardSpec_ = keyboardSpec;
keyboardScale_ = 1.0f / keyboardSpec_.repeatWidth * keyboardSpec_.keys.length / nKeys_; keyboardScale_ = zoom_ / keyboardSpec_.repeatWidth * keyboardSpec_.keys.length / nKeys_;
invalidate(); invalidate();
} }
@ -68,13 +70,20 @@ public class KeyboardView extends View {
} }
} }
public void setScrollZoom(float offset, float zoom) {
offset_ = offset;
zoom_ = zoom;
keyboardScale_ = zoom_ / keyboardSpec_.repeatWidth * keyboardSpec_.keys.length / nKeys_;
invalidate();
}
@Override @Override
protected void onDraw(Canvas canvas) { protected void onDraw(Canvas canvas) {
super.onDraw(canvas); super.onDraw(canvas);
getDrawingRect(drawingRect_); getDrawingRect(drawingRect_);
float xscale = (drawingRect_.width() - strokeWidth_) * keyboardScale_; float xscale = (drawingRect_.width() - strokeWidth_) * keyboardScale_;
float yscale = (drawingRect_.height() - strokeWidth_) / keyboardSpec_.height; float yscale = (drawingRect_.height() - strokeWidth_) / keyboardSpec_.height;
float x0 = drawingRect_.left + strokeWidth_ * 0.5f; float x0 = drawingRect_.left + strokeWidth_ * 0.5f + offset_;
float y0 = drawingRect_.top + strokeWidth_ * 0.5f; float y0 = drawingRect_.top + strokeWidth_ * 0.5f;
for (int i = 0; i < nKeys_; i++) { for (int i = 0; i < nKeys_; i++) {
KeySpec ks = keyboardSpec_.keys[i % keyboardSpec_.keys.length]; KeySpec ks = keyboardSpec_.keys[i % keyboardSpec_.keys.length];
@ -224,7 +233,7 @@ public class KeyboardView extends View {
/* convert x and y to KeyboardSpec space */ /* convert x and y to KeyboardSpec space */
float xscale = (drawingRect_.width() - strokeWidth_) * keyboardScale_; float xscale = (drawingRect_.width() - strokeWidth_) * keyboardScale_;
float yscale = (drawingRect_.height() - strokeWidth_) / keyboardSpec_.height; float yscale = (drawingRect_.height() - strokeWidth_) / keyboardSpec_.height;
float xk = (x - 0.5f * strokeWidth_) / xscale; float xk = (x - 0.5f * strokeWidth_ - offset_) / xscale;
float yk = (y - 0.5f * strokeWidth_) / yscale; float yk = (y - 0.5f * strokeWidth_) / yscale;
for (int i = 0; i < nKeys_; i++) { for (int i = 0; i < nKeys_; i++) {
KeySpec ks = keyboardSpec_.keys[i % keyboardSpec_.keys.length]; KeySpec ks = keyboardSpec_.keys[i % keyboardSpec_.keys.length];
@ -317,6 +326,9 @@ public class KeyboardView extends View {
private float textSize_; private float textSize_;
private float keyboardScale_; private float keyboardScale_;
private float offset_;
private float zoom_;
private KeyboardSpec keyboardSpec_; private KeyboardSpec keyboardSpec_;
private int nKeys_; private int nKeys_;
private int firstKey_; private int firstKey_;

@ -0,0 +1,192 @@
/*
* Copyright 2013 Google Inc.
*
* 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 com.levien.synthesizer.android.widgets.keyboard;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class ScrollStripView extends View {
public ScrollStripView(Context context, AttributeSet attrs) {
super(context, attrs);
drawingRect_ = new Rect();
paint_ = new Paint();
touchDown_ = new boolean[2];
touchx_ = new float[2];
offset_ = -2000.0f; // TODO: make more systematic
zoom_ = 3.0f;
}
public void bindKeyboard(KeyboardView kv) {
keyboardView_ = kv;
kv.setScrollZoom(offset_, zoom_);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
getDrawingRect(drawingRect_);
float x0 = 100f + offset_;
float x1 = x0 + 100f * zoom_;
paint_.setStyle(Paint.Style.STROKE);
canvas.drawLine(x0, drawingRect_.top, x0, drawingRect_.bottom, paint_);
canvas.drawLine(x1, drawingRect_.top, x1, drawingRect_.bottom, paint_);
}
private boolean onTouchDown(int id, float x) {
touchx_[id] = x;
if (!touchDown_[0] && !touchDown_[1]) {
deltaAtTouch_ = offset_ - x;
} else {
deltaAtTouch_ = offset_ - touchx_[0];
zoomAtTouch_ = zoom_;
scaleAtTouch_ = zoom_ / (touchx_[1] - touchx_[0]);
}
touchDown_[id] = true;
return false;
}
private boolean onTouchUp(int id) {
touchDown_[id] = false;
if (touchDown_[1 - id]) {
// Going from 2-finger (zoom) back to 1-finger (scroll)
deltaAtTouch_ = offset_ - touchx_[1 - id];
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
boolean redraw = false;
if (actionCode == MotionEvent.ACTION_DOWN) {
int pointerId = event.getPointerId(0);
if (pointerId < 2 && pointerId >= 0) {
redraw = onTouchDown(pointerId, event.getX());
}
} else if (actionCode == MotionEvent.ACTION_POINTER_DOWN) {
int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
int pointerId = event.getPointerId(pointerIndex);
if (pointerId < 2 && pointerId >= 0) {
redraw = onTouchDown(pointerId, event.getX(pointerIndex));
}
} else if (actionCode == MotionEvent.ACTION_UP) {
int pointerId = event.getPointerId(0);
if (pointerId < 2 && pointerId >= 0) {
onTouchUp(pointerId);
}
} else if (actionCode == MotionEvent.ACTION_POINTER_UP) {
int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
int pointerId = event.getPointerId(pointerIndex);
if (pointerId < 2 && pointerId >= 0) {
onTouchUp(pointerId);
}
} else if (actionCode == MotionEvent.ACTION_MOVE) {
for (int pointerIndex = 0; pointerIndex < event.getPointerCount(); pointerIndex++) {
int pointerId = event.getPointerId(pointerIndex);
if (pointerId < 2 && pointerId >= 0) {
touchx_[pointerId] = event.getX(pointerIndex);
}
}
if (touchDown_[0] && !touchDown_[1]) {
offset_ = touchx_[0] + deltaAtTouch_;
redraw = true;
} else if (!touchDown_[0] && touchDown_[1]) {
offset_ = touchx_[1] + deltaAtTouch_;
redraw = true;
} else if (touchDown_[0] && touchDown_[1]) {
zoom_ = scaleAtTouch_ * (touchx_[1] - touchx_[0]);
offset_ = touchx_[0] + zoom_ / zoomAtTouch_ * deltaAtTouch_;
redraw = true;
}
offset_ = Math.min(0, offset_);
// TODO: max offset
}
if (redraw) {
if (keyboardView_ != null) {
keyboardView_.setScrollZoom(offset_, zoom_);
}
invalidate();
}
return true;
}
/**
* Layout measurement for this widget.
* This method just sets a basic minimum size and makes the widget maximized otherwise.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0;
int height = 0;
float density = getResources().getDisplayMetrics().density;
int maxHeight = (int) (50.0f * density + 0.5f);
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.AT_MOST:
width = widthSize;
break;
case MeasureSpec.UNSPECIFIED:
width = 10;
break;
}
switch (heightMode) {
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.AT_MOST:
height = Math.min(maxHeight, heightSize);
break;
case MeasureSpec.UNSPECIFIED:
height = 10;
break;
}
setMeasuredDimension(width, height);
}
Rect drawingRect_;
Paint paint_;
float offset_;
float zoom_;
// touch state
boolean[] touchDown_;
float[] touchx_;
float deltaAtTouch_;
float zoomAtTouch_;
float scaleAtTouch_;
KeyboardView keyboardView_;
}
Loading…
Cancel
Save