自微信出现以来取得了很好的成绩,语音对讲的实现更加方便了人与人之间的交流。今天来实践一下微信的语音对讲的录音实现,这个也比较容易实现。在此,我将该按钮封装成为一个控件,并通过策略模式的方式实现录音和界面的解耦合,以方便我们在实际情况中对录音方法的不同需求(例如想要实现wav格式的编码时我们也就不能再使用MediaRecorder,而只能使用AudioRecord进行处理)。
效果图:
实现思路:
1.在微信中我们可以看到实现语音对讲的是通过点按按钮来完成的,因此在这里我选择重新自己的控件使其继承自button并重写ontouchEvent方法,来实现对录音的判断。
2.在ontouchEvent方法中,
当我们按下按钮时,首先显示录音的对话框,然后调用录音准备方法并开始录音,接着开启一个计时线程,每隔0.1秒的时间获取一次录音音量的大小,并通过Handler根据音量大小更新Dialog中的显示图片;
当我们移动手指时,若手指向上移动距离大于50,在Dialog中显示松开手指取消录音的提示,并将isCanceled变量(表示我们最后是否取消了录音)置为true,上移动距离小于20时,我们恢复Dialog的图片,并将isCanceled置为false;
当抬起手指时,我们首先关闭录音对话框,接着调用录音停止方法并关闭计时线程,然后我们判断是否取消录音,若是的话则删除录音文件,否则判断计时时间是否太短,最后调用回调接口中的recordEnd方法。
3.在这里为了适应不同的录音需求,我使用了策略模式来进行处理,将每一个不同的录音方法视为一种不同的策略,根据自己的需要去改写。
注意问题
1.在ontouchEvent的返回值中应该返回true,这样才能屏蔽之后其他的触摸事件,否则当手指滑动离开button之后将不能在响应我们的触摸方法。
2.不要忘记为自己的App添加权限:
<uses-permission androID:name="androID.permission.RECORD_AUdio" /><uses-permission androID:name="androID.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission androID:name="androID.permission.READ_EXTERNAL_STORAGE" />
代码参考
Recordbutton 类,我们的自定义控件,重新复写了ontouchEvent方法
package com.example.recordtest;import androID.annotation.Suppresslint;import androID.app.Dialog;import androID.content.Context;import androID.os.Handler;import androID.os.Message;import androID.util.AttributeSet;import androID.vIEw.Gravity;import androID.vIEw.LayoutInflater;import androID.vIEw.MotionEvent;import androID.vIEw.VIEw;import androID.Widget.button;import androID.Widget.ImageVIEw;import androID.Widget.TextVIEw;import androID.Widget.Toast;public class Recordbutton extends button { private static final int MIN_RECORD_TIME = 1; // 最短录音时间,单位秒 private static final int RECORD_OFF = 0; // 不在录音 private static final int RECORD_ON = 1; // 正在录音 private Dialog mRecordDialog; private RecordStrategy mAudioRecorder; private Thread mRecordThread; private RecordListener Listener; private int recordState = 0; // 录音状态 private float recodeTime = 0.0f; // 录音时长,如果录音时间太短则录音失败 private double voiceValue = 0.0; // 录音的音量值 private boolean isCanceled = false; // 是否取消录音 private float downY; private TextVIEw dialogTextVIEw; private ImageVIEw dialogimg; private Context mContext; public Recordbutton(Context context) { super(context); // Todo auto-generated constructor stub init(context); } public Recordbutton(Context context,AttributeSet attrs,int defStyle) { super(context,attrs,defStyle); // Todo auto-generated constructor stub init(context); } public Recordbutton(Context context,AttributeSet attrs) { super(context,attrs); // Todo auto-generated constructor stub init(context); } private voID init(Context context) { mContext = context; this.setText("按住 说话"); } public voID setAudioRecord(RecordStrategy record) { this.mAudioRecorder = record; } public voID setRecordListener(RecordListener Listener) { this.Listener = Listener; } // 录音时显示Dialog private voID showVoiceDialog(int flag) { if (mRecordDialog == null) { mRecordDialog = new Dialog(mContext,R.style.Dialogstyle); mRecordDialog.setContentVIEw(R.layout.dialog_record); dialogimg = (ImageVIEw) mRecordDialog .findVIEwByID(R.ID.record_dialog_img); dialogTextVIEw = (TextVIEw) mRecordDialog .findVIEwByID(R.ID.record_dialog_txt); } switch (flag) { case 1: dialogimg.setimageResource(R.drawable.record_cancel); dialogTextVIEw.setText("松开手指可取消录音"); this.setText("松开手指 取消录音"); break; default: dialogimg.setimageResource(R.drawable.record_animate_01); dialogTextVIEw.setText("向上滑动可取消录音"); this.setText("松开手指 完成录音"); break; } dialogTextVIEw.setTextSize(14); mRecordDialog.show(); } // 录音时间太短时Toast显示 private voID showWarnToast(String toastText) { Toast toast = new Toast(mContext); VIEw warnVIEw = LayoutInflater.from(mContext).inflate( R.layout.toast_warn,null); toast.setVIEw(warnVIEw); toast.setGravity(Gravity.CENTER,0);// 起点位置为中间 toast.show(); } // 开启录音计时线程 private voID callrecordTimeThread() { mRecordThread = new Thread(recordThread); mRecordThread.start(); } // 录音Dialog图片随录音音量大小切换 private voID setDialogImage() { if (voiceValue < 600.0) { dialogimg.setimageResource(R.drawable.record_animate_01); } else if (voiceValue > 600.0 && voiceValue < 1000.0) { dialogimg.setimageResource(R.drawable.record_animate_02); } else if (voiceValue > 1000.0 && voiceValue < 1200.0) { dialogimg.setimageResource(R.drawable.record_animate_03); } else if (voiceValue > 1200.0 && voiceValue < 1400.0) { dialogimg.setimageResource(R.drawable.record_animate_04); } else if (voiceValue > 1400.0 && voiceValue < 1600.0) { dialogimg.setimageResource(R.drawable.record_animate_05); } else if (voiceValue > 1600.0 && voiceValue < 1800.0) { dialogimg.setimageResource(R.drawable.record_animate_06); } else if (voiceValue > 1800.0 && voiceValue < 2000.0) { dialogimg.setimageResource(R.drawable.record_animate_07); } else if (voiceValue > 2000.0 && voiceValue < 3000.0) { dialogimg.setimageResource(R.drawable.record_animate_08); } else if (voiceValue > 3000.0 && voiceValue < 4000.0) { dialogimg.setimageResource(R.drawable.record_animate_09); } else if (voiceValue > 4000.0 && voiceValue < 6000.0) { dialogimg.setimageResource(R.drawable.record_animate_10); } else if (voiceValue > 6000.0 && voiceValue < 8000.0) { dialogimg.setimageResource(R.drawable.record_animate_11); } else if (voiceValue > 8000.0 && voiceValue < 10000.0) { dialogimg.setimageResource(R.drawable.record_animate_12); } else if (voiceValue > 10000.0 && voiceValue < 12000.0) { dialogimg.setimageResource(R.drawable.record_animate_13); } else if (voiceValue > 12000.0) { dialogimg.setimageResource(R.drawable.record_animate_14); } } // 录音线程 private Runnable recordThread = new Runnable() { @OverrIDe public voID run() { recodeTime = 0.0f; while (recordState == RECORD_ON) { { try { Thread.sleep(100); recodeTime += 0.1; // 获取音量,更新dialog if (!isCanceled) { voiceValue = mAudioRecorder.getAmplitude(); recordHandler.sendEmptyMessage(1); } } catch (InterruptedException e) { e.printstacktrace(); } } } } }; @Suppresslint("HandlerLeak") private Handler recordHandler = new Handler() { @OverrIDe public voID handleMessage(Message msg) { setDialogImage(); } }; @OverrIDe public boolean ontouchEvent(MotionEvent event) { // Todo auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 按下按钮 if (recordState != RECORD_ON) { showVoiceDialog(0); downY = event.getY(); if (mAudioRecorder != null) { mAudioRecorder.ready(); recordState = RECORD_ON; mAudioRecorder.start(); callrecordTimeThread(); } } break; case MotionEvent.ACTION_MOVE: // 滑动手指 float moveY = event.getY(); if (downY - moveY > 50) { isCanceled = true; showVoiceDialog(1); } if (downY - moveY < 20) { isCanceled = false; showVoiceDialog(0); } break; case MotionEvent.ACTION_UP: // 松开手指 if (recordState == RECORD_ON) { recordState = RECORD_OFF; if (mRecordDialog.isShowing()) { mRecordDialog.dismiss(); } mAudioRecorder.stop(); mRecordThread.interrupt(); voiceValue = 0.0; if (isCanceled) { mAudioRecorder.deleteoldfile(); } else { if (recodeTime < MIN_RECORD_TIME) { showWarnToast("时间太短 录音失败"); mAudioRecorder.deleteoldfile(); } else { if (Listener != null) { Listener.recordEnd(mAudioRecorder.getfilePath()); } } } isCanceled = false; this.setText("按住 说话"); } break; } return true; } public interface RecordListener { public voID recordEnd(String filePath); }}
Dialog布局:
<?xml version="1.0" enCoding="utf-8"?><linearLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" androID:orIEntation="vertical" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:layout_gravity="center" androID:gravity="center" androID:background="@drawable/record_bg" androID:padding="20dp" > <ImageVIEw androID:ID="@+ID/record_dialog_img" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" /> <TextVIEw androID:ID="@+ID/record_dialog_txt" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:textcolor="@androID:color/white" androID:layout_margintop="5dp" /></linearLayout>
录音时间太短的Toast布局:
<?xml version="1.0" enCoding="utf-8"?><linearLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:background="@drawable/record_bg" androID:padding="20dp" androID:gravity="center" androID:orIEntation="vertical" > <ImageVIEw androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:src="@drawable/voice_to_short" /> <TextVIEw androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:textcolor="@androID:color/white" androID:textSize="15sp" androID:text="时间太短 录音失败" /></linearLayout>
自定义的Dialogstyle,对话框样式
<style name="Dialogstyle"> <item name="androID:windowBackground">@androID:color/transparent</item> <item name="androID:windowFrame">@null</item> <item name="androID:windowNoTitle">true</item> <item name="androID:windowIsfloating">true</item> <item name="androID:windowIsTranslucent">true</item> <item name="androID:windowAnimationStyle">@androID:style/Animation.Dialog</item> <!-- 显示对话框时当前的屏幕是否变暗 --> <item name="androID:backgroundDimEnabled">false</item></style>
RecordStrategy 录音策略接口
package com.example.recordtest;/** * RecordStrategy 录音策略接口 * @author acer */public interface RecordStrategy { /** * 在这里进行录音准备工作,重置录音文件名等 */ public voID ready(); /** * 开始录音 */ public voID start(); /** * 录音结束 */ public voID stop(); /** * 录音失败时删除原来的旧文件 */ public voID deleteoldfile(); /** * 获取录音音量的大小 * @return */ public double getAmplitude(); /** * 返回录音文件完整路径 * @return */ public String getfilePath();}
个人写的一个录音实践策略
package com.example.recordtest;import java.io.file;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;import androID.media.MediaRecorder;import androID.os.Environment;public class AudioRecorder implements RecordStrategy { private MediaRecorder recorder; private String filename; private String fileFolder = Environment.getExternalStorageDirectory() .getPath() + "/TestRecord"; private boolean isRecording = false; @OverrIDe public voID ready() { // Todo auto-generated method stub file file = new file(fileFolder); if (!file.exists()) { file.mkdir(); } filename = getCurrentDate(); recorder = new MediaRecorder(); recorder.setoutputfile(fileFolder + "/" + filename + ".amr"); recorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置MediaRecorder的音频源为麦克风 recorder.setoutputFormat(MediaRecorder.OutputFormat.RAW_AMR);// 设置MediaRecorder录制的音频格式 recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);// 设置MediaRecorder录制音频的编码为amr } // 以当前时间作为文件名 private String getCurrentDate() { SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HHmmss"); Date curDate = new Date(System.currentTimeMillis());// 获取当前时间 String str = formatter.format(curDate); return str; } @OverrIDe public voID start() { // Todo auto-generated method stub if (!isRecording) { try { recorder.prepare(); recorder.start(); } catch (IllegalStateException e) { // Todo auto-generated catch block e.printstacktrace(); } catch (IOException e) { // Todo auto-generated catch block e.printstacktrace(); } isRecording = true; } } @OverrIDe public voID stop() { // Todo auto-generated method stub if (isRecording) { recorder.stop(); recorder.release(); isRecording = false; } } @OverrIDe public voID deleteoldfile() { // Todo auto-generated method stub file file = new file(fileFolder + "/" + filename + ".amr"); file.deleteOnExit(); } @OverrIDe public double getAmplitude() { // Todo auto-generated method stub if (!isRecording) { return 0; } return recorder.getMaxAmplitude(); } @OverrIDe public String getfilePath() { // Todo auto-generated method stub return fileFolder + "/" + filename + ".amr"; }}
MainActivity
package com.example.recordtest;import androID.os.Bundle;import androID.app.Activity;import androID.vIEw.Menu;public class MainActivity extends Activity { Recordbutton button; @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.activity_main); button = (Recordbutton) findVIEwByID(R.ID.btn_record); button.setAudioRecord(new AudioRecorder()); } @OverrIDe public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main,menu); return true; }}
源码下载:AndroID仿微信语音对讲录音
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。
总结以上是内存溢出为你收集整理的Android仿微信语音对讲录音功能全部内容,希望文章能够帮你解决Android仿微信语音对讲录音功能所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)