Android程序设计:快速录音小程序

在一个项目中要使用到录音,所以找了许多资料,写这个简单程序当练练。最后扩展功能并集成到那个项目。
先看图片:
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);
             */
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值