Welcome

首页 / 移动开发 / Android / Android仿微信语音聊天功能

本文实例讲述了Android仿微信语音聊天功能代码。分享给大家供大家参考。具体如下:
项目效果如下:




具体代码如下:
AudioManager.java

package com.xuliugen.weichat;import java.io.File;import java.io.IOException;import java.util.UUID;import android.media.MediaRecorder;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(); } String fileName = generateFileName(); File file = new File(dir, fileName); mCurrentFilePath =file.getAbsolutePath(); mMediaRecorder = new MediaRecorder(); // 设置输出文件 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();} } /*** 随机生成文件的名称*/ private String generateFileName() {return UUID.randomUUID().toString() + ".amr"; } public int getVoiceLevel(int maxlevel) {if (isPrepare) { try {// mMediaRecorder.getMaxAmplitude() 1~32767return maxlevel * mMediaRecorder.getMaxAmplitude() / 32768 + 1; } catch (Exception e) { }}return 1; } /*** 释放资源*/ public void release() {//mMediaRecorder.stop();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; }}
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;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; private float mTime; // 是否触发longClick private 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; /** 获取音量大小的线程*/ private Runnable mGetVoiceLevelRunnable = new Runnable() {public void run() { while (isRecording) {try { Thread.sleep(100); mTime += 0.1f; mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);} 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);} }; /*** 以下2个方法是构造方法*/ public AudioRecorderButton(Context context, AttributeSet attrs) {super(context, attrs);mDialogManager = new DialogManager(context);String dir = "/storage/sdcard0/my_weixin";//String dir = Environment.getExternalStorageDirectory()+"/my_weixin";mAudioManager = AudioManager.getInstance(dir);mAudioManager.setOnAudioStateListener(new 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); } /*** 录音完成后的回调*/ public interface AudioFinishRecorderListener {void onFinish(float seconds, String filePath); } private AudioFinishRecorderListener audioFinishRecorderListener; public void setAudioFinishRecorderListener(AudioFinishRecorderListener listener) {audioFinishRecorderListener = listener; } /*** 屏幕的触摸事件*/ @Override public boolean onTouchEvent(MotionEvent event) {int action = event.getAction();int x = (int) event.getX();// 获得x轴坐标int y = (int) event.getY();// 获得y轴坐标switch (action) {case MotionEvent.ACTION_DOWN: changeState(STATE_RECORDING); break;case MotionEvent.ACTION_MOVE: if (isRecording) {// 如果想要取消,根据x,y的坐标看是否需要取消if (wantToCancle(x, y)) { changeState(STATE_WANT_TO_CANCEL);} else { changeState(STATE_RECORDING);} } break;case MotionEvent.ACTION_UP: if (!mReady) {reset();return super.onTouchEvent(event); } if (!isRecording || mTime < 0.6f) {mDialogManager.tooShort();mAudioManager.cancel();mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1000);// 延迟显示对话框 } 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(); 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; } /*** 改变*/ 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_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 **/public class DialogManager { private AlertDialog.Builder builder; private ImageView mIcon; private ImageView mVoice; private TextView mLable; private Context mContext;private AlertDialog dialog;//用于取消AlertDialog.Builder /*** 构造方法 传入上下文*/ public DialogManager(Context context) {this.mContext = context; } // 显示录音的对话框 public void showRecordingDialog() {builder = new AlertDialog.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(); } public void 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("手指上滑,取消发送");} } // 显示想取消的对话框 public void 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("松开手指,取消发送");} } // 显示时间过短的对话框 public void 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("录音时间过短");} } // 显示取消的对话框 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);// 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;public class MainActivity extends Activity { private ListView mListView; private ArrayAdapter<Recorder> mAdapter; private List<Recorder> mDatas = new ArrayList<MainActivity.Recorder>(); private AudioRecorderButton mAudioRecorderButton; private View animView; @Override protected 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.setAudioFinishRecorderListener(new AudioFinishRecorderListener() { public void onFinish(float seconds, String filePath) {Recorder recorder = new Recorder(seconds, filePath);mDatas.add(recorder);mAdapter.notifyDataSetChanged(); //通知更新的内容mListView.setSelection(mDatas.size() - 1); //将lisview设置为最后一个 }});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();// 播放录音MediaManager.playSound(mDatas.get(position).filePath,new MediaPlayer.OnCompletionListener() {public void onCompletion(MediaPlayer mp) { animView.setBackgroundResource(R.drawable.adj);} }); }}); } @Override protected void onPause() {super.onPause();MediaManager.pause(); } @Override protected void onResume() {super.onResume();MediaManager.resume(); } @Override protected void onDestroy() {super.onDestroy();MediaManager.release(); } class Recorder {float time;String filePath;public Recorder(float time, String filePath) { super(); this.time = time; this.filePath = filePath;}public float getTime() { return time;}public void setTime(float time) { this.time = time;}public String getFilePath() { return filePath;}public void 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;public class MediaManager { private static MediaPlayer mMediaPlayer;private static boolean isPause; /*** 播放音乐* @param filePath* @param onCompletionListener*/ public static void playSound(String filePath,OnCompletionListener onCompletionListener) {if (mMediaPlayer == null) { mMediaPlayer = new MediaPlayer(); //设置一个error监听器 mMediaPlayer.setOnErrorListener(new OnErrorListener() {public boolean onError(MediaPlayer arg0, int arg1, int arg2) { mMediaPlayer.reset(); return false;} });} else { mMediaPlayer.reset();}try { mMediaPlayer.setAudioStreamType(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;} } /*** 当前是isPause状态*/ public static void resume() {if (mMediaPlayer != null && isPause) {mMediaPlayer.start(); isPause = false;} } /*** 释放资源*/ public static void 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;public class RecoderAdapter extends ArrayAdapter<Recorder> { private Context mContext; private List<Recorder> mDatas; private int mMinItemWidth; //最小的item宽度 private int mMaxItemWidth; //最大的item宽度 private LayoutInflater mInflater; public RecoderAdapter(Context context, List<Recorder> datas) {super(context, -1, datas);mContext = context;mDatas = datas;//获取屏幕的宽度WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);DisplayMetrics outMetrics = new DisplayMetrics();wm.getDefaultDisplay().getMetrics(outMetrics);mMaxItemWidth = (int) (outMetrics.widthPixels * 0.7f);mMinItemWidth = (int) (outMetrics.widthPixels * 0.15f);mInflater = LayoutInflater.from(context); } /*** 定义一个ViewHolder*/ private class ViewHolder {TextView seconds; // 时间View length; // 长度 } @Override public View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder = null;if (convertView == null) { convertView = mInflater.inflate(R.layout.item_recoder, parent,false); holder = new ViewHolder(); 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; }}
以上就是本文的全部内容,希望对大家的学习有所帮助。