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的私信我,我每天都看私信