在一个项目中要使用到录音,所以找了许多资料,写这个简单程序当练练。最后扩展功能并集成到那个项目。
先看图片:
1:主界面 2:录音中,麦克风有动画效果。
3:改名 4:改名后
项目功能:可以录音,录音过程动画,播放录音,改名和删除录音文件。
项目扩展:扩展列表ListView显示更多内容,扩展录音过程显示音量大小等。
布局简单
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.nfst.avrecorder.MainActivity$PlaceholderFragment" >
<com.baoyz.swipemenulistview.SwipeMenuListView
android:id="@+id/lvRecorder"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" >
</com.baoyz.swipemenulistview.SwipeMenuListView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="horizontal" >
<TextView
android:id="@+id/txtAcTimes"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="時長:00:00" />
<TextView
android:id="@+id/txtAcName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="文件名:" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal" >
<ImageView
android:id="@+id/imgAcAction"
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="center"
android:src="@drawable/ic_mic_pressed48" />
<Button
android:id="@+id/btnStart"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="錄音" />
<Button
android:id="@+id/btnPlay"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="播放" />
</LinearLayout>
</LinearLayout>
一个Activity就完成。程序如下:
package com.nfst.avrecorder;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import com.baoyz.swipemenulistview.SwipeMenu;
import com.baoyz.swipemenulistview.SwipeMenuCreator;
import com.baoyz.swipemenulistview.SwipeMenuItem;
import com.baoyz.swipemenulistview.SwipeMenuListView;
import com.baoyz.swipemenulistview.SwipeMenuListView.OnMenuItemClickListener;
import com.nfst.utils.DensityUtils;
import com.nfst.utils.FileHelper;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.BounceInterpolator;
import android.view.animation.ScaleAnimation;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private MediaRecorder mdRecorder = null;
private MediaPlayer mp = null;
private String outputFile = null;
private String savePath = null;
private static final String strFileExn = ".amr";
private Button btnStart, btnPlay;
private ImageView imgAcAction;
private TextView txtAcName, txtAcTimes;
private SwipeMenuListView listView = null;
private Boolean isAcRecording = false;
private Timer timer = new Timer();
private TimerTask task = null;
private Integer iRecordTime = 0;
private boolean sdcardExit;
private ArrayList<String> recordFiles;
private ArrayAdapter<String> adapter;
private Animation mAnimation = null;
// private int deviceWidth;
private void ShowMsg(String msg) {
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
// Log.i("NFSTacRecorder.app", msg);
}
private String getFileName() {
String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
return String.format("AC_%s" + strFileExn, timeStamp);
}
private void startCount() {
iRecordTime = 0;
txtAcTimes.setTextColor(Color.RED);
task = new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() { // UI thread
@Override
public void run() {
iRecordTime++;
txtAcTimes.setText("時長:" + ComData.intToDateTimeString(iRecordTime));
}
});
}
};
timer.schedule(task, 0, 1000);
}
private void stopCount() {
txtAcTimes.setTextColor(Color.BLACK);
iRecordTime = 0;
if (task != null) {
task.cancel();
task = null;
}
}
private void startRecorder() {
btnStart.setText("停止");
String fn = getFileName();
imgAcAction.setImageResource(R.drawable.ic_mic48);
txtAcName.setText(fn);
outputFile = savePath + fn;
// Animation mAnimation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.anim_mic_scale);
mAnimation = new ScaleAnimation(0.5f, 1.0f, 0.5f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
mAnimation.setDuration(1000);
mAnimation.setRepeatCount(Animation.INFINITE);
mAnimation.setRepeatMode(Animation.REVERSE);
imgAcAction.setAnimation(mAnimation);
try {
stopMic();
mdRecorder = new MediaRecorder();
mdRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT); // .mic
mdRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_WB); // .amr
// mdRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); //.3gp
mdRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_WB);
mdRecorder.setOutputFile(outputFile);
// mdRecorder.setMaxDuration(MAX_LENGTH);//錄音最長時間
mdRecorder.prepare();
mdRecorder.start();
// --顯示錄音時長--每一秒顯示一次
// updateMicStatus();
startCount();
recordFiles.add(0, fn);// 插入到第一個,最近錄製的在最上面。
adapter.notifyDataSetInvalidated();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void stopMic() {
if (mdRecorder != null) {
mdRecorder.stop();
mdRecorder.release();
mdRecorder = null;
}
}
private void stopRecorder() {
btnStart.setText("錄音");
imgAcAction.setAnimation(null);
imgAcAction.setImageResource(R.drawable.ic_mic_pressed48);
stopCount();
stopMic();
txtAcTimes.setText("時長:00:00");
}
private void getRecordFiles() {
recordFiles = new ArrayList<String>();
// File files[] = new File(savePath).listFiles();
// if (files != null) {
// for (int i = 0; i < files.length; i++) {
// String fn = files[i].getName().toLowerCase();
// if (fn.endsWith(".mp3") || fn.endsWith(".amr") || fn.endsWith(".mp4") || fn.endsWith(".3gp")) {
// // recordFiles.add(0, files[i].getName());
// recordFiles.add(files[i].getName());
// }
// }
// }
String fn;
File files[] = new File(savePath).listFiles(new SoundFileFilter());
if (files != null) {
for (int i = 0; i < files.length; i++) {
fn = files[i].getName();
// recordFiles.add(fn.substring(0, fn.lastIndexOf(".") - 1));
recordFiles.add(fn);
}
}
Collections.sort(recordFiles);// 升序 一定要先排序后,再倒序,這樣文件名大的會在前面。
Collections.reverse(recordFiles);// 降序
}
private class SoundFileFilter implements FileFilter {
public boolean accept(File pathname) {
if (pathname.getName().endsWith(".mp3")) return true;
if (pathname.getName().endsWith(".mp4")) return true;
if (pathname.getName().endsWith(".amr")) return true;
if (pathname.getName().endsWith(".3gp")) return true;
return false;
}
}
private void playSound(String strFile) {
if (!strFile.isEmpty() && new File(strFile).exists()) {
if (mp == null) mp = new MediaPlayer();
try {
mp.reset();
mp.setDataSource(strFile);
mp.prepare();
mp.setVolume(0.9f, 0.9f);
mp.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
SwipeMenuCreator creator = new SwipeMenuCreator() {
@Override
public void create(SwipeMenu menu) {
// 创建一个Item
SwipeMenuItem renameItem = new SwipeMenuItem(getApplicationContext());
renameItem.setBackground(new ColorDrawable(Color.rgb(0x33, 0x66, 0xcc)));
// openItem.setWidth(dp2px(90));
renameItem.setWidth(DensityUtils.dp2px(MainActivity.this, 70));
renameItem.setTitle("更名");
renameItem.setTitleSize(18);
renameItem.setTitleColor(Color.WHITE);
menu.addMenuItem(renameItem);
// 再创建一个Item
SwipeMenuItem deleteItem = new SwipeMenuItem(getApplicationContext());
deleteItem.setBackground(new ColorDrawable(Color.rgb(0xF9, 0x3F, 0x25)));
deleteItem.setWidth(DensityUtils.dp2px(MainActivity.this, 70));
deleteItem.setTitle("刪除");
deleteItem.setTitleSize(18);
deleteItem.setTitleColor(Color.WHITE);
// deleteItem.setIcon(R.drawable.ic_delete);
menu.addMenuItem(deleteItem);
}
};
// private int getDeviceWidth() {
// return getResources().getDisplayMetrics().widthPixels;
// }
private void changeFileName(final String strOleName, final int intPositon) {
AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.this);
final EditText edtNewName = new EditText(MainActivity.this);
edtNewName.setText(null);
dlg.setTitle("原名稱:" + strOleName).setView(edtNewName).setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (!edtNewName.getText().toString().isEmpty()) {
String strNewName = edtNewName.getText().toString();
if (!strNewName.toLowerCase().endsWith(strFileExn)) strNewName += strFileExn;
FileHelper.rename(savePath + strOleName, savePath + strNewName);
recordFiles.set(intPositon, strNewName);
adapter.notifyDataSetInvalidated();
}
}
}).setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).show();
}
private void initView() {
listView = (SwipeMenuListView) findViewById(R.id.lvRecorder);
btnStart = (Button) findViewById(R.id.btnStart);
btnPlay = (Button) findViewById(R.id.btnPlay);
imgAcAction = (ImageView) findViewById(R.id.imgAcAction);
txtAcTimes = (TextView) findViewById(R.id.txtAcTimes);
txtAcName = (TextView) findViewById(R.id.txtAcName);
// deviceWidth = getDeviceWidth();
savePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + ComData.DST_FOLDER_NAME + File.separator;
FileHelper.createDir(savePath);
// savePathCache = savePath + "Cache" + File.separator;
// checkDir(savePathCache);
// 将菜单生成器设置给ListView即可
listView.setMenuCreator(creator);
getRecordFiles();
adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, recordFiles);
listView.setAdapter(adapter);
}
private void addEventHandler() {
btnStart.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isAcRecording) {
isAcRecording = !isAcRecording;
stopRecorder();
} else {
isAcRecording = !isAcRecording;
startRecorder();
}
}
});
btnPlay.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (outputFile != null && new File(outputFile).exists()) {
playSound(outputFile);
}
}
});
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String fn = savePath + File.separator + recordFiles.get(position);
playSound(fn);
}
});
listView.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(int position, SwipeMenu menu, int index) {
String fn = recordFiles.get(position);
switch (index) {
case 0:
changeFileName(fn, position);
break;
case 1:
ConfirmDialog dlg = new ConfirmDialog(MainActivity.this, "是否刪除選中的錄音文件?");
if (dlg.showDialog() == DialogResult.OK) {
FileHelper.delete(savePath + fn);
recordFiles.remove(position);
adapter.notifyDataSetInvalidated();
ShowMsg(String.format("刪除%s錄音成功!", fn));
}
break;
}
return true;
}
});
listView.setCloseInterpolator(new BounceInterpolator());
listView.setOpenInterpolator(new BounceInterpolator());
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sdcardExit = Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
if (sdcardExit) {
initView();
addEventHandler();
} else {
ShowMsg("請插入擴展卡SDCard");
}
}
private void stopPlay() {
if (mp != null) mp.stop();
}
@Override
protected void onStop() {
stopPlay();
super.onStop();
}
@Override
public void onBackPressed() {
stopPlay();
super.onBackPressed();
}
@Override
protected void onDestroy() {
stopRecorder();
if (timer != null) {
timer.cancel();
timer = null;
}
if (mp != null) {
mp.stop();
mp.release();
mp = null;
}
super.onDestroy();
}
/**
* 更新话筒状态 分贝是也就是相对响度 分贝的计算公式K=20lg(Vo/Vi) Vo当前振幅值 Vi基准值为600:我是怎么制定基准值的呢? 当20
* * Math.log10(mMediaRecorder.getMaxAmplitude() / Vi)==0的时候vi就是我所需要的基准值
* 当我不对着麦克风说任何话的时候,测试获得的mMediaRecorder.getMaxAmplitude()值即为基准值。
* Log.i("mic_", "麦克风的基准值:" + mMediaRecorder.getMaxAmplitude());前提时不对麦克风说任何话
*/
private int BASE = 600;
private int SPACE = 200; // 间隔取样时间
private ImageView view;
private final Handler mHandler = new Handler();
private Runnable mUpdateMicStatusTimer = new Runnable() {
public void run() {
updateMicStatus();
}
};
private void updateMicStatus() {
if (mdRecorder != null && view != null) {
// int vuSize = 10 * mMediaRecorder.getMaxAmplitude() / 32768;
int ratio = mdRecorder.getMaxAmplitude() / BASE;
int db = 0;// 分贝
if (ratio > 1) db = (int) (20 * Math.log10(ratio));
System.out.println("分贝值:" + db + " " + Math.log10(ratio));
// switch (db / 4) {
// case 0:
// view.setImageBitmap(null);
// break;
// case 1:
// view.setImageResource(R.drawable.logo64);
// break;
// case 2:
// view.setImageResource(R.drawable.logo96);
// break;
// case 3:
// view.setImageResource(R.drawable.logo124);
// break;
// case 4:
// view.setImageResource(R.drawable.logo135);
// break;
// case 5:
// view.setImageResource(R.drawable.logo512);
// break;
// default:
// view.setImageResource(R.drawable.ic_launcher);
// break;
// }
mHandler.postDelayed(mUpdateMicStatusTimer, SPACE);
/*
* if (db > 1) { vuSize = (int) (20 * Math.log10(db)); Log.i("mic_",
* "麦克风的音量的大小:" + vuSize); } else Log.i("mic_", "麦克风的音量的大小:" + 0);
*/
}
}
}