Android模仿微信语音聊天功能-IT蓝豹
2015-10-16 14:17
656 查看
项目效果如下:
代码如下:
伦理片 http://www.dotdy.com/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;
}
}
相关文章推荐
- 新版支付宝能否革了微信的命
- 微信支付出现支付请求参数错误,请核实再试或交易出错,请稍后再试的可能原因
- 微信Android资源混淆打包工具,如何让应用安装包立减1M
- 微渠道发展 BAE交通运输平台和java呼声,微信mysql数据库开发实例 --图文开发教程
- 微信API(还能发邮件,短信,IM聊天)
- 浙江工商学院电信学院微信平台
- 小程序反应大问题
- 给初学者提供的PHP文件上传小程序
- 微信模板开发及开发流程
- 微信公众号接入JS SDK问题小结
- 微信公众号运营辅助工具汇总
- 微信公众平台昵称乱码emoji表情过滤
- 微信JS SDK Demo
- 微信分享实现及成功回调注意事项(一)
- 微信支付-返回签名错误
- 微信硬件平台wifi设备通信第一篇
- 微信入口也不是万能的,至少电商、理财和O2O这三类它伺候不了
- 微信公众号
- 微信硬件平台开发前。。。
- 微信开发中的另一个坑:不能重复提交订单