A Piano Keyboarad View in Android
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context; | |
import android.content.res.TypedArray; | |
import android.graphics.Canvas; | |
import android.graphics.Color; | |
import android.graphics.Paint; | |
import android.graphics.RectF; | |
import android.util.AttributeSet; | |
import android.view.View; | |
import com.theonepiano.smartpiano.R; | |
import com.theonepiano.smartpiano.util.Utils; | |
import java.util.ArrayList; | |
import java.util.List; | |
public final class Keyboard extends View { | |
private int ROUND_X = 2, ROUND_Y = 2; | |
private float mWhiteKeyWidth, mWhiteKeyHeight, mBlackKeyWidth, mBlackKeyHeight; | |
/** | |
* The anchors pixels of all key. | |
*/ | |
private List<Float> mKeyAnchors = new ArrayList<>(); | |
private Paint mPaint = new Paint(); | |
/** | |
* The regular piano with 52 white keys,total 88 keys. | |
*/ | |
public static final int TYPE_PIANO = 52; | |
/** | |
* The light piano with 36 white keys,total 61 keys. | |
*/ | |
public static final int TYPE_LIGHT = 36; | |
/** | |
* Current keyboard type.{@link #TYPE_PIANO} or {@link #TYPE_LIGHT} | |
*/ | |
private int mKeyboardType = TYPE_PIANO; | |
public Keyboard(Context context) { | |
super(context); | |
} | |
public Keyboard(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.Keyboard); | |
mKeyboardType = ta.getInt(R.styleable.Keyboard_pianoType, TYPE_PIANO); | |
ta.recycle(); | |
} | |
public Keyboard(Context context, AttributeSet attrs, int defStyleAttr) { | |
super(context, attrs, defStyleAttr); | |
} | |
@Override | |
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { | |
super.onLayout(changed, left, top, right, bottom); | |
int whiteKeyCount = mKeyboardType; | |
//Notice:We should multiply by 1.0f to convert width to float type to reduce mistakes. | |
mWhiteKeyWidth = getWidth() * 1.0f / whiteKeyCount; | |
float blackWhiteWidthRatio = 13f / 20f; | |
mBlackKeyWidth = mWhiteKeyWidth * blackWhiteWidthRatio; | |
mWhiteKeyHeight = getHeight(); | |
float blackWhiteHeightRatio = 95f / 144f; | |
mBlackKeyHeight = mWhiteKeyHeight * blackWhiteHeightRatio; | |
} | |
@Override | |
public void draw(Canvas canvas) { | |
super.draw(canvas); | |
mPaint.setAntiAlias(true); | |
canvas.drawColor(Color.TRANSPARENT); | |
if (mKeyboardType == TYPE_PIANO) { | |
drawForPianoKeyboard(canvas); | |
} else if (mKeyboardType == TYPE_LIGHT) { | |
drawForLightKeyboard(canvas); | |
} | |
} | |
/** | |
* Drawer 88 key for piano. | |
* | |
* @param canvas | |
*/ | |
private void drawForPianoKeyboard(Canvas canvas) { | |
int whiteCount = drawKeys(canvas, 0, 3); | |
float base = computeSpecificDividerX(whiteCount); | |
for (int i = 0; i < 7; i++) { | |
float start = i * 7 * mWhiteKeyWidth; | |
whiteCount = whiteCount + drawOctave(canvas, base + start); | |
} | |
base = computeSpecificDividerX(whiteCount); | |
drawKeys(canvas, base, 1); | |
} | |
/** | |
* Draw 61 key for light piano. | |
* | |
* @param canvas | |
*/ | |
private void drawForLightKeyboard(Canvas canvas) { | |
int whiteCount = 0; | |
float base = computeSpecificDividerX(whiteCount); | |
for (int i = 0; i < 5; i++) { | |
float start = i * 7 * mWhiteKeyWidth; | |
whiteCount = whiteCount + drawOctave(canvas, base + start); | |
} | |
base = computeSpecificDividerX(whiteCount); | |
drawKeys(canvas, base, 1); | |
} | |
/** | |
* @param canvas | |
* @param base | |
* @param count | |
* @return white key count which been drew. | |
*/ | |
private int drawKeys(Canvas canvas, float base, int count) { | |
if (count <= 0) { | |
return 0; | |
} | |
if (count > 12) { | |
count = 12; | |
} | |
int whiteCount = 0; | |
float left, top, right, bottom; | |
for (int i = 0; i < count; i++) { | |
if (i == 1 || i == 3 || i == 6 || i == 8 || i == 10) { | |
//Black key | |
float axis = computeSpecificDividerX(whiteCount); | |
left = axis - mBlackKeyWidth / 2; | |
right = axis + mBlackKeyWidth / 2; | |
top = 0; | |
bottom = mBlackKeyHeight; | |
mKeyAnchors.add(base + left); | |
drawBlackKey(canvas, base + left, top, base + right, bottom); | |
} else { | |
//White key | |
left = computeSpecificDividerX(whiteCount); | |
right = computeSpecificDividerX(whiteCount + 1); | |
top = 0; | |
bottom = mWhiteKeyHeight; | |
mKeyAnchors.add(base + right); | |
drawWhiteKey(canvas, base + left, top, base + right, bottom); | |
whiteCount++; | |
} | |
} | |
return whiteCount; | |
} | |
private int drawOctave(Canvas canvas, float base) { | |
return drawKeys(canvas, base, 12); | |
} | |
private void drawWhiteKey(Canvas canvas, float left, float top, float right, float bottom) { | |
mPaint.setStyle(Paint.Style.STROKE); | |
mPaint.setColor(Color.parseColor("#000000")); | |
RectF rect = new RectF(left, top, right, bottom); | |
canvas.drawRoundRect(rect, ROUND_X, ROUND_Y, mPaint); | |
mPaint.reset(); | |
} | |
private void drawBlackKey(Canvas canvas, float left, float top, float right, float bottom) { | |
RectF rect = new RectF(left, top, right, bottom); | |
// mPaint.setStyle(Paint.Style.STROKE); | |
// mPaint.setColor(Color.parseColor("#FFFFFF")); | |
// canvas.drawRoundRect(rect, ROUND_X, ROUND_Y, mPaint); | |
mPaint.setStyle(Paint.Style.FILL); | |
mPaint.setColor(Color.parseColor("#000000")); | |
canvas.drawRoundRect(rect, ROUND_X, ROUND_Y, mPaint); | |
mPaint.reset(); | |
} | |
/** | |
* Set the keyboard type. {@link #TYPE_PIANO} or {@link #TYPE_LIGHT}. | |
* | |
* @param keyboardType | |
*/ | |
public void setKeyboardType(int keyboardType) { | |
if (mKeyboardType == keyboardType) return; | |
mKeyboardType = keyboardType; | |
} | |
private float computeSpecificDividerX(int i) { | |
return i * mWhiteKeyWidth; | |
} | |
/** | |
* @param axis | |
* @return | |
*/ | |
public int determineKeyNumber(float axis) { | |
int number = 0; | |
for (int i = 0; i < mKeyAnchors.size(); i++) { | |
float r = Math.abs(mKeyAnchors.get(i) - axis); | |
if (r <= mBlackKeyWidth / 2 || r <= mWhiteKeyWidth) { | |
number = i; | |
break; | |
} | |
} | |
return number; | |
} | |
/** | |
* Obtain the key pixels by key number. | |
* | |
* @param number | |
* @return | |
*/ | |
public float obtainKeyPixels(int number) { | |
if (Utils.isCollectionEmpty(mKeyAnchors)) { | |
return 0; | |
} | |
if (number < 0) { | |
return 0; | |
} | |
if (number >= mKeyAnchors.size()) { | |
return mKeyAnchors.size() - 1; | |
} | |
return mKeyAnchors.get(number); | |
} | |
/** | |
* Get current white key count. | |
* | |
* @return | |
*/ | |
public int getWhiteKeyCount() { | |
return mKeyboardType; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment