Shortcuts API 简介
快捷方式在各类App中已经十分常见,快捷方式可以让用户直达想要使用的功能,例如快速打开扫一扫、快速打开健康码等。对此,Android提供了Shortcuts API,本文介绍如何使用Shortcuts API
来实现快捷方式。
在Android中,快捷方式通常显示在App的启动器或支持的助理中(例如Google 助理)。每个快捷方式对应一个或者多个Intent
,当用户选中某个快捷方式时,系统会启动对应的Intent
。
需要注意:
- 只有包含
和
的Activity
(即启动页)可以拥有快捷方式。 - 不同设备支持的快捷方式数量可能不同,通常每个启动器最多显示4个快捷方式,可以通过
ShortcutManagerCompat.getMaxShortcutCountPerActivity(context)
来获取设备支持的快捷方式数量。
Android 支持下列三种快捷方式:
- 静态快捷方式:通过xml配置生成。
- 动态快捷方式:在运行时通过代码配置生成、更新和移除。
- 桌面快捷方式:在运行时通过代码配置生成、更新,需要用户同意后才能添加。
下面分别介绍这三种类型的快捷方式如何实现。
静态快捷方式
快捷方式配置
在res/xml目录下创建shortcuts.xml,如下图:
注意,此方式仅在Android N_MR1(25)以上可用。
文件内容如下:
配置文件中,每个shortcut
标签必须配置的值为android:shortcutId
和android:shortcutShortLabel
,其余为可选配置。
属性名 | 属性值 |
---|---|
android:shortcutId | id, 不能设置为字符串资源,必须配置 |
android:shortcutShortLabel | 简述,建议限制在10个字符以内,字符串资源,必须配置 |
android:shortcutLongLabel | 详细描述,显示空间足够大时会显示此值,建议限制在25个字符内,字符串资源,非必须 |
android:icon | 图标,图片的路径或图片资源文件,非必须 |
android:enabled | 是否可用,布尔值,非必须 |
android:shortcutDisabledMessage | 用户点击不可用的快捷方式时的提示语,仅在enable为false时生效,字符串资源,非必须 |
在Manifest中添加快捷方式配置
在AndroidManifest
中启动页标签下添加meta-data
,如下:
效果如图:
动态快捷方式
可以在运行时通过代码添加或移除动态快捷方式,代码如下:
class ShortcutsActivity : AppCompatActivity() { private val locationShortcutId = "location" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding: LayoutShortcutsActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_shortcuts_activity) binding.includeTitle.tvTitle.text = "Shortcuts Api" binding.btnCreateShortcut.setOnClickListener { // 创建动态快捷方式 createDynamicShortcuts() } binding.btnRemoveShortcut.setOnClickListener { // 根据ID移除指定的动态快捷方式 ShortcutManagerCompat.removeDynamicShortcuts(this, arrayListOf(locationShortcutId)) // 移除所有的动态快捷方式 // ShortcutManagerCompat.removeAllDynamicShortcuts(this) } } private fun createDynamicShortcuts() { val dynamicShortcuts = ShortcutManagerCompat.getDynamicShortcuts(this) val locationShortcut = ShortcutInfoCompat.Builder(this, locationShortcutId) .setShortLabel(getString(R.string.location_shortcuts_short_label)) .setLongLabel(getString(R.string.location_shortcuts_long_label)) .setDisabledMessage(getString(R.string.shortcuts_disable_message)) .setIcon(IconCompat.createWithResource(this, R.drawable.icon_location)) .setIntent(Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, GpsSignalActivity::class.java.name) }) .build() if (dynamicShortcuts.isEmpty() || !dynamicShortcuts.contains(locationShortcut)) { ShortcutManagerCompat.pushDynamicShortcut(this, locationShortcut) } } }
效果如图:
桌面快捷方式
运行时创建
桌面快捷方式也十分常见,例如支付宝可以将扫一扫、乘车码等固定到桌面。可以通过如下代码添加桌面快捷方式:
class ShortcutsActivity : AppCompatActivity() { private val locationShortcutId = "location" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... binding.btnCreatePinShortcut.setOnClickListener { // 创建桌面快捷方式 createPinShortcuts() } } private fun createPinShortcuts() { // 先判断是否支持添加桌面快捷方式 if (ShortcutManagerCompat.isRequestPinShortcutSupported(this)) { val pinShortcutInfo = ShortcutInfoCompat.Builder(this, locationShortcutId) .setShortLabel(getString(R.string.location_shortcuts_short_label)) .setLongLabel(getString(R.string.location_shortcuts_long_label)) .setDisabledMessage(getString(R.string.shortcuts_disable_message)) .setIcon(IconCompat.createWithResource(this, R.drawable.icon_location)) .setIntent(Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, GpsSignalActivity::class.java.name) }) .build() val pinnedShortcutCallbackIntent = ShortcutManagerCompat.createShortcutResultIntent(this, pinShortcutInfo) val successCallback = PendingIntent.getBroadcast(this, 0, pinnedShortcutCallbackIntent, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_MUTABLE else 0) ShortcutManagerCompat.requestPinShortcut(this, pinShortcutInfo, successCallback.intentSender) } } }
效果如图:
支持用户主动创建
可以通过配置创建快捷方式专用的Activity来支持用户主动创建桌面快捷方式,代码如下:
class CreateCameraShortcutActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding: LayoutCreateShortcutsActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_create_shortcuts_activity) binding.tvTips.text = "Do you want to add the Camera Launcher icon to your home screen?" binding.btnAddShortcut.setOnClickListener { createPinShortcuts() } binding.btnReject.setOnClickListener { finish() } } private fun createPinShortcuts() { if (ShortcutManagerCompat.isRequestPinShortcutSupported(this)) { val pinShortcutInfo = ShortcutInfoCompat.Builder(this, "camera") .setShortLabel(getString(R.string.camera_shortcuts_short_label)) .setLongLabel(getString(R.string.camera_shortcuts_long_label)) .setDisabledMessage(getString(R.string.shortcuts_disable_message)) .setIcon(IconCompat.createWithResource(this, R.drawable.icon_camera)) .setIntent(Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, CameraActivity::class.java.name) }) .build() val pinnedShortcutCallbackIntent = ShortcutManagerCompat.createShortcutResultIntent(this, pinShortcutInfo) setResult(RESULT_OK, pinnedShortcutCallbackIntent) finish() } } } // 在 manifest中配置activity
效果如图:
打开多个Activity
每个快捷方式都可以配置多个Intent
,当用户选中此快捷方式时,会链式的打开所有Intent
对应的页面,代码如下:
// 静态快捷方式// 动态快捷方式(桌面快捷方式与此类似) class ShortcutsActivity : AppCompatActivity() { private val locationShortcutId = "location" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... binding.btnCreateMultipleIntentsShortcut.setOnClickListener { createMultipleIntentsDynamicShortcuts() } } private fun createMultipleIntentsDynamicShortcuts() { val dynamicShortcuts = ShortcutManagerCompat.getDynamicShortcuts(this) val locationShortcut = ShortcutInfoCompat.Builder(this, locationShortcutId) .setShortLabel(getString(R.string.location_shortcuts_short_label)) .setLongLabel(getString(R.string.location_shortcuts_long_label)) .setDisabledMessage(getString(R.string.shortcuts_disable_message)) .setIcon(IconCompat.createWithResource(this, R.drawable.icon_location)) .setIntents(arrayOf( Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, HomeActivity::class.java.name) }, Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, GpsSignalActivity::class.java.name) })) .build() if (dynamicShortcuts.isEmpty() || !dynamicShortcuts.contains(locationShortcut)) { ShortcutManagerCompat.pushDynamicShortcut(this, locationShortcut) } } }
效果如图:
更新快捷方式
启用和禁用
在需要的时候,例如当定位不可用时禁止定位快捷方式,定位恢复可用时重新启用快捷方式,代码如下:
class ShortcutsActivity : AppCompatActivity() { private val locationShortcutId = "location" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... binding.btnEnableShortcut.setOnClickListener { // 启用快捷方式 ShortcutManagerCompat.enableShortcuts(this, arrayListOf(ShortcutInfoCompat.Builder(this, locationShortcutId) .setShortLabel(getString(R.string.location_shortcuts_short_label)) .setLongLabel(getString(R.string.location_shortcuts_long_label)) .setDisabledMessage(getString(R.string.shortcuts_disable_message)) .setIcon(IconCompat.createWithResource(this, R.drawable.icon_location)) .setIntent(Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, GpsSignalActivity::class.java.name) }) .build() )) } binding.btnDisableShortcut.setOnClickListener { // 禁用快捷方式,需要传入禁用原因,当用户点击时会显示 ShortcutManagerCompat.disableShortcuts(this, arrayListOf(locationShortcutId), "Location function is currently unavailable") } } }
效果如图:
注意,根据效果图可以看到,禁用后动态快捷方式会被移除,再次启用后不会自动添加,需要手动添加。
更新快捷方式的样式
在需要的时候,例如用户切换语言时可以更新快捷方式的样式,显示匹配当前语言的文案,代码如下:
class ShortcutsActivity : AppCompatActivity() { private val locationShortcutId = "location" private var english = true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... binding.btnUpdateShortcut.setOnClickListener { updateDynamicShortcuts() } } private fun updateDynamicShortcuts() { english = !english ShortcutManagerCompat.updateShortcuts(this, arrayListOf( ShortcutInfoCompat.Builder(this, locationShortcutId) .setShortLabel(if (english) getString(R.string.location_shortcuts_short_label) else "使用定位") .setLongLabel(if (english) getString(R.string.location_shortcuts_long_label) else "通过定位获取位置信息") .setDisabledMessage(if (english) getString(R.string.shortcuts_disable_message) else "此快捷方式不可用") .setIcon(IconCompat.createWithResource(this, R.drawable.icon_location)) .setIntent(Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, GpsSignalActivity::class.java.name) }) .build() )) } }
效果如图:
注意,通过代码只能更新动态快捷方式和桌面快捷方式,如果更新了静态快捷方式会崩溃。
示例
整合之后做了个示例Demo,代码如下:
// 快捷方式配置文件// 弹窗样式的Activity Style // Manifest // 示例Activity class ShortcutsActivity : AppCompatActivity() { private val locationShortcutId = "location" private var english = true @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding: LayoutShortcutsActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_shortcuts_activity) binding.includeTitle.tvTitle.text = "Shortcuts Api" binding.btnCreateShortcut.setOnClickListener { // 创建动态快捷方式 createDynamicShortcuts() } binding.btnRemoveShortcut.setOnClickListener { // 根据ID移除指定的动态快捷方式 ShortcutManagerCompat.removeDynamicShortcuts(this, arrayListOf(locationShortcutId)) // 根据ID移除指定的动态快捷方式 // ShortcutManagerCompat.removeAllDynamicShortcuts(this) } binding.btnCreatePinShortcut.setOnClickListener { // 创建桌面快捷方式 createPinShortcuts() } binding.btnCreateMultipleIntentsShortcut.setOnClickListener { createMultipleIntentsDynamicShortcuts() } binding.btnEnableShortcut.setOnClickListener { // 启用快捷方式 ShortcutManagerCompat.enableShortcuts(this, arrayListOf(ShortcutInfoCompat.Builder(this, locationShortcutId) .setShortLabel(getString(R.string.location_shortcuts_short_label)) .setLongLabel(getString(R.string.location_shortcuts_long_label)) .setDisabledMessage(getString(R.string.shortcuts_disable_message)) .setIcon(IconCompat.createWithResource(this, R.drawable.icon_location)) .setIntent(Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, GpsSignalActivity::class.java.name) }) .build() )) } binding.btnDisableShortcut.setOnClickListener { // 禁用快捷方式,需要传入禁用原因,当用户点击时会显示 ShortcutManagerCompat.disableShortcuts(this, arrayListOf(locationShortcutId), "Location function is currently unavailable") } binding.btnUpdateShortcut.setOnClickListener { // 更新快捷方式 updateDynamicShortcuts() } } private fun createDynamicShortcuts() { val dynamicShortcuts = ShortcutManagerCompat.getDynamicShortcuts(this) val locationShortcut = ShortcutInfoCompat.Builder(this, locationShortcutId) .setShortLabel(getString(R.string.location_shortcuts_short_label)) .setLongLabel(getString(R.string.location_shortcuts_long_label)) .setDisabledMessage(getString(R.string.shortcuts_disable_message)) .setIcon(IconCompat.createWithResource(this, R.drawable.icon_location)) .setIntent(Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, GpsSignalActivity::class.java.name) }) .build() if (dynamicShortcuts.isEmpty() || !dynamicShortcuts.contains(locationShortcut)) { ShortcutManagerCompat.pushDynamicShortcut(this, locationShortcut) } } private fun createMultipleIntentsDynamicShortcuts() { val dynamicShortcuts = ShortcutManagerCompat.getDynamicShortcuts(this) val locationShortcut = ShortcutInfoCompat.Builder(this, locationShortcutId) .setShortLabel(getString(R.string.location_shortcuts_short_label)) .setLongLabel(getString(R.string.location_shortcuts_long_label)) .setDisabledMessage(getString(R.string.shortcuts_disable_message)) .setIcon(IconCompat.createWithResource(this, R.drawable.icon_location)) .setIntents(arrayOf( Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, HomeActivity::class.java.name) }, Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, GpsSignalActivity::class.java.name) })) .build() if (dynamicShortcuts.isEmpty() || !dynamicShortcuts.contains(locationShortcut)) { ShortcutManagerCompat.pushDynamicShortcut(this, locationShortcut) } } private fun updateDynamicShortcuts() { english = !english ShortcutManagerCompat.updateShortcuts(this, arrayListOf( ShortcutInfoCompat.Builder(this, locationShortcutId) .setShortLabel(if (english) getString(R.string.location_shortcuts_short_label) else "使用定位") .setLongLabel(if (english) getString(R.string.location_shortcuts_long_label) else "通过定位获取位置信息") .setDisabledMessage(if (english) getString(R.string.shortcuts_disable_message) else "此快捷方式不可用") .setIcon(IconCompat.createWithResource(this, R.drawable.icon_location)) .setIntent(Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, GpsSignalActivity::class.java.name) }) .build() )) } private fun createPinShortcuts() { // 先判断是否支持添加桌面快捷方式 if (ShortcutManagerCompat.isRequestPinShortcutSupported(this)) { val pinShortcutInfo = ShortcutInfoCompat.Builder(this, locationShortcutId) .setShortLabel(getString(R.string.location_shortcuts_short_label)) .setLongLabel(getString(R.string.location_shortcuts_long_label)) .setDisabledMessage(getString(R.string.shortcuts_disable_message)) .setIcon(IconCompat.createWithResource(this, R.drawable.icon_location)) .setIntent(Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, GpsSignalActivity::class.java.name) }) .build() val pinnedShortcutCallbackIntent = ShortcutManagerCompat.createShortcutResultIntent(this, pinShortcutInfo) val successCallback = PendingIntent.getBroadcast(this, 0, pinnedShortcutCallbackIntent, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_MUTABLE else 0) ShortcutManagerCompat.requestPinShortcut(this, pinShortcutInfo, successCallback.intentSender) } } } // 支持用户创建桌面快捷方式 class CreateCameraShortcutActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding: LayoutCreateShortcutsActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_create_shortcuts_activity) binding.tvTips.text = "Do you want to add the Camera Launcher icon to your home screen?" binding.btnAddShortcut.setOnClickListener { createPinShortcuts() } binding.btnReject.setOnClickListener { finish() } } private fun createPinShortcuts() { if (ShortcutManagerCompat.isRequestPinShortcutSupported(this)) { val pinShortcutInfo = ShortcutInfoCompat.Builder(this, "camera") .setShortLabel(getString(R.string.camera_shortcuts_short_label)) .setLongLabel(getString(R.string.camera_shortcuts_long_label)) .setDisabledMessage(getString(R.string.shortcuts_disable_message)) .setIcon(IconCompat.createWithResource(this, R.drawable.icon_camera)) .setIntent(Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, CameraActivity::class.java.name) }) .build() val pinnedShortcutCallbackIntent = ShortcutManagerCompat.createShortcutResultIntent(this, pinShortcutInfo) setResult(RESULT_OK, pinnedShortcutCallbackIntent) finish() } } } // 支持用户创建桌面快捷方式 class CreateLocationShortcutActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding: LayoutCreateShortcutsActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_create_shortcuts_activity) binding.tvTips.text = "Do you want to add the Location Launcher icon to your home screen?" binding.btnAddShortcut.setOnClickListener { createPinShortcuts() } binding.btnReject.setOnClickListener { finish() } } private fun createPinShortcuts() { if (ShortcutManagerCompat.isRequestPinShortcutSupported(this)) { val pinShortcutInfo = ShortcutInfoCompat.Builder(this, "location") .setShortLabel(getString(R.string.location_shortcuts_short_label)) .setLongLabel(getString(R.string.location_shortcuts_long_label)) .setDisabledMessage(getString(R.string.shortcuts_disable_message)) .setIcon(IconCompat.createWithResource(this, R.drawable.icon_location)) .setIntent(Intent(Intent.ACTION_VIEW).apply { component = ComponentName(packageName, GpsSignalActivity::class.java.name) }) .build() val pinnedShortcutCallbackIntent = ShortcutManagerCompat.createShortcutResultIntent(this, pinShortcutInfo) setResult(RESULT_OK, pinnedShortcutCallbackIntent) finish() } } }
ExampleDemo github
ExampleDemo gitee
以上就是Android 快捷方式实现实例详解的详细内容,更多关于Android 桌面快捷方式的资料请关注脚本之家其它相关文章!