最近做一个项目,有类似于微信和按住录音,松开停止录音的效果,这些类似于录音的功能很容易做,但是由于自己的大意,那个触摸事件的触摸点的采集总是有问题,烦躁了好几天,今天总算将bug抓了出来,特以此文,祭奠那死去的bug.
这个功能比较简单,直接贴出Code:
/** * 录音功能 */ private void recordSound() { try { recorder = new MediaRecorder(); // 创建记录器对象 recorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频源, 麦克风 recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_WB); // 设置输出格式, 3gp recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_WB); // 设置编码 filePath = Environment.getExternalStorageDirectory() + "/" + System.currentTimeMillis() + ".amr"; recorder.setOutputFile(filePath); // 设置文件路径 recorder.prepare(); // 准备 Log.i(TAG, "开始录音。。。。。。。"); //TODO:检测声音的大小 recorder.start(); // 开始 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
/** * 停止录音的功能 */ private void stopRecord() { if (recorder != null) { endTime = System.currentTimeMillis(); Log.i(TAG, "停止录音endTime:" + endTime); recorder.stop();// 停止 recorder.release();// 释放 recorder = null;// 回收 } }
这里用到了PopupWindow及关键方法getMaxAmplitude的调用
Code:
/** * 创建popupWindow */ private void createPopupWindow() { if(popupWindow != null) return; View view = View.inflate(getApplicationContext(), R.layout.popupwindow_record_sound, null); popupWindow = new PopupWindow(view); iv_rcd_hint_anim = (ImageView) view.findViewById(R.id.iv_rcd_hint_anim); popupWindow.setWidth(DensityUtil.dip2px(RecordSoundActivity.this, 150)); popupWindow.setHeight(DensityUtil.dip2px(RecordSoundActivity.this, 200)); }
Code:
/** * 展示popupWindow */ private void showPopupWindow(){ if(popupWindow.isShowing()) return; int xoff = (rl_recordSound.getWidth()-popupWindow.getWidth())/2; int yoff = rl_recordSound.getHeight()+popupWindow.getHeight()-100; popupWindow.showAsDropDown(rl_recordSound, xoff , -yoff); }
Code:
if (popupWindow.isShowing()) popupWindow.dismiss();
由于上面的图不是美丽的美工小美女做的,是从微信里面反编译出来的,有点丑,不过不打紧,目前功能第一,以后才是界面的事。
1.由于音量是在不断变化中的,所以getMaxAmplitude不是只执行一次,应该在一个线程中执行,要每隔一段时间采样一次,为了简单起见,用void java.util.Timer.schedule(TimerTask task, long delay, long period)
每隔200ms采样一次,然后将采集到的数据handler出去交给UI Thread处理以便让UI界面有动态变化的效果,这里需要注意的问题是:①当界面被另外一个界面覆盖后②:停止录音时 这两个情况都需要将录音那个对象回收掉:
recorder.stop();// 停止
recorder.release();// 释放
recorder = null;// 回收
但在getMaxAmplitude是在线程中执行的,很可能就在recorder刚被回收后的那一刻,线程执行到了recorder.getMaxAmplitude();这个方法,会报空指针异常:NullPointerException,还有其他什么的乱七八糟的破玩意,所以捕获了一下异常的老子。
Code:
new Timer().schedule(new TimerTask() { @Override public void run() { // TODO Auto-generated method stub try { if(recorder != null){//下面要加锁!!!! synchronized (this) { int maxAmplitude = recorder.getMaxAmplitude(); Log.i(TAG,"当前声音---音量:" + maxAmplitude); int index = maxAmplitude/200; if(index>6) index=6; Message msg = new Message(); msg.what = GET_BG_INDEX; msg.obj = index; mHandler.sendMessage(msg); } } } catch (Exception e) { e.printStackTrace(); Log.i(TAG,"没有获取到音量"); } } }, 0, 200);
private Handler mHandler = new Handler(){ public void handleMessage(Message msg) { switch (msg.what) { case GET_BG_INDEX: int index = (Integer) msg.obj; Log.i(TAG,"index:"+index); iv_rcd_hint_anim.setBackgroundResource(rcd_amin_bg[index]); break; } } };
这是关键的一步,老子就是在这一步被卡了几天,搞得欲仙欲死。其实一切代码逻辑都正常,关键老子为了屏幕适配加在布局文件中加了一个ScrollView,然后在自定义了一个RelativeLayout,然后写回调来处理触摸事件,结果Action_Move只能采样一小会,根本不能在你手指不规则移动时一直采样,这就令人非常蛋疼了,搞得我一直以为我Touch事件哪里出了问题,后来将ScrollView直接去掉,哗,整个世界清静了...
Code:
rl_recordSound.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: createPopupWindow(); iv_recordSound .setBackgroundResource(R.drawable.voice_after); showPopupWindow(); recordSound(); break; case MotionEvent.ACTION_MOVE: float getX = event.getX(); float getY = event.getY(); int left = rl_recordSound.getLeft(); int right = rl_recordSound.getRight(); int top = rl_recordSound.getTop(); int bottom = rl_recordSound.getBottom(); float r=(right-left)/2;//圆半径 float ox=r+left;//圆心x坐标 float oy=r+top;//圆心y坐标 Double L=Math.pow(Math.abs((getX-150)), 2)+Math.pow(Math.abs((getY -150)), 2); L = Math.sqrt(L); if(L>r){ if (popupWindow.isShowing()) popupWindow.dismiss(); iv_recordSound.setBackgroundResource(R.drawable.voice_before); stopRecord(); toNextActivity(); break; } break; case MotionEvent.ACTION_UP: iv_recordSound .setBackgroundResource(R.drawable.voice_before); if (popupWindow.isShowing()) popupWindow.dismiss(); stopRecord(); toNextActivity(); break; } return true; } });
布局没其他要讲的,只要没有被ScrollView等类似的玩意嵌套住就ok了,
①:这个界面的屏幕的适配<问题不严重>
②:ScrollView与其他View的嵌套使用问题
③:ScrollView的监听机制。
录音效果及Touch事件的分发,布布扣,bubuko.com
原文:http://blog.csdn.net/sunalongl/article/details/23543425