ממשק המשתמש של A2UI עובר מרכיבים שמתמקדים באינטרנט לרכיבים מקוריים של Android. מנוע העיבוד החדש של Jetpack Compose ל-A2UI בגרסה 0.9 מאפשר למפתחים לעבד באופן מקורי ממשק משתמש דינמי שנוצר על ידי LLM. במקום תצוגות אינטרנט, הוא משתמש במסגרת של שחקנים מבוססי-Coroutine, במחזורי חיים של מחלקות אטומות ובשילוב מובנה של Material 3.
בסשן הזה נסביר על הארכיטקטורה החדשה, איך עצי רכיבים מבוססי-שרת מתורגמים לצומתי Compose ריאקטיביים, ואיך מעברי פריסה מובנים מטפלים בעדכונים מבניים בצורה חלקה בלי לחסום את ממשק המשתמש.
תמליל
שלום לכולם, וברוכים הבאים להרצאה הזו שבה נדבר על איך ליצור ממשק משתמש באפליקציות ל-Android באמצעות ספריית A2UI החדשה שלנו.
קוראים לי ניק בוצ'ר, אני מנהל מוצר שעובד על Jetpack Compose.
שלום לכולם, אני אנטון, מהנדס בצוות של Compose.
אני עובד על מנוע העיבוד החדש הזה של Jetpack Compose A2UI, ואנחנו מאוד נרגשים לספר לכם עליו היום.
כן, עכשיו עם העלייה ביכולות ה-AI, הרבה אפליקציות בוחנות איך AI יכול לעזור לכם לשפר או ליצור חוויות חדשות למשתמשים שלהן.
אחת מהחוויות המרגשות שאנשים מסוימים בוחנים היא ממשק משתמש גנרטיבי, שבו קצוות עורפיים של סוכנים יכולים ליצור ממשקי משתמש חדשים לגמרי למשתמשים בזמן הריצה, ואנחנו חושבים שזה מעניין מאוד.
אבל לצערנו, כרגע הרבה תגובות של קצוות עורפיים של סוכנים לכודות בתוך גושי טקסט, כמו זה.
לדוגמה, ביקשתי מסוכן AI ליצור לי תכונה חדשה להזמנת נסיעה.
אז הנה ההנחיה.
וכמו שאתם רואים, היא נתנה תשובה די טובה, אבל היא לכודה בתוך גוש הטקסט הזה שקשה לי לעבוד איתו, והוא לא נראה כמו שאר האפליקציה שלי.
ממשק משתמש גנרטיבי מאפשר לכם ליצור חוויה כזו, שבה אתם יכולים ליצור ממשק משתמש חדש תוך כדי תנועה בתגובה לקלט דינמי, וכך לפתוח אפשרויות להתאמה אישית רבה יותר או לחוויות חדשות נוספות.
הנה דוגמה לממשק משתמש שנוצר באמצעות רכיבים רגילים וייעודיים.
אז אתם בהחלט יכולים ליצור חוויה כזו בעצמכם כבר היום, אבל תצטרכו להגדיר פורמט משלכם להחלפה עם הקצה העורפי, זרם אירועים משלכם, פונקציות וכו'.
A2UI, ראשי תיבות של Agent-to-UI, הוא פרוטוקול פתוח בפורמט JSON שמשמש להגדרת כל הדברים האלה.
אחד הדברים שאני ממש אוהב ב-A2UI הוא שזה פורמט הצהרתי.
הנציג רק מחזיר ייצוג JSON של ממשק המשתמש לעיבוד.
הוא לא מחזיר קוד שהלקוח צריך להפעיל, מה שעלול לחשוף אתכם לפרצות אבטחה מסוימות.
הוא פשוט הצהרתי.
אז אילו עוד מושגים צריך לדעת על A2UI?
יש רק שני מושגים עיקריים: קטלוגים ופלטפורמות.
הקטלוג כולל רכיבים כמו טקסט, כפתור, שורה ועמודה, שהנציג יכול להשתמש בהם כדי ליצור את ממשק המשתמש.
בקטלוג יש גם פונקציות שהנציג יכול להשתמש בהן כדי לעצב טקסט, לאמת קלט של משתמשים או לפתוח כתובות URL.
קטלוג הוא חוזה שמגדיר באילו רכיבים ופונקציות האפליקציה תומכת, והנציג יכול להשתמש רק בהם.
כמו שניק ציין, זה אומר שהנציג אף פעם לא מריץ קוד שרירותי באפליקציה.
הוא יוצר ממשק משתמש מאבני הבניין שאתם מעצבים וסומכים עליהן.
מושג חשוב נוסף הוא "משטח".
פלטפורמה היא חלק אחד בממשק המשתמש שהסוכן שולט בו, כמו הדוגמה הזו של מתכנן הנסיעות בשקף.
הנציג מתאר את ממשק המשתמש של פלטפורמה ב-JSON באמצעות הודעות הפרוטוקול של A2UI, כמו createSurface ו-updateComponents, ומשתמש ברכיבים ובפונקציות שמוגדרים בקטלוג.
אז כשמדברים על קטלוג, קטלוג הוא מושג בסיסי מאוד ב-A2UI, כי הוא קובע באילו רכיבים הסוכן יכול להשתמש כדי ליצור ממשק.
המפרט של A2UI מגדיר משהו שנקרא "הקטלוג הבסיסי", שהוא קבוצה של רכיבים נפוצים שאפשר להשתמש בהם כדי ליצור את רוב ממשקי המשתמש.
מנוע הרינדור של Jetpack Compose A2UI מספק הטמעה של הקטלוג הבסיסי באמצעות רכיבי Material 3.
רוב הרכיבים בקטלוג מוכנים לשימוש מיידי.
יש שלושה רכיבי מדיה: תמונה, וידאו ונגן אודיו, שבהם צריך לחבר את הספרייה שבחרתם.
לדוגמה, אתם יכולים לבחור ב-Coil לטעינת תמונות וב-Media3 לנגן הווידאו והאודיו.
אבל הכוח האמיתי של A2UI ושל ספריית הרינדור החדשה שלנו הוא שאתם יכולים ליצור רכיבים וקטלוגים משלכם, בהתאמה אישית מלאה, ולשלוט באופן מלא במה שהסוכן יכול להשתמש בו כדי ליצור את ממשק המשתמש.
נחזור לקטלוגים בהמשך.
אוקיי, בואו נראה איך זה נראה.
אז בואו נעבור למכשיר ההדגמה.
זו אפליקציית ההדגמה שלנו, שבה נשתמש, ואנחנו נבנה את ה-renderer של A2UI כדי להראות לכם כמה זה קל.
אז מדובר בקבוצה של הנחיות, כמו שאפשר לראות בצ'יפים הקטנים האלה באמצע המסך, שאפשר לשלוח לסוכן.
אז אם נלחץ על אחת מההנחיות, היא תישלח לנציג אנושי אמיתי בקצה העורפי, אז תחזיקו אצבעות להדגמה, ובנוסף להנחיה, אנחנו גם מוסיפים כמה הוראות מערכת.
אז אנחנו מספרים לקצה העורפי על פרוטוקול A2UI, על קטלוג הרכיבים, במקרה הזה, הקטלוג הבסיסי שבו אנחנו משתמשים כאן, שבו הוא יכול להשתמש כדי לבנות את ממשק המשתמש, ואנחנו נותנים לו כמה דוגמאות לאיך לבנות את זה.
ההטמעה של סשן הצ'אט באפליקציית ההדגמה שלנו כבר יודעת איך לחלץ הודעות A2UI בפורמט JSON גולמי מהתשובות של הנציג.
לכן, במקרה הזה, הנציג השיב עם טקסט ואחריו הודעות פרוטוקול A2UI: createSurface, updateComponents.
אבל כרגע, האפליקציה שלנו רק מציגה את ה-JSON הגולמי הזה ולא מעבדת אותו כממשק משתמש.
אוקיי, לפני שנתחיל להטמיע את הקוד, נחזור לשקפים לשנייה אחת, נחזור צעד אחד אחורה ונסביר על עיצוב המערכת שבה נשתמש כדי להטמיע את זה.
הנה, זה מתחיל.
כן, אז זו התוכנית.
לכן, סוכן ה-AI מעביר תשובה לאפליקציה שלנו, שמתקבלת על ידי המחלקה ChatSession.
ה-ChatSession מחלץ הודעות JSON גולמיות של A2UI מהתשובות של הנציג בזמן שהן מוזרמות, ומעביר אותן למעבד ההודעות של A2UI.
מעבד ההודעות מוגדר עם מנתח שממיר את הודעות ה-JSON הגולמיות האלה לייצוג סמנטי.
לאחר מכן, המעבד מאמת את ההודעות מול הגדרת הקטלוג וממפה אותן לקבוצה של מודלים של פלטפורמות.
מודלים של ממשק המשתמש – אלה הדברים שמגדירים איך ממשק המשתמש נראה, ואת החלקים בממשק המשתמש שהסוכן שולט בהם.
לאחר מכן, המודלים האלה של פלטפורמות עוברים רינדור כממשק משתמש מקורי של פיתוח נייטיב באמצעות הפלטפורמה הקומפוזבילית A2UI שסופקה על ידי הספרייה שלנו.
התקשורת בין הסוכן לממשק המשתמש היא דו-כיוונית, כך שכשהמשתמש יוצר אינטראקציה עם ממשק המשתמש, הפעולות והשגיאות של המשתמש זורמות בחזרה לסוכן.
לסיכום, יש שלושה אבני בניין עיקריות: הקטלוג, מעבד הבקשות והפלטפורמה.
בואו נכתוב את הקוד.
כן, בואו נבנה אותו אם נוכל להחליף מכונות.
אוקיי, אז השלב הראשון הוא להוסיף את התלות.
כאן אפשר לראות שכבר כללנו את התלות של Material 3 A2UI, וכבר סנכרנו את הפרויקט כדי שלא נצטרך לשבת ולצפות ב-Gradle.
זוהי התלות ברמה העליונה שמגדירה את ההטמעה של הקטלוג הבסיסי באמצעות רכיבי Material 3, אבל יש תלויות אחרות ברמה מפורטת יותר מתחתיה.
אז אם אתם לא רוצים להשתמש בקטלוג הבסיסי, ובהמשך נסביר למה, אתם יכולים להשתמש בקטלוגים המפורטים יותר.
אבל זה יביא – ימשוך את השאר באופן טרנזיטיבי.
אוקיי.
אז אמרנו שיש שלושה דברים.
השלב הראשון היה להגדיר קטלוג.
אז כאן באפליקציה שלנו, אנחנו הולכים ליצור פונקציית מפעל כדי לעזור לבנות מופע של הטמעת Material 3 של הקטלוג הבסיסי.
אז אנחנו משתמשים בפונקציית הספרייה הזו כאן, קטלוג בסיסי של חומר A2UI, שגם לו יש גרסה.
וכמו שאמרנו, הוא מספק את כל הרכיבים הנפוצים שאתם צריכים, אבל אנחנו לא רוצים להכריח אתכם להשתמש בספריות מסוימות לטעינת תמונות או לעיבוד מדיה, אז אתם צריכים לספק אותן בעצמכם.
אז כאן, למשל, אנטון כבר יצר את ה-wrapper הזה סביב ה-AsyncImage composable של Coil, שבו נשתמש כדי לטעון תמונות, וגם קצת לוגיקה נוספת להדגמה שלנו כדי לחפש תמונות מוויקיפדיה.
כך אנחנו יכולים לחבר את זה בקלות רבה לרכיבי הקטלוג.
באופן דומה, יש לנו הטמעות דומות לעיבוד, לווידאו ולאודיו, שבהן נשתמש בספריית Media3 כדי לעבד אותם באמצעות ExoPlayer.
בצורה פשוטה כזו, אנחנו יכולים לחבר אליו את הרכיבים שלנו.
הדבר הבא שהקטלוג צריך הוא קבוצה של פונקציות סטנדרטיות.
אז אלה פונקציות שהלקוח יכול לספק.
שוב, אנחנו שולחים הטמעה של זה יחד עם הקטלוג הבסיסי, אבל אנחנו צריכים לספק לו הקשר, שבו תשתמשו כדי לבצע פעולות מסוימות, כמו פתיחת URI אם משתמש מקיש על פריט.
זהו, השלמנו את השלב הראשון.
יצרנו קטלוג.
השלב הבא הוא להגדיר את מעבד הבקשות.
אז זה מה שמנתח בפועל את התשובות וכן הלאה.
אז נעבור למודל התצוגה של האפליקציה, שבו קודם כל ניצור מופע של הקטלוג שהגדרנו קודם, כי נצטרך אותו.
ואז ניצור את מנתח ההודעות הזה, אובייקט מעבד ההודעות, שצריך גישה לקטלוג כדי לאמת את התגובות מול הרכיבים שבהם אנחנו רוצים להשתמש.
רכיב נוסף שאנחנו צריכים ליצור הוא מנתח, שהוא למעשה האובייקט שאחראי על ניתוח הודעות ה-JSON כשהן חוזרות.
כאן אנחנו יוצרים את האובייקט ChatSession.
כמו שציינתי קודם, ChatSession כבר מחלץ את ההודעות הגולמיות של A2UI מהתשובות של הנציג.
כשהודעת A2UI חדשה מוכנה, היא מפעילה את פונקציית ה-callback הזו של onA2uiMessage.
לכן, כל מה שצריך לעשות הוא להעביר כל אחת מההודעות האלה כשהן מגיעות למעבד.
אנחנו קוראים לפונקציה processInput, נותנים לה מנתח והודעה.
לכן, הפונקציה processInput מנתחת כל הודעה ואז מוסיפה את העבודה לתור של המעבד כדי לאמת אותה מול הקטלוג ולעדכן את המודלים של המשטח.
לכן, המעבד חושף את הקבוצה הנוכחית של מודלים פעילים של פני השטח בתור activeSurfaces StateFlow.
ב-ChatSession יש לוגיקה מסוימת להוספת מודלים של ממשק למצבי ממשק המשתמש של תשובות הסוכן לעיבוד.
לכן, אנחנו פשוט מעבירים את ה-StateFlow של activeSurfaces כפרמטר ל-ChatSession.
כמו שציינתי, הפונקציה processInput מוסיפה את העבודה לתור של המעבד.
אבל כדי להתחיל את העיבוד בפועל, אנחנו צריכים להפעיל coroutine בשרשור ברקע.
לכן נשתמש בהיקף של מודל התצוגה ונגדיר את ה-dispatcher שמוגדר כברירת מחדל.
וב-coroutine הזה, אנחנו רק צריכים לקרוא ל-processor.collectMessages.
אז בזמן שה-coroutine הזו פעילה, המעבד יטפל בהודעות מתור ההודעות.
אז עוד דבר שאנחנו צריכים לעשות הוא שכשהמשתמש מוחק את היסטוריית הצ'אט, אנחנו צריכים גם למחוק את הפלטפורמות הנוכחיות.
לכן, אנחנו קוראים ל-clearSurfaces.
אוקיי.
אז ביצענו את שני השלבים הראשונים.
יצרנו את הקטלוג, יצרנו את המעבד לניתוח ההודעות.
השלב הבא הוא להציג את התשובות האלה בממשק המשתמש.
לכן אנחנו אוספים את המשטחים האלה כשהם מנותחים על ידי המנתח.
ועכשיו בממשק המשתמש שלנו, נעביר אותם באובייקט התשובה הזה של הנציג ופשוט נחזור עליהם.
ואז נשתמש ברכיב ה-composable של משטח A2UI ונעביר אליו את המודל, כל אחד מהמודלים, בזמן שאנחנו עוברים עליהם כדי לעבד את התוכן במקום שבו אנחנו רוצים שהוא יופיע.
הערה אחת כאן היא שהסוכן עשוי לבחור להשתמש במשהו כמו רשימה אם הוא בוחר להציג, אתם יודעים, כמות גדולה של תוכן.
לכן, צריך להגדיר את הגובה המקסימלי של הרכיב הזה כדי לוודא שהוא יעובד כמו שצריך.
זהו, הטמענו רכיב עיבוד בסיסי של A2UI.
אז עכשיו, אם נחזור לאפליקציה, נפעיל אותה מחדש ונלחץ שוב על ההנחיה הזו, אז בתקווה שהסוכן ישתמש בזה כדי, כן, הנה, כדי להציג ממשק משתמש אמיתי.
אז פשוט נכתוב את הקוד הזה עכשיו.
זה לא אחיזת עיניים.
אבל כן, הוא משתמש בתמונות אמיתיות.
הוא משתמש בכרטיסיות, בתיבות דו-שיח, בחלוניות לבחירת תאריך, בסרגלי הזזה, אתם יודעים, בכל הרכיבים הרגילים של Material שאתם מכירים, כדי ליצור את האפליקציה במקום להציג רק תשובה טקסטואלית.
מכיוון שזו אפליקציה אמיתית שמשתמשת ברכיבים אמיתיים של Material, היא עושה את הדברים שרכיבים אמיתיים עושים.
הביצועים שלו דומים לאלה של אפליקציה מקומית.
הוא מגיב למצב כהה כאן.
אלה רכיבי Material.
לכן, אם יש לכם רכיב קומפוזבילי חיצוני של עיצוב Material שבו התאמתם אישית את הצבעים, הצורות, הטיפוגרפיה וכו', הרכיבים האלה יקלטו אותם כך שהם ייראו כמו שאר האפליקציה.
כמו שניק ציין, מכיוון שאלה רכיבי Compose מקוריים, יש להם סמנטיקה מקורית.
זה אומר שכל תכונות הנגישות המובנות של Compose נתמכות.
כך שדברים כמו TalkBack, שינוי קנה מידה של גופנים וניווט באמצעות המקלדת פועלים באופן מיידי.
עכשיו, בגלל שאתם מפעילים נציג כדי ליצור ממשק משתמש חדש בזמן הריצה, יכול להיות שתהיה השהיה מסוימת.
יכול להיות שתצטרכו לחכות עד שהנציג יענה.
הספרייה עושה כל מה שאפשר כדי לצמצם את זמן האחזור הזה.
אז אם נשתמש בסוג כזה של נזריק כמה כמה המתנות כדי להראות לך קצת יותר לאט כדי להראות מה קורה איך אנחנו ממזערים את זה.
כן, כי לפעמים לוקח זמן עד שהסוכן מעביר את התשובה המלאה, אז אנחנו משתמשים במשהו שנקרא עיבוד הדרגתי.
לכן, ברגע שרכיב הבסיס מגיע, אנחנו מתחילים להציג את ממשק המשתמש של הפלטפורמה.
כל רכיבי מאגר הנתונים מציגים פלייסהולדרים של טעינה לרכיבי הצאצא שלהם אם רכיבי הצאצא עדיין לא הגיעו.
כשרכיבי הצאצא מגיעים, הם מונפשים באמצעות סכימת התנועה של Material 3.
אוקיי.
ואם נוכל לעבור להנחיה הבאה רק כדי להראות תכונה אחרת של הספרייה.
הנה עוד הנחיה שבה אנחנו מזמינים שולחן לארוחת ערב.
וכאן הסוכן בוחר להשתמש ברכיב הסליידר הזה, כן, הוא השתמש בו.
כאן אתם יכולים לראות שאפשר לגרור את ממשק המשתמש שהסוכן יצר תוך כדי תנועה, ולבצע בו פעולות.
שימו לב שלא רק המספר ברכיב משתנה, אלא גם מחרוזת הפורמט הזו למטה שבה הוא נספר.
Like tell me that's not doing a round trip back to the agent just to update that, right?
זהו קישור דו-כיווני.
לכן רכיב הסרגל קשור למודל הנתונים בזיכרון של המשטח הזה.
כל הרכיבים שמקושרים לאותו נתיב במודל הנתונים מתעדכנים באופן אוטומטי כשהערך משתנה.
אז פס ההזזה קורא את הערך שצריך להציג, וכשאני גורר את פס ההזזה, הוא כותב אותו בחזרה למודל כדי לעדכן אותו.
כל זה קורה באופן מקומי, בלי צורך לשלוח את הבקשה לסוכן.
אבל אנחנו יכולים גם לבקש מהסוכן לעדכן את הנתונים.
אז נניח שיש 6 אנשים.
שימו לב לסליידר כשהנציג עונה.
הוא פשוט קפץ ל-6.
בואו נראה מה קיבלנו מהנציג.
במקום לשלוח תיאור חדש לגמרי של ממשק המשתמש, הסוכן שלח רק הודעה קטנה אחת updateDataModel שעדכנה את הנתיב של הסליידר.
מכיוון שהסליידר קשור לנתיב הזה, הוא מתעדכן במקום.
לכן, מודל הנתונים של כל משטח מבוסס על מצב תמונת המצב של Compose, וכל מאפיין של רכיב שקשור לנתיב הוא מצב נגזר.
לכן, בכל פעם שהמשתמש או הנציג מעדכנים את הערך במודל הנתונים, כל הרכיבים שמקושרים לאותו ערך נוצרים מחדש באופן אוטומטי.
אוקיי.
אז נניח שאנחנו רוצים ליצור קמפיינים וללחוץ על כפתור ההזמנה, מה יקרה כאן, אנטון?
כרגע לא קורה כלום.
הכפתור שולח פעולה, אבל אנחנו עדיין לא שולחים אותה לנציג.
אז בואו נתקן את זה.
אוקיי, אז בדיוק כמו שהיינו צריכים להתחבר למעבד כדי לעבד את הקלט, נצטרך גם להתחבר לכל פלט, לכל פעולה שהמשתמש מבצע בממשק המשתמש שהנציג יצר.
כדי לעשות את זה, אנחנו משתמשים בתהליך outboundEvents שהמעבד חושף, ואוספים את הנתונים האלה.
אז זו ההזדמנות שלכם להוסיף כל לוגיקה שתרצו, למשל, כשהמשתמש לוחץ על פעולה או מבצע משהו בממשק המשתמש.
אז כאן אנחנו שולחים את כל הפעולות בחזרה לנציג כדי שיחליט מה לעשות, אבל בשלב הזה אתם יכולים גם להחליט אם לקרוא לקצה העורפי שלכם, או לעבור למסך אחר באפליקציה, זה תלוי בכם.
אז כאן אנטון חיבר את זה, ועכשיו כשאנחנו לוחצים על הפעולה לאישור ההזמנה, היא תשלח את הפעולה שביצעתם בחזרה לנציג.
וכאן, אתם יודעים, אתם יכולים להחליט להפעיל פונקציונליות מסוימת בקצה העורפי.
בואו נראה מה אנחנו בעצם שולחים לנציג.
זו הודעת הפעולה של A2UI.
יש לו הקשר, וההקשר מכיל קבוצה של מאפיינים שהנציג ביקש לשלוח כשהפעולה הזו מתבצעת.
אז כשאני לוחץ על הכפתור, המאפיינים הספציפיים האלה נפתרים באותו רגע על סמך הערכים הנוכחיים במודל הנתונים, וזה מה שאנחנו שולחים לנציג.
אוקיי, ועד עכשיו הסוכן שיתף איתנו פעולה, אבל אנחנו מדברים פה על AI, ו-AI יכול לטעות.
אז מה בואו נראה מה קורה אם הסוכן מחזיר אתם יודעים רע אתם יודעים עושה טעות בממשק המשתמש שלו שהוא מתאר לנו בחזרה.
בהנחיה האחרונה הזו, אנחנו בעצם מבקשים מהסוכן לעשות טעות רק כדי שנוכל להדגים לכם את זה.
בואו נראה מה יקרה.
כן.
כמו שאפשר לראות, אחד מהרכיבים מסומן בשגיאה.
ועכשיו היא תוקנה.
אז זו לולאת התיקון העצמי של הסוכן.
לפני שכל רכיב מגיע לממשק המשתמש, אנחנו מאמתים את ההודעה הזו מול הסכימה של הקטלוג.
אם הרכיב לא תואם לסכימה, הוא מסומן כשגיאה.
המנוע לעיבוד גרפי של הטמעת הקטלוג הבסיסי מציג את השגיאה הזו רק ברכיב הספציפי הזה, בזמן ששאר ממשק המשתמש עדיין מוצג.
מאחורי הקלעים, כלי העיבוד שולח הודעת שגיאה לסוכן כדי להודיע לו מה השתבש.
אז במקרה הזה, שלחנו הודעה שבה נאמר שסוג הרכיב "סוג לא ידוע שלא קיים" לא נמצא בקטלוג.
אחר כך, הנציג שלח תשובה נוספת שעדכנה את ממשק המשתמש הקיים, והממשק תוקן.
כן.
בהטמעה הנוכחית של הקטלוג הבסיסי של Material, אנחנו מציגים את השגיאות מיד, אבל ברכיב בהתאמה אישית, אתם יכולים להחליט להמשיך להציג אינדיקטור טעינה של רכיב אם יש שגיאה בזמן ההמתנה שהנציג יתקן אותה.
כך יכול להיות שהשגיאה תוסתר לחלוטין מהמשתמש.
ואם כבר מדברים על קטלוגים ורכיבים בהתאמה אישית, עד עכשיו השתמשנו רק בקטלוג הבסיסי כמו שהוא מגיע, אבל כמובן שאפשר להתאים אותו לצרכים שלכם.
אז אתם יכולים לראות שכשאנחנו משתמשים בפונקציית המפעל כדי ליצור את הקטלוג, הם ערכי ברירת מחדל לכל הרכיבים השונים.
כך שאפשר לחבר בקלות רכיבים משלכם אם רוצים.
לדוגמה, אם אנחנו רוצים להוסיף מערכת עיצוב משלנו באפליקציה וכפתור משלה, אנחנו יכולים להוסיף אותם בקלות.
אז אנחנו פשוט משנים את הפרמטר הזה כשאנחנו יוצרים מופע של הקטלוג, וניצור את הגדרת הרכיב הזו.
אז אם ניצור מחלקה חדשה כדי להטמיע את זה, אפשר לראות שיש כאן ממשק, הקטלוג הבסיסי של A2UI של כפתור הסוג.
אז אם נטמיע את זה, נוכל לחבר רכיב משלנו.
כן.
מכיוון שאנחנו משנים רכיב בקטלוג הבסיסי, אנחנו צריכים להטמיע את הממשק הזה של לחצן הקטלוג הבסיסי של A2UI.
לכן, בממשק הזה כבר יש מידע על הרכיב, כמו השם, התיאור וסכימת המאפיינים.
אז כל מה שצריך לעשות כדי להטמיע גרסה משלנו הוא להטמיע את הפונקציה הקומפוזבילית הזו.
היא מקבלת כמה פרמטרים.
הפרמטרים האלה הם ערכים שמתקבלים מהנציג אחרי אימות.
אז אלה דברים כמו מזהה הצאצא, שמפנה לרכיב שיהיה התוכן של הכפתור.
יש לו גם וריאנט שמתאר את סוג הכפתור, הגדרת הפעולה ועוד כמה פרמטרים.
בהטמעה של הכפתור בסביבת הייצור, נצטרך להשתמש בכל הפרמטרים האלה, אבל למטרות ההדגמה שלנו, אנחנו צריכים רק את הצאצא ואת הפעולה.
בואו נטמיע את הרכיב הקומפוזבילי הזה.
אוקיי, אז בפונקציה הקומפוזבילית הזו, אנחנו הולכים להפיק סוג משלנו של כפתור לייק מותאם אישית.
כמו שאפשר לראות, אנחנו יכולים להשתמש בפרמטרים שניתנו ואומתו על ידי A2UI.
לדוגמה, בפונקציה onClick, אנחנו יכולים לשלוח את הפעולה הזו.
אנחנו יכולים להשתמש בסוג הספרייה הזה DispatchAction ולהעביר לו את הפעולה ש-A2UI הגדיר.
אז אנחנו משתמשים בכפתור משלנו, אבל אנחנו עדיין רוצים להציג את תוכן הצאצא, התווית, או כל מה שהסוכן החליט להשתמש בו.
כדי לעשות את זה, אנחנו משתמשים בפונקציה observeA2uiComponentState ומעבירים לה את ה-childId כדי לקבל נקודת אחיזה לתוכן הצאצא שצריך לעבד.
ואז נשתמש בפונקציה הקומפוזבילית A2uiComponent ונעביר לה את המצב של רכיב הצאצא כדי לעבד אותו.
וכאן אפשר לראות שאנחנו מעבדים את תוכן הצאצא רק אם הוא נטען בהצלחה, אבל אפשר גם להוסיף כאן לוגיקה משלכם למקרה שהוא נטען או אם יש מצב שגיאה, אתם יכולים להחליט מה אתם רוצים להציג.
אז אם נחזור לאחת מהנחיות הדוגמה שלנו באפליקציית ההדגמה, ועכשיו נחליף את הכפתור באמצעות, אתם יודעים, כפתור Material רגיל.
זה כל כך עדין, אני מקווה שאתם רואים את זה.
אפשר לראות שגם כאן אנחנו משתמשים בכפתור בהתאמה אישית.
מגניב, אז ככה אפשר, אתם יודעים, להחליף חלק מהסוגים של הספרייה, אבל מה אם רוצים להרחיב את הספרייה עם סוגים מותאמים אישית משלכם?
אנחנו יכולים לעשות את זה בקלות על ידי הוספה לקטלוג עצמו.
אז אנטון ייצור כאן מחלקה חדשה כדי להטמיע רכיב של דירוג כוכבים.
לכן, נוסיף משהו לאפליקציה שלנו שיאפשר לנו לדרג מסעדות בזמן שאנחנו בנסיעה ההיפותטית הזו.
אז בגלל שאנחנו לא מחליפים רכיב קיים, אנחנו משתמשים בממשק הכללי יותר הזה, ממשק הרכיב A2UI, שאפשר לראות שיש לו כמה פרמטרים שאנחנו צריכים לשנות.
אז שני השדות הראשונים הם השם והתיאור.
אז זה מתאר מה הרכיב שלנו ומה הוא עושה.
הם חשובים מאוד כי כשאנחנו מבצעים סריאליזציה של הקטלוג ושולחים אותו לקצה העורפי כדי להגיד לו במה הוא יכול להשתמש כדי ליצור ממשק משתמש, זה מה שהסוכן ישתמש בו כדי להחליט אם להשתמש ברכיב הזה כשהוא מגיב.
לכן, צריך לתאר את הפעולה בפירוט ולומר ל-AI שאתם יודעים בדיוק מה היא עושה.
המאפיין הבא שצריך לשנות הוא המאפיין properties, שבעצם מגדיר את הנתונים שהרכיב הזה מטפל בהם.
אנחנו יוצרים סרגל דירוג כוכבים כדי שתוכלו לדרג מסעדה, ויש שני מאפיינים שאנחנו צריכים.
הוא יכלול את הערך, כמו מספר הכוכבים שהמשתמש בחר, שיהיה מאפיין שניתן לשינוי שהמשתמש יכול לקיים איתו אינטראקציה ולשנות אותו, ואת מספר הכוכבים המקסימלי.
אז כאן אנחנו יכולים לראות שאנטון משתמש במאפיין הדינמי הזה מסוג מספר בשביל הערך.
אז זה כמו, אתם יודעים, מספר הכוכבים שנבחרו בפועל.
גם לזה יש תיאור משלו שמסביר לסוכן מה הערך הזה עושה.
ויש לנו מאפיין מסוג סטטי, אז אנחנו פשוט משתמשים במאפיין A2UI הזה מסוג מספר, כדי להגדיר את המספר המקסימלי של כוכבים שהרכיב הזה יכול להציג.
אחרי שנגדיר את שני המאפיינים האלה, נצטרך רק להחזיר אותם.
אז כדי לשנות את המאפיין properties, נחזיר רשימה של המאפיינים.
ככה אתם אומרים לסוכן שזה מה שהרכיב הזה עושה.
כן.
כמו שניק ציין, הגדרות המאפיינים האלה נשלחות כחלק מהסכימה של הקטלוג כדי שהסוכן יידע איך להשתמש ברכיב הזה.
אבל במאפיין הזה, אותן הגדרות מאפיינים משמשות גם לחילוץ ערכים מוקלדים בזמן הריצה.
כך יש מקור קובע אחד.
בואו נראה מה צריך לעשות כדי להטמיע את זה.
אז קודם כול, כדי לגשת למאפיין max, למשל, מכיוון שהמאפיין max הוא מאפיין סטטי, זה אומר שהנציג שולח את הערך שלו בתוך השורה באובייקט המאפיינים.
כדי לגשת אליו, אנחנו צריכים לקבל אותו מאובייקט המאפיינים על ידי הפניה אליו באמצעות אובייקט המאפיין המתאים בתור המפתח.
מכיוון שהמאפיין הזה מוגדר כמספר, אנחנו צריכים גם להמיר אותו ל-int ולהגדיר את ברירת המחדל.
הדבר הבא הוא מאפיין הערך.
מאפיין הערך הוא מאפיין דינמי, כלומר הוא מפנה למשהו במודל הנתונים.
לכן, כדי לגשת אליו, אנחנו צריכים להשתמש ב-API properties.bind ולהעביר את אובייקט הנכס כמפתח כפרמטר.
אז מה שזה עושה, זה מחזיר את הערך הנוכחי שמאוחסן במודל הנתונים של המאפיין הזה, וגם נרשם לעדכונים עתידיים.
לכן, בכל פעם שמודל הנתונים מתעדכן, הרכיב הזה יורכב מחדש באופן אוטומטי.
אז באופן דומה, גם המרנו ל-int והגדרנו ברירת מחדל.
עוד דבר אחד שצריך לעשות הוא להגדיר קישור דו-כיווני כדי שנוכל לשנות את הדירוג.
כדי לעשות את זה, אנחנו משתמשים ב-API של bindUpdater.
אנחנו מעבירים את אותו אובייקט מאפיין שמגדיר את המאפיין, וכתוצאה מכך אנחנו מקבלים את bindUpdater שמחזיר למבדה שמאפשרת לנו לשנות ערך של המאפיין הזה במודל הנתונים.
עכשיו אנחנו מוכנים להוציא את רכיב דירוג הכוכבים.
אוקיי.
אחרי שהגדרנו רכיב מותאם אישית משלנו, אנחנו צריכים להוסיף אותו לקטלוג שבו אנחנו משתמשים.
אז קודם השתמשנו בקטלוג הבסיסי כמו שהוא.
עכשיו אנחנו צריכים ליצור קטלוג חדש, שיכלול את כל הקטלוג הבסיסי ויוסיף את הרכיב המותאם אישית שלנו.
אז קטלוג מוגדר משלושה דברים.
הוא מורכב ממזהה, קבוצה של רכיבים וקבוצה של פונקציות.
בשביל המזהה, נשתמש ב-URI כדי להגדיר קטלוג שמזוהה באופן ייחודי, וגם את הגרסה שאנחנו עובדים איתה.
ואז, בשביל הרכיבים, ניקח את כל הרכיבים מהקטלוג הבסיסי, ונשתמש באופרטור ההוספה כדי להוסיף את הרכיב המותאם אישית שיצרנו.
ובשביל הפונקציות, נשתמש בפונקציות מהקטלוג הבסיסי.
וכך, הרחבנו את הקטלוג הבסיסי והוספנו את סוג הרכיב החדש בהתאמה אישית.
כמו שאתם רואים, די קל ליצור קטלוג בהתאמה אישית.
לכן, הנציג לומד איך להשתמש בקטלוג מההוראות במערכת שאנחנו שולחים לו.
אז עכשיו, אחרי שיצרנו קטלוג בהתאמה אישית, בואו נעדכן גם את הוראות המערכת.
לכן, נעביר את אובייקט הקטלוג לפונקציה שיוצרת את הוראות המערכת.
אז הנה זה.
עד עכשיו שלחנו הגדרה סטטית של הסכימה לקטלוג הבסיסי, כי השתמשנו בקטלוג הרגיל הזה.
עכשיו, כשיש לנו קטלוג מותאם אישית, אנחנו יכולים ליצור את הסכימה באופן דינמי באמצעות הפונקציה toJsonSchemaString.
כך ייווצר ייצוג JSON מלא של הקטלוג הזה, כולל רכיב דירוג הכוכבים שהוספנו עכשיו.
ההוראות למערכת כוללות גם כמה דוגמאות לתשובות של הנציג.
לכן, אנחנו משנים גם את מזהה הקטלוג כאן, כדי שהדוגמאות יתייחסו לקטלוג הנכון.
אוקיי.
כך הרחבנו את הקטלוג הבסיסי והוספנו סוגים מותאמים אישית משלנו.
אז אם נפרוס מחדש וננסה את אחת מההנחיות האלה, נקבל ממשק משתמש לדירוג.
ואז הנציג עונה.
וכאן, אתם יכולים לראות שרכיב הכוכב הזה הוא רכיב חדש לגמרי שהסוכן לא ראה עד שהטמענו אותו בזמן אמת.
זה לא חלק מהקטלוג הבסיסי, פשוט יצרנו את זה.
כשלוחצים על הכוכב, פונקציית הלמבדה bindUpdater נקראת והערך מתעדכן במודל הנתונים.
ואם נשלח את הדירוגים, נוכל לבדוק את הפעולה, וההקשר של הפעולה יכלול את הערכים שהגדרנו עכשיו.
אוקיי, עכשיו נחזור לשקפים.
אז עברנו על הרבה דברים די מהר, אבל אני מקווה שהראינו איך אפשר לעבור בקלות מסוכנים שמגיבים כגוש טקסט לסוכנים שיוצרים ממשקי משתמש מקוריים אמיתיים.
אנחנו מקווים שהצלחנו להראות לכם שהשילוב הוא די פשוט.
יש שלושה רכיבים עיקריים שצריך לעשות: ליצור קטלוג של רכיבים, להטמיע לוגיקה של ניתוח ואז להציג אותו בממשק המשתמש.
ואני מקווה שהראינו שהספרייה עושה הרבה בשבילכם כבר מההתחלה, דברים כמו עיבוד הדרגתי או אימות הסכימה, לולאות התיקון העצמי או הטיפול בשגיאות, היא עושה הרבה.
ואני מקווה שהצלחנו להראות לכם היום שקל מאוד להתאים אותו אישית.
הראינו איך הקטלוג הבסיסי כבר מגיב להתאמת העיצוב של Google Material Design אם אתם משתמשים ב-Material, אבל ממש פשוט להרחיב או להחליף רכיבים בתוך הקטלוג.
אבל אני חושב שהכוח האמיתי טמון ביצירת קטלוג משלכם.
אם אתם רוצים שליטה מלאה על ממשק המשתמש שהנציג יכול להשתמש בו, אתם יכולים ליצור רכיבים בהתאמה אישית מלאה.
אם רוצים לתת לו, אתם יודעים, חופש פעולה מוחלט, אפשר לתת לו רכיבים בסיסיים מאוד, דברים כמו תוויות טקסט וכפתורים, ולהגיד, "תיצור את ממשק המשתמש".
אם אתם רוצים יותר שליטה, אתם יכולים ליצור רכיבים מורכבים יותר ולהגיד: "אלה הדברים היחידים שאפשר להשתמש בהם".
כך אתם יכולים לשלוט במה שהנציג יכול לעשות ולהציג למשתמשים שלכם.
אז זו ספריית הרינדור החדשה של A2UI וספריית Jetpack שהשקנו לאחרונה.
implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")היא נמצאת כרגע בגרסת אלפא, וגרסת אלפא 02 יצאה ממש לאחרונה.
כדאי ללחוץ על הקישורים האלה כדי לבדוק את זה.
יש לנו מסמכי תיעוד ב-developer.android.com, וגם כמה דוגמאות.
יש לנו דוגמה בסיסית שמציגה את הקטלוג הבסיסי, וגם דוגמה מורכבת יותר, ואת האפליקציה לדוגמה JetPacker שיצרנו עכשיו, שתראו שוב בהרצאה המרכזית של ADS בהמשך היום, שמדגימה איך אפשר ליצור את הרכיבים המורכבים יותר האלה כדי לשלוט במה שהמשתמש רואה.
כמו שאמרתי, אנחנו כרגע בשלב אלפא, אז זה זמן מצוין לבדוק את זה ולתת לנו משוב והגשת בקשה להוספת תכונה, ולספר לנו, אתם יודעים, איך הייתם רוצים להשתמש בזה, או "הייתי רוצה להשתמש בזה אם זה היה יכול לעשות X או Y".
אנחנו ממליצים לכם לעיין בספרייה ולשלוח לנו משוב.
וזה מה שקיבלנו.
אני כבר לא יכול לחכות לראות מה אתם והסוכנים שלכם תיצרו בעזרתו.
תודה רבה.
תודה.