在项目开发中,有个需求:实现模仿微信录音,发送语音的功能。长按按钮录音,弹框显示语音时间,以及上滑取消发送。我重写了一个发送语音的控件,以实现该功能。

首先添加权限:

AudioRecorderButton,自己实现的自定义录音控件

public class AudioRecorderButton extends Button {

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;// 已经开始录音

private static final int DISTANCE_Y_CANCEL = 50;

private DialogManager mDialogManager;

private AudioManager mAudioManager;

// 是否触发longClick

private boolean mReady;

android.media.AudioManager audioManager;

private static final int MSG_AUDIO_PREPARED = 0x110;

private static final int MSG_VOICE_CHANGED = 0x111;

private static final int MSG_DIALOG_DIMISS = 0x112;

private static final int MSG_TIME_OUT = 0x113;

private static final int UPDATE_TIME = 0x114;

private boolean mThreadFlag = false;

private int time = 0;

private float mTime;

/*

* 获取音量大小的线程

*/

private Runnable mGetVoiceLevelRunnable = new Runnable() {

public void run() {

while (isRecording) {

try {

Thread.sleep(100);

mTime += 0.1f;

time++;

if (isWantToCancel) {

} else {

if (time % 10 == 0) {

mHandler.sendEmptyMessage(UPDATE_TIME);

}

}

mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);

if (mTime >= 10.0f) {//如果时间超过10秒,自动结束录音

while (!mThreadFlag) {//记录已经结束了录音,不需要再次结束,以免出现问题

mDialogManager.dimissDialog();

mAudioManager.release();

if (audioFinishRecorderListener != null) {

//发消息给主线程,告诉他reset();

mHandler.sendEmptyMessage(MSG_TIME_OUT);

audioFinishRecorderListener.onFinish(mTime, mAudioManager.getCurrentFilePath());

}

mThreadFlag = !mThreadFlag;

}

isRecording = false;

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

};

private Handler mHandler = new Handler() {

@Override

public void 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_DIMISS:

mDialogManager.dimissDialog();

break;

case MSG_TIME_OUT://录音超时

reset();

break;

case UPDATE_TIME://更新时间

if (time % 10 == 0) {

mDialogManager.updateTime(time / 10);

}

break;

}

}

};

/**

* 以下2个方法是构造方法

*/

public AudioRecorderButton(Context context, AttributeSet attrs) {

super(context, attrs);

mDialogManager = new DialogManager(context);

audioManager = (android.media.AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

String dir = Environment.getExternalStorageDirectory().getPath() + "/" + LibraryContantsUtil.APP_NAME + "/audio";

mAudioManager = AudioManager.getInstance(dir);

mAudioManager.setOnAudioStateListener(new AudioManager.AudioStateListener() {

public void wellPrepared() {

if (!isOverDue) {

mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);//开启线程

}

}

});

// 由于这个类是button所以在构造方法中添加监听事件

setOnLongClickListener(new OnLongClickListener() {

public boolean onLongClick(View v) {

if (!isOverDue) {

mReady = true;

mAudioManager.prepareAudio();

}

return true;

}

});

}

public AudioRecorderButton(Context context) {

this(context, null);

}

/**

* 录音完成后的回调

*/

public interface AudioFinishRecorderListener {

void onFinish(float seconds, String filePath);

}

private AudioFinishRecorderListener audioFinishRecorderListener;

public void setAudioFinishRecorderListener(AudioFinishRecorderListener listener) {

audioFinishRecorderListener = listener;

}

android.media.AudioManager.OnAudioFocusChangeListener afChangeListener = new android.media.AudioManager.OnAudioFocusChangeListener() {

public void onAudioFocusChange(int focusChange) {

if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT) {

// Pause playback

} else if (focusChange == android.media.AudioManager.AUDIOFOCUS_GAIN) {

// Resume playback

LogUtil.e("===", "+++");

} else if (focusChange == android.media.AudioManager.AUDIOFOCUS_LOSS) {

audioManager.abandonAudioFocus(afChangeListener);

// Stop playback

}

}

};

public void myRequestAudioFocus() {

audioManager.requestAudioFocus(afChangeListener, android.media.AudioManager.STREAM_MUSIC, android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);

}

/**

* 屏幕的触摸事件

*/

@Override

public boolean onTouchEvent(MotionEvent event) {

// int x = (int) event.getX();// 获得x轴坐标

float last = 0;// 获得y轴坐标

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

mThreadFlag = false;

changeState(STATE_RECORDING);

myRequestAudioFocus();

break;

case MotionEvent.ACTION_MOVE:

if (isRecording) {

// 如果想要取消,根据x,y的坐标看是否需要取消

if (event.getY() < 0 && Math.abs(event.getY()) > 120) {

changeState(STATE_WANT_TO_CANCEL);

} else {

changeState(STATE_RECORDING);

}

}

break;

case MotionEvent.ACTION_UP:

if (!mReady) {

reset();

return super.onTouchEvent(event);

}

if (!isRecording || mTime <= 1.0f) {//小于1秒

mDialogManager.tooShort();

mAudioManager.cancel();

mHandler.sendEmptyMessage(MSG_DIALOG_DIMISS);//显示对话框

} else if (mCurrentState == STATE_RECORDING) { // 正在录音的时候,结束

mDialogManager.dimissDialog();

mAudioManager.release();

if (audioFinishRecorderListener != null) {

audioFinishRecorderListener.onFinish(mTime, mAudioManager.getCurrentFilePath());

}

} else if (mCurrentState == STATE_WANT_TO_CANCEL) { // 想要取消

mDialogManager.dimissDialog();

mAudioManager.cancel();

}

reset();

audioManager.abandonAudioFocus(afChangeListener);

}

break;

return super.onTouchEvent(event);

}

/**

* 恢复状态及标志位

*/

private void reset() {

isRecording = false;

mTime = 0;

time = 1;

mReady = false;

changeState(STATE_NORMAL);

}

private boolean wantToCancle(int x, int y) {

if (y < 0) {

return true;

} else {

return false;

}

}

boolean isWantToCancel = false;

/**

* 改变

*/

private void changeState(int state) {

if (mCurrentState != state) {

mCurrentState = state;

switch (state) {

case STATE_NORMAL:

setBackgroundResource(R.drawable.shape_voice_chat_circle);

setText("按住说话");

break;

case STATE_RECORDING:

if (isRecording) {

mDialogManager.recording();

}

setBackgroundResource(R.drawable.shape_voice_chat_circle);

setText("松开结束");

isWantToCancel = false;

break;

case STATE_WANT_TO_CANCEL:

setBackgroundResource(R.drawable.shape_voice_chat_circle);

setText("松开手指,取消发送");

mDialogManager.wantToCancel();

isWantToCancel = true;

break;

}

}

}

}

下面是DialogManager,录音时控制Dialog的显示隐藏,以及需要Dialog中需要向用户展示的内容。

public class DialogManager {

private AlertDialog.Builder builder;

private ImageView mIcon;

// private ImageView mVoice;

private TextView mLable;

private Context context;

private AlertDialog dialog;//用于取消AlertDialog.Builder

/**

* 构造方法 传入上下文

*/

public DialogManager(Context context) {

this.context = context;

}

// 显示录音的对话框

public void showRecordingDialog() {

builder = new AlertDialog.Builder(context, R.style.voice_chat_dialog);

LayoutInflater inflater = LayoutInflater.from(context);

View view = inflater.inflate(R.layout.dialog_recorder, null);

mIcon = (ImageView) view.findViewById(R.id.id_recorder_dialog_icon);

mLable = (TextView) view.findViewById(R.id.id_recorder_dialog_label);

// GlideUtil.loadSquarePicture(R.drawable.gif_record, mIcon);

builder.setView(view);

builder.create();

dialog = builder.show();

dialog.setCanceledOnTouchOutside(false);//设置点击外部不消失

}

public void recording() {

if (dialog != null && dialog.isShowing()) { //显示状态

mIcon.setVisibility(View.VISIBLE);

mLable.setVisibility(View.VISIBLE);

// GlideUtil.loadSquarePicture(R.drawable.gif_record, mIcon);

mIcon.setImageResource(R.mipmap.ic_record);

mLable.setText("手指上滑,取消发送");

}

}

// 显示想取消的对话框

public void wantToCancel() {

if (dialog != null && dialog.isShowing()) { //显示状态

mIcon.setVisibility(View.VISIBLE);

mLable.setVisibility(View.VISIBLE);

mIcon.setImageResource(R.mipmap.ic_release_to_cancel);

mLable.setText("松开手指,取消发送");

}

}

public void updateTime(int time) {

if (dialog != null && dialog.isShowing()) { //显示状态

mIcon.setVisibility(View.VISIBLE);

mLable.setVisibility(View.VISIBLE);

mLable.setText(time + "s");

}

}

// 显示时间过短的对话框

public void tooShort() {

if (dialog != null && dialog.isShowing()) { //显示状态

mIcon.setVisibility(View.VISIBLE);

mLable.setVisibility(View.VISIBLE);

mLable.setText("录音时间过短");

}

}

// 显示取消的对话框

public void dimissDialog() {

if (dialog != null && dialog.isShowing()) { //显示状态

dialog.dismiss();

dialog = null;

}

}

// 显示更新音量级别的对话框

public void updateVoiceLevel(int level) {

if (dialog != null && dialog.isShowing()) { //显示状态

mIcon.setVisibility(View.VISIBLE);

mLable.setVisibility(View.VISIBLE);

// 在这里没有做操作

//可以再这里根据音量大小,设置图片,实现效果;

}

}

}

下面是AudioManager,录音类

public class AudioManager {

private MediaRecorder mMediaRecorder;

private String mDir;

private String mCurrentFilePath;

private static AudioManager mInstance;

private boolean isPrepare;

private AudioManager(String dir) {

mDir = dir;

}

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 mAudioStateListener;

/**

* 回调方法

*/

public void setOnAudioStateListener(AudioStateListener listener) {

mAudioStateListener = listener;

}

// 去准备

public void prepareAudio() {

try {

isPrepare = false;

File dir = new File(mDir);

if (!dir.exists()) {

dir.mkdirs();

} else {

if (!dir.isDirectory()) {

dir.delete();

dir.mkdirs();

}

}

String fileName = generateFileName();

File file = new File(dir, fileName);

mCurrentFilePath = file.getAbsolutePath();

mMediaRecorder = new MediaRecorder();

// 设置输出文件

mMediaRecorder.setOutputFile(file.getAbsolutePath());

// 设置MediaRecorder的音频源为麦克风

mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);

// 设置音频格式

mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);

// 设置音频编码

mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

// 准备录音

mMediaRecorder.prepare();

// 开始

mMediaRecorder.start();

// 准备结束

isPrepare = true;

if (mAudioStateListener != null) {

mAudioStateListener.wellPrepared();

}

} catch (IllegalStateException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

/**

* 随机生成文件的名称

*/

private String generateFileName() {

return UUID.randomUUID().toString() + ".amr";

}

public int getVoiceLevel(int maxlevel) {

if (isPrepare) {

try {

return maxlevel * mMediaRecorder.getMaxAmplitude() / 32768 + 1;

} catch (Exception e) {

}

}

return 1;

}

/**

* 释放资源

*/

public void release() {

if (mMediaRecorder != null) {

mMediaRecorder.reset();

mMediaRecorder = null;

}

}

/**

* 取消录音

*/

public void cancel() {

release();

if (mCurrentFilePath != null) {

File file = new File(mCurrentFilePath);

file.delete();

mCurrentFilePath = null;

}

}

public String getCurrentFilePath() {

return mCurrentFilePath;

}

}

下面是MediaManager,播放,暂停录音

public class MediaManager {

private static MediaPlayer mMediaPlayer;

private static boolean isPause;

private String currentFilePath;

private AudioManager.AudioStateListener onAudioStateListener;

static Context context;

public MediaManager(Context context) {

this.context = context;

}

/**

* 播放音乐

*

* @param filePath

* @param onCompletionListener

*/

public static void playSound(String filePath, MediaPlayer.OnCompletionListener onCompletionListener) {

if (mMediaPlayer == null) {

mMediaPlayer = new MediaPlayer();

// mMediaPlayer = MediaPlayer.create(context, Uri.fromFile(new File(filePath)));

//设置一个error监听器

mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {

public boolean onError(MediaPlayer arg0, int arg1, int arg2) {

mMediaPlayer.reset();

return false;

}

});

} else {

mMediaPlayer.reset();

}

try {

mMediaPlayer.setAudioStreamType(android.media.AudioManager.STREAM_MUSIC);

mMediaPlayer.setOnCompletionListener(onCompletionListener);

mMediaPlayer.setDataSource(filePath);

mMediaPlayer.prepare();

mMediaPlayer.start();

} catch (Exception e) {

}

}

/**

* 暂停播放

*/

public static void pause() {

if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {

//正在播放的时候

mMediaPlayer.pause();

isPause = true;

}

}

public static boolean isPlaying() {

if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {

return true;

} else {

return false;

}

}

/**

* 当前是isPause状态

*/

public static void resume() {

if (mMediaPlayer != null && isPause) {

mMediaPlayer.start();

isPause = false;

}

}

/**

* 释放资源

*/

public static void release() {

if (mMediaPlayer != null) {

mMediaPlayer.release();

mMediaPlayer = null;

}

}

}

至此,其整体的代码已经编写完成了,在需要录音的地方调用:

audioRecorderButton.setAudioFinishRecorderListener(new AudioRecorderButton.AudioFinishRecorderListener() {

@Override

public void onFinish(float seconds, String filePath) {

//seconds 录音时长,filePath 录音文件的位置

//录音完成

upLoadVoice(filePath, (int) seconds);

}

});

Screenshot_2017-07-25-16-24-38-397_com.zijing.xin.png

android按住录音按钮_Android模仿微信录音、发送语音效果实现相关推荐

  1. 李长军android语音开发_Android模仿微信录音、发送语音效果实现

    在项目开发中,有个需求:实现模仿微信录音,发送语音的功能.长按按钮录音,弹框显示语音时间,以及上滑取消发送.我重写了一个发送语音的控件,以实现该功能. 首先添加权限: AudioRecorderBut ...

  2. android按住录音按钮_Android实现录音方法(仿微信语音、麦克风录音、发送语音、解决5.0以上BUG)...

    先给大家展示下效果图,如果大家感觉不错,请参考使用方法, 效果图如下所示: 使用方法: 录音工具类:AudioRecoderUtils.java,代码如下: public class AudioRec ...

  3. android标题栏添加按钮_Android和iOS设计差异点有哪些?

    加微信  "786318553" 进设计群  111 文章来源:Echo的设计笔记由于设计师.产品经理使用的移动设备大部分是iPhone,所以在做设计时,容易忽略Android和i ...

  4. android 仿微信选取相册_Android模仿微信选择图片

    前言 最近公司需要做一个类似微信那种选择头像和上传图片的功能,本想上github上找的,后来想了想,还是自己做一个,不仅方便以后用(毕竟自己写的修改起来也比较方便),还可以学到一些知识,废话少说,先看 ...

  5. android仿微博头像_Android仿微信微博多图展示效果

    1.简介 这是一个用于实现像微信朋友圈和微博的类似的九宫格图片展示控件,通过自定义viewgroup实现,使用方便. 多图根据屏幕适配,单张图片时需要自己指定图片的宽高: 2.使用方法 引用: com ...

  6. Android弹幕功能实现,模仿斗鱼直播的弹幕效果

    转载请注明出处:http://blog.csdn.net/sinyu890807/article/details/51933728 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭 ...

  7. android金币动效_Android 仿余额宝数字跳动动画效果完整代码

    一:想都不用想的,有图有真相,看着爽了,在看下面源码 二:实例源码分析 ①:首先定义接口 package com.demo.tools.view; /** * 数字动画自定义 * * @author ...

  8. JS+HTML+CSS模仿微信界面发送信息

    JS+HTML+CSS仿微信聊天界面 前端新人又来报道咯 欢迎大家指教 功能: 1.点击头像可以模拟切换用户. 2.输入信息,点击发送可以显示在屏幕上. 3.新信息在上方,旧信息在下方,方便有滚动条的 ...

  9. Android弹幕功能实现,模仿斗鱼直播的弹幕效果,跪了

    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_ ...

最新文章

  1. 机器学习数据预处理之缺失值:前向填充
  2. 取给定正整数的指定bit位開始的指定长度的数据
  3. 2、EVE-NG镜像导入(Dynamipshe和IOL)
  4. jupyter代码字体大小_你可能并不知道这样定制炫酷的jupyter主题
  5. how to debug Opportunity change implementation - entry onOKParticipantDialog
  6. python 日期格式校验_python – 如何验证时间格式?
  7. 你都用python来做什么-你都用Python来做什么?看看网友们的各种牛X操作
  8. HTML5 canvas图形库 RGraph【转】
  9. .NET LINQ 限定符操作
  10. ylbtech-LanguageSamples-Libraries(库)
  11. php中文歌词,PHP 爬虫抓取歌词
  12. Python numpy.ones函数方法的使用
  13. 计算机五笔打字口诀,学电脑五笔打字-不用背口诀-5分钟轻松学会五笔打字-快来试试吧!.doc...
  14. 光纤熔接机的光纤对准方式
  15. vue 根据链接生成二维码(功能实现)
  16. 爬取上交所公司信息,根据公司股票代码获取公司注册地址以及地址的经纬度
  17. Power Supply 文件节点和电池服务属性对照
  18. ORA-00392: log 4 of thread 2 is being cleared, operation not allowed
  19. 光伏-储能并网系统仿真(MATLAB/SIMULINK)-part1
  20. strtol函数的使用

热门文章

  1. thinkPHP生成微信支付平台证书
  2. 西南交通大学暑期夏令营面试
  3. 面试题promise原理
  4. win32 015使用菜单和加速键
  5. 新南威尔士大学预科学生宿舍
  6. 帆船指南-原则-船体结构
  7. 银行数字化的两难:安全还是效率?
  8. 【2022年第一期 CANN训练营进阶班应用课】第一次大作业
  9. Linux磁盘分区与LVM详解
  10. ssm毕设项目酒店管理系统08281(java+VUE+Mybatis+Maven+Mysql+sprnig)