תמיכה בגדלים שונים של מסכים מאפשרת גישה לאפליקציה במגוון רחב של מכשירים ולמספר גדול של משתמשים.
כדי לתמוך בכמה שיותר גדלים של מסכים – בין אם מדובר במסכים של מכשירים שונים או בחלונות שונים של אפליקציות במצב ריבוי חלונות – כדאי לעצב את פריסות האפליקציה כך שיהיו רספונסיביות ומותאמות. פריסות רספונסיביות או דינמיות מספקות חוויית משתמש אופטימלית בלי קשר לגודל המסך, ומאפשרות לאפליקציה להתאים לטלפונים, לטאבלטים, למכשירים מתקפלים, למכשירי ChromeOS, לכיוון לאורך ולרוחב ולהגדרות תצוגה שניתן לשנות את הגודל שלהן, כמו מצב מסך מפוצל וממשק מחשב.
פריסות רספונסיביות/מותאמות משתנות בהתאם לשטח התצוגה הזמין. השינויים יכולים להיות קטנים, כמו התאמות פריסה שממלאות את המקום (עיצוב רספונסיבי), או גדולים, כמו החלפה מלאה של פריסה אחת באחרת, כדי שהאפליקציה תתאים בצורה הטובה ביותר לגדלים שונים של מסכים (עיצוב מותאם).
כערכת כלים דקלרטיבית לממשק משתמש, Jetpack Compose היא כלי אידיאלי לעיצוב ולהטמעה של פריסות שמשתנות באופן דינמי כדי להציג תוכן בצורה שונה בגדלים שונים של מסכים.
מיומנויות ב-Android
הצגת הקוד ב-GitHubאיך יוצרים אפליקציה מותאמת
adaptive כדי לעדכן את ממשק המשתמש של האפליקציה, כך שיתאים למכשירים ולמסכים שונים של Android.android skills add adaptiveשינויים גדולים בפריסה של רכיבים קומפוזביליים ברמת התוכן צריכים להיות מפורשים
רכיבי קומפוזבל ברמת האפליקציה וברמת התוכן תופסים את כל שטח התצוגה שזמין לאפליקציה. בסוגים האלה של רכיבי קומפוזבל, יכול להיות שכדאי לשנות את הפריסה הכוללת של האפליקציה במסכים גדולים.
אל תשתמשו בערכים של חומרה פיזית כדי לקבל החלטות לגבי פריסה. יכול להיות שתרצו לקבל החלטות על סמך ערך מוחשי קבוע (האם המכשיר הוא טאבלט? האם למסך הפיזי יש יחס גובה-רוחב מסוים?), אבל התשובות לשאלות האלה לא בהכרח יעזרו לכם לקבוע את השטח שזמין לממשק המשתמש.
בטאבלטים, יכול להיות שהאפליקציה פועלת במצב ריבוי חלונות, כלומר היא חולקת את המסך עם אפליקציה אחרת. במצב ממשק מחשב או ב-ChromeOS, יכול להיות שהאפליקציה פועלת בחלון שאפשר לשנות את הגודל שלו. יכול להיות שיהיו אפילו כמה מסכים פיזיים, כמו במכשיר מתקפל. בכל המקרים האלה, הגודל הפיזי של המסך לא רלוונטי להחלטה איך להציג את התוכן.
במקום זאת, קבלו החלטות על סמך החלק בפועל של המסך שמוקצה לאפליקציה, כפי שמתואר במדדי החלון הנוכחיים שסופקו על ידי ספריית WindowManager של Jetpack. דוגמה לשימוש ב-WindowManager באפליקציית Compose מופיעה בדוגמה של JetNews.
אם הפריסות שלכם יתאימו לשטח התצוגה הזמין, תצטרכו פחות טיפול מיוחד כדי לתמוך בפלטפורמות כמו ChromeOS ובגורמי צורה כמו טאבלטים ומכשירים מתקפלים.
אחרי שקובעים את המדדים של המרחב הזמין לאפליקציה, ממירים את הגודל הגולמי לקטגוריית גודל חלון, כמו שמתואר בקטע שימוש בקטגוריות של גודל חלון. מחלקות של גודל חלון הן נקודות עצירה (breakpoint) שנועדו לאזן בין הפשטות של הלוגיקה של האפליקציה לבין הגמישות שמאפשרת לבצע אופטימיזציה של האפליקציה לרוב הגדלים של המסכים.
הגודל של חלון האפליקציה מתייחס לחלון הכולל של האפליקציה, לכן כדאי להשתמש במחלקות כדי לקבל החלטות לגבי הפריסה שמשפיעות על הפריסה הכוללת של האפליקציה. אפשר להעביר את מחלקות הגודל של החלון כמצב, או לבצע לוגיקה נוספת כדי ליצור מצב נגזר ולהעביר אותו לפונקציות ה-Composable בתצוגת עץ.
@Composable fun MyApp( windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true).windowSizeClass ) { // Decide whether to show the top app bar based on window size class. val showTopAppBar = windowSizeClass.isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND) // MyScreen logic is based on the showTopAppBar boolean flag. MyScreen( showTopAppBar = showTopAppBar, /* ... */ ) }
גישה שכבתית מגבילה את הלוגיקה של גודל התצוגה למיקום אחד, במקום לפזר אותה באפליקציה בהרבה מקומות שצריך לסנכרן. מיקום אחד יוצר מצב, שאפשר להעביר באופן מפורש לרכיבים אחרים שניתנים ליצירה, בדיוק כמו כל מצב אחר של האפליקציה. העברה מפורשת של המצב מפשטת את ה-composables הנפרדים, כי הם מקבלים את סיווג גודל החלון או את ההגדרות שצוינו יחד עם נתונים אחרים.
אפשר לעשות שימוש חוזר ברכיבים קומפוזביליים גמישים בתצוגת עץ
ככל שניתן למקם רכיבים קומפוזביליים במגוון רחב יותר של מקומות, כך אפשר לעשות בהם שימוש חוזר. אם צריך למקם רכיב קומפוזבילי במיקום ספציפי עם גודל ספציפי, סביר להניח שלא יהיה אפשר להשתמש בו שוב בהקשרים אחרים. זה גם אומר שרכיבים נפרדים וניתנים לשימוש חוזר לא צריכים להיות תלויים באופן משתמע במידע גלובלי על גודל המסך.
דמיינו רכיב Composable בתצוגה מפורטת של רשימה, שיכול להציג חלונית אחת או שתי חלוניות זו לצד זו:
ההחלטה לגבי פריסת רשימה ופירוט צריכה להיות חלק מהפריסה הכוללת של האפליקציה, ולכן ההחלטה מועברת מפונקציה קומפוזבילית ברמת התוכן:
@Composable fun AdaptivePane( showOnePane: Boolean, /* ... */ ) { if (showOnePane) { OnePane(/* ... */) } else { TwoPane(/* ... */) } }
מה אם אתם רוצים שרכיב Composable ישנה את הפריסה שלו באופן עצמאי על סמך שטח התצוגה הזמין, למשל כרטיס שמציג פרטים נוספים אם יש מספיק מקום? אתם רוצים להפעיל לוגיקה מסוימת על סמך גודל מסך זמין, אבל איזה גודל בדיוק?
אל תנסו להשתמש בגודל המסך האמיתי של המכשיר. המיקום לא יהיה מדויק בסוגים שונים של מסכים, וגם אם האפליקציה לא מוצגת במסך מלא.
מכיוון שהרכיב ה-composable הוא לא רכיב ברמת התוכן, אל תשתמשו ישירות במדדי החלון הנוכחיים.
אם הרכיב ממוקם עם מרווח פנימי (למשל עם inset), או אם האפליקציה כוללת רכיבים כמו מסילות Navigation או סרגלי האפליקציה, כמות שטח התצוגה שזמין לפונקציה הקומפוזבילית עשויה להיות שונה באופן משמעותי מהשטח הכולל שזמין לאפליקציה.
משתמשים ברוחב שניתן בפועל לרכיב ה-composable כדי לעבד את עצמו. יש שתי דרכים להשיג את הרוחב הזה:
אם רוצים לשנות את המיקום או את האופן שבו התוכן מוצג, משתמשים באוסף של משתני שינוי או בפריסה בהתאמה אישית כדי שהפריסה תהיה רספונסיבית. זה יכול להיות פשוט כמו למלא את כל המקום הפנוי ברכיב צאצא, או לפרוס את הצאצאים בכמה עמודות אם יש מספיק מקום.
אם אתם רוצים לשנות את מה שאתם מציגים, השתמשו ב-
BoxWithConstraintsכחלופה יעילה יותר.BoxWithConstraintsמספק אילוצי מדידה שבהם אפשר להשתמש כדי לקרוא לפונקציות קומפוזביליות שונות על סמך שטח התצוגה הזמין. עם זאת, יש לזה מחיר:BoxWithConstraintsדוחה את הקומפוזיציה עד לשלב הפריסה, שבו האילוצים האלה ידועים, וכתוצאה מכך צריך לבצע יותר עבודה במהלך הפריסה.
@Composable fun Card(/* ... */) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(/* ... */) Title(/* ... */) } } else { Row { Column { Title(/* ... */) Description(/* ... */) } Image(/* ... */) } } } }
הצגת כל הנתונים בגדלים שונים של מסכים
כשמטמיעים רכיב Composable שמנצל את שטח המסך הנוסף, יכול להיות שתתפתו להיות יעילים ולטעון נתונים כתוצאת לוואי של גודל המסך הנוכחי.
עם זאת, פעולה כזו מנוגדת לעיקרון של זרימת נתונים חד-כיוונית, שבה אפשר להעביר נתונים למעלה ולספק אותם לרכיבים הניתנים ליצירה כדי לעבד אותם כראוי. צריך לספק מספיק נתונים לפונקציה הקומפוזבילית כדי שתמיד יהיה לה מספיק תוכן לכל גודל תצוגה, גם אם לא תמיד נעשה שימוש בחלק מהתוכן.
@Composable fun Card( imageUrl: String, title: String, description: String ) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(imageUrl) Title(title) } } else { Row { Column { Title(title) Description(description) } Image(imageUrl) } } } }
בהמשך לדוגמה של Card, שימו לב שהערך של description תמיד מועבר אל Card. למרות שמשתמשים ב-description רק כשהרוחב מאפשר להציג אותו, Card תמיד דורש את description, בלי קשר לרוחב הזמין.
אם תמיד תעבירו מספיק תוכן, הפריסות הדינמיות יהיו פשוטות יותר כי הן יהיו פחות תלויות במצב, ולא יופעלו תופעות לוואי כשעוברים בין גדלים שונים של תצוגה (שיכולים להתרחש בגלל שינוי גודל החלון, שינוי הכיוון או קיפול ופתיחה של המכשיר).
העיקרון הזה גם מאפשר לשמור על המצב של הרכיבים כשמשנים את הפריסה. אם תעבירו למעלה מידע שלא בהכרח משתמשים בו בכל גודלי המסך, תוכלו לשמור על מצב האפליקציה כשהגודל של הפריסה משתנה.
לדוגמה, אפשר להרים דגל בוליאני של showMore כדי שמצב האפליקציה יישמר כששינוי גודל התצוגה גורם לפריסה לעבור בין הסתרה והצגה של תוכן:
@Composable fun Card( imageUrl: String, title: String, description: String ) { var showMore by remember { mutableStateOf(false) } BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(imageUrl) Title(title) } } else { Row { Column { Title(title) Description( description = description, showMore = showMore, onShowMoreToggled = { newValue -> showMore = newValue } ) } Image(imageUrl) } } } }
מידע נוסף
מידע נוסף על פריסות דינמיות ב-Compose זמין במקורות המידע הבאים:
אפליקציות לדוגמה
- CanonicalLayouts הוא מאגר של תבניות עיצוב מוכחות שמספקות חוויית משתמש אופטימלית במסכים גדולים.
- JetNews מראה איך לעצב אפליקציה שמתאימה את ממשק המשתמש שלה כדי לנצל את שטח התצוגה הזמין.
- Reply היא אפליקציה לדוגמה עם עיצוב רספונסיבי שתומכת בניידים, בטאבלטים ובמכשירים מתקפלים.
- Now in Android היא אפליקציה שמשתמשת בפריסות דינמיות כדי לתמוך בגדלים שונים של מסכים.
סרטונים