android实现录音功能demo

范彭亮
2023-12-01

主要功能实现

主要实现录音功能,录音,停止录音,保存录音,然后可以查看录音列表,还用sqlite数据库实现了用户的注册登录功能。

视频演示

视频

录音相关代码

录音fragment,RecordingFragment

package example.com.recording.fragments;

import android.content.DialogInterface;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import de.greenrobot.event.EventBus;
import example.com.recording.R;
import example.com.recording.bean.ListChangeEvent;
import example.com.recording.util.date.DateFormatUtil;

/**
 * 录音的fragment
 */

public class RecordingFragment extends Fragment implements View.OnClickListener {

    // 开始录音
    private Button start;
    // 重置
    private Button reset;
    // 录音类
    private MediaRecorder mediaRecorder;
    // 以文件的形式保存
//    private File recordFile;

    private TextView titleTv;

    //总的分钟、秒的显示TextView
    private TextView timeTv1, timeTv2;
    //总秒
    private int seconds;
    //总分钟
    private int minute;
    //执行周期性或定时任务类
    private ScheduledExecutorService service;

    //线程是否停止的判断
    private boolean isStart = false;
    //是否重置的判断重置了handler就不在接收消息
    private boolean isReset = false;
    //handler接收线程传过来的消息显示当前定时时间
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:
                    setData();
                    break;
            }
        }
    };

    //文件路径
    private String fileName;
    //获取sd根目录
    public static final String ROOT_FILE = Environment.getExternalStorageDirectory().getAbsolutePath();

    private String path;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_recording, null);
        initView(view);
        Listener();
        titleTv.setText("录音");
        return view;
    }

    private void initView(View view) {
        titleTv = (TextView) view.findViewById(R.id.base_title_tv);
        start = (Button) view.findViewById(R.id.start);
        reset = (Button) view.findViewById(R.id.reset);
        reset.setEnabled(false);
        timeTv1 = (TextView) view.findViewById(R.id.time_tv1);
        timeTv2 = (TextView) view.findViewById(R.id.time_tv2);
        path = ROOT_FILE + "/" + getActivity().getPackageName() + "/record";
    }

    private void Listener() {
        start.setOnClickListener(this);
        reset.setOnClickListener(this);
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public void onClick(View v) {
        int Id = v.getId();
        switch (Id) {
            case R.id.start:
                isReset = false;
                if (isStart) {
                    isStart = false;
                    pauseRecording();
                    reset.setEnabled(true);
                    pauseTime();
                    start.setText("开始");
                } else {
                    isStart = true;
                    start.setText("暂停");
                    reset.setEnabled(false);
                    startRecording();
                    start();
                }
                break;
            case R.id.reset:
                isStop = true;
                isReset = true;
                isStart = false;
                start.setText("开始");
                if (service != null) {
                    resetTime();
                }
                stopRecord();
                break;
        }
    }

    private boolean isStop = true;

    //录音完成保存或删除文件
    private void recordOver(String s) {
        final File recordFile = new File(s);
        if (recordFile != null && recordFile.exists()) {
            final EditText et = new EditText(getActivity());
            et.setText(recordFile.getName().substring(0, recordFile.getName().length() - 4));
            new AlertDialog.Builder(getActivity()).setTitle("文件名")
                    .setView(et)
                    .setCancelable(false)
                    .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            String s = et.getText().toString();
                            //文件名为空就按源文件保存
                            if (s.isEmpty()) {
                                Toast.makeText(getActivity(), "文件名不能为空", Toast.LENGTH_SHORT).show();
                            } else {
                                recordFile.renameTo(new File(path + "/" + s + ".amr"));
                                EventBus.getDefault().post(new ListChangeEvent());
                            }
                        }
                    })
                    .setNegativeButton("删除", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            recordFile.delete();
                        }
                    })
                    .show();
        } else {
            Toast.makeText(getActivity(), "还未录制", Toast.LENGTH_SHORT).show();
        }

    }

    //暂停计时
    private void pauseTime() {
        service.shutdown();
    }

    //重置后的操作
    private void resetTime() {
        service.shutdown();
        service = null;
        seconds = 0;
        minute = 0;
        setText();
    }

    //设置时间数据
    private void setData() {
        if (!isReset) {
            if (seconds == 59) {
                seconds = 0;
                minute = minute + 1;
                if (minute == 59) {
                    minute = 0;
                    Toast.makeText(getActivity(), "已是最大数值", Toast.LENGTH_SHORT).show();
                    start.setText("开始");
                    isStart = false;
                    service.shutdown();
                }
            } else {
                seconds = seconds + 1;
            }
            setText();
        }
    }

    //显示时间
    private void setText() {
        timeTv1.setText(getNum(minute));
        timeTv2.setText(getNum(seconds));
    }

    //数据转换成两位String
    private String getNum(int num) {
        return String.format("%02d", num);
    }

    //保存每次录制的路径
    private List<String> mList = new ArrayList<>();

    //开始录音
    private void startRecording() {

        if(isStop){
            isStop = false;
            mList.clear();
        }

        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }

        fileName = path + "/" + getTime() + ".amr";
        mediaRecorder = new MediaRecorder();
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        // 选择amr格式
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
        mediaRecorder.setOutputFile(fileName);
        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        try {
            // 准备好开始录音
            mediaRecorder.prepare();
            mediaRecorder.start();
        } catch (IllegalStateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    //暂停录音
    private void pauseRecording() {
        try {
            mediaRecorder.stop();
            mediaRecorder.release();
            //录制的文件路径添加到列表
            mList.add(fileName);
        } catch (Exception e) {
            Log.e("Exception", e.getMessage(), e);
            Toast.makeText(getActivity(), "录制异常", Toast.LENGTH_SHORT).show();
        }
    }

    // 完成录音
    private void stopRecord() {

        reset.setEnabled(false);
        // 最后合成的音频文件
        fileName = path + "/" + getTime() + ".amr";
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(fileName);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        FileInputStream fileInputStream = null;
        try {
            for (int i = 0; i < mList.size(); i++) {

                Log.e("stopRecord",mList.get(i));
                File file = new File(mList.get(i));
                // 把因为暂停所录出的多段录音进行读取
                fileInputStream = new FileInputStream(file);
                byte[] mByte = new byte[fileInputStream.available()];
                int length = mByte.length;
                // 第一个录音文件的前六位是不需要删除的
                if (i == 0) {
                    while (fileInputStream.read(mByte) != -1) {
                        fileOutputStream.write(mByte, 0, length);
                    }
                }
                // 之后的文件,去掉前六位
                else {
                    while (fileInputStream.read(mByte) != -1) {

                        fileOutputStream.write(mByte, 6, length - 6);
                    }
                }
            }
            recordOver(fileName);
        } catch (Exception e) {
            // 这里捕获流的IO异常,万一系统错误需要提示用户
            e.printStackTrace();
            Toast.makeText(getActivity(), "录音合成出错,请重试!", Toast.LENGTH_LONG).show();
        } finally {
            try {
                fileOutputStream.flush();
                fileInputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        // 不管合成是否成功、删除录音片段
        for (int i = 0; i < mList.size(); i++) {
            File file = new File(mList.get(i));
            if (file.exists()) {
                file.delete();
            }
        }

    }

    //获取当前时间作为文件名
    private String getTime() {
        return DateFormatUtil.getDateFormat(System.currentTimeMillis(), "yyyyMMddHHmmss");
    }

    //启动倒计时
    private void start() {
        service = Executors.newScheduledThreadPool(1);
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                handler.sendEmptyMessage(1);
            }
        }, 1, 1, TimeUnit.SECONDS);
    }

}

相关xml界面
fragment_recording

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <include
        android:id="@+id/title"
        layout="@layout/base_title" />

    <LinearLayout
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:background="@drawable/shape_oval"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/time_tv1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:gravity="center"
            android:text="00"
            android:textSize="35sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=":"
            android:textSize="35sp" />

        <TextView
            android:id="@+id/time_tv2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:gravity="center"
            android:text="00"
            android:textSize="35sp" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="horizontal">

        <Button
            android:id="@+id/reset"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/start"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="20dp"
            android:text="@string/reset" />

        <Button
            android:id="@+id/start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="20dp"
            android:text="@string/start" />
    </LinearLayout>
</LinearLayout>

shape_oval

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">

    <solid android:color="@color/base_orange"/>


</shape>

下载链接

demo下载链接

 类似资料: