项目效果如下:
代码如下:
AudioManager.java
- package com.xuliugen.weichat;
- import java.io.File;
- import java.io.IOException;
- import java.util.UUID;
- import android.media.MediaRecorder;
- publicclassAudioManager{
- privateMediaRecorder mMediaRecorder;
- privateString mDir;
- privateString mCurrentFilePath;
- privatestaticAudioManager mInstance;
- privateboolean isPrepare;
- privateAudioManager(String dir){
- mDir = dir;
- }
- publicstaticAudioManager getInstance(String dir){
- if(mInstance ==null){
- synchronized(AudioManager.class){
- if(mInstance ==null){
- mInstance =newAudioManager(dir);
- }
- }
- }
- return mInstance;
- }
- /** * 使用接口 用于回调 */
- publicinterfaceAudioStateListener{
- void wellPrepared();
- }
- publicAudioStateListener mAudioStateListener;
- /** * 回调方法 */
- publicvoid setOnAudioStateListener(AudioStateListener listener){
- mAudioStateListener = listener;
- }
- // 去准备
- publicvoid prepareAudio(){
- try{
- isPrepare =false;
- File dir =newFile(mDir);
- if(!dir.exists()){
- dir.mkdirs();
- }
- String fileName = generateFileName();
- File file =newFile(dir, fileName);
- mCurrentFilePath =file.getAbsolutePath();
- mMediaRecorder =newMediaRecorder();
- // 设置输出文件
- mMediaRecorder.setOutputFile(dir.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();
- }
- }
- /** * 随机生成文件的名称 */
- privateString generateFileName(){
- return UUID.randomUUID().toString()+".amr";
- }
- publicint getVoiceLevel(int maxlevel){
- if(isPrepare){
- try{
- // mMediaRecorder.getMaxAmplitude() 1~32767
- return maxlevel * mMediaRecorder.getMaxAmplitude()/32768+1;
- }catch(Exception e){
- }
- }
- return1;
- }
- /** * 释放资源 */
- publicvoid release(){
- //mMediaRecorder.stop();
- mMediaRecorder.reset();
- mMediaRecorder =null;
- }
- /** * 取消录音 */
- publicvoid cancel(){
- release();
- if(mCurrentFilePath !=null){
- File file =newFile(mCurrentFilePath);
- file.delete();
- mCurrentFilePath =null;
- }
- }
- publicString getCurrentFilePath(){
- return mCurrentFilePath;
- }
- }
AudioRecorderButton.java
- package com.xuliugen.weichat;
- import android.content.Context;
- import android.os.Environment;
- import android.os.Handler;
- import android.os.Message;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.View;
- import android.widget.Button;
- import com.xuliugen.weichat.R;
- import com.xuliugen.weichat.AudioManager.AudioStateListener;
- publicclassAudioRecorderButtonextendsButton{
- privatestaticfinalint STATE_NORMAL =1;// 默认的状态
- privatestaticfinalint STATE_RECORDING =2;// 正在录音
- privatestaticfinalint STATE_WANT_TO_CANCEL =3;// 希望取消
- privateint mCurrentState = STATE_NORMAL;// 当前的状态
- privateboolean isRecording =false;// 已经开始录音
- privatestaticfinalint DISTANCE_Y_CANCEL =50;
- privateDialogManager mDialogManager;
- privateAudioManager mAudioManager;
- privatefloat mTime;
- // 是否触发longClick
- privateboolean mReady;
- privatestaticfinalint MSG_AUDIO_PREPARED =0x110;
- privatestaticfinalint MSG_VOICE_CHANGED =0x111;
- privatestaticfinalint MSG_DIALOG_DIMISS =0x112;
- /* * 获取音量大小的线程 */
- privateRunnable mGetVoiceLevelRunnable =newRunnable(){
- publicvoid run(){
- while(isRecording){
- try{
- Thread.sleep(100);
- mTime +=0.1f;
- mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);
- }catch(InterruptedException e){
- e.printStackTrace();
- }
- }
- }
- };
- privateHandler mHandler =newHandler(){
- @Override
- publicvoid handleMessage(Message msg){
- switch(msg.what){
- case MSG_AUDIO_PREPARED:
- // 显示對話框在开始录音以后
- mDialogManager.showRecordingDialog();
- isRecording =true;
- // 开启一个线程
- newThread(mGetVoiceLevelRunnable).start();
- break;
- case MSG_VOICE_CHANGED:
- mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7));
- break;
- case MSG_DIALOG_DIMISS:
- mDialogManager.dimissDialog();
- break;
- }
- super.handleMessage(msg);
- }
- };
- /** * 以下2个方法是构造方法 */
- publicAudioRecorderButton(Context context,AttributeSet attrs){
- super(context, attrs);
- mDialogManager =newDialogManager(context);
- String dir ="/storage/sdcard0/my_weixin";
- //String dir = Environment.getExternalStorageDirectory()+"/my_weixin";
- mAudioManager =AudioManager.getInstance(dir);
- mAudioManager.setOnAudioStateListener(newAudioStateListener(){
- publicvoid wellPrepared(){
- mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);
- }
- });
- // 由于这个类是button所以在构造方法中添加监听事件
- setOnLongClickListener(newOnLongClickListener(){
- publicboolean onLongClick(View v){
- mReady =true;
- mAudioManager.prepareAudio();
- returnfalse;
- }
- });
- }
- publicAudioRecorderButton(Context context){
- this(context,null);
- }
- /** * 录音完成后的回调 */
- publicinterfaceAudioFinishRecorderListener{
- void onFinish(float seconds,String filePath);
- }
- privateAudioFinishRecorderListener audioFinishRecorderListener;
- publicvoid setAudioFinishRecorderListener(AudioFinishRecorderListener listener){
- audioFinishRecorderListener = listener;
- }
- /** * 屏幕的触摸事件 */
- @Override
- publicboolean onTouchEvent(MotionEventevent){
- int action =event.getAction();
- int x =(int)event.getX();// 获得x轴坐标
- int y =(int)event.getY();// 获得y轴坐标
- switch(action){
- caseMotionEvent.ACTION_DOWN:
- changeState(STATE_RECORDING);
- break;
- caseMotionEvent.ACTION_MOVE:
- if(isRecording){
- // 如果想要取消,根据x,y的坐标看是否需要取消
- if(wantToCancle(x, y)){
- changeState(STATE_WANT_TO_CANCEL);
- }else{
- changeState(STATE_RECORDING);
- }
- }
- break;
- caseMotionEvent.ACTION_UP:
- if(!mReady){
- reset();
- returnsuper.onTouchEvent(event);
- }
- if(!isRecording || mTime <0.6f){
- mDialogManager.tooShort();
- mAudioManager.cancel();
- mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS,1000);// 延迟显示对话框
- }elseif(mCurrentState == STATE_RECORDING){// 正在录音的时候,结束
- mDialogManager.dimissDialog();
- mAudioManager.release();
- if(audioFinishRecorderListener !=null){
- audioFinishRecorderListener.onFinish(mTime,mAudioManager.getCurrentFilePath());
- }
- }elseif(mCurrentState == STATE_WANT_TO_CANCEL){// 想要取消
- mDialogManager.dimissDialog();
- mAudioManager.cancel();
- }
- reset();
- break;
- }
- returnsuper.onTouchEvent(event);
- }
- /** * 恢复状态及标志位 */
- privatevoid reset(){
- isRecording =false;
- mTime =0;
- mReady =false;
- changeState(STATE_NORMAL);
- }
- privateboolean wantToCancle(int x,int y){
- if(x <0|| x > getWidth()){// 超过按钮的宽度
- returntrue;
- }
- // 超过按钮的高度
- if(y <-DISTANCE_Y_CANCEL || y > getHeight()+ DISTANCE_Y_CANCEL){
- returntrue;
- }
- returnfalse;
- }
- /** * 改变 */
- privatevoid 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_WANT_TO_CANCEL:
- setBackgroundResource(R.drawable.btn_recorder_recording);
- setText(R.string.str_recorder_want_cancel);
- mDialogManager.wantToCancel();
- break;
- }
- }
- }
- }
DialogManager.java
- package com.xuliugen.weichat;
- import android.app.AlertDialog;
- import android.content.Context;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.widget.ImageView;
- import android.widget.TextView;
- import com.xuliugen.weichat.R;
- /** * 用于管理Dialog * * @author xuliugen * */
- publicclassDialogManager{
- privateAlertDialog.Builder builder;
- privateImageView mIcon;
- privateImageView mVoice;
- privateTextView mLable;
- privateContext mContext;
- privateAlertDialog dialog;//用于取消AlertDialog.Builder
- /** * 构造方法 传入上下文 */
- publicDialogManager(Context context){
- this.mContext = context;
- }
- // 显示录音的对话框
- publicvoid showRecordingDialog(){
- builder =newAlertDialog.Builder(mContext, R.style.AudioDialog);
- LayoutInflater inflater =LayoutInflater.from(mContext);
- View view = inflater.inflate(R.layout.dialog_recorder,null);
- 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);
- builder.setView(view);
- builder.create();
- dialog = builder.show();
- }
- publicvoid recording(){
- if(dialog !=null&& dialog.isShowing()){//显示状态
- mIcon.setVisibility(View.VISIBLE);
- mVoice.setVisibility(View.VISIBLE);
- mLable.setVisibility(View.VISIBLE);
- mIcon.setImageResource(R.drawable.recorder);
- mLable.setText("手指上滑,取消发送");
- }
- }
- // 显示想取消的对话框
- publicvoid wantToCancel(){
- if(dialog !=null&& dialog.isShowing()){//显示状态
- mIcon.setVisibility(View.VISIBLE);
- mVoice.setVisibility(View.GONE);
- mLable.setVisibility(View.VISIBLE);
- mIcon.setImageResource(R.drawable.cancel);
- mLable.setText("松开手指,取消发送");
- }
- }
- // 显示时间过短的对话框
- publicvoid tooShort(){
- if(dialog !=null&& dialog.isShowing()){//显示状态
- mIcon.setVisibility(View.VISIBLE);
- mVoice.setVisibility(View.GONE);
- mLable.setVisibility(View.VISIBLE);
- mIcon.setImageResource(R.drawable.voice_to_short);
- mLable.setText("录音时间过短");
- }
- }
- // 显示取消的对话框
- publicvoid dimissDialog(){
- if(dialog !=null&& dialog.isShowing()){//显示状态
- dialog.dismiss();
- dialog =null;
- }
- }
- // 显示更新音量级别的对话框
- publicvoid updateVoiceLevel(int level){
- if(dialog !=null&& dialog.isShowing()){//显示状态
- // mIcon.setVisibility(View.VISIBLE);
- // mVoice.setVisibility(View.VISIBLE);
- // mLable.setVisibility(View.VISIBLE);
- //设置图片的id
- int resId = mContext.getResources().getIdentifier("v"+level,"drawable", mContext.getPackageName());
- mVoice.setImageResource(resId);
- }
- }
- }
MainActivity.java
- package com.xuliugen.weichat;
- import java.util.ArrayList;
- import java.util.List;
- import com.xuliugen.weichat.AudioRecorderButton.AudioFinishRecorderListener;
- import android.app.Activity;
- import android.graphics.drawable.AnimationDrawable;
- import android.media.MediaPlayer;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.AdapterView;
- import android.widget.ArrayAdapter;
- import android.widget.ListView;
- import android.widget.AdapterView.OnItemClickListener;
- publicclassMainActivityextendsActivity{
- privateListView mListView;
- privateArrayAdapter<Recorder> mAdapter;
- privateList<Recorder> mDatas =newArrayList<MainActivity.Recorder>();
- privateAudioRecorderButton mAudioRecorderButton;
- privateView animView;
- @Override
- protectedvoid 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.setAudioFinishRecorderListener(newAudioFinishRecorderListener(){
- publicvoid onFinish(float seconds,String filePath){
- Recorder recorder =newRecorder(seconds, filePath);
- mDatas.add(recorder);
- mAdapter.notifyDataSetChanged();//通知更新的内容
- mListView.setSelection(mDatas.size()-1);//将lisview设置为最后一个
- }
- });
- mAdapter =newRecoderAdapter(this, mDatas);
- mListView.setAdapter(mAdapter);
- //listView的item点击事件
- mListView.setOnItemClickListener(newOnItemClickListener(){
- publicvoid 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();
- // 播放录音
- MediaManager.playSound(mDatas.get(position).filePath,newMediaPlayer.OnCompletionListener(){
- publicvoid onCompletion(MediaPlayer mp){
- animView.setBackgroundResource(R.drawable.adj);
- }
- });
- }
- });
- }
- @Override
- protectedvoid onPause(){
- super.onPause();
- MediaManager.pause();
- }
- @Override
- protectedvoid onResume(){
- super.onResume();
- MediaManager.resume();
- }
- @Override
- protectedvoid onDestroy(){
- super.onDestroy();
- MediaManager.release();
- }
- classRecorder{
- float time;
- String filePath;
- publicRecorder(float time,String filePath){
- super();
- this.time = time;
- this.filePath = filePath;
- }
- publicfloat getTime(){
- return time;
- }
- publicvoid setTime(float time){
- this.time = time;
- }
- publicString getFilePath(){
- return filePath;
- }
- publicvoid setFilePath(String filePath){
- this.filePath = filePath;
- }
- }
- }
MediaManager.java
- package com.xuliugen.weichat;
- import android.media.AudioManager;
- import android.media.MediaPlayer;
- import android.media.MediaPlayer.OnCompletionListener;
- import android.media.MediaPlayer.OnErrorListener;
- publicclassMediaManager{
- privatestaticMediaPlayer mMediaPlayer;
- privatestaticboolean isPause;
- /** * 播放音乐 * @param filePath * @param onCompletionListener */
- publicstaticvoid playSound(String filePath,OnCompletionListener onCompletionListener){
- if(mMediaPlayer ==null){
- mMediaPlayer =newMediaPlayer();
- //设置一个error监听器
- mMediaPlayer.setOnErrorListener(newOnErrorListener(){
- publicboolean onError(MediaPlayer arg0,int arg1,int arg2){
- mMediaPlayer.reset();
- returnfalse;
- }
- });
- }else{
- mMediaPlayer.reset();
- }
- try{
- mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
- mMediaPlayer.setOnCompletionListener(onCompletionListener);
- mMediaPlayer.setDataSource(filePath);
- mMediaPlayer.prepare();
- mMediaPlayer.start();
- }catch(Exception e){
- }
- }
- /** * 暂停播放 */
- publicstaticvoid pause(){
- if(mMediaPlayer !=null&& mMediaPlayer.isPlaying()){//正在播放的时候
- mMediaPlayer.pause();
- isPause =true;
- }
- }
- /** * 当前是isPause状态 */
- publicstaticvoid resume(){
- if(mMediaPlayer !=null&& isPause){
- mMediaPlayer.start();
- isPause =false;
- }
- }
- /** * 释放资源 */
- publicstaticvoid release(){
- if(mMediaPlayer !=null){
- mMediaPlayer.release();
- mMediaPlayer =null;
- }
- }
- }
RecoderAdapter.java
- package com.xuliugen.weichat;
- import java.util.List;
- import android.content.Context;
- import android.util.DisplayMetrics;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.view.WindowManager;
- import android.widget.ArrayAdapter;
- import android.widget.TextView;
- import com.xuliugen.weichat.MainActivity.Recorder;
- publicclassRecoderAdapterextendsArrayAdapter<Recorder>{
- privateContext mContext;
- privateList<Recorder> mDatas;
- privateint mMinItemWidth;//最小的item宽度
- privateint mMaxItemWidth;//最大的item宽度
- privateLayoutInflater mInflater;
- publicRecoderAdapter(Context context,List<Recorder> datas){
- super(context,-1, datas);
- mContext = context;
- mDatas = datas;
- //获取屏幕的宽度
- WindowManager wm =(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- DisplayMetrics outMetrics =newDisplayMetrics();
- wm.getDefaultDisplay().getMetrics(outMetrics);
- mMaxItemWidth =(int)(outMetrics.widthPixels *0.7f);
- mMinItemWidth =(int)(outMetrics.widthPixels *0.15f);
- mInflater =LayoutInflater.from(context);
- }
- /** * 定义一个ViewHolder */
- privateclassViewHolder{
- TextView seconds;// 时间
- View length;// 长度
- }
- @Override
- publicView getView(int position,View convertView,ViewGroup parent){
- ViewHolder holder =null;
- if(convertView ==null){
- convertView = mInflater.inflate(R.layout.item_recoder, parent,false);
- holder =newViewHolder();
- holder.seconds =(TextView) convertView.findViewById(R.id.id_recoder_time);
- holder.length = convertView.findViewById(R.id.id_recoder_lenght);
- convertView.setTag(holder);
- }else{
- holder =(ViewHolder) convertView.getTag();
- }
- holder.seconds.setText(Math.round(getItem(position).time)+"\"");
- ViewGroup.LayoutParams lp = holder.length.getLayoutParams();
- lp.width =(int)(mMinItemWidth +(mMaxItemWidth /60f)* getItem(position).time);
- return convertView;
- }
- }