原创文字,转载请标明出处:

利用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



android小程序-电子钢琴-滑动连续响应的更多相关文章

  1. android小程序-电子钢琴-多点触控

    我在第一篇博客<android小程序-电子钢琴-滑动连续响应>中实现了一个简单地7键钢琴,这几天把它又完善了一下,增加了多点触控,按键也增加了一个低音区和一个高音区,使得又可以多弹一点简单 ...

  2. android小程序之幸运菜谱

    android小程序之幸运菜谱 前言:刚刚结束短短5天的android公开课程,收获不少,写下来记录一下吧!(因为学校校企公开课的缘故才偶然接触的android,所以只学了这几天,不喜勿喷) 一开始得 ...

  3. 小程序组件 scroll-view 滑动

    小程序组件 scroll-view 中分别有上下竖向滑动和左右横向滑动之分,在这次项目中刚好需要用到横向滑动,但在测试过程中发现横向滑动没有了效果(静止在那里没移动过),经调试发现: 1.scroll ...

  4. 微信小程序列表项滑动显示删除按钮

    微信小程序并没有提供列表控件,所以也没有iOS上惯用的列表项左滑删除的功能,SO只能自己干了. 原理很简单,用2个层,上面的层显示正常的内容,下面的层显示一个删除按钮,就是记录手指滑动的距离,动态的来 ...

  5. 微信小程序页面左右滑动事件

    微信小程序提供了页面的上下滚动的事件,在页面的js文件中, page({ onPageScroll(e) { console.log(e.scrollTop) } }) 但是不是滑动事件,滑动事件需要 ...

  6. 微信小程序两种滑动方式

    竖向滑动: <scroll-view scroll-y="true" style="height: 200rpx;"> <view style ...

  7. 小程序swiper 快速滑动闪屏

    bindchange: function(e){ if(e.detail.source == "touch") { this.setData({ current: current ...

  8. 小程序框架之视图层 View~事件系统~WXS响应事件

    WXS响应事件 基础库 2.4.4 开始支持,低版本需做兼容处理. 背景 有频繁用户交互的效果在小程序上表现是比较卡顿的,例如页面有 2 个元素 A 和 B,用户在 A 上做 touchmove 手势 ...

  9. 微信小程序tab切换,可滑动切换,导航栏跟随滚动实现

    简介 看到今日头条小程序页面可以滑动切换,而且tab导航条也会跟着滚动,点击tab导航,页面滑动,切导航栏也会跟着滚动,就想着要怎么实现这个功能 像商城类商品类目如果做成左右滑动切换类目用户体验应该会 ...

随机推荐

  1. SQL SERVER 字符合并多行为一列

    [字符合并多行为一列] 思路1:行转列,在与字符拼接(适用每组列数名相同) 思路2:转xml,去掉多余字符(适用所有) 假设兴趣表Hobbys Name Hobby 小张 打篮球 小张 踢足球 Nam ...

  2. ubuntu 远程登录服务器和服务器中下载代码

    1.首先用远程登录:ssh 名称@地址 2.提示输入密码. 3.进入到你所要文件目录. 输入:tar cf 文件明.tar 你自己喜欢的文件/ 下载:scp erpAmazonAPI.tar 主机名称 ...

  3. Ping 的TTL理解

    http://www.webkaka.com/tutorial/zhanzhang/2017/061570/ 根据自己的扩展重新整理了一下,虽然不是运维,想了解一点东西就希望了解清楚. 一.含义 “T ...

  4. idea中查看java类继承图

    复习java io,看了书里的这张图,太模糊了,想弄个出来放大看,一开始试了idea发现只能看父类,后来折腾了一会试了其他工具才发现还是idea好用. :) 一.打开type hierachy 光标放 ...

  5. vscode代码保存时自动格式化成ESLint风格(支持VUE)

    一.问题 vscode的默认的代码格式化ctrl+shift+f 无法通过eslint的代码风格检查是一个非常蛋疼的问题 同样在进行vue项目开发的时候,使用eslint代码风格检查是没啥问题的,但是 ...

  6. 在springboot中验证表单信息(六)

    构建工程 创建一个springboot工程,由于用到了 web .thymeleaf.validator.el,引入相应的起步依赖和依赖,代码清单如下: 1 2 3 4 5 6 7 8 9 10 11 ...

  7. eclipse安装scala环境

    1.安装eclipse插件,依次点击Help->Eclipse Marketplace 2.输入scala,点击go,进行搜索 3,出现了Scala IDE4.7X,点击右下方的Install进 ...

  8. 文件操作_26th,Nov 2018

    文件:操作系统问应用程序或用户提供一种操作硬盘的虚拟单位 --文件时操作系统提供的虚拟单位 --应用程序或用户对文件的读写操作都是向操作系统发送指令 --文件包括文本文件和二进制文件 为何要用文件:文 ...

  9. python requests 的cookie 操作

    结论: 1.requests模块的请求和响应分别有cookie对象. 可以通过此对象设置和获取cookie. 2.通过在requests.get,requests.post等方法请求中传入cookie ...

  10. memory prefix pre,pro,penta,pseudo out _p 1

    1● pre 前的,预先   2● pro 在前,很多,赞同       3● penta 5,五     4● pseaudo   伪,假 pseudo