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