本文中实现录音时候、指针摆动的功能主要是参考SoundRecorder的。主要是其中的VUMeter类,VUMeter是通过Recorder.getMaxAmplitude()的值计算,画出指针的偏移摆动。
下面直接上代码:
VUMeter类:
- package hfut.geron.record;
- import hfut.geron.memorybook.R;
- import android.content.Context;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.graphics.drawable.Drawable;
- import android.util.AttributeSet;
- import android.view.View;
- public class VUMeter extends View {
- static final float PIVOT_RADIUS = 3.5f;
- static final float PIVOT_Y_OFFSET = 10f;
- static final float SHADOW_OFFSET = 2.0f;
- static final float DROPOFF_STEP = 0.18f;
- static final float SURGE_STEP = 0.35f;
- static final long ANIMATION_INTERVAL = 70;
- Paint mPaint, mShadow;
- float mCurrentAngle;
- RecordHelper mRecorder;
- public VUMeter(Context context) {
- super(context);
- init(context);
- }
- public VUMeter(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context);
- }
- void init(Context context) {
- Drawable background = context.getResources().getDrawable(R.drawable.record_bg2);
- setBackgroundDrawable(background);
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mPaint.setColor(Color.WHITE);
- mShadow = new Paint(Paint.ANTI_ALIAS_FLAG);
- mShadow.setColor(Color.argb(60, 0, 0, 0));
- mRecorder = null;
- mCurrentAngle = 0;
- }
- public void setRecorder(RecordHelper recorder) {
- mRecorder = recorder;
- invalidate();
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- final float minAngle = (float)Math.PI/8;
- final float maxAngle = (float)Math.PI*7/8;
- float angle = minAngle;
- if (mRecorder != null)
- angle += (float)(maxAngle - minAngle)*mRecorder.getMaxAmplitude()/32768;
- if (angle > mCurrentAngle)
- mCurrentAngle = angle;
- else
- mCurrentAngle = Math.max(angle, mCurrentAngle - DROPOFF_STEP);
- mCurrentAngle = Math.min(maxAngle, mCurrentAngle);
- float w = getWidth();
- float h = getHeight();
- float pivotX = w/2;
- float pivotY = h - PIVOT_RADIUS - PIVOT_Y_OFFSET;
- float l = h*4/5;
- float sin = (float) Math.sin(mCurrentAngle);
- float cos = (float) Math.cos(mCurrentAngle);
- float x0 = pivotX - l*cos;
- float y0 = pivotY - l*sin;
- canvas.drawLine(x0 + SHADOW_OFFSET, y0 + SHADOW_OFFSET, pivotX + SHADOW_OFFSET, pivotY + SHADOW_OFFSET, mShadow);
- canvas.drawCircle(pivotX + SHADOW_OFFSET, pivotY + SHADOW_OFFSET, PIVOT_RADIUS, mShadow);
- canvas.drawLine(x0, y0, pivotX, pivotY, mPaint);
- canvas.drawCircle(pivotX, pivotY, PIVOT_RADIUS, mPaint);
- if (mRecorder != null)
- postInvalidateDelayed(ANIMATION_INTERVAL);
- }
- }
录音类:RecordHelper.java
- package hfut.geron.record;
- import java.io.File;
- import java.io.IOException;
- import java.text.SimpleDateFormat;
- import android.content.Context;
- import android.media.MediaPlayer;
- import android.media.MediaPlayer.OnCompletionListener;
- import android.media.MediaPlayer.OnErrorListener;
- import android.media.MediaRecorder;
- import android.util.Log;
- public class RecordHelper implements OnCompletionListener, OnErrorListener{
- private MediaRecorder mRecorder ;
- private String record_path;
- public static final int IDLE_STATE = 0; //空闲状态
- public static final int RECORDING_STATE = 1; //录音状态
- int mState = IDLE_STATE; //状态标示,默认是空闲状态
- public static final int NO_ERROR = 0;
- public static final int SDCARD_ACCESS_ERROR = 1;
- public static final int INTERNAL_ERROR = 2;
- public interface OnStateChangedListener {
- public void onStateChanged(int state);
- public void onError(int error);
- }
- OnStateChangedListener mOnStateChangedListener = null;
- public void setOnStateChangedListener(OnStateChangedListener listener) {
- mOnStateChangedListener = listener;
- }
- public void startRecording(int outputfileformat, String suffix, Context context) {
- Log.d("Infor", "start...");
- stopRecording();
- if(CommonUtils.isSdCardAvailable()){
- File audioRecPath = new File(CommonUtils.RECORD_PATH);
- //判断存储音频的文件夹是否存在,如果不存在则进行创建操作
- if(!audioRecPath.exists()){
- audioRecPath.mkdir();
- }
- if(audioRecPath != null ){
- //创建临时文件,以Record_开头
- SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyyMMdd_hhmmss");
- String record_name = "Record_"+sDateFormat.format(new java.util.Date());
- File audioRecFile;
- try {
- audioRecFile = File.createTempFile(record_name, suffix, audioRecPath);
- } catch (IOException e) {
- // TODO Auto-generated catch block
- setError(SDCARD_ACCESS_ERROR);
- return;
- }
- mRecorder= new MediaRecorder();
- //设置采样的音频源为麦克风
- mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
- //设置输出文件格式
- mRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
- //设置音频的编码方式
- mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
- //设置输出文件
- Log.d("Infor", audioRecFile.getAbsolutePath());
- record_path= audioRecFile.getAbsolutePath();
- mRecorder.setOutputFile(audioRecFile.getAbsolutePath());
- try{
- mRecorder.prepare();
- }catch(IOException e){
- setError(INTERNAL_ERROR);
- mRecorder.reset();
- mRecorder.release();
- mRecorder = null;
- return;
- }
- //开始录音
- if(this.mState==RecordHelper.IDLE_STATE){
- mRecorder.start();
- setState(RECORDING_STATE);
- }
- }
- }
- else{
- Log.d("Infor", "error,内存卡打不开。。。");
- setError(SDCARD_ACCESS_ERROR);
- }
- }
- public void stopRecording() {
- if (mRecorder == null)
- return;
- mRecorder.stop();
- mRecorder.release();
- mRecorder = null;
- setState(IDLE_STATE);
- }
- void setState(int state){//设置状态
- if (state == mState)
- return;
- mState = state;
- signalStateChanged(mState);
- }
- public int getMaxAmplitude() {//得到录音的振幅
- if (mState != RECORDING_STATE)
- return 0;
- return mRecorder.getMaxAmplitude();
- }
- //实现对自定义接口OnStateChangedListener的两个方法调用的条件
- private void signalStateChanged(int state) {
- if (mOnStateChangedListener != null)
- mOnStateChangedListener.onStateChanged(state);
- }
- private void setError(int error) {
- if (mOnStateChangedListener != null)
- mOnStateChangedListener.onError(error);
- }
- public String getPath(){
- return record_path;
- }
- /*继承接口方法*/
- public boolean onError(MediaPlayer mp, int what, int extra) {
- // TODO Auto-generated method stub
- return true;
- }
- public void onCompletion(MediaPlayer mp) {
- // TODO Auto-generated method stub
- }
- }
RecordActivity:
- package hfut.geron.memorybook.onebook;
- import java.io.File;
- import hfut.geron.memorybook.R;
- import hfut.geron.record.CommonUtils;
- import hfut.geron.record.RecordHelper;
- import hfut.geron.record.RecordHelper.OnStateChangedListener;
- import hfut.geron.record.VUMeter;
- import android.app.Activity;
- import android.app.Dialog;
- import android.content.Context;
- import android.content.DialogInterface;
- import android.content.Intent;
- import android.media.MediaRecorder;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.os.PowerManager;
- import android.os.PowerManager.WakeLock;
- import android.util.Log;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.ImageButton;
- import android.widget.TextView;
- import android.widget.Toast;
- public class RecordActivity extends Activity implements OnClickListener, OnStateChangedListener {
- private TextView record_text_time;
- private ImageButton record_btn_begin,record_btn_end;
- private VUMeter mVUMeter;//VUMeter类是通过mRecorder.getMaxAmplitude()的值计算,画出指针的偏移摆动
- WakeLock mWakeLock;//用WakeLock请求休眠锁,让其不会休眠
- private String time;
- private RecordHelper mRecordHelper;//录音类,实现录音功能
- private int s1=0;
- private int s2=0;
- private int m1=0;
- private int m2=0;
- private final static int RECORD_ISOVER = 1;
- private final static int SDCARD_ACCESS_ERROR=2;
- private final static int NOTES_RECORD=1;
- private Handler time_handler= new Handler();
- private Runnable runnable = new Runnable(){
- public void run() {
- // TODO Auto-generated method stub
- if(s1==10){
- s2++;
- s1=0;
- }
- if(s2==6){
- s2=0;
- m1++;
- }
- if(m1==10){
- m1=0;
- m2++;
- }
- time=""+m2+m1+":"+s2+s1;
- record_text_time = (TextView) findViewById(R.id.record_text_time);
- record_text_time.setText(time);
- s1++;
- time_handler.postDelayed(runnable, 1000);
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- this.setContentView(R.layout.recordialog);
- PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK,
- "SoundRecorder");//请求休眠锁
- mRecordHelper = new RecordHelper();
- mRecordHelper.setOnStateChangedListener(this);
- mVUMeter = (VUMeter)this.findViewById(R.id.record_VUMeter);
- mVUMeter.setRecorder(mRecordHelper);
- record_text_time = (TextView)this.findViewById(R.id.record_text_time);
- record_btn_begin = (ImageButton)this.findViewById(R.id.record_btn_begin);
- record_btn_end= (ImageButton)this.findViewById(R.id.record_btn_end);
- record_btn_begin.setOnClickListener(this);
- record_btn_end.setOnClickListener(this);
- record_btn_begin.setId(1);
- record_btn_end.setId(2);
- }
- public void onClick(View v) {
- // TODO Auto-generated method stub
- int id=v.getId();
- switch(id){
- case 1: //录音开始
- time_handler.post(runnable);
- Log.d("Infor", "录音开始...");
- mRecordHelper.startRecording(MediaRecorder.OutputFormat.DEFAULT,
- ".amr", this);
- record_btn_end.setClickable(true);
- record_btn_begin.setClickable(false);
- break;
- case 2: //录音结束
- record_btn_begin.setClickable(true);
- time_handler.removeCallbacks(runnable);
- s1=s2=m1=m2=0;
- Log.d("Infor", "录音关闭...");
- mRecordHelper.stopRecording();
- showDialog(RECORD_ISOVER);
- break;
- }
- }
- public void onStateChanged(int state) {
- // TODO Auto-generated method stub
- Log.d("Infor", "录音状态发生改变");
- if (state == RecordHelper.RECORDING_STATE) {
- mWakeLock.acquire(); // 录音的时候让其不休眠
- } else {
- if (mWakeLock.isHeld())
- mWakeLock.release();
- }
- }
- public void onError(int error) {
- // TODO Auto-generated method stub
- Log.d("Infor", "error:"+error);
- this.showDialog(error);
- }
- @Override
- protected Dialog onCreateDialog(int id) {
- // TODO Auto-generated method stub
- switch(id){
- case RECORD_ISOVER:
- Log.d("Infor", "here");
- return CommonUtils.getDialog(this, "提示", "确定要保存该录音音频文件吗?", 0,
- "是", "否", new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- // TODO Auto-generated method stub
- Log.d("Infor", "保存该录音音频");
- Toast.makeText(RecordActivity.this, "录音文件保存成功...", Toast.LENGTH_SHORT).show();
- Intent intent = new Intent(RecordActivity.this,NoteActivity.class);
- intent.putExtra("recordpath", mRecordHelper.getPath());
- RecordActivity.this.setResult(NOTES_RECORD, intent);
- RecordActivity.this.finish();
- }
- }, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- // TODO Auto-generated method stub
- //删除之前的录音文件
- Log.d("Infor", "取消保存该录音音频");
- record_text_time.setText("00:00");
- Log.d("Infor", "PATH:"+mRecordHelper.getPath());
- File file = new File(mRecordHelper.getPath());
- if(file.exists()){
- Log.d("Infor", "存在");
- try{
- file.delete();
- }catch(Exception e){
- Log.d("Infor", "删除不了");
- }
- }
- }
- });
- case SDCARD_ACCESS_ERROR:
- return CommonUtils.getDialog(this, "提示", "无法读取内存卡..", 0, "是", null, new DialogInterface.OnClickListener(){
- public void onClick(DialogInterface dialog, int which) {
- // TODO Auto-generated method stub
- RecordActivity.this.finish();
- }
- }, null);
- default:
- return null;
- }
- }
- }
结果截图:
要点:
1、该程序中使用了用WakeLock请求休眠锁,让其不会休眠。这样能保证录音过程中屏幕常亮。
2、该程序中主要使用VUMeter,自定义该类,VUMeter类是通过mRecorder.getMaxAmplitude()的值计算,画出指针的偏移摆动。
3、RecordHelper类中定义了一个接口OnStateChangedListener ,定义如下
public interface OnStateChangedListener {
public void onStateChanged(int state);
public void onError(int error);
}
并在RecordHelper类中实例化该接口并set,如下:
OnStateChangedListener mOnStateChangedListener = null;
public void setOnStateChangedListener(OnStateChangedListener listener) {
mOnStateChangedListener = listener;
}
定义在何种情况下使用该接口中的两个方法:
//实现对自定义接口OnStateChangedListener的两个方法调用的条件
private void signalStateChanged(int state) {
if (mOnStateChangedListener != null)
mOnStateChangedListener.onStateChanged(state);
}
private void setError(int error) {
if (mOnStateChangedListener != null)
mOnStateChangedListener.onError(error);
}
在RecordActivity类中就可以继承该接口
class RecordActivity extends Activity implements OnStateChangedListener{}
并
mRecordHelper = new RecordHelper();
mRecordHelper.setOnStateChangedListener(this);