Android开发长按录制自己的声音

106 阅读2分钟
Android开发长按录制自己的声音

长按按钮录制声音,有多少s显示,并且录制完可以播放

一、思路:

用AudioManager,思路一句半句说不完的,还是直接上手代码吧

二、效果图:

在这里插入图片描述

三、关键代码:
package com.cong.recordmyvoicenewdemo

import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.graphics.Typeface
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.Settings
import android.view.MotionEvent
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import com.cong.recordmyvoicenewdemo.databinding.ActivityMyAudioRecordBinding
import com.cong.recordmyvoicenewdemo.manager.MyAudioPlayManager
import com.cong.recordmyvoicenewdemo.manager.MyAudioRecordManager
import com.cong.recordmyvoicenewdemo.manager.MyIAudioPlayListener
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.disposables.Disposable
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {

    private var disposableRecordTime: Disposable? = null
    private val recordMaxTime = 15L
    private var localAudioUri: Uri?= null
    private lateinit var mContext: Context

    protected lateinit var mBinding:ActivityMyAudioRecordBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBinding = ActivityMyAudioRecordBinding.inflate(layoutInflater)
        setContentView(mBinding.root)
        mContext = this
        MyAudioRecordManager.getInstance().resetAudioUri()


        mBinding.clStartRecordAudio.setOnTouchListener(mOnVoiceBtnTouchListener)

        mBinding.clStartRecordAudio.setOnClickListener {
            if (mBinding.tvClickPlay.visibility == View.VISIBLE && (localAudioUri != null)){
                if (MyAudioPlayManager.getInstance().isPlaying) {
                    if (!MyAudioPlayManager.getInstance().isStopOrComplete){
                        MyAudioPlayManager.getInstance().pausePlay()
                    } else {
                        MyAudioPlayManager.getInstance().stopPlay()
                    }
                } else {
                    if (!MyAudioPlayManager.getInstance().isStopOrComplete) {
                        MyAudioPlayManager.getInstance().pauseChangeToPlay()
                    } else {
                        MyAudioPlayManager.getInstance().startPlay(
                                mContext, localAudioUri , object :
                                MyIAudioPlayListener {
                            override fun onStart(uri: Uri?) {

                                mBinding.tvClickPlay.setText(R.string.audio_playing)
                            }

                            override fun onStop(uri: Uri?) {

                                mBinding.tvClickPlay.setText(R.string.click_play)
                            }

                            override fun onComplete(uri: Uri?) {

                                mBinding.tvClickPlay.setText(R.string.click_play)

                            }

                            override fun onPause(uri: Uri?) {

                                mBinding.tvClickPlay.setText(R.string.click_play)

                            }

                        }, false
                        )
                    }
                }
            }
        }

        //重录
        mBinding.tvAgainRecord.setOnClickListener {
            if (MyAudioPlayManager.getInstance().isPlaying){
                show(R.string.again_record_playing_tip)
            } else {
                MyAudioPlayManager.getInstance().stopPlay()
                MyAudioRecordManager.getInstance().deleteAudioFile()
                currentRecordTime = 0
                dealCurrentTimeShow(currentRecordTime,true)
            }

        }
    }

    private var currentRecordTime : Long = 0
    private var mLastTouchY = 0f
    private var mUpDirection = false
    private var isStartRecording = false
    @SuppressLint("ClickableViewAccessibility")
    private val mOnVoiceBtnTouchListener =
            View.OnTouchListener { v, event ->
                if (mBinding.tvClickPlay.visibility == View.VISIBLE) {
                    return@OnTouchListener false
                }
                val mOffsetLimit = 70 * v.context.resources.displayMetrics.density
                //val permissions = arrayOf(Manifest.permission.RECORD_AUDIO)
                if (!PermissionAppUtils.hasPermissions(v.context, PermissionAppUtils.Group.RECORD_AUDIO_STORAGE)
                        && event.action == MotionEvent.ACTION_DOWN
                ) {
                        PermissionAppUtils.requestCombinedPermission(mContext,
                                PermissionAppUtils.Group.RECORD_AUDIO_STORAGE) {
                            if (it.granted) {

                            } else {
                                if (it.shouldShowRequestPermissionRationale) {
                                    //禁止,不做操作

                                } else {
                                    //永久禁止,需求说只弹前面的弹框,永久禁止就直接跳设置
                                    startAppSettings(mContext)
                                }
                            }
                        }
                    return@OnTouchListener true
                }
                if (event.action == MotionEvent.ACTION_DOWN) {
                    if (MyAudioPlayManager.getInstance().isPlaying) {
                        MyAudioPlayManager.getInstance().stopPlay()
                    }

                    mBinding.ivStartRecordAudio.setImageResource(R.mipmap.ic_record_audio_press)
                    //开始录音,录之前先删掉之前的本地录的
                    MyAudioRecordManager.getInstance().deleteAudioFile()
                    currentRecordTime = 0
                    isStartRecording = true
                    MyAudioRecordManager.getInstance().startRec(mContext)

                    mLastTouchY = event.y
                    mUpDirection = false
                    if (disposableRecordTime == null || disposableRecordTime!!.isDisposed) {
                        disposableRecordTime = Observable.interval(1, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
                                .take(recordMaxTime)
                                .subscribe {

                                    currentRecordTime = it + 1
                                    dealCurrentTimeShow(currentRecordTime, true)
                                    //录够15s停止录制
                                    if (currentRecordTime == recordMaxTime) {
                                        MyAudioRecordManager.getInstance().stopRec()
                                        showRecordAfter()
                                    }
                                }
                    }

                } else if (event.action == MotionEvent.ACTION_MOVE) {
                    mBinding.ivStartRecordAudio.setImageResource(R.mipmap.ic_record_audio_press)
                    //滑动时暂不处理,让它继续录
//                if (mLastTouchY - event.y > mOffsetLimit && !mUpDirection) {
//                    AudioRecordManager.getInstance().willCancelRecord()
//                    mUpDirection = true
//                    (v as TextView).setText(R.string.rc_voice_press_to_input)
//                    v.background = v.getContext()
//                        .resources
//                        .getDrawable(
//                            R.drawable.rc_ext_voice_idle_button
//                        )
//                } else if (event.y - mLastTouchY > -mOffsetLimit && mUpDirection) {
//                    AudioRecordManager.getInstance().continueRecord()
//                    mUpDirection = false
//                    (v as TextView).background = v.getContext()
//                        .resources
//                        .getDrawable(
//                            R.drawable.rc_ext_voice_touched_button
//                        )
//                    v.setText(R.string.rc_voice_release_to_send)
//                }
                } else if (event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL) {
                    mBinding.ivStartRecordAudio.setImageResource(R.mipmap.ic_record_audio)
                    //松手停止录制
                    if (isStartRecording) {
                        MyAudioRecordManager.getInstance().stopRec()
                        if (currentRecordTime < 3) {
                            show(R.string.record_audio_not_enough_tip)
                            currentRecordTime = 0
                            dealCurrentTimeShow(currentRecordTime, true)
                            MyAudioRecordManager.getInstance().deleteAudioFile()
                        } else {
                            showRecordAfter()
                        }
                        isStartRecording = false
                    }

                    disposableRecordTime?.run {
                        if (!isDisposed) {
                            dispose()
                        }
                    }
                }

                true
            }


    /**
     * @desc : 处理当前时间录制显示
     * @author : congge on 2022-05-19 17:07
     **/
    private fun dealCurrentTimeShow(time:Long,isShow:Boolean){
        if (!isShow){
            mBinding.tvCurrentRecordTime.visibility = View.GONE
            mBinding.progressTime.visibility = View.GONE
            mBinding.tvRecordingTip.visibility = View.GONE
        } else {
            if (time > 0){
                mBinding.tvCurrentRecordTime.text = "${time}s"
                mBinding.tvCurrentRecordTime.setTextSizes(24)
                mBinding.tvCurrentRecordTime.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
                mBinding.tvCurrentRecordTime.setTextColor(getColors(R.color.color_1C1C1E))
                mBinding.progressTime.visibility = View.VISIBLE
                mBinding.progressTime.setCurrent(time.toInt())
                mBinding.tvRecordingTip.visibility = View.VISIBLE
            } else {
                mBinding.tvCurrentRecordTime.setText(R.string.record_audio_max_time)
                mBinding.tvCurrentRecordTime.setTextSizes(14)
                mBinding.tvCurrentRecordTime.setTextColor(getColors(R.color.color_636366))
                mBinding.tvCurrentRecordTime.typeface = Typeface.defaultFromStyle(Typeface.NORMAL)
                mBinding.progressTime.visibility = View.GONE
                mBinding.tvRecordingTip.visibility = View.GONE
                mBinding.ivStartRecordAudio.setImageResource(R.mipmap.ic_record_audio)
                mBinding.tvClickPlay.visibility = View.GONE
                mBinding.tvAgainRecord.visibility = View.GONE

            }
            mBinding.tvCurrentRecordTime.visibility = View.VISIBLE
        }
    }

    /**
     * @desc : 展示录制后的界面或者之前有语音
     * @author : congge on 2022-05-20 9:21
     **/
    private fun showRecordAfter(voiceUrl:String ?= "",duration:Int = 0){
        if (MyAudioRecordManager.getInstance().audioUri != null ){
            localAudioUri = MyAudioRecordManager.getInstance().audioUri
            mBinding.tvAgainRecord.visibility = View.VISIBLE
            mBinding.ivStartRecordAudio.setImageResource(R.mipmap.ic_audio_play)
            mBinding.tvClickPlay.visibility = View.VISIBLE
            mBinding.progressTime.visibility = View.GONE
            mBinding.tvRecordingTip.visibility = View.GONE
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        MyAudioRecordManager.getInstance().stopRec()
        MyAudioPlayManager.getInstance().stopPlay()
        disposableRecordTime?.run {
            if (!isDisposed){
                dispose()
            }
        }
    }

    /**
     * @desc : 跳转到应用的设置界面
     * @author : congge on 2022-02-25 14:46
     **/
    fun startAppSettings(context: Context) {
        val intent = Intent(
                Settings.ACTION_APPLICATION_DETAILS_SETTINGS
        )
        intent.data = Uri.parse("package:" + context.packageName)
        context.startActivity(intent)
    }
}
四、项目demo源码结构图:

在这里插入图片描述
有问题或者需要完整源码demo的私信我,我每天都看私信