原創文字,轉載請标明出處:
利用Button實作簡單地電子鋼琴,可以簡單地響應按鈕的click事件來發出相應的聲音。但是這樣不能達到手指在螢幕滑動,而連續發聲的效果,就像手指在真實鋼琴按鍵上滑過一樣。本文就是為了解決這個問題。思路:通過父控件響應touchevent,在響應函數中判斷位置是否在按鈕所在位置,或是從一個按鈕移動到另一個按鈕内,進而進行相應的操作。
形狀檔案:res/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>
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>
布局檔案:res/layout
activitymain.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<LinearLayout
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/text"
android:text="電子鋼琴 by ZH" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:id="@+id/Keys"
android:layout_height="0dp"
android:layout_weight="5"
android:orientation="horizontal"
android:padding="10dp" >
<Button
android:id="@+id/button1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button"
android:text="1" />
<Button
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button"
android:text="2" />
<Button
android:id="@+id/button3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button"
android:text="3" />
<Button
android:id="@+id/button4"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button"
android:text="4" />
<Button
android:id="@+id/button5"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button"
android:text="5" />
<Button
android:id="@+id/button6"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button"
android:text="6" />
<Button
android:id="@+id/button7"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button"
android:text="7" />
</LinearLayout>
</LinearLayout>
源檔案:
MyMusicUtils.java
/**
* 音樂播放幫助類
*/
package com.example.android_simple_piano;
import java.util.HashMap;
import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;
public class MyMusicUtils {
// 資源檔案
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;
/**
*
* @param context
* 用于soundpool.load
* @param no
* 播放聲音的編号
*/
public MyMusicUtils(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 {
// TODO Auto-generated method stub
soundPool.release();
super.finalize();
}
}
MainActivity.java
package com.example.android_simple_piano;
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;
public class MainActivity extends Activity {
private Button button[];//按鈕數組
private MyMusicUtils utils;//工具類
private View parent;//父視圖
private int buttonId[];//按鈕id
private boolean havePlayed[];//是否已經播放了聲音,當手指在同一個按鈕内滑動,且已經發聲,就為true
private int currentKey;//手指目前所在按鈕
private int lastKey;//上一個按鈕
private View keys;//按鈕們所在的視圖
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//建立工具類
utils = new MyMusicUtils(getApplicationContext());
//按鈕資源Id
buttonId = new int[7];
buttonId[0] = R.id.button1;
buttonId[1] = R.id.button2;
buttonId[2] = R.id.button3;
buttonId[3] = R.id.button4;
buttonId[4] = R.id.button5;
buttonId[5] = R.id.button6;
buttonId[6] = R.id.button7;
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;
}
currentKey = 0;
lastKey = 0;
parent = (View) findViewById(R.id.parent);
parent.setClickable(true);
parent.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int temp;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
temp = isInAnyScale(event.getX(), event.getY(), button);
if (temp != -1) {// 在某個按鍵範圍内
currentKey = temp;
button[currentKey]
.setBackgroundResource(R.drawable.button_pressed);
// 播放音階
utils.soundPlay(currentKey);
Log.i("--", "sound" + currentKey);
havePlayed[currentKey] = true;
}
break;
case MotionEvent.ACTION_MOVE:
temp = currentKey;
for (int i = temp + 1; i >= temp - 1; i--) {
//當在兩端的按鈕時,會有一邊越界
if (i < 0 || i >= button.length) {
continue;
}
if (isInScale(event.getX(), event.getY(), button[i])) {// 在某個按鍵内
if (i == currentKey) {
// 在目前按鍵内且未發音
if (!havePlayed[i]) {
utils.soundPlay(currentKey);
Log.i("--", "sounD" + i);
}
break;
} else {// 在相鄰按鍵内
lastKey = currentKey;
// 設定目前按鍵
currentKey = i;
button[currentKey]
.setBackgroundResource(R.drawable.button_pressed);
// 發音
utils.soundPlay(currentKey);
Log.i("--", "sound" + currentKey);
havePlayed[currentKey] = true;
// 設定上一個按鍵
button[lastKey]
.setBackgroundResource(R.drawable.button);
havePlayed[lastKey] = false;
break;
}
}
}
break;
case MotionEvent.ACTION_UP:
lastKey = currentKey;
button[currentKey].setBackgroundResource(R.drawable.button);
havePlayed[currentKey] = false;
break;
}
return true;
}
});
keys = (View) findViewById(R.id.Keys);
}
/**
* 判斷某個點是否在某個按鈕的範圍内
*
* @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;
}
}
效果圖:
聲音檔案:http://pan.baidu.com/s/1hq0xXC4
參考文章:
事件分發:http://blog.csdn.net/guolin_blog/article/details/9097463
坐标問題:http://www.cnblogs.com/zhengbeibei/archive/2013/05/07/3065999.html
轉載于:https://www.cnblogs.com/zhanghang-BadCoder/p/6476474.html