最近做一個項目,有類似於微信和按住錄音,鬆開停止錄音的效果,這些類似於錄音的功能很容易做,但是由於自己的大意,那個觸摸事件的觸摸點的採集總是有問題,煩躁了好幾天,今天總算將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的調用
PopupWindow的使用:
1.初始化popupWindow:
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));
}
2.顯示popupWindow:
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);
}
3.隱藏popupWindow:
Code:
if (popupWindow.isShowing())
popupWindow.dismiss();
效果圖:
由於上面的圖不是美麗的美工小美女做的,是從微信裏面反編譯出來的,有點醜,不過不打緊,目前功能第一,以後纔是界面的事。
getMaxAmplitude的調用:
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);
改變主界面的handler的具體處理:
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;
}
}
};
三:Touch事件的處理:
這是關鍵的一步,老子就是在這一步被卡了幾天,搞得欲仙欲死。其實一切代碼邏輯都正常,關鍵老子爲了屏幕適配加在佈局文件中加了一個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的監聽機制。