探索Android 7.1 app快捷方式(App Shortcuts)

Google已经发布了Android Nougat 7.1(API 25) 版本,但是这个不是一个小的发布版,事实上它绑定了一些有趣的功能在底层。其中一个附加功能就是App快捷方式。

  • App快捷方式对于暴露你的app的actions然后把用户带入你的流程中去都是非常不错的
  • 快捷方式可以是静态或者动态的
  • 静态的方式一旦定义了就不能更改了(你只可以通过App重新部署来更改)
  • 动态的方式可以被动态地改变
  • 一旦你通过快捷方式开启一个Activity,你可以创建一个Activity的回退栈
  • 快捷方式可以被重新排序,但是只针对他们各自的类型,静态快捷方式总是会处于底部因为他们是最先被添加的(他们没有可以被定义的排名优先级)
  • 这些快捷方式的标签都是CharSequence,你可以通过spans来操作他们

如果你想通过一步一步的指南来实现,请接着看。

是什么及为什么?

App快捷方式是将你应用的常见操作或者任务暴露给发射器的一种手段。用户可以通过长按app的启动图标看到快捷方式。

他们有两种类型:

  • 静态:被静态定义在资源文件里,不能被更改除非你修改了文件并且重新部署了app
  • 动态:发布在运行时,快捷方式可以被更新不需要重新部署app

注意:你的app最多只可以有5个快捷方式

通过暴露你的常见任务,你的用户可以不需要额外的指示便可直接返回你的app

怎么做

给你的App添加快捷方式是相当简单的。让我们从创建一个简单的静态快捷方式开始。

注意:你必须要使用Android Nougat 7.1 的设备和一个支持快捷方式的launcher(比如Pixel launcher或者Now launcher)

静态快捷方式

我假设你已经有了或者创建了一个新的Android Studio项目。下面配置你的AndroidManifest.xml,添加如下meta-data标志到你的主Activity中:

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  package="com.catinean.appshortcutsdemo">

  <application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
        <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>

      <meta-data
        android:name="android.app.shortcuts"
        android:resource="@xml/shortcuts" />
    </activity>
  </application>

</manifest>  

在meta-data中,android:resource的key对应一个被定义在res/xml/shortcuts.xml的资源文件。在这你需要定义你的所有的静态快捷方式,然后将一个可以打开你app指定的activity的快捷方式添加进去(在我的例子里我创建了一个假想的StaticShortcutActivity)

<?xml version="1.0" encoding="utf-8"?>  
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">  
  <shortcut
    android:enabled="true"
    android:icon="@drawable/ic_static_shortcut"
    android:shortcutDisabledMessage="@string/static_shortcut_disabled_message"
    android:shortcutId="static"
    android:shortcutLongLabel="@string/static_shortcut_long_label"
    android:shortcutShortLabel="@string/static_shortcut_short_label">
    <intent
      android:action="android.intent.action.VIEW"
      android:targetClass="com.catinean.appshortcutsdemo.StaticShortcutActivity"
      android:targetPackage="com.catinean.appshortcutsdemo" />
  </shortcut>
</shortcuts>  

你可以看到这个file根目录的tag是,它可以包含多个块。他们的每一个,正如你所想,都代表一个静态的快捷方式。以下的配置可以设置在每一个快捷方式下:

  • enabled: 作为状态声明,表明是否开启快捷方式。如果你想要禁用你的静态快捷方式,你可以把它置为false,或者简单粗暴地把它从集合中移除。
  • icon: icon显示在快捷方式的左边。在我的例子里,我从Android Studio中创建了一个简单的Vector Drawable并作为一个icon。
  • shortcutDisabledMessage: 当你禁用了你的快捷方式,它会从这些快捷方式中消失,用户可以通过长按app图标进行显示,但是可以将一个快捷方式绑定到launcher上(通过在想要的的启动界面长按及拖拽它),因此当禁用了被绑定的快捷方式将显示灰色,当点击它的时候会出现显示这个message的Toast。
  • shortcutLongLabel:当launcher有足够的空间时,快捷方式长文本会显示
  • shortcutShortLabel:快捷方式的一段简短的描述。这个域是必须的。这个将会出现在你的launcher上。
  • intent: 当点击时你的快捷方式你定义intent(或者intents)将开启

让我们看看我们的静态快捷方式长什么样子:


这里写图片描述

很好用也很简单,但是可以看到,当点击回退键后,用户却回到了launcher界面。那怎样让用户回到app里呢?为了实现这个功能,我们可以在之前的基础上添加多个intent标签:

<?xml version="1.0" encoding="utf-8"?>  
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">  
  <shortcut
  ...>
    <intent
      android:action="android.intent.action.MAIN"
      android:targetClass="com.catinean.appshortcutsdemo.MainActivity"
      android:targetPackage="com.catinean.appshortcutsdemo" />
    <intent
      android:action="android.intent.action.VIEW"
      android:targetClass="com.catinean.appshortcutsdemo.StaticShortcutActivity"
      android:targetPackage="com.catinean.appshortcutsdemo" />
  </shortcut>
</shortcuts>

注意看,我们在之前已有的intent之上添加了一个额外的指向MainActivity。这个将会创建一个intents的回退栈,最后一个Activity是由快捷方式打开。在我们的例子里,回退栈看起来似乎是MainActivity -> Static ShortcutActivity,因此当我们按下回退键时,用户会回到MainActivity:


这里写图片描述

添加一个静态的快捷方式是非常简单的,现在让我们关注定义一些动态的快捷方式:

动态快捷方式

正如他们名字所述,这个快捷方式可以在运行时被动态修改而不需要重新部署app。如你所想的,这个不是通过静态资源文件(shortcuts.xml)定义的,而是通过code创建。

让我们添加我们的第一个动态快捷方式。为了实现它,你将必须使用ShortcutManager和ShortcutInfo.Builder。我将在我的MainActivity的onCreate()方法中构建第一个动态快捷方式:

@Override
protected void onCreate(Bundle savedInstanceState) {

    ...

    ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);

    ShortcutInfo webShortcut = new ShortcutInfo.Builder(this, "shortcut_web")
            .setShortLabel("catinean.com")
            .setLongLabel("Open catinean.com web site")
            .setIcon(Icon.createWithResource(this, R.drawable.ic_dynamic_shortcut))
            .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("https://catinean.com")))
            .build();

    shortcutManager.setDynamicShortcuts(Collections.singletonList(webShortcut));
}

这儿我们获得了shortcutManager以及ShortcutInfo的构造器。通过使用ShortcutInfo.Builder我们可以设置我们想要创建的快捷方式的各种属性。上面我们使用的builder的所有方法都与静态快捷方式的相同属性相对应,因此我不再赘述。然而,有一个属性是有点不同的,即shortcut_web,它是快捷方式的id,作为第二个参数被定义在StaticInfo.Builder的构造器里。我已经定义了intent来打开网页。最后,我把动态的快捷方式设置到ShortcutManager里。现在看看我们的快捷方式是什么样子:


这里写图片描述

看起来很6啊~现在我们的app有2个快捷方式-一个静态一个动态。

我们来添加另一个指向app内部的activity的快捷方式,然后看看我们可以怎样给它创建一个回退栈。

@Override
protected void onCreate(Bundle savedInstanceState) {

    ...

    ShortcutInfo dynamicShortcut = new ShortcutInfo.Builder(this, "shortcut_dynamic")
            .setShortLabel("Dynamic")
            .setLongLabel("Open dynamic shortcut")
            .setIcon(Icon.createWithResource(this, R.drawable.ic_dynamic_shortcut_2))
            .setIntents(
                    new Intent[]{
                            new Intent(Intent.ACTION_MAIN, Uri.EMPTY, this, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK),
                            new Intent(DynamicShortcutActivity.ACTION)
                    })
            .build();

    shortcutManager.setDynamicShortcuts(Arrays.asList(webShortcut, dynamicShortcut));
}

可以看到现在我们为了创建一个回退栈在builder上使用了setIntents()。

第一个的intent相当于MainActivity。我们指定它为FLAG_ACTIVITY_CLEAR_TASK是为了清除任何一个存在的且与app有关的任务,并创建MainActivity为当前的根activity,第二个intent对应DynamicShortcutActivity(这只是一个我创建的空的Activity)。为了实现这个,我们需要提供一个Intent附带一个特殊的Action,这是一个被定义在DynamicShortcutActivity的静态字符串,与之对应的intent-filter的action被定义在AndroidManifest.xml:

<activity  
      android:name=".DynamicShortcutActivity"
      android:label="Dynamic shortcut activity">
      <intent-filter>
        <action android:name="com.catinean.appshortcutsdemo.OPEN_DYNAMIC_SHORTCUT" />
        <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>
</activity>  

通过这个顺序声明intents数组,我们确保通过我们创建的快捷方式,当用户打开DynamicShortcutActivity之后按下back键,MainActivity也将被打开。

让我们看看效果:


这里写图片描述

快捷方式的排序

我们现在已有了一个静态快捷方式和两个动态快捷方式,我们如何指定一个自定义的顺序给他们呢?仔细看看ShortcutInfo.Builder的方法,其中一个吸引了我们的注意:setRank(int)。通过设置一个自定义的排序方法给一个动态的快捷方式,我们可以控制他们出现的顺序:排名越高,快捷方式越靠顶部。

比方说我们想要第二个快捷方式(catinean.com)置顶,我们可以动态改变已添加进去的快捷方式的排名。我们在MainActivity里的按钮按下的时候实现:

findViewById(R.id.main_rank_button).setOnClickListener(new View.OnClickListener() {

      @Override
      public void onClick(View view) {
          ShortcutInfo webShortcut = new ShortcutInfo.Builder(MainActivity.this, "shortcut_web")
                  .setRank(1)
                  .build();

          ShortcutInfo dynamicShortcut = new ShortcutInfo.Builder(MainActivity.this, "shortcut_dynamic")
                  .setRank(0)
                  .build();

          shortcutManager.updateShortcuts(Arrays.asList(webShortcut, dynamicShortcut));
      }
});

在这个按钮的click监听器里,我们使用同样的ID创建新的ShortcutInfo给每一个我们之前添加过的快捷方式,但是现在我们给shortcut_web设置了一个更高的排名,把更低的排名给shortcut_dynamic。最后我们使用了ShortcutManager里的updateShortcuts(List)方法去使用新排名更新快捷方式:


这里写图片描述

你可以从gif图上看到静态快捷方式位于了列表的底部。事实上,我们不能改变静态快捷方式的排名,他们会按照在shortcuts.xml文件里定义的顺序进行展示。如果我们只有一个静态快捷方式,那他的默认排名是0并且不能被改变。

额外的一些小姿势

如果注意看ShortcutInfo.Builder里的setShortLabel(CharSequence)这个方法,我们可以看到他接收了一个CharSequence作为一个参数。这意味着什么?对,这意味着我们可以使用自定义的spans去玩它。

比方说如果我们想在按下按钮的时候把label的颜色变为红色,我们可以创建一个SpannableStringBuilder以及使用ForegroundColorSpan设置一个我们想要的颜色,用spannableStringBuilder作为一个shortLabel(因为SpannableStringBuilder就是一个CharSequence)

findViewById(R.id.main_rank_button).setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View view) {

        ForegroundColorSpan colorSpan = new ForegroundColorSpan(getResources().getColor(android.R.color.holo_red_dark, getTheme()));
        String label = "catinean.com";
        SpannableStringBuilder colouredLabel = new SpannableStringBuilder(label);
        colouredLabel.setSpan(colorSpan, 0, label.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);

        ShortcutInfo webShortcut = new ShortcutInfo.Builder(MainActivity.this, "shortcut_web")
                .setShortLabel(colouredLabel)
                .setRank(1)
                .build();

        ...
    }
});


这里写图片描述

本文为翻译的文章,想看英文原文的朋友可以点原文链接

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值