Android仿微信语音聊天界面设计
有段时间没有看视频了,昨天晚上抽了点空时间,又看了下鸿洋大神的视频教程,又抽时间写了个学习记录。代码和老师讲的基本一样,网上也有很多相同的博客。我只是在AndroidStudio环境下写的。
—-主界面代码——
publicclassMainActivityextendsActivity{ privateListViewmListView; privateArrayAdapter<Recorder>mAdapter; privateList<Recorder>mDatas=newArrayList<Recorder>(); privateAudioRecorderButtonmAudioRecorderButton; privateViewanimView; @Override protectedvoidonCreate(BundlesavedInstanceState){ 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(newAudioRecorderButton.AudioFinishRecorderCallBack(){ publicvoidonFinish(floatseconds,StringfilePath){ Recorderrecorder=newRecorder(seconds,filePath); mDatas.add(recorder); //更新数据 mAdapter.notifyDataSetChanged(); //设置位置 mListView.setSelection(mDatas.size()-1); } }); mAdapter=newRecoderAdapter(this,mDatas); mListView.setAdapter(mAdapter); //listView的item点击事件 mListView.setOnItemClickListener(newOnItemClickListener(){ publicvoidonItemClick(AdapterView<?>arg0,Viewview,intposition,longid){ //声音播放动画 if(animView!=null){ animView.setBackgroundResource(R.drawable.adj); animView=null; } animView=view.findViewById(R.id.id_recoder_anim); animView.setBackgroundResource(R.drawable.play_anim); AnimationDrawableanimation=(AnimationDrawable)animView.getBackground(); animation.start(); //播放录音 MediaPlayerManager.playSound(mDatas.get(position).filePath,newMediaPlayer.OnCompletionListener(){ publicvoidonCompletion(MediaPlayermp){ //播放完成后修改图片 animView.setBackgroundResource(R.drawable.adj); } }); } }); } @Override protectedvoidonPause(){ super.onPause(); MediaPlayerManager.pause(); } @Override protectedvoidonResume(){ super.onResume(); MediaPlayerManager.resume(); } @Override protectedvoidonDestroy(){ super.onDestroy(); MediaPlayerManager.release(); }
—自定义Button——-
/** *@param *@authorldm *@description自定义Button *@time2016/6/259:26 */ publicclassAudioRecorderButtonextendsButton{ //按钮正常状态(默认状态) privatestaticfinalintSTATE_NORMAL=1; //正在录音状态 privatestaticfinalintSTATE_RECORDING=2; //录音取消状态 privatestaticfinalintSTATE_CANCEL=3; //记录当前状态 privateintmCurrentState=STATE_NORMAL; //是否开始录音标志 privatebooleanisRecording=false; //判断在Button上滑动距离,以判断是否取消 privatestaticfinalintDISTANCE_Y_CANCEL=50; //对话框管理工具类 privateDialogManagermDialogManager; //录音管理工具类 privateAudioManagermAudioManager; //记录录音时间 privatefloatmTime; //是否触发longClick privatebooleanmReady; //录音准备 privatestaticfinalintMSG_AUDIO_PREPARED=0x110; //音量发生改变 privatestaticfinalintMSG_VOICE_CHANGED=0x111; //取消提示对话框 privatestaticfinalintMSG_DIALOG_DIMISS=0x112; /** *@description获取音量大小的线程 *@authorldm *@time2016/6/259:30 *@param */ privateRunnablemGetVoiceLevelRunnable=newRunnable(){ publicvoidrun(){ while(isRecording){//判断正在录音 try{ Thread.sleep(100); mTime+=0.1f;//录音时间计算 mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);//每0.1秒发送消息 }catch(InterruptedExceptione){ e.printStackTrace(); } } } }; privateHandlermHandler=newHandler(){ @Override publicvoidhandleMessage(Messagemsg){ switch(msg.what){ caseMSG_AUDIO_PREPARED: //显示对话框 mDialogManager.showRecordingDialog(); isRecording=true; //开启一个线程计算录音时间 newThread(mGetVoiceLevelRunnable).start(); break; caseMSG_VOICE_CHANGED: //更新声音 mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7)); break; caseMSG_DIALOG_DIMISS: //取消对话框 mDialogManager.dimissDialog(); break; } super.handleMessage(msg); } }; publicAudioRecorderButton(Contextcontext,AttributeSetattrs){ super(context,attrs); mDialogManager=newDialogManager(context); //录音文件存放地址 Stringdir=Environment.getExternalStorageDirectory()+"/ldm_voice"; mAudioManager=AudioManager.getInstance(dir); mAudioManager.setOnAudioStateListener(newAudioManager.AudioStateListener(){ publicvoidwellPrepared(){ mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED); } }); //由于这个类是button所以在构造方法中添加监听事件 setOnLongClickListener(newOnLongClickListener(){ publicbooleanonLongClick(Viewv){ mReady=true; mAudioManager.prepareAudio(); returnfalse; } }); } publicAudioRecorderButton(Contextcontext){ this(context,null); } /** *@description录音完成后的回调 *@authorldm *@time2016/6/2511:18 *@param */ publicinterfaceAudioFinishRecorderCallBack{ voidonFinish(floatseconds,StringfilePath); } privateAudioFinishRecorderCallBackfinishRecorderCallBack; publicvoidsetFinishRecorderCallBack(AudioFinishRecorderCallBacklistener){ finishRecorderCallBack=listener; } /** *@param *@description处理Button的OnTouchEvent事件 *@authorldm *@time2016/6/259:35 */ @Override publicbooleanonTouchEvent(MotionEventevent){ //获取TouchEvent状态 intaction=event.getAction(); //获得x轴坐标 intx=(int)event.getX(); //获得y轴坐标 inty=(int)event.getY(); switch(action){ caseMotionEvent.ACTION_DOWN://手指按下 changeState(STATE_RECORDING); break; caseMotionEvent.ACTION_MOVE://手指移动 if(isRecording){ //根据x,y的坐标判断是否需要取消 if(wantToCancle(x,y)){ changeState(STATE_CANCEL); }else{ changeState(STATE_RECORDING); } } break; caseMotionEvent.ACTION_UP://手指放开 if(!mReady){ reset(); returnsuper.onTouchEvent(event); } if(!isRecording||mTime<0.6f){//如果时间少于0.6s,则提示录音过短 mDialogManager.tooShort(); mAudioManager.cancel(); //延迟显示对话框 mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS,1000); }elseif(mCurrentState==STATE_RECORDING){ //如果状态为正在录音,则结束录制 mDialogManager.dimissDialog(); mAudioManager.release(); if(finishRecorderCallBack!=null){ finishRecorderCallBack.onFinish(mTime,mAudioManager.getCurrentFilePath()); } }elseif(mCurrentState==STATE_CANCEL){//想要取消 mDialogManager.dimissDialog(); mAudioManager.cancel(); } reset(); break; } returnsuper.onTouchEvent(event); } /** *恢复状态及标志位 */ privatevoidreset(){ isRecording=false; mTime=0; mReady=false; changeState(STATE_NORMAL); } privatebooleanwantToCancle(intx,inty){ //超过按钮的宽度 if(x<0||x>getWidth()){ returntrue; } //超过按钮的高度 if(y<-DISTANCE_Y_CANCEL||y>getHeight()+DISTANCE_Y_CANCEL){ returntrue; } returnfalse; } /** *@param *@description根据状态改变Button显示 *@authorldm *@time2016/6/259:36 */ privatevoidchangeState(intstate){ if(mCurrentState!=state){ mCurrentState=state; switch(state){ caseSTATE_NORMAL: setBackgroundResource(R.drawable.btn_recorder_normal); setText(R.string.str_recorder_normal); break; caseSTATE_RECORDING: setBackgroundResource(R.drawable.btn_recorder_recording); setText(R.string.str_recorder_recording); if(isRecording){ mDialogManager.recording(); } break; caseSTATE_CANCEL: setBackgroundResource(R.drawable.btn_recorder_recording); mDialogManager.wantToCancel(); setText(R.string.str_recorder_want_cancel); break; } } } }
—-对话框管理工具类——
/** *@description对话框管理工具类 *@authorldm *@time2016/6/2511:53 *@param */ publicclassDialogManager{ //弹出对话框 privateDialogmDialog; //录音图标 privateImageViewmIcon; //音量显示图标 privateImageViewmVoice; //对话框上提示文字 privateTextViewmLable; //上下文对象 privateContextmContext; publicDialogManager(Contextcontext){ this.mContext=context; } /** *@param *@description显示对话框 *@authorldm *@time2016/6/259:56 */ publicvoidshowRecordingDialog(){ //根据指定sytle实例化Dialog mDialog=newDialog(mContext,R.style.AudioDialog); LayoutInflaterinflater=LayoutInflater.from(mContext); Viewview=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正在录音状态的对话框 *@authorldm *@time2016/6/2510:08 */ publicvoidrecording(){ 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取消录音状态对话框 *@authorldm *@time2016/6/2510:08 */ publicvoidwantToCancel(){ 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时间过短提示的对话框 *@authorldm *@time2016/6/2510:09 */ publicvoidtooShort(){ 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 *@authorldm *@time2016/6/25取消(关闭)对话框 */ publicvoiddimissDialog(){ if(mDialog!=null&&mDialog.isShowing()){//显示状态 mDialog.dismiss(); mDialog=null; } } //显示更新音量级别的对话框 publicvoidupdateVoiceLevel(intlevel){ if(mDialog!=null&&mDialog.isShowing()){//显示状态 mIcon.setVisibility(View.VISIBLE); mVoice.setVisibility(View.VISIBLE); mLable.setVisibility(View.VISIBLE); //设置图片的id,我们放在drawable中的声音图片是以v+数字格式的 intresId=mContext.getResources().getIdentifier("v"+level,"drawable",mContext.getPackageName()); mVoice.setImageResource(resId); } } }
—-声音播放工具类——
/** *@param *@authorldm *@description播放声音工具类 *@time2016/6/2511:29 */ publicclassMediaPlayerManager{ //播放音频API类:MediaPlayer privatestaticMediaPlayermMediaPlayer; //是否暂停 privatestaticbooleanisPause; /** *@param *filePath:文件路径 *onCompletionListener:播放完成监听 *@description播放声音 *@authorldm *@time2016/6/2511:30 */ publicstaticvoidplaySound(StringfilePath,MediaPlayer.OnCompletionListeneronCompletionListener){ if(mMediaPlayer==null){ mMediaPlayer=newMediaPlayer(); //设置一个error监听器 mMediaPlayer.setOnErrorListener(newMediaPlayer.OnErrorListener(){ publicbooleanonError(MediaPlayerarg0,intarg1,intarg2){ mMediaPlayer.reset(); returnfalse; } }); }else{ mMediaPlayer.reset(); } try{ mMediaPlayer.setAudioStreamType(android.media.AudioManager.STREAM_MUSIC); mMediaPlayer.setOnCompletionListener(onCompletionListener); mMediaPlayer.setDataSource(filePath); mMediaPlayer.prepare(); mMediaPlayer.start(); }catch(Exceptione){ } } /** *@param *@description暂停播放 *@authorldm *@time2016/6/2511:31 */ publicstaticvoidpause(){ if(mMediaPlayer!=null&&mMediaPlayer.isPlaying()){//正在播放的时候 mMediaPlayer.pause(); isPause=true; } } /** *@param *@description重新播放 *@authorldm *@time2016/6/2511:31 */ publicstaticvoidresume(){ if(mMediaPlayer!=null&&isPause){ mMediaPlayer.start(); isPause=false; } } /** *@param *@description释放操作 *@authorldm *@time2016/6/2511:32 */ publicstaticvoidrelease(){ if(mMediaPlayer!=null){ mMediaPlayer.release(); mMediaPlayer=null; } }
—–录音操作工具类—–
/** *@param *@authorldm *@description录音管理工具类 *@time2016/6/259:39 */ publicclassAudioManager{ //AudioRecord:主要是实现边录边播(AudioRecord+AudioTrack)以及对音频的实时处理。 //优点:可以语音实时处理,可以实现各种音频的封装 privateMediaRecordermMediaRecorder; //录音文件 privateStringmDir; //当前录音文件目录 privateStringmCurrentFilePath; //单例模式 privatestaticAudioManagermInstance; //是否准备好 privatebooleanisPrepare; //私有构造方法 privateAudioManager(Stringdir){ mDir=dir; } //对外公布获取实例的方法 publicstaticAudioManagergetInstance(Stringdir){ if(mInstance==null){ synchronized(AudioManager.class){ if(mInstance==null){ mInstance=newAudioManager(dir); } } } returnmInstance; } /** *@param *@authorldm *@description录音准备工作完成回调接口 *@time2016/6/2511:14 */ publicinterfaceAudioStateListener{ voidwellPrepared(); } publicAudioStateListenermAudioStateListener; /** *@param *@description供外部类调用的设置回调方法 *@authorldm *@time2016/6/2511:14 */ publicvoidsetOnAudioStateListener(AudioStateListenerlistener){ mAudioStateListener=listener; } /** *@param *@description录音准备工作 *@authorldm *@time2016/6/2511:15 */ publicvoidprepareAudio(){ try{ isPrepare=false; Filedir=newFile(mDir); if(!dir.exists()){ dir.mkdirs();//文件不存在,则创建文件 } StringfileName=generateFileName(); Filefile=newFile(dir,fileName); mCurrentFilePath=file.getAbsolutePath(); mMediaRecorder=newMediaRecorder(); //设置输出文件路径 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(Exceptione){ e.printStackTrace(); } } /** *@param *@description随机生成录音文件名称 *@authorldm *@time2016/6/25、 */ privateStringgenerateFileName(){ //随机生成不同的UUID returnUUID.randomUUID().toString()+".amr"; } /** *@param *@description获取音量值 *@authorldm *@time2016/6/259:49 */ publicintgetVoiceLevel(intmaxlevel){ if(isPrepare){ try{ //getMaxAmplitude返回的数值最大是32767 returnmaxlevel*mMediaRecorder.getMaxAmplitude()/32768+1;//返回结果1-7之间 }catch(Exceptione){ e.printStackTrace(); } } return1; } /** *@param *@description释放资源 *@authorldm *@time2016/6/259:50 */ publicvoidrelease(){ mMediaRecorder.stop(); mMediaRecorder.reset(); mMediaRecorder=null; } /** *@param *@description录音取消 *@authorldm *@time2016/6/259:51 */ publicvoidcancel(){ release(); if(mCurrentFilePath!=null){ //取消录音后删除对应文件 Filefile=newFile(mCurrentFilePath); file.delete(); mCurrentFilePath=null; } } /** *@param *@description获取当前文件路径 *@authorldm *@time2016/6/259:51 */ publicStringgetCurrentFilePath(){ returnmCurrentFilePath; } }
代码中有注释,就不贴图了,和微信语音聊天界面一样的,所以叫仿微信嘛,呵呵。运行了也可以看到效果。所有代码可以从这里下载:http://xiazai.jb51.net/201611/yuanma/AndroidWXchat(jb51.net).rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。