admin管理员组文章数量:1415673
I've noticed an issue that consistently happens when combining LazyVerticalGrid
default focus handling with smooth scroll animations. If I hold down the up button on the remote the grid will start up focusing to the item directly above the previous item in the same column then as the scroll speeds up it will jump over to the last column.
I have a guess as to why this is. The system does not see the target item in the layout so it defaults to the "closest" 1-dimensional item index the target row relative to the previous item which is always in the last (far right) column.
Any idea how to workaround this limitation and maintain column during focus traversal? I've tried setting up custom focus traversal with .focusProperties {}
modifier, defining the up and down directions, but once the target item is no longer in the layout it throws an exception because it's trying to traverse to a FocusRequester the isn't installed.
Update: Goggle knows about this on their issue tracker. Appears it has to do with LazyGrid
using the same "BeyondBoundsLayout modifier" as LazyList
which looks only at the first offscreen item to move focus to, not the entire row.
Minimal Reproducible Example (Trouble Building? Paste into New Project -> Television -> Empty Activity template)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val colors = arrayOf(
Color.Red,
Color(255, 165, 0),
Color.Yellow,
Color.Green
)
setContent {
LazyVerticalGrid(
state = rememberLazyGridState(),
columns = GridCells.Fixed(4)
) {
for (i in 0 until 100) {
item {
ClassicCard(
modifier = Modifier.padding(10.dp),
image = {
Box(modifier = Modifier
.fillMaxWidth()
.aspectRatio(16/9f)
.background(colors[i % 4]))
},
title = {
Text("Item ${i + 1}")
},
onClick = {}
)
}
}
}
}
}
}
Custom Focus Traversal Example (Throws IllegalStateException if up or down button is held)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val numItems = 100
val cols = 4
val colors = arrayOf(
Color.Red,
Color(255, 165, 0),
Color.Yellow,
Color.Green
)
val focusRequesters = Array(numItems) { FocusRequester() }
setContent {
LazyVerticalGrid(
state = rememberLazyGridState(),
columns = GridCells.Fixed(cols)
) {
for (i in 0 until numItems) {
val addUpFocus = i - cols in focusRequesters.indices
val addDownFocus = i + cols in focusRequesters.indices
val focusPropertiesMod = when {
addUpFocus && addDownFocus -> {
Modifier.focusProperties {
up = focusRequesters[i - cols]
down = focusRequesters[i + cols]
}
}
addUpFocus -> {
Modifier.focusProperties {
up = focusRequesters[i - cols]
}
}
addDownFocus -> {
Modifier.focusProperties {
down = focusRequesters[i + cols]
}
}
else -> {
Modifier
}
}
item {
ClassicCard(
modifier = Modifier
.padding(10.dp)
.focusRequester(focusRequesters[i])
.then(focusPropertiesMod),
image = {
Box(modifier = Modifier
.fillMaxWidth()
.aspectRatio(16/9f)
.background(colors[i % 4]))
},
title = {
Text("Item ${i + 1}")
},
onClick = {}
)
}
}
}
}
}
}
Dependencies
[versions]
agp = "8.6.0-alpha06"
kotlin = "1.9.0"
coreKtx = "1.15.0"
appcompat = "1.7.0"
composeBom = "2025.01.01"
tvFoundation = "1.0.0-alpha12"
tvMaterial = "1.0.0"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.10.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
androidx-compose-bom = { group = "androidxpose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui-tooling-preview = { group = "androidxpose.ui", name = "ui-tooling-preview" }
androidx-ui-tooling = { group = "androidxpose.ui", name = "ui-tooling" }
androidx-ui-test-manifest = { group = "androidxpose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidxpose.ui", name = "ui-test-junit4" }
androidx-tv-foundation = { group = "androidx.tv", name = "tv-foundation", version.ref = "tvFoundation" }
androidx-tv-material = { group = "androidx.tv", name = "tv-material", version.ref = "tvMaterial" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = ".jetbrains.kotlin.android", version.ref = "kotlin" }
本文标签: androidHow to maintain column during focus traversal LazyVerticalGrid on TVStack Overflow
版权声明:本文标题:android - How to maintain column during focus traversal LazyVerticalGrid on TV - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745236293a2649056.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论