יצירת פריסות שניתן לגלול בהן בטלוויזיה

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

התנהגות ברירת המחדל של הגלילה מותאמת לטלוויזיה

החל מ-Compose Foundation 1.7.0, פריסות רגילות של lazy (כמו LazyRow ו-LazyColumn) כוללות תמיכה מובנית בתכונות של מיקום המיקוד. זו הדרך המומלצת ליצור קטלוגים לאפליקציות לטלוויזיה, כי היא עוזרת לשמור על פריטים ממוקדים שיהיו גלויים ולמקם אותם בצורה אינטואיטיבית למשתמש.

כדי להטמיע רשימה בסיסית שאפשר לגלול בה, משתמשים ברכיבי lazy רגילים. הרכיבים האלה מטפלים אוטומטית בניווט באמצעות כפתורי החיצים (D-pad) ומציגים את הפריט הממוקד.

import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items

@Composable
fun MovieCatalog(movies: List<Movie>) {
    LazyRow {
        items(movies) { movie ->
            MovieCard(
                movie = movie,
                onClick = { /* Handle click */ }
            )
        }
    }
}

התאמה אישית של התנהגות הגלילה באמצעות BringIntoViewSpec

אם העיצוב שלכם דורש נקודת 'ציר' ספציפית (לדוגמה, שמירת הפריט הממוקד בדיוק 30% מהקצה השמאלי), אתם יכולים להתאים אישית את התנהגות הגלילה באמצעות BringIntoViewSpec. הפונקציה הזו מחליפה את הפונקציונליות הישנה יותר של pivotOffsets ומאפשרת לכם להגדיר בדיוק איך אזור התצוגה צריך לגלול כדי להכיל פריט ממוקד.

1. הגדרה של BringIntoViewSpec בהתאמה אישית

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

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PositionFocusedItemInLazyLayout(
    parentFraction: Float = 0.3f,
    childFraction: Float = 0f,
    content: @Composable () -> Unit,
) {
    val bringIntoViewSpec = remember(parentFraction, childFraction) {
        object : BringIntoViewSpec {
            override fun calculateScrollDistance(
                offset: Float,       // Item's initial position
                size: Float,         // Item's size
                containerSize: Float // Container's size
            ): Float {
                // Calculate the offset position of the item's leading edge.
                val initialTargetForLeadingEdge =
                    parentFraction * containerSize - (childFraction * size)
                // If the item fits in the container, and scrolling would cause
                // its trailing edge to be clipped, adjust targetForLeadingEdge
                // to prevent over-scrolling near the end of list.
                val targetForLeadingEdge = if (size <= containerSize &&
                    (containerSize - initialTargetForLeadingEdge) < size) {
                    // If clipped, align the item's trailing edge with the
                    // container's trailing edge.
                    containerSize - size
                } else {
                    initialTargetForLeadingEdge
                }
                // Return scroll distance relative to initial item position.
                return offset - targetForLeadingEdge
            }
        }
    }

    // Apply the spec to all scrollables in the hierarchy
    CompositionLocalProvider(
        LocalBringIntoViewSpec provides bringIntoViewSpec,
        content = content,
    )
}

2. החלת המפרט בהתאמה אישית

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

PositionFocusedItemInLazyLayout(
    parentFraction = 0.3f, // Pivot 30% from the edge
    childFraction = 0.5f   // Center of the item aligns with the pivot
) {
    LazyColumn {
        items(sectionList) { section ->
            // This row and its items will respect the 30% pivot
            LazyRow { /* ... */ }
        }
    }
}

3. ביטול ההסכמה לפריסות מוטמעות ספציפיות

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

private val DefaultBringIntoViewSpec = object : BringIntoViewSpec {}

PositionFocusedItemInLazyLayout {
    LazyColumn {
        item {
            // This row will ignore the custom pivot and use default behavior
            CompositionLocalProvider(LocalBringIntoViewSpec provides DefaultBringIntoViewSpec) {
                LazyRow { /* ... */ }
            }
        }
    }
}

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

העברה מ-TV Foundation ל-Compose Foundation

הפריסות העצלניות שספציפיות לטלוויזיה ב-androidx.tv.foundation הוצאו משימוש, ועכשיו משתמשים בפריסות הרגילות של Compose Foundation.

עדכונים של פניות קשורות

מוודאים ש-build.gradle משתמש בגרסה 1.7.0 ואילך עבור:

  • androidx.compose.foundation
  • androidx.compose.runtime

מיפוי רכיבים

כדי לבצע את ההעברה, צריך לעדכן את הייבוא ולהסיר את הקידומת Tv מהרכיבים:

רכיב טלוויזיה שיצא משימוש החלפה של Compose Foundation
TvLazyRow LazyRow
TvLazyColumn LazyColumn
TvLazyHorizontalGrid LazyHorizontalGrid
TvLazyVerticalGrid LazyVerticalGrid
pivotOffsets BringIntoViewSpec (באמצעות LocalBringIntoViewSpec)