mcpField
C
compose-focus-navigation

compose-focus-navigation

github.com/chrisbanes开发1.1k1.9k 次安装1 天前更新
代码仓库

关于 compose-focus-navigation

compose-focus-navigation 是一项面向 Jetpack Compose 开发者的焦点导航 skill,指导如何配置并测试键盘、D-pad 或遥控器操作。它强调显式处理例外焦点路径,并通过用户实际使用的输入验证焦点状态。

主要功能

  1. 01按需添加焦点请求与状态观察
  2. 02仅在默认空间搜索不合适时配置例外路径
  3. 03只处理需要自定义行为的按键事件
  4. 04刷新后按语义标识恢复焦点
  5. 05使用按键输入和焦点语义测试

适用场景

  1. 01设置初始焦点或恢复焦点
  2. 02修正特定方向上的焦点跳转
  3. 03为刷新后的列表选择稳定焦点
  4. 04验证键盘或 D-pad 导航行为

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/chrisbanes/skills --skill compose-focus-navigation

安装后在 Claude Code 中输入/compose-focus-navigation确认已加载。

详细介绍

Compose:焦点导航

核心原则

焦点是有状态的 UI 行为:明确目标和特殊边缘,再通过用户的键盘、方向键或遥控器输入来驱动并验证。

流程

  1. 从已支持焦点的组件开始。仅在需要实现特定行为时添加钩子:
需求添加
普通按钮/文本字段/可点击组件的焦点无需额外添加;使用可聚焦组件
以编程方式设置初始/恢复焦点FocusRequester + Modifier.focusRequester(...)
对焦点变化做出视觉或状态响应Modifier.onFocusChanged { ... }
尚不支持焦点的自定义交互界面Modifier.focusable(),并酌情添加角色/语义
  1. 从 LaunchedEffect 请求初始焦点或恢复焦点,并将其键设为目标可用的条件。对于惰性内容,按稳定的项目 ID 保存请求器,并仅在项目完成组合后请求焦点。在 AnimatedContent 中,渲染标识、标签、请求器归属和 effect 键都应始终使用内容 lambda 的目标;捕获外部状态会导致退出和进入的内容具有相同标识。 在代码审查中,如果指出在可组合函数主体中运行 requestFocus(),那么在说明应通过哪个事件或键控 effect 于目标可用后发出请求之前,这条审查意见并不完整。
  2. 保留默认的空间搜索,除非某条具体边缘、跳转或陷阱存在问题。仅使用 focusProperties 编码这些例外。
  3. 仅为非普通点击或焦点遍历行为处理按键。只消费实际处理的事件;在开销较大的拥有方对快速方向键操作进行节流,而不是在整个屏幕范围内节流。
  4. 刷新后按语义标识恢复焦点:如果焦点所在 ID 仍存在,就保留它;否则选择一个确定性的回退目标。
  5. 使用一次具体的按键/方向键交互和焦点语义进行测试。审查的行为由用户触发时,不要用直接修改状态替代,也不要让输入成为条件性操作。仅使用屏幕截图验证焦点外观。
  6. 当所有有意设置的目标和特殊边缘都已编码、加载/刷新行为具有稳定的焦点策略,并且测试采用与用户相同的输入模型时,即可完成。

例如,只有在两种行为都必需时,才请求并观察焦点:

val requester = remember { FocusRequester() }

Button(
    onClick = onClick,
    modifier = Modifier
        .focusRequester(requester)
        .onFocusChanged { state -> isFocused = state.isFocused },
) {
    Text("Play")
}

通过 effect 而非可组合函数主体请求焦点:

val initialFocus = remember { FocusRequester() }

LaunchedEffect(initialFocus) {
    initialFocus.requestFocus()
}

如果目标在加载后才出现,请将请求的键设为相应条件:

LaunchedEffect(items.isNotEmpty()) {
    if (items.isNotEmpty()) {
        firstItemRequester.requestFocus()
    }
}

仅当默认空间搜索有误时,才使用 focusProperties:

Modifier.focusProperties {
    up = headerRequester
    down = firstRowRequester
    left = FocusRequester.Cancel
}

过多的硬编码链接会造成焦点图过时。对于特殊按键行为,只消费实际处理的事件:

Modifier.onPreviewKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp && event.key == Key.Back) {
        onBack()
        true
    } else {
        false
    }
}

通过用户输入测试焦点:

composeTestRule.onNodeWithTag("screen").performKeyInput {
    pressKey(Key.DirectionDown)
}

composeTestRule.onNodeWithTag("play-button").assertIsFocused()

更全面的测试结构选择请参阅 Compose UI 测试模式。

评论

支持 Markdown