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");
	}
	

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

时间: 2024-10-19 07:14:34

android项目 之 记事本(5)----- 添加录音的相关文章

android项目 之 记事本(13) ----- 查看图片及播放录音

本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020 今天就来实现下查看图片及录音的功能,在编辑或者浏览记事时,点击图片,打开一个自定义Activity(当然了,也可以调用系统的图库来查看)来查看所添加的图片的原始图片,而不是缩放后的图片,同理,用自定义Activity来查看录音文件,实现播放录音的功能.上图: 从图中也可以看出,我们首先要创建两个Activity,当然了,布局文件也是少不了的,如下: activity_show_

android项目 之 记事本(15) ----- 保存手写及绘图

本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020 之前,忘了写如何将手写和绘图保存,现在补上. 首先看如何保存绘图,先看效果图: 因为记事本的绘图功能主要用到了画布,而在构建画布时,指定了Bitmap,也就是说在画布上的所画的东西都被保存在了Bitmap中,因此,我们只要保存该Bitmap,就可以将我们的所绘制的图形以图片的形式保存,主要代码如下: /* * 保存所绘图形 * 返回绘图文件的存储路径 * */ public S

android项目 之 记事本(11) ----- 添加数据库

这节就来为我们的记事本添加数据库支持,这样,就可以在添加记事后将其保存在数据库中,方便下次浏览,修改,删除等. 先看效果图: 三张图片分别演示了保存记事,查看记事,删除记事. 对于数据库而言,无非就是涉及到数据库的创建,增删改查. 为了将数据库的操作封装起来,单独写了一个类,如下: 数据库操作 DatabaseOperation.java [java] view plaincopy import android.content.Context; import android.database.C

android项目 之 记事本(12) ----- 图片的等比例缩放及给图片添加边框

本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020 在Android的UI开发中经常会遇到图片的缩放,就比如记事本,现在的图片都比较大,如果将原图不经缩放直接放在屏幕上,则会占满整个屏幕,而且有时图片会比屏幕还大,这时就不能完全的显示整个图片,所以,必须要进行缩放,但在缩放时,该如何缩放呢,长和宽的缩放比例设置为多少合适呢,为了保持原图的纵横比,所以要最好的方法就是约束缩放比例,也就是等比例缩放,相信大家都用过PS中的缩放图片的

android项目 之 记事本(6)----- 添加手写

想必大家都用过QQ的白板功能,里面主要有两项,一个是涂鸦功能,其实类似于上节的画板功能,而另一个就是手写,那记事本怎么能没有这个功能呢,今天就来为我们的记事本添加手写功能. 先上图,看看效果: 看了效果图,是不是心动了呢?那就赶紧着手做吧,其实,手写功能并不难实现,大体就是全屏书写,定时发送handle消息,更新activity. 实现手写功能的主要步骤: 1. 自定义两个View,一个是TouchView,用于在上面画图,另一个是EditText,用于将手写的字显示在其中,并且,要将两个自定义

android项目 之 记事本(14) ----- 手势缩放与拖拽图片

本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020 上节实现了查看图片及录音的功能,其中查看图片,可以调用系统的图库来查看图片,也可以自定义Activity来查看图片,今天就在上节的基础上,实现手势缩放与拖拽图片. 想必大家都用过系统的图库,浏览图片时,可以通过手势放大或缩小图片,旋转图片,拖拽图片等功能,我们也为自已定义的查看图片的Activity增加手势缩放与拖拽图片的功能,效果如下图: 上面四幅图中,演示了通过手势(多点触

android项目 之 记事本(7)----- 手写功能之删除、恢复和清空

上一节,为记事本添加了手写的功能,但是没有实现底部按钮的各种功能,这节就先实现撤销,恢复和清空的功用. 因为不会录制屏幕成gif图片,所以就以图片形式给出吧,不是很形象,凑合着看: 显然,需要为底部GridView的添加item单击事件: private GridView paint_bottomMenu; paint_bottomMenu = (GridView)findViewById(R.id.paintBottomMenu); paint_bottomMenu.setOnItemClic

android项目 之 记事本(9) ----- 画板功能之橡皮擦、画笔大小和画笔颜色

上节已实现了画板中的绘制,删除,恢复入清空的功能,还有橡皮擦,设置画笔大小和画笔颜色没有实现,这节就将这几个功能逐一实现. 先看效果图: 以上图中,第一个展示了设置画笔颜色的功能,第二个展示了设置画笔大小的颜色,而第三个则展示了橡皮擦的功能,细心的可以发现,这节将图标颜色设置为了蓝色,并且,增加了最左边的按钮(其实,就是在gridview中多增加了一个item). 下面分别讨论,橡皮擦,设置画笔大小,设置画笔颜色的主要思想:     1. 橡皮擦功能:                    基本

android项目 之 记事本(10) ----- 手写功能之设置画笔大小和画笔颜色

上一节,实现了画板的所有功能,包括设置画笔大小,设置画笔颜色,橡皮擦等功能,而手写,也可以添加设置笔迹大小和颜色的功能,这节就顺势实现手写的调整笔迹大小和调整笔迹的颜色. 先看图: 其实,手写和画板的这些功能都类似,直接复用之前的代码就行,原理如下:      1. 设置画笔大小的功能:                    1)初始化画笔. 2)设置画笔的大小为所选择的大小. 3)用一个变量记住当前画笔的大小,用于在进行其他操作后还保持之前设置的画笔大小. 2. 设置画笔颜色的功能: