Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 仿微信語音識別

Android 仿微信語音識別

編輯:關於Android編程

參考於:Android模仿微信語音聊天功能,這代碼跑起來有問題,自己改動了一下,基本上沒什麼大問題

先貼下效果圖

\\\

 

1、三個布局文件

activity_main.xml


    
    

    <framelayout android:layout_height="wrap_content" android:layout_width="match_parent">

        

        
        

        
    </framelayout>


dialog_manger.xml


    

        

        
    

    


item_layout


    

    <framelayout android:background="@drawable/chatto_bg_focused" android:id="@+id/recorder_length" android:layout_centervertical="true" android:layout_height="wrap_content" android:layout_toleftof="@id/item_icon" android:layout_width="wrap_content">
        
      
       
    </framelayout>
    
    


2.自定義的類 (1)DialogManger
package com.nickming.view;

import com.example.weixin_record.R;

import android.app.Dialog;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
/**
 * 
 * @ClassName:  DialogManager   
 * @Description:對話框管理類
 * @author:  張  維
 * @date:   2016-5-23 下午4:56:03   
 *
 */
public class DialogManager {

	/**
	 * 以下為dialog的初始化控件,包括其中的布局文件
	 */

	private Dialog mDialog;

	private ImageView mIcon;
	private ImageView mVoice;

	private TextView mLable;

	private Context mContext;

	public DialogManager(Context context) {
		mContext = context;
	}

	public void showRecordingDialog() {

		mDialog = new Dialog(mContext,R.style.Theme_audioDialog);
		// 用layoutinflater來引用布局
		LayoutInflater inflater = LayoutInflater.from(mContext);
		View view = inflater.inflate(R.layout.dialog_manager, null);
		mDialog.setContentView(view);
		
		
		mIcon = (ImageView) mDialog.findViewById(R.id.dialog_icon);
		mVoice = (ImageView) mDialog.findViewById(R.id.dialog_voice);
		mVoice.setBackgroundResource(R.drawable.play02);
		AnimationDrawable drawable = (AnimationDrawable) mVoice
				.getBackground();
		drawable.start();
		mLable = (TextView) mDialog.findViewById(R.id.recorder_dialogtext);
		mDialog.show();
		
	}

	/**
	 * 設置正在錄音時的dialog界面
	 */
	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(R.string.shouzhishanghua);
		}
	}

	/**
	 * 取消界面
	 */
	public void wantToCancel() {
		// TODO Auto-generated method stub
		if (mDialog != null && mDialog.isShowing()) {
			mIcon.setVisibility(View.VISIBLE);
			mVoice.setVisibility(View.GONE);
			mLable.setVisibility(View.VISIBLE);

			mIcon.setImageResource(R.drawable.cancel);
			mLable.setText(R.string.want_to_cancle);
		}

	}

	// 時間過短
	public void timeShort() {
		// TODO Auto-generated method stub
		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(R.string.timeshort);
		}

	}

	// 隱藏dialog
	public void dimissDialog() {
		// TODO Auto-generated method stub

		if (mDialog != null && mDialog.isShowing()) {
			mDialog.dismiss();
			mDialog = null;
		}

	}

	public void updateVoiceLevel(int level) {
		// TODO Auto-generated method stub

		if (mDialog != null && mDialog.isShowing()) {

			//先不改變它的默認狀態
//			mIcon.setVisibility(View.VISIBLE);
//			mVoice.setVisibility(View.VISIBLE);
//			mLable.setVisibility(View.VISIBLE);

			//通過level來找到圖片的id,也可以用switch來尋址,但是代碼可能會比較長
			int resId = mContext.getResources().getIdentifier("v" + level,
					"drawable", mContext.getPackageName());
			
			mVoice.setImageResource(resId);
		}

	}

}
(2)AudioRecordButton
package com.nickming.view;

import com.example.weixin_record.R;
import com.example.weixin_record.R.string;
import com.nickming.view.AudioManager.AudioStageListener;

import android.R.bool;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
/**
 * 
 * @ClassName:  AudioRecordButton   
 * @Description:自定義的button按鈕 
 * @author:  張  維
 * @date:   2016-5-23 下午2:13:20   
 *
 */
public class AudioRecordButton extends Button implements AudioStageListener {

	private static final int STATE_NORMAL = 1;
	private static final int STATE_RECORDING = 2;
	private static final int STATE_WANT_TO_CANCEL = 3;

	private static final int DISTANCE_Y_CANCEL = 50;

	private int mCurrentState = STATE_NORMAL;
	// 已經開始錄音
	private boolean isRecording = false;

	private DialogManager mDialogManager;

	private AudioManager mAudioManager;

	private float mTime = 0;
	// 是否觸發了onlongclick,准備好了
	private boolean mReady;

	/**
	 * 先實現兩個參數的構造方法,布局會默認引用這個構造方法, 
	 * 用一個 構造參數的構造方法來引用這個方法 * @param context
	 */

	public AudioRecordButton(Context context) {
		this(context, null);
		// TODO Auto-generated constructor stub
	}

	public AudioRecordButton(Context context, AttributeSet attrs) {
		super(context, attrs);

		mDialogManager = new DialogManager(getContext());

		// 這裡沒有判斷儲存卡是否存在,有空要判斷
		String dir = Environment.getExternalStorageDirectory()
				+ "/temp";
		mAudioManager = AudioManager.getInstance(dir);
		mAudioManager.setOnAudioStageListener(this);
       
		setOnLongClickListener(new OnLongClickListener() {

			@Override
			public boolean onLongClick(View v) {
				// TODO Auto-generated method
				mReady = true;
				mAudioManager.prepareAudio();
				return false;
			}
		});
		
	}
	
	/**
	 * 錄音完成後的回調,回調給activiy,可以獲得mtime和文件的路徑
	 * @author nickming
	 *
	 */
	public interface AudioFinishRecorderListener{
		void onFinished(float mtime,String filePath);
	}
	
	private AudioFinishRecorderListener mListener;
	
	public void setAudioFinishRecorderListener(AudioFinishRecorderListener listener)
	{
		mListener=listener;
	}

	// 獲取音量大小的runnable
	private Runnable mGetVoiceLevelRunnable = new Runnable() {

		@Override
		public void run() {
			// TODO Auto-generated method stub
			while (isRecording) {
				try {
					Thread.sleep(100);
					mTime += 0.1f;
					mhandler.sendEmptyMessage(MSG_VOICE_CHANGE);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	};

	// 准備三個常量
	private static final int MSG_AUDIO_PREPARED = 0X110;
	private static final int MSG_VOICE_CHANGE = 0X111;
	private static final int MSG_DIALOG_DIMISS = 0X112;

	private Handler mhandler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case MSG_AUDIO_PREPARED:
				// 顯示應該是在audio end prepare之後回調
				mDialogManager.showRecordingDialog();
				isRecording = true;
				new Thread(mGetVoiceLevelRunnable).start();

				// 需要開啟一個線程來變換音量
				break;
			case MSG_VOICE_CHANGE:
				mDialogManager.updateVoiceLevel(mAudioManager.getVoiceLevel(7));

				break;
			case MSG_DIALOG_DIMISS:

				break;

			}
		};
	};

	// 在這裡面發送一個handler的消息
	@Override
	public void wellPrepared() {
		// TODO Auto-generated method stub
		mhandler.sendEmptyMessage(MSG_AUDIO_PREPARED);
	}

	/**
	 * 直接復寫這個監聽函數
	 */
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		int action = event.getAction();
		int x = (int) event.getX();
		int y = (int) event.getY();

		switch (action) {
		
		case MotionEvent.ACTION_DOWN://表示用戶開始觸摸.
			changeState(STATE_RECORDING);
			break;
		case MotionEvent.ACTION_MOVE://表示用戶在移動(手指或者其他)

			if (isRecording) {

				// 根據x,y來判斷用戶是否想要取消
				if (wantToCancel(x, y)) {
					changeState(STATE_WANT_TO_CANCEL);
				} else {
					changeState(STATE_RECORDING);
				}

			}

			break;
		case MotionEvent.ACTION_UP://表示用戶抬起了手指 
			// 首先判斷是否有觸發onlongclick事件,沒有的話直接返回reset
			if (!mReady) {
				reset();
				return super.onTouchEvent(event);
			}
			// 如果按的時間太短,還沒准備好或者時間錄制太短,就離開了,則顯示這個dialog
			if (!isRecording || mTime < 0.6f) {
				mDialogManager.timeShort();//取消對話框
				mAudioManager.cancel();
				mhandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1300);// 持續1.3s
			} else if (mCurrentState == STATE_RECORDING) {//正常錄制結束

				mDialogManager.dimissDialog();
				
				mAudioManager.release();// release釋放一個mediarecorder
				
				if (mListener!=null) {// 並且callbackActivity,保存錄音
				
					mListener.onFinished(mTime, mAudioManager.getCurrentFilePath());
				}
				
				
				
				

			} else if (mCurrentState == STATE_WANT_TO_CANCEL) {
				// cancel
				mAudioManager.cancel();
				mDialogManager.dimissDialog();
			}
			reset();// 恢復標志位

			break;

		}

		return super.onTouchEvent(event);
	}

	/**
	 * 回復標志位以及狀態
	 */
	private void reset() {
		// TODO Auto-generated method stub
		isRecording = false;
		changeState(STATE_NORMAL);
		mReady = false;
		mTime = 0;
	}

	private boolean wantToCancel(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;
	}

	private void changeState(int state) {
		
		if (mCurrentState != state) {
			mCurrentState = state;
			switch (mCurrentState) {
			case STATE_NORMAL:
				setBackgroundResource(R.drawable.button_recordnormal);
				setText(R.string.normal);

				break;
			case STATE_RECORDING:
				setBackgroundResource(R.drawable.button_recording);
				setText(R.string.recording);
				if (isRecording) {
					mDialogManager.recording();
					// 復寫dialog.recording();
				}
				break;

			case STATE_WANT_TO_CANCEL:
				setBackgroundResource(R.drawable.button_recording);
				setText(R.string.want_to_cancle);
				// 取消
				mDialogManager.wantToCancel();
				break;

			}
		}

	}

	@Override
	public boolean onPreDraw() {
		return false;
	}

}
(3)MediaRecorder
package com.nickming.view;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

import android.media.MediaRecorder;
/**
 * 
 * @ClassName:  AudioManager   
 * @Description: 錄音的管理類(准備工作)
 * @author:  張  維
 * @date:   2016-5-23 下午2:10:35   
 *
 */
public class AudioManager {

	private MediaRecorder mRecorder;
	private String mDirString;
	private String mCurrentFilePath;

	private boolean isPrepared;// 是否准備好了

	/**
	 * 單例化的方法 
	 *  1 先聲明一個static 類型的變量a
	 *  2 在聲明默認的構造函數 
	 *  3 再用public synchronized static
	 *   類名 getInstance() { if(a==null) { a=new 類();} return a; } 或者用以下的方法
	 */

	/**
	 * 單例化這個類
	 */
	private static AudioManager mInstance;

	private AudioManager(String dir) {
		mDirString=dir;
	}

	public static AudioManager getInstance(String dir) {
		if (mInstance == null) {
			synchronized (AudioManager.class) {
				if (mInstance == null) {
					mInstance = new AudioManager(dir);
				
				}
			}
		}
		return mInstance;

	}

	/**
	 * 回調函數,准備完畢,准備好後,button才會開始顯示錄音框
	 * 
	 * @author nickming
	 *
	 */
	public interface AudioStageListener {
		void wellPrepared();
	}

	public AudioStageListener mListener;

	public void setOnAudioStageListener(AudioStageListener listener) {
		mListener = listener;
	}

	// 准備方法
	public void prepareAudio() {
		try {
			// 一開始應該是false的
			isPrepared = false;

			File dir = new File(mDirString);
			//判斷對象file是否存在
			if (!dir.exists()) {
				//創建此抽象路徑指定的目錄,包括所有必須但不存在的父目錄。(及可以創建多級目錄,無論是否存在父目錄)
				dir.mkdirs();
			}

			String fileNameString = generalFileName();
			File file = new File(dir, fileNameString);

			mCurrentFilePath = file.getAbsolutePath();

			mRecorder = new MediaRecorder();
			// 設置輸出文件
			mRecorder.setOutputFile(file.getAbsolutePath());
			// 設置meidaRecorder的音頻源是麥克風
			mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
			// 設置文件音頻的輸出格式為amr
			mRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
			// 設置音頻的編碼格式為amr
			mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

			// 嚴格遵守google官方api給出的mediaRecorder的狀態流程圖
			mRecorder.prepare();

			mRecorder.start();
			// 准備結束
			isPrepared = true;
			// 已經准備好了,可以錄制了
			if (mListener != null) {
				mListener.wellPrepared();
			}

		} catch (IllegalStateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * 隨機生成文件的名稱
	 * 
	 * @return
	 */
	private String generalFileName() {
		
        //UUID.randomUUID().toString()是javaJDK提供的一個自動生成主鍵的方法。
		return UUID.randomUUID().toString() + ".amr";
	}

	// 獲得聲音的大小
	public int getVoiceLevel(int maxLevel) {
		// mRecorder.getMaxAmplitude()這個是音頻的振幅范圍,值域是1-32767
		if (isPrepared) {
			try {
				// 取證+1,否則去不到7
				return maxLevel * mRecorder.getMaxAmplitude() / 32768 + 1;
			} catch (Exception e) {
				
			}
		}

		return 1;
	}

	// 釋放資源
	public void release() {
		// 嚴格按照api流程進行
		mRecorder.stop();
		mRecorder.release();
		mRecorder = null;

	}

	// 取消,因為prepare時產生了一個文件,所以cancel方法應該要刪除這個文件,
	// 這是與release的方法的區別
	public void cancel() {
		release();
		if (mCurrentFilePath != null) {
			File file = new File(mCurrentFilePath);
			file.delete();//刪除文件
			mCurrentFilePath = null;
		}

	}

	public String getCurrentFilePath() {
		return mCurrentFilePath;
	}

}


3.調用的類 (1)RecorderAdapter
package com.example.weixin_record;

import java.util.List;

import com.example.weixin_record.MainActivity.Recorder;

import android.content.Context;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.TextView;
/**
 * 
 * @ClassName:  RecorderAdapter   
 * @Description:list的適配器 
 * @author:  張  維
 * @date:   2016-5-23 下午4:04:02   
 *
 */
public class RecorderAdapter extends ArrayAdapter {


	private LayoutInflater inflater;

	private int mMinItemWith;// 設置對話框的最大寬度和最小寬度
	private int mMaxItemWith;

	public RecorderAdapter(Context context, List dataList) {
		super(context, -1, dataList);
		
		inflater = LayoutInflater.from(context);

		// 獲取系統寬度
		WindowManager wManager = (WindowManager) context
				.getSystemService(Context.WINDOW_SERVICE);
		DisplayMetrics outMetrics = new DisplayMetrics();
		wManager.getDefaultDisplay().getMetrics(outMetrics);
		mMaxItemWith = (int) (outMetrics.widthPixels * 0.7f);
		mMinItemWith = (int) (outMetrics.widthPixels * 0.15f);
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder viewHolder = null;
		if (convertView == null) {
			convertView = inflater.inflate(R.layout.item_layout, parent, false);
			
			viewHolder=new ViewHolder();
			viewHolder.seconds=(TextView) convertView.findViewById(R.id.recorder_time);
			viewHolder.length=convertView.findViewById(R.id.recorder_length);
			
			convertView.setTag(viewHolder);
		}else {
			viewHolder=(ViewHolder) convertView.getTag();
		}
		
		
		viewHolder.seconds.setText(Math.round(getItem(position).time)+"\"");
		ViewGroup.LayoutParams lParams=viewHolder.length.getLayoutParams();
		lParams.width=(int) (mMinItemWith+mMaxItemWith/60f*getItem(position).time);
		viewHolder.length.setLayoutParams(lParams);
		
		return convertView;
	}

	class ViewHolder {
		TextView seconds;// 時間
		View length;// 對話框長度
	}

}

(2)MediaManager
package com.example.weixin_record;

import java.io.IOException;

import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.util.Log;
/**
 * 
 * @ClassName:  MediaManager   
 * @Description:播放的管理類
 * @author:  張  維
 * @date:   2016-5-23 下午4:04:43   
 *
 */
public class MediaManager {


	private static MediaPlayer mPlayer;
	
	private static boolean isPause;

	public static  void playSound(String filePathString,
			OnCompletionListener onCompletionListener) {
		
		if (mPlayer==null) {
			mPlayer=new MediaPlayer();
			//保險起見,設置報錯監聽
			mPlayer.setOnErrorListener(new OnErrorListener() {
				
				@Override
				public boolean onError(MediaPlayer mp, int what, int extra) {
					Log.i("info", "");
					mPlayer.reset();
					return false;
				}
			});
		}else {
			mPlayer.reset();//就回復
		}
		
		try {
			mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
			mPlayer.setOnCompletionListener(onCompletionListener);
			mPlayer.setDataSource(filePathString);
			mPlayer.prepare();
			mPlayer.start();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//停止函數
	public static void pause(){
		if (mPlayer!=null&&mPlayer.isPlaying()) {
			mPlayer.pause();
			isPause=true;
		}
	}
	
	//繼續
	public static void resume()
	{
		if (mPlayer!=null&&isPause) {
			mPlayer.start();
			isPause=false;
		}
	}
	

	public  static void release()
	{
		if (mPlayer!=null) {
			mPlayer.release();
			mPlayer=null;
		}
	}
}

(3)MainActivity
package com.example.weixin_record;

import java.util.ArrayList;
import java.util.List;

import com.nickming.view.AudioRecordButton;
import com.nickming.view.AudioRecordButton.AudioFinishRecorderListener;

import android.app.Activity;
import android.graphics.drawable.AnimationDrawable;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends Activity {
	AudioRecordButton button;

	private ListView mlistview;
	private ArrayAdapter mAdapter;
	private View viewanim;
	private List mDatas = new ArrayList();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		mlistview = (ListView) findViewById(R.id.listview);

		button = (AudioRecordButton) findViewById(R.id.recordButton);
		button.setAudioFinishRecorderListener(new AudioFinishRecorderListener() {

			@Override
			public void onFinished(float seconds, String filePath) {
				// TODO Auto-generated method stub
				Recorder recorder = new Recorder(seconds, filePath);
				mDatas.add(recorder);
				mAdapter.notifyDataSetChanged();
				mlistview.setSelection(mDatas.size() - 1);
			}
		});

		mAdapter = new RecorderAdapter(this, mDatas);
		mlistview.setAdapter(mAdapter);
		
		mlistview.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView parent, View view,
					int position, long id) {
				
				// 播放動畫
				if (viewanim!=null) {//讓第二個播放的時候第一個停止播放
					viewanim.setBackgroundResource(R.drawable.adj);
					viewanim=null;
				}
				viewanim = view.findViewById(R.id.show_anim01);
				viewanim.setBackgroundResource(R.drawable.play);
				AnimationDrawable drawable = (AnimationDrawable) viewanim
						.getBackground();
				drawable.start();

				// 播放音頻
				Log.i("info", "position==="+position);
				MediaManager.playSound(mDatas.get(position).filePathString,
						new MediaPlayer.OnCompletionListener() {

							@Override
							public void onCompletion(MediaPlayer mp) {
							viewanim.setBackgroundResource(R.drawable.adj);

							}
						});
			}
		});
	}
	
	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		MediaManager.pause();
	}
	
	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		MediaManager.resume();
	}
	
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		MediaManager.release();
	}

	class Recorder {
		float time;
		String filePathString;

		public Recorder(float time, String filePathString) {
			super();
			this.time = time;
			this.filePathString = filePathString;
		}

		public float getTime() {
			return time;
		}

		public void setTime(float time) {
			this.time = time;
		}

		public String getFilePathString() {
			return filePathString;
		}

		public void setFilePathString(String filePathString) {
			this.filePathString = filePathString;
		}

	}

}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved