Android语言基础教程(192)Android多媒体应用播放音频与视频范例之带音量控制的音乐播放器:别再静音人生!手把手教你打造会“呼吸”的BGM播放器,网友:这代码会蹦迪!

打造带音量控制的Android音乐播放器

作为一个常年和Android“斗智斗勇”的老码农,我见过太多播放器:有的点开就静音到让人怀疑人生,有的突然外放吓得同事摔了键盘……今天咱们就撸个带智商+情商的音乐播放器,重点解决两大灵魂拷问:“声儿呢?!” 和**“太吵了闭嘴!”**。

一、MediaPlayer:它可不是个“傻白甜”

别看MediaPlayer只是个类,人家内心戏多着呢!新手常把它当MP3点击器,结果分分钟表演“闪退三连”:

val mediaPlayer = MediaPlayer().apply {
    setDataSource("你的音频路径") // 路径一错,立刻扑街!
    prepare() // 纯真版:在主线程调?系统反手一个ANR警告!
    start() // 最怕此时手机响起微信电话…音乐秒变BGM伴奏
}

高级玩法:给它穿上“防弹衣”!

  1. prepareAsync()异步准备——相当于让播放器自己蹲角落加载,不堵UI线程;
  2. 状态监听连环套
mediaPlayer.setOnPreparedListener {
    // 此时才能安全点击播放,别像急着拆快递把盒子撕烂了
    btn_play.isEnabled = true
}
mediaPlayer.setOnCompletionListener {
    // 循环播放?在这偷偷调mediaPlayer.seekTo(0)就行
}
二、音量控制:SeekBar的“丝滑魔术”

为什么你的音量拖拽像卡了痰?因为缺了进度反馈闭环!记住这个公式:

// ① 初始化音量滑杆(0f-1f范围)
val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
seekBar_volume.max = 100
seekBar_volume.progress = (currentVolume.toFloat() / maxVolume * 100).toInt()

// ② 拖拽时实时调节(这才是灵魂!)
seekBar_volume.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
        if (fromUser) {
            val volume = progress / 100.0f
            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (volume * maxVolume).toInt(), 0)
            // 第三个flag:0表示安静调整,AudioManager.FLAG_PLAY_SOUND会作死地播放调节音效…
        }
    }
})
三、音频焦点争夺战:比抢车位还刺激!

你的App放音乐时突然来了电话?没处理音频焦点的话,就会变成**“音乐+铃声”双重奏**!学学高情商播放器:

val audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run {
    setOnAudioFocusChangeListener { focusChange ->
        when (focusChange) {
            AudioManager.AUDIOFOCUS_LOSS -> mediaPlayer.pause() // 别人霸占车位,咱先熄火
            AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> mediaPlayer.pause() // 临时被抢,比如导航说话
            AudioManager.AUDIOFOCUS_GAIN -> mediaPlayer.start() // 重新抢回方向盘!
        }
    }
    build()
}
// 申请焦点!没这步就像没驾照开车
audioManager.requestAudioFocus(audioFocusRequest)
四、完整代码:会“蹦迪”的播放器长这样!

(Activity完整版,粘贴即用)

class MainActivity : AppCompatActivity() {

    private lateinit var mediaPlayer: MediaPlayer
    private lateinit var audioManager: AudioManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 初始化管理器
        audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
        setupMediaPlayer()
        setupVolumeSeekBar()
    }

    private fun setupMediaPlayer() {
        mediaPlayer = MediaPlayer.create(this, R.raw.your_music) // 资源文件扔进res/raw/
        btn_play.setOnClickListener {
            if (!mediaPlayer.isPlaying) {
                mediaPlayer.start()
                btn_play.text = "暂停"
            } else {
                mediaPlayer.pause()
                btn_play.text = "播放"
            }
        }
    }

    private fun setupVolumeSeekBar() {
        val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
        val currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
        seekBar_volume.max = 100
        seekBar_volume.progress = (currentVolume.toFloat() / maxVolume * 100).toInt()

        seekBar_volume.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                if (fromUser) {
                    audioManager.setStreamVolume(
                        AudioManager.STREAM_MUSIC,
                        (progress / 100.0f * maxVolume).toInt(), 0
                    )
                }
            }
            // 必须重写但用不到的方法…(假装这里有空实现)
        })
    }

    override fun onDestroy() {
        mediaPlayer.release() // 别忘了!否则下次打开播放器会变“哑巴”
        super.onDestroy()
    }
}
五、踩坑预警:血泪总结3条保命技巧
  1. 资源释放是爸爸:MediaPlayer用完必须release(),否则内存泄漏多来几次,App直接表演“原地去世”;
  2. 生命周期要偷窥:App退到后台记得暂停,监听到onPause()时就该让播放器“眯一会儿”;
  3. 异常处理不能浪IllegalStateException是MediaPlayer的祖传大招,所有操作都得包在try-catch里。

现在运行你的播放器——拖动滑杆时,是不是终于有了“掌控全局”的爽感?这不仅是技术实现,更是和Android系统的一场深度对话。当你理解音频焦点、系统服务协作时,你的代码就真正“活”过来了。

彩蛋:想让播放器更智能?试试在音频焦点丢失时自动淡出音量,恢复时再淡入,用户体验直接封神!(提示:用ObjectAnimator操作音量值+Handler延时)


总结:从“哑巴应用”到“智能DJ”,只需搞定MediaPlayer的状态机管理、SeekBar的实时反馈机制、音频焦点的场景化协作。你的代码不止能跑,还能带着节奏跑!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值