卡片导航

大多数基于卡片的插件都是使用多个 代表不同“页面”的卡片的 插件界面为了提供有效的用户体验 应该在插件中的卡片之间使用简单自然的导航方式。

最初在 Gmail 插件中,界面不同卡片之间的过渡 Gmail 显示的堆栈顶部的卡片。

首页卡片导航

Google Workspace 插件推出 首页和 非内容相关卡片为了适应内容相关卡片和非内容相关卡片, Google Workspace 插件具有内部卡片堆叠 。打开插件时 都会触发相应的 homepageTrigger,以创建第一个 首页卡片(下图中的深蓝色“首页”卡片)。 如果未定义 homepageTrigger,系统会创建并显示默认卡片, 并推送到非上下文堆栈中第一张卡片是根卡。

您的插件可以创建更多与内容无关的卡片,并将其推送到 (图中蓝色的“推送的卡片”) 。插件界面会显示堆栈中的顶部卡片,因此推送新的 将卡片添加到堆栈会更改显示效果,而从堆栈中弹出卡片会返回 显示之前的卡片

如果您的插件有 内容相关触发器 当用户进入该上下文时触发触发器。触发器函数 构建上下文卡片,但界面显示会根据 DisplayStyle 新卡片:

  • 如果 DisplayStyleREPLACE(默认值)、内容相关卡片(深橙色) “内容相关”会替换当前的 卡片。这样可以有效地在顶部启动一个新的内容相关卡片堆叠 内容相关堆栈的卡片中
  • 如果 DisplayStylePEEK 时,界面会改为创建一个快速查看标头,该标头显示在 底部,覆盖当前卡片。提示标题 显示新卡片的标题,并提供用户按钮控件, 他们可以决定是否查看新卡片。如果他们点击查看 按钮时,该卡片会替换当前卡片(如上所述) REPLACE)。

您可以创建更多与情境相关的卡片 将它们推入堆叠中(图中的黄色“推入的卡片”)。正在更新 卡片堆栈会更改插件界面,以显示最顶部的卡片。如果用户 离开上下文,堆栈上的上下文卡片会被移除, 对最顶部的非内容相关卡片或首页的更新。

如果用户输入的上下文是您的插件未定义 未创建新卡片且 系统会保持当前卡片的状态

Navigation 操作 下文所述的操作仅对来自同一背景的卡片执行操作;例如 popToRoot() 系统仅会弹出所有其他内容相关卡片 不会影响首页卡片

相比之下, 按钮 始终便于用户从内容相关卡片导航到 非内容相关卡片

您可以在不同卡片之间添加或移除卡片 卡片堆叠Navigation 类提供从堆栈推送和弹出卡片的函数。构建 有效的卡片导航, widgets 使用导航 操作。您可以按任意顺序 同时显示多张卡片,但您无法移除初始首页卡片 在插件启动时首次推送到堆栈中的广告单元名称。

如需导航到新卡片以响应用户与微件的互动,请执行以下操作: 请按以下步骤操作:

  1. 创建 Action 对象 并将其与 回调函数 定义。
  2. 调用 widget 的相应 widget 处理程序函数 在该 widget 上设置 Action
  3. 实现执行导航的回调函数。此函数 被赋予了操作事件对象 作为参数,必须执行以下操作:
    1. 创建 Navigation 对象来定义卡片的更改。单个 Navigation 对象可以 包含多个导航步骤,这些步骤按顺序执行 它们会添加到对象中。
    2. 构建 ActionResponse 使用 ActionResponseBuilder 类和 Navigation 对象。
    3. 返回构建的 ActionResponse

构建导航控件时,您可以使用以下 Navigation 对象函数:

函数 说明
Navigation.pushCard(Card) 将卡片推送到当前堆栈。这需要先构建卡片。
Navigation.popCard() 从堆叠顶部移除一张卡片。相当于点击插件标题行中的返回箭头。此操作不会移除根卡。
Navigation.popToRoot() 从堆栈中移除根卡以外的所有卡片。实际上,系统会重置该卡片堆叠。
Navigation.popToNamedCard(String) 从堆栈中弹出卡片,直至到达具有指定名称或堆栈的根卡片。您可以使用 CardBuilder.setName(String) 函数为卡片指定名称。
Navigation.updateCard(Card) 就地替换当前卡片,刷新其在界面中的显示内容。

如果用户互动或事件会导致卡片在同一 上下文, 使用 Navigation.pushCard(), Navigation.popCard(), 和Navigation.updateCard() 方法来替换现有卡片。如果用户互动或事件 会导致在不同的情境中重新呈现卡片, ActionResponseBuilder.setStateChanged() 在这些情况下强制重新执行您的插件。

以下是导航示例:

  • 如果互动或事件更改了当前卡片的状态(例如, 向任务列表添加任务),请使用 updateCard()
  • 如果互动或事件提供了更多详细信息或提示用户 进一步操作(例如,点击某项的标题以查看更多详情,或者 按下按钮来创建新的日历活动),请使用 pushCard() 显示新页面,同时允许用户使用 返回按钮。
  • 如果某个互动或事件更新了上一张卡片中的状态(例如, 通过详情视图更新商品标题),请使用类似 popCard(), popCard(), pushCard(previous), 和pushCard(current) 以更新上一张卡片和当前卡片。

正在刷新卡

借助 Google Workspace 插件,用户能够 重新运行在以下位置注册的 Apps 脚本触发器函数,以刷新卡片: 。用户通过附加菜单项触发此刷新:

Google Workspace 插件边栏

此操作会自动添加到由 homepageTriggercontextualTrigger 触发函数(如插件清单中所指定) 文件(内容相关卡片和非内容相关卡片堆栈的“根”)。

退回多张银行卡

附加卡片示例

首页或上下文触发器函数用于构建和返回 单个 Card 对象或 Card 对象, 应用界面。

如果只有一张卡片,系统会将其添加到非上下文堆栈或上下文堆栈中 作为根卡,托管应用界面会显示该卡片。

如果返回的数组包含多个已构建 Card 对象,托管应用则会显示一张新卡片,其中包含一个 每张卡片的标题列表当用户点击其中任何标题时 会显示相应的卡片。

当用户从该列表中选择一张卡片时,该卡片将被推送到 当前堆栈,托管应用将显示该堆栈。通过 按钮可让用户返回到 卡片标题列表。

这个“flat”如果您的插件不需要 创建不同卡片之间的过渡不过,在大多数情况下 练习直接定义卡片过渡方式, 情境触发器函数会返回单个卡片对象。

示例

下例显示了如何构建多个带导航栏的卡片 按钮。这些卡片可以添加到 通过推送返回的卡片实现上下文或非上下文堆栈 由 createNavigationCard() 提供。

  /**
   *  Create the top-level card, with buttons leading to each of three
   *  'children' cards, as well as buttons to backtrack and return to the
   *  root card of the stack.
   *  @return {Card}
   */
  function createNavigationCard() {
    // Create a button set with actions to navigate to 3 different
    // 'children' cards.
    var buttonSet = CardService.newButtonSet();
    for(var i = 1; i <= 3; i++) {
      buttonSet.addButton(createToCardButton(i));
    }

    // Build the card with all the buttons (two rows)
    var card = CardService.newCardBuilder()
        .setHeader(CardService.newCardHeader().setTitle('Navigation'))
        .addSection(CardService.newCardSection()
            .addWidget(buttonSet)
            .addWidget(buildPreviousAndRootButtonSet()));
    return card.build();
  }

  /**
   *  Create a button that navigates to the specified child card.
   *  @return {TextButton}
   */
  function createToCardButton(id) {
    var action = CardService.newAction()
        .setFunctionName('gotoChildCard')
        .setParameters({'id': id.toString()});
    var button = CardService.newTextButton()
        .setText('Card ' + id)
        .setOnClickAction(action);
    return button;
  }

  /**
   *  Create a ButtonSet with two buttons: one that backtracks to the
   *  last card and another that returns to the original (root) card.
   *  @return {ButtonSet}
   */
  function buildPreviousAndRootButtonSet() {
    var previousButton = CardService.newTextButton()
        .setText('Back')
        .setOnClickAction(CardService.newAction()
            .setFunctionName('gotoPreviousCard'));
    var toRootButton = CardService.newTextButton()
        .setText('To Root')
        .setOnClickAction(CardService.newAction()
            .setFunctionName('gotoRootCard'));

    // Return a new ButtonSet containing these two buttons.
    return CardService.newButtonSet()
        .addButton(previousButton)
        .addButton(toRootButton);
  }

  /**
   *  Create a child card, with buttons leading to each of the other
   *  child cards, and then navigate to it.
   *  @param {Object} e object containing the id of the card to build.
   *  @return {ActionResponse}
   */
  function gotoChildCard(e) {
    var id = parseInt(e.parameters.id);  // Current card ID
    var id2 = (id==3) ? 1 : id + 1;      // 2nd card ID
    var id3 = (id==1) ? 3 : id - 1;      // 3rd card ID
    var title = 'CARD ' + id;

    // Create buttons that go to the other two child cards.
    var buttonSet = CardService.newButtonSet()
      .addButton(createToCardButton(id2))
      .addButton(createToCardButton(id3));

    // Build the child card.
    var card = CardService.newCardBuilder()
        .setHeader(CardService.newCardHeader().setTitle(title))
        .addSection(CardService.newCardSection()
            .addWidget(buttonSet)
            .addWidget(buildPreviousAndRootButtonSet()))
        .build();

    // Create a Navigation object to push the card onto the stack.
    // Return a built ActionResponse that uses the navigation object.
    var nav = CardService.newNavigation().pushCard(card);
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();
  }

  /**
   *  Pop a card from the stack.
   *  @return {ActionResponse}
   */
  function gotoPreviousCard() {
    var nav = CardService.newNavigation().popCard();
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();
  }

  /**
   *  Return to the initial add-on card.
   *  @return {ActionResponse}
   */
  function gotoRootCard() {
    var nav = CardService.newNavigation().popToRoot();
    return CardService.newActionResponseBuilder()
        .setNavigation(nav)
        .build();
  }