简介

如果你的App构建的target是Android 7.1,你就可以使用Shortcuts为用户提供一些常用功能的入口,Shortcuts可以显示在那些支持Shortcuts的launcher上,如Pixel launcher。Shortcuts的出现可以让用户更快捷的使用一些常用的功能,而不用从launcher activity一步步点击。

Shortcuts

Shortcuts通过长按launcher上的App图标激活,激活后,会弹出条状可点击的快捷方式。它的触发方式与App图标位置的移动并不冲突。当长按图标后,首先会弹出Shortcuts快捷方式,当手指按住不放,并向任意方向拖动图标时,Shortcuts快捷方式将会消失,App图标进入悬浮状态。点击每个Shortcut后面的双横杠时,还可将此shortcut添加至桌面快捷方式。

Shortcuts可以通过两种方式添加:

  • 静态:在AndroidManifest.xml中注册,在xml文件中添加shortcut的各项属性
  • 动态:由ShortcutManager管理,完成Shortcuts的添加,更新,删除等各项操作

静态的Shortcuts和动态的可以共存,它们之间也有优先级之分,稍后会说明。一个App最多可添加5个Shortcuts(静态+动态)

看起来是不是和Broadcast的注册有点类似。接下来就逐一介绍

静态Shortcuts

由于静态Shortcuts的特性,在下一次应用更新前,由静态方式添加的Shortcuts的内容无法改变。所以建议通过静态方式添加一些固定且常用的功能,比如扫码

静态添加的主要步骤

  • AndroidManifest.xml中找到launcher activity,在这个activity的节点下添加<meta-data>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <activity android:name=".MainActivity">
    <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <meta-data
    android:name="android.app.shortcuts"
    android:resource="@xml/shortcuts" />
    </activity>
  • res/xml/目录下创建shortcuts.xml(文件名不一定叫这个名字,只要与<meta-data>中的android:resource能对应就行),在这个文件中填写所有静态Shortcuts的属性

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
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:shortcutId="adjacentactivity"
android:enabled="true"
android:icon="@drawable/ic_menu_camera"
android:shortcutShortLabel="@string/adjacentactivity"
android:shortcutLongLabel="@string/adjacentactivity"
android:shortcutDisabledMessage="@string/adjacentactivity">
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="com.hnust.lzm.newfuncdemo"
android:targetClass="com.hnust.lzm.newfuncdemo.activity.multiWindowActivities.AdjacentActivity" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
<shortcut
android:shortcutId="LaunchBoundsActivity"
android:enabled="true"
android:icon="@drawable/ic_menu_send"
android:shortcutShortLabel="@string/LaunchBoundsActivity"
android:shortcutLongLabel="@string/LaunchBoundsActivity"
android:shortcutDisabledMessage="@string/LaunchBoundsActivity">
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="com.hnust.lzm.newfuncdemo"
android:targetClass="com.hnust.lzm.newfuncdemo.activity.multiWindowActivities.LaunchBoundsActivity" />
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="com.hnust.lzm.newfuncdemo"
android:targetClass="com.hnust.lzm.newfuncdemo.activity.multiWindowActivities.AdjacentActivity" />
<categories android:name="android.shortcut.conversation" />
</shortcut>
</shortcuts>

Shortcut的主要属性

  • android:shortcutId 每个Shortcut的唯一ID
  • android:enabled 该Shortcut是否可用,不可用时图标会变灰,点击后无响应或弹出自定义的提示
  • android:icon Shortcut显示的图标,如果添加到了桌面快捷方式,则此图标会被设置成桌面快捷方式图标
  • android:shortcutShortLabel 添加至桌面快捷方式后快捷方式的名称
  • android:shortcutLongLabel 长按图标后显示在Shortcuts快捷组内的名称
  • android:shortcutDisabledMessage 当快捷方式被禁用时显示的提示语

在第一个shortcut里,可以看到一个<intent>标签,这个intent就是用户选中快捷方式之后将会触发的intent。

在第二个shortcut里,可以看到两个<intent>标签。在同一个shortcut里,允许存在多个Intent,多个Intent的触发方式是:所有Intent都会被触发,然后依次入Activity栈,最终显示给用户交互的Activity是位于栈顶,也就是最后一个Intent所调起的Activity。当用户点击back键后,Activity会依次出栈,用户也将会看到所有由Intent调起的Activity

动态Shortcuts

动态的Shortcuts比较灵活,可以在用户使用app的过程中进行添加, 更新, 删除等操作。

1
2
3
4
5
6
7
8
9
10
11
ShortcutManager shortcutManager = getSystemService(Context.SHORTCUT_SERVICE);
ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")
.setShortLabel("Web site")
.setLongLabel("Open the web site")
.setIcon(Icon.createWithResource(context, R.drawable.icon_website))
.setIntent(new Intent(Intent.ACTION_VIEW,
Uri.parse("https://www.baidu.com/")))
.build();
shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));

上面的代码就添加了一个Web Site的Shortcut。每一个shortcut在代码中以ShortcutInfo体现,然后通过ShortcutManager进行相应的操作。应用被关闭以后,动态Shortcuts依旧存在。

ShortcutManager的主要方法

  • 添加:setDynamicShortcuts(List)设置整个的动态快捷方式,addDynamicShortcuts(List)向已有快捷方式列表中新的快捷方式添加
  • 更新:updateShortcuts(List)更新已存在的快捷方式
  • 删除:removeDynamicShortcuts(List)移除指定的动态快捷方式,removeAllDynamicShortcuts()移除所有的动态快捷方式
  • 禁用:disableShortcuts(List)禁用任何已存在的快捷方式,disableShortcuts(List, Charsquence) 点击时会给出错误提示
  • getMaxShortcutCountPerActivity()返回当前launcher最多支持的Shortcuts个数
  • getDynamicShortcuts() 返回动态快捷方式的List<ShortcutInfo>
  • getManifestShortcuts() 返回静态快捷方式的List<ShortcutInfo>

与静态快捷方式相似,动态快捷方式同样支持多个Intent构建back stack,只需要将setIntent()改为setIntents()

1
2
3
4
5
6
7
8
9
ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")
.setShortLabel("Web site")
.setLongLabel("Open the web site")
.setIcon(Icon.createWithResource(context, R.drawable.icon_website))
.setIntents(new Intent[]{
new Intent(Intent.ACTION_MAIN, Uri.EMPTY, this, targetActivity.class),
new Intent(Intent.ACTION_VIEW,Uri.parse("https://www.baidu.com/"))
})
.build();

注意点

  • 禁用和删除指定的快捷方式时,传入的List参数只需要带上指定快捷方式的ID即可,如

    1
    2
    3
    //对ID为“id1”的快捷方式进行操作
    shortcutManager.disableShortcuts(Arrays.asList("id1"));
    shortcutManager.removeDynamicShortcuts(Arrays.asList("id1"));
  • 更新操作时,保持待更新的Shortcut的ID不变,其余的变化即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //对ID为“id1”的快捷方式进行更新操作
    ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1") //ID保持不变
    .setShortLabel("A new web site")
    .setLongLabel("Open the new web site")
    .setIcon(Icon.createWithResource(context, R.drawable.icon_website))
    .setIntent(new Intent(Intent.ACTION_VIEW,
    Uri.parse("https://www.google.com/")))
    .build();
    shortcutManager.updateShortcuts(Arrays.asList(shortcut));
  • 不可以通过ShortcutManager对静态快捷方式进行修改操作,若执行了修改会抛出IllegalArgumentException

优先级

优先级主要体现在快捷方式弹出时与App图标的之间距离,在快捷方式组中,越靠近App图标的快捷方式优先级越高

  • 静态快捷方式的优先级永远高于动态快捷方式
  • 静态快捷方式的优先级按照shortcuts.xml文件中的顺序依次排列
  • 动态快捷方式可通过setRank(int rank)来手动设置优先级

    1
    2
    3
    ShortcutInfo shortcut = new ShortcutInfo.Builder(MainActivity.this, "shortcut")
    .setRank(1)
    .build();

    rank值越大,优先度越低。静态快捷方式地rank值为0

  • setRank()不能传入负值,否则会抛出异常