快捷设置是“快捷设置”面板中显示的图块,代表用户可以点按以快速完成周期性任务的操作。您的应用可以通过 TileService 类向用户提供自定义功能块,并使用 Tile 对象跟踪功能块的状态。例如,您可以创建一个功能块,让用户能够开启或关闭应用提供的 VPN。
决定何时创建卡片
我们建议您为希望用户经常访问或需要快速访问(或两者兼而有之)的特定功能创建卡片。最有效的卡片是同时具备这两种特性的卡片,可让用户快速执行常用操作。
例如,您可以为健身应用创建一个功能块,让用户能够快速开始锻炼。不过,我们不建议为同一应用创建可让用户查看其整个锻炼记录的卡片。
为帮助提高卡片的可检测性和易用性,我们建议您避免某些做法:
避免使用卡片启动应用。请改用应用快捷方式或标准启动器。
避免将功能块用于一次性用户操作。请改用应用快捷方式或通知。
避免创建过多功能块。我们建议每个应用最多使用两个。请改用应用快捷方式。
避免使用仅显示信息但无法与用户互动的卡片。请改用通知或微件。
创建功能块
如需创建功能块,您需要先创建合适的功能块图标,然后在应用的清单文件中创建并声明 TileService。
TileService 才会充当与卡片交互的界面。快捷设置示例提供了一个有关如何创建和管理功能块的示例。
创建自定义图标
您需要提供一个自定义图标,该图标会显示在“快捷设置”面板中的功能块上。(您将在声明 TileService 时添加此图标,如下一部分所述。)图标必须为纯白色,具有透明背景,尺寸为 24 x 24dp,并且采用 VectorDrawable 形式。
创建一个图标,直观地暗示功能块的用途。这有助于用户确定您的卡片是否符合其需求。例如,您可以为健身应用的卡片创建一个秒表图标,以便用户开始锻炼。
创建和声明 TileService
为图块创建扩展 TileService 类的服务。
class MyQSTileService : TileService() { // Called when the user adds your tile. override fun onTileAdded() { super.onTileAdded() } // Called when your app can update your tile. override fun onStartListening() { super.onStartListening() } // Called when your app can no longer update your tile. override fun onStopListening() { super.onStopListening() } // Called when the user taps on your tile in an active or inactive state. override fun onClick() { super.onClick() } // Called when the user removes your tile. override fun onTileRemoved() { super.onTileRemoved() } }
在应用的清单文件中声明 TileService。添加 TileService 的名称和标签(android:label 最多 18 个字符)、您在上一部分中创建的自定义图标以及适当的权限。
<!-- 18-character limit for android:label. -->
<service
android:name=".MyQSTileService"
android:exported="true"
android:label="@string/my_default_tile_label"
android:icon="@drawable/my_default_icon_label"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
管理 TileService
在应用清单中创建并声明 TileService 后,您必须管理其状态。
TileService 是一项绑定服务。当
应用请求或系统需要与 通信时,系统会绑定 TileService。典型的绑定服务生命周期包含以下四个回调方法:onCreate()、onBind()、onUnbind() 和 onDestroy()。每当服务进入新的生命周期阶段时,系统都会调用这些方法。
TileService 生命周期概览
除了控制绑定服务生命周期的回调之外,您还必须实现特定于 TileService 生命周期的其他方法。这些方法可能会在 onCreate() 和 onDestroy() 之外调用,因为 Service 生命周期方法和 TileService 生命周期方法是在两个单独的异步线程中调用的。
TileService 生命周期包含以下方法,每当 TileService 进入新的生命周期阶段时,系统都会调用这些方法:
onTileAdded():仅当用户首次添加您的功能块时,以及用户移除并再次添加您的功能块时,系统才会调用此方法。这是执行任何一次性初始化的最佳时机。不过,这可能无法满足所有必要的初始化。onStartListening()和onStopListening():每当应用更新功能块时,系统都会调用这些方法,并且会经常调用。TileService仍绑定在onStartListening()和onStopListening()之间,从而允许您的应用修改功能块并推送更新。onTileRemoved():仅当用户移除您的功能块时,系统才会调用此方法。
选择聆听模式
TileService 会在活动模式或非活动模式下监听。我们建议您使用主动模式,您需要在应用清单中声明该模式。否则,TileService 为标准模式,无需声明。
不要假设您的 TileService 会在 onStartListening() 和 onStopListening() 方法对之外存在。
主动模式(推荐)
对于在自己的进程中监听和监控其状态的 TileService,请使用活跃模式。处于活动模式的 TileService 会绑定到 onTileAdded()、onTileRemoved()、点按事件,以及应用进程请求时。
如果您的 TileService 在其图块状态应由其自身进程更新时收到通知,我们建议您使用主动模式。活动图块可以减轻系统负担,因为每次向用户显示“快捷设置”面板时,都不必绑定活动图块。
可以调用静态 TileService.requestListeningState() 方法来请求启动监听状态,并接收对 onStartListening() 的回调。
您可以通过将 META_DATA_ACTIVE_TILE 添加到应用的清单文件中来声明活跃模式。
<service ...>
<meta-data android:name="android.service.quicksettings.ACTIVE_TILE"
android:value="true" />
...
</service>
非活动模式
非活动模式是标准模式。如果 TileService 在用户可以看到卡片时绑定,则处于非活跃模式。这意味着,您的 TileService 可能会在无法控制的情况下再次创建和绑定。当用户未查看卡片时,它也可能会解除绑定并销毁。
用户打开其“快捷设置”面板后,您的应用会收到对 onStartListening() 的回调。在 onStartListening() 到 onStopListening() 期间,您可以根据需要多次更新 Tile 对象。
您无需声明非活跃模式:请勿将 META_DATA_ACTIVE_TILE 添加到应用的清单文件中。
功能块状态概览
用户添加您的卡片后,该卡片始终处于以下状态之一。
STATE_ACTIVE:表示开启或启用状态。用户可以在此状态下与您的卡片互动。例如,对于可让用户发起定时锻炼练习的健身应用功能块,
STATE_ACTIVE表示用户已发起锻炼练习,并且计时器正在运行。STATE_INACTIVE:表示关闭或暂停状态。用户可以在此状态下与您的卡片互动。再次以健身应用卡片为例,
STATE_INACTIVE中的卡片表示用户尚未开始锻炼,但如果他们愿意,可以开始锻炼。STATE_UNAVAILABLE:表示暂时不可用状态。用户无法在此状态下与您的卡片互动。例如,
STATE_UNAVAILABLE中的图块表示由于某种原因,用户目前无法使用该图块。
系统只会设置 Tile 对象的初始状态。您可以在 Tile 对象的整个生命周期内设置其状态。
系统可能会为功能块图标和背景着色,以反映 Tile 对象的状态。设置为 STATE_ACTIVE 的 Tile 对象最暗,STATE_INACTIVE 和 STATE_UNAVAILABLE 越来越亮。确切的色调取决于制造商和版本。
更新功能块
收到 onStartListening() 回调后,您就可以更新功能块。
根据图块的模式,您的图块可以至少更新一次,直到收到对 onStopListening() 的回调。
在活跃模式下,您可以在收到对 onStopListening() 的回调之前更新一次功能块。在非活跃模式下,您可以在 onStartListening() 到 onStopListening() 之间任意多次更新卡片。
您可以通过调用 getQsTile() 来检索 Tile 对象。如需更新 Tile 对象的特定字段,请调用以下方法:
将 Tile 对象的字段设置为正确的值后,您必须调用 updateTile() 来更新卡块。这将使系统解析更新后的功能块数据并更新界面。
data class StateModel(val enabled: Boolean, val label: String, val icon: Icon) override fun onStartListening() { super.onStartListening() val state = getStateFromService() qsTile.label = state.label qsTile.contentDescription = state.label qsTile.state = if (state.enabled) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE qsTile.icon = state.icon qsTile.updateTile() }
处理点按操作
如果您的卡片处于 STATE_ACTIVE 或 STATE_INACTIVE 状态,用户可以点按卡片来触发操作。然后,系统会调用应用的 onClick() 回调。
应用收到对 onClick() 的回调后,可以启动对话框或 activity、触发后台工作或更改功能块的状态。
var counter = 0 override fun onClick() { super.onClick() counter++ qsTile.state = if (counter % 2 == 0) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE qsTile.label = "Clicked $counter times" qsTile.contentDescription = qsTile.label qsTile.updateTile() }
启动对话框
showDialog() 会收起“快捷设置”面板并显示一个对话框。
如果操作需要额外输入或用户同意,请使用对话框为操作添加上下文。
启动活动
startActivityAndCollapse() 会在收起面板的同时启动 activity。如果需要显示的信息比对话框中的信息更详细,或者操作具有高度互动性,则 activity 非常有用。
如果您的应用需要大量用户互动,则应用应仅在万不得已的情况下启动 activity。请考虑改用对话框或切换开关。
长按某个功能块会向用户显示应用信息界面。如需替换此行为,改为启动用于设置偏好设置的 activity,请使用 ACTION_QS_TILE_PREFERENCES 向您的某个 activity 添加 <intent-filter>。
从 Android API 28 开始,PendingIntent 必须具有 Intent.FLAG_ACTIVITY_NEW_TASK:
if (Build.VERSION.SDK_INT >= 28) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }
您也可以在特定 Activity 部分的 AndroidManifest.xml 中添加该标志。
将功能块标记为可切换
如果您的功能块主要用作双态开关(这是功能块最常见的行为),我们建议您将其标记为可切换。这有助于向操作系统提供有关功能块行为的信息,并提高整体无障碍性。
将 TOGGLEABLE_TILE 元数据设置为 true,以将功能块标记为可切换。
<service ...>
<meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE"
android:value="true" />
</service>
仅在安全锁定的设备上执行安全操作
在锁定的设备上,您的卡片可能会显示在锁屏界面上。如果功能块包含敏感信息,请检查 isSecure() 的值以确定设备是否处于安全状态,并且您的 TileService 应相应地更改其行为。
如果图块操作在锁定状态下可以安全执行,请使用 startActivity() 在锁定的屏幕上启动 activity。
如果功能块操作不安全,请使用 unlockAndRun() 提示用户解锁设备。如果成功,系统会执行您传递到此方法的 Runnable 对象。
对功能块进行分类
为了提升“快捷设置”中的用户体验,您可以对功能块进行分类。 系统会将功能块整理到“连接”“显示”和“隐私”等类别中。系统会使用这些类别在“快捷设置”的编辑模式下对功能块进行排序和分组,以便用户更轻松地找到和管理这些功能块。
实现
如需为 TileService 指定类别,请在 AndroidManifest.xml 文件中的服务声明内添加元数据字段:
- 在
AndroidManifest.xml中,在TileService的<service>元素内添加<meta-data>元素。 android:name:将此值设为android.service.quicksettings.TILE_CATEGORY。android:value:分配一个预定义的类别常量,例如android.service.quicksettings.CATEGORY_CONNECTIVITY或android.service.quicksettings.CATEGORY_DISPLAY。
如以下示例所示:
<service
android:name=".MyConnectivityTileService"
[...]
>
<meta-data android:name="android.service.quicksettings.TILE_CATEGORY"
android:value="android.service.quicksettings.CATEGORY_CONNECTIVITY" />
</service>
该 API 提供了一组预定义的类别供您选择。这些类别在 TileService 类中定义为字符串常量。
如果未指定类别,系统会自动分配默认类别:
- 来自系统应用:适用于属于系统应用的卡片。
- 来自您安装的应用:适用于用户安装的应用中的功能块。
虽然 Google Pixel 设备会在“快捷设置”中使用类别,但 OEM 可以在各自的系统界面中使用或忽略此类别信息。
提示用户添加您的功能块
如需手动添加功能块,用户必须执行以下几个步骤:
- 向下滑动即可打开“快捷设置”面板。
- 点按“修改”按钮。
- 滚动浏览设备上的所有功能块,直到找到您的功能块。
- 按住相应图块,然后将其拖动到活跃图块列表中。
用户也可以随时移动或移除您的卡片。
从 Android 13 开始,您可以使用 requestAddTileService() 方法,让用户能够更轻松地将您的功能块添加到设备中。此方法会向用户显示一条请求,提示他们直接将您的图块快速添加到其“快捷设置”面板。提示包括应用名称、提供的标签和图标。
public void requestAddTileService (
ComponentName tileServiceComponentName,
CharSequence tileLabel,
Icon icon,
Executor resultExecutor,
Consumer<Integer> resultCallback
)
回调包含有关以下内容的信息:功能块是否已添加、未添加、是否已存在或是否发生了任何错误。
请自行斟酌何时提示用户以及提示频率。我们建议仅在具体使用情境下调用 requestAddTileService(),例如当用户首次与功能块提供的功能互动时。
如果某个
ComponentName 之前已被用户拒绝过足够多次,系统可能会选择停止处理该 的请求。用户由用于检索此服务的 Context 确定:它必须与当前用户匹配。