从状态栏往下拉的时候,就会出现“快速设置”界面,如下图所示。快速启动界面可以让用户快速设置和操作。对于急需或频繁使用的控件和操作,保留“快速设置”图块,且不应将其用作启动应用的快捷方式。android7.0添加了新的API,让开发者可以为自己的应用添加快速设置图标。这篇文件将要分析快速启动的设计。当然,只是个人见解。
快速设置图标有两种类型,一种是system UI预置的(QSTile),另一种是第三方应用添加的(CustiomTile)。system UI使用Tile来代表一个快速设置。本文只介绍system UI预置的QSTile,关于第三方应用的Tile后续再分析。
一、Tile数据加载
1、主要类介绍
QSTile:快速设置Tile的基类,快速设置都继承自这个类。它使用Host提供的Looper, 管理着快速设置(以下文章都用Tile来指代快速设置)的状态(State)变化。Tile通过重载handleUpdateState方法来更新状态。如果监听到状态变化,或者点击事件需要更新状态,使用refreshState来更新State。
QSTileHost: Host的实现,管理Tile 状态的变化。它包含各种Tile的Control类,用来设置和监听Tile。
State: Tile的状态,包含图标,名称等信息。
H:继承自Handler, 通过looper跑在QSTileHost的线程里。用来处理Tile的各种事件。
Tunable: 接口,配置变化的回调。QSTileHost实现这个接口。
TunerSercice:用来监听各种配置变化。
2、Tile加载流程
这里从创建View开始跟踪(之前的流程可以参考http://blog.youkuaiyun.com/zhudaozhuan/article/details/50817180),过程如下图所示:
在TunerService的addTunable的方法,会监听ContentProvider中的Tile的变化(一般是增加或减少Tile),监听的函数是registerContentObserver。Tile是存储在Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser),相关代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
当Tile发生变化或第一次添加的时候,会执行QSTileHost的onTuningChanged的方法,在这个方法里会创建所有需要显示的Tile。部分代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
createTile方法,根据tileSpec创建响应的Tile,使用的是工厂方法。最终会把创建的Tile都存储在QSTileHost的mTiles,如果有注册QSTile.Host.Callback的监听,也会回调响应的onTilesChanged方法。
自此,QSTileHost已经获取到了Tile的数据。
二、未展开的界面显示
显示Tile的地方有两个,请看前面的两张图,一个是未展开的状态,只显示前面几个Tile;另一个是全部展开的状态,可以显示所有的Tile,还有其他的设置功能。这里先介绍未展开的Tile界面。
1.主要类介绍
QSPanel: 用来显示快速设置的界面。
QuickQSPanel: QSPanel的子类,里面包含一个HeaderTileLayout,用来显示少数几个Tile的界面。在快速设置界面的顶部。
HeaderTileLayout: QuickQSPanel创建的View,用来显示少数几个Tile的界面。
TileRecord: 包含Tile数据,Tile界面(QSTileBaseView)等数据。
QSTileBaseView: 继承自LinearLayout,用来显示Tile。不显示标题。
QSTileView: 继承自QSTileBaseView, 用来显示Tile,会显示标题。HeaderTileLayout不使用这个。
2. 界面显示流程
未展开的Tile显示在QuickQSPanel的HeaderTileLayout里。可以查看QuickStatusBarHeader的xml文件:quick_status_bar_expanded_header.xml。在QuickQSPanel的构造函数中,创建并加入HeaderTileLayout:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
它的父类QSPanel的构造函数会加入一个Brightness View和Footer View, 这里并不需要显示。加载Tile的时序图如下所示:
在QSPanel的addTile中,会去创建TileRecord。TileRecord包含Tile的界面和tile的数据,同时还包含一个callback,这在处理事件的时候,会用到。然后在HeaderTileLayout的add Tile方法,会把这个QSTileBaseView添加进去。相关代码代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
三、未展开界面事件处理
主要有点击事件,长按事件等。
1.点击事件
onClick的事件,参考前面addTile的代码,里面有创建一个Click类,用来监听onClick事件。时序图如下:
1)QuickQSPanel有重载了QSPanle的onTileClick事件,会执行handleSecondaryClick。从上图可以看出,它默认的行为也是执行QSTile的handleClick事件。每个QSTile的子类都可以重载handleSecondaryClick方法,来区别未展开和展开的Tile点击事件。所以,点击QuickQSPanel的Tile界面,执行的是handleSecondaryClick。
2)QSPanel的点击事件流程。触发点击事件后,执行QSTile的handleClick方法。这是一个虚方法,各个QSTile的子类需要重载这个方法。有些Tile会在这里执行刷新状态refreshState, 有些Tile会执行showDetail。
3)refreshState的流程,QSTile的子类,要在这里更新Tile的State
4)showDetail的流程。这里有一个要注意的地方是,在QSTile.Callback的onShowDetail方法中,会判断shouldShowDetail的方法。这个方法在未展开的panel里,返回的是!mExpanded, 而在展开的Panel里,返回的是
QuickQSPanle是QSPanel的子类,可能会重载QSPanel的方法。跟踪代码的时候,需要注意是否有重载。像onTileClick就有重载。
2.longClick事件
长按事件也是在QSPanl的addTile方法里创建的,最后会执行到handleLongClick的方法。在这里会从getLongClickIntent里面,获取一个Intent, 启动该Intent的activity。getLongClickIntent是一个虚方法,每个QSTile的子类, 需要重载该方法。