对于 TV 应用,浏览体验依赖于高效的焦点导航。使用标准 Compose Foundation 延迟布局,您可以创建高性能的垂直和水平列表,这些列表会自动处理焦点驱动的滚动,以使活跃项保持在视图中。
针对电视优化的默认滚动行为
从 Compose Foundation 1.7.0 开始,标准延迟布局(例如 LazyRow 和 LazyColumn)内置了对焦点定位功能的支持。这是为 TV 应用构建目录的推荐方式,因为它有助于让获得焦点的项保持可见,并以直观的方式为用户定位。
如需实现基本的可滚动列表,请使用标准延迟组件。这些组件会自动处理方向键导航,并将焦点项显示在视图中。
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 中 TV 专用的延迟布局已弃用,取而代之的是标准 Compose Foundation 布局。
依赖项更新
验证您的 build.gradle 是否使用 1.7.0 或更高版本:
androidx.compose.foundationandroidx.compose.runtime
组件映射
如需迁移,请更新导入内容,并从组件中移除 Tv 前缀:
| 已废弃的 TV 组件 | Compose Foundation 替代方案 |
|---|---|
| TvLazyRow | LazyRow |
| TvLazyColumn | LazyColumn |
| TvLazyHorizontalGrid | LazyHorizontalGrid |
| TvLazyVerticalGrid | LazyVerticalGrid |
| pivotOffsets | BringIntoViewSpec(通过 LocalBringIntoViewSpec) |