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,並檢查在上一次調用該方法後他們是否已被移除。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章