大部分的卡片式外掛程式都使用多張資訊卡建構,分別代表外掛程式介面的不同「頁面」。為提供有效的使用者體驗,建議您透過外掛程式,以簡單自然的方式瀏覽資訊卡。
原本在 Gmail 外掛程式中,使用者切換 UI 資訊卡時,是透過將資訊卡推送至或從單一卡片堆疊,以及 Gmail 顯示的堆疊頂端資訊卡進行轉換。
Google Workspace 外掛程式推出首頁和非內容卡。為配合內容卡和非內容卡,Google Workspace 外掛程式為每種資訊卡都有內部資訊卡堆疊。在主機中開啟外掛程式時,對應的 homepageTrigger
會觸發,在堆疊中建立第一個首頁資訊卡 (下圖中的深藍色「首頁」資訊卡)。如未定義 homepageTrigger
,系統會建立、顯示預設資訊卡,然後推送至非關聯堆疊。第一張資訊卡是根卡片。
您的外掛程式可以建立其他非內容資訊卡,並在使用者瀏覽外掛程式時,將這些資訊卡推送至堆疊 (圖中為藍色的「推送的卡片」)。外掛程式 UI 會在堆疊中顯示頂端資訊卡,因此將新資訊卡推送至堆疊會改變顯示畫面,從堆疊中彈出資訊卡則會將顯示畫面傳回至先前的資訊卡。
如果外掛程式已定義內容相關觸發條件,當使用者進入該結構定義時,觸發條件就會觸發。觸發條件函式會建構內容相關資訊卡,但系統會根據新卡片的 DisplayStyle
更新 UI 顯示內容:
- 如果
DisplayStyle
為REPLACE
(預設值),內容相關資訊卡 (圖表中的深橘色「內容」資訊卡) 會取代目前顯示的資訊卡。這麼做可有效在非內容資訊卡堆疊上方啟動新的內容卡堆疊,且此內容資訊卡是內容堆疊的「根」資訊卡。 - 如果
DisplayStyle
為PEEK
,UI 會改為建立顯示在外掛程式側欄底部的迅速瀏覽標頭,重疊在目前的資訊卡上。預覽標頭會顯示新資訊卡的標題,並提供使用者按鈕控制項,讓使用者決定是否要查看新的資訊卡。如果使用者點選「View」按鈕,資訊卡會取代目前的資訊卡 (如上所述,用REPLACE
)。
您可以建立其他內容卡,並將其推送至堆疊中 (圖中黃色的「推送的卡片」)。更新資訊卡堆疊後,外掛程式 UI 會變更為顯示最頂端的資訊卡。當使用者離開內容,系統會移除堆疊上的內容資訊卡,並將更新到最頂層的非內容資訊卡或首頁。
如果使用者輸入的內容並未定義內容相關觸發條件,則系統不會建立新資訊卡,且仍會顯示目前的資訊卡。
以下所述的 Navigation
動作只會對相同情境的資訊卡執行操作;舉例來說,來自內容相關資訊卡的 popToRoot()
只會彈出所有其他內容相關資訊卡,且不會影響首頁資訊卡。
相反地,系統一律會使用
按鈕,讓使用者從內容卡前往非內容資訊卡。瀏覽方法
您可以從資訊卡堆疊中新增或移除資訊卡,藉此建立資訊卡之間的轉場效果。Navigation
類別提供從堆疊推送及彈出資訊卡的功能。如要打造有效的卡片導覽功能,請將小工具設為使用導覽動作。您可以同時推送或彈出多張資訊卡,但無法在外掛程式啟動時移除首次推送至堆疊上的初始首頁資訊卡。
如要前往新資訊卡以回應使用者與小工具的互動,請按照下列步驟操作:
- 建立
Action
物件,並將其與您定義的回呼函式建立關聯。 - 呼叫小工具適當的小工具處理常式函式,以設定該小工具的
Action
。 - 實作進行導覽的回呼函式。這個函式會將動作事件物件做為引數指定,且必須執行下列操作:
- 建立
Navigation
物件以定義資訊卡變更。單一Navigation
物件可包含多個導覽步驟,這些步驟會按照新增到物件的順序執行。 - 使用
ActionResponseBuilder
類別和Navigation
物件建構ActionResponse
物件。 - 傳回已建構的
ActionResponse
。
- 建立
建構導覽控制項時,您可以使用下列 Navigation
物件函式:
函式 | 說明 |
---|---|
Navigation.pushCard(Card) |
將資訊卡推送至目前的堆疊。這需要先完全建構卡片。 |
Navigation.popCard() |
從分疊頂端移除一張資訊卡。相當於點選外掛程式標題列中的返回箭頭。這項操作不會移除根卡片。 |
Navigation.popToRoot() |
從堆疊中移除所有資訊卡 (根資訊卡除外)。基本上重設卡片堆疊。 |
Navigation.popToNamedCard(String) |
從堆疊中彈出資訊卡,直到到達包含指定名稱或堆疊根資訊卡的資訊卡。您可以使用 CardBuilder.setName(String) 函式指派資訊卡名稱。 |
Navigation.updateCard(Card) |
直接取代目前的卡片,讓卡片在 UI 中顯示。 |
Navigation 最佳做法
如果使用者互動或事件應導致在相同情境下重新轉譯資訊卡,請使用 Navigation.pushCard()
、Navigation.popCard()
和 Navigation.updateCard()
方法取代現有資訊卡。如果使用者的互動或事件會導致資訊卡在不同的情境下重新轉譯,請使用 ActionResponseBuilder.setStateChanged()
在這些情境中強制重新執行外掛程式。
以下是導覽範例:
- 如果互動或事件會變更目前資訊卡的狀態 (例如將任務新增至工作清單),請使用
updateCard()
。 - 如果互動或事件提供更多詳細資料,或提示使用者進行進一步動作 (例如:按一下項目標題查看更多詳細資料,或按下按鈕建立新的日曆活動),請使用
pushCard()
顯示新頁面,同時讓使用者使用返回按鈕離開新頁面。 - 如果互動或事件會更新上一張資訊卡中的狀態 (例如透過詳細資料檢視畫面更新項目標題),請使用
popCard()
、popCard()
、pushCard(previous)
和pushCard(current)
等形式更新先前的資訊卡和目前的資訊卡。
正在重新整理資訊卡
Google Workspace 外掛程式會重新執行資訊清單中註冊的 Apps Script 觸發條件函式,讓使用者能重新整理卡片。使用者透過外掛程式選單項目觸發這個重新整理:
這個動作會自動新增至由 homepageTrigger
或 contextualTrigger
觸發函式產生的資訊卡,如外掛程式的資訊清單檔案 (結構定義和非結構定義資訊卡堆疊的「根」) 所指定。
退回多張卡片
首頁或內容相關觸發條件函式可用來建構及傳回單一 Card
物件,或應用程式 UI 顯示的 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();
}