把最近写的一个吹气demo分享一下,实现效果:吹气风车转动,持续吹气风车加速,停止吹气风车减速,补气继续加速。
demo地址:https://github.com/SiKang123/BlowWindMill
目录:
MySensor只提供start(),shutDown(),destory()(之前有其他传感器的使用,这里去掉了,只演示吹气,接口可以忽略)
下面看AduioSensor类,主要功能是打开麦克风录音,每当分贝达到吹气的标准就给UI线程一个通知
package com.blow.main.sensor;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Handler;
import android.util.Log;
import com.blow.main.ActivityBlow;
/**
* Created by SiKang on 2016/1/5.
*/
public class AudioSensor extends MySensor {
private static final String TAG = "AudioSensor";
private static AudioSensor mMicSensor = null;
static final int SAMPLE_RATE_IN_HZ = 8000;
static final int BUFFER_SIZE = AudioRecord.getMinBufferSize(SAMPLE_RATE_IN_HZ,
AudioFormat.CHANNEL_IN_DEFAULT, AudioFormat.ENCODING_PCM_16BIT);
AudioRecord mAudioRecord;
boolean isGetVoiceRun;//是否正在录音
private Handler mHandler;//UI线程Handler
private AudioSensor(Handler handler) {
super();
this.mHandler = handler;
isGetVoiceRun = false;
//初始化麦克风
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
SAMPLE_RATE_IN_HZ, AudioFormat.CHANNEL_IN_DEFAULT,
AudioFormat.ENCODING_PCM_16BIT, BUFFER_SIZE);
}
//获取实例
public static AudioSensor getInstance(Handler handler) {
if (mMicSensor == null) {
synchronized (AudioSensor.class) {
if (mMicSensor == null) {
mMicSensor = new AudioSensor(handler);
}
}
}
return mMicSensor;
}
//开始录音
@Override
public void start() {
if (isGetVoiceRun) {
Log.e(TAG, "正在录音");
return;
}
if (mAudioRecord == null) {
throw new NullPointerException("mAudioRecord初始化失败!");
}
isGetVoiceRun = true;
new Thread(new Runnable() {
int index = 0;
@Override
public void run() {
mAudioRecord.startRecording();
short[] buffer = new short[BUFFER_SIZE];
double volumeSum = 0;
while (isGetVoiceRun) {
//(计算分贝是在网上找的算法)
//r是实际读取的数据长度,一般而言r会小于buffersize
int r = mAudioRecord.read(buffer, 0, BUFFER_SIZE);
long v = 0;
// 将 buffer 内容取出,进行平方和运算
for (int i = 0; i < buffer.length; i++) {
v += buffer[i] * buffer[i];
}
// 平方和除以数据总长度,得到音量大小。
double mean = v / (double) r;
double volume = 10 * Math.log10(mean);
// if (needVolume > 0) {
//采集平均值测试不太稳定,上下浮动较大(用iPhone测试麦克风得到的数据非常稳定)这里用定值75,试了华为、魅族、三星、HTC感觉比取均值要稳定
if (volume > 75) {
//通知UI线程
Log.d(TAG, volume + "");
mHandler.sendEmptyMessage(ActivityBlow.BLOW_START);
}
// }
// 采集前10次最大最小分贝差 计算吹气标准
// if (!gotNeedVolume) {
// if (index++ == 0) {
// //初始化最大和最小的采集分别
// maxVolume = volume;
// minVolume = volume;
// } else if (volume > maxVolume) {
// maxVolume = volume;
// } else if (volume < minVolume) {
// minVolume = volume;
// }
// //计算出吹气标准分贝
// if (index >= 10) {
// double avg = (maxVolume + minVolume) / 2;
// needVolume = avg * 2;
// Log.d(TAG, needVolume + "");
// gotNeedVolume = true;
// }
//
// }
//采集前10次分贝平均值计算吹气标准
// if (!gotNeedVolume) {
// //采集10前10次录入
// if (index >= 10) {
// //将吹气判定标准设为平均值的1.5倍
// needVolume = (volumeSum / 10) * 1.5;
// Log.d(TAG, needVolume + "");
// gotNeedVolume = true;
// }else{
// volumeSum+=volume;
// index++;
// }
// }
synchronized (mLock) {
try {
mLock.wait(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if (mAudioRecord != null) {
mAudioRecord.stop();
}
}
}).start();
}
@Override
public void shutDown() {
isGetVoiceRun = false;
}
@Override
public void destory() {
if (mAudioRecord != null) {
mAudioRecord.release();
mAudioRecord = null;
}
mMicSensor = null;
}
}
然后是ActivityBlow,主要思路是,当收到AudioSensor发来的通知,先判断风车是不是正在转动,不是则开启一个匀速转动风车的线程,同时开启一个加速线程和一个等待线程,等待线程会计时,计时到300ms便停止加速开始减速,减速中又开始吹气的话停止减速,开启加速,并重新计时,Handler每次收到AudioSensor的消息都会重置 记时变量。
所以持续吹气会一直加速,一旦停止吹气,就会开始减速
下面是代码:
package com.blow.main;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.blow.main.sensor.AudioSensor;
import com.blow.main.sensor.MySensor;
/**
* Created by SiKang on 2016/1/14.
*/
public class ActivityBlow extends Activity implements View.OnTouchListener {
private final String TAG = "ActivityBlowTest";
private ImageView mWindmillImg;//风车需要转动的部位
private Button mBlowBtn;//吹气按钮
private MySensor blowSenser;
private boolean isBlowing;//正在吹起标识
private boolean isBegin;//是否按下按钮(按下才接受吹起)
private float blowAngle;
private int blowTime;
public static final int BLOW_START = 0;
public static final int BLOWING = 1;
public static final int BLOW_END = 2;
private boolean isReset;
private boolean isPause;
DisplayMetrics dm;
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.activity_blow);
dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
initLayout();
initData();
blowSenser = AudioSensor.getInstance(handler);
}
public void initData() {
isPause = false;//是否进入onPause
isBlowing = false;//转动线程是否已开启
isBegin = false;//是否按住吹起按钮
blowAngle = 0;//风车当前角度
isReset = false;//是否补气
}
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case BLOW_START:
//收到吹气通知,先判断是否按住按钮
if (isBegin) {
//重置计时和补气标识
if (!isReset)
isReset = true;
if (blowTime > 0)
blowTime = 0;
if (!isBlowing) {
//如果风车没有转动,转动线程开启
isBlowing = true;
new Thread(new blowRun()).start();
}
}
break;
case BLOWING:
//更新风车角度
mWindmillImg.setRotation(blowAngle);
break;
case BLOW_END:
//转动停止
isBlowing = false;
break;
}
return false;
}
});
protected void initLayout() {
//获得控件
mWindmillImg = (ImageView) this.findViewById(R.id.activity_blow_windmill);
mBlowBtn = (Button) this.findViewById(R.id.activity_blow_btn);
mBlowBtn.setOnTouchListener(this);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
int btnWidth = dm.heightPixels / 6;
layoutParams.width = btnWidth;//设置宽高
layoutParams.height = btnWidth;
layoutParams.setMargins((dm.widthPixels - btnWidth) / 2, dm.heightPixels / 6 * 4, 0, 0);
mBlowBtn.setLayoutParams(layoutParams);
}
//开启麦克风
@Override
protected void onStart() {
super.onStart();
isPause = false;
blowSenser.start();
}
//注销麦克风
@Override
protected void onPause() {
super.onPause();
blowSenser.shutDown();
isPause = true;
}
//销毁
@Override
protected void onDestroy() {
blowSenser.destory();
super.onDestroy();
}
//风车每一帧转动的角度
private float rotationAngle = 1f;
//转动风车
class blowRun implements Runnable {
@Override
public void run() {
//开始转动的同时开启计时和加速线程
new Thread(new stopRun()).start();
new Thread(new rotationRun()).start();
//开始转动,每次转动1度,5ms一次
while (isBlowing && !isPause) {
blowAngle += rotationAngle;
if (blowAngle > 360) {
blowAngle = 1;
}
handler.sendEmptyMessage(BLOWING);
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//控制加速
class rotationRun implements Runnable {
@Override
public void run() {
//持续加速,直到isReset为false(计时线程达到300ms会将其设为false)
while (isReset && !isPause) {
//最快速度10
if (rotationAngle <= 10) {
rotationAngle += 0.4;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//控制减速
class stopRun implements Runnable {
@Override
public void run() {
//每100ms计一次,blowTime在吹气中会被置0,达到3停止加速,并结束循环
while (blowTime <= 3 && !isPause) {
blowTime++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//停止加速
isReset = false;
//开始减速(如果重新吹气,isReset会被赋值true,循环停止),否则一直将速度减至0
while (rotationAngle > 0 && !isReset && !isPause) {
rotationAngle -= 0.4;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果while停止是因为重新吹气,则开始加速,并重新开始计时
if (isReset) {
new Thread(new stopRun()).start();
new Thread(new rotationRun()).start();
} else {
//如果停止是因为速度已为0,通知UI线程,转动结束
handler.sendEmptyMessage(BLOW_END);
}
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
isBegin = false;
}
if (event.getAction() == MotionEvent.ACTION_DOWN) {
isBegin = true;
}
return false;
}
}