打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Android 仿微信实现语音聊天功能

在此感谢鸿洋大神,因为我这是在慕课上看大神的视频做出来的。

代码中我已经添加了很多很多注释,不光是为了大家,也是为了自己能够更加透彻的理解该功能


支持原创,也不算原创了哈哈~

http://blog.csdn.net/lhk147852369/article/details/78658055

注意注意:

Android 6.0动态获取录音权限,我并没有加上,所以你们需要在写完代码后,运行时在权限管理中指定该权限

否则会崩溃哦~~, 当然你们可以改变as中的targerversion<23就可以了

话不多说,直接上效果图:



MainActivity :

  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.   
  4.     private ListView mListView;  
  5.     private ArrayAdapter<Recorder> mAdapter;  
  6.     private List<Recorder> mDatas =new ArrayList<>();  
  7.   
  8.     private AudioRecorderButton mAudioRecorderButton;  
  9.     private View mAnimView;  
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.activity_main);  
  14.   
  15.   
  16.         initView();  
  17.         setListViewAdapter();  
  18.   
  19.   
  20.     }  
  21.   
  22.     private void initView(){  
  23.         mListView = findViewById(R.id.id_listview);  
  24.         mAudioRecorderButton = findViewById(R.id.id_recorder_button);  
  25.   
  26.         mAudioRecorderButton.setAudioFinishRecorderListener(new AudioRecorderButton.AudioFinishRecorderListener() {  
  27.             @Override  
  28.             public void onFinish(float seconds, String filePath) {  
  29.                 //每完成一次录音  
  30.                 Recorder recorder = new Recorder(seconds,filePath);  
  31.                 mDatas.add(recorder);  
  32.                 //更新adapter  
  33.                 mAdapter.notifyDataSetChanged();  
  34.                 //设置listview 位置  
  35.                 mListView.setSelection(mDatas.size()-1);  
  36.             }  
  37.         });  
  38.     }  
  39.   
  40.     private void setListViewAdapter(){  
  41.         mAdapter = new RecorderAdapter(this, mDatas);  
  42.         mListView.setAdapter(mAdapter);  
  43.   
  44.         mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {  
  45.             @Override  
  46.             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {  
  47.                 //如果第一个动画正在运行, 停止第一个播放其他的  
  48.                 if (mAnimView != null) {  
  49.                     mAnimView.setBackgroundResource(R.drawable.adj);  
  50.                     mAnimView = null;  
  51.                 }  
  52.                 //播放动画  
  53.                 mAnimView = view.findViewById(R.id.id_recorder_anim);  
  54.                 mAnimView.setBackgroundResource(R.drawable.play_anim);  
  55.                 AnimationDrawable animation = (AnimationDrawable) mAnimView.getBackground();  
  56.                 animation.start();  
  57.   
  58.                 //播放音频  完成后改回原来的background  
  59.                 MediaManager.playSound(mDatas.get(position).filePath, new MediaPlayer.OnCompletionListener() {  
  60.                     @Override  
  61.                     public void onCompletion(MediaPlayer mp) {  
  62.                         mAnimView.setBackgroundResource(R.drawable.adj);  
  63.                     }  
  64.                 });  
  65.             }  
  66.         });  
  67.     }  
  68.   
  69.     /** 
  70.      * 根据生命周期 管理播放录音 
  71.      */  
  72.     @Override  
  73.     protected void onPause() {  
  74.         super.onPause();  
  75.         MediaManager.pause();  
  76.     }  
  77.   
  78.     @Override  
  79.     protected void onResume() {  
  80.         super.onResume();  
  81.         MediaManager.resume();  
  82.     }  
  83.   
  84.     @Override  
  85.     protected void onDestroy() {  
  86.         super.onDestroy();  
  87.         MediaManager.release();  
  88.     }  
  89.   
  90.     //数据类  
  91.     class Recorder{  
  92.   
  93.         float time;  
  94.         String filePath;  
  95.   
  96.         public float getTime() {  
  97.             return time;  
  98.         }  
  99.   
  100.         public void setTime(float time) {  
  101.             this.time = time;  
  102.         }  
  103.   
  104.         public String getFilePath() {  
  105.             return filePath;  
  106.         }  
  107.   
  108.         public void setFilePath(String filePath) {  
  109.             this.filePath = filePath;  
  110.         }  
  111.   
  112.         public Recorder(float time, String filePath) {  
  113.             super();  
  114.             this.time = time;  
  115.             this.filePath = filePath;  
  116.         }  
  117.     }  
  118. }  

主页面:

需要注意:改下自定义button的包名哦,不然会找不到的

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.     android:orientation="vertical"  
  8.     tools:context="com.lgoutech.weixin_recorder.MainActivity">  
  9.   
  10.    <ListView  
  11.        android:layout_width="match_parent"  
  12.        android:layout_height="0dp"  
  13.        android:layout_weight="1"  
  14.        android:id="@+id/id_listview"  
  15.        android:background="#ebebeb"  
  16.        android:divider="@null"  
  17.        android:dividerHeight="10dp"  
  18.        ></ListView>  
  19.   
  20.     <LinearLayout  
  21.         android:layout_width="match_parent"  
  22.         android:layout_height="wrap_content"  
  23.         android:orientation="vertical"  
  24.         >  
  25.         <View  
  26.             android:layout_width="wrap_content"  
  27.             android:layout_height="1dp"  
  28.             android:background="#ccc"  
  29.             />  
  30.         <com.lgoutech.weixin_recorder.view.AudioRecorderButton  
  31.             android:id="@+id/id_recorder_button"  
  32.             android:layout_width="match_parent"  
  33.             android:layout_height="wrap_content"  
  34.             android:layout_marginLeft="50dp"  
  35.             android:layout_marginRight="50dp"  
  36.             android:gravity="center"  
  37.             android:layout_marginTop="7dp"  
  38.             android:layout_marginBottom="7dp"  
  39.             android:padding="5dp"  
  40.             android:text="@string/str_recorder_normal"  
  41.             android:textColor="#727272"  
  42.             android:background="@drawable/btn_recorder_normal"  
  43.             android:minHeight="0dp"  
  44.             >  
  45.   
  46.         </com.lgoutech.weixin_recorder.view.AudioRecorderButton>  
  47.   
  48.   
  49.     </LinearLayout>  
  50. </LinearLayout>  

自定义的dialog、xml文件:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     android:layout_width="wrap_content"  
  6.     android:layout_height="wrap_content"  
  7.     android:orientation="vertical"  
  8.     android:padding="20dp"  
  9.     android:gravity="center"  
  10.     android:background="@drawable/dialog_loading_bg"  
  11.     tools:context="com.lgoutech.weixin_recorder.MainActivity">  
  12.   
  13.     <LinearLayout  
  14.         android:layout_width="wrap_content"  
  15.         android:layout_height="wrap_content"  
  16.         android:orientation="horizontal"  
  17.         >  
  18.         <ImageView  
  19.             android:id="@+id/id_recorder_dialog_icon"  
  20.             android:layout_width="wrap_content"  
  21.             android:layout_height="wrap_content"  
  22.             android:src="@drawable/recorder"  
  23.             android:visibility="visible"  
  24.             />  
  25.         <ImageView  
  26.             android:id="@+id/id_recorder_dialog_voice"  
  27.             android:layout_width="wrap_content"  
  28.             android:layout_height="wrap_content"  
  29.             android:src="@drawable/v1"  
  30.             android:visibility="visible"  
  31.             />  
  32.     </LinearLayout>  
  33.     <TextView  
  34.         android:id="@+id/id_recorder_dialog_label"  
  35.         android:layout_width="wrap_content"  
  36.         android:layout_height="wrap_content"  
  37.         android:layout_marginTop="5dp"  
  38.         android:text="手指上划,取消发送"  
  39.         android:textColor="#FFFFFF"  
  40.         />  
  41. </LinearLayout>  

ListView的Item布局:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="60dp"  
  7.     android:layout_marginTop="5dp"  
  8.     tools:context="com.lgoutech.weixin_recorder.MainActivity">  
  9.     <ImageView  
  10.         android:id="@+id/id_icon"  
  11.         android:layout_width="40dp"  
  12.         android:layout_height="40dp"  
  13.         android:layout_alignParentRight="true"  
  14.         android:layout_centerVertical="true"  
  15.         android:layout_marginRight="5dp"  
  16.         android:src="@drawable/icon"  
  17.         />  
  18.   
  19.     <LinearLayout  
  20.         android:id="@+id/id_recorder_length"  
  21.         android:layout_width="wrap_content"  
  22.         android:layout_height="wrap_content"  
  23.         android:layout_centerVertical="true"  
  24.         android:orientation="vertical"  
  25.         android:layout_toLeftOf="@id/id_icon"  
  26.         android:background="@drawable/chatto_bg_focused"  
  27.         >  
  28.   
  29.         <View  
  30.             android:id="@+id/id_recorder_anim"  
  31.             android:layout_width="25dp"  
  32.             android:layout_height="25dp"  
  33.             android:layout_gravity="center_vertical|right"  
  34.             android:background="@drawable/adj"  
  35.             />  
  36.           
  37.     </LinearLayout>  
  38.   
  39.     <TextView  
  40.         android:id="@+id/id_recorder_time"  
  41.         android:layout_width="wrap_content"  
  42.         android:layout_height="wrap_content"  
  43.         android:layout_toLeftOf="@id/id_recorder_length"  
  44.         android:layout_marginRight="3dp"  
  45.         android:textColor="#ff777777"  
  46.         android:layout_centerVertical="true"  
  47.         />  
  48.   
  49. </RelativeLayout>  

录音管理类:

  1. public class AudioManager {  
  2.   
  3.     private MediaRecorder mMediaRecorder;  
  4.     private String mDir;  
  5.     private String mCurrentFilePath;  
  6.   
  7.     private static AudioManager mInstance;  
  8.   
  9.     private boolean isPrepared;  
  10.     public AudioManager(String dir){  
  11.         mDir = dir;  
  12.     };  
  13.   
  14.   
  15.   
  16.     /** 
  17.      * 回调准备完毕 
  18.      */  
  19.     public interface AudioStateListener {  
  20.         void wellPrepared();  
  21.     }  
  22.   
  23.     public AudioStateListener mListener;  
  24.   
  25.     public void setOnAudioStateListener(AudioStateListener listener){  
  26.         mListener = listener;  
  27.     }  
  28.   
  29.     public static AudioManager getInstance(String dir){  
  30.         if (mInstance == null) {  
  31.             synchronized (AudioManager.class) {  
  32.                 if (mInstance == null) {  
  33.                     mInstance = new AudioManager(dir);  
  34.                 }  
  35.             }  
  36.         }  
  37.         return mInstance;  
  38.     }  
  39.   
  40.   
  41.     /** 
  42.      * 准备 
  43.      */  
  44.     public void prepareAudio() {  
  45.         try {  
  46.             isPrepared = false;  
  47.             File dir = new File(mDir);  
  48.             if (!dir.exists()) {  
  49.                 dir.mkdir();  
  50.             }  
  51.             String fileName = generateFileName();  
  52.   
  53.             File file = new File(dir, fileName);  
  54.   
  55.             mCurrentFilePath = file.getAbsolutePath();  
  56.   
  57.             mMediaRecorder = new MediaRecorder();  
  58.             //设置输出文件  
  59.             mMediaRecorder.setOutputFile(file.getAbsolutePath());  
  60.             //设置MediaRecorder的音频源为麦克风  
  61.             mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);  
  62.             //设置音频格式  
  63.             mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);  
  64.             //设置音频的格式为amr  
  65.             mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);  
  66.             mMediaRecorder.prepare();  
  67.             mMediaRecorder.start();  
  68.             //准备结束  
  69.             isPrepared = true;  
  70.             if (mListener != null) {  
  71.                 mListener.wellPrepared();  
  72.             }  
  73.         } catch (IOException e) {  
  74.             e.printStackTrace();  
  75.         }  
  76.     }  
  77.   
  78. //    生成UUID唯一标示符  
  79. //    算法的核心思想是结合机器的网卡、当地时间、一个随即数来生成GUID  
  80. //    .amr音频文件  
  81.     private String generateFileName() {  
  82.         return UUID.randomUUID().toString()+".amr";  
  83.     }  
  84.   
  85.     public int getVoiceLevel(int maxLevel) {  
  86.         if (isPrepared) {  
  87.             //获得最大的振幅getMaxAmplitude() 1-32767  
  88.             try {  
  89.                 return maxLevel * mMediaRecorder.getMaxAmplitude()/32768+1;  
  90.             } catch (Exception e) {  
  91.   
  92.             }  
  93.         }  
  94.         return 1;  
  95.     }  
  96.   
  97.     public void release() {  
  98.         mMediaRecorder.stop();  
  99.         mMediaRecorder.release();  
  100.         mMediaRecorder = null;  
  101.     }  
  102.   
  103.     public void cancel(){  
  104.         release();  
  105.         if(mCurrentFilePath!=null) {  
  106.             File file = new File(mCurrentFilePath);  
  107.             file.delete();  
  108.             mCurrentFilePath = null;  
  109.         }  
  110.     }  
  111.     public String getCurrentFilePath() {  
  112.         return mCurrentFilePath;  
  113.     }  
  114. }  

最重要的自定义按钮,很多逻辑都在这里:

  1. /** 
  2.  * 自定义按钮 实现录音等功能 
  3.  * Created by Administrator on 2017/11/28. 
  4.  */  
  5.   
  6. @SuppressLint("AppCompatCustomView")  
  7. public class AudioRecorderButton extends Button implements AudioManager.AudioStateListener {  
  8.   
  9.     //手指滑动 距离  
  10.     private static final int DISTANCE_Y_CANCEL = 50;  
  11.     //状态  
  12.     private static final int STATE_NORMAL = 1;  
  13.     private static final int STATE_RECORDING = 2;  
  14.     private static final int STATE_WANT_TO_CANCEL = 3;  
  15.     //当前状态  
  16.     private int mCurState = STATE_NORMAL;  
  17.     //已经开始录音  
  18.     private boolean isRecording = false;  
  19.   
  20.     private DialogManager mDialogManager;  
  21.     private AudioManager mAudioManager;  
  22.   
  23.     private float mTime;  
  24.     //是否触发onlongclick  
  25.     private boolean mReady;  
  26.   
  27.     public AudioRecorderButton(Context context) {  
  28.         this(context, null);  
  29.     }  
  30.   
  31.     public AudioRecorderButton(Context context, AttributeSet attrs) {  
  32.         super(context, attrs);  
  33.         mDialogManager = new DialogManager(getContext());  
  34.         //偷个懒,并没有判断 是否存在, 是否可读。  
  35.   
  36.         String dir = Environment.getExternalStorageDirectory() + "/recorder_audios";  
  37.   
  38.         mAudioManager = new AudioManager(dir);  
  39.         mAudioManager.setOnAudioStateListener(this);  
  40.         //按钮长按 准备录音 包括start  
  41.         setOnLongClickListener(new OnLongClickListener() {  
  42.             @Override  
  43.             public boolean onLongClick(View v) {  
  44.                 mReady = true;  
  45.                 mAudioManager.prepareAudio();  
  46.                 return false;  
  47.             }  
  48.         });  
  49.     }  
  50.   
  51.     /** 
  52.      * 录音完成后的回调 
  53.      */  
  54.     public interface AudioFinishRecorderListener{  
  55.         //时长  和 文件  
  56.         void onFinish(float seconds,String filePath);  
  57.     }  
  58.   
  59.     private AudioFinishRecorderListener mListener;  
  60.   
  61.     public void setAudioFinishRecorderListener (AudioFinishRecorderListener listener){  
  62.         mListener = listener;  
  63.     }  
  64.     //获取音量大小的Runnable  
  65.     private Runnable mGetVoiceLevelRunnable = new Runnable() {  
  66.         @Override  
  67.         public void run() {  
  68.             while (isRecording) {  
  69.                 try {  
  70.                     Thread.sleep(100);  
  71.                     mTime += 0.1;  
  72.                     mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);  
  73.                 } catch (InterruptedException e) {  
  74.                     e.printStackTrace();  
  75.                 }  
  76.             }  
  77.         }  
  78.     };  
  79.   
  80.     private static final int MSG_AUDIO_PREPARED = 0X110;  
  81.     private static final int MSG_VOICE_CHANGED = 0X111;  
  82.     private static final int MSG_DIALOG_DIMISS = 0X112;  
  83.   
  84.     private Handler mHandler = new Handler() {  
  85.         @Override  
  86.         public void handleMessage(Message msg) {  
  87.             switch (msg.what) {  
  88.                 case MSG_AUDIO_PREPARED :  
  89.                     //TODO 真正现实应该在audio end prepared以后  
  90.                     mDialogManager.showRecordingDialog();  
  91.                     isRecording = true;  
  92.   
  93.                     new Thread(mGetVoiceLevelRunnable).start();  
  94.                     break;  
  95.                 case MSG_VOICE_CHANGED :  
  96.                     mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7));  
  97.   
  98.                     break;  
  99.                 case MSG_DIALOG_DIMISS :  
  100.                     mDialogManager.dimissDialog();  
  101.                     break;  
  102.             }  
  103.         }  
  104.     };  
  105.   
  106.     @Override  
  107.     public void wellPrepared() {  
  108.         mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);  
  109.     }  
  110.   
  111.     @Override  
  112.     public boolean onTouchEvent(MotionEvent event) {  
  113.         int action = event.getAction();  
  114.         int x = (int) event.getX();  
  115.         int y = (int) event.getY();  
  116.         switch (action) {  
  117.             case MotionEvent.ACTION_DOWN:  
  118.                 //TODO  
  119.                 isRecording = true;  
  120.                 changeState(STATE_RECORDING);  
  121.   
  122.                 break;  
  123.             case MotionEvent.ACTION_MOVE:  
  124.   
  125.                 if (isRecording) {  
  126.   
  127.   
  128.                 //根据想x,y的坐标,判断是否想要取消  
  129.                 if (wantToCancel(x, y)) {  
  130.   
  131.                     changeState(STATE_WANT_TO_CANCEL);  
  132.                 } else {  
  133.   
  134.                     changeState(STATE_RECORDING);  
  135.                 }  
  136.                 }  
  137.                 break;  
  138.             case MotionEvent.ACTION_UP:  
  139.                 //如果longclick 没触发  
  140.                 if (!mReady) {  
  141.                     reset();  
  142.                     return super.onTouchEvent(event);  
  143.                 }  
  144.                 //触发了onlongclick 没准备好,但是已经prepared 已经start  
  145.                 //所以消除文件夹  
  146.                 if(!isRecording||mTime<0.6f){  
  147.                     mDialogManager.tooShort();  
  148.                     mAudioManager.cancel();  
  149.                     mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1300);  
  150.                 }else if(mCurState==STATE_RECORDING){//正常录制结束  
  151.   
  152.                     mDialogManager.dimissDialog();  
  153.                     mAudioManager.release();  
  154.                     if (mListener != null) {  
  155.                         mListener.onFinish(mTime,mAudioManager.getCurrentFilePath());  
  156.                     }  
  157.   
  158.                 }else if (mCurState == STATE_RECORDING) {  
  159.   
  160.                     mDialogManager.dimissDialog();  
  161.                     //release  
  162.                     //callbacktoAct  
  163.                 } else if (mCurState == STATE_WANT_TO_CANCEL) {  
  164.                     mDialogManager.dimissDialog();  
  165.                     mAudioManager.cancel();  
  166.                     //cancel  
  167.                 }  
  168.   
  169.                 reset();  
  170.   
  171.                 break;  
  172.   
  173.         }  
  174.         return super.onTouchEvent(event);  
  175.     }  
  176.   
  177.     /** 
  178.      * 恢复状态 标志位 
  179.      */  
  180.     private void reset() {  
  181.         isRecording = false;  
  182.         mReady = false;  
  183.         changeState(STATE_NORMAL);  
  184.         mTime = 0;  
  185.   
  186.     }  
  187.   
  188.     private boolean wantToCancel(int x, int y) {  
  189.         //如果左右滑出 button  
  190.         if (x < 0 || x > getWidth()) {  
  191.             return true;  
  192.         }  
  193.         //如果上下滑出 button  加上我们自定义的距离  
  194.         if (y < -DISTANCE_Y_CANCEL || y > getHeight() + DISTANCE_Y_CANCEL) {  
  195.             return true;  
  196.         }  
  197.         return false;  
  198.     }  
  199.   
  200.     //改变状态  
  201.     private void changeState(int state) {  
  202.         if (mCurState != state) {  
  203.             mCurState = state;  
  204.             switch (state) {  
  205.                 case STATE_NORMAL:  
  206.                     setBackgroundResource(R.drawable.btn_recorder_normal);  
  207.                     setText(R.string.str_recorder_normal);  
  208.                     break;  
  209.                 case STATE_RECORDING:  
  210.                     setBackgroundResource(R.drawable.btn_recording);  
  211.                     setText(R.string.str_recorder_recording);  
  212.   
  213.                     if (isRecording) {  
  214.                         mDialogManager.recording();  
  215.                     }  
  216.                     break;  
  217.                 case STATE_WANT_TO_CANCEL:  
  218.                     setBackgroundResource(R.drawable.btn_recording);  
  219.                     setText(R.string.str_recorder_want_cancel);  
  220.                     mDialogManager.wantToCancel();  
  221.                     break;  
  222.             }  
  223.         }  
  224.     }  
  225.   
  226.   
  227. }  

Dialog管理类:

  1. public class DialogManager {  
  2.     private Dialog mDialog;  
  3.   
  4.     private ImageView mIcon;  
  5.     private ImageView mVoice;  
  6.   
  7.     private TextView mLable;  
  8.   
  9.     private Context mContext;  
  10.   
  11.     public DialogManager(Context context){  
  12.         mContext = context;  
  13.     }  
  14.   
  15.     public void showRecordingDialog(){  
  16.         mDialog = new Dialog(mContext, R.style.Theme_AudioDialog);  
  17.         LayoutInflater inflater = LayoutInflater.from(mContext);  
  18.         View view=inflater.inflate(R.layout.dialog_recorder,null);  
  19.         mDialog.setContentView(view);  
  20.   
  21.         mIcon = view.findViewById(R.id.id_recorder_dialog_icon);  
  22.         mVoice = view.findViewById(R.id.id_recorder_dialog_voice);  
  23.         mLable = view.findViewById(R.id.id_recorder_dialog_label);  
  24.   
  25.         mDialog.show();  
  26.     }  
  27.   
  28.     //正在播放时的状态  
  29.     public void recording() {  
  30.         if (mDialog != null&&mDialog.isShowing()) {  
  31.             mIcon.setVisibility(View.VISIBLE);  
  32.             mVoice.setVisibility(View.VISIBLE);  
  33.             mLable.setVisibility(View.VISIBLE);  
  34.   
  35.             mIcon.setImageResource(R.drawable.recorder);  
  36.             mLable.setText("手指上划,取消发送");  
  37.         }  
  38.     }  
  39.     //想要取消  
  40.     public void wantToCancel(){  
  41.         if (mDialog != null&&mDialog.isShowing()) {  
  42.             mIcon.setVisibility(View.VISIBLE);  
  43.             mVoice.setVisibility(View.GONE);  
  44.             mLable.setVisibility(View.VISIBLE);  
  45.   
  46.             mIcon.setImageResource(R.drawable.cancel);  
  47.             mLable.setText("松开手指,取消发送");  
  48.         }  
  49.     }  
  50.     //录音时间太短  
  51.     public void tooShort() {  
  52.         if (mDialog != null&&mDialog.isShowing()) {  
  53.             mIcon.setVisibility(View.VISIBLE);  
  54.             mVoice.setVisibility(View.GONE);  
  55.             mLable.setVisibility(View.VISIBLE);  
  56.   
  57.             mIcon.setImageResource(R.drawable.voice_to_short);  
  58.             mLable.setText("录音时间过短");  
  59.         }  
  60.     }  
  61.     //关闭dialog  
  62.     public void dimissDialog(){  
  63.         if (mDialog != null&&mDialog.isShowing()) {  
  64.             mDialog.dismiss();  
  65.             mDialog = null;  
  66.         }  
  67.     }  
  68.   
  69.     /** 
  70.      * 通过level更新voice上的图片 
  71.      * 
  72.      * @param level 
  73.      */  
  74.     public void updateVoiceLevel(int level){  
  75.         if (mDialog != null&&mDialog.isShowing()) {  
  76.   
  77.             int resId = mContext.getResources().getIdentifier("v" + level, "drawable", mContext.getPackageName());  
  78.             mVoice.setImageResource(resId);  
  79.         }  
  80.     }  
  81. }  

播放录音类:

  1. /** 
  2.  * Created by Administrator on 2017/11/28. 
  3.  * 播放录音类 
  4.  */  
  5.   
  6. public class MediaManager {  
  7.     private static MediaPlayer mMediaPlayer;  
  8.   
  9.     private static boolean isPause;  
  10.   
  11.     //播放录音  
  12.     public static void playSound(String filePath, MediaPlayer.OnCompletionListener onCompletionListener){  
  13.         if (mMediaPlayer == null) {  
  14.             mMediaPlayer = new MediaPlayer();  
  15.             //播放错误 防止崩溃  
  16.             mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {  
  17.                 @Override  
  18.                 public boolean onError(MediaPlayer mp, int what, int extra) {  
  19.                     mMediaPlayer.reset();  
  20.                     return false;  
  21.                 }  
  22.             });  
  23.         }else{  
  24.             mMediaPlayer.reset();  
  25.         }  
  26.         try {  
  27.             mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);  
  28.             mMediaPlayer.setOnCompletionListener(onCompletionListener);  
  29.             mMediaPlayer.setDataSource(filePath);  
  30.             mMediaPlayer.prepare();  
  31.             mMediaPlayer.start();  
  32.   
  33.         } catch (IOException e) {  
  34.             e.printStackTrace();  
  35.         }  
  36.     }  
  37.   
  38.   
  39.     /** 
  40.      * 如果 播放时间过长,如30秒 
  41.      * 用户突然来电话了,则需要暂停 
  42.      */  
  43.     public static void pause() {  
  44.         if (mMediaPlayer != null&&mMediaPlayer.isPlaying()) {  
  45.             mMediaPlayer.pause();  
  46.             isPause = true;  
  47.         }  
  48.     }  
  49.   
  50.     /** 
  51.      * 播放 
  52.      */  
  53.     public static void resume(){  
  54.         if (mMediaPlayer != null && isPause) {  
  55.             mMediaPlayer.start();  
  56.             isPause = false;  
  57.         }  
  58.     }  
  59.   
  60.     /** 
  61.      * activity 被销毁  释放 
  62.      */  
  63.     public static void release(){  
  64.         if (mMediaPlayer != null) {  
  65.             mMediaPlayer.release();  
  66.             mMediaPlayer = null;  
  67.         }  
  68.     }  
  69. }  

listview 的Adapter:

  1. /** 
  2.  *list的adapter 
  3.  * Created by Administrator on 2017/11/28. 
  4.  */  
  5.   
  6. public class RecorderAdapter extends ArrayAdapter<Recorder> {  
  7.   
  8.     //item 最小最大值  
  9.     private int mMinItemWidth;  
  10.     private int mMaxIItemWidth;  
  11.   
  12.     private LayoutInflater mInflater;  
  13.   
  14.     public RecorderAdapter(@NonNull Context context, List<Recorder> datas) {  
  15.         super(context,-1, datas);  
  16.         mInflater = LayoutInflater.from(context);  
  17.   
  18.         //获取屏幕宽度  
  19.         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);  
  20.         DisplayMetrics outMetrics = new DisplayMetrics();  
  21.         wm.getDefaultDisplay().getMetrics(outMetrics);  
  22.   
  23.         //item 设定最小最大值  
  24.         mMaxIItemWidth = (int) (outMetrics.widthPixels * 0.7f);  
  25.         mMinItemWidth = (int) (outMetrics.widthPixels * 0.15f);  
  26.     }  
  27.   
  28.     @NonNull  
  29.     @Override  
  30.     public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {  
  31.   
  32.         ViewHolder holder = null;  
  33.         if (convertView == null) {  
  34.             convertView = mInflater.inflate(R.layout.item_recorder, parent, false);  
  35.             holder = new ViewHolder();  
  36.             holder.seconds = convertView.findViewById(R.id.id_recorder_time);  
  37.             holder.length = convertView.findViewById(R.id.id_recorder_length);  
  38.             convertView.setTag(holder);  
  39.         }else {  
  40.             holder = (ViewHolder) convertView.getTag();  
  41.         }  
  42.         //设置时间  matt.round 四舍五入  
  43.         holder.seconds.setText(Math.round(getItem(position).time)+"\"");  
  44.         //设置背景的宽度  
  45.         ViewGroup.LayoutParams lp = holder.length.getLayoutParams();  
  46.         //getItem(position).time  
  47.         lp.width = (int) (mMinItemWidth + (mMaxIItemWidth / 60f*getItem(position).time));  
  48.   
  49.         return convertView;  
  50.     }  
  51.   
  52.     private class ViewHolder{  
  53.         TextView seconds;  
  54.         View length;  
  55.     }  
  56. }  


在string.xml中

  1. <string name="str_recorder_normal">按住说话</string>  
  2. <string name="str_recorder_recording">松开结束</string>  
  3. <string name="str_recorder_want_cancel">松开手指,取消发送</string>  

在style.xml中

  1. <style name="Theme_AudioDialog">  
  2.         <item name="android:windowBackground">@android:color/transparent</item>  
  3.         <!--Dialog的windowFrame框为无-->  
  4.         <item name="android:windowFrame">@null</item>  
  5.         <!--是否浮现在activity之上-->  
  6.         <item name="android:windowIsFloating">true</item>  
  7.         <!--//是否半透明-->  
  8.         <item name="android:windowIsTranslucent">true</item>  
  9.         <!--/背景是否模糊显示-->  
  10.         <item name="android:backgroundDimEnabled">true</item>  
  11.     </style>  


好了以上就是所有的代码了。

不懂得可以问我哦,或者自己查查百度吧。

我的注释已经很多了,理理逻辑,应该很好懂的。


图片资源:资源图片

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
Android 录音功能直接拿去用
[转]android通过webservice验证用户
ListView和ScrollView的冲突解决办法
Android拍照,上传,预览综合【修改】
android.hardware.camera2详解(实时更新,未完待续...)
用SurfaceView和MediaPlayer做一个Android视频播放器
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服