錄音效果及Touch事件的分發

最近做一個項目,有類似於微信和按住錄音,鬆開停止錄音的效果,這些類似於錄音的功能很容易做,但是由於自己的大意,那個觸摸事件的觸摸點的採集總是有問題,煩躁了好幾天,今天總算將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的監聽機制。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章