פריסת כרטיסים קלאסית רחבה עם רשת

סמל של דירוג ארבעה כוכבים

בהנחיות לעיצוב אפליקציות לטלוויזיה מוגדרת וריאציה של כרטיס שנקראת כרטיס קלאסי רחב, שבה מוצגת תמונה לצד בלוק תוכן עם תיאור. כשיוצרים פריסות מורכבות דו-ממדיות למסכי טלוויזיה, שימוש במיקום מסורתי שמבוסס על קואורדינטות או בקונטיינרים Row ו-Column עם קינון עמוק עלול להקשות על הקריאה, התחזוקה והארגון מחדש של הקוד. כרטיס קלאסי רחב הוא תרחיש שימוש אידיאלי ל-API של Jetpack Compose‏ Grid, כי הוא עוזר ליצור מבנה נקי של אלמנטים זה לצד זה באמצעות אזורי פריסה דו-ממדיים סמנטיים שהוגדרו על ידי המשתמש.

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

שיטות מומלצות

מטמיעים את פריסת הכרטיסים באמצעות Grid. השימוש בקונטיינר Grid משפר את קריאות הקוד, כי הוא מפריד בין המידות של הפריסה הפיזית לבין המיקום של פריטי הצאצא. במקום להסתמך על אינדקסים מדויקים של רצועות, מגדירים אזורים בעלי שם סמנטי באמצעות הפונקציה area ומציבים רכיבים הניתנים להרכבה של צאצאים באמצעות משנה המאפיינים gridItem. אפשר לשלב בין גדלים גמישים של רצועות, כמו GridTrackSize.Auto ו-minmax(0.dp, 1.fr), לבין רווחים מדויקים בין הרצועות (gap(row = 8.dp, column = 16.dp)), כדי לארגן בצורה ברורה ויציבה רכיבי כרטיסים מורכבים זה לצד זה.

אלמנטים

  • Grid: פריסת קונטיינר דו-ממדית ב-Jetpack Compose שמסדרת רכיבי composable צאצאים בשורות ובעמודות על סמך lambda של הגדרה
  • gridItem: תוסף Modifier שמקשר רכיב צאצא בממשק המשתמש לareaId ספציפי עם שם בתוך פריסת Grid
  • area: פונקציית DSL להגדרת תצורה בתוך GridConfigurationScope שממפה את הסמנטיקה areaId לטווחים או לטווחים ספציפיים של שורות ועמודות
  • Card: רכיב Container שניתן להרכבה ב-Material Design, שמקבץ רכיבי ממשק משתמש וסגנונות שקשורים זה לזה
  • GridTrackSize.MinMax: מאפיין של גודל רצועה שמגדיר גבולות גמישים (min ו-max) לממדי שורה או עמודה
  • GridTrackSize.Auto: מאפיין של גודל טראק שקובע את הגודל של טראק השורה או העמודה באופן אוטומטי כך שיתאים לתוכן שלו (column(GridTrackSize.Auto))
  • gap: פונקציית הגדרה שמציינת את המרווח האנכי (row) והאופקי (column) בין טראקים ברשת

צעדים

מגדירים מזהים של אזורים, מגדירים את האזורים ואת רצועות הרשת, ואז ממקמים את רכיבי ה-Composable של הצאצא.

1. הגדרת מזהים של אזורים סמנטיים

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

enum class CardArea {
    Image,
    Title,
    Subtitle,
    Description,
    ExtraText,
}

2. הגדרת טראקים של רשת דו-ממדית ואזורים עם שמות

בתוך ה-lambda של רכיב ה-Grid composable config, מגדירים את העמודות והשורות שמרכיבות את מבנה הכרטיס. משתמשים ב-GridTrackSize.Auto עבור עמודת התמונות כדי שהתמונה הממוזערת תתאים לרוחב העמודה. משתמשים ב-minmax(0.dp, 1.fr) בעמודת התוכן כדי שהיא תתרחב בצורה גמישה על פני הרוחב שנותר בכרטיס. מגדירים את המרווח בין הרצועות באמצעות gap(row = 8.dp, column = 16.dp). ממפים את המזהים CardArea לקואורדינטות של המיקום הפיזי שלהם באמצעות הפונקציה area.

Grid(
    config = {
        // Define columns: left column for image, right for content
        column(GridTrackSize.Auto)
        column(minmax(0.dp, 1.fr))

        // Define row tracks for the vertical content stack
        row(GridTrackSize.Auto)
        row(GridTrackSize.Auto)
        row(GridTrackSize.Auto)
        row(GridTrackSize.Auto)

        // Map semantic identifiers to grid coordinates and spans
        area(CardArea.Image, row = 1, column = 1, rowSpan = 4)
        area(CardArea.Title, row = 1, column = 2)
        area(CardArea.Subtitle, row = 2, column = 2)
        area(CardArea.Description, row = 3, column = 2)
        area(CardArea.ExtraText, row = 4, column = 2)

        gap(row = 8.dp, column = 16.dp)
    }
) {
    // Child elements placed in Step 3
}

3. מיקום של פונקציות קומפוזיציה צאצא באמצעות משנה gridItem

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

@Composable
fun WideClassicCard(
    imageContent: @Composable () -> Unit,
    title: String,
    subtitle: String,
    description: String,
    extraText: String,
    modifier: Modifier = Modifier,
) {
    Card(modifier = modifier) {
        Grid(
            config = {
                // Define columns: left column for image, right for content
                column(GridTrackSize.Auto)
                column(minmax(0.dp, 1.fr))

                // Define row tracks for the vertical content stack
                row(GridTrackSize.Auto)
                row(GridTrackSize.Auto)
                row(GridTrackSize.Auto)
                row(GridTrackSize.Auto)

                // Map semantic identifiers to grid coordinates and spans
                area(CardArea.Image, row = 1, column = 1, rowSpan = 4)
                area(CardArea.Title, row = 1, column = 2)
                area(CardArea.Subtitle, row = 2, column = 2)
                area(CardArea.Description, row = 3, column = 2)
                area(CardArea.ExtraText, row = 4, column = 2)

                gap(row = 8.dp, column = 16.dp)
            },
        ) {
            Box(modifier = Modifier.gridItem(CardArea.Image)) {
                imageContent()
            }
            Text(
                text = title,
                style = MaterialTheme.typography.titleLarge,
                modifier = Modifier
                    .gridItem(CardArea.Title)
                    .padding(top = 16.dp, end = 16.dp),
            )

            Text(
                text = subtitle,
                style = MaterialTheme.typography.titleMedium,
                modifier = Modifier
                    .gridItem(CardArea.Subtitle)
                    .padding(end = 16.dp),
            )

            Text(
                text = description,
                style = MaterialTheme.typography.bodyMedium,
                modifier = Modifier
                    .gridItem(CardArea.Description)
                    .padding(end = 16.dp),
            )

            Text(
                text = extraText,
                style = MaterialTheme.typography.labelSmall,
                modifier = Modifier
                    .gridItem(CardArea.ExtraText)
                    .padding(bottom = 16.dp, end = 16.dp),
            )
        }
    }
}

תוצאות

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

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