Android学习笔记之自定义View(钢琴键盘部件)

转眼都过去大半个月了,自己选择了很多路,也想让自己走出个名堂来。总是把很多事情都想得太美好。或者说一无所知的走下去必有收获。最近在家里闲着,工作也不好找,所以自己趁空余时间学习了,在学习中看到了自定义视图,觉得很不错,分享给大家,希望能有更多的朋友一起来学习,这是我的QQ群170548167

先来描述一下功能:

1、点击按键,可以播放音频,所有这些都是有view类来管理的。

2、用户可以使用多个手指点击

先上图:


 

这里先看看第一部分,自定义View视图:

 

 

public class PianoKeyborad extends View {

    private Context mContext;

    //最多支持5个手指弹奏
    public static final int MAX_FINGERS = 5;
    //设置5个黑色的按键
//    public static final int BLACK_KEYS_COUNT = 5;
    // 7个白色按键
    public static final int WHITE_KEYS_COUNT = 7;

    //黑色按钮的宽比
    public static final float BLACK_TO_WHITE_WIDTH_RATIO = 0.625f;
    //黑色按钮的高比
    public static final float BLACK_TO_WHITE_HEIGHT_RATIO = 0.58f;
    //设置最多支持5个手指弹奏
    private Point[] mFingerPoints = new Point[MAX_FINGERS];
    private int[] mFingerTones = new int[MAX_FINGERS];
    //默认的颜色和按下的颜色(白色按下、黑色按下)
    private Paint mWhiteKeyPaint, mWhiteKeyHitPaint,
            mBlackKeyPaint, mBlackKeyHitPaint;
    //设置音符的按键
    private Paint mCKeyPaint, mCSharpKeyPaint, mDKeyPaint,
            mDSharpKeyPaint, mEKeyPaint, mFKeyPaint,
            mFSharpKeyPaint, mGKeyPaint, mGSharpKeyPaint,
            mAKeyPaint, mASharpKeyPaint, mBKeyPaint;
    private Rect mCKey = new Rect(), mCSharpKey = new Rect(),
            mDKey = new Rect(), mDSharpKey = new Rect(),
            mEKey = new Rect(), mFKey = new Rect(),
            mFSharpKey = new Rect(), mGKey = new Rect(),
            mGSharpKey = new Rect(), mAKey = new Rect(),
            mASharpKey = new Rect(), mBKey = new Rect();


    private MotionEvent.PointerCoords mPointerCoords;

    public PianoKeyborad(Context context) {
        super(context);
        mContext = context;
    }

    public PianoKeyborad(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public PianoKeyborad(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mPointerCoords = new MotionEvent.PointerCoords();
        Arrays.fill(mFingerPoints, null);
        Arrays.fill(mFingerTones, -1);
        setupPaints();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();

    }

    private void setupPaints() {
        mWhiteKeyPaint = new Paint();
        mWhiteKeyPaint.setStyle(Paint.Style.STROKE);
        mWhiteKeyPaint.setColor(Color.BLACK);
        mWhiteKeyPaint.setStrokeWidth(3);
        mWhiteKeyPaint.setAntiAlias(true);
        mCKeyPaint = mWhiteKeyPaint;
        mDKeyPaint = mWhiteKeyPaint;
        mEKeyPaint = mWhiteKeyPaint;
        mFKeyPaint = mWhiteKeyPaint;
        mGKeyPaint = mWhiteKeyPaint;
        mAKeyPaint = mWhiteKeyPaint;
        mBKeyPaint = mWhiteKeyPaint;

        mWhiteKeyHitPaint = new Paint(mWhiteKeyPaint);
        mWhiteKeyHitPaint.setColor(Color.LTGRAY);
        mWhiteKeyHitPaint.setStyle(Paint.Style.FILL_AND_STROKE);

        mBlackKeyPaint = new Paint();
        mBlackKeyPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mBlackKeyPaint.setColor(Color.BLACK);
        mBlackKeyPaint.setAntiAlias(true);
        mCSharpKeyPaint = mBlackKeyPaint;
        mDSharpKeyPaint = mBlackKeyPaint;
        mFSharpKeyPaint = mBlackKeyPaint;
        mGSharpKeyPaint = mBlackKeyPaint;
        mASharpKeyPaint = mBlackKeyPaint;

        mBlackKeyHitPaint = new Paint(mBlackKeyPaint);
        mBlackKeyHitPaint.setColor(Color.DKGRAY);
    }
}

这段代码显示了PianoKeyBoard类所需的成员变量以及构造函数和onAttachedToWindow()onDetachedFromWindow()回调。  接下来创建了不同的Paint对象,每个对象对应一个可能的按键状态。另外注意的是,这里为每个按键创建了一个Rect成员对象,并用默认值初始化这些变量。

 

第二部分,onLayout()和onDraw()方法:

 

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        //获取屏幕大小
        int width = getWidth();
        int height = getHeight();
        //计算每个按键的大小
        int whiteKeyWidth = width / WHITE_KEYS_COUNT;
        int blackKeyWidth = (int) (whiteKeyWidth * BLACK_TO_WHITE_WIDTH_RATIO);
        int blackKeyHeight = (int) (height * BLACK_TO_WHITE_HEIGHT_RATIO);

        mCKey.set(0 * whiteKeyWidth, 0, 1 * whiteKeyWidth, height);
        mDKey.set(1 * whiteKeyWidth, 0, 2 * whiteKeyWidth, height);
        mEKey.set(2 * whiteKeyWidth, 0, 3 * whiteKeyWidth, height);
        mFKey.set(3 * whiteKeyWidth, 0, 4 * whiteKeyWidth, height);
        mGKey.set(4 * whiteKeyWidth, 0, 5 * whiteKeyWidth, height);
        mAKey.set(5 * whiteKeyWidth, 0, 6 * whiteKeyWidth, height);
        mBKey.set(6 * whiteKeyWidth, 0, 7 * whiteKeyWidth, height);

        mCSharpKey.set(1 * whiteKeyWidth - (blackKeyWidth / 2), 0,
                1 * whiteKeyWidth + (blackKeyWidth / 2), blackKeyHeight);
        mDSharpKey.set(2 * whiteKeyWidth - (blackKeyWidth / 2), 0,
                2 * whiteKeyWidth + (blackKeyWidth / 2), blackKeyHeight);
        mFSharpKey.set(4 * whiteKeyWidth - (blackKeyWidth / 2), 0,
                4 * whiteKeyWidth + (blackKeyWidth / 2), blackKeyHeight);
        mGSharpKey.set(5 * whiteKeyWidth - (blackKeyWidth / 2), 0,
                5 * whiteKeyWidth + (blackKeyWidth / 2), blackKeyHeight);
        mASharpKey.set(6 * whiteKeyWidth - (blackKeyWidth / 2), 0,
                6 * whiteKeyWidth + (blackKeyWidth / 2), blackKeyHeight);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制白色按键
        canvas.drawRect(mCKey, mCKeyPaint);
        canvas.drawRect(mDKey, mDKeyPaint);
        canvas.drawRect(mEKey, mEKeyPaint);
        canvas.drawRect(mFKey, mFKeyPaint);
        canvas.drawRect(mGKey, mGKeyPaint);
        canvas.drawRect(mAKey, mAKeyPaint);
        canvas.drawRect(mBKey, mBKeyPaint);

        //绘制黑色按键,这里黑色按键会在白色按键上面
        canvas.drawRect(mCSharpKey, mCSharpKeyPaint);
        canvas.drawRect(mDSharpKey, mDSharpKeyPaint);
        canvas.drawRect(mFSharpKey, mFSharpKeyPaint);
        canvas.drawRect(mGSharpKey, mGSharpKeyPaint);
        canvas.drawRect(mASharpKey, mASharpKeyPaint);
    }


在onLayout()方法中,计算了每个按键的大小和位置。在onDraw()方法中,应避免执行任何耗时操作,而只关注实际的绘制,从而避免潜在的性能问题。

 

最后一部分,也就是添加点击按键的效果了,onTouchEvent()方法:

 

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //记录用了多少个手指头来点击屏幕
        int pointerCount = event.getPointerCount();
        //有多个手指头点击屏幕,在此判断。
        //如果当前手指数量大于设置的最大数量
        int cappedPointerCount = pointerCount > MAX_FINGERS ? MAX_FINGERS : pointerCount;
        int actionIndex = event.getActionIndex();
        int action = event.getActionMasked();
        int id = event.getPointerId(actionIndex);
        //检查是否收到了手指的按下或者抬起的动作
        if ((action == MotionEvent.ACTION_DOWN ||
                action == MotionEvent.ACTION_POINTER_DOWN)
                && id < MAX_FINGERS) {
            mFingerPoints[id] = new Point((int) event.getX(actionIndex),
                    (int) event.getY(actionIndex));
        } else if ((action == MotionEvent.ACTION_POINTER_UP ||
                action == MotionEvent.ACTION_UP)
                && id < MAX_FINGERS) {
            mFingerPoints[id] = null;
            invalidateKey(mFingerTones[id]);
            mFingerTones[id] = -1;

        }
        for (int i = 0; i < cappedPointerCount; i++) {
            int index = event.findPointerIndex(i);
            if (mFingerPoints[i] != null && index != -1) {
                mFingerPoints[i].set((int) event.getX(index),
                        (int) event.getY(index));
                int tone = getToneForPoint(mFingerPoints[i]);
                if (tone != mFingerTones[i] && tone != -1) {
                    invalidateKey(mFingerTones[i]);
                    mFingerTones[i] = tone;
                    invalidateKey(mFingerTones[i]);
                    if (!isKeyDown(i)) {
                        event.getPointerCoords(index, mPointerCoords);
                        Toast.makeText(mContext, "***" + mFingerTones[i], Toast.LENGTH_SHORT).show();

                    }
                }
            }
        }
        updatePaints();
        return true;
    }


这里要说一下,对于每一个MotionEvent事件,都要检查是否有新的手指触摸了屏幕。如果有的话,需要新建一个Point对象,并把它存储到数组中以便追踪用户的手指轨迹。如果有ACTION_UP事件发生,则需要移除相应的Point对象。

 

接下来需要遍历所有通过MotionEvent追踪的point,并检查在上一次调用该方法后他们是否已被移除。


 

 

你可能感兴趣的:(学习笔记)