Scrollbare Layouts für Fernseher erstellen

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

Standardmäßiges Scrollverhalten, das für die TV-Wiedergabe optimiert ist

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 sie dazu beiträgt, dass fokussierte Elemente sichtbar bleiben und intuitiv für den Nutzer positioniert werden.

Verwenden Sie die Standard-Lazy-Komponenten, um eine einfache scrollbare Liste zu implementieren. Diese Komponenten übernehmen automatisch die Navigation mit dem Steuerkreuz und bringen das fokussierte Element in den Blick.

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. wenn das fokussierte Element genau 30% vom linken Rand entfernt sein soll, können Sie das Scrollverhalten mit einem BringIntoViewSpec anpassen. Diese Funktion ersetzt die ältere pivotOffsets-Funktion. Sie können damit genau festlegen, wie der Viewport gescrollt werden soll, um ein fokussiertes Element zu berücksichtigen.

1. Benutzerdefiniertes BringIntoViewSpec definieren

Mit dem folgenden Helper-Composable können Sie einen „Pivot“ basierend auf den Anteilen von über- und untergeordneten Elementen definieren. Mit parentFraction wird festgelegt, wo im Container das Element platziert werden soll, und mit childFraction wird bestimmt, welcher Teil des Elements mit 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

Umschließen Sie Ihre Layouts mit dem Helfer, um die Positionierung anzuwenden. Das ist nützlich, um in verschiedenen Zeilen Ihres Katalogs eine „einheitliche Fokuslinie“ 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 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 ein leeres BringIntoViewSpec übergeben, wird das Standardverhalten des Frameworks aktiviert.

Migration von TV Foundation zu Compose Foundation

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

Abhängigkeitsupdates

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

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

Komponentenzuordnung

Aktualisieren Sie zum Migrieren Ihre Importe und entfernen Sie das Präfix Tv aus Ihren Komponenten:

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