快速录音需求,还要求音频文件很小,呵呵,那就干吧:
package com.zzq.mydemo.test;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import com.zzq.mydemo.BaseActivity;
import com.zzq.mydemo.R;
import java.io.File;
import java.io.IOException;
/**
* 录音 Activity
*
*
*/
public class RecordingActivity extends BaseActivity {
private Dialog mDialog;
private TextView mTvCountdown;
private TimeCount mTimeCount;
private MediaRecorder mRecorder;
private String mFileName;
private String mFilePath;
public static final String FILE_NAME = "FILE_NAME";
public static final String FILE_PATH = "FILE_PATH";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showDialog();
initMediaRecorder();
// 多加 100毫秒,用于补偿 TimeCount的时间损耗,解决跳 1秒的 BUG【目前很好用】
mTimeCount = new TimeCount(10100, 1000);
}
private void initMediaRecorder() {
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setAudioChannels(1);
// 设置录音文件的清晰度,这里将 1s音频压缩到 1k大小
mRecorder.setAudioSamplingRate(8000);
mRecorder.setAudioEncodingBitRate(8000);
}
@Override
protected void onDestroy() {
stopRecording();
if (mTimeCount != null) {
mTimeCount.cancel();
}
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
mDialog = null;
}
super.onDestroy();
}
private void showDialog() {
mDialog = new Dialog(this, R.style.NormalDialogStyle);
View view = View.inflate(this, R.layout.dialog_recording, null);
WindowManager.LayoutParams lp = mDialog.getWindow().getAttributes();
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.CENTER;
mDialog.setContentView(view, lp);
// 设置点击对话框外部是否关闭对话框
mDialog.setCanceledOnTouchOutside(true);
// 点击对话框外部或者点击返回键都会触发此回调
mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
forResult();
}
});
mTvCountdown = (TextView) view.findViewById(R.id.tv_countdown);
Button btnRecording = (Button) view.findViewById(R.id.btn_recording);
btnRecording.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startRecording();
break;
case MotionEvent.ACTION_UP:
forResult();
break;
default:
break;
}
return true;
}
});
mDialog.show();
}
private class TimeCount extends CountDownTimer {
private TimeCount(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
@Override
public void onTick(long millisUntilFinished) {
mTvCountdown.setText(millisUntilFinished / 1000 + "s");
}
@Override
public void onFinish() {
forResult();
}
}
private void startRecording() {
mTimeCount.start();
setFileNameAndPath();
// 设置录音文件的保存路径
mRecorder.setOutputFile(mFilePath);
try {
mRecorder.prepare();
mRecorder.start();
} catch (IOException e) {
Log.e("RecordingActivity", "prepare() failed");
}
}
/**
* 设置录音文件的名字和保存路径
*/
private void setFileNameAndPath() {
mFileName = System.currentTimeMillis() + ".aac";
mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/SoundRecorder/" + mFileName;
File file = new File(mFilePath);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
}
private void stopRecording() {
if (mRecorder != null) {
try {
mRecorder.stop();
} catch (IllegalStateException e) {
// 如果当前java状态和jni里面的状态不一致
Log.e("RecordingActivity", "stop() failed");
}
mRecorder.release();
mRecorder = null;
}
}
private void forResult() {
Intent intent = new Intent();
intent.putExtra(FILE_NAME, mFileName);
intent.putExtra(FILE_PATH, mFilePath);
setResult(RESULT_OK, intent);
finish();
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
android:orientation="vertical"
android:paddingLeft="30dp"
android:paddingRight="30dp">
<TextView
android:id="@+id/tv_countdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:gravity="center"
android:text="10s"
android:textColor="#ff0000"
android:textSize="30sp" />
<Button
android:id="@+id/btn_recording"
android:layout_width="250dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:layout_marginBottom="30dp"
android:layout_marginTop="30dp"
android:text="按住开始录音"
android:textColor="#ffffff"
android:textSize="18sp" />
</LinearLayout>
其中有趣的点:
1、录音前先初始化 MediaRecorder:initMediaRecorder(),省时间,做到点击录音后就可以马上去录音;
2、通过调整 setAudioSamplingRate(int i) 和 setAudioEncodingBitRate(int i) 可以控制所录音频的质量,从而调整音频文件大小;
3、退出时要及时清理垃圾;
4、TimeCount有个跳 1秒的 BUG,这是程序运行耗时不断累积造成的,通过多加 100毫秒的方式,来补偿 TimeCount 的时间损耗,解决跳 1秒的 BUG,目前很好用(不知道有没有坑 >_<|||):
mTimeCount = new TimeCount(10100, 1000);
5、设置 setCanceledOnTouchOutside(true) 时,要监听此行为并结束录音:
// 设置点击对话框外部是否关闭对话框
mDialog.setCanceledOnTouchOutside(true);
// 点击对话框外部或者点击返回键都会触发此回调
mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
forResult();
}
});
6、用了 “按住录音,放开停止” 的方式:
Button btnRecording = (Button) view.findViewById(R.id.btn_recording);
btnRecording.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startRecording();
break;
case MotionEvent.ACTION_UP:
forResult();
break;
default:
break;
}
return true;
}
});
下面是调用录音页面及播放所录音频的代码:
public class MainActivity extends BaseActivity {
private MediaPlayer mMediaPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onJump(View view) {
Intent intent = new Intent(this, RecordingActivity.class);
startActivityForResult(intent, 1);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
String fileName = data.getStringExtra(RecordingActivity.FILE_NAME);
String filePath = data.getStringExtra(RecordingActivity.FILE_PATH);
if (!filePath.isEmpty()) {
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
stopPlaying();
return;
}
playAudio(false, filePath);
}
}
}
private void playAudio(boolean isInternet, String filePath) {
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
stopPlaying();
}
});
}
try {
if (isInternet) {
mMediaPlayer.setDataSource(this, Uri.parse(filePath));
} else {
mMediaPlayer.setDataSource(filePath);
}
mMediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
mMediaPlayer.start();
}
private void stopPlaying() {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
}
@Override
protected void onDestroy() {
stopPlaying();
super.onDestroy();
}
}
参考文章:
1、https://www.jianshu.com/p/6bbb51ac4938
2、https://www.jianshu.com/p/6d91a8d7b974
3、https://blog.csdn.net/hecheng2009/article/details/74807032
4、https://blog.csdn.net/feiyu1947/article/details/85332322