JavaScript
public class PianoView extends View {
private static final int[] NOTES = {60, 62, 64, 65, 67, 69, 71};
private Paint mKeyPaint;
private Paint mBlackKeyPaint;
private SparseBooleanArray mActivePointers;
private SoundPool mSoundPool;
private int[] mSoundIds;
public PianoView(Context context) {
super(context);
init(null, 0);
}
public PianoView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public PianoView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
mKeyPaint = new Paint();
mKeyPaint.setColor(Color.WHITE);
mKeyPaint.setStyle(Paint.Style.FILL);
mBlackKeyPaint = new Paint();
mBlackKeyPaint.setColor(Color.BLACK);
mBlackKeyPaint.setStyle(Paint.Style.FILL);
mActivePointers = new SparseBooleanArray();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
int keyWidth = width / NOTES.length;
for (int i = 0; i < NOTES.length; i++) {
int note = NOTES[i];
if (note % 12 == 1 || note % 12 == 4 || note % 12 == 6 || note % 12 == 9 || note % 12 == 11) {
// Draw black key
canvas.drawRect(i * keyWidth + keyWidth * 3 / 4, 0, (i + 1) * keyWidth - keyWidth / 4, height / 2, mBlackKeyPaint);
} else {
// Draw white key
canvas.drawRect(i * keyWidth, 0, (i + 1) * keyWidth, height, mKeyPaint);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
int pointerIndex = event.getActionIndex();
int pointerId = event.getPointerId(pointerIndex);
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
mActivePointers.put(pointerId, true);
int noteIndex = getNoteIndex(event.getX(pointerIndex), event.getY(pointerIndex));
int note = NOTES[noteIndex];
playNote(note);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
for (int i = 0; i < event.getPointerCount(); i++) {
int id = event.getPointerId(i);
if (mActivePointers.get(id)) {
int noteIndex2 = getNoteIndex(event.getX(i), event.getY(i));
int note2 = NOTES[noteIndex2];
playNote(note2);
}
}
invalidate();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
mActivePointers.put(pointerId, false);
stopNote();
invalidate();
break;
}
return true;
}
private int getNoteIndex(float x, float y) {
int width = getWidth();
int height = getHeight();
int keyWidth = width / NOTES.length;
int keyIndex = (int) (x / keyWidth);
int note = NOTES[keyIndex];
// Check if the touch event is on a black key
if (note % 12 == 1 || note % 12 == 4 || note % 12 == 6 || note % 12 == 9 || note % 12 == 11) {
float blackKeyWidth = keyWidth * 3 / 4;
float blackKeyHeight = height / 2;
float blackKeyY = blackKeyHeight;
if (y < blackKeyHeight) {
blackKeyY = 0;
}
float blackKeyX = keyIndex * keyWidth + keyWidth - blackKeyWidth / 2;
if (x < blackKeyX - keyWidth / 4 || x > blackKeyX + blackKeyWidth + keyWidth / 4) {
// Touch event is not on the black key, use the white key instead
keyIndex++;
note = NOTES[keyIndex];
} else {
return keyIndex;
}
}
return keyIndex;
}
private void playNote(int note) {
if (mSoundPool == null) {
mSoundPool = new SoundPool(7, AudioManager.STREAM_MUSIC, 0);
mSoundIds = new int[NOTES.length];
for (int i = 0; i < NOTES.length; i++) {
mSoundIds[i] = mSoundPool.load(getContext(), getSoundResourceId(NOTES[i]), 1);
}
}
int index = Arrays.binarySearch(NOTES, note);
if (index >= 0 && index < mSoundIds.length) {
mSoundPool.play(mSoundIds[index], 1.0f, 1.0f, 0, 0, 1.0f);
}
}
private void stopNote() {
if (mSoundPool != null) {
mSoundPool.release();
mSoundPool = null;
mSoundIds = null;
}
}
private int getSoundResourceId(int note) {
int resourceId = getResources().getIdentifier("note_" + note, "raw", getContext().getPackageName());
return resourceId;
}
}