当前位置: 代码迷 >> Android >> Android仿照微信语音聊天功能-IT蓝豹
  详细解决方案

Android仿照微信语音聊天功能-IT蓝豹

热度:112   发布时间:2016-04-27 22:44:24.0
Android模仿微信语音聊天功能-IT蓝豹

项目效果如下:

这里写图片描述

这里写图片描述

代码如下:

AudioManager.java

  1. package com.xuliugen.weichat;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.util.UUID;
  5. import android.media.MediaRecorder;
  6. publicclassAudioManager{
  7. privateMediaRecorder mMediaRecorder;
  8. privateString mDir;
  9. privateString mCurrentFilePath;
  10. privatestaticAudioManager mInstance;
  11. privateboolean isPrepare;
  12. privateAudioManager(String dir){
  13. mDir = dir;
  14. }
  15. publicstaticAudioManager getInstance(String dir){
  16. if(mInstance ==null){
  17. synchronized(AudioManager.class){
  18. if(mInstance ==null){
  19. mInstance =newAudioManager(dir);
  20. }
  21. }
  22. }
  23. return mInstance;
  24. }
  25. /** * 使用接口 用于回调 */
  26. publicinterfaceAudioStateListener{
  27. void wellPrepared();
  28. }
  29. publicAudioStateListener mAudioStateListener;
  30. /** * 回调方法 */
  31. publicvoid setOnAudioStateListener(AudioStateListener listener){
  32. mAudioStateListener = listener;
  33. }
  34. // 去准备
  35. publicvoid prepareAudio(){
  36. try{
  37. isPrepare =false;
  38. File dir =newFile(mDir);
  39. if(!dir.exists()){
  40. dir.mkdirs();
  41. }
  42. String fileName = generateFileName();
  43. File file =newFile(dir, fileName);
  44. mCurrentFilePath =file.getAbsolutePath();
  45. mMediaRecorder =newMediaRecorder();
  46. // 设置输出文件
  47. mMediaRecorder.setOutputFile(dir.getAbsolutePath());
  48. // 设置MediaRecorder的音频源为麦克风
  49. mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
  50. // 设置音频格式
  51. mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
  52. // 设置音频编码
  53. mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
  54. // 准备录音
  55. mMediaRecorder.prepare();
  56. // 开始
  57. mMediaRecorder.start();
  58. // 准备结束
  59. isPrepare =true;
  60. if(mAudioStateListener !=null){
  61. mAudioStateListener.wellPrepared();
  62. }
  63. }catch(IllegalStateException e){
  64. e.printStackTrace();
  65. }catch(IOException e){
  66. e.printStackTrace();
  67. }
  68. }
  69. /** * 随机生成文件的名称 */
  70. privateString generateFileName(){
  71. return UUID.randomUUID().toString()+".amr";
  72. }
  73. publicint getVoiceLevel(int maxlevel){
  74. if(isPrepare){
  75. try{
  76. // mMediaRecorder.getMaxAmplitude() 1~32767
  77. return maxlevel * mMediaRecorder.getMaxAmplitude()/32768+1;
  78. }catch(Exception e){
  79. }
  80. }
  81. return1;
  82. }
  83. /** * 释放资源 */
  84. publicvoid release(){
  85. //mMediaRecorder.stop();
  86. mMediaRecorder.reset();
  87. mMediaRecorder =null;
  88. }
  89. /** * 取消录音 */
  90. publicvoid cancel(){
  91. release();
  92. if(mCurrentFilePath !=null){
  93. File file =newFile(mCurrentFilePath);
  94. file.delete();
  95. mCurrentFilePath =null;
  96. }
  97. }
  98. publicString getCurrentFilePath(){
  99. return mCurrentFilePath;
  100. }
  101. }

AudioRecorderButton.java

  1. package com.xuliugen.weichat;
  2. import android.content.Context;
  3. import android.os.Environment;
  4. import android.os.Handler;
  5. import android.os.Message;
  6. import android.util.AttributeSet;
  7. import android.view.MotionEvent;
  8. import android.view.View;
  9. import android.widget.Button;
  10. import com.xuliugen.weichat.R;
  11. import com.xuliugen.weichat.AudioManager.AudioStateListener;
  12. publicclassAudioRecorderButtonextendsButton{
  13. privatestaticfinalint STATE_NORMAL =1;// 默认的状态
  14. privatestaticfinalint STATE_RECORDING =2;// 正在录音
  15. privatestaticfinalint STATE_WANT_TO_CANCEL =3;// 希望取消
  16. privateint mCurrentState = STATE_NORMAL;// 当前的状态
  17. privateboolean isRecording =false;// 已经开始录音
  18. privatestaticfinalint DISTANCE_Y_CANCEL =50;
  19. privateDialogManager mDialogManager;
  20. privateAudioManager mAudioManager;
  21. privatefloat mTime;
  22. // 是否触发longClick
  23. privateboolean mReady;
  24. privatestaticfinalint MSG_AUDIO_PREPARED =0x110;
  25. privatestaticfinalint MSG_VOICE_CHANGED =0x111;
  26. privatestaticfinalint MSG_DIALOG_DIMISS =0x112;
  27. /* * 获取音量大小的线程 */
  28. privateRunnable mGetVoiceLevelRunnable =newRunnable(){
  29. publicvoid run(){
  30. while(isRecording){
  31. try{
  32. Thread.sleep(100);
  33. mTime +=0.1f;
  34. mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);
  35. }catch(InterruptedException e){
  36. e.printStackTrace();
  37. }
  38. }
  39. }
  40. };
  41. privateHandler mHandler =newHandler(){
  42. @Override
  43. publicvoid handleMessage(Message msg){
  44. switch(msg.what){
  45. case MSG_AUDIO_PREPARED:
  46. // 显示對話框在开始录音以后
  47. mDialogManager.showRecordingDialog();
  48. isRecording =true;
  49. // 开启一个线程
  50. newThread(mGetVoiceLevelRunnable).start();
  51. break;
  52. case MSG_VOICE_CHANGED:
  53. mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7));
  54. break;
  55. case MSG_DIALOG_DIMISS:
  56. mDialogManager.dimissDialog();
  57. break;
  58. }
  59. super.handleMessage(msg);
  60. }
  61. };
  62. /** * 以下2个方法是构造方法 */
  63. publicAudioRecorderButton(Context context,AttributeSet attrs){
  64. super(context, attrs);
  65. mDialogManager =newDialogManager(context);
  66. String dir ="/storage/sdcard0/my_weixin";
  67. //String dir = Environment.getExternalStorageDirectory()+"/my_weixin";
  68. mAudioManager =AudioManager.getInstance(dir);
  69. mAudioManager.setOnAudioStateListener(newAudioStateListener(){
  70. publicvoid wellPrepared(){
  71. mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);
  72. }
  73. });
  74. // 由于这个类是button所以在构造方法中添加监听事件
  75. setOnLongClickListener(newOnLongClickListener(){
  76. publicboolean onLongClick(View v){
  77. mReady =true;
  78. mAudioManager.prepareAudio();
  79. returnfalse;
  80. }
  81. });
  82. }
  83. publicAudioRecorderButton(Context context){
  84. this(context,null);
  85. }
  86. /** * 录音完成后的回调 */
  87. publicinterfaceAudioFinishRecorderListener{
  88. void onFinish(float seconds,String filePath);
  89. }
  90. privateAudioFinishRecorderListener audioFinishRecorderListener;
  91. publicvoid setAudioFinishRecorderListener(AudioFinishRecorderListener listener){
  92. audioFinishRecorderListener = listener;
  93. }
  94. /** * 屏幕的触摸事件 */
  95. @Override
  96. publicboolean onTouchEvent(MotionEventevent){
  97. int action =event.getAction();
  98. int x =(int)event.getX();// 获得x轴坐标
  99. int y =(int)event.getY();// 获得y轴坐标
  100. switch(action){
  101. caseMotionEvent.ACTION_DOWN:
  102. changeState(STATE_RECORDING);
  103. break;
  104. caseMotionEvent.ACTION_MOVE:
  105. if(isRecording){
  106. // 如果想要取消,根据x,y的坐标看是否需要取消
  107. if(wantToCancle(x, y)){
  108. changeState(STATE_WANT_TO_CANCEL);
  109. }else{
  110. changeState(STATE_RECORDING);
  111. }
  112. }
  113. break;
  114. caseMotionEvent.ACTION_UP:
  115. if(!mReady){
  116. reset();
  117. returnsuper.onTouchEvent(event);
  118. }
  119. if(!isRecording || mTime <0.6f){
  120. mDialogManager.tooShort();
  121. mAudioManager.cancel();
  122. mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS,1000);// 延迟显示对话框
  123. }elseif(mCurrentState == STATE_RECORDING){// 正在录音的时候,结束
  124. mDialogManager.dimissDialog();
  125. mAudioManager.release();
  126. if(audioFinishRecorderListener !=null){
  127. audioFinishRecorderListener.onFinish(mTime,mAudioManager.getCurrentFilePath());
  128. }
  129. }elseif(mCurrentState == STATE_WANT_TO_CANCEL){// 想要取消
  130. mDialogManager.dimissDialog();
  131. mAudioManager.cancel();
  132. }
  133. reset();
  134. break;
  135. }
  136. returnsuper.onTouchEvent(event);
  137. }
  138. /** * 恢复状态及标志位 */
  139. privatevoid reset(){
  140. isRecording =false;
  141. mTime =0;
  142. mReady =false;
  143. changeState(STATE_NORMAL);
  144. }
  145. privateboolean wantToCancle(int x,int y){
  146. if(x <0|| x > getWidth()){// 超过按钮的宽度
  147. returntrue;
  148. }
  149. // 超过按钮的高度
  150. if(y <-DISTANCE_Y_CANCEL || y > getHeight()+ DISTANCE_Y_CANCEL){
  151. returntrue;
  152. }
  153. returnfalse;
  154. }
  155. /** * 改变 */
  156. privatevoid changeState(int state){
  157. if(mCurrentState != state){
  158. mCurrentState = state;
  159. switch(state){
  160. case STATE_NORMAL:
  161. setBackgroundResource(R.drawable.btn_recorder_normal);
  162. setText(R.string.str_recorder_normal);
  163. break;
  164. case STATE_RECORDING:
  165. setBackgroundResource(R.drawable.btn_recorder_recording);
  166. setText(R.string.str_recorder_recording);
  167. if(isRecording){
  168. mDialogManager.recording();
  169. }
  170. break;
  171. case STATE_WANT_TO_CANCEL:
  172. setBackgroundResource(R.drawable.btn_recorder_recording);
  173. setText(R.string.str_recorder_want_cancel);
  174. mDialogManager.wantToCancel();
  175. break;
  176. }
  177. }
  178. }
  179. }

DialogManager.java

  1. package com.xuliugen.weichat;
  2. import android.app.AlertDialog;
  3. import android.content.Context;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.widget.ImageView;
  7. import android.widget.TextView;
  8. import com.xuliugen.weichat.R;
  9. /** * 用于管理Dialog * * @author xuliugen * */
  10. publicclassDialogManager{
  11. privateAlertDialog.Builder builder;
  12. privateImageView mIcon;
  13. privateImageView mVoice;
  14. privateTextView mLable;
  15. privateContext mContext;
  16. privateAlertDialog dialog;//用于取消AlertDialog.Builder
  17. /** * 构造方法 传入上下文 */
  18. publicDialogManager(Context context){
  19. this.mContext = context;
  20. }
  21. // 显示录音的对话框
  22. publicvoid showRecordingDialog(){
  23. builder =newAlertDialog.Builder(mContext, R.style.AudioDialog);
  24. LayoutInflater inflater =LayoutInflater.from(mContext);
  25. View view = inflater.inflate(R.layout.dialog_recorder,null);
  26. mIcon =(ImageView) view.findViewById(R.id.id_recorder_dialog_icon);
  27. mVoice =(ImageView) view.findViewById(R.id.id_recorder_dialog_voice);
  28. mLable =(TextView) view.findViewById(R.id.id_recorder_dialog_label);
  29. builder.setView(view);
  30. builder.create();
  31. dialog = builder.show();
  32. }
  33. publicvoid recording(){
  34. if(dialog !=null&& dialog.isShowing()){//显示状态
  35. mIcon.setVisibility(View.VISIBLE);
  36. mVoice.setVisibility(View.VISIBLE);
  37. mLable.setVisibility(View.VISIBLE);
  38. mIcon.setImageResource(R.drawable.recorder);
  39. mLable.setText("手指上滑,取消发送");
  40. }
  41. }
  42. // 显示想取消的对话框
  43. publicvoid wantToCancel(){
  44. if(dialog !=null&& dialog.isShowing()){//显示状态
  45. mIcon.setVisibility(View.VISIBLE);
  46. mVoice.setVisibility(View.GONE);
  47. mLable.setVisibility(View.VISIBLE);
  48. mIcon.setImageResource(R.drawable.cancel);
  49. mLable.setText("松开手指,取消发送");
  50. }
  51. }
  52. // 显示时间过短的对话框
  53. publicvoid tooShort(){
  54. if(dialog !=null&& dialog.isShowing()){//显示状态
  55. mIcon.setVisibility(View.VISIBLE);
  56. mVoice.setVisibility(View.GONE);
  57. mLable.setVisibility(View.VISIBLE);
  58. mIcon.setImageResource(R.drawable.voice_to_short);
  59. mLable.setText("录音时间过短");
  60. }
  61. }
  62. // 显示取消的对话框
  63. publicvoid dimissDialog(){
  64. if(dialog !=null&& dialog.isShowing()){//显示状态
  65. dialog.dismiss();
  66. dialog =null;
  67. }
  68. }
  69. // 显示更新音量级别的对话框
  70. publicvoid updateVoiceLevel(int level){
  71. if(dialog !=null&& dialog.isShowing()){//显示状态
  72. // mIcon.setVisibility(View.VISIBLE);
  73. // mVoice.setVisibility(View.VISIBLE);
  74. // mLable.setVisibility(View.VISIBLE);
  75. //设置图片的id
  76. int resId = mContext.getResources().getIdentifier("v"+level,"drawable", mContext.getPackageName());
  77. mVoice.setImageResource(resId);
  78. }
  79. }
  80. }

MainActivity.java

  1. package com.xuliugen.weichat;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import com.xuliugen.weichat.AudioRecorderButton.AudioFinishRecorderListener;
  5. import android.app.Activity;
  6. import android.graphics.drawable.AnimationDrawable;
  7. import android.media.MediaPlayer;
  8. import android.os.Bundle;
  9. import android.view.View;
  10. import android.widget.AdapterView;
  11. import android.widget.ArrayAdapter;
  12. import android.widget.ListView;
  13. import android.widget.AdapterView.OnItemClickListener;
  14. publicclassMainActivityextendsActivity{
  15. privateListView mListView;
  16. privateArrayAdapter<Recorder> mAdapter;
  17. privateList<Recorder> mDatas =newArrayList<MainActivity.Recorder>();
  18. privateAudioRecorderButton mAudioRecorderButton;
  19. privateView animView;
  20. @Override
  21. protectedvoid onCreate(Bundle savedInstanceState){
  22. super.onCreate(savedInstanceState);
  23. setContentView(R.layout.activity_main);
  24. mListView =(ListView) findViewById(R.id.id_listview);
  25. mAudioRecorderButton =(AudioRecorderButton) findViewById(R.id.id_recorder_button);
  26. mAudioRecorderButton.setAudioFinishRecorderListener(newAudioFinishRecorderListener(){
  27. publicvoid onFinish(float seconds,String filePath){
  28. Recorder recorder =newRecorder(seconds, filePath);
  29. mDatas.add(recorder);
  30. mAdapter.notifyDataSetChanged();//通知更新的内容
  31. mListView.setSelection(mDatas.size()-1);//将lisview设置为最后一个
  32. }
  33. });
  34. mAdapter =newRecoderAdapter(this, mDatas);
  35. mListView.setAdapter(mAdapter);
  36. //listView的item点击事件
  37. mListView.setOnItemClickListener(newOnItemClickListener(){
  38. publicvoid onItemClick(AdapterView<?> arg0,View view,int position,long id){
  39. // 播放动画(帧动画)
  40. if(animView !=null){
  41. animView.setBackgroundResource(R.drawable.adj);
  42. animView =null;
  43. }
  44. animView = view.findViewById(R.id.id_recoder_anim);
  45. animView.setBackgroundResource(R.drawable.play_anim);
  46. AnimationDrawable animation =(AnimationDrawable) animView.getBackground();
  47. animation.start();
  48. // 播放录音
  49. MediaManager.playSound(mDatas.get(position).filePath,newMediaPlayer.OnCompletionListener(){
  50. publicvoid onCompletion(MediaPlayer mp){
  51. animView.setBackgroundResource(R.drawable.adj);
  52. }
  53. });
  54. }
  55. });
  56. }
  57. @Override
  58. protectedvoid onPause(){
  59. super.onPause();
  60. MediaManager.pause();
  61. }
  62. @Override
  63. protectedvoid onResume(){
  64. super.onResume();
  65. MediaManager.resume();
  66. }
  67. @Override
  68. protectedvoid onDestroy(){
  69. super.onDestroy();
  70. MediaManager.release();
  71. }
  72. classRecorder{
  73. float time;
  74. String filePath;
  75. publicRecorder(float time,String filePath){
  76. super();
  77. this.time = time;
  78. this.filePath = filePath;
  79. }
  80. publicfloat getTime(){
  81. return time;
  82. }
  83. publicvoid setTime(float time){
  84. this.time = time;
  85. }
  86. publicString getFilePath(){
  87. return filePath;
  88. }
  89. publicvoid setFilePath(String filePath){
  90. this.filePath = filePath;
  91. }
  92. }
  93. }

MediaManager.java

  1. package com.xuliugen.weichat;
  2. import android.media.AudioManager;
  3. import android.media.MediaPlayer;
  4. import android.media.MediaPlayer.OnCompletionListener;
  5. import android.media.MediaPlayer.OnErrorListener;
  6. publicclassMediaManager{
  7. privatestaticMediaPlayer mMediaPlayer;
  8. privatestaticboolean isPause;
  9. /** * 播放音乐 * @param filePath * @param onCompletionListener */
  10. publicstaticvoid playSound(String filePath,OnCompletionListener onCompletionListener){
  11. if(mMediaPlayer ==null){
  12. mMediaPlayer =newMediaPlayer();
  13. //设置一个error监听器
  14. mMediaPlayer.setOnErrorListener(newOnErrorListener(){
  15. publicboolean onError(MediaPlayer arg0,int arg1,int arg2){
  16. mMediaPlayer.reset();
  17. returnfalse;
  18. }
  19. });
  20. }else{
  21. mMediaPlayer.reset();
  22. }
  23. try{
  24. mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
  25. mMediaPlayer.setOnCompletionListener(onCompletionListener);
  26. mMediaPlayer.setDataSource(filePath);
  27. mMediaPlayer.prepare();
  28. mMediaPlayer.start();
  29. }catch(Exception e){
  30. }
  31. }
  32. /** * 暂停播放 */
  33. publicstaticvoid pause(){
  34. if(mMediaPlayer !=null&& mMediaPlayer.isPlaying()){//正在播放的时候
  35. mMediaPlayer.pause();
  36. isPause =true;
  37. }
  38. }
  39. /** * 当前是isPause状态 */
  40. publicstaticvoid resume(){
  41. if(mMediaPlayer !=null&& isPause){
  42. mMediaPlayer.start();
  43. isPause =false;
  44. }
  45. }
  46. /** * 释放资源 */
  47. publicstaticvoid release(){
  48. if(mMediaPlayer !=null){
  49. mMediaPlayer.release();
  50. mMediaPlayer =null;
  51. }
  52. }
  53. }

RecoderAdapter.java

  1. package com.xuliugen.weichat;
  2. import java.util.List;
  3. import android.content.Context;
  4. import android.util.DisplayMetrics;
  5. import android.view.LayoutInflater;
  6. import android.view.View;
  7. import android.view.ViewGroup;
  8. import android.view.WindowManager;
  9. import android.widget.ArrayAdapter;
  10. import android.widget.TextView;
  11. import com.xuliugen.weichat.MainActivity.Recorder;
  12. publicclassRecoderAdapterextendsArrayAdapter<Recorder>{
  13. privateContext mContext;
  14. privateList<Recorder> mDatas;
  15. privateint mMinItemWidth;//最小的item宽度
  16. privateint mMaxItemWidth;//最大的item宽度
  17. privateLayoutInflater mInflater;
  18. publicRecoderAdapter(Context context,List<Recorder> datas){
  19. super(context,-1, datas);
  20. mContext = context;
  21. mDatas = datas;
  22. //获取屏幕的宽度
  23. WindowManager wm =(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  24. DisplayMetrics outMetrics =newDisplayMetrics();
  25. wm.getDefaultDisplay().getMetrics(outMetrics);
  26. mMaxItemWidth =(int)(outMetrics.widthPixels *0.7f);
  27. mMinItemWidth =(int)(outMetrics.widthPixels *0.15f);
  28. mInflater =LayoutInflater.from(context);
  29. }
  30. /** * 定义一个ViewHolder */
  31. privateclassViewHolder{
  32. TextView seconds;// 时间
  33. View length;// 长度
  34. }
  35. @Override
  36. publicView getView(int position,View convertView,ViewGroup parent){
  37. ViewHolder holder =null;
  38. if(convertView ==null){
  39. convertView = mInflater.inflate(R.layout.item_recoder, parent,false);
  40. holder =newViewHolder();
  41. holder.seconds =(TextView) convertView.findViewById(R.id.id_recoder_time);
  42. holder.length = convertView.findViewById(R.id.id_recoder_lenght);
  43. convertView.setTag(holder);
  44. }else{
  45. holder =(ViewHolder) convertView.getTag();
  46. }
  47. holder.seconds.setText(Math.round(getItem(position).time)+"\"");
  48. ViewGroup.LayoutParams lp = holder.length.getLayoutParams();
  49. lp.width =(int)(mMinItemWidth +(mMaxItemWidth /60f)* getItem(position).time);
  50. return convertView;
  51. }
  52. }
  相关解决方案