大多數的卡片式外掛程式都是使用多個資訊卡建構,這些資訊卡代表外掛程式介面的不同「頁面」。為了提供良好的使用者體驗,建議您在外掛程式中使用簡單自然的資訊卡導覽方式。
在 Gmail 外掛程式中,原本會透過推送和彈出卡片至單一卡片堆疊,並由 Gmail 顯示堆疊頂端的卡片,處理介面上不同卡片之間的轉場效果。
Google Workspace 外掛程式推出首頁和非情境卡。為了支援內容相關和不相關的資訊卡,Google Workspace 外掛程式會為每個資訊卡建立內部資訊卡堆疊。在主機上開啟外掛程式時,對應的 homepageTrigger
會觸發,以便在堆疊中建立第一個首頁資訊卡 (下圖中的深藍色「首頁」資訊卡)。如果未定義 homepageTrigger
,系統會建立、顯示並將預設資訊卡推送至非情境堆疊。第一張資訊卡是根資訊卡。
外掛程式可以建立其他非情境資訊卡,並在使用者瀏覽外掛程式時,將這些資訊卡推送至堆疊 (圖表中的藍色「推送資訊卡」)。外掛程式 UI 會顯示堆疊中的頂端資訊卡,因此將新資訊卡推送至堆疊會變更顯示畫面,而從堆疊中彈出資訊卡會將顯示畫面還原為先前的資訊卡。
如果外掛程式已定義情境觸發事件,當使用者進入該情境時,系統就會觸發觸發事件。觸發函式會建立內容卡片,但 UI 顯示會根據新卡片的 DisplayStyle
更新:
- 如果
DisplayStyle
為REPLACE
(預設值),系統會以內容卡片 (圖表中的深橘色「內容」卡片) 取代目前顯示的卡片。這麼做可在非內容相關資訊卡堆疊上方啟動新的內容相關資訊卡堆疊,而這張內容相關資訊卡是內容相關堆疊的根資訊卡。 - 如果
DisplayStyle
是PEEK
,UI 會改為建立一個顯示在外掛程式側欄底部的窺視標頭,並疊加目前的資訊卡。Peek 標題列會顯示新資訊卡的標題,並提供使用者按鈕控制項,讓他們決定是否要查看新資訊卡。如果使用者按一下「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.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();
}