Android开发全局音量调整的实现方式详解
ChenYhong 人气:0引言
之前参与过一个项目,开发的是一个系统级别的软件,安装在定制的设备上,设备没有控制音量的按键,因此软件需要实现一个在任意页面都能控制音量的功能。
实现方案是在所有页面的顶部加上一个触发音量控制弹窗的按钮,用户点击该按钮后显示音量控制弹窗。
全局添加按钮
参与项目时,已经出了第一版了,包含的页面很多,因此一个个页面去加肯定不合适。项目中所有Activity
都继承了一个自定义的BaseActivity
,所以只能在这个BaseActivity
中做文章。
Android中,每个Activity
都包含一个DecorView
,DecorView
内部包含一个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) } }
效果如图:
加载全部内容