يتم إنشاء معظم الإضافات المستندة إلى البطاقات باستخدام عدة بطاقات تمثّل "صفحات" مختلفة من واجهة الإضافة. لتوفير تجربة مستخدم فعّالة، يجب استخدام تنقّل بسيط وطبيعي بين البطاقات في الإضافة.
في الإضافات الأصلية في Gmail، تتم معالجة الانتقالات بين البطاقات المختلفة لواجهة المستخدم من خلال دفع البطاقات وإظهارها من وإلى حزمة بطاقات واحدة، مع عرض Gmail للبطاقة العلوية من الحزمة.
توفّر إضافات Google Workspace
الصفحات الرئيسية و
البطاقات غير السياقية. لاستيعاب البطاقات السياقية وغير السياقية، توفّر إضافات Google Workspace حزمة بطاقات داخلية لكلّ منها. عند فتح إضافة
في مضيف، يتم تشغيل homepageTrigger
المطابق لإنشاء بطاقة
الصفحة الرئيسية الأولى في الحزمة (بطاقة "الصفحة الرئيسية" الزرقاء الداكنة في المخطّط البياني أدناه).
في حال عدم تحديد homepageTrigger
، يتم إنشاء بطاقة تلقائية وعرضها
ودفعها إلى الحزمة غير السياقية. هذه البطاقة الأولى هي بطاقة جذر.
يمكن أن تنشئ الإضافة بطاقات إضافية غير سياقية وتدفعها إلى الحزمة (الزرقة "البطاقات التي تم دفعها" في المخطّط البياني) أثناء تنقّل المستخدم في الإضافة. تعرِض واجهة مستخدم الإضافة البطاقة العلوية في الحزمة، لذا فإنّ دفع بطاقات جديدة إلى الحزمة يؤدي إلى تغيير العرض، كما أنّ إزالة البطاقات من الحزمة تؤدي إلى إعادة عرض البطاقات السابقة.
إذا كانت الإضافة تحتوي على
عامل تشغيل سياقي محدّد،
يتم تشغيل العامل عندما يدخل المستخدم هذا السياق. تعمل وظيفة التفعيل على
إنشاء البطاقة السياقية، ولكن يتم تعديل عرض واجهة المستخدم استنادًا إلى
DisplayStyle
للبطاقة الجديدة:
- إذا كان
DisplayStyle
REPLACE
(الإعداد التلقائي)، ستحلّ البطاقة السياقية (بطاقة "سياقية" باللون البرتقالي الداكن في المخطّط البياني) محلّ البطاقة المعروضة حاليًا. يؤدي ذلك إلى بدء تجميع بطاقات سياقية جديد على قمة تجميع البطاقات غير السياقية، وهذه البطاقة السياقية هي بطاقة الجذر للتجميع السياقي. - إذا كان الرمز
DisplayStyle
هوPEEK
، تنشئ واجهة المستخدم بدلاً من ذلك عنوانًا يظهر في أسفل الشريط الجانبي للإضافة، ويظهر فوق البطاقة الحالية. يعرض عنوان معاينة البطاقة عنوان البطاقة الجديدة ويوفّر عناصر التحكّم في الأزرار للمستخدمين التي تتيح لهم تحديد ما إذا كانوا يريدون عرض البطاقة الجديدة أم لا. إذا نقر المستخدم على الزر عرض ، ستحلّ البطاقة محلّ البطاقة الحالية (كما هو موضّح أعلاه باستخدامREPLACE
).
يمكنك إنشاء بطاقات سياقية إضافية ونقلها إلى الحزمة (وهي "البطاقات التي تمّ دفعها" الصفراء في المخطّط البياني). يؤدي تعديل حزمة البطاقات إلى تغيير واجهة مستخدِم الإضافة لعرض البطاقة في أعلى الحزمة. إذا خرج المستخدم من سياق معيّن، تتم إزالة البطاقات السياقية في الحزمة ويتم تعديل الشاشة لتظهر أعلى بطاقة غير سياقية أو الصفحة الرئيسية.
إذا أدخل المستخدم سياقًا لا تحدّد إضافتك عاملاً لبدء التفاعل في السياق، لن يتم إنشاء بطاقة جديدة وسيظلّ عرض البطاقة الحالية.
لا تعمل إجراءات Navigation
الموضّحة أدناه إلا على البطاقات من السياق نفسه. على سبيل المثال، يؤدي استخدام الإجراء
popToRoot()
من داخل بطاقة سياقية إلى عرض كل البطاقات السياقية الأخرى فقط،
ولن يؤثّر في بطاقات الصفحة الرئيسية.
في المقابل، يكون الزر
متاحًا دائمًا للمستخدم للانتقال من بطاقاتك السياقية إلى بطاقاتك غير السياقية.طرق التنقّل
يمكنك إنشاء انتقالات بين البطاقات من خلال إضافة بطاقات أو إزالتها من
حِزم البطاقات. تقدّم فئة Navigation
وظائف لدفع البطاقات وإظهارها من الحزم. لإنشاء تنقّل فعال في البطاقة، عليك ضبط
تطبيقاتك المصغّرة لاستخدام إجراءات التنقّل. يمكنك دفع أو عرض
بطاقات متعددة في الوقت نفسه، ولكن لا يمكنك إزالة بطاقة الصفحة الرئيسية الأولية
التي يتم دفعها أولاً إلى الحزمة عند بدء الإضافة.
للانتقال إلى بطاقة جديدة استجابةً لتفاعل المستخدم مع التطبيق المصغّر، اتّبِع الخطوات التالية:
- أنشئ عنصر
Action
واربطه بأحد دوالّ الاستدعاء التي تحدّدها. - استخدِم
دالة معالِج التطبيق المصغّر المناسبة
لضبط
Action
في هذا التطبيق المصغّر. - نفِّذ دالة ردّ الاتصال التي تُجري التنقّل. تتلقّى هذه الدالة
عنصر حدث إجراء
كوسيطة ويجب أن تُجري ما يلي:
- أنشئ عنصر
Navigation
لتحديد تغيير البطاقة. يمكن أن يحتوي عنصرNavigation
واحد على عدة خطوات تنقّل، ويتم تنفيذها بالترتيب الذي تمت فيه إضافتها إلى العنصر. - أنشئ كائن
ActionResponse
باستخدام فئةActionResponseBuilder
وكائنNavigation
. - ارجع إلى الصفحة التي تم إنشاؤها
ActionResponse
.
- أنشئ عنصر
عند إنشاء عناصر التحكّم في التنقّل، يمكنك استخدام وظائف العنصر التالية
Navigation
:
الوظيفة | الوصف |
---|---|
Navigation.pushCard(Card) |
تدفع بطاقة إلى الحزمة الحالية. يتطلّب ذلك إنشاء البطاقة بالكامل أولاً. |
Navigation.popCard() |
تزيل بطاقة واحدة من أعلى الحزمة. يعادل النقر على سهم الرجوع في صف عنوان الإضافة. ولا يؤدي ذلك إلى إزالة البطاقات الجذر. |
Navigation.popToRoot() |
تزيل جميع البطاقات من الحزمة باستثناء البطاقة الجذر. يؤدي ذلك إلى إعادة ضبط حزمة البطاقات هذه. |
Navigation.popToNamedCard(String) |
تُخرج البطاقات من الحزمة إلى أن تصل إلى بطاقة تحمل الاسم المحدّد أو بطاقة الجذر للحزمة. يمكنك تعيين أسماء للبطاقات باستخدام الدالة CardBuilder.setName(String) . |
Navigation.updateCard(Card) |
يؤدي هذا الإجراء إلى استبدال البطاقة الحالية في مكانها، مع تعديل طريقة عرضها في واجهة المستخدم. |
أفضل الممارسات المتعلّقة بالتنقّل
إذا كان من المفترض أن يؤدّي تفاعل المستخدم أو الحدث إلى إعادة عرض البطاقات في السياق
نفسه، استخدِم methods
Navigation.pushCard()
،
Navigation.popCard()
،
وNavigation.updateCard()
لاستبدال البطاقات الحالية. إذا كان من المفترض أن يؤدي تفاعل المستخدم أو الحدث إلى
إعادة عرض البطاقات في سياق مختلف، استخدِم
ActionResponseBuilder.setStateChanged()
لفرض إعادة تنفيذ الإضافة في هذه السياقات.
في ما يلي أمثلة على التنقّل:
- إذا غيّر تفاعل أو حدث حالة البطاقة الحالية (على سبيل المثال،
إضافة مهمة إلى قائمة مهام)، استخدِم
updateCard()
. - إذا كان التفاعل أو الحدث يقدّم المزيد من التفاصيل أو يطلب من المستخدم اتخاذ إجراء إضافي (على سبيل المثال، النقر على عنوان عنصر للاطّلاع على مزيد من التفاصيل أو الضغط على زر لإنشاء حدث جديد في "تقويم Google")، استخدِم رمز
pushCard()
لعرض الصفحة الجديدة مع السماح للمستخدم بالخروج من الصفحة الجديدة باستخدام زر الرجوع. - إذا عدّل تفاعل أو حدث الحالة في بطاقة سابقة (على سبيل المثال،
تعديل عنوان عنصر من خلال العرض التفصيلي)، استخدِم رمزًا مثل
popCard()
،popCard()
،pushCard(previous)
، وpushCard(current)
لتعديل البطاقة السابقة والبطاقة الحالية.
إعادة تحميل البطاقات
تمنح إضافات Google Workspace المستخدمين إمكانية تحديث بطاقتك من خلال إعادة تشغيل دالة التفعيل في Apps Script المسجّلة في بيان التطبيق. يُجري المستخدمون عملية إعادة التحميل هذه من خلال عنصر قائمة في الإضافة:
تتم إضافة هذا الإجراء تلقائيًا إلى البطاقات التي تم إنشاؤها بواسطة وظائف التفعيل homepageTrigger
أو
contextualTrigger
، كما هو محدّد في ملف بيان
الإضافة ("الجذور" لحِزم البطاقات السياقية وغير السياقية).
عرض بطاقات متعددة
تُستخدَم دوالّ العناصر المشغِّلة للصفحة الرئيسية أو السياق لإنشاء
إما عنصر
Card
واحد أو صفيف من عناصر
Card
يعرضه
واجهة مستخدِم التطبيق.
إذا كانت هناك بطاقة واحدة فقط، تتم إضافتها إلى الحزمة غير السياقية أو السياقية بصفتها البطاقة الجذر وتعرضها واجهة مستخدم التطبيق المضيف.
إذا كانت الصفيف المعروضة تتضمّن أكثر من عنصر
Card
واحد مُنشأ، يعرض التطبيق المضيف بطاقة جديدة بدلاً من ذلك تحتوي على
قائمة بعناوين كل بطاقة. عندما ينقر المستخدم على أيّ من هذه العناوين، يعرض واجهة المستخدم البطاقة المقابلة.
عندما يختار المستخدم بطاقة من القائمة، يتم دفع هذه البطاقة إلى الحزمة الحالية ويعرضها التطبيق المضيف. يعيد الزر
المستخدم إلى قائمة رؤوس البطاقات.يمكن أن يكون ترتيب البطاقات "المسطّح" هذا مناسبًا إذا لم تكن الإضافة بحاجة إلى أي عمليات انتقال بين البطاقات التي تنشئها. في معظم الحالات، من الأفضل تحديد عمليات انتقال البطاقات مباشرةً، وجعل وظائف الصفحة الرئيسية والمشغِّلات السياقية تُعرِض كائن بطاقة واحدًا.
مثال
في ما يلي مثال يوضّح كيفية إنشاء عدة بطاقات باستخدام buttons للتنقل بين هذه البطاقات. يمكن إضافة هذه البطاقات إلى ملف السياق المرتبط أو غير المرتبط من خلال دفع البطاقة التي يعرضها 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();
}