协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
compose-focus-navigation【开发与工程】Use when writing or reviewing Jetpack Compose UI for TV, keyboard, desktop, accessibility focus, D-pad navigation, FocusRequester, focusProperties, key events, or initial focus behavior.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Focus is stateful UI behavior: make targets and exceptional edges explicit, then drive and verify it through the user's keyboard, D-pad, or remote input.
| Need | Add |
|---|---|
| Normal button/text field/clickable focus | Nothing extra; use the focusable component |
| Programmatic initial/restored focus | FocusRequester + Modifier.focusRequester(...) |
| Visual or state reaction to focus changes | Modifier.onFocusChanged { ... } |
| Custom interactive surface that is not already focusable | Modifier.focusable() plus role/semantics as appropriate |
LaunchedEffect, keyed to the condition that makes the target present. For lazy content, keep requesters by stable item id and request only after the item is composed. Inside AnimatedContent, use the content lambda's target consistently for rendered identity, tags, requester ownership, and the effect key; captured outer state gives outgoing and incoming content the same identity.
In a review, a finding about requestFocus() running in the composable body
is incomplete until it names the event or keyed effect that should issue
the request after its target is available.focusProperties.For example, request and observe focus only when both behaviors are required:
val requester = remember { FocusRequester() }
Button(
onClick = onClick,
modifier = Modifier
.focusRequester(requester)
.onFocusChanged { state -> isFocused = state.isFocused },
) {
Text("Play")
}
Call focus requests from an effect, not the composable body:
val initialFocus = remember { FocusRequester() }
LaunchedEffect(initialFocus) {
initialFocus.requestFocus()
}
If the target appears after loading, key the request to the condition:
LaunchedEffect(items.isNotEmpty()) {
if (items.isNotEmpty()) {
firstItemRequester.requestFocus()
}
}
Use focusProperties only when default spatial search is wrong:
Modifier.focusProperties {
up = headerRequester
down = firstRowRequester
left = FocusRequester.Cancel
}
Too many hard-coded links create stale focus graphs. For special key behavior, consume only the handled event:
Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Back) {
onBack()
true
} else {
false
}
}
Test focus through user input:
composeTestRule.onNodeWithTag("screen").performKeyInput {
pressKey(Key.DirectionDown)
}
composeTestRule.onNodeWithTag("play-button").assertIsFocused()
Broader test-shape choices are in Compose UI testing patterns.