資訊卡瀏覽

大多數的卡片型外掛程式都是使用多個資訊卡建構,代表外掛程式介面的不同「頁面」。如要提供有效的使用者體驗,建議您以簡單自然的方式在外掛程式中切換資訊卡。

一開始在 Gmail 外掛程式中,不同 UI 資訊卡之間的轉換,是透過單一資訊卡堆疊推送及彈出,並由 Gmail 顯示堆疊最頂端的資訊卡。

首頁資訊卡導覽

Google Workspace 外掛程式推出了首頁和非內容資訊卡。為配合內容資訊卡和非內容資訊卡,Google Workspace 外掛程式會為其套用內部資訊卡堆疊。在主機中開啟外掛程式時,對應的 homepageTrigger 會觸發,在堆疊中建立第一個首頁資訊卡 (下圖中的深藍色「首頁」資訊卡)。如未定義 homepageTrigger,系統會建立、顯示預設卡片,並推送至非內容堆疊上。第一張卡片是卡。

外掛程式可以建立其他與內容無關的資訊卡,並在使用者瀏覽外掛程式時,將其推送至堆疊 (圖表中的藍色「推送卡片」)。外掛程式 UI 會顯示堆疊中的頂端資訊卡,因此將新資訊卡推送至堆疊會變更顯示畫面,從堆疊中移除資訊卡則會返回之前的資訊卡。

如果您的外掛程式有已定義的內容相關觸發條件,系統就會在使用者輸入該內容時觸發觸發條件。觸發函式會建構內容相關資訊卡,但 UI 顯示會根據新卡片的 DisplayStyle 更新:

  • 如果 DisplayStyleREPLACE (預設值),內容資訊卡 (圖表中的深橘色「內容」資訊卡) 會取代目前顯示的資訊卡。這樣做能有效地在非內容資訊卡堆疊之上啟動新的關聯資訊卡堆疊,而內容相關資訊卡是結構定義堆疊的「根」資訊卡。
  • 如果 DisplayStylePEEK,UI 就會改為在外掛程式側欄底部建立短暫顯示標頭,並與目前的資訊卡重疊。提示標頭會顯示新資訊卡的標題,並提供使用者按鈕控制項,讓使用者決定是否要檢視新卡片。如果他們按一下「View」按鈕,資訊卡就會取代目前的資訊卡 (如上所述,替換為 REPLACE)。

您可以建立其他內容資訊卡,並將其推送至堆疊 (圖表中為黃色「推送的卡片」)。更新資訊卡堆疊會變更外掛程式 UI,以顯示最頂端的資訊卡。如果使用者離開結構定義,系統就會移除堆疊上的內容資訊卡,並顯示更新到內容最不具關聯的資訊卡或首頁。

如果使用者輸入的結構定義未定義內容觸發條件,系統就不會建立新卡片,且目前的資訊卡仍會顯示。

下文所述的 Navigation 動作只會對相同情境的資訊卡執行;舉例來說,來自內容資訊卡的 popToRoot() 只會彈出所有其他內容資訊卡,也不會影響首頁資訊卡。

相對地,使用者一律可以使用 按鈕,從內容資訊卡前往非內容資訊卡。

您可以在資訊卡堆疊中新增或移除資訊卡,藉此在資訊卡之間建立轉場效果。Navigation 類別提供用於從堆疊推送和彈出資訊卡的功能。如要建構有效的卡片導覽功能,請將小工具設為使用導覽動作。您可以同時推送或彈出多張資訊卡,但無法移除在外掛程式啟動時首次推送至堆疊上的初始首頁資訊卡。

如要前往新資訊卡以回應使用者與小工具互動,請按照下列步驟操作:

  1. 建立 Action 物件,並將其與您定義的回呼函式建立關聯。
  2. 呼叫小工具的適當小工具處理常式函式,設定該小工具的 Action
  3. 實作執行導覽的回呼函式。這個函式會將操作事件物件做為引數,且必須執行下列操作:
    1. 建立 Navigation 物件來定義卡片變更。單一 Navigation 物件可包含多個導覽步驟,這些步驟將依新增至物件的順序執行。
    2. 使用 ActionResponseBuilder 類別和 Navigation 物件建構 ActionResponse 物件。
    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 Script 觸發條件函式,藉此重新整理資訊卡。使用者會透過外掛程式選單項目觸發這個重新整理程序:

Google Workspace 外掛程式側欄

此動作會自動新增至 homepageTriggercontextualTrigger 觸發函式產生的資訊卡中,如同外掛程式的資訊清單檔案 (內容相關資訊卡和非內容資訊卡堆疊的「根」項目)。

傳回多張卡片

外掛程式資訊卡範例

首頁或內容相關觸發條件函式可用來建構及傳回應用程式 UI 顯示的單一 Card 物件或 Card 物件陣列。

如果只有一個資訊卡,系統會將卡片新增至非結構定義或內容堆疊,做為根資訊卡,主機應用程式 UI 則會顯示這張資訊卡。

如果傳回的陣列包含多個建構的 Card 物件,主機應用程式會改為顯示新的資訊卡,其中包含每張卡片標頭的清單。當使用者點選上述任一標頭時,UI 就會顯示對應的資訊卡。

使用者從清單中選取資訊卡時,該資訊卡將推送到目前的堆疊上,而主機應用程式會顯示該資訊卡。 按鈕會將使用者導向資訊卡標頭清單。

如果附加元件不需在您建立的卡片之間轉換,這種「平面卡」安排就很適合。不過,在多數情況下,最好直接定義卡片轉換,讓首頁和內容相關觸發條件函式傳回單一卡片物件。

範例

以下範例說明如何建構多張資訊卡,其中包含可切換使用的導覽按鈕。只要在特定結構定義中或外部推送 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();
  }