Scrollbare Layouts für Fernseher erstellen

Bei TV-Apps basiert das Surfen auf einer effizienten, fokusbasierten Navigation. Mit den standardmäßigen Lazy Layouts von Compose Foundation können Sie leistungsstarke vertikale und horizontale Listen erstellen, die automatisch das fokusgesteuerte Scrollen verarbeiten, damit aktive Elemente im Blick bleiben.

Standard-Scrollverhalten für Fernseher optimiert

Ab Compose Foundation 1.7.0 bieten standardmäßige Lazy Layouts (wie LazyRow und LazyColumn) integrierte Unterstützung für Funktionen zur Fokuspositionierung. Dies ist die empfohlene Methode zum Erstellen von Katalogen für TV-Apps, da so fokussierte Elemente sichtbar bleiben und für den Nutzer intuitiv positioniert werden.

Wenn Sie eine einfache scrollbare Liste implementieren möchten, verwenden Sie die standardmäßigen Lazy-Komponenten. Diese Komponenten übernehmen automatisch die Navigation über das Steuerkreuz und rücken das fokussierte Element in den Vordergrund.

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 */ }
            )
        }
    }
}

Scrollverhalten mit BringIntoViewSpec anpassen

Wenn Ihr Design einen bestimmten „Drehpunkt“ erfordert (z. B. dass das fokussierte Element genau 30% vom linken Rand entfernt bleibt), können Sie das Scrollverhalten mit einem BringIntoViewSpec anpassen. Diese Funktion ersetzt die ältere pivotOffsets-Funktion, da Sie genau festlegen können, wie der Darstellungsbereich gescrollt werden soll, um ein fokussiertes Element zu berücksichtigen.

1. Benutzerdefinierten BringIntoViewSpec definieren

Mit der folgenden komponierbaren Funktion können Sie einen „Pivot“ basierend auf übergeordneten und untergeordneten Bruchteilen definieren. Der parentFraction bestimmt, wo im Container das Element platziert werden soll, und der childFraction bestimmt, welcher Teil des Elements an diesem Punkt ausgerichtet wird.

@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. Benutzerdefinierte Spezifikation anwenden

Verwenden Sie den Helper, um die Positionierung auf Ihre Layouts anzuwenden. Dies ist nützlich, um eine „konsistente Fokuslinie“ über verschiedene Zeilen Ihres Katalogs hinweg zu erstellen.

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. Deaktivierung für bestimmte verschachtelte Layouts

Wenn Sie ein bestimmtes verschachteltes Layout haben, das das Standard-Scrollverhalten anstelle Ihres benutzerdefinierten Pivots verwenden soll, geben Sie den DefaultBringIntoViewSpec an:

private val DefaultBringIntoViewSpec = object : BringIntoViewSpec {}

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

Wenn Sie einen leeren BringIntoViewSpec übergeben, wird das Standardverhalten des Frameworks übernommen.

Migration von TV Foundation zu Compose Foundation

Die TV-spezifischen Lazy-Layouts in androidx.tv.foundation wurden zugunsten der Standard-Compose Foundation-Layouts eingestellt.

Abhängigkeitsupdates

Prüfen Sie, ob Ihre build.gradle Version 1.7.0 oder höher für Folgendes verwendet:

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

Komponentenzuordnung

Aktualisieren Sie für die Migration Ihre Importe und entfernen Sie das Präfix Tv aus Ihren Komponenten:

Eingestellte TV-Komponente Ersatz für Compose Foundation
TvLazyRow LazyRow
TvLazyColumn LazyColumn
TvLazyHorizontalGrid LazyHorizontalGrid
TvLazyVerticalGrid LazyVerticalGrid
pivotOffsets BringIntoViewSpec (über LocalBringIntoViewSpec)