Android SystemUI 通知面板实现
这篇文章给大家分享下Android SystemUI中下拉通知面板时所看到的开关面板(即QS面板)的实现原理,包括其整体架构,UI构建流程与事件处理流程,对这块感兴趣的同学可以看看
一.QS面板构成元素解析
QS面板实际上有多种状态,包括:
- Quick Quick Settings (QQS) : 即初级展开面板,是一次下拉面板看到的简版QS面板,包含少量的开关,如下左侧的图
- Quick Settings (QS) : 完整QS面板,是二次下拉面板看到的完成QS面板,其包含更多的开关,如下右侧的图
- 另外还有开关编辑面板,开关详情页面,本文不展开描述
备注:SystemUI中称通知栏下拉面板开关区域中的单个开关为Tile
先看看整个QS面板中主要的几大类簇:
下面分别介绍各个类簇的主要作用
1. QSTile类簇,该类簇包括的主要类如下,类图如下
packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java packages/SystemUI/src/com/android/systemui/qs/tiles/XxxTile.java
该类簇构成了的Tile
的"后端",负责处理单个Tile
的逻辑处理,其中:
- QSTile : 接口,主要定义了所有
Tile
的通用行为,如注册监听,点击事件的处理,Tile
视图中Icon元素(QSIconView)的构建,刷新Tile
状态(state)等 - QSTileImpl : 实现了
QSTile
定义的通用行为,同时提供了一系列的抽象接口(详见类图)允许不同类型的子类去做差异化实现.后续所有的开关都需要继承自QSTileImpl
. 如WifiTile
,通过继承QSTileImpl
来享用其提供的通用方法,不需要每个开关都去做实现,同时利用差异化接口便可实现开关自身的特有逻辑,如WifiTile
的handleClick
可以打开Wifi开关,而BluetoothTile
的handleClick
则可以打开蓝牙开关
2.QSTileView类簇 该类簇包括的主要类如下,类图如下
packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
该类簇构成了的Tile的视图层,负责处理单个Tile
的界面展示,其中:
- qs/QSTileView : 抽象类,定义了
Tile
视图相关的操作,如点击事件设置,视图刷新onStateChanged(State state)
等 - QSTileBaseView : 实现了
QSTileView
中定义的抽象接口,同时在其构造方法中完成了Tile
视图的构建,包括背景的处理,点击效果的处理(如ripple),点击事件的处理等. 在QQS面板中使用 - QSTileView : 注意与上面的同名抽象类区分开来,这里的起名容易让人疑惑,实际上这里的
QSTileView
继承了QSTileBaseView
,在其构建的视图的基础上,扩展了label
相关的东西,label
即为开关中的文字描述视图,如蓝牙开关对应的label
为"蓝牙". 在QS面板中使用
3.QSHost类簇,该类簇包括的主要类如下,类图如下
packages/SystemUI/src/com/android/systemui/qs/QSHost.java packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSFactory.java packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
该类簇主要完成单个Tile
的构建,其中:
- QSHost : 接口,主要向外界提供获取
QSTile
集合的接口(getTiles()
)以及作为Tile
对外沟通的桥梁,例如点击某个开关后需要触发收起面板的操作,开关便会通过QSHost
来触发收起面板的操作 - QSTileHost : 实现了
QSHost
中定义的接口,同时扩展了创建Tile
后端对象QSTile
和创建Tile
视图对象QSTileView
的接口. 创建时使用了工厂模式,由QSFactoryImpl
类实现
其中QSTileHost
作为外界创建Tile
的入口,会在对象构造的过程中先去创建Tile
后端对象QSTile
集合,这个集合在后续创建完整Tile
对象时会用到. 具体创建哪些Tile
则是通过获取配置在config.xml
中的字段来决定了,具体过程可查看QSTileHost.onTuningChanged(String key, String newValue)
方法
4.QSPanel类簇,该类簇包括的主要类如下,类图如下
packages/SystemUI/src/com/android/systemui/qs/QSPanel.java packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java packages/SystemUI/src/com/android/systemui/qs/QSPanel#QSTileLayout packages/SystemUI/src/com/android/systemui/qs/TileLayout.java packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
该类簇主要完成QS面板元素的动态添加,其中:
- QSPanel : 对应我们前面说的QS面板,即二次展开面板,是该页面的顶层容器,其嵌套在一个
ScrollView
中. 负责动态添加相应的元素,添加的元素包括 [亮度条 / 根据屏幕方向动态添加开关容器 QSTileLayout / Footer ] 等. 同时为这些元素提供了一系列的操作接口,如
- 为开关容器创建具体开关对象(通过QSHost) 并为其添加刷新监听器,在后续开关后端收到开关状态刷新需求时,将刷新需求分发到对应的开关视图层.
- 开关详情页(Details)的创建与刷新
- QS面板展开状态变化时做相应的处理(
setExpanded(boolean expanded)
)
- QuickQSPanel : 对应我们前面说的初级展开面板QQS面板,继承自
QSPanel
并对展示的Tile
数做了限制(通过setMaxTiles(int)
),同时复写了父类提供的添加子元素的方法,按需添加QQS面板的元素,因为QQS面板是QS面板的精简版,所以很多子元素未做添加 - QSTileLayout : 开关容器接口,主要定义了开关容器绘制相关的接口
- TileLayout : QQS面板开关容器类,负责精简QS面板的绘制,继承自 Viewgroup
- PagedTileLayout : QS面板开关容器类,负责QS面板的绘制,继承自 ViewPager
5.QS类簇,该类簇包括的主要类如下,类图如下
packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java packages/SystemUI/src/com/android/systemui/qs/QSFragment.java packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
该类簇主要作为整个QS面板的顶层容器,主要处理QS面板的展开/收起逻辑,其中:
- QS : 接口,主要定义了 QS 面板展开/收起相关的接口
- QSFragment : 继承自
Fragment
,实现了 QS 接口,主要负责接收 QS 面板展开/收起状态的改变,并将最新状态同步给该 Fragment 中的 View 元素,如 QSContainerImpl - QSContainerImpl : 添加到
QSFragment
中的自定义 View,继承自FrameLayout
对应布局qs_panel.xml
,主要通过接收来自QSFragment
的面板展开/收起状态的变化并做刷新
至此,整个QS面板中的关键类就介绍完了,下面将通过几条主线来洞悉整个QS面板的具体实现:
- QS面板开关集合构建流程
Tile
后端是如何与Tile
视图层产生联系的Tile
的一次点击事件背后的流程是怎么样的