העברת ממשק המשתמש לפריסות רספונסיביות

אפליקציות ל-Android צריכות לתמוך בסביבה עסקית הולכת וגדלה של גורמים שונים של צורות מכשיר. ממשק המשתמש של האפליקציה צריך להיות רספונסיבי כדי להתאים למגוון רחב של גדלי מסכים, וגם לכיוונים ולמצבים שונים של המכשיר.

ממשק המשתמש הרספונסיבי מבוסס על עקרונות הגמישות וההמשכיות.

גמישות מתייחסת לפריסות שמנצלות בצורה אופטימלית את המרחב הזמין, ומסתגלות כשהמרחב הזמין משתנה. ההתאמות יכולות להתבצע בדרכים רבות: פשוט להגדיל את הגודל של תצוגה מפורטת אחת, לשנות את המיקום של התצוגות המפורטות כך שיהיה קל יותר לגשת אליהן, להציג או להסתיר תצוגות מפורטות נוספות או שילוב של כל הפעולות האלה.

המשכיות מתייחסת לחוויית משתמש חלקה במעבר מגודל חלון אחד לגודל אחר. חוויית המשתמש צריכה להמשיך ללא הפרעה. שינוי בגודל יכול להיות מלווה בהרס וביצירה מחדש של כל היררכיית התצוגה, ולכן חשוב שהמשתמש לא יאבד את המיקום או את הנתונים שלו.

דברים שצריך להימנע מהם

אין להשתמש בערכים פיזיים של חומרה כדי לקבל החלטות לגבי הפריסה. יכול להיות שתתפתתו לקבל החלטות על סמך ערך קבוע, אבל במצבים רבים הערכים האלה לא מועילים לקביעת המרחב שבו ממשק המשתמש יכול לפעול.

יכול להיות שחלונות של אפליקציות ישתנו בגודל כשהן פועלות במצב של חלונות מרובים, 'תמונה בתוך תמונה' או חלונות בפורמט חופשי, כמו ב-ChromeOS. יכול להיות גם יותר ממסך פיזי אחד, למשל במכשיר מתקפל או במכשיר עם כמה מסכים. בכל המקרים האלה, גודל המסך הפיזי לא רלוונטי להחלטה איך להציג את התוכן.

כמה מכשירים שמוצגים בהם חלונות של אפליקציות בגדלים שונים.
איור 1. גודל החלון יכול להיות שונה מהגודל הפיזי של המכשיר או של המסך.

מאותה סיבה, מומלץ להימנע מהשבתה של האפליקציה לכיוון או ליחס גובה-רוחב ספציפיים. המכשיר עצמו עשוי להיות בכיוון מסוים, אבל האפליקציה עשויה להיות בכיוון אחר על סמך גודל החלון שלה בלבד. לדוגמה, בטאבלט בפריסה לרוחב במצב ריבוי חלונות, אפליקציה יכולה להיות בפריסה לאורך כי היא גבוהה יותר מאשר רחבה.

בנוסף, אין לנסות לקבוע אם המכשיר הוא טלפון או טאבלט. ההגדרה של טאבלט היא יחסית סובייקטיבית: האם היא מבוססת על גודל מסוים, או על יחס גובה-רוחב מסוים, או על שילוב של גודל ויחס גובה-רוחב? ככל שיופיעו פורמטים חדשים, ההנחות האלה עשויות להשתנות וההבחנה תאבד מחשיבותה.

במקום לנסות אחת מהשיטות הקודמות, כדאי להשתמש בנקודות עצירה ובקטגוריות של גודל חלון.

נקודות עצירה וקטגוריות של גדלי חלונות

החלק בפועל של המסך שהוקצה לאפליקציה הוא החלון של האפליקציה. הוא יכול לתפוס את המסך כולו או חלק ממנו, לכן כדאי להשתמש בגודל החלון כשמקבלים החלטות ברמה גבוהה לגבי הפריסה של האפליקציה.

כשאתם מתכננים למספר גורמי צורה, כדאי למצוא ערכי סף שבהם ההחלטות ברמה גבוהה מתפצלות לכיוונים שונים. לשם כך, מרתף הפריסה הרספונסיבית של Material Design מספק נקודות עצירה לרוחב ולגובה, שמאפשרות למפות גדלים גולמיים לקבוצות נפרדות וסטנדרטיות שנקראות כיתות של גדלי חלונות. בגלל השימוש הנפוץ בגלילה אנכית, רוב האפליקציות מתמקדות בעיקר בקטגוריות הרוחב, כך שאפשר לבצע אופטימיזציה של רוב האפליקציות לכל גדלי המסכים על ידי טיפול רק בכמה נקודות עצירה. (מידע נוסף על סיווג חלונות לפי גודל זמין במאמר שימוש בסיווג חלונות לפי גודל).

רכיבים קבועים בממשק המשתמש

ההנחיות לגבי פריסה של Material Design מגדירות אזורים לסרגלי האפליקציות, לניווט ולתוכן. בדרך כלל, שני הרכיבים הראשונים הם רכיבי ממשק משתמש קבועים שנמצאים בשורש (או בסמוך מאוד לשורש) של היררכיית התצוגה. חשוב לזכור שהמונח 'קבוע' לא בהכרח אומר שהתצוגה תמיד גלויה, אלא שהיא נשארת במקום בזמן שתוכן אחר עשוי לזוז או להשתנות. לדוגמה, רכיב ניווט יכול להיות בחלונית הזזה שנמצאת מחוץ למסך, אבל החלונית תמיד קיימת.

רכיבים קבועים יכולים להיות רספונסיביים, ובדרך כלל הם תופסים את כל רוחב החלון או את כל גובה החלון. לכן, מומלץ להשתמש בקטגוריות גודל כדי לקבוע איפה למקם אותם. כך אפשר להגדיר את המקום שנותר לתוכן. בקטע הקוד הבא, הפעילות משתמשת בסרגל תחתון במסכים קומפקטיים ובסרגל אפליקציות בחלק העליון במסכים גדולים יותר. בפריסות שעומדות בדרישות נעשה שימוש בנקודות עצירה לפי רוחב, כפי שמתואר למעלה.

<!-- res/layout/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- content view(s) -->

    <com.google.android.material.bottomappbar.BottomAppBar
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        ... />
</androidx.constraintlayout.widget.ConstraintLayout>


<!-- res/layout-w600dp/main_activity.xml -->
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        ... />

    <!-- content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>

תוכן

אחרי שממקמים את רכיבי ממשק המשתמש הקבועים, אפשר להשתמש במרחב שנותר לתוכן, למשל באמצעות NavHostFragment עם תרשים הניווט של האפליקציה. שיקולים נוספים מפורטים במאמר ניווט בממשקי משתמש עם תגובה דינמית.

לוודא שכל הנתונים זמינים בגדלים שונים

רוב מסגרות האפליקציות כיום משתמשות במודל נתונים נפרד מהרכיבים של Android שתורמים לממשק המשתמש (פעילויות, קטעי קוד ורכיבי תצוגה). ב-Jetpack, התפקיד הזה מתבצע בדרך כלל על ידי ViewModels, שיש להם את היתרון הנוסף של הישרדות בשינויי תצורה (למידע נוסף, ראו סקירה כללית על ViewModels).

כשמטמיעים פריסה שמתאימה לגדלים שונים, יכול להיות שיהיה לכם רצון להשתמש במודל נתונים אחר על סמך הגודל הנוכחי. עם זאת, הדבר סותר את העיקרון של זרימת נתונים חד-כיוונית. הנתונים צריכים לזרום למטה אל התצוגות, ואירועים כמו אינטראקציות של משתמשים צריכים לזרום למעלה. יצירת תלות בכיוון השני, שבו מודל הנתונים תלוי בהגדרה של שכבת ממשק המשתמש, מסבכת את התהליך באופן משמעותי. כשהאפליקציה משתנה בגודל, צריך להביא בחשבון את ההמרה ממודל נתונים אחד למשנהו.

במקום זאת, כדאי לאפשר למודל הנתונים להכיל את סיווג הגודל הגדול ביותר, ואז תוכלו להציג, להסתיר או לשנות את המיקום של תוכן בממשק המשתמש באופן סלקטיבי כדי להתאים אותו לסיווג הגודל הנוכחי. ריכזנו כאן כמה שיטות שתוכלו להשתמש בהן כדי לקבוע איך הפריסה שלכם תתנהג במעבר בין קטגוריות הגדלים.

הרחבת התוכן

פריסות קנוניות: פיד

המרחב המורחב יכול להיות הזדמנות פשוט להגדיל את התמונות ולעצב מחדש את התוכן כך שיהיה נגיש יותר.

להגדיל את האוספים באפליקציות רבות מוצג אוסף של פריטים בקונטיינר שאפשר לגלול בו, כמו RecyclerView או ScrollView. אם מגדילים את הקונטיינר באופן אוטומטי, אפשר להציג יותר תוכן. עם זאת, חשוב לוודא שהתוכן בתוך הקונטיינר לא יימתח או יעוות יותר מדי. לדוגמה, אם הרוחב לא קומפקטי, כדאי להשתמש במנהל פריסה אחר כמו GridLayoutManager,‏ StaggeredGridLayoutManager או FlexboxLayout ב-RecyclerView.

מכשיר מקופל ומקופל פתוח, שבו מוצג איך מנהלי פריסה שונים פורסים את האפליקציה באופן שונה בהתאם לקטגוריית הרוחב.
איור 2. מנהלי פריסה שונים לקטגוריות שונות של גדלי חלונות.

אפשר גם להשתמש בפריטים בודדים בגדלים או בצורות שונים כדי להציג יותר תוכן ולזהות בקלות רבה יותר את גבולות הפריטים.

הדגשת הרכיב הראשי באתר אם לפריסת המודעות יש מוקד עניין מסוים, כמו תמונה או סרטון, כדאי להרחיב אותו כשחלון האפליקציה מתרחב כדי לשמור על תשומת הלב של המשתמש. אפשר לסדר מחדש רכיבים תומכים אחרים סביב התצוגה הראשית או מתחתיה.

יש הרבה דרכים ליצור פריסה כזו, אבל ConstraintLayout מתאים במיוחד למטרה הזו כי הוא מספק הרבה דרכים להגביל את הגודל של תצוגת הצאצא – כולל לפי אחוזים או אכיפת יחס גובה-רוחב – ולמקם את הצאצאים שלו ביחס אליו או לצאצאים אחרים. מידע נוסף על כל היכולות האלה זמין במאמר יצירת ממשק משתמש רספונסיבי באמצעות ConstraintLayout.

הצגת תוכן שניתן לכווץ כברירת מחדל כשיש מקום פנוי, כדאי להציג תוכן שבלעדיו הגישה אליו תהיה אפשרית רק דרך אינטראקציה נוספת של המשתמש, כמו הקשה, גלילה או תנועות. לדוגמה, תוכן שמופיע בממשק עם כרטיסיות במצב דחוס יכול להיות מסודר מחדש בעמודות או ברשימת כשיש יותר מקום.

להרחיב את השוליים. אם המרחב גדול כל כך שלא ניתן למצוא התאמה מושכת גם אחרי שמשתמשים בכל התוכן, אפשר להרחיב את השוליים של הפריסה כך שהתוכן יישאר במרכז, והתצוגות הנפרדות יהיו בגדלים טבעיים עם מרווחים טבעיים ביניהן.

לחלופין, רכיב במסך מלא יכול להפוך לממשק משתמש של תיבת דו-שיח צפה. האפשרות הזו מתאימה במיוחד כשהרכיב הזה דורש התמקדות בלעדית כדי לבצע משימה מיידית של המשתמש, כמו כתיבת אימייל או יצירת אירוע ביומן.

טלפון רגיל שבו מוצגת תיבת דו-שיח במסך מלא, וטלפון מתקפל פתוח שבו מוצגת אותה תיבת דו-שיח כחלון צף.
איור 3. תיבת דו-שיח במסך מלא הופכת לתיבת דו-שיח רגילה ברוחב בינוני ורחב.

הוספת תוכן

פריסות קנוניות: חלונית תמיכה, תצוגת רשימה מפורטת

שימוש בחלונית תמיכה בחלונית התומכת מוצג תוכן נוסף או פעולות לפי הקשר שקשורות לתוכן הראשי, כמו תגובות במסמך או פריטים בפלייליסט. בדרך כלל, המודעות האלה משתמשות בשליש התחתון של המסך לגובה מורחב, או בשליש האחרון לגובה מורחב.

חשוב לחשוב איפה למקם את התוכן הזה כשאין מספיק מקום להצגת החלונית. הנה כמה חלופות שכדאי לבדוק:

  • חלונית ההזזה הצדדית בקצה העורפי באמצעות DrawerLayout
  • חלונית ההזזה התחתונה באמצעות BottomSheetBehavior
  • תפריט או חלון קופץ שאפשר לגשת אליהם בהקשה על סמל תפריט
איור 4. דרכים חלופיות להצגת תוכן נוסף בחלונית תומכת.

יוצרים פריסת שתי חלוניות. במסכים גדולים מוצגת שילוב של תכונות שבדרך כלל מופיעות בנפרד במסכים קטנים יותר. דפוס אינטראקציה נפוץ באפליקציות רבות הוא הצגת רשימה של פריטים, כמו אנשי קשר או תוצאות חיפוש, ומעבר לפרטי הפריט כשבוחרים אותו. במקום להגדיל את הרשימה במסכים גדולים יותר, אפשר להשתמש בתצוגת הפרטים של הרשימה כדי להציג את שתי התכונות זו לצד זו בפריסה של שתי חלוניות. בניגוד לחלונית תמיכה, חלונית הפרטים בתצוגת רשימה עם פרטים היא רכיב עצמאי שאפשר להציג בנפרד במסכים קטנים יותר.

כדי להטמיע תצוגת רשימה עם פרטים, משתמשים בווידג'ט הייעודי SlidingPaneLayout. הווידג'ט הזה מחשב באופן אוטומטי אם יש מספיק מקום כדי להציג את שני החלונות יחד על סמך הערך של layout_width שצוין לשני החלונות, וניתן לחלק את המקום שנותר באמצעות layout_weight. אם אין מספיק מקום, כל חלונית תשתמש ברוחב המלא של הפריסה, וחלונית הפרטים תזוז מחוץ למסך או תופיע מעל חלונית הרשימה.

רכיב SlidingPaneLayout שמוצגות בו שתי החלונות של פריסה של רשימה עם פרטים במכשיר עם מסך רחב.
איור 5. פריסת SlidingPaneLayout שמוצגות בה שתי חלוניות ברוחב מורחב וחלונית אחת ברוחב קומפקטי.

במאמר יצירת פריסה עם שני חלונות מפורט מידע נוסף על השימוש ב-SlidingPaneLayout. חשוב לזכור שהדפוס הזה עשוי להשפיע על המבנה של תרשים הניווט (ראו ניווט בממשקי משתמש רספונסיביים).

מקורות מידע נוספים