栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 移动开发 > Android

Android仿微信录音功能

Android 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android仿微信录音功能

提要:需求是开发类似微信发语音的功能,没有语音转文字。网上看了一些代码,不能拿来直接用,部分代码逻辑有问题,所以想把自己的代码贴出来,仅供参考。

功能:

a、设置最大录音时长和录音倒计时(为了方便测试,最大时长设置为15秒,开始倒计时设置为7秒)

b、在录音之前检查录音和存储权限

源码:

1、录音对话框管理类DialogManager:


public class DialogManager {
  private alertDialog.Builder builder;
  private alertDialog dialog;
  private ImageView mIcon;
  private ImageView mVoice;
  private TextView mLabel;
 
  private Context context;
 
  
  public DialogManager(Context context) {
    this.context = context;
  }
 
  
  public void showRecordingDialog() {
    builder = new alertDialog.Builder(context, R.style.AudioRecorderDialogStyle);
    LayoutInflater inflater = LayoutInflater.from(context);
    View view = inflater.inflate(R.layout.audio_recorder_dialog, null);
    mIcon = view.findViewById(R.id.iv_dialog_icon);
    mVoice = view.findViewById(R.id.iv_dialog_voice);
    mLabel = view.findViewById(R.id.tv_dialog_label);
 
    builder.setView(view);
    dialog = builder.create();
    dialog.show();
    dialog.setCanceledonTouchOutside(false);
  }
 
  
  public void recording() {
    if (dialog != null && dialog.isShowing()) { //显示状态
      mIcon.setVisibility(View.VISIBLE);
      mVoice.setVisibility(View.VISIBLE);
      mLabel.setVisibility(View.VISIBLE);
 
      mIcon.setImageResource(R.drawable.ic_audio_recorder);
      mVoice.setImageResource(R.drawable.ic_audio_v1);
      mLabel.setText(R.string.audio_record_dialog_up_to_cancel);
    }
  }
 
  
  public void wantToCancel() {
    if (dialog != null && dialog.isShowing()) { //显示状态
      mIcon.setVisibility(View.VISIBLE);
      mVoice.setVisibility(View.GONE);
      mLabel.setVisibility(View.VISIBLE);
 
      mIcon.setImageResource(R.drawable.ic_audio_cancel);
      mLabel.setText(R.string.audio_record_dialog_release_to_cancel);
    }
  }
 
  
  public void tooShort() {
    if (dialog != null && dialog.isShowing()) { //显示状态
      mIcon.setVisibility(View.VISIBLE);
      mVoice.setVisibility(View.GONE);
      mLabel.setVisibility(View.VISIBLE);
 
      mLabel.setText(R.string.audio_record_dialog_too_short);
    }
  }
 
  // 显示取消的对话框
  public void dismissDialog() {
    if (dialog != null && dialog.isShowing()) { //显示状态
      dialog.dismiss();
      dialog = null;
    }
  }
 
  
  public void updateVoiceLevel(int level) {
    if (dialog != null && dialog.isShowing()) { //显示状态
      mIcon.setVisibility(View.VISIBLE);
      mVoice.setVisibility(View.VISIBLE);
      mLabel.setVisibility(View.VISIBLE);
 
      int resId = context.getResources().getIdentifier("ic_audio_v" + level, "drawable", context.getPackageName());
      mVoice.setImageResource(resId);
    }
  }
 
  public void updateTime(int time) {
    if (dialog != null && dialog.isShowing()) { //显示状态
      mIcon.setVisibility(View.VISIBLE);
      mVoice.setVisibility(View.VISIBLE);
      mLabel.setVisibility(View.VISIBLE);
      mLabel.setText(time + "s");
    }
  }
}

2、录音管理类AudioManager

 
public class AudioManager {
  private MediaRecorder mMediaRecorder;
  private String mDir;
  private String mCurrentFilePath;
 
  private static AudioManager mInstance;
 
  private boolean isPrepared;
 
  private AudioManager(String dir) {
    this.mDir = dir;
  }
 
  //单例模式:在这里实例化AudioManager并传入录音文件地址
  public static AudioManager getInstance(String dir) {
    if (mInstance == null) {
      synchronized (AudioManager.class) {
 if (mInstance == null) {
   mInstance = new AudioManager(dir);
 }
      }
    }
    return mInstance;
  }
 
  
  public interface AudioStateListener {
    void wellPrepared();
  }
 
  public AudioStateListener mListener;
 
  
  public void setonAudioStateListener(AudioStateListener listener) {
    mListener = listener;
  }
 
  
  public void prepareAudio() {
    try {
      isPrepared = false;
      File dir = FileUtils.createNewFile(mDir);
      String fileName = generateFileName();
 
      File file = new File(dir, fileName);
      mCurrentFilePath = file.getAbsolutePath();
      Logger.t("AudioManager").i("audio file name :" + mCurrentFilePath);
 
      mMediaRecorder = new MediaRecorder();
      //设置输出文件
      mMediaRecorder.setOutputFile(mCurrentFilePath);
      //设置MediaRecorder的音频源为麦克风
      mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
      //设置音频格式
      mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
      //设置音频的格式为AAC
      mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
      //准备录音
      mMediaRecorder.prepare();
      //开始
      mMediaRecorder.start();
      //准备结束
      isPrepared = true;
      if (mListener != null) {
 mListener.wellPrepared();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
 
  
  private String generateFileName() {
    return UUID.randomUUID().toString() + ".m4a";
  }
 
  public int getVoiceLevel(int maxLevel) {
    if (isPrepared) {
      try {
 //获得最大的振幅getMaxAmplitude() 1-32767
 return maxLevel * mMediaRecorder.getMaxAmplitude() / 32768 + 1;
      } catch (Exception e) {
      }
    }
    return 1;
  }
 
  
  public void release() {
    if (mMediaRecorder != null) {
      mMediaRecorder.stop();
      mMediaRecorder.release();
      mMediaRecorder = null;
    }
  }
 
  public void cancel() {
    release();
    if (mCurrentFilePath != null) {
      File file = new File(mCurrentFilePath);
      FileUtils.deleteFile(file);
      mCurrentFilePath = null;
    }
  }
 
  public String getCurrentFilePath() {
    return mCurrentFilePath;
  }
}

3、自定义录音按钮AudioRecorderButton


public class AudioRecorderButton extends AppCompatButton {
  private Context mContext;
  //取消录音Y轴位移
  private static final int DISTANCE_Y_CANCEL = 80;
  //录音最大时长限制
  private static final int AUDIO_RECORDER_MAX_TIME = 15;
  //录音倒计时时间
  private static final int AUDIO_RECORDER_COUNT_DOWN = 7;
  //状态
  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 boolean isRecording = false;
  //是否触发onLongClick
  private boolean mReady;
 
  private DialogManager mDialogManager;
  private AudioManager mAudioManager;
  private android.media.AudioManager audioManager;
 
  public AudioRecorderButton(Context context) {
    this(context, null);
  }
 
  public AudioRecorderButton(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.mContext = context;
    mDialogManager = new DialogManager(context);
    audioManager = (android.media.AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
 
    String dir = SdUtils.getCustomFolder("Audios");//创建文件夹
    mAudioManager = AudioManager.getInstance(dir);
    mAudioManager.setonAudioStateListener(new AudioManager.AudioStateListener() {
      @Override
      public void wellPrepared() {
 mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);
      }
    });
    //按钮长按 准备录音 包括start
    setonLongClickListener(new onLongClickListener() {
      @Override
      public boolean onLongClick(View v) {
 //先判断有没有录音和存储权限,有则开始录音,没有就申请权限
 int hasAudioPermission = ContextCompat.checkSelfPermission(mContext, Manifest.permission.RECORD_AUDIO);
 int hasStoragePermission = ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE);
 if (hasAudioPermission == PackageManager.PERMISSION_GRANTED && hasStoragePermission == PackageManager.PERMISSION_GRANTED) {
   mReady = true;
   mAudioManager.prepareAudio();
 } else {
   RxPermissions permissions = new RxPermissions((FragmentActivity) mContext);
   Disposable disposable = permissions.request(Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE)
.subscribe(new Consumer() {
  @Override
  public void accept(Boolean granted) {
    if (!granted) {
      ToastUtils.showShort("发送语音功能需要赋予录音和存储权限");
    }
  }
});
 }
 return true;
      }
    });
  }
 
  private static final int MSG_AUDIO_PREPARED = 0x110;
  private static final int MSG_VOICE_CHANGED = 0x111;
  private static final int MSG_DIALOG_DISMISS = 0x112;
  private static final int MSG_TIME_OUT = 0x113;
  private static final int UPDATE_TIME = 0x114;
 
  private boolean mThreadFlag = false;
  //录音时长
  private float mTime;
 
  //获取音量大小的Runnable
  private Runnable mGetVoiceLevelRunnable = new Runnable() {
    @Override
    public void run() {
      while (isRecording) {
 try {
   Thread.sleep(100);
   mTime += 0.1f;
   mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);
   if (mTime >= AUDIO_RECORDER_MAX_TIME) {//如果时间超过60秒,自动结束录音
     while (!mThreadFlag) {//记录已经结束了录音,不需要再次结束,以免出现问题
mDialogManager.dismissDialog();
mAudioManager.release();
if (audioFinishRecorderListener != null) {
  //先回调,再Reset,不然回调中的时间是0
  audioFinishRecorderListener.onFinish(mTime, mAudioManager.getCurrentFilePath());
  mHandler.sendEmptyMessage(MSG_TIME_OUT);
}
mThreadFlag = !mThreadFlag;
     }
     isRecording = false;
   } else if (mTime >= AUDIO_RECORDER_COUNT_DOWN) {
     mHandler.sendEmptyMessage(UPDATE_TIME);
   }
 } catch (InterruptedException e) {
   e.printStackTrace();
 }
      }
    }
  };
 
  private Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
      switch (msg.what) {
 case MSG_AUDIO_PREPARED:
   mDialogManager.showRecordingDialog();
   isRecording = true;
   new Thread(mGetVoiceLevelRunnable).start();
   break;
 case MSG_VOICE_CHANGED:
   mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7));
   break;
 case MSG_DIALOG_DISMISS:
   mDialogManager.dismissDialog();
   break;
 case MSG_TIME_OUT:
   reset();
   break;
 case UPDATE_TIME:
   int countDown = (int) (AUDIO_RECORDER_MAX_TIME - mTime);
   mDialogManager.updateTime(countDown);
   break;
      }
      return true;
    }
  });
 
  
  public interface AudioFinishRecorderListener {
    
    void onFinish(float seconds, String filePath);
  }
 
  private AudioFinishRecorderListener audioFinishRecorderListener;
 
  public void setAudioFinishRecorderListener(AudioFinishRecorderListener listener) {
    audioFinishRecorderListener = listener;
  }
 
  android.media.AudioManager.onAudioFocusChangeListener onAudioFocusChangeListener = new android.media.AudioManager.onAudioFocusChangeListener() {
    @Override
    public void onAudioFocusChange(int focusChange) {
      if (focusChange == android.media.AudioManager.AUDIOFOCUS_LOSS) {
 audioManager.abandonAudioFocus(onAudioFocusChangeListener);
      }
    }
  };
 
  public void myRequestAudioFocus() {
    audioManager.requestAudioFocus(onAudioFocusChangeListener, android.media.AudioManager.STREAM_MUSIC, android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
  }
 
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    Logger.t("AudioManager").i("x :" + event.getX() + "-Y:" + event.getY());
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
 mThreadFlag = false;
 isRecording = true;
 changeState(STATE_RECORDING);
 myRequestAudioFocus();
 break;
      case MotionEvent.ACTION_MOVE:
 if (isRecording) {
   //根据想x,y的坐标,判断是否想要取消
   if (event.getY() < 0 && Math.abs(event.getY()) > DISTANCE_Y_CANCEL) {
     changeState(STATE_WANT_TO_CANCEL);
   } else {
     changeState(STATE_RECORDING);
   }
 }
 break;
      case MotionEvent.ACTION_UP:
 //如果longClick 没触发
 if (!mReady) {
   reset();
   return super.onTouchEvent(event);
 }
 //触发了onLongClick 没准备好,但是已经prepared已经start
 //所以消除文件夹
 if (!isRecording || mTime < 1.0f) {
   mDialogManager.tooShort();
   mAudioManager.cancel();
   mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DISMISS, 1000);
 } else if (mCurrentState == STATE_RECORDING) {//正常录制结束
   mDialogManager.dismissDialog();
   mAudioManager.release();
   if (audioFinishRecorderListener != null) {
     audioFinishRecorderListener.onFinish(mTime, mAudioManager.getCurrentFilePath());
   }
 } else if (mCurrentState == STATE_WANT_TO_CANCEL) {
   mDialogManager.dismissDialog();
   mAudioManager.cancel();
 }
 reset();
 audioManager.abandonAudioFocus(onAudioFocusChangeListener);
 break;
    }
    return super.onTouchEvent(event);
  }
 
  
  private void reset() {
    isRecording = false;
    mTime = 0;
    mReady = false;
    changeState(STATE_NORMAL);
  }
 
  
  private void changeState(int state) {
    if (mCurrentState != state) {
      mCurrentState = state;
      switch (state) {
 case STATE_NORMAL:
   setText(R.string.audio_record_button_normal);
   break;
 case STATE_RECORDING:
   if (isRecording) {
     mDialogManager.recording();
   }
   setText(R.string.audio_record_button_recording);
   break;
 case STATE_WANT_TO_CANCEL:
   mDialogManager.wantToCancel();
   setText(R.string.audio_record_button_cancel);
   break;
      }
    }
  }
}

4、DialogStyle



  
  true
  @style/ActivityAnimTheme
  @style/MenuStyle

 


  @android:color/transparent
  
  true
  
  @null
  
  true
  
  true
  
  @style/ActivityDialogAnimation

 


  
  false

 


  @anim/fade_in
  @anim/fade_out

5、DialogLayout



 
  
 
    
 
    
 
  
 
  

6、用到的字符串


按住 说话
松开 结束
松开手指 取消发送
手指上划,取消发送
松开手指,取消发送
录音时间过短

7、使用:按钮的样式不需要写在自定义Button中,方便使用


 
 AudioRecorderButton audioRecorderButton = findViewById(R.id.btn_audio_recorder);
 audioRecorderButton.setAudioFinishRecorderListener(new AudioRecorderButton.AudioFinishRecorderListener() {
   @Override
   public void onFinish(float seconds, String filePath) {
     Logger.i(seconds + "秒:" + filePath);
   }
 });

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/154679.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号