有段时间没有看视频了,昨天晚上抽了点空时间,又看了下鸿洋大神的视频教程,又抽时间写了个学习记录。代码和老师讲的基本一样,网上也有很多相同的博客。我只是在AndroidStudio环境下写的。

—-主界面代码——

    public class MainActivity extends Activity {private ListView mListView;private ArrayAdapter<Recorder> mAdapter;private List<Recorder> mDatas = new ArrayList<Recorder>();private AudioRecorderButton mAudioRecorderButton;private View animView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mListView = (ListView) findViewById(R.id.id_listview);mAudioRecorderButton = (AudioRecorderButton) findViewById(R.id.id_recorder_button);mAudioRecorderButton.setFinishRecorderCallBack(new AudioRecorderButton.AudioFinishRecorderCallBack() {public void onFinish(float seconds, String filePath) {Recorder recorder = new Recorder(seconds, filePath);mDatas.add(recorder);//更新数据mAdapter.notifyDataSetChanged();//设置位置mListView.setSelection(mDatas.size() - 1);}});mAdapter = new RecoderAdapter(this, mDatas);mListView.setAdapter(mAdapter);//listView的item点击事件mListView.setOnItemClickListener(new OnItemClickListener() {public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {// 声音播放动画if (animView != null) {animView.setBackgroundResource(R.drawable.adj);animView = null;}animView = view.findViewById(R.id.id_recoder_anim);animView.setBackgroundResource(R.drawable.play_anim);AnimationDrawable animation = (AnimationDrawable) animView.getBackground();animation.start();// 播放录音MediaPlayerManager.playSound(mDatas.get(position).filePath, new MediaPlayer.OnCompletionListener() {public void onCompletion(MediaPlayer mp) {//播放完成后修改图片animView.setBackgroundResource(R.drawable.adj);}});}});}@Overrideprotected void onPause() {super.onPause();MediaPlayerManager.pause();}@Overrideprotected void onResume() {super.onResume();MediaPlayerManager.resume();}@Overrideprotected void onDestroy() {super.onDestroy();MediaPlayerManager.release();}

—自定义Button——-

/*** @param* @author ldm* @description 自定义Button* @time 2016/6/25 9:26*/
public class AudioRecorderButton extends Button {// 按钮正常状态(默认状态)private static final int STATE_NORMAL = 1;//正在录音状态private static final int STATE_RECORDING = 2;//录音取消状态private static final int STATE_CANCEL = 3;//记录当前状态private int mCurrentState = STATE_NORMAL;//是否开始录音标志private boolean isRecording = false;//判断在Button上滑动距离,以判断 是否取消private static final int DISTANCE_Y_CANCEL = 50;//对话框管理工具类private DialogManager mDialogManager;//录音管理工具类private AudioManager mAudioManager;//记录录音时间private float mTime;// 是否触发longClickprivate boolean mReady;//录音准备private static final int MSG_AUDIO_PREPARED = 0x110;//音量发生改变private static final int MSG_VOICE_CHANGED = 0x111;//取消提示对话框private static final int MSG_DIALOG_DIMISS = 0x112;/*** @description 获取音量大小的线程* @author ldm* @time 2016/6/25 9:30* @param*/private Runnable mGetVoiceLevelRunnable = new Runnable() {public void run() {while (isRecording) {//判断正在录音try {Thread.sleep(100);mTime += 0.1f;//录音时间计算mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);//每0.1秒发送消息} catch (InterruptedException e) {e.printStackTrace();}}}};private Handler mHandler = new Handler() {@Overridepublic 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;}super.handleMessage(msg);}};public AudioRecorderButton(Context context, AttributeSet attrs) {super(context, attrs);mDialogManager = new DialogManager(context);//录音文件存放地址String dir = Environment.getExternalStorageDirectory() + "/ldm_voice";mAudioManager = AudioManager.getInstance(dir);mAudioManager.setOnAudioStateListener(new AudioManager.AudioStateListener() {public void wellPrepared() {mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);}});// 由于这个类是button所以在构造方法中添加监听事件setOnLongClickListener(new OnLongClickListener() {public boolean onLongClick(View v) {mReady = true;mAudioManager.prepareAudio();return false;}});}public AudioRecorderButton(Context context) {this(context, null);}/*** @description 录音完成后的回调* @author ldm* @time  2016/6/25 11:18* @param*/public interface AudioFinishRecorderCallBack {void onFinish(float seconds, String filePath);}private AudioFinishRecorderCallBack finishRecorderCallBack;public void setFinishRecorderCallBack(AudioFinishRecorderCallBack listener) {finishRecorderCallBack = listener;}/*** @param* @description 处理Button的OnTouchEvent事件* @author ldm* @time 2016/6/25 9:35*/@Overridepublic boolean onTouchEvent(MotionEvent event) {//获取TouchEvent状态int action = event.getAction();// 获得x轴坐标int x = (int) event.getX();// 获得y轴坐标int y = (int) event.getY();switch (action) {case MotionEvent.ACTION_DOWN://手指按下changeState(STATE_RECORDING);break;case MotionEvent.ACTION_MOVE://手指移动if (isRecording) {//根据x,y的坐标判断是否需要取消if (wantToCancle(x, y)) {changeState(STATE_CANCEL);} else {changeState(STATE_RECORDING);}}break;case MotionEvent.ACTION_UP://手指放开if (!mReady) {reset();return super.onTouchEvent(event);}if (!isRecording || mTime < 0.6f) {//如果时间少于0.6s,则提示录音过短mDialogManager.tooShort();mAudioManager.cancel();// 延迟显示对话框mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1000);} else if (mCurrentState == STATE_RECORDING) {//如果状态为正在录音,则结束录制mDialogManager.dimissDialog();mAudioManager.release();if (finishRecorderCallBack != null) {finishRecorderCallBack.onFinish(mTime, mAudioManager.getCurrentFilePath());}} else if (mCurrentState == STATE_CANCEL) { // 想要取消mDialogManager.dimissDialog();mAudioManager.cancel();}reset();break;}return super.onTouchEvent(event);}/*** 恢复状态及标志位*/private void reset() {isRecording = false;mTime = 0;mReady = false;changeState(STATE_NORMAL);}private boolean wantToCancle(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;}/*** @param* @description 根据状态改变Button显示* @author ldm* @time 2016/6/25 9:36*/private void changeState(int state) {if (mCurrentState != state) {mCurrentState = state;switch (state) {case STATE_NORMAL:setBackgroundResource(R.drawable.btn_recorder_normal);setText(R.string.str_recorder_normal);break;case STATE_RECORDING:setBackgroundResource(R.drawable.btn_recorder_recording);setText(R.string.str_recorder_recording);if (isRecording) {mDialogManager.recording();}break;case STATE_CANCEL:setBackgroundResource(R.drawable.btn_recorder_recording);mDialogManager.wantToCancel();setText(R.string.str_recorder_want_cancel);break;}}}
}

—-对话框管理工具类——

/*** @description 对话框管理工具类* @author ldm* @time  2016/6/25 11:53* @param */
public class DialogManager {//弹出对话框private Dialog mDialog;//录音图标private ImageView mIcon;//音量显示 图标private ImageView mVoice;//对话框上提示文字private TextView mLable;//上下文对象private Context mContext;public DialogManager(Context context) {this.mContext = context;}/*** @param* @description 显示对话框* @author ldm* @time 2016/6/25 9:56*/public void showRecordingDialog() {//根据指定sytle实例化DialogmDialog = new Dialog(mContext, R.style.AudioDialog);LayoutInflater inflater = LayoutInflater.from(mContext);View view = inflater.inflate(R.layout.dialog_recorder, null);mDialog.setContentView(view);mIcon = (ImageView) view.findViewById(R.id.id_recorder_dialog_icon);mVoice = (ImageView) view.findViewById(R.id.id_recorder_dialog_voice);mLable = (TextView) view.findViewById(R.id.id_recorder_dialog_label);mDialog.show();}/*** @param* @description 正在录音状态的对话框* @author ldm* @time 2016/6/25 10:08*/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("手指上滑,取消发送");}}/*** @param* @description 取消录音状态对话框* @author ldm* @time 2016/6/25 10:08*/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("松开手指,取消发送");}}/*** @param* @description时间过短提示的对话框* @author ldm* @time 2016/6/25 10:09*/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("录音时间过短");}}/*** @param* @description* @author ldm* @time 2016/6/25 取消(关闭)对话框*/public void dimissDialog() {if (mDialog != null && mDialog.isShowing()) { //显示状态mDialog.dismiss();mDialog = null;}}// 显示更新音量级别的对话框public void updateVoiceLevel(int level) {if (mDialog != null && mDialog.isShowing()) { //显示状态mIcon.setVisibility(View.VISIBLE);mVoice.setVisibility(View.VISIBLE);mLable.setVisibility(View.VISIBLE);//设置图片的id,我们放在drawable中的声音图片是以v+数字格式的int resId = mContext.getResources().getIdentifier("v" + level, "drawable", mContext.getPackageName());mVoice.setImageResource(resId);}}}

—-声音播放工具类——

/*** @param* @author ldm* @description 播放声音工具类* @time 2016/6/25 11:29*/
public class MediaPlayerManager {//播放音频API类:MediaPlayerprivate static MediaPlayer mMediaPlayer;//是否暂停private static boolean isPause;/*** @param* filePath:文件路径* onCompletionListener:播放完成监听* @description 播放声音* @author ldm* @time 2016/6/25 11:30*/public static void playSound(String filePath, MediaPlayer.OnCompletionListener onCompletionListener) {if (mMediaPlayer == null) {mMediaPlayer = new MediaPlayer();//设置一个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) {}}/*** @param* @description 暂停播放* @author ldm* @time 2016/6/25 11:31*/public static void pause() {if (mMediaPlayer != null && mMediaPlayer.isPlaying()) { //正在播放的时候mMediaPlayer.pause();isPause = true;}}/*** @param* @description 重新播放* @author ldm* @time 2016/6/25 11:31*/public static void resume() {if (mMediaPlayer != null && isPause) {mMediaPlayer.start();isPause = false;}}/*** @param* @description 释放操作* @author ldm* @time 2016/6/25 11:32*/public static void release() {if (mMediaPlayer != null) {mMediaPlayer.release();mMediaPlayer = null;}}

—–录音操作工具类—–

/*** @param* @author ldm* @description 录音管理工具类* @time 2016/6/25 9:39*/
public class AudioManager {//AudioRecord: 主要是实现边录边播(AudioRecord+AudioTrack)以及对音频的实时处理。// 优点:可以语音实时处理,可以实现各种音频的封装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;}/*** @param* @author ldm* @description 录音准备工作完成回调接口* @time 2016/6/25 11:14*/public interface AudioStateListener {void wellPrepared();}public AudioStateListener mAudioStateListener;/*** @param* @description 供外部类调用的设置回调方法* @author ldm* @time 2016/6/25 11:14*/public void setOnAudioStateListener(AudioStateListener listener) {mAudioStateListener = listener;}/*** @param* @description 录音准备工作* @author ldm* @time 2016/6/25 11:15*/public void prepareAudio() {try {isPrepare = false;File dir = new File(mDir);if (!dir.exists()) {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);// 设置音频格式为RAW_AMRmMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);// 设置音频编码为AMR_NBmMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);// 准备录音mMediaRecorder.prepare();// 开始,必需在prepare()后调用mMediaRecorder.start();// 准备完成isPrepare = true;if (mAudioStateListener != null) {mAudioStateListener.wellPrepared();}} catch (Exception e) {e.printStackTrace();}}/*** @param* @description 随机生成录音文件名称* @author ldm* @time 2016/6/25 、*/private String generateFileName() {//随机生成不同的UUIDreturn UUID.randomUUID().toString() + ".amr";}/*** @param* @description 获取音量值* @author ldm* @time 2016/6/25 9:49*/public int getVoiceLevel(int maxlevel) {if (isPrepare) {try {// getMaxAmplitude返回的数值最大是32767return maxlevel * mMediaRecorder.getMaxAmplitude() / 32768 + 1;//返回结果1-7之间} catch (Exception e) {e.printStackTrace();}}return 1;}/*** @param* @description 释放资源* @author ldm* @time 2016/6/25 9:50*/public void release() {mMediaRecorder.stop();mMediaRecorder.reset();mMediaRecorder = null;}/*** @param* @description 录音取消* @author ldm* @time 2016/6/25 9:51*/public void cancel() {release();if (mCurrentFilePath != null) {//取消录音后删除对应文件File file = new File(mCurrentFilePath);file.delete();mCurrentFilePath = null;}}/*** @param* @description 获取当前文件路径* @author ldm* @time 2016/6/25 9:51*/public String getCurrentFilePath() {return mCurrentFilePath;}
}
代码中有注释,就不贴图了,和微信语音聊天界面一样的,所以叫仿微信 嘛,呵呵。运行了也可以看到效果。所有代码可以从这里下载:
http://download.csdn.net/detail/true100/9559210

Android仿微信语音聊天界面相关推荐

  1. Android仿微信语音聊天界面设计

    这篇文章主要为大家详细介绍了Android仿微信语音聊天界面设计代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 有段时间没有看视频了,昨天晚上抽了点空时间,又看了下鸿洋大神的视频教程,又抽时间 ...

  2. Android仿微信气泡聊天界面设计

    Android仿微信气泡聊天界面设计 微信的气泡聊天是仿iPhone自带短信而设计出来的,不过感觉还不错可以尝试一下仿着微信的气泡聊天做一个Demo,给大家分享一下!效果图如下: 气泡聊天最终要的是素 ...

  3. android 仿微信语音聊天

    android 仿微信语音聊天 跟着imooc老师学习 代码地址: https://github.com/tingsky9985/Weixin_Recorder

  4. android仿微信语音聊天功能,Android仿微信发送语音消息的功能及示例代码

    微信的发送语音是有一个向上取消的,我们使用ontouchlistener来监听手势,然后做出相应的操作就行了. 直接上代码: //语音操作对象 private mediaplayer mplayer ...

  5. Android 仿微信语音聊天音量大小显示控件

    某日用微信语音功能聊天,发现当我使用语音功能时,会弹出一个窗口,窗口中间有一个控件会实时的显示我说话声音的大小(即分贝).当时觉得挺好玩,决定也仿制一个,效果如下 分析控件显示效果,可判断左边是一个i ...

  6. Android仿微信语音聊天

    1.项目界面展示: 2.项目代码整体结构简析: (1)AudioRecorderButton类(录音按钮):State:STATE_NORMAL[正常状态].STATE_RECORDERING[正在录 ...

  7. 转-Android仿微信气泡聊天界面设计

    微信的气泡聊天是仿iPhone自带短信而设计出来的,不过感觉还不错可以尝试一下仿着微信的气泡聊天做一个Demo,给大家分享一下!效果图如下: 气泡聊天最终要的是素材,要用到9.png文件的素材,这样气 ...

  8. Android仿微信语音聊天demo

    其实我接触android时间也不是很久,但是发现android远远比我们想象的要有趣并且复杂很多,所以还是要多花点时间来写一写这些demo例子,这个程序是我从慕课网上学来的,因为毕竟要自己手写,才能体 ...

  9. Android 仿微信语音聊天,正式加入字节跳动

    /** 恢复标志位 */ private void resetState() { isRecording = false; isReady = false; changeState(STATE_NOR ...

  10. Android 仿微信语音聊天,flutter项目结构

    4.录音类里有两个成员:录音长度,录音路径. 下面贴一下代码: 自定义Button package com.zms.wechatrecorder.view; import com.zms.wechat ...

最新文章

  1. 【Linux】一步一步学Linux——pmap命令(145)
  2. 文本数据可视化_如何使用TextHero快速预处理和可视化文本数据
  3. 编辑器推荐:Visual Studio Code(VSCode/VSC)
  4. 大学计算机应用基础考试题库,大学计算机应用基础考试题库
  5. php+使用go编译,golang如何编译
  6. 长生不死、名人复活?疯狂的AI时代,人类竟要靠IA实现“永生”
  7. java并发编程LockSupport讲解
  8. Facebook 开源首个全卷积语音识别工具包 wav2letter++
  9. 计算机专业盲打,在win7系统电脑练习盲打的方法
  10. Jquery中$(document).ready(function(){ })函数
  11. 三星android+l,高配原生安卓!移动定制版三星I9008L评测
  12. 一个比Profiler和Netch更好用的软件代理加速工具
  13. jsp内置对象request
  14. EasyUI 中combobox利用拼音进行检索
  15. HttpClient Java 常用写法总结
  16. 网页加速器1.0.5.6 免费版
  17. 最稳定的工作,是你认可的那份
  18. 本地项目及依赖上传私服nexus
  19. 人体三维重建SMPL、SMPLX、SMPLifyX学习笔记
  20. 世界读书日丨程序员经典语录值得读一读

热门文章

  1. 浅谈工业互联网与产业互联网区别
  2. 神农班2019年总结
  3. 新安装Visio2013每次打开都提示正在配置,解决办法
  4. 苹果手机(IOS)蓝牙相关知识【配对后蓝牙设置界面i标识】【连接参数的限制】[只以地址作为识别依据]
  5. ROS入门(十)——两只小乌龟(乌龟跟随C++实现)
  6. 数学建模学习笔记——预测类型1
  7. 郑捷《机器学习算法原理与编程实践》学习笔记(第三章 决策树的发展)(一 )_ID3...
  8. 玩转树莓派二、树莓派配置工具 raspi-config 使用指南
  9. 无缝衔接的人会遭报应吗_怎么看待分手后无缝衔接的人?
  10. 视频物体检测(VID) FGFA:Flow-Guided Feature Aggregation for Video Object Detection