Android-简易版弹钢琴

本文介绍如何在Android平台上实现一个简单的七键弹钢琴应用。通过drawable资源文件button_selector.xml和button.xml的设计,实现钢琴键的视觉效果和交互功能。

目标效果:

 

1.drawable下新建button_selector.xml页面:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/button_pressed" android:state_pressed="true"></item>
    <item android:drawable="@drawable/button"></item>

</selector>


2.drawable下新建button.xml页面:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >

    <corners
        android:bottomLeftRadius="10dp"
        android:bottomRightRadius="10dp" >
    </corners>

    <stroke
        android:width="2dp"
        android:color="#605C59" />

    <gradient
        android:angle="270"
        android:endColor="#FFFFFF"
        android:startColor="#F5F5F5" />

</shape>


3.drawable下新建button_pressed.xml页面:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >

    <solid android:color="#A4A4A4" />

    <corners
        android:bottomLeftRadius="10dp"
        android:bottomRightRadius="10dp" >
    </corners>

    <stroke
        android:width="2dp"
        android:color="#605C59" />

</shape>


4.新建PanioMusic.java类
package com.example.weixu.view;

/**
 * 音乐播放帮助类
 */

import java.util.HashMap;
import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;

import com.example.weixu.playpanio.R;

public class PanioMusic {
    // 资源文件
    int Music[] = {R.raw.do1, R.raw.re2, R.raw.mi3, R.raw.fa4, R.raw.sol5,
            R.raw.la6, R.raw.si7,};
    SoundPool soundPool;
    HashMap<Integer, Integer> soundPoolMap;

    public PanioMusic(Context context) {
        soundPool = new SoundPool(2, AudioManager.STREAM_MUSIC, 100);
        soundPoolMap = new HashMap<Integer, Integer>();
        for (int i = 0; i < Music.length; i++) {
            soundPoolMap.put(i, soundPool.load(context, Music[i], 1));
        }
    }

    public int soundPlay(int no) {
        return soundPool.play(soundPoolMap.get(no), 100, 100, 1, 0, 1.0f);
    }

    public int soundOver() {
        return soundPool.play(soundPoolMap.get(1), 100, 100, 1, 0, 1.0f);
    }

    @Override
    protected void finalize() throws Throwable {
        soundPool.release();
        super.finalize();
    }
}



5.activity_main.xml页面:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/llparent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <LinearLayout
        android:id="@+id/llKeys"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="5"
        android:orientation="horizontal"
        android:padding="10dp" >

        <Button
            android:id="@+id/btPanioOne"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@drawable/button"
            android:text="1" />

        <Button
            android:id="@+id/btPanioTwo"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@drawable/button"
            android:text="2" />

        <Button
            android:id="@+id/btPanioThree"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@drawable/button"
            android:text="3" />

        <Button
            android:id="@+id/btPanioFour"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@drawable/button"
            android:text="4" />

        <Button
            android:id="@+id/btPanioFive"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@drawable/button"
            android:text="5" />

        <Button
            android:id="@+id/btPanioSix"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@drawable/button"
            android:text="6" />

        <Button
            android:id="@+id/btPanioSeven"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@drawable/button"
            android:text="7" />
    </LinearLayout>

</LinearLayout>


6.MainActivity.java页面:
package com.example.weixu.playpanio;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Button;

import com.example.weixu.view.PanioMusic;

public class MainActivity extends Activity {
    private Button button[];// 按钮数组
    private PanioMusic utils;// 工具类
    private View parent;// 父视图
    private int buttonId[];// 按钮id
    private boolean havePlayed[];// 是否已经播放了声音,当手指在同一个按钮内滑动,且已经发声,就为true
    private View keys;// 按钮们所在的视图
    private int pressedkey[];

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        init();
        parent = (View) findViewById(R.id.llparent);
        parent.setClickable(true);

        parent.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int temp;
                int tempIndex;
                int pointercount;
                pointercount = event.getPointerCount();
                for (int count = 0; count < pointercount; count++) {
                    boolean moveflag = false;// 标记是否是在按键上移动
                    temp = isInAnyScale(event.getX(count), event.getY(count),
                            button);
                    if (temp != -1) {// 事件对应的是当前点
                        switch (event.getActionMasked()) {
                            case MotionEvent.ACTION_DOWN:
                                // // 单独一根手指或最先按下的那个
                                // pressedkey = temp;
                            case MotionEvent.ACTION_POINTER_DOWN:
                                Log.i("--", "count" + count);
                                pressedkey[count] = temp;
                                if (!havePlayed[temp]) {// 在某个按键范围内
                                    button[temp]
                                            .setBackgroundResource(R.drawable.button_pressed);
                                    // 播放音阶
                                    utils.soundPlay(temp);
                                    Log.i("--", "sound" + temp);
                                    havePlayed[temp] = true;
                                }
                                break;
                            case MotionEvent.ACTION_MOVE:
                                temp = pressedkey[count];
                                for (int i = temp + 1; i >= temp - 1; i--) {
                                    // 当在两端的按钮时,会有一边越界
                                    if (i < 0 || i >= button.length) {
                                        continue;
                                    }
                                    if (isInScale(event.getX(count),
                                            event.getY(count), button[i])) {// 在某个按键内
                                        moveflag = true;
                                        if (i != temp) {// 在相邻按键内
                                            boolean laststill = false;
                                            boolean nextstill = false;
                                            // 假设手指已经从上一个位置抬起,但是没有真的抬起,所以不移位
                                            pressedkey[count] = -1;
                                            for (int j = 0; j < pointercount; j++) {
                                                if (pressedkey[j] == temp) {
                                                    laststill = true;
                                                }
                                                if (pressedkey[j] == i) {
                                                    nextstill = true;
                                                }
                                            }

                                            if (!nextstill) {// 移入的按键没有按下
                                                // 设置当前按键
                                                button[i]
                                                        .setBackgroundResource(R.drawable.button_pressed);
                                                // 发音
                                                utils.soundPlay(i);
                                                havePlayed[i] = true;
                                            }

                                            pressedkey[count] = i;

                                            if (!laststill) {// 没有手指按在上面
                                                // 设置上一个按键
                                                button[temp]
                                                        .setBackgroundResource(R.drawable.button);
                                                havePlayed[temp] = false;
                                            }

                                            break;
                                        }
                                    }
                                }
                                break;
                            case MotionEvent.ACTION_UP:
                            case MotionEvent.ACTION_POINTER_UP:
                                // 事件与点对应
                                tempIndex = event.getActionIndex();
                                if (tempIndex == count) {
                                    Log.i("--", "index" + tempIndex);
                                    boolean still = false;
                                    // 当前点已抬起
                                    for (int t = count; t < 5; t++) {
                                        if (t != 4) {
                                            if (pressedkey[t + 1] >= 0) {
                                                pressedkey[t] = pressedkey[t + 1];
                                            } else {
                                                pressedkey[t] = -1;
                                            }
                                        } else {
                                            pressedkey[t] = -1;
                                        }

                                    }
                                    for (int i = 0; i < pressedkey.length; i++) {// 是否还有其他点
                                        if (pressedkey[i] == temp) {
                                            still = true;
                                            break;
                                        }
                                    }
                                    if (!still) {// 已经没有手指按在该键上
                                        button[temp]
                                                .setBackgroundResource(R.drawable.button);
                                        havePlayed[temp] = false;
                                        Log.i("--", "button" + temp + "up");
                                    }
                                    break;
                                }
                        }
                    }
                    //
                    if (event.getActionMasked() == MotionEvent.ACTION_MOVE
                            && !moveflag) {
                        if (pressedkey[count] != -1) {
                            button[pressedkey[count]]
                                    .setBackgroundResource(R.drawable.button);
                            havePlayed[pressedkey[count]] = false;
                        }
                    }
                }
                return false;
            }
        });

        keys = (View) findViewById(R.id.llKeys);
    }

    private void init() {
        // 新建工具类
        utils = new PanioMusic(getApplicationContext());

        // 按钮资源Id
        buttonId = new int[7];
        buttonId[0] = R.id.btPanioOne;
        buttonId[1] = R.id.btPanioTwo;
        buttonId[2] = R.id.btPanioThree;
        buttonId[3] = R.id.btPanioFour;
        buttonId[4] = R.id.btPanioFive;
        buttonId[5] = R.id.btPanioSix;
        buttonId[6] = R.id.btPanioSeven;

        button = new Button[7];
        havePlayed = new boolean[7];

        // 获取按钮对象
        for (int i = 0; i < button.length; i++) {
            button[i] = (Button) findViewById(buttonId[i]);
            button[i].setClickable(false);
            havePlayed[i] = false;
        }

        pressedkey = new int[5];
        for (int j = 0; j < pressedkey.length; j++) {
            pressedkey[j] = -1;
        }

    }

    /**
     * 判断某个点是否在某个按钮的范围内
     *
     * @param x      横坐标
     * @param y      纵坐标
     * @param button 按钮对象
     * @return 在:true;不在:false
     */
    private boolean isInScale(float x, float y, Button button) {
        // keys.getTop()是获取按钮所在父视图相对其父视图的右上角纵坐标

        if (x > button.getLeft() && x < button.getRight()
                && y > button.getTop() + keys.getTop()
                && y < button.getBottom() + keys.getTop()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 判断某个点是否在一个按钮集合中的某个按钮内
     *
     * @param x      横坐标
     * @param y      纵坐标
     * @param button 按钮数组
     * @return
     */
    private int isInAnyScale(float x, float y, Button[] button) {
        // keys.getTop()是获取按钮所在父视图相对其父视图的右上角纵坐标

        for (int i = 0; i < button.length; i++) {
            if (x > button[i].getLeft() && x < button[i].getRight()
                    && y > button[i].getTop() + keys.getTop()
                    && y < button[i].getBottom() + keys.getTop()) {
                return i;
            }
        }
        return -1;
    }
}


7.AndroidManifest.xml页面对某个Activity页面进行设置横屏
android:screenOrientation="landscape"


8.另外,每个按键的音效需要提前导入res下raw文件夹中。


源码: 点击打开链接


这个小例子主要用到android手势部分和Mediaplayer部分的知识。 手势部分没啥问题,我们可以通过MotionEvent中定义的Event Code来判断当前手势的状态(按下或者释放等) imageButton_white1.setOnTouchListener(new View.OnTouchListener() { public boolean onTouch(View view, MotionEvent motionEvent) { if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { play(R.raw.white1);//这个时候创建播放要用的资源文件 imageButton_white1.setImageResource(R.drawable.whiteback1); } if (motionEvent.getAction() == MotionEvent.ACTION_UP) { imageButton_white1.setImageResource(R.drawable.white1); } return false; } }); 复制代码 Mediaplayer部分目前还有个问题没有解决: 每次按下盘时我都要调用相应的音符 mediaPlayer01 = MediaPlayer.create(Piano.this, resource); 随着按次数的增加create的次数也随着增加。 只要create的次数到了32次之后,再按盘就不会响了,这个时候只要改变一下屏幕的方向程序就可以继续运行。 我的理解是create后的资源没有被释放导致的。 但我在代码中也加入了 mediaPlayer01.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { public void onCompletion(MediaPlayer arg0) { mediaPlayer01.release(); Toast.makeText(Piano.this, "资源释放了!", Toast.LENGTH_SHORT).show(); } }); 复制代码 这样的监听代码。在资源文件播放完成后释放。但运行过程中这部分没有起作用(因为Toast部分没有执行)。 目前还不知道是什么原因造成的。   PS: 今天找到一个解决方法 不使用mediaplayer的监听事件来释放mediaplayer资源了。直接在play的时候先release一次,然后create资源文件。 不过这种做法稍微有点不大合理 为什么资源文件在事件触发的时候才create就不会被正常监听到呢。如果哪位同学知道原因的请帮忙解答下
评论 10
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值