标签:
在上一篇文章中,我们实现了按钮和对话框的交互。没有读的可以点击下面的链接查看:
http://www.cnblogs.com/fuly550871915/p/4836108.html
在这一篇文章中,我们接着往下做,实现核心部分,即录音功能的实现。这里需要读者具备一定的MediaPlayer这个类的一些基础知识。
首先我们要在添加一下权限,切记,这个步骤千万不要忘记了。代码如下:
1 <uses-permission android:name="android.permission.RECORD_AUDIO"/> 2 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> 3 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 4 <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" /> 5 <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
下面我们就可以痛快的编写实现录音功能的类了。代码如下:
1 package com.fuly.util; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.util.UUID; 6 7 import android.media.MediaRecorder; 8 9 10 //一个管理录音的类 11 public class RecoderManager { 12 13 private MediaRecorder mMediaRcoder;//录音器 14 15 16 private static RecoderManager mRecoderManager; 17 private RecoderManagerListener mListener; 18 private static String dir;//音频存放的文件夹 19 private String mCurPath;//用来记录音频即时存放的路径名 20 21 private boolean isPrepared ;//判断录音器是否已经准备好了 22 23 24 25 26 private RecoderManager(String dir){ 27 28 this.dir = dir;//传入保存音频的文件夹的地址 29 30 } 31 32 public static RecoderManager getRecoderMananger(String dir){ 33 34 if(mRecoderManager == null){ 35 36 synchronized (RecoderManager.class) { 37 38 if(mRecoderManager == null){ 39 40 mRecoderManager = new RecoderManager(dir); 41 42 } 43 } 44 } 45 46 47 return mRecoderManager; 48 49 } 50 51 52 53 54 //提供一个回调接口,当录音准备好了后,调用该接口的方法,录音正式开始,此时就可以获取声音等级等东西了 55 56 public interface RecoderManagerListener{ 57 58 void wellPrepared();//当录音准备好了就会调用这个方法 59 } 60 public void setOnRecoderManagerListener(RecoderManagerListener listener){ 61 this.mListener = listener; 62 } 63 64 65 //录音的准备工作,要准备好录音存取的文件地址,录音器的准备等 66 public void recoderPrepared(){ 67 68 isPrepared = false; 69 70 71 File mDir = new File(dir); 72 73 if(!mDir.exists()){ 74 mDir.mkdir();//生成文件夹 75 } 76 77 String fileName = generateName(); //录下的声音所输出的文件名 78 File file = new File(mDir,fileName);//最终在文件夹mDir下面生成文件fileName 79 mCurPath = file.getAbsolutePath();//记录下即时存放所录音频的文件的完整路径名 80 81 82 try { 83 /* 84 * 下面的代码为初始化录音的这个实例,并做录音准备工作 85 */ 86 mMediaRcoder = new MediaRecorder(); 87 88 //设置音频输出到哪个文件中,注意该参数应该是一个完成的路径,最终文件应该是.mar格式的。 89 mMediaRcoder.setOutputFile(file.getAbsolutePath()); 90 //设置音频源为我们的麦克风 91 mMediaRcoder.setAudioSource(MediaRecorder.AudioSource.MIC); 92 //设置音频格式 93 mMediaRcoder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB); 94 //设置音频的编码格式为amr 95 mMediaRcoder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 96 97 98 mMediaRcoder.prepare(); 99 mMediaRcoder.start(); 100 101 isPrepared = true; 102 103 if(isPrepared){ 104 105 mListener.wellPrepared();//回调,即回调按钮里重写的该方法 106 } 107 108 109 110 } catch (IllegalStateException e) { 111 e.printStackTrace(); 112 } catch (IOException e) { 113 e.printStackTrace(); 114 } 115 116 } 117 118 119 //该方法用来随机生成文件名 120 private String generateName() { 121 122 return UUID.randomUUID().toString()+".amr"; 123 } 124 125 126 127 128 //通过音频获得声音的级别,转化为1~maxLevel之间 129 public int getVoiceLevel(int maxLevel){ 130 131 if(mMediaRcoder != null){ 132 133 try { 134 return maxLevel*mMediaRcoder.getMaxAmplitude()/32768+1; 135 } catch (IllegalStateException e) {//在这里,我们捕捉一下错误,是为了不让影响程序进行。 136 //因为就算音频没法捕捉到,也不是什么大事,只要声音录制到了就可以正常进行。 137 //所以在此忽略掉这个错误 138 } 139 } 140 141 return 1; //没有捕捉到音频,就默认为等级为1,并返回 142 } 143 144 145 146 //释放资源 147 public void release(){ 148 149 if(mMediaRcoder != null){ 150 151 mMediaRcoder.stop(); 152 mMediaRcoder.release(); 153 mMediaRcoder = null; 154 } 155 156 } 157 158 //录音取消时的操作 159 public void cancel(){ 160 161 //注意此刻一定不要只调用mMediaRecoder.release()方法。除非你调用它之前 162 //再调用一下它的stop方法。一定注意顺序.不然会除非你release()的时候,录音却没停止。 163 //但是程序也不报错,就出现闪退。血泪教训啊 164 release();//调用我们刚刚写好的release() 165 166 167 if(mCurPath != null){ 168 File file = new File(mCurPath); 169 if(file.exists()){ 170 file.delete(); 171 mCurPath = null; 172 } 173 } 174 } 175 176 //提供一个获取录音存放的路径的方法 177 public String getPath(){ 178 179 return mCurPath; 180 } 181 182 }
最后,将录音功能集成到按钮中。这个可能要复杂一些,希望你有耐心做下去。具体代码如下:
1 package com.fuly.util; 2 3 4 import com.fuly.irecoder.R; 5 import com.fuly.util.RecoderManager.RecoderManagerListener; 6 import com.fuly.util.RecoderManager; 7 8 import android.content.Context; 9 import android.os.Environment; 10 import android.os.Handler; 11 import android.util.AttributeSet; 12 import android.util.Log; 13 import android.view.MotionEvent; 14 import android.view.View; 15 import android.widget.Button; 16 17 18 19 20 //定义我们自己的录音按钮 21 public class RecoderButton extends Button implements RecoderManagerListener{ 22 23 //按钮的三个状态 24 25 private static final int STATE_NORMAL = 1;//正常 26 private static final int STATE_RECODING = 2;//录音状态 27 private static final int STATE_CACLE = 3;//取消状态 28 29 private int mCurState = STATE_NORMAL;//记录当前按钮状态 30 31 private int Y = 50;//限定手指移动的上下宽度 32 33 private DialogManager mDialogManager;//对话框管理类 34 private RecoderManager mRecoderManager;//录音器的管理类 35 36 private boolean isRecoding = false; 37 private boolean isLongClick =false;//是否为长安按钮,默认为没有触发 38 39 40 41 private float mTime=0;//用来记录录音的时长 42 43 private RecoderButtonListener mListener;//用来传递数据的实体 44 45 46 public RecoderButton(Context context) { 47 48 this(context,null); 49 } 50 51 52 public RecoderButton(Context context, AttributeSet attrs) { 53 54 super(context, attrs); 55 56 57 mDialogManager = new DialogManager(context);//实例化对话框管理类 58 59 String path = Environment.getExternalStorageDirectory()+"//MyAudio"; 60 Log.d("付勇焜的文件夹---->",path); 61 mRecoderManager = RecoderManager.getRecoderMananger(path);//获取一个实例 62 63 mRecoderManager.setOnRecoderManagerListener(this); 64 65 setOnLongClickListener(new OnLongClickListener() { 66 67 public boolean onLongClick(View v) { 68 69 isLongClick = true; 70 mRecoderManager.recoderPrepared(); 71 return false; 72 } 73 }); 74 75 } 76 77 78 //定义一个回调接口,用来将数据返回,录音的时长和录音存放的路径 79 80 public interface RecoderButtonListener{ 81 82 void onFinish(int mTime,String filePath); 83 } 84 85 public void setOnRecoderButtonListener( RecoderButtonListener listener){ 86 87 this.mListener = listener; 88 } 89 90 91 92 93 94 95 96 97 98 private static final int CHANGE_VOICE = 0X110; 99 private static final int DIALOG_DISS = 0X111; 100 private static final int MEDIA_PREPARED = 0X112; 101 102 103 104 105 private Runnable mRunnable = new Runnable(){ 106 107 108 public void run() { 109 110 while(isRecoding){ 111 112 try { 113 Thread.sleep(100); 114 } catch (InterruptedException e) { 115 e.printStackTrace(); 116 } 117 118 mTime+=0.1f; 119 120 mHandler.sendEmptyMessage(CHANGE_VOICE); 121 122 } 123 124 } 125 126 }; 127 128 private Handler mHandler = new Handler(){ 129 130 public void handleMessage(android.os.Message msg) { 131 132 switch(msg.what){ 133 134 case MEDIA_PREPARED: 135 mDialogManager.dialogShow(); 136 isRecoding = true; 137 new Thread(mRunnable).start(); 138 break; 139 140 case CHANGE_VOICE: 141 142 //获取声音等级,并在对话框中改变 143 mDialogManager.updateVoiceLevel(mRecoderManager.getVoiceLevel(7)); 144 145 break; 146 case DIALOG_DISS: 147 mDialogManager.dialogDismiss(); 148 break; 149 } 150 151 }; 152 }; 153 154 155 156 //回调方法,当该方法在RecoderManager中被调用时,说明录音器已经准备完毕 157 //可以开始录音了 158 public void wellPrepared() { 159 160 161 mHandler.sendEmptyMessage(MEDIA_PREPARED); 162 } 163 164 165 166 //捕捉按钮点击事件 167 public boolean onTouchEvent(MotionEvent event) { 168 169 int x = (int) event.getX(); 170 int y =(int)event.getY(); 171 172 switch(event.getAction()){ 173 174 175 case MotionEvent.ACTION_DOWN: 176 177 changeState(STATE_RECODING);//按下按钮,改变按钮状态 178 179 break; 180 case MotionEvent.ACTION_MOVE: 181 182 if(isRecoding){ 183 184 if(wantCancel(x,y)){ //如果检测到取消,则改变按钮状态为取消 185 186 changeState(STATE_CACLE); 187 mDialogManager.dialogRecoderCancel(); 188 189 190 }else{ 191 changeState(STATE_RECODING); 192 mDialogManager.dialogRecoding(); 193 194 } 195 } 196 197 198 break; 199 case MotionEvent.ACTION_UP: 200 //手指抬起的几种情况 201 //(1)正常录音结束后的抬起 (2)取消录音的抬起 (3)迅速抬起,此时会造成录音时间过短 202 //(2)可能录音器还没准备好,就手指抬起了。 也可看成是录音的时间太短(5)没有触发长安安钮就抬起 203 204 if(!isLongClick){ //如果没有触发长安安钮 205 206 reset(); 207 return super.onTouchEvent(event); 208 } 209 210 if(!isRecoding||mTime<0.6f){//如果没有准备好录音器或者录音时间太短 211 212 mDialogManager.tooShort(); 213 mRecoderManager.cancel(); 214 mHandler.sendEmptyMessageDelayed(DIALOG_DISS, 2000); 215 216 }else if(mCurState == STATE_RECODING){//正常录音结束 217 218 //在这里应该返回录音的文件路径和时长给播放器 219 220 mDialogManager.dialogDismiss(); 221 mRecoderManager.release(); 222 223 //此时应将录音的时长和路径传递给MainActivity 224 if(mListener != null){ 225 mListener.onFinish((int)mTime, mRecoderManager.getPath()); 226 } 227 228 }else if(mCurState == STATE_CACLE){// 如果为取消录音的抬起 229 230 mDialogManager.dialogDismiss(); 231 mRecoderManager.cancel(); 232 } 233 234 235 reset();//各种设置复位 236 237 break; 238 default: 239 break; 240 } 241 242 return super.onTouchEvent(event); 243 } 244 245 246 247 //复位 248 private void reset() { 249 250 isRecoding = false; 251 isLongClick =false; 252 mTime = 0; 253 mCurState = STATE_NORMAL; 254 setText(R.string.btn_normal); 255 256 } 257 258 259 260 //检查手指移动范围,从而确定用户是否想取消录音 261 private boolean wantCancel(int x, int y) { 262 263 if(x<0||x>getWidth()){ 264 265 return true; 266 } 267 268 if(y<-Y||y>getHeight()+Y){ 269 return true; 270 } 271 return false; 272 } 273 274 275 276 //改变状态,包括按钮等 277 private void changeState(int state) { 278 279 if(mCurState != state){ 280 281 mCurState = state; 282 283 284 285 switch(mCurState){ 286 287 case STATE_NORMAL: 288 289 setText(R.string.btn_normal); 290 291 break; 292 case STATE_RECODING: 293 294 setText(R.string.btn_recoding); 295 296 break; 297 case STATE_CACLE: 298 299 300 setText(R.string.btn_cancel); 301 302 303 break; 304 default: 305 break; 306 307 } 308 } 309 310 } 311 312 313 314 }
至此,恭喜你,这个项目基本上完成一半了。下面我们运行一下android程序,然后对着手机麦克风说话,是不是会弹出对话框,而且对话框上的图标会随着你说话声音的大小而跳动呢?快运行一下吧。
标签:
原文地址:http://www.cnblogs.com/fuly550871915/p/4836204.html