快速设置QuickSettings是Android7.0推出的新特性,就是我们平时手机常用的下拉快捷控制,可以直接从通知栏显示一些关键的设置和操作,例如:打开/关闭手电筒、蓝牙、Wifi 等等许多功能,用户甚至无需解锁就可以操作这些功能,非常简单方便,可以一起来研究一下。
先来看看真实手机上的快速设置截图:
截图中显示布局边界是我为了方便开发调试专门添加上的,并将其放在最开始位置 ,点击右上角编辑按钮进入(不同手机可能位置不同),可以增加、移除、拖拽调整位置等。
接下来我们一起从开发者角度来看看如何实现快速设置。
首先我们需要创建一个类继承TileService,TileService是android.app.Service 的子类,也就是说它是四大组件里的一种服务。这个服务比较特殊,不需要在程序中配置开启,系统默认能够识别并调用,只需像正常service配置那样在Manifest文件中注册即可。
<service android:name=".QuickSettingsService"
android:label="快速设置Demo"
android:icon="@drawable/icon_holiday_mode"
android:description="@string/app_name"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:exported="true">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE"/>
</intent-filter>
</service>
在这里我们需要注意一些点:
1)lable:快捷设置的名称,不配置默认显示应用名称。
2)icon:快捷设置的图标,不配置默认显示一个黑块,需要配置。
3)description:快捷设置的描述,显示在名称下面,颜色稍浅,如不插卡时移动数据下面显示的没有SIM卡,不配置默认不显示。(需要注意:这个必须使用引用,不能直接字符文本,否则会报错)
4)permission:android.permission.BIND_QUICK_SETTINGS_TILE,需要的特殊权限,必须配置。
5)exported:主要作用是否支持其它应用调用当前组件,true允许被启动,false不允许被启动,默认值是由有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。此处必须配置为true,否则添加上快捷设置栏里,任何功能都不会生效。(需要注意:andriod12即sdk31及以上版本需要显示声明android:exported 这个属性)
6)intent-filter:action android:name="android.service.quicksettings.action.QS_TILE",意图过滤器,必须配置。
接下来我们看看创建的TileService继承类都需要做些什么。
class QuickSettingsService : TileService() {
val TAG: String = "QuickSettingsService"
// 当用户从编辑栏添加Tile到快速设置中调用
override fun onTileAdded() {
Log.d(TAG, "onTileAdded")
}
// 当用户从快速设置移除Tile时调用
override fun onTileRemoved() {
Log.d(TAG, "onTileRemoved")
}
// 当用户点击Tile时调用
override fun onClick() {
Log.d(TAG, "onClick")
}
// 当用户打开下拉菜单时调用,当Tile并没有在编辑栏拖到设置栏中不会调用,在TileAdded添加之后会调用一次
override fun onStartListening() {
Log.d(TAG, "onStartListening")
}
// 当用户打开下拉菜单时调用,当Tile并没有在编辑栏拖到设置栏中不会调用,在TileRemoved移除之前会调用一次
override fun onStopListening() {
Log.d(TAG, "onStopListening")
}
}
里面主要有几个生命周期状态相关的回调方法:
1)onTileAdded():当用户从编辑栏添加Tile到快速设置中调用
2)onTileRemoved():当用户从快速设置移除Tile时调用
3)onClick():当用户点击Tile时调用
4)onStartListening():当用户打开下拉菜单时调用,当Tile并没有在编辑栏拖到设置栏中不会调用,在TileAdded添加之后会调用一次
5)onStopListening():当用户打开下拉菜单时调用,当Tile并没有在编辑栏拖到设置栏中不会调用,在TileRemoved移除之前会调用一次
现在运行工程,就可以在编辑栏里看到新添加的快速设置Tile了,也可以拖到设置栏里。因为里面没有添加任何功能代码,所以仅仅只是显示出来,没有其他效果。这时候我们一般可以在onClick()方法里实现一些具体功能,常见的比如点击Tile打开关闭某些功能和切换显示图标,或者打开指定页面。
点击Tile切换显示图标
if (qsTile.state === Tile.STATE_ACTIVE) {
qsTile.label = "inactive"
qsTile.icon = Icon.createWithResource(applicationContext, R.drawable.icon_daily_mode)
qsTile.state = Tile.STATE_INACTIVE
} else {
qsTile.label = "active"
qsTile.icon = Icon.createWithResource(applicationContext, R.drawable.icon_holiday_mode)
qsTile.state = Tile.STATE_ACTIVE
}
getQsTile()获得qsTile对象后,可以设置State、Icon、tilte等,State有两种:STATE_ACTIVE激活或打开状态,STATE_ACTIVE未激活或关闭状态。Icon的话,系统会默认渲染统一的两种状态颜色,保持一致。设置完后,运行发现并没有生效,其实还需要updateTile()方法来触发状态刷新。
点击Tile打开指定页面
// 打开指定页面
val intent = Intent(this, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
// 跳转指定页面,并关闭下拉菜单
startActivityAndCollapse(intent)
完整代码如下:
class QuickSettingsService : TileService() {
val TAG: String = "QuickSettingsService"
// 当用户从编辑栏添加Tile到快速设置中调用
override fun onTileAdded() {
Log.d(TAG, "onTileAdded")
}
// 当用户从快速设置移除Tile时调用
override fun onTileRemoved() {
Log.d(TAG, "onTileRemoved")
}
// 当用户点击Tile时调用
override fun onClick() {
Log.d(TAG, "onClick")
// 切换显示图标
if (qsTile.state === Tile.STATE_ACTIVE) {
qsTile.label = "inactive"
qsTile.icon = Icon.createWithResource(applicationContext, R.drawable.icon_daily_mode)
qsTile.state = Tile.STATE_INACTIVE
} else {
qsTile.label = "active"
qsTile.icon = Icon.createWithResource(applicationContext, R.drawable.icon_holiday_mode)
qsTile.state = Tile.STATE_ACTIVE
}
qsTile.updateTile()
// // 打开指定页面
// val intent = Intent(this, MainActivity::class.java)
// intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
// // 跳转指定页面,并关闭下拉菜单
// startActivityAndCollapse(intent)
}
// 当用户打开下拉菜单时调用,当Tile并没有在编辑栏拖到设置栏中不会调用,在TileAdded添加之后会调用一次
override fun onStartListening() {
Log.d(TAG, "onStartListening")
}
// 当用户打开下拉菜单时调用,当Tile并没有在编辑栏拖到设置栏中不会调用,在TileRemoved移除之前会调用一次
override fun onStopListening() {
Log.d(TAG, "onStopListening")
}
}
还有一些其他可使用方法:
isLocked() 返回屏幕是否被锁定
isSecure() 返回设备是否处于安全状态
unlockAndRun(Runnable) 在执行Runnable前提示用户解锁设备
showDialog(Dialog) 折叠下拉菜单并显示对话框