在RK平台-Android15下拉框,新增 音量控制条
文章目录
前言
在Android15 平台定制化产品中的下拉框中新增音量条调节功能,市场上部分教育类产品见到过这样的使用需求场景、手机都是有的。实际的默认的Android源码是没有这个功能的,自己来实现下。
一、 需求
在RK3568 Android15 产品上面实现 SystemUI 下拉框新增音量条功能。
需要实现基本效果图如下:

二、参考资料
Android12_SystemUI下拉框新增音量控制条
Android13_SystemUI下拉框新增音量控制条
提醒:
- 上面是之前自己总结 在MTK平台 Android12 Android13 上面实现的 下拉框增加音量条的需求,已经帮助很多人在不同芯片平台不同Android 版本上面实现功能。
- 这里 以RK3576 平台,Android15 上面再次实现功能
- 自己经验来说:Android12/Android13 中的实现方式对于Android15 来说也具有极大的参考价值,其中架构思想依然没有变化,很多类更换了或者新增部分方法,都可以参考借鉴,并稍微修改实现即可。
三、更新文件-实现方案
更新文件-新增文件
修改的文件
frameworks/base/packages/SystemUI/AndroidManifest.xml
frameworks/base/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
frameworks/base/packages/SystemUI/AndroidManifest.xml
新增的文件
frameworks/base/packages/SystemUI/res/drawable-hdpi/icon_volume_test.png
frameworks/base/packages/SystemUI/res/drawable-xhdpi/icon_volume_test.png
frameworks/base/packages/SystemUI/res/drawable-xxhdpi/icon_volume_test.png
frameworks/base/packages/SystemUI/res/drawable-xxxhdpi/icon_volume_test.png
frameworks/base/packages/SystemUI/res/drawable/volume_progress_drawable.xml
frameworks/base/packages/SystemUI/res/drawable/volume_progress_full_drawable.xml
frameworks/base/packages/SystemUI/res/layout/quick_settings_volume_dialog.xml
frameworks/base/packages/SystemUI/src/com/android/systemui/settings/volume/
frameworks/base/packages/SystemUI/src/com/android/systemui/util/ApplicationContextProvider.kt
frameworks/base/packages/SystemUI/src/com/android/systemui/util/ContextProvider.java
frameworks/base/packages/SystemUI/src/com/android/systemui/util/SoundUtils.kt
frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeChangeObserver.java
frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeController.java
frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeSliderController.java
frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeSliderView.java

实现方案
思路
如果是第一次集成,强烈建议优先看一看之前的篇章,会有更好的思路,特别是Android13 集成,它的代码和Android15 差不多,如下:
Android12_SystemUI下拉框新增音量控制条
Android13_SystemUI下拉框新增音量控制条
集成分几个步骤和要解决的问题:
- 1) 思想:完全copy 已有的亮度控制条逻辑实现,熟悉、理解亮度条 实现原理、架构、步骤。再进一步去实现需求,添加音量条功能
- 2) 音量条UI创建,知道如何创建音量条、逻辑、业务搞清楚
- 3) 音量条UI添加到QsPanel 里面去
- 4) 添加的UI音量条如何受控
- 5) 高版本源码、不分架构变化 存在音量条的监听、控制逻辑需要自行实现了
具体实现
再次建议,再编码之前,建议看一下之前的文章Android12 / Android13 上面的指导,同步看一下 文章后面的架构分析,方便理解、高效编码。
1、UI定义部分-VolumeSliderView-ToggleSeekBar
VolumeSliderView 路径:frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeSliderView.java
看类定义,就是一个 FrameLayout,它是用来包裹音量条SeekBar 的,看类定义:用来展示和操作SeekBar的
看左侧的方法不都是用来操作 SeekBar 的嘛:它本身持有 ToggleSeekBar


ToggleSeekBar 路径:frameworks/base/packages/SystemUI/src/com/android/systemui/brightness/ToggleSeekBar.java
这个就是一个进度条,这里用来亮度显示的,我们可以直接用它来作为音量的进度控制条,它就是一个SeekBar

2、接口定义-ToggleSlider
frameworks/base/packages/SystemUI/src/com/android/systemui/brightness/ToggleSlider.java
这个就是一个进度条解控,这里用来定义亮度接口控制和回调的,我们可以直接用它来作为音量的进度控制条的控制接口定义,便于更好控制SeekBar

3、UI资源定义
这里完全参考亮度条取名,直接复制改名,然后:更为为自己的UI资源

3、添加UI到QSPanel -QSPanelController- VolumeSliderController - VolumeController
ViewModel 类:VolumeSliderController
直接看类定义:这个类就是一个ViewModel
/**
* {@code ViewController} for a {@code VolumeSliderView}
*
* This class allows to control the views of a {@code VolumeSliderView} and get callbacks
* when the views change value. It also propagates and manipulates another {@link ToggleSlider} as a
* mirror.
*
* @see VolumeSliderController
*/
public class VolumeSliderController extends ViewController<VolumeSliderView> implements ToggleSlider {
工厂模式,加载布局:

布局绑定设置监听事件:

对外音量控制类 VolumeController
简要说明这个类的作用:
- 释放对外控制音量条接口、对SeekBar 的回调
- 对内包装一层,封装音量调ViewModel, 实现对音量条控制的传递。是一个实际意义上的控制器。
- 不仅仅持有控制SeekBar 的方法接口,并传递到内部控制,动画也是在这个控制层实现的。
看类定义,就是一个实现 SeekBar 控制器接口的类:
这里特别强调一下: 内部持有 private final ToggleSlider mControl; 这个特别重要:持有 SeekBar 控制器接口,如果这个接口是ViewModel 是Toggleslider 实现类。这就实现了
接口引用,封装了一层ViewModel 的控制器,我们再看看 ViewModel VolumeSliderController类的 定义:public class VolumeSliderController extends ViewController<VolumeSliderView> implements ToggleSlider {

**看类的创建,工厂模式 **
如,我们上面分析,传递的就是一个 SeekBar 的控制器类 ToggleSlider,属于接口传递范畴:

QSPanelController-QS面板控制器
这个类,单从 音量控制部分,主要做了以下几个事情:如上部分分析
- 绑定音量条View 到ViewModel 类
VolumeSliderController - 绑定音量控制器
VolumeController和ViewModel 类VolumeSliderController - 初始化、设置监听等
在构造方法中:绑定音量条View 到ViewModel 类 VolumeSliderController , 绑定音量控制器 VolumeController和ViewModel 类 VolumeSliderController, 如下:
// modify by fangchen start
Log.d(TAG,"============QSPanelController gouzao ====")
mVolumeSliderController = mVolumeSliderFactory.create(getContext(), mView);
mView.setVolumeView(mVolumeSliderController.getRootView());
mVolumeController = volumeControllerFactory.create(mVolumeSliderController);
// modify by fangchen end
viewModel init 操作处理View生命周期:
这里有一个init 操作,ViwModel 里面机制,如下: mVolumeSliderController.init()
@Override
public void onInit() {
super.onInit();
mMediaHost.setExpansion(MediaHostState.EXPANDED);
mMediaHost.setShowsOnlyActiveMedia(false);
mMediaHost.init(MediaHierarchyManager.LOCATION_QS);
mQsCustomizerController.init();
mBrightnessSliderController.init();
// modify by fangchen start
Log.d(TAG,"============onInit mVolumeSliderController.init ====")
mVolumeSliderController.init();
// modify by fangchen end
}

添加音量条到QS 控制面板 QSPanel
前面讲的几个都是 控制相关的类,这里搞清楚 音量调、进度条 到底是如何添加到 QS 快捷面板QSPanel 里面去的。
看 QSPanel 类定义,如下:它就是我们属性的快捷面板,下拉或者展开时候的一个View ,在之前的 SystemUI 专栏 其实已经对它很熟悉了。
/** View that represents the quick settings tile panel (when expanded/pulled down). **/
public class QSPanel extends LinearLayout implements Tunable {
参考添加亮度条的案例,原有的逻辑,如下:

我们参考后,自己写一个音量条的UI,添加进去不就可以了嘛:这里注意添加有顺序 index 的,亮度调为0 放在第一位,音量条设置为1, 放在第二个位置即可: addView(view, 1);

问题:既然我们添加了音量条VolumeView 到 QSPanel 中,那这个方法到底在哪里调用的:继续看 QSPanel 控制器 QSPanelController,如下代码,再次分析:

// modify by fangchen start
Log.d(TAG,"============QSPanelController gouzao ====")
mVolumeSliderController = mVolumeSliderFactory.create(getContext(), mView);
mView.setVolumeView(mVolumeSliderController.getRootView());
mVolumeController = volumeControllerFactory.create(mVolumeSliderController);
// modify by fangchen end
做了以下几个事情:其中就是创建亮度条,放到QSPanel 对应位置中去
mVolumeSliderFactory.create(getContext(), mView):mVolumeSliderFactory工厂绑定父View QSPanel View,并同步加载音量条mView.setVolumeView(mVolumeSliderController.getRootView());:QsPanel设置音量条加载到QSPanel的view中,放到对应位置中去volumeControllerFactory.create(mVolumeSliderController);:音量控制器和音量ViewModel之间的绑定
4、音量控制
前面分析了UI 、资源、各种控制器Controller,那么音量进度条到底怎么控制音量,下发控制的。也就是说 拖动音量进度条,如何控制音量。
其实在分析 VolumeSliderController 这个ViewModel 已经讲过 设置监听事件,再次展示如下:

但是在ViewModel 中并没有直接处理,这个它做好了本分工作只处理View 跟 Data 的数据绑定工作,控制类它不处理:

继续看这个回调接口:mListener

如下:它就是SeekBar 接口 ToggleSlider 定义的一个方法,

那 这个Listener 到底是谁? 如下: 不就是 VolumeController 嘛,如下定义及回调:在控制器中处理控制业务,控制音量业务。


5、音量监听 - VolumeChangeObserver
上面分析了基本所有的业务:UI、控制、模型、封装、绑定、事件,实测有个问题,在下拉框展示的时候,去控制音量,那么你的音量条需要自动跟随音量变化来变化UI的。
如下,操控右边音量条情况下,下拉框中的音量条应该同步跟随变化才行。

以前Android12 机制自带,Android13 、Android14、 Android15 需要自己写了。 所以我们需要自己监听音量变化来更新UI。
源码如下:
package com.android.systemui.settings.volume;
import android.content.Context;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.os.Handler;
import android.util.Log;
import com.android.systemui.util.SoundUtils;
public class VolumeChangeObserver extends ContentObserver {
private Context mContext;
// private VolumeControllerListener mVolumeControllerListener;
private VolumeController mVolumeControllerListener;
private int toSetVolume=0;
private final String TAG = "VolumeChangeObserver";
public VolumeChangeObserver(Context context, VolumeController volumeControllerListener, Handler handler) {
super(handler);
mContext = context;
mVolumeControllerListener=volumeControllerListener;
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
//Log.d(TAG, "System volume setting changed. Current music volume: " + currentVolume);
int nowVoiceValue = SoundUtils.INSTANCE.get100CurrentVolume();
Log.d(TAG," onChange===toSetVolume:"+toSetVolume + " nowVoiceValue:"+nowVoiceValue);
if (toSetVolume==0&&nowVoiceValue!=0) {
Log.d(TAG,"==============toSetVolume==0&&nowVoiceValue!=0 return ===========" );
return;
}
// tognzhi huidiaoqi
if(mVolumeControllerListener != null) {
mVolumeControllerListener.onVolumeChanged(currentVolume);
}
}
public void setVoice(int value){
// this value is 100 kedu zhi
toSetVolume=value;
Log.d(TAG, "setVoice volume: " + toSetVolume);
}
}
在音量控制器VolumeController 中去监听、回调音量变化 并 接收回调,动态更新
mVolumeObserver = new VolumeChangeObserver(mContext, this, new Handler());
ContextProvider.get().getContext().getContentResolver().registerContentObserver(
Settings.System.CONTENT_URI,
true,
mVolumeObserver
);
public void onVolumeChanged(int newVolume) {
Log.d(TAG," ===============onVolumeChanged====newVolume:"+newVolume);
mBackgroundHandler.post(mUpdateSliderRunnable);
}
6、CentralSurfacesImpl 一个集成化的界面管理中心 - 亮度初始化
CentralSurfacesImpl 是 Android SystemUI 的核心控制类之一,它接管了原 StatusBar.java 的职责,负责管理状态栏、导航栏、锁屏、快速设置面板等系统界面的整体框架
看类定义:
/**
* A class handling initialization and coordination between some of the key central surfaces in
* System UI: The notification shade, the keyguard (lockscreen), and the status bar.
*
* This class is not our ideal architecture because it doesn't enforce much isolation between these
* three mostly disparate surfaces. In an ideal world, this class would not exist. Instead, we would
* break it up into three modules -- one for each of those three surfaces -- and we would define any
* APIs that are needed for these surfaces to communicate with each other when necessary.
*
* <b>If at all possible, please avoid adding additional code to this monstrous class! Our goal is
* to break up this class into many small classes, and any code added here will slow down that goal.
* </b>
*
* Note that ActivityStarter logic here is deprecated and should be added here as well as
* {@link ActivityStarterImpl}
*/
@SysUISingleton
public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
-
一个处理系统UI中若干核心界面初始化与协调的类:通知面板、锁屏界面以及状态栏。
-
此类并非我们理想中的架构,因为它未能在三个差异显著的界面之间强制实现足够的隔离。在理想情况下,此类本不应存在。取而代之的是,我们应将其拆分为三个独立模块——每个界面对应一个模块——并定义这些界面在必要时相互通信所需的接口。
-
如有可能,请避免向这个庞大的类添加更多代码!我们的目标是将此类拆分为多个小型类,任何在此新增的代码都将延缓这一目标的实现。
构造音量条的VolumeSliderController ,参考亮度条, VolumeSliderController.Factory volumeSliderFactory,,并在构造方法中传递 对象



核心职责与继承关系
CentralSurfacesImpl 是一个具体实现类,其核心定位和结构如下:
| 方面 | 说明 |
|---|---|
| 继承关系 | 继承自 CoreStartable 类,并实现了 CentralSurfaces 接口。 |
| 核心职责 | 作为SystemUI的“中央控制器”,负责协调和初始化多个关键UI组件。 |
| 替代关系 | 在Android 12及之后的版本中,它取代了旧的 StatusBar.java,成为管理状态栏相关功能的主类。 |
关键功能与源码解析
其核心工作集中在makeStatusBarView()方法中。下表梳理了它在初始化时创建的几个主要子系统:
| 功能模块 | 关键方法/操作 | 作用与说明 |
|---|---|---|
| 状态栏视图 | makeStatusBarView() | 整个初始化过程的入口,构建状态栏的根视图。 |
| 快速设置(QS)面板 | 在makeStatusBarView()中初始化 | 负责下拉状态栏后看到的快捷开关面板。通过 FragmentHostManager 加载 QSFragment。 |
| 导航栏/任务栏 | createNavigationBar() | 创建屏幕底部的导航栏。在大屏设备(如平板)上,会转换为任务栏(Taskbar),这是Android 12+的一个重要变化。 |
| 手势导航系统 | 通过TaskbarDelegate及EdgeBackGestureHandler间接管理 | 负责处理边缘返回等手势操作,与任务栏的显示和交互紧密相关。 |
代码分析要点
在阅读 CentralSurfacesImpl 源码时,可以重点关注以下几点:
-
依赖注入 (
Dagger):该类及它初始化的组件(如QSTileHost)大量使用@Inject注解,这是现代SystemUI管理复杂依赖关系的典型方式。 -
插件化与可扩展性:通过
ExtensionController、PluginManager等机制,系统允许动态更换部分实现(如QS面板),提高了可定制性。 -
状态与通信:它通过
CommandQueue、StatusBarStateController等与系统其他部分(如WindowManager)通信,并监听状态变化(如锁屏、电量)来更新UI。
四、源码架构分析
这里仅列举几个类的几个作用,其它分析如上已经分析了部分,实际分析还得自己去体会。
这里先列举一下几个类的关键作用:
| 源码 | 作用 |
|---|---|
| QSPanel | 快捷面板UI:亮度条和音量条就在这个UI面板中显示 |
| QSPanelController | 面板控制器:添加亮度条、音量条UI |
| CentralSurfacesImpl | 初始化所有组件、工厂 |
| Classifier | 定义抽象的:触摸、传感事件 |
| AndroidManifest | 配置四大组件:这里配置我们自己定义的 ContextProvider 、ApplicationContextProvider |
| quick_settings_volume_dialog | 音量条布局 |
| volume_progress_drawable、volume_progress_full_drawable | 音量条背景 |
| SoundUtils | 自定义音量工具类 |
| VolumeSliderView | 包裹音量条SeekBar 的 UI组件 FrameLayout |
| VolumeSliderController | 音量条控制器 ,就是一个ViewModel |
| VolumeController | 音量条控制器,封装了VolumeSliderController 分层思想 |
| VolumeChangeObserver | 音量监听,其它方式设置音量时候 同步SystemUI快捷面板中的音量条 |
总结
- 在Android15 ,RK 平台上面实现 SystemUI 下拉框新增亮度条的功能。 实际可以参考之前文章
- 务必理解 SystemUI基本架构、熟悉、了解 亮度条渲染控制操作,参考后再实现音量条操作
- 根据各大网友实际反馈的集成小问题,都是没有理解类、架构、细节点未注意导致,遇到问题建议自己差错
- Android15 和 Android12/13 集成,在类定义、接口 有少许变化,实际集成肯定需要自己去改就行了。
2035

被折叠的 条评论
为什么被折叠?



