תמיכה בגדלים שונים של מסכים מאפשרת גישה לאפליקציה ממגוון רחב של מכשירים וממספר גדול יותר של משתמשים.
כדי לתמוך בכמה שיותר גדלים של מסכים – בין אם מדובר במסכים של מכשירים שונים או בחלונות שונים של אפליקציות במצב ריבוי חלונות – צריך לעצב את פריסות האפליקציה כך שיהיו רספונסיביות וניתנות להתאמה. פריסות רספונסיביות או דינמיות מספקות חוויית משתמש אופטימלית בלי קשר לגודל המסך, ומאפשרות לאפליקציה להתאים לטלפונים, לטאבלטים, למכשירים מתקפלים, למכשירי ChromeOS, למצבי תצוגה לאורך ולרוחב ולתצורות תצוגה שניתן לשנות את הגודל שלהן, כמו מצב מסך מפוצל וחלונות במחשב.
פריסות רספונסיביות/אדפטיביות משתנות בהתאם לשטח הזמין להצגה. השינויים יכולים להיות קטנים, כמו התאמות בפריסה כדי למלא את השטח (עיצוב רספונסיבי), או גדולים, כמו החלפה מלאה של פריסה אחת בפריסה אחרת כדי שהאפליקציה תתאים בצורה הטובה ביותר לגדלים שונים של מסכים (עיצוב אדפטיבי).
ערכת הכלים Jetpack Compose היא ערכת כלים הצהרתית לממשק משתמש, ולכן היא אידיאלית לעיצוב ולהטמעה של פריסות שמשתנות באופן דינמי כדי להציג תוכן בצורה שונה בגדלים שונים של מסכים.
כישורים ב-Android
הצגה ב-GitHubהתאמת האפליקציה
adaptive מיומנות Android כדי לעדכן את ממשק המשתמש של האפליקציה כך שיתאים למכשירי Android שונים ולמסכים שונים.android skills add adaptiveהגדרה מפורשת של שינויים גדולים בפריסה של רכיבים קומפוזביליים ברמת התוכן
רכיבי composable ברמת האפליקציה וברמת התוכן תופסים את כל שטח התצוגה שזמין לאפליקציה. בסוגים האלה של רכיבי composable, יכול להיות שכדאי לשנות את הפריסה הכוללת של האפליקציה במסכים גדולים.
אל תשתמשו בערכים של חומרה פיזית כדי לקבל החלטות לגבי פריסת הרכיבים. יכול להיות שתתפתו לקבל החלטות על סמך ערך קבוע ומוחשי (האם המכשיר הוא טאבלט? האם למסך הפיזי יש יחס רוחב-גובה מסוים?), אבל התשובות לשאלות האלה לא בהכרח יעזרו לכם לקבוע את השטח שזמין לממשק המשתמש.
בטאבלטים, יכול להיות שאפליקציה פועלת במצב ריבוי חלונות, כלומר האפליקציה יכולה לפצל את המסך עם אפליקציה אחרת. במצב ממשק מחשב או ב-ChromeOS, יכול להיות שאפליקציה נמצאת בחלון שניתן לשינוי גודל. יכול להיות שיש יותר ממסך פיזי אחד, כמו במכשיר מתקפל. בכל המקרים האלה, גודל המסך הפיזי לא רלוונטי להחלטה איך להציג את התוכן.
במקום זאת, כדאי לקבל החלטות על סמך החלק בפועל של המסך שהוקצה לאפליקציה, כפי שמתואר במדדי החלון הנוכחיים שסופקו על ידי ספריית WindowManager של Jetpack. דוגמה לשימוש ב-WindowManager באפליקציית Compose מופיעה בדוגמה JetNews.
התאמת הפריסות לשטח המסך הזמין גם מצמצמת את כמות הטיפול המיוחד שנדרש כדי לתמוך בפלטפורמות כמו ChromeOS ובגורמי צורה כמו טאבלטים ומכשירים מתקפלים.
אחרי שקובעים את המדדים של השטח שזמין לאפליקציה, ממירים את הגודל הגולמי למחלקת גודל חלון כמו שמתואר במאמר שימוש במחלקות גודל חלון. נקודות עצירה של גודל החלון נועדו לאזן בין פשטות הלוגיקה של האפליקציה לבין הגמישות שמאפשרת לבצע אופטימיזציה של האפליקציה לרוב גדלי המסכים.
מחלקות גודל החלון מתייחסות לחלון הכולל של האפליקציה, ולכן כדאי להשתמש במחלקות כדי לקבל החלטות לגבי פריסת הרכיבים שמשפיעות על הפריסה הכוללת של האפליקציה. אפשר להעביר את המחלקות של גודל החלון כמצב, או לבצע לוגיקה נוספת כדי ליצור מצב נגזר להעברה למרכיבים הניתנים להרכבה מקוננים.
@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, /* ... */ ) }
גישה מבוססת-שכבות מגבילה את הלוגיקה של גודל התצוגה למיקום יחיד, במקום לפזר אותה באפליקציה במקומות רבים שצריך לשמור על סנכרון ביניהם. מיקום יחיד יוצר מצב, שאפשר להעביר אותו במפורש לרכיבי Composable אחרים, בדיוק כמו כל מצב אחר של האפליקציה. העברה מפורשת של מצב מפשטת את הקומפוזיציות האישיות, כי הקומפוזיציות מקבלות את מחלקת גודל החלון או את ההגדרה שצוינה יחד עם נתונים אחרים.
אפשר לעשות שימוש חוזר ברכיבים קומפוזביליים גמישים ומקוננים
רכיבים קומפוזביליים הם שימושיים יותר כשאפשר למקם אותם במגוון רחב של מקומות. אם רכיב קומפוזבילי צריך להיות ממוקם במיקום ספציפי בגודל ספציפי, סביר להניח שלא ניתן יהיה להשתמש בו מחדש בהקשרים אחרים. זה גם אומר שרכיבי composable נפרדים שאפשר להשתמש בהם שוב לא צריכים להיות תלויים באופן מרומז במידע על גודל התצוגה הגלובלי.
דמיינו רכיב composable מקונן שמטמיע פריסת רשימה עם פרטים, שיכולה להציג חלונית אחת או שתי חלוניות זו לצד זו:
ההחלטה לגבי רשימה ופירוט צריכה להיות חלק מהפריסה הכוללת של האפליקציה, כך שההחלטה מועברת מרכיב הניתן להרכבה ברמת התוכן:
@Composable fun AdaptivePane( showOnePane: Boolean, /* ... */ ) { if (showOnePane) { OnePane(/* ... */) } else { TwoPane(/* ... */) } }
מה אם רוצים שרכיב שאפשר להרכיב ישנה את הפריסה שלו באופן עצמאי על סמך שטח המסך שזמין, למשל, כרטיס שבו מוצגים פרטים נוספים אם יש מקום? אתם רוצים לבצע לוגיקה מסוימת על סמך גודל התצוגה הזמין, אבל איזה גודל בדיוק?
לא מומלץ לנסות להשתמש בגודל המסך בפועל של המכשיר. הנתון הזה לא יהיה מדויק עבור סוגים שונים של מסכים, וגם לא יהיה מדויק אם האפליקציה לא מוצגת במסך מלא.
מכיוון שהרכיב הניתן להרכבה הוא לא רכיב שאפשר להרכיב ברמת התוכן, אל תשתמשו במדדים של החלון הנוכחי באופן ישיר.
אם הרכיב ממוקם עם ריווח פנימי (padding) (למשל עם שוליים פנימיים), או אם האפליקציה כוללת רכיבים כמו סרגלי ניווט או סרגלי אפליקציות, כמות שטח התצוגה שזמין לקומפוזבילי עשויה להיות שונה באופן משמעותי מהשטח הכולל שזמין לאפליקציה.
משתמשים ברוחב שמוקצה בפועל לקומפוזבילי כדי לעבד את עצמו. יש שתי אפשרויות לקבל את הרוחב הזה:
אם רוצים לשנות איפה או איך התוכן מוצג, אפשר להשתמש באוסף של משנים או בפריסה בהתאמה אישית כדי שהפריסה תהיה רספונסיבית. לדוגמה, אפשר למלא את כל השטח הפנוי בילדים, או להציג את הילדים בכמה עמודות אם יש מספיק מקום.
אם רוצים לשנות את מה שמוצג, אפשר להשתמש ב-
BoxWithConstraintsכחלופה יעילה יותר. BoxWithConstraintsמספק אילוצים למדידה שבהם אפשר להשתמש כדי לקרוא לפונקציות שונות של קומפוזיציה בהתאם לשטח התצוגה הזמין. עם זאת, יש לכך מחיר מסוים, כיBoxWithConstraintsדוחה את ההרכבה עד לשלב הפריסה, כשהמגבלות האלה ידועות, ולכן נדרשת יותר עבודה במהלך הפריסה.
@Composable fun Card(/* ... */) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(/* ... */) Title(/* ... */) } } else { Row { Column { Title(/* ... */) Description(/* ... */) } Image(/* ... */) } } } }
הצגת כל הנתונים בגדלים שונים של מסכים
כשמטמיעים קומפוזיציה שמנצלת את שטח התצוגה הנוסף, יכול להיות שיהיה לכם פיתוי להיות יעילים ולטעון נתונים כתוצאה לוואי של גודל התצוגה הנוכחי.
עם זאת, פעולה כזו מנוגדת לעיקרון של זרימת נתונים חד-כיוונית, שבה אפשר להעביר נתונים ולספק אותם לרכיבים הניתנים להרכבה כדי להציג אותם בצורה מתאימה. צריך לספק מספיק נתונים לרכיב הניתן להרכבה, כך שתמיד יהיה לו מספיק תוכן לכל גודל תצוגה, גם אם חלק מהתוכן לא תמיד יהיה בשימוש.
@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 היא אפליקציה שמשתמשת בפריסות מותאמות כדי לתמוך בגדלים שונים של מסכים.
סרטונים