Android仿微信语音聊天界面设计
程序员文章站
2024-02-28 12:37:40
有段时间没有看视频了,昨天晚上抽了点空时间,又看了下鸿洋大神的视频教程,又抽时间写了个学习记录。代码和老师讲的基本一样,网上也有很多相同的博客。我只是在androidstu...
有段时间没有看视频了,昨天晚上抽了点空时间,又看了下鸿洋大神的视频教程,又抽时间写了个学习记录。代码和老师讲的基本一样,网上也有很多相同的博客。我只是在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.jb51.net/201611/yuanma/androidwxchat(jb51.net).rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。