您的位置:首页 > 移动开发 > 微信开发

模仿微信语音聊天功能(3) 核心部分,录音功能的实现

2015-09-24 18:31 881 查看
在上一篇文章中,我们实现了按钮和对话框的交互。没有读的可以点击下面的链接查看:

/article/6123593.html

在这一篇文章中,我们接着往下做,实现核心部分,即录音功能的实现。这里需要读者具备一定的MediaPlayer这个类的一些基础知识。

首先我们要在添加一下权限,切记,这个步骤千万不要忘记了。代码如下:

<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>


下面我们就可以痛快的编写实现录音功能的类了。代码如下:

package com.fuly.util;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

import android.media.MediaRecorder;

//一个管理录音的类
public class RecoderManager {

private MediaRecorder mMediaRcoder;//录音器

private static RecoderManager mRecoderManager;
private RecoderManagerListener mListener;
private static String dir;//音频存放的文件夹
private String mCurPath;//用来记录音频即时存放的路径名

private boolean isPrepared ;//判断录音器是否已经准备好了

private RecoderManager(String dir){

this.dir = dir;//传入保存音频的文件夹的地址

}

public static RecoderManager getRecoderMananger(String dir){

if(mRecoderManager == null){

synchronized (RecoderManager.class) {

if(mRecoderManager == null){

mRecoderManager = new RecoderManager(dir);

}
}
}

return mRecoderManager;

}

//提供一个回调接口,当录音准备好了后,调用该接口的方法,录音正式开始,此时就可以获取声音等级等东西了

public interface RecoderManagerListener{

void wellPrepared();//当录音准备好了就会调用这个方法
}
public void setOnRecoderManagerListener(RecoderManagerListener listener){
this.mListener = listener;
}

//录音的准备工作,要准备好录音存取的文件地址,录音器的准备等
public void recoderPrepared(){

isPrepared = false;

File mDir = new File(dir);

if(!mDir.exists()){
mDir.mkdir();//生成文件夹
}

String fileName = generateName(); //录下的声音所输出的文件名
File file = new File(mDir,fileName);//最终在文件夹mDir下面生成文件fileName
mCurPath = file.getAbsolutePath();//记录下即时存放所录音频的文件的完整路径名

try {
/*
* 下面的代码为初始化录音的这个实例,并做录音准备工作
*/
mMediaRcoder = new MediaRecorder();

//设置音频输出到哪个文件中,注意该参数应该是一个完成的路径,最终文件应该是.mar格式的。
mMediaRcoder.setOutputFile(file.getAbsolutePath());
//设置音频源为我们的麦克风
mMediaRcoder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置音频格式
mMediaRcoder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
//设置音频的编码格式为amr
mMediaRcoder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

mMediaRcoder.prepare();
mMediaRcoder.start();

isPrepared = true;

if(isPrepared){

mListener.wellPrepared();//回调,即回调按钮里重写的该方法
}

} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}

//该方法用来随机生成文件名
private String generateName() {

return UUID.randomUUID().toString()+".amr";
}

//通过音频获得声音的级别,转化为1~maxLevel之间
public int getVoiceLevel(int maxLevel){

if(mMediaRcoder != null){

try {
return maxLevel*mMediaRcoder.getMaxAmplitude()/32768+1;
} catch (IllegalStateException e) {//在这里,我们捕捉一下错误,是为了不让影响程序进行。
//因为就算音频没法捕捉到,也不是什么大事,只要声音录制到了就可以正常进行。
//所以在此忽略掉这个错误
}
}

return 1; //没有捕捉到音频,就默认为等级为1,并返回
}

//释放资源
public void release(){

if(mMediaRcoder != null){

mMediaRcoder.stop();
mMediaRcoder.release();
mMediaRcoder = null;
}

}

//录音取消时的操作
public void cancel(){

//注意此刻一定不要只调用mMediaRecoder.release()方法。除非你调用它之前
//再调用一下它的stop方法。一定注意顺序.不然会除非你release()的时候,录音却没停止。
//但是程序也不报错,就出现闪退。血泪教训啊
release();//调用我们刚刚写好的release()

if(mCurPath != null){
File file = new File(mCurPath);
if(file.exists()){
file.delete();
mCurPath = null;
}
}
}

//提供一个获取录音存放的路径的方法
public String getPath(){

return mCurPath;
}

}


最后,将录音功能集成到按钮中。这个可能要复杂一些,希望你有耐心做下去。具体代码如下:

package com.fuly.util;

import com.fuly.irecoder.R;
import com.fuly.util.RecoderManager.RecoderManagerListener;
import com.fuly.util.RecoderManager;

import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;

//定义我们自己的录音按钮
public class RecoderButton extends Button implements RecoderManagerListener{

//按钮的三个状态

private static final int STATE_NORMAL = 1;//正常
private static final int STATE_RECODING = 2;//录音状态
private static final int STATE_CACLE = 3;//取消状态

private int mCurState = STATE_NORMAL;//记录当前按钮状态

private int Y = 50;//限定手指移动的上下宽度

private DialogManager mDialogManager;//对话框管理类
private RecoderManager mRecoderManager;//录音器的管理类

private boolean isRecoding = false;
private boolean isLongClick =false;//是否为长安按钮,默认为没有触发

private float mTime=0;//用来记录录音的时长

private RecoderButtonListener mListener;//用来传递数据的实体

public RecoderButton(Context context) {

this(context,null);
}

public RecoderButton(Context context, AttributeSet attrs) {

super(context, attrs);

mDialogManager = new DialogManager(context);//实例化对话框管理类

String path = Environment.getExternalStorageDirectory()+"//MyAudio";
Log.d("付勇焜的文件夹---->",path);
mRecoderManager = RecoderManager.getRecoderMananger(path);//获取一个实例

mRecoderManager.setOnRecoderManagerListener(this);

setOnLongClickListener(new OnLongClickListener() {

public boolean onLongClick(View v) {

isLongClick = true;
mRecoderManager.recoderPrepared();
return false;
}
});

}

//定义一个回调接口,用来将数据返回,录音的时长和录音存放的路径

public interface RecoderButtonListener{

void onFinish(int mTime,String filePath);
}

public void setOnRecoderButtonListener( RecoderButtonListener listener){

this.mListener = listener;
}

private static final int CHANGE_VOICE = 0X110;
private static final int DIALOG_DISS = 0X111;
private static final int MEDIA_PREPARED = 0X112;

private Runnable mRunnable = new Runnable(){

public void run() {

while(isRecoding){

try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}

mTime+=0.1f;

mHandler.sendEmptyMessage(CHANGE_VOICE);

}

}

};

private Handler mHandler = new Handler(){

public void handleMessage(android.os.Message msg) {

switch(msg.what){

case MEDIA_PREPARED:
mDialogManager.dialogShow();
isRecoding = true;
new Thread(mRunnable).start();
break;

case CHANGE_VOICE:

//获取声音等级,并在对话框中改变
mDialogManager.updateVoiceLevel(mRecoderManager.getVoiceLevel(7));

break;
case DIALOG_DISS:
mDialogManager.dialogDismiss();
break;
}

};
};

//回调方法,当该方法在RecoderManager中被调用时,说明录音器已经准备完毕
//可以开始录音了
public void wellPrepared() {

mHandler.sendEmptyMessage(MEDIA_PREPARED);
}

//捕捉按钮点击事件
public boolean onTouchEvent(MotionEvent event) {

int x = (int) event.getX();
int y =(int)event.getY();

switch(event.getAction()){

case MotionEvent.ACTION_DOWN:

changeState(STATE_RECODING);//按下按钮,改变按钮状态

break;
case MotionEvent.ACTION_MOVE:

if(isRecoding){

if(wantCancel(x,y)){ //如果检测到取消,则改变按钮状态为取消

changeState(STATE_CACLE);
mDialogManager.dialogRecoderCancel();

}else{
changeState(STATE_RECODING);
mDialogManager.dialogRecoding();

}
}

break;
case MotionEvent.ACTION_UP:
//手指抬起的几种情况
//(1)正常录音结束后的抬起 (2)取消录音的抬起 (3)迅速抬起,此时会造成录音时间过短
//(2)可能录音器还没准备好,就手指抬起了。 也可看成是录音的时间太短(5)没有触发长安安钮就抬起

if(!isLongClick){ //如果没有触发长安安钮

reset();
return super.onTouchEvent(event);
}

if(!isRecoding||mTime<0.6f){//如果没有准备好录音器或者录音时间太短

mDialogManager.tooShort();
mRecoderManager.cancel();
mHandler.sendEmptyMessageDelayed(DIALOG_DISS, 2000);

}else if(mCurState == STATE_RECODING){//正常录音结束

//在这里应该返回录音的文件路径和时长给播放器

mDialogManager.dialogDismiss();
mRecoderManager.release();

//此时应将录音的时长和路径传递给MainActivity
if(mListener != null){
mListener.onFinish((int)mTime, mRecoderManager.getPath());
}

}else if(mCurState == STATE_CACLE){// 如果为取消录音的抬起

mDialogManager.dialogDismiss();
mRecoderManager.cancel();
}

reset();//各种设置复位

break;
default:
break;
}

return super.onTouchEvent(event);
}

//复位
private void reset() {

isRecoding = false;
isLongClick =false;
mTime = 0;
mCurState = STATE_NORMAL;
setText(R.string.btn_normal);

}

//检查手指移动范围,从而确定用户是否想取消录音
private boolean wantCancel(int x, int y) {

if(x<0||x>getWidth()){

return true;
}

if(y<-Y||y>getHeight()+Y){
return true;
}
return false;
}

//改变状态,包括按钮等
private void changeState(int state) {

if(mCurState != state){

mCurState = state;

switch(mCurState){

case STATE_NORMAL:

setText(R.string.btn_normal);

break;
case STATE_RECODING:

setText(R.string.btn_recoding);

break;
case STATE_CACLE:

setText(R.string.btn_cancel);

break;
default:
break;

}
}

}

}


至此,恭喜你,这个项目基本上完成一半了。下面我们运行一下android程序,然后对着手机麦克风说话,是不是会弹出对话框,而且对话框上的图标会随着你说话声音的大小而跳动呢?快运行一下吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: