首页 > 系统 > Android > 正文

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

2019-12-12 04:31:50
字体:
来源:转载
供稿:网友

有段时间没有看视频了,昨天晚上抽了点空时间,又看了下鸿洋大神的视频教程,又抽时间写了个学习记录。代码和老师讲的基本一样,网上也有很多相同的博客。我只是在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; @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.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);     }    });   }  }); } @Override protected void onPause() {  super.onPause();  MediaPlayerManager.pause(); } @Override protected void onResume() {  super.onResume();  MediaPlayerManager.resume(); } @Override protected 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; // 是否触发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; /**  * @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() {  @Override  public 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  */ @Override public 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实例化Dialog  mDialog = 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类:MediaPlayer private 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_AMR   mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);   // 设置音频编码为AMR_NB   mMediaRecorder.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() {  //随机生成不同的UUID  return UUID.randomUUID().toString() + ".amr"; } /**  * @param  * @description 获取音量值  * @author ldm  * @time 2016/6/25 9:49  */ public int getVoiceLevel(int maxlevel) {  if (isPrepare) {   try {    // getMaxAmplitude返回的数值最大是32767    return 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://xiazai.VeVB.COm/201611/yuanma/AndroidWXchat(VeVB.COm).rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表