為電視建立可捲動的版面配置

電視應用程式的瀏覽體驗取決於以焦點為基礎的有效導覽。 使用標準 Compose Foundation 延遲版面配置,即可建立高效能的直向和橫向清單,自動處理焦點驅動的捲動,讓有效項目保持在檢視畫面中。

針對電視最佳化的預設捲動行為

從 Compose Foundation 1.7.0 開始,標準延遲版面配置 (例如 LazyRowLazyColumn) 內建支援焦點定位功能。建議您使用這種方式為電視應用程式建立目錄,因為這樣可確保焦點項目保持可見,並以直覺式方式呈現給使用者。

如要導入基本可捲動清單,請使用標準延遲元件。這些元件會自動處理 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

您可以使用下列輔助組合函式,根據父項和子項的分數定義「樞紐」。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)