有时,需要将重要的事以语音的形式记录下来,这个在生活中很常见,今天就为记事本添加录音的功能,先看图:
其实在第一节界面设计中,可以看出记事本的功能选项,其中底部选项栏的第三个就是添加录音。
主要步骤如下:
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"); }
至此,记事本的添加录音功能已实现,由于还没有用到数据库,所以目前没有实现记事的增删改查等功能,这个以后再实现,贯彻一个主线,先搭好框架,再实现功能。