Android应用-简易录音机的实现

作者: jkGeng | 来源:发表于2017-10-14 22:11 被阅读114次

前言
录音机是大家手机里的常见应用程序。今天我们来设计一个简单的录音机应用,同时了解一下相关的Android知识。

开发工具
Android Studio 2.3.3

实现过程
界面设计
界面上设置了开始录音(停止录音)、播放录音(暂停播放)的Button,显示当前录音或播放时间的Textview和显示录音音量的ProgressBar。
活动的.xml文件是通过Android的约束布局(ConstraintLayout)编辑的,这种布局适合使用可视化的方式来编写界面,通过约束的方式来指定各个控件的位置和关系,可以有效地解决布局嵌套过多的问题。由于相应的代码会自动生成,这里就不展示代码了。
感兴趣的读者可以参考下面这篇文章,里面比较详细地讲解了约束布局的常见用法:http://blog.csdn.net/guolin_blog/article/details/53122387
效果如图:

image.png

权限申请
程序运行前首先需要申请以下权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>)
<uses-permission android:name="android.permission.RECORD_AUDIO"/>  

分别表示调用麦克风和SD卡写入

其他功能
首先,在活动中添加控件

private ImageButton start;
private ImageButton play;
private TextView time;
private ImageView volume;
private ProgressBar progressBar;

主活动中还定义了如下变量:

    private MediaRecorder mediaRecorder=new MediaRecorder();  //用于录音
    private File file=new File("/mnt/sdcard","new.amr");  //创建一个临时的音频文件
    private MediaPlayer mediaPlayer=new MediaPlayer();  //用于播放录音
    private long currenttime;  //用于确定当前录音时间
    private boolean isrecording=false;  //用于判断当前是否在录音
    private boolean Isplaying=false;  //用于判断是否正在处于播放录音状态

然后,在OnCreate()中进行初始化:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        start=(ImageButton)findViewById(R.id.startButton);
        play=(ImageButton)findViewById(R.id.playButton);
        time=(TextView)findViewById(R.id.time);
        volume=(ImageView)findViewById(R.id.volume);
        progressBar=(ProgressBar)findViewById(R.id.progress);
        start.setOnClickListener(this);
        play.setOnClickListener(this);
        mediaPlayer.setOnCompletionListener(playerListener);
    }

除此之外,由于录音音量和录音/播放时间需要隔一段时间显示一次,需要在线程中调用相关方法,具体实现如下:

    //定义Handler
    private Handler volumehandler=new Handler();  //显示音量
    private Handler timehandler=new Handler();  //显示录音时间
    private Handler playhandler=new Handler();  //显示播放时间
private Runnable runnable_1=new Runnable() {  //音量
        @Override
        public void run() {
            int volume=0;
            int ratio;
            ratio = mediaRecorder.getMaxAmplitude() / 600;
            if(ratio>1){
                volume= (int) (20 * Math.log10(ratio));
            } //将getMaxAmplitude()的返回值转换为分贝
            progressBar.setMax(15);//将最大音量设置为60分贝
            progressBar.setProgress(volume/4);//显示录音音量
            volumehandler.postDelayed(runnable_1, 100);
        }
    };
private Runnable runnable_2=new Runnable() {//录音时间
        @Override
        public void run() {
            time.setText(getTime((int) (System.currentTimeMillis()-currenttime))); 
//调用前先获取当前时间,再通过System.currentTimeMillis()-currenttime获得录音时间并转换成文本
            timehandler.postDelayed(runnable_2,1000);
        }
    };
private Runnable runnable_3=new Runnable() {//播放时间
        @Override
        public void run() {
            time.setText(getTime(mediaPlayer.getCurrentPosition()));
//MediaPlayer类的getCurrentPosition()方法可以获得音频的当前播放时间
            playhandler.postDelayed(runnable_3,1000);
        }
    };

这里getTime方法用于将一个毫秒值转换为“xx:xx”的文本,代码如下:

private String getTime(int timeMs) {
        int total=timeMs/1000;
        StringBuilder stringBuilder=new StringBuilder();
        Formatter formatter=new Formatter(stringBuilder,Locale.getDefault());
        int seconds=total%60;
        int minutes=(total/60)%60;
        int hours=total/3600;
        stringBuilder.setLength(0);
        if (hours>0){
            return formatter.format("%d:%02d:%02d",hours,minutes,seconds).toString();
        }
        else{
            return formatter.format("%02d:%02d",minutes,seconds).toString();
        }
    }

按钮逻辑

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.startButton:
                if (isrecording) {
                    stop();
                } else {
                    record();
                }
                break;
            case R.id.playButton:
                if(mediaPlayer.isPlaying()){
                    pause();
                }
                else{
                    play();
                }
                break;
            default:
                break;

        }
    }

录音是通过MediaRecorder类实现的,具体的录音和停止方法如下:

private void record(){
        if(file.exists()) {
            file.delete();
        }
        mediaRecorder.reset();
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        //设置音频来源为麦克风
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
        //默认的输出格式和编码方式
        mediaRecorder.setOutputFile(file.getAbsolutePath());//指定音频输出路径
        try {
            mediaRecorder.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mediaRecorder.start();//开始录音
        start.setImageResource(ic_media_pause);
        isrecording=true;
        currenttime=System.currentTimeMillis();
        volumehandler.postDelayed(runnable_1,100);
        timehandler.postDelayed(runnable_2,1000);
    }
private void stop(){
        mediaRecorder.stop();
        start.setImageResource(btn_radio);
        volumehandler.removeCallbacks(runnable_1);
        timehandler.removeCallbacks(runnable_2);//停止相关线程
        time.setText("00:00");
        isrecording=false;
        Toast.makeText(this,"录音完成!", Toast.LENGTH_SHORT).show();
    }

播放/暂停音频是通过MediaPlayer类实现的,具体实现方法如下:

private void play(){
        if(!Isplaying) {
            if (file != null && file.exists()) {
                try {
                    mediaPlayer.setDataSource(MainActivity.this, Uri.fromFile(file));//设置音频来源
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                Toast.makeText(this, "未发现录音文件", Toast.LENGTH_SHORT).show();
                return;
            }
            try {
                mediaPlayer.prepare();
            } catch (IOException e) {
                e.printStackTrace();
            }
            Isplaying = true;
        }
        mediaPlayer.start();//开始播放
        play.setImageResource(ic_media_pause);
        playhandler.postDelayed(runnable_3,1000);
    }
 private void pause(){
        mediaPlayer.pause();
        playhandler.removeCallbacks(runnable_3);
        play.setImageResource(ic_media_play);
    }

设置OnCompletionListener监听音频播放完成:

MediaPlayer.OnCompletionListener playerListener = new MediaPlayer.OnCompletionListener(){
        @Override
        public void onCompletion(MediaPlayer mediaPlayer) {
            play.setImageResource(ic_media_play);
            playhandler.removeCallbacks(runnable_3);
            time.setText("00:00");
            Isplaying=false;
            mediaPlayer.reset();
        }
    };

可拓展处
实现用户自定义录音文件名称和保存路径、实现用户选择录音文件播放、界面进一步优化等。

个人总结
通过这次的小项目,学到了一些相关的知识。但是由于刚刚入门不久,这个项目的实现非常简单,有些地方也参考了别人的Demo,可以拓展的方面还有很多,接下来还需要更多的研究和学习。如果文章存在错误,欢迎读者批评指正。
谢谢大家!

相关文章

网友评论

    本文标题:Android应用-简易录音机的实现

    本文链接:https://www.haomeiwen.com/subject/qpifuxtx.html