因为公司的项目需要,要做一个有暂停功能的录音播放器。查询了资料发现Android原生的录音API--MediaRecorder没有暂停功能。一旦调用MediaRecorder的stop方法,就会生成一个录音文件,所以只能采用一个间接的方式去把每个录音文件给拼接起来。

网上查了好多资料,看了某大神的例子,选出了这个最简单的方式,amr格式的录音文件的合并,只需要把每个录音文件的二进制数据的前6位数据给删除,然后一起保存起来就实现了无缝拼接。比如录了两个文件,a.amr和b.amr,只需要把b.amr的头6位数据删除,然后把两个文件的字节流合并成一个文件,就实现了无缝拼接。

经过一整天的测试和修改,写出了最符合项目需求的Demo(已测试,没bug)。

代码下载链接点这里!!!!

先来看看效果图:

录音过程中会计时,可暂停,点击完成录音会生成最终的录音文件

录音结束后会生成相应文件夹下的录音列表,点击选择播放,播放可暂停。

代码中带有详细的注释,仔细看就会懂。

  
package com.example.mediarecorde;import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;public class MainActivity extends Activity implements OnClickListener,OnItemClickListener {// 语音文件private String fileName = null;// 音频文件保存的路径private String path = "";// 界面控件zprivate Button startRecord;// 开始录音private Button startPlay;// 开始播放private Button stopRecord;// 完成录音private Button stopPlay;// 停止播放private TextView time;// 计时显示private ListView mListView;// 音频文件列表private MAdapter mAdapter;private Button delete;// 删除按钮private Button pausePlay;// 暂停播放// 语音操作对象private MediaPlayer mPlayer = null;// 播放器private MediaRecorder mRecorder = null;// 录音器private boolean isPause = false;// 当前录音是否处于暂停状态private boolean isPausePlay = false;// 当前播放器是否处于暂停状态private ArrayList<String> mList = new ArrayList<String>();// 待合成的录音片段private ArrayList<String> list = new ArrayList<String>();// 已合成的录音片段private String deleteStr = null; // 列表中要删除的文件名private Timer timer;private String playFileName = null;// 选中的播放文件// 相关变量private int second = 0;private int minute = 0;private int hour = 0;private View whichSelecte = null;// 记录被选中的Itemprivate long limitTime = 0;// 录音文件最短事件1秒@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化录音列表initList();// 初始化控件initView();}// 初始化界面private void initView() {delete = (Button) findViewById(R.id.delete);delete.setOnClickListener(this);// 对按钮的可点击事件的控制是保证不出现空指针的重点!!delete.setEnabled(false);pausePlay = (Button) findViewById(R.id.pausePlay);pausePlay.setOnClickListener(this);pausePlay.setEnabled(false);startRecord = (Button) findViewById(R.id.startRecord);startRecord.setOnClickListener(this);stopRecord = (Button) findViewById(R.id.stopRecord);stopRecord.setOnClickListener(this);stopRecord.setEnabled(false);startPlay = (Button) findViewById(R.id.startPlay);startPlay.setOnClickListener(this);startPlay.setEnabled(false);stopPlay = (Button) findViewById(R.id.stopPlay);stopPlay.setOnClickListener(this);stopPlay.setEnabled(false);time = (TextView) findViewById(R.id.time);mListView = (ListView) findViewById(R.id.listview);mAdapter = new MAdapter(this, list);mListView.setAdapter(mAdapter);mListView.setOnItemClickListener(this);}// 初始化录音列表private void initList() {path = Environment.getExternalStorageDirectory().getAbsolutePath()+ "/Anhry/" + getPackageName() + "/Record";// 判断SD卡是否存在if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {Toast.makeText(this, "SD卡状态异常,无法获取录音列表!", Toast.LENGTH_LONG).show();} else {// 根据后缀名进行判断、获取文件夹中的音频文件File file = new File(path);File files[] = file.listFiles();if (files != null) {for (int i = 0; i < files.length; i++) {if (files[i].getName().indexOf(".") >= 0) {// 只取.amr .mp3// .mp4 文件String fileStr = files[i].getName().substring(files[i].getName().indexOf("."));if (fileStr.toLowerCase().equals(".mp3")|| fileStr.toLowerCase().equals(".amr")|| fileStr.toLowerCase().equals(".mp4"))list.add(files[i].getName());}}}}}// 设置点击事件@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.startRecord:// 开始录音// 判断SD卡是否存在if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {Toast.makeText(this, "SD卡状态异常,请检查后重试!", Toast.LENGTH_LONG).show();break;}// 开始录音startRecord();// 录音计时recordTime();break;case R.id.stopRecord:// 完成录音if (isPause) {// 完成录音stopRecord();} else {// 暂停录音try {pauseRecord();} catch (InterruptedException e) {// 当一个线程处于等待,睡眠,或者占用,也就是说阻塞状态,而这时线程被中断就会抛出这类错误// 上百次测试还未发现这个异常,但是需要捕获e.printStackTrace();}}break;case R.id.startPlay:// 播放录音playRecord();break;case R.id.stopPlay:// 停止播放startPlay.setEnabled(true);stopPlay.setEnabled(false);startRecord.setEnabled(true);pausePlay.setEnabled(false);if (mPlayer != null) {// 释放资源// 对MediaPlayer多次使用而不释放资源就会出现MediaPlayer create faild 的异常mPlayer.release();mPlayer = null;}delete.setEnabled(true);break;case R.id.delete:// 删除录音文件deleteRecord();break;case R.id.pausePlay:// 暂停播放if (isPausePlay) {pausePlay.setText("暂停播放");pausePlay.setEnabled(true);isPausePlay = false;mPlayer.start();} else {if (mPlayer != null) {mPlayer.pause();}pausePlay.setText("继续播放");pausePlay.setEnabled(true);isPausePlay = true;}break;default:break;}}// 判断点击事件的时间间隔// 点击速度过快,比如在同一秒中点击三次,只会产生一个录音文件,因为命名一样。private boolean limitTime() {limitTime = System.currentTimeMillis() - limitTime;if (limitTime >= 1100) {limitTime = System.currentTimeMillis();return true;} else {return false;}}// 删除录音文件private void deleteRecord() {// 删除所选中的录音文件File file = new File(playFileName);if (file.exists()) {file.delete();list.remove(deleteStr);mAdapter.notifyDataSetChanged();time.setText("");} else {list.remove(deleteStr);mAdapter.notifyDataSetChanged();}startPlay.setEnabled(false);playFileName = null;delete.setEnabled(false);startRecord.setEnabled(true);time.setText("您本次的录音时长为:       00:00:00");}// 播放录音private void playRecord() {// 对按钮的可点击事件的控制是保证不出现空指针的重点!!startRecord.setEnabled(false);delete.setEnabled(false);stopPlay.setEnabled(true);startPlay.setEnabled(false);pausePlay.setEnabled(true);if (mPlayer != null) {mPlayer.release();mPlayer = null;}mPlayer = new MediaPlayer();// 播放完毕的监听mPlayer.setOnCompletionListener(new OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {// 播放完毕改变状态,释放资源mPlayer.release();mPlayer = null;startRecord.setEnabled(true);startPlay.setEnabled(true);stopPlay.setEnabled(false);delete.setEnabled(true);pausePlay.setEnabled(false);}});try {// 播放所选中的录音mPlayer.setDataSource(playFileName);mPlayer.prepare();mPlayer.start();} catch (Exception e) {// 若出现异常被捕获后,同样要释放掉资源// 否则程序会不稳定,不适合正式项目上使用if (mPlayer != null) {mPlayer.release();mPlayer = null;}Toast.makeText(this, "播放失败,可返回重试!", Toast.LENGTH_LONG).show();stopPlay.setEnabled(false);delete.setEnabled(true);pausePlay.setEnabled(false);}}// 完成录音private void stopRecord() {mRecorder.release();mRecorder = null;isPause = false;startRecord.setEnabled(true);startRecord.setText("开始录音");stopRecord.setEnabled(false);timer.cancel();// 最后合成的音频文件fileName = path + "/" + getTime() + ".amr";String fileName1 = getTime() + ".amr";FileOutputStream fileOutputStream = null;try {fileOutputStream = new FileOutputStream(fileName);} catch (FileNotFoundException e) {e.printStackTrace();}FileInputStream fileInputStream = null;try {for (int i = 0; i < mList.size(); i++) {File file = new File(mList.get(i));// 把因为暂停所录出的多段录音进行读取fileInputStream = new FileInputStream(file);byte[] mByte = new byte[fileInputStream.available()];int length = mByte.length;// 第一个录音文件的前六位是不需要删除的if (i == 0) {while (fileInputStream.read(mByte) != -1) {fileOutputStream.write(mByte, 0, length);}}// 之后的文件,去掉前六位else {while (fileInputStream.read(mByte) != -1) {fileOutputStream.write(mByte, 6, length - 6);}}}list.add(fileName1);mAdapter.notifyDataSetChanged();} catch (Exception e) {// 这里捕获流的IO异常,万一系统错误需要提示用户e.printStackTrace();Toast.makeText(this, "录音合成出错,请重试!", Toast.LENGTH_LONG).show();} finally {try {fileOutputStream.flush();fileInputStream.close();} catch (Exception e) {e.printStackTrace();}// 录音结束 、时间归零minute = 0;hour = 0;second = 0;}// 不管合成是否成功、删除录音片段for (int i = 0; i < mList.size(); i++) {File file = new File(mList.get(i));if (file.exists()) {file.delete();}}}// 暂停录音private void pauseRecord() throws InterruptedException {if (System.currentTimeMillis()-limitTime<1100) {//录音文件不得低于一秒钟Toast.makeText(this, "录音时间长度不得低于1秒钟!", Toast.LENGTH_SHORT).show();return ;}stopRecord.setEnabled(true);mRecorder.stop();mRecorder.release();timer.cancel();isPause = true;// 将录音片段加入列表mList.add(fileName);startRecord.setEnabled(true);startRecord.setText("继续录音");stopRecord.setText("完成录音");}// 开始录音private void startRecord() {stopRecord.setText("暂停录音");startRecord.setText("录音中...");startRecord.setEnabled(false);startPlay.setEnabled(false);stopRecord.setEnabled(true);delete.setEnabled(false);if (!isPause) {// 新录音清空列表mList.clear();}File file = new File(path);if (!file.exists()) {file.mkdirs();}fileName = path + "/" + getTime() + ".amr";isPause = false;mRecorder = new MediaRecorder();mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 选择amr格式mRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);mRecorder.setOutputFile(fileName);mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);try {mRecorder.prepare();} catch (Exception e) {// 若录音器启动失败就需要重启应用,屏蔽掉按钮的点击事件。 否则会出现各种异常。Toast.makeText(this, "录音器启动失败,请返回重试!", Toast.LENGTH_LONG).show();startPlay.setEnabled(false);stopPlay.setEnabled(false);delete.setEnabled(false);startRecord.setEnabled(false);stopRecord.setEnabled(false);mRecorder.release();mRecorder = null;this.finish();}if (mRecorder != null) {mRecorder.start();limitTime = System.currentTimeMillis();}}// 计时器异步更新界面Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {time.setText("您本次的录音时长为:       "+ String.format("%1$02d:%2$02d:%3$02d", hour, minute,second));super.handleMessage(msg);}};// 录音计时private void recordTime() {TimerTask timerTask = new TimerTask() {@Overridepublic void run() {second++;if (second >= 60) {second = 0;minute++;if (minute >= 60) {minute = 0;hour++;}}handler.sendEmptyMessage(1);}};timer = new Timer();timer.schedule(timerTask, 1000, 1000);}// 获得当前时间private String getTime() {SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");Date curDate = new Date(System.currentTimeMillis());// 获取当前时间String time = formatter.format(curDate);return time;}// 录音列表被点击事件@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position,long id) {// 屏蔽点击事件的一种方式if (mRecorder == null) {startPlay.setEnabled(true);if (mPlayer == null || !mPlayer.isPlaying()) {delete.setEnabled(true);} else {delete.setEnabled(false);}}startPlay.setText("播放录音");// 列表文件的选中效果if (whichSelecte != null) {whichSelecte.setBackgroundColor(getResources().getColor(R.color.no));}view.setBackgroundColor(getResources().getColor(R.color.yes));// 要播放文件的路径playFileName = path + "/" + list.get(position);// 要删除文件的名称deleteStr = list.get(position);whichSelecte = view;time.setText(list.get(position));}// Activity被销毁的时候 释放资源@Overrideprotected void onDestroy() {// 删除片段if (mList != null && mList.size() > 0) {for (int i = 0; i < mList.size(); i++) {File file = new File(mList.get(i));if (file.exists()) {file.delete();}}}if (null != mRecorder) {mRecorder.stop();mRecorder.release();mRecorder = null;}if (null != mPlayer) {mPlayer.stop();mPlayer.release();mPlayer = null;}if (timer != null) {timer.cancel();}super.onDestroy();}// 来电暂停@Overrideprotected void onPause() {if (mRecorder != null) {// 暂停录音try {pauseRecord();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}if (mPlayer != null) {// 暂停播放mPlayer.pause();isPausePlay = true;pausePlay.setText("继续播放");pausePlay.setEnabled(true);}super.onPause();}}

这是主程序的代码,另外ListView的适配器、XML布局文件、资源文件都是些很简单的基础。

代码下载链接点这里!!!!

Android录音器(可暂停、续录、续播)完整Demo相关推荐

  1. Android录音器实验报告,Android实现录音(MediaRecorder)

    Android提供了两个API用于录音的实现:MediaRecorder 和 AudioRecord,各有优劣. 1.MediaRecorder 已经集成了录音.编码.压缩等,支持少量的录音音频格式, ...

  2. Android录音器

    http://blog.csdn.net/a601445984/article/details/44239717

  3. android ‘低’仿支付宝我的应用功能!(含完整Demo)

    '低'配支付宝我的应用功能 我的环境 界面和需求分析 代码思路 最近项目需求,要求我仿造支付宝功能,写一个类似的功能,想了1天,实操2天终于搞定了!! 先来看看实现的效果: 效果一 效果二 效果三 我 ...

  4. Android视频编辑器(一)通过OpenGL预览、录制视频以及断点续录等

    前言 如今的视频类app可谓是如日中天,火的不行.比如美拍.快手.VUE.火山小视频.抖音小视频等等.而这类视频的最基础和核心的功能就是视频录制和视频编辑功能.包括了手机视频录制.美白.加滤镜.加水印 ...

  5. Android Mp3格式录音,含有暂停,计时功能

    Mp3录音,边录边转: 博主前端时间做的项目,遇到关于android录音相关的问题.由于android系统自带的录音Audiorecord录制出来的文件格式为PCM,或者简单的加上WAV头文件,转换成 ...

  6. 转 如何使android录音实现内录功能,BroadcastReceiver实现android来去电录音功能(外录)...

    因为原生android没有提供来去电内录功能,所以只能通过麦克进行通话录音, /** * 来去电录音,因为去电没有接听的状态,只要拨出就会开始录音 * * @author jauken * @date ...

  7. android连接蓝牙控制音乐播放器播放暂停/上一曲/下一曲,且断开蓝牙暂停音乐

    客户需求要蓝牙设备可以控制音乐播放器的暂停等操作,当时只做了蓝牙的权限配置,未对这些操作做处理. 1.配置清单文件 <serviceandroid:name=".PlayerServi ...

  8. android 录音原始文件_Android 录音详解(一)—— MediaRecorder、AudioRecord、生成wav格式、边录边播...

    Android 录音详解(一)-- MediaRecorder.AudioRecord.生成wav格式.边录边播 越来越多的 APP 都用到了手机的录音功能,比如搜索.聊天.输入.K歌等... 本系列 ...

  9. Android 录音声波图

    图像类: package com.akm.test;/*** Created by toge on 15/12/9.*/ import android.content.Context; import ...

最新文章

  1. 【连载】【黑金动力社区原创力作】《液晶驱动与GUI 基础教程》 --序言(一)
  2. 7-8 超速判断 (C语言)
  3. python为什么慢_python-为什么startswith比切片慢
  4. 《Go语言编程》学习笔记 (二)
  5. 网络信息安全之防火墙的设计 (三)
  6. MATLAB2014b下运行cuda6.5安装方法及例程
  7. php 单词替换,如何在PHP中替换字符串中的单词?
  8. 卸载 插件_永远都不会卸载的CAD插件,好用到怀疑人生
  9. mysql mydumper_系统运维|Mydumper-MySQL数据库备份工具
  10. hive读取hdfs存放文件_数据获取层之Flume快速入门(一) 实时监控单个追加文件
  11. 《程序设计与数据结构》第八周学习总结
  12. (ContrastiveCrop)Crafting Better Contrastive Views for Siamese Representation Learning
  13. 车牌识别算法库EasyPR的编译实战
  14. JavaScript将小写金额转换成大写
  15. vue鼠标划过移入移出触发方法
  16. kafka重新分配partition
  17. 国际象棋棋盘 java_java打印国际象棋棋盘的方法
  18. 示波器FFT频谱分析的使用方法和注意点
  19. WCF技术剖析之十四:泛型数据契约和集合数据契约(上篇)
  20. HbuilderX下载安装教程

热门文章

  1. 不和谐的最佳高级用户提示
  2. 超级账本与企业以太坊联盟(EEA)宣布结盟共同为区块链制定标准
  3. mp4转gif 转换_【最新】腾讯视频格式转换(qlv转mp4),适用于新版本。
  4. NDTV的完整形式是什么?
  5. 创弘星鹏电商:提高抖音好评率具体的做法是什么
  6. 三维曲面的绘制(Python/MATLAB)
  7. SystemUI NotificationPanelView展开
  8. 爬虫系列教程四:动态网页api分析实例之爬取dropbox上的pdf
  9. 再谱新篇 | 美格智能SLM750模组连获日本三大运营商权威认证
  10. 遍历List集合的五种方式