android项目 之 记事本(5)----- 添加录音

             有时,需要将重要的事以语音的形式记录下来,这个在生活中很常见,今天就为记事本添加录音的功能,先看图:

             其实在第一节界面设计中,可以看出记事本的功能选项,其中底部选项栏的第三个就是添加录音。

             主要步骤如下:

              1.   录音Activity的界面设计。

              2.   在语音按钮的监听器中添加Intent,跳转到录音Activity,这里同样是用startActivityforResult(Intent intent,int requestCode)。

              3.   在录音activity中实现录音的功能,并将最终录音文件路径返回。

              4.   在添加记事Activity中取出返回的录音文件的路径,并将相应的录音图标添加在记事中。

         

            1 录音Activity的界面设计:

                 录音的布局文件activity_record.xml 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:background="@drawable/bg"
    >
   
 	 <LinearLayout 
      	    android:layout_width="match_parent"
      	    android:layout_height="match_parent"
      	    android:orientation="vertical"
      	   	android:gravity="center"
      	   	android:layout_centerInParent="true"
      	    >
      	
      	<LinearLayout 
      	    android:layout_width="match_parent"
      	    android:layout_height="wrap_content"
      	    android:orientation="horizontal"
      	    android:gravity="center"
      	    android:layout_margin="5dp"
      	    >
      	<ImageView
      	android:id="@+id/iv_record_wave_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        
        android:layout_margin="5dp"
        android:background="@anim/record_wave_left"
        />
        <ImageView 
        android:id="@+id/iv_microphone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:src="@drawable/record_microphone_icon"
        android:layout_margin="5dp"
        />
         <ImageView 
        android:id="@+id/iv_record_wave_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        
        android:layout_margin="5dp"
        android:background="@anim/record_wave_right"
        />  
   
      	  </LinearLayout>  
      	   <TextView 
        android:id="@+id/tv_recordTime"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#499df7"
        android:textSize="20sp"
        android:text="00:00:00"
        android:gravity="center"
        android:layout_margin="5dp"
        /> 
      	</LinearLayout>
        
        
        
    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:background="@drawable/navigationbar_bg"
        android:layout_alignParentBottom="true"
        android:gravity="center"
        >
         <Button 
             android:id="@+id/btn_record"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:background="@drawable/tabbar_record_start"
             
             />
    </LinearLayout>
    
</RelativeLayout>

              同样,标题栏复用的是title_add.xml,这个在前面几节中已经给出过,这里只是在java代码里将标题换成了“录音”

 

       2  在语音按钮的监听器中添加Intent,跳转到录音Activity,代码为:

		//语音
		case 2:
		intent = new Intent(AddActivity.this,ActivityRecord.class);
		startActivityForResult(intent, 4);
		break;

      

       3  在录音activity中实现录音的功能

          既然要实现录音,少不了权限,因此在AndroidManifest.xml添加

	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
	<uses-permission android:name="android.permission.RECORD_AUDIO"/>

           在这里,不仅实现了录音的功能,同时,也实现了计时,试听,以及用逐帧动画的功能。具体代码里有注释

package com.example.notes;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.content.Intent;
import android.graphics.drawable.AnimationDrawable;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class ActivityRecord extends Activity {
	private Button btn_record;
	private ImageView iv_microphone;
	private TextView tv_recordTime;
	private ImageView iv_record_wave_left,iv_record_wave_right;
	
	private AnimationDrawable ad_left,ad_right;
	
	private int isRecording = 0;
	private int isPlaying = 0;
	
	private Timer mTimer;
	//语音操作对象
	private MediaPlayer mPlayer = null;
	private MediaRecorder mRecorder = null;
	
	//语音保存路径 
	private String FilePath = null;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
		setContentView(R.layout.activity_record);
		getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.title_add);
		
		TextView title = (TextView)findViewById(R.id.tv_title);
		title.setText("录音");
		
		Button btn_save = (Button)findViewById(R.id.bt_save);
		btn_save.setOnClickListener(new ClickEvent());
		Button btn_back = (Button)findViewById(R.id.bt_back);
		btn_back.setOnClickListener(new ClickEvent());
		
		
		
		btn_record = (Button)findViewById(R.id.btn_record);
		btn_record.setOnClickListener(new ClickEvent());
		
		iv_microphone = (ImageView)findViewById(R.id.iv_microphone);
		iv_microphone.setOnClickListener(new ClickEvent());
		
		iv_record_wave_left = (ImageView)findViewById(R.id.iv_record_wave_left);
		iv_record_wave_right = (ImageView)findViewById(R.id.iv_record_wave_right);
		
		ad_left = (AnimationDrawable)iv_record_wave_left.getBackground();
		//ad_left = (AnimationDrawable)iv_record_wave_left.getDrawable();
		ad_right = (AnimationDrawable)iv_record_wave_right.getBackground();
		//ad_right = (AnimationDrawable)iv_record_wave_right.getDrawable();
		
		
		tv_recordTime = (TextView)findViewById(R.id.tv_recordTime);
	}
	
	final Handler handler = new Handler(){
		public void handleMessage(Message msg) {
			switch(msg.what){
			    case 1 :
			    	String time[] = tv_recordTime.getText().toString().split(":");
					int hour = Integer.parseInt(time[0]);
					int minute = Integer.parseInt(time[1]);
					int second = Integer.parseInt(time[2]);
					
					if(second < 59){
						second++;
						
					}
					else if(second == 59 && minute < 59){
						minute++;
						second = 0;
						
					}
					if(second == 59 && minute == 59 && hour < 98){
						hour++;
						minute = 0;
						second = 0;
					}
					
					time[0] = hour + "";
					time[1] = minute + "";
					time[2] = second + "";
					//调整格式显示到屏幕上
					if(second < 10)
						time[2] = "0" + second;
					if(minute < 10)
						time[1] = "0" + minute;
					if(hour < 10)
						time[0] = "0" + hour;
					
					//显示在TextView中
					tv_recordTime.setText(time[0]+":"+time[1]+":"+time[2]);
					
					break;
			
			}
			
		}
	};
	
	class ClickEvent implements OnClickListener{

		@Override
		public void onClick(View v) {
			switch(v.getId()){
			//点击的是开始录音按钮 
			case  R.id.btn_record :
				//开始录音 
				if(isRecording == 0){
					
					//每一次调用录音,可以录音多次,至多满意为至,最后只将最后一次的录音文件保存,其他的删除
					if(FilePath != null){
						File oldFile = new File(FilePath);
						oldFile.delete();
					}
					
					
					//获得系统当前时间,并以该时间作为文件名
			  		SimpleDateFormat   formatter   =   new   SimpleDateFormat   ("yyyyMMddHHmmss");  
			        Date   curDate   =   new   Date(System.currentTimeMillis());//获取当前时间 
			        String   str   =   formatter.format(curDate);  
			        
			        str = str + "record.amr";
			        File dir = new File("/sdcard/notes/");
			        File file = new File("/sdcard/notes/",str);
			        if (!dir.exists()) { 
			        	dir.mkdir(); 
			        } 
			        else{
			        	if(file.exists()){
			        		file.delete();
			        	}
			        }
					
					FilePath = dir.getPath() +"/"+ str;
					//计时器
					mTimer = new Timer();
					
					//将麦克图标设置成不可点击,
					iv_microphone.setClickable(false);
					//将显示的时间设置为00:00:00
					tv_recordTime.setText("00:00:00");
					//将按钮换成停止录音
					isRecording = 1;
					btn_record.setBackgroundResource(R.drawable.tabbar_record_stop);
					
					mRecorder = new MediaRecorder();
					mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
					mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
					mRecorder.setOutputFile(FilePath);
					mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
					
					try {
						mRecorder.prepare();
					} catch (IllegalStateException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					
					mRecorder.start();
					mTimer.schedule(new TimerTask() {
						
						@Override
						public void run() {
							Message message = new Message();
							message.what = 1;
							handler.sendMessage(message);
							
						}
					},1000, 1000);
					//播放动画
					ad_left.start();
					ad_right.start();
				}
				//停止录音
				else{
					//将按钮换成开始录音
					isRecording = 0;
					btn_record.setBackgroundResource(R.drawable.tabbar_record_start);
					mRecorder.stop();
					mTimer.cancel();
					mTimer = null;
					
					mRecorder.release();
					mRecorder = null;
					
					//将麦克图标设置成可点击,
					iv_microphone.setClickable(true);
					//停止动画
					ad_left.stop();
					ad_right.stop();
					Toast.makeText(ActivityRecord.this, "单击麦克图标试听,再次点击结束试听", Toast.LENGTH_LONG).show();
				}
				break;
			//如果单击的是麦克图标,则可以是进入试听模式,再次点击,停止播放
			case R.id.iv_microphone :
				if(FilePath == null)
					Toast.makeText(ActivityRecord.this, "没有录音广播可以播放,请先录音", Toast.LENGTH_LONG).show();
				else{
					//试听
					if(isPlaying == 0){
						isPlaying = 1;
						mPlayer = new MediaPlayer();
						tv_recordTime.setText("00:00:00");
						mTimer = new Timer();
						mPlayer.setOnCompletionListener(new MediaCompletion());
						try {
							mPlayer.setDataSource(FilePath);
							mPlayer.prepare();
							mPlayer.start();
						} catch (IllegalArgumentException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (SecurityException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (IllegalStateException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						mTimer.schedule(new TimerTask() {
							
							@Override
							public void run() {
								Message message = new Message();
								message.what = 1;
								handler.sendMessage(message);
								
							}
						}, 1000,1000);
						
						//播放动画
						ad_left.start();
						ad_right.start();
					}
					//结束试听
					else{
						isPlaying = 0;
						mPlayer.stop();
						mPlayer.release();
						mPlayer = null;
						mTimer.cancel();
						mTimer = null;
						//停止动画
						ad_left.stop();
						ad_right.stop();
					}
				}
				break;
			
			//点击确定按钮 
			case R.id.bt_save :
				//将最终的录音文件的路径返回
				Intent intent = getIntent();
				Bundle b = new Bundle();
				b.putString("audio", FilePath);
				intent.putExtras(b);
				setResult(RESULT_OK, intent);
				
				ActivityRecord.this.finish();
				break;
			case R.id.bt_back :
				//返回前将录音的文件删除
				if(FilePath != null){
					File oldFile = new File(FilePath);
					oldFile.delete();
				}
				ActivityRecord.this.finish();
				break;
				
			}
		}
		
	}
	
	class MediaCompletion implements OnCompletionListener{

		@Override
		public void onCompletion(MediaPlayer mp) {
			mTimer.cancel();
			mTimer = null;
			isPlaying = 0;
			//停止动画
			ad_left.stop();
			ad_right.stop();
			Toast.makeText(ActivityRecord.this, "播放完毕", Toast.LENGTH_LONG).show();
			tv_recordTime.setText("00:00:00");
		}
		
	}
	
	
	
	
}


           其中,逐帧动画用的AnimationDrawable类,只需要在res/anim中新建动画列表文件即可,这里动画是为了在录音和试听的过程中,麦克两边呈现出现波形,给程序添加美感。

              动画列表文件----麦克左边的波形:record_wave_left.xml

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item 
        android:drawable="@drawable/record_wave_left_01"
        android:duration="500"/>
      <item 
        android:drawable="@drawable/record_wave_left_02"
        android:duration="500"/>
        <item 
        android:drawable="@drawable/record_wave_left_03"
        android:duration="500"/>
          <item 
        android:drawable="@drawable/record_wave_left_04"
        android:duration="500"/>
    
</animation-list>

              动画列表文件----麦克右边的波形:record_wave_right.xml

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" >
     <item 
        android:drawable="@drawable/record_wave_right_01"
        android:duration="500"/>
      <item 
        android:drawable="@drawable/record_wave_right_02"
        android:duration="500"/>
        <item 
        android:drawable="@drawable/record_wave_right_03"
        android:duration="500"/>
          <item 
        android:drawable="@drawable/record_wave_right_04"
        android:duration="500"/>

</animation-list>

           这里的图片资源文件,这里就不给出了,可以根据自己需要,更换其他的,当然了播放动画的快慢可以设置上述文件中的duration值。

            应用时,只需要设置相应的ImageView的backgroud属性为 "@anim/相应文件名" , 这时就可以利用AnimationDrawable的开始,停止播放动画的方法,即stop(),start()方法。  

           

            另外需要注意的是,在录音Activity中动实时显示计时,这里用到了Handler,因为不能在Timer线程中绘制UI,所以不能直接在TimerTask的run方法中实现动态显示时间。

            当然,还有一些小功能,例如,可以录音界面中多次录音,并且点击麦克图标就能实现播放,再次点击就可停止播放,只有在点击完成按钮时,才将最后一次的录音文件保存,之前的录音文件删除等这些功能 ,可以在代码中看出。

        

          4.   在添加记事Activity中取出返回的录音文件的路径,并将相应的录音图标添加在记事中。

           在onActivityResult()里添加如下代码:

	//返回的是录音文件
	else if(requestCode == 4){
		extras = data.getExtras();
		String audioPath = extras.getString("audio");
		bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.record_microphone_icon);
		//插入录音图标
		InsertBitmap(bitmap,80);
	}
			

        这里的InsertBitmap(bitmap,80),  是将之前的将图片等比例缩放,并将其添加进EditText中的代码写成一个函数,用到时,只需要将要添加的图片bitmap,以及缩放的最大值作为函数的参数调用该函数即可。

        这里给出该函数,其实就是之前在添加照片那一节中的一些代码。

	//将图片等比例缩放到合适的大小并添加在EditText中
	void InsertBitmap(Bitmap bitmap,int S){

		int imgWidth = bitmap.getWidth();
		int imgHeight = bitmap.getHeight();
		double partion = imgWidth*1.0/imgHeight;
		double sqrtLength = Math.sqrt(partion*partion + 1);
		//新的缩略图大小
		double newImgW = S*(partion / sqrtLength);
		double newImgH = S*(1 / sqrtLength);
		float scaleW = (float) (newImgW/imgWidth);
		float scaleH = (float) (newImgH/imgHeight);
		
		Matrix mx = new Matrix();
		//对原图片进行缩放
		mx.postScale(scaleW, scaleH);
		bitmap = Bitmap.createBitmap(bitmap, 0, 0, imgWidth, imgHeight, mx, true);
		final ImageSpan imageSpan = new ImageSpan(this,bitmap);
		SpannableString spannableString = new SpannableString("test");
		spannableString.setSpan(imageSpan, 0, spannableString.length(), SpannableString.SPAN_MARK_MARK);
		//光标移到下一行
		//et_Notes.append("\n");
		Editable editable = et_Notes.getEditableText();
		int selectionIndex = et_Notes.getSelectionStart();
		spannableString.getSpans(0, spannableString.length(), ImageSpan.class);
		
		//将图片添加进EditText中
		editable.insert(selectionIndex, spannableString);
		//添加图片后自动空出两行 
		et_Notes.append("\n");
	}
	

         至此,记事本的添加录音功能已实现,由于还没有用到数据库,所以目前没有实现记事的增删改查等功能,这个以后再实现,贯彻一个主线,先搭好框架,再实现功能。


 

 

 

 

 

     

       



 

          


 


 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。