这段时间做一个项目有即时聊天功能,里面有遇到录音权限的问题,百度了个遍判断权限问题,没有好的解决方案,然而自己代码变通了一下,发现问题其实也还好,先附上录音工具
package .view;
import android.app.Dialog;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
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 com.app.application.AppConfig;
import com.app.tool.FToast;
import com.nbappdev.aikaopu_personal.R;
import com.nbappdev.aikaopu_personal.utils.AudioManager;
/**
* 自定语音按钮
*/
public class AudioRecordButton extends Button implements AudioManager.AudioStageListener{
private Dialog mDialog; //语音对话框
private ImageView mIcon;
private ImageView mVoice;
private TextView mLable;
private Context mContext;
private AudioManager mAudioManager;
private boolean mReady=false; //开始录制
private boolean isRecording = false; //是否开始录音
private boolean recordPermissions = true; //录音权限
private float mTime = 0; //时间
private static final int MSG_AUDIO_PREPARED = 0X110;
private static final int MSG_VOICE_CHANGE = 0X111;
private static final int MSG_DIALOG_DIMISS = 0X112;
private static final int MSG_VOICE_TIME_OUT = 0X113;
private AudioRecordButtonHandler mHandler = new AudioRecordButtonHandler();
private static final int STATE_NORMAL = 1;
private static final int STATE_RECORDING = 2;
private static final int STATE_WANT_TO_CANCEL = 3;
private int mCurrentState = STATE_NORMAL;
private static final int DISTANCE_Y_CANCEL = 50;
private boolean time_out = false; //录制超时
private Runnable runnable;
private Handler handler;
private OnFinishedRecordListener onFinishedRecordListener;
public void setOnFinishedRecordListener(OnFinishedRecordListener onFinishedRecordListener) {
this.onFinishedRecordListener = onFinishedRecordListener;
}
public AudioRecordButton(Context context) {
super(context);
this.mContext = context;
}
public AudioRecordButton(final Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
if( Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ //判断SD卡是否存在
mAudioManager = AudioManager.getInstance(AppConfig.DIR_AUDIO);
mAudioManager.setOnAudioStageListener(this);
recordPermissions = true;
}else {
FToast.show(mContext,"未发现sd卡,无法使用录音功能");
}
}
private class AudioRecordButtonHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_AUDIO_PREPARED:
mTime = 0;
showRecordingDialog();
isRecording = true;
new Thread(mGetVoiceLevelRunnable).start();
handler = new Handler();
runnable = new Runnable() {
@Override
public void run() {
mHandler.sendEmptyMessage(MSG_VOICE_TIME_OUT);
}
};
handler.postDelayed(runnable, 61000);
break;
case MSG_VOICE_CHANGE:
updateVoiceLevel(mAudioManager.getVoiceLevel(7));
break;
case MSG_DIALOG_DIMISS:
mAudioManager.cancel();
dimissDialog();
break;
case MSG_VOICE_TIME_OUT:
time_out = true;
dimissDialog();
mAudioManager.release();
if (onFinishedRecordListener!=null && mReady) {
if (mTime>60){
mTime = 60;
}
onFinishedRecordListener.onFinishedRecord(mAudioManager.getCurrentFilePath(),(int)mTime);
FToast.show(mContext, "录制时间不能超过60秒");
}
handler.removeCallbacks(runnable);
reset();
break;
}
}
};
/**
* 监听屏幕触摸事件
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN: //按下
changeDialogState(STATE_RECORDING);
break;
case MotionEvent.ACTION_MOVE: //移动时
if (isRecording) {
if (wantToCancel(x, y)) {
changeDialogState(STATE_WANT_TO_CANCEL);
}else {
changeDialogState(STATE_RECORDING);
}
}
break;
case MotionEvent.ACTION_UP: //抬起时
if (time_out){
time_out = false;
return super.onTouchEvent(event);
}
if (!mReady) {
reset();
return super.onTouchEvent(event);
}
if (!isRecording || mTime < 0.6f) {
tooShort();
mAudioManager.cancel();
reset();
mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS,0);
}else if (mCurrentState == STATE_RECORDING) {
dimissDialog();
mAudioManager.release();
if (onFinishedRecordListener!=null) {
if (mTime>60){
mTime =60;
}
onFinishedRecordListener.onFinishedRecord(mAudioManager.getCurrentFilePath(),(int)mTime);
}
reset();
}else if (mCurrentState == STATE_WANT_TO_CANCEL) {
mAudioManager.cancel();
dimissDialog();
reset();
}
break;
}
return super.onTouchEvent(event);
}
/**
* 改变对话框状态
* @param state
*/
private void changeDialogState(int state) {
if (mCurrentState != state) {
mCurrentState = state;
switch (mCurrentState) {
case STATE_NORMAL:
setBackgroundResource(R.drawable.button_recordnormal);
setText("按住 说话");
break;
case STATE_RECORDING:
setBackgroundResource(R.drawable.button_recording);
setText("松开 结束");
if (isRecording) {
recording();
}
break;
case STATE_WANT_TO_CANCEL:
setBackgroundResource(R.drawable.button_recording);
setText("松开手指,取消发送");
wantToCancel();
break;
}
}
}
private boolean wantToCancel(int x, int y) {
if (x < 0 || x > getWidth()) { // 判断是否在左边,右边,上边,下边
return true;
}
if (y < -DISTANCE_Y_CANCEL || y > getHeight() + DISTANCE_Y_CANCEL) {
return true;
}
return false;
}
private void reset() {
isRecording = false;
changeDialogState(STATE_NORMAL);
mReady = false;
mTime = 0;
}
/**
* 开始录音
*/
public void startRecord(){
if (recordPermissions){
mReady = true;
mAudioManager.prepareAudio();
}
}
@Override
public boolean onPreDraw() {
return false;
}
//*****************************************Diaolog语音对话框********************************//
/**
* 显示语音对话框
*/
public void showRecordingDialog() {
mDialog = new Dialog(mContext, R.style.Theme_audioDialog);
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.voice_dialog, null);
mDialog.setContentView(view);
mIcon = (ImageView) mDialog.findViewById(R.id.dialog_icon);
mVoice = (ImageView) mDialog.findViewById(R.id.dialog_voice);
mLable = (TextView) mDialog.findViewById(R.id.recorder_dialogtext);
mDialog.show();
}
/**
* 设置正在录音时的dialog界面
*/
public void recording() {
if (mDialog != null && mDialog.isShowing()) {
mIcon.setVisibility(View.VISIBLE);
mVoice.setVisibility(View.VISIBLE);
mLable.setVisibility(View.VISIBLE);
mIcon.setImageResource(R.drawable.recorder);
mLable.setText("手指上滑,取消发送");
}
}
/**
* 取消录制
*/
public void wantToCancel() {
if (mDialog != null && mDialog.isShowing()) {
mIcon.setVisibility(View.VISIBLE);
mVoice.setVisibility(View.GONE);
mLable.setVisibility(View.VISIBLE);
mIcon.setImageResource(R.drawable.cancel);
mLable.setText("松开手指,取消发送");
}
}
/**
* 录制时间过短
*/
public void tooShort() {
if (mDialog != null && mDialog.isShowing()) {
mIcon.setVisibility(View.VISIBLE);
mVoice.setVisibility(View.GONE);
mLable.setVisibility(View.VISIBLE);
mIcon.setImageResource(R.drawable.voice_to_short);
mLable.setText("录音时间过短");
}
}
/**
* 隐藏dialog
*/
public void dimissDialog() {
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
mDialog = null;
}
}
public void updateVoiceLevel(int level) {
if (mDialog != null && mDialog.isShowing()) {
int resId = mContext.getResources().getIdentifier("v" + level,
"drawable", mContext.getPackageName());
mVoice.setImageResource(resId);
}
}
/**
* 开始录制
*/
@Override
public void wellPrepared() {
Log.i("触发","准备完成");
mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);
}
/**
* 异步获取音量
*/
private Runnable mGetVoiceLevelRunnable = new Runnable() {
@Override
public void run() {
while (isRecording) {
try {
Thread.sleep(100);
mTime += 0.1f;
mHandler.sendEmptyMessage(MSG_VOICE_CHANGE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
/**
* 录制完成回调
*/
public interface OnFinishedRecordListener {
void onFinishedRecord(String audioPath,int voice_length);
}
}
然后录音管理工具类
<pre name="code" class="java">package .utils;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;
import java.io.File;
import java.io.IOException;
/**
* 语音录制管理类
*/
public class AudioManager {
private MediaRecorder mRecorder;
private String mDirString; //文件夹路径
private String mCurrentFilePathString; //当前的详细路径
private static AudioManager mInstance;
public AudioStageListener mListener;
private boolean isPrepared;
// 音频获取源
public static int audioSource = MediaRecorder.AudioSource.MIC;
// 设置音频采样率,44100是目前的标准,但是某些设备仍然支持22050,16000,11025
public static int sampleRateInHz = 44100;
// 设置音频的录制的声道CHANNEL_IN_STEREO为双声道,CHANNEL_CONFIGURATION_MONO为单声道
public static int channelConfig = AudioFormat.CHANNEL_IN_STEREO;
// 音频数据格式:PCM 16位每个样本。保证设备支持。PCM 8位每个样本。不一定能得到设备支持。
public static int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
// 缓冲区字节大小
public static int bufferSizeInBytes = 0;
public void setOnAudioStageListener(AudioStageListener listener) {
mListener = listener;
}
private AudioManager(String dir) {
mDirString=dir;
}
public static AudioManager getInstance(String dir) {
if (mInstance == null) {
synchronized (AudioManager.class) {
if (mInstance == null) {
mInstance = new AudioManager(dir);
}
}
}
return mInstance;
}
/**
* 准备开始录制
*/
public void prepareAudio() {
try {
isPrepared = false;
File dir = new File(mDirString);
if (!dir.exists()) {
dir.mkdirs();
}
String fileNameString = System.currentTimeMillis()+".m4a";
File file = new File(dir, fileNameString);
mCurrentFilePathString = file.getAbsolutePath();
mRecorder = new MediaRecorder();
// 设置输出文件
mRecorder.setOutputFile(file.getAbsolutePath());
// 设置meidaRecorder的音频源是麦克风
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
// 设置文件音频的输出格式为MPEG_4
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
// 设置音频的编码格式为AAC
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.prepare();
mRecorder.start();
isPrepared = true;
Log.i("调用","开始调用");
if (mListener != null) {
mListener.wellPrepared();
}
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 获取声音的涨幅
* @param maxLevel
* @return
*/
public int getVoiceLevel(int maxLevel) {
if (isPrepared) {
try {
return maxLevel * mRecorder.getMaxAmplitude() / 32768 + 1;
} catch (Exception e) {
e.printStackTrace();
}
}
return 1;
}
/**
* 资源释放
*/
public void release() {
try {
if (mRecorder!=null){
mRecorder.stop();
mRecorder.release();
mRecorder = null;
}
}catch (Exception e){
e.printStackTrace();
}
}
public void cancel() {
release();
if (mCurrentFilePathString != null) {
File file = new File(mCurrentFilePathString);
file.delete();
mCurrentFilePathString = null;
}
}
/**
*判断是否有录音权限
* @return
*/
public static boolean isHasPermission(){
bufferSizeInBytes = 0;
bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz,
channelConfig, audioFormat);
AudioRecord audioRecord = new AudioRecord(audioSource, sampleRateInHz,
channelConfig, audioFormat, bufferSizeInBytes);
try{
audioRecord.startRecording();
}catch (IllegalStateException e){
e.printStackTrace();
}
Log.i("录音权限","录音权限"+audioRecord.getRecordingState());
if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
return false;
}
audioRecord.stop();
audioRecord.release();
audioRecord = null;
return true;
}
/**
* 获取当前语音的路径
* @return
*/
public String getCurrentFilePath() {
return mCurrentFilePathString;
}
public interface AudioStageListener {
void wellPrepared();
}
}
布局文件我就不贴了,开始调用录音之前<pre name="code" class="java">if (AudioManager.isHasPermission()){
down_talk.startRecord(); //开始录音
}
判断一下就行了,第一次弄录音功能,代码都是网上找的,然后自己稍微改一下,这个判断其实你只要静态注册了录音权限的话都会返回true的,<span style="font-family: Arial, Helvetica, sans-serif;">这里的作用只是真正录音之前先</span>
<span style="font-family:Arial, Helvetica, sans-serif;">让用户允许录音权限而已,然后执行开始录音,因为第一次肯定会弹起录音权限对话框,所以当你手指离开的时候会调用</span><span style="font-family: Arial, Helvetica, sans-serif;">onTouchEvent的</span><span style="font-family: Arial, Helvetica, sans-serif;">MotionEvent.ACTION_UP事件,这地方</span>
<span style="font-family: Arial, Helvetica, sans-serif;">我判断了很多因素,基本上只会执行走正确流程的录音方式</span>
<pre name="code" class="java">
<pre name="code" class="java">