You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose LazyRow焦点项报错:LayoutCoordinate操作仅在isAttached为true时有效

问题描述

我计划使用Jetpack Compose开发一款Android TV应用,页面中包含一个带有多个条目的LazyRow组件,需求是当条目获得焦点时改变其背景色与边框样式。但快速切换条目焦点时,出现了错误提示:LayoutCoordinate operations are only valid when isAttached is true。

相关代码:

@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun MainScreen(
    mainViewModel: MainViewModel = viewModel()
) {

    val isLoading by mainViewModel.isLoading.collectAsState() 

    if (isLoading){
        Box(modifier = Modifier
            .fillMaxWidth()
            .padding(top = 16.dp), contentAlignment = Alignment.Center
    ){
            Box(modifier = Modifier
                .size(40.dp)
                .background(
                    color = Color.White,
                    shape = CircleShape
                )
                .padding(all = 8.dp)
            ){

                CircularProgressIndicator(
                    color = MaterialTheme.colorScheme.primary
                )
            }
        }
    } else {

        LazyColumn(
            modifier = Modifier.padding(top = 16.dp)
        ) {

            items(mainViewModel.homeSections.size) {
                Section(homeSection = mainViewModel.homeSections[it])
            }
        }
    }
} 


@Composable
fun Section(homeSection: HomeSection) {

    var selectedItem by remember { mutableIntStateOf(-1) } 

    Column(
        modifier = Modifier
            .padding(bottom = 16.dp)
    ) {
        Row(
            horizontalArrangement = Arrangement.Start,
            verticalAlignment = Alignment.CenterVertically
        ) {

            Box(
                modifier = Modifier
                    .width(8.dp)
                    .height(32.dp)
                    .background(
                        color = MaterialTheme.colorScheme.primary,
                        shape = RoundedCornerShape(
                            topEnd = 80.dp,
                            bottomEnd = 80.dp
                        )
                    )
            )

            Spacer(modifier = Modifier.width(16.dp))

            Text(
                text = homeSection.name,
                style = TextStyle(
                    fontWeight = FontWeight.W700
                )
            )
        } 
 
        Spacer(modifier = Modifier.height(16.dp)) 

        LazyRow {
            items(homeSection.posts.size) {
                Column(
                    modifier = Modifier
                        .width(120.dp)
                        .height(200.dp)
                        .background(if (selectedItem == it) Color.Gray.copy(alpha = 0.3f) else Color.Transparent)
                        .border(
                            width = if (selectedItem == it) 2.dp else 0.dp,
                            color = if (selectedItem == it) MaterialTheme.colorScheme.primary else Color.Transparent
                        )
                        .onFocusChanged { f ->
                            if (f.isFocused) selectedItem = it
                            else if (selectedItem == it) selectedItem = -1
                        }
                        .focusable()
                        .clickable {}
                        .padding(
                            top = 8.dp,
                            start = 8.dp,
                            end = 8.dp
                        )
                ) {

                    AsyncImage(
                        model = homeSection.posts[it].thumbnail,
                        contentDescription = "",
                        contentScale = ContentScale.FillBounds,
                        modifier = Modifier
                            .fillMaxWidth()
                            .height(140.dp)
                    ) 

                    Spacer(modifier = Modifier.height(8.dp)) 

                    Text(

                        text = homeSection.posts[it].title,
                        style = TextStyle(
                            fontSize = TextUnit(
                                12f,
                                TextUnitType.Sp
                            )
                        ),
                        textAlign = TextAlign.Center,
                        maxLines = 2,
                        overflow = TextOverflow.Ellipsis
                    )
                }
            }
        }
    }
}
解决方案

问题根源

快速切换焦点时,LazyRow的部分条目可能已被Compose的回收机制从布局树中移除(isAttached为false),但onFocusChanged回调仍在触发,此时修改状态会导致布局操作在未附着的组件上执行,引发错误。

具体修复步骤

1. 替换焦点回调并增加附着检查

用Modifier.onFocusEvent()替代onFocusChanged,在处理焦点事件前先判断组件是否已附着,避免无效的布局操作:

2. 优化后的Section组件代码

@Composable
fun Section(homeSection: HomeSection) {

    var selectedItem by remember { mutableIntStateOf(-1) } 

    Column(
        modifier = Modifier
            .padding(bottom = 16.dp)
    ) {
        // 标题部分保持不变
        Row(
            horizontalArrangement = Arrangement.Start,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Box(
                modifier = Modifier
                    .width(8.dp)
                    .height(32.dp)
                    .background(
                        color = MaterialTheme.colorScheme.primary,
                        shape = RoundedCornerShape(
                            topEnd = 80.dp,
                            bottomEnd = 80.dp
                        )
                    )
            )

            Spacer(modifier = Modifier.width(16.dp))

            Text(
                text = homeSection.name,
                style = TextStyle(
                    fontWeight = FontWeight.W700
                )
            )
        } 
 
        Spacer(modifier = Modifier.height(16.dp)) 

        LazyRow {
            items(homeSection.posts.size) { index ->
                Column(
                    modifier = Modifier
                        .width(120.dp)
                        .height(200.dp)
                        .background(if (selectedItem == index) Color.Gray.copy(alpha = 0.3f) else Color.Transparent)
                        .border(
                            width = if (selectedItem == index) 2.dp else 0.dp,
                            color = if (selectedItem == index) MaterialTheme.colorScheme.primary else Color.Transparent
                        )
                        .onFocusEvent { event ->
                            // 先检查组件是否已附着在布局树中
                            if (event.isAttached) {
                                when (event.type) {
                                    FocusEventType.Focus -> selectedItem = index
                                    FocusEventType.Unfocus -> {
                                        if (selectedItem == index) selectedItem = -1
                                    }
                                    else -> {}
                                }
                            }
                        }
                        .focusable()
                        .clickable {}
                        .padding(
                            top = 8.dp,
                            start = 8.dp,
                            end = 8.dp
                        )
                ) {
                    AsyncImage(
                        model = homeSection.posts[index].thumbnail,
                        contentDescription = "",
                        contentScale = ContentScale.FillBounds,
                        modifier = Modifier
                            .fillMaxWidth()
                            .height(140.dp)
                    ) 

                    Spacer(modifier = Modifier.height(8.dp)) 

                    Text(
                        text = homeSection.posts[index].title,
                        style = TextStyle(fontSize = 12.sp),
                        textAlign = TextAlign.Center,
                        maxLines = 2,
                        overflow = TextOverflow.Ellipsis
                    )
                }
            }
        }
    }
}

3. 额外优化建议

  • 改用TvLazyRow(来自androidx.tv.foundation.lazy.TvLazyRow)替代普通LazyRow,它针对TV的焦点导航和条目回收逻辑做了专门优化。
  • 避免在Lazy组件的item中直接使用it作为索引,改用命名参数(如index)提升代码可读性。

内容的提问来源于stack exchange,提问作者علیرضا شجاعی

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 11:44:52