模仿微信语音聊天功能(3) 核心部分,录音功能的实现
2015-09-24 18:31
881 查看
在上一篇文章中,我们实现了按钮和对话框的交互。没有读的可以点击下面的链接查看:
/article/6123593.html
在这一篇文章中,我们接着往下做,实现核心部分,即录音功能的实现。这里需要读者具备一定的MediaPlayer这个类的一些基础知识。
首先我们要在添加一下权限,切记,这个步骤千万不要忘记了。代码如下:
下面我们就可以痛快的编写实现录音功能的类了。代码如下:
最后,将录音功能集成到按钮中。这个可能要复杂一些,希望你有耐心做下去。具体代码如下:
至此,恭喜你,这个项目基本上完成一半了。下面我们运行一下android程序,然后对着手机麦克风说话,是不是会弹出对话框,而且对话框上的图标会随着你说话声音的大小而跳动呢?快运行一下吧。
/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程序,然后对着手机麦克风说话,是不是会弹出对话框,而且对话框上的图标会随着你说话声音的大小而跳动呢?快运行一下吧。
相关文章推荐
- 模仿微信语音聊天功能(2)对话框的实现
- weiphp微信支付开发教程
- ios9适配 微信支付/支付宝支付
- 模仿微信语音聊天功能(1)项目介绍及自定义按钮实现
- iOS 9系统策略更新,使用微信SDK的开发者注意升级
- 免费课程《微信公众号开发》开讲啦!!!
- Nagios 安装及微信短信提醒
- JSSDK,微信JS接口,分享朋友圈狀態捕獲,項目實例
- ios9适配中的分享(微信、新浪微博、QQ、QQ空间、支付宝)
- 微信开发的资料
- iOS 9系统策略更新,使用微信SDK的开发者注意升级
- java编写的九九乘法表小程序
- 自定义控件--CircleImageView(类似于QQ、微信圆形头像自定义控件)
- Nagios 安装及微信短信提醒
- 爱快云微信连wifi3.1用户前期准备工作
- 微信红包体系设计分析
- 一个c#即时监控小程序
- iOS 微信支付接入最新的完整流程
- 使用zookeeper来解决在分布式系统中单节点维护微信token生命周期的容灾demo【已抽象分离】[分布式锁][9.28更新]
- 微信连wifi3.1总结