Android开发全局音量调整的实现方式详解

引言

之前参与过一个项目,开发的是一个系统级别的软件,安装在定制的设备上,设备没有控制音量的按键,因此软件需要实现一个在任意页面都能控制音量的功能。

实现方案是在所有页面的顶部加上一个触发音量控制弹窗的按钮,用户点击该按钮后显示音量控制弹窗。

全局添加按钮

参与项目时,已经出了第一版了,包含的页面很多,因此一个个页面去加肯定不合适。项目中所有Activity都继承了一个自定义的BaseActivity,所以只能在这个BaseActivity中做文章。

Android中,每个Activity都包含一个DecorViewDecorView内部包含一个FrameLayout,可以通过android.R.id.content来获取,我们的布局包含在这个FrameLayout中。

因此如果需要在所有的页面都添加View,那么在BaseActivity中实现向android.R.id.content对应的FrameLayout添加View的逻辑,然后所有的Activity就都可以自动添加View了。

实现代码如下:

object DensityUtil {
 @JvmStatic
 fun dp2Px(dpValue: Int): Int {
 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue.toFloat(), Resources.getSystem().displayMetrics).toInt()
 }
 @JvmStatic
 fun px2Dp(pxValue: Int): Int {
 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, pxValue.toFloat(), Resources.getSystem().displayMetrics).toInt()
 }
}
// Base类
open class BaseActivity : AppCompatActivity() {
 override fun onResume() {
 super.onResume()
 // 在onResume中添加,确保在主布局添加完成后添加,避免被遮挡。
 initVolumeControllerView()
 }
 private fun initVolumeControllerView() {
 val controllerView = AppCompatImageView(this)
 controllerView.layoutParams = FrameLayout.LayoutParams(DensityUtil.dp2Px(80), DensityUtil.dp2Px(12)).apply {
 gravity = Gravity.START
 marginStart = DensityUtil.dp2Px(20)
 topMargin = DensityUtil.dp2Px(10)
 }
 controllerView.setImageResource(R.drawable.shape_vollume_controller)
 controllerView.setOnClickListener {
 runOnUiThread { Toast.makeText(this, "点击了全局按钮", Toast.LENGTH_SHORT).show() }
 }
 val rootView = findViewById<FrameLayout>(android.R.id.content)
 rootView.addView(controllerView)
 }
}

效果如图:

音量控制

AudioManager类提供了控制音量的方法。

实现音量控制代码如下:

class VolumeControllerDialog : DialogFragment() {
 private var binding: LayoutVolumeContollerDialogBinding? = null
 private var currentVolume = 0
 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
 dialog?.window?.run {
 setBackgroundDrawable(ContextCompat.getDrawable(requireContext(), android.R.color.transparent))
 decorView.setBackgroundResource(android.R.color.transparent)
 val layoutParams = attributes
 layoutParams.width = DensityUtil.dp2Px(360)
 layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT
 layoutParams.gravity = Gravity.CENTER
 attributes = layoutParams
 }
 binding = DataBindingUtil.inflate(inflater, R.layout.layout_volume_contoller_dialog, container, false)
 return binding?.root
 }
 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 super.onViewCreated(view, savedInstanceState)
 val audioManager = requireContext().getSystemService(Context.AUDIO_SERVICE) as AudioManager
 currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
 val step = 1
 binding?.run {
 btnMute.text = getMuteButtonString(audioManager.isStreamMute(AudioManager.STREAM_MUSIC))
 btnIncreaseVolume.setOnClickListener {
 // 增加音量
 currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
 audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume + step, AudioManager.FLAG_SHOW_UI or AudioManager.FLAG_PLAY_SOUND)
 btnMute.text = getMuteButtonString(audioManager.isStreamMute(AudioManager.STREAM_MUSIC))
 }
 btnReduceVolume.setOnClickListener {
 //减少音量
 currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
 audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, if (currentVolume - step < 0) 0 else currentVolume - step, AudioManager.FLAG_SHOW_UI or AudioManager.FLAG_PLAY_SOUND)
 btnMute.text = getMuteButtonString(audioManager.isStreamMute(AudioManager.STREAM_MUSIC))
 }
 btnMute.setOnClickListener {
 // 静音或取消静音
 val currentMute = audioManager.isStreamMute(AudioManager.STREAM_MUSIC)
 if (currentVolume == 0) {
 btnMute.text = getMuteButtonString(true)
 } else {
 btnMute.text = getMuteButtonString(!currentMute)
 }
 val setVolume = if (currentMute) {
 currentVolume
 } else {
 currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
 0
 }
 audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, setVolume, AudioManager.FLAG_SHOW_UI or AudioManager.FLAG_PLAY_SOUND)
 }
 }
 }
 private fun getMuteButtonString(mute: Boolean): String {
 return if (mute) "UnMute" else "Mute"
 }
}
open class BaseActivity : AppCompatActivity() {
 override fun onResume() {
 super.onResume()
 // 在onResume中添加,确保在主布局添加完成后添加,避免被遮挡。
 initVolumeControllerView()
 }
 private fun initVolumeControllerView() {
 val controllerView = AppCompatImageView(this)
 controllerView.layoutParams = FrameLayout.LayoutParams(DensityUtil.dp2Px(80), DensityUtil.dp2Px(12)).apply {
 gravity = Gravity.START
 marginStart = DensityUtil.dp2Px(20)
 topMargin = DensityUtil.dp2Px(10)
 }
 controllerView.setImageResource(R.drawable.shape_vollume_controller)
 controllerView.setOnClickListener {
 VolumeControllerDialog().show(supportFragmentManager, null)
 }
 val rootView = findViewById<FrameLayout>(android.R.id.content)
 rootView.addView(controllerView)
 }
}

效果如图:

作者:ChenYhong

%s 个评论

要回复文章请先登录注册