Android TV Compose迁移:EPG节目指南实现问题求助
解决方案
一、实现频道栏与节目栏联动垂直滚动
要实现每个频道对应一行节目,且和侧边频道栏同步滚动,核心思路是共享滚动状态,让两个垂直滚动容器(LazyColumn)使用同一个LazyListState,确保滚动位置完全同步。
步骤与代码示例
- 定义基础数据类:
data class Channel( val id: String, val name: String ) data class Program( val id: String, val title: String, val startTime: Long, val duration: Long )
- 构建联动滚动布局:
@Composable fun EpgScreen(channels: List<Channel>, channelPrograms: Map<String, List<Program>>) { // 共享同一个滚动状态,确保两个LazyColumn同步滚动 val scrollState = rememberLazyListState() Row(modifier = Modifier.fillMaxSize()) { // 侧边频道栏 LazyColumn( state = scrollState, modifier = Modifier.width(120.dp).fillMaxHeight(), contentPadding = PaddingValues(vertical = 8.dp) ) { items(channels) { channel -> ChannelItem(channel = channel) } } // 节目内容栏:每个频道对应一行横向滚动的节目 LazyColumn( state = scrollState, modifier = Modifier.weight(1f).fillMaxHeight(), contentPadding = PaddingValues(vertical = 8.dp) ) { items(channels) { channel -> ProgramRow(programs = channelPrograms[channel.id] ?: emptyList()) } } } } @Composable fun ChannelItem(channel: Channel) { Text( text = channel.name, modifier = Modifier .fillMaxWidth() .height(60.dp) // 固定高度,和节目行高度保持一致 .padding(horizontal = 16.dp, vertical = 16.dp) .focusable(true) .focusIndicator(), fontSize = 18.sp ) } @Composable fun ProgramRow(programs: List<Program>) { LazyRow( modifier = Modifier.height(60.dp).fillMaxWidth(), // 和频道项高度一致 contentPadding = PaddingValues(horizontal = 8.dp) ) { items(programs) { program -> ProgramItem(program = program) } } } @Composable fun ProgramItem(program: Program) { val durationInMinutes = (program.duration / 60000).toString() Text( text = "${program.title}\n$durationInMinutes' ", modifier = Modifier .width(150.dp) .height(52.dp) .padding(8.dp) .focusable(true) .focusIndicator(), fontSize = 14.sp ) }
关键注意点
- 确保频道项和节目行的高度完全一致,否则滚动时会出现错位
- 使用同一个
LazyListState绑定两个LazyColumn,实现滚动联动
二、修复TV端焦点高亮不显示问题
仅设置focusable(true)不足以显示焦点高亮,需要结合Compose TV的焦点指示符或自定义焦点样式:
方案1:使用官方focusIndicator()修饰符
Compose TV库提供了focusIndicator(),可以快速添加默认焦点高亮效果,直接添加到focusable()之后即可:
Modifier .focusable(true) .focusIndicator() // 自动添加系统默认焦点框
方案2:自定义焦点高亮样式
如果默认样式不符合需求,可以通过drawBehind自定义焦点框:
Modifier .focusable(true) .drawBehind { if (isFocused) { val borderWidth = 2.dp.toPx() drawRect( color = Color.White, style = Stroke(width = borderWidth), topLeft = Offset(borderWidth, borderWidth), size = size - Size(borderWidth * 2, borderWidth * 2) ) } }
关键注意点
- 确保依赖了Compose TV相关库(如
androidx.tv:tv-foundation) - 焦点修饰符的顺序:
focusable()要在focusIndicator()或自定义绘制修饰符之前
内容的提问来源于stack exchange,提问作者techker
相关产品推荐
相关产品推荐

