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

时间:2021-06-03 06:53:41

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

android项目 之 记事本(5)-----  添加录音android项目 之 记事本(5)-----  添加录音android项目 之 记事本(5)-----  添加录音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");
}

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