Compose SelectionState API — 主动控制文本选择

各位 Compose 爱好者们,早上好,今天我们来看一个 Compose 的新特性,SelectionState API ------ 可以主动选择文本的能力。

我在之前的一篇文章破坏性更新 - 解读 Jetpack Compose 1.12中,提到过这个变更。

以往的 Compose 中,实际上已经有一个可以实现选择的控件 ------ SelectionContainer,但它更像是一个纯粹的交互容器:把文本放进去,用户就可以长按、拖动来选择内容。

如果你想在代码中主动做点事情------例如点击按钮后全选、清除当前选择,或者把已选文本同步到界面其他位置------可用的入口并不多。

Compose 1.12.0 新增的 SelectionState,就是用来解决这个问题的。

有了它,选择状态可以被提升出来,供外部观察、双向控制,而不再只是容器内部的临时交互状态。

SelectionState 是什么

SelectionState 用来保存某个 SelectionContainer 的选择状态。它既能让我们拿到当前选中的文本,也提供了一组方法,用于在代码中修改选择范围。

在 Composable 中,通常通过 rememberSelectionState() 创建:

kotlin 复制代码
val selectionState = rememberSelectionState()

然后把它交给对应的 SelectionContainer

kotlin 复制代码
SelectionContainer(state = selectionState) {
    Text(
        "可以通过代码主动选择的文本内容。"
    )
}

这里有一个需要注意的约束:

同一个 SelectionState 不能同时传给多个 SelectionContainer

它描述的是某一个容器的选择状态,而不是全局的选择管理器。多个容器需要独立选择时,就应分别创建各自的 SelectionState

另外,rememberSelectionState() 会自动处理状态保存与恢复;如果是更复杂的状态管理场景,也可以配合 SelectionState.SaverrememberSaveable 使用。

主动控制选择范围

SelectionState 提供了四个直接的控制方法。

全选

selectAll() 会选择容器中全部可选择的 Text 内容。

kotlin 复制代码
Button(onClick = { selectionState.selectAll() }) {
    Text("全选")
}

需要留意的是,如果把 SelectionContainer 放在 LazyColumn 这类懒布局的外层,selectAll() 只能选择当前已经进入 Composition 的文本,并不会自动把未组合的列表项也选中。

清除选择

clear() 会清除当前选择,同时移除选择手柄。

kotlin 复制代码
Button(onClick = { selectionState.clear() }) {
    Text("清除选择")
}

指定范围选择

select(TextRange) 可以选中指定范围的文本:

kotlin 复制代码
Button(onClick = { selectionState.select(TextRange(0, 4)) }) {
    Text("选择指定范围")
}

这里的 TextRange 是相对于整个 SelectionContainer 内全部 Text全局文本位置 ,而不是某一个单独 Text 的局部下标。

这意味着,即使容器内有多个 Text,也可以用一个范围跨越它们进行选择。

按单词扩展

extendSelectionByWord() 会把当前选择扩展到下一个单词边界。

kotlin 复制代码
Button(onClick = { selectionState.extendSelectionByWord() }) {
    Text("按单词扩展")
}

它的行为有两个细节:

  • 如果当前没有选择内容,会先选中容器中的第一个单词;
  • 如果下一个单词位于另一个 Text 中,选择会继续扩展到下一个 Text

这里的"单词边界"遵循 Unicode 的文本分词规则,因此空格、标点等非单词字符也会参与边界判断。若用户从右向左反向选择,扩展方向也会跟随当前选择的活动端,而不是始终向后增加内容。

读取当前选中的文本

控制选择只是第一步,更实用的是我们终于可以直接观察"用户到底选了什么"。

selectionState.selectedTexts 会返回当前被选中的文本:

kotlin 复制代码
val selectedTexts: List<AnnotatedString> = selectionState.selectedTexts

返回值是 List<AnnotatedString>,并且按照界面中的视觉/布局顺序排列。容器中每个参与选择的 Text,都会作为一个 AnnotatedString 出现在这个列表里。

这个属性由 mutableStateOf 支撑,因此它本身就是可观察的 Compose State。选择范围变化后,读取它的 Composable 会自动重组:

kotlin 复制代码
Text(text = "当前选中:${selectionState.selectedTexts}")

实际项目里,一般会把它转换成你真正需要的形式,例如用于展示已选字符数、复制内容预览,或者驱动一组只在存在选择时才显示的操作按钮。

完整示例

下面这个示例把前面的几个能力放在一起:全选、清除、按范围选择、按单词扩展,以及把当前选择结果直接展示在界面上。

kotlin 复制代码
@Composable
fun ProgrammaticSelectionExample() {
    val selectionState = rememberSelectionState()

    Column {
        Row {
            Button(
                onClick = { selectionState.selectAll() },
            ) {
                Text("全选")
            }

            Button(
                onClick = { selectionState.clear() },
            ) {
                Text("清除选择")
            }
        }

        Row {
            Button(
                onClick = { selectionState.select(TextRange(0, 4)) },
            ) {
                Text("选择指定范围")
            }
            Button(
                onClick = { selectionState.extendSelectionByWord() },
            ) {
                Text("按单词扩展")
            }
        }

        SelectionContainer(state = selectionState) {
            Text(
                "可以通过代码主动选择的文本内容。",
                style = MaterialTheme.typography.headlineLarge
            )
        }

        Text(text = "当前选中:${selectionState.selectedTexts}")
    }
}

原文中的演示效果如下:

总结

SelectionState 本身不是大的 API 变更,但它让 SelectionContainer 的选择除了靠用户手势,也能由代码主动驱动。

当你的页面需要实现选中文本的计数、复制前预览、批量操作,或者给文本选择提供额外快捷操作时,就不必再绕过 Compose 的选择机制,自己维护一套选择范围了。

它的使用方式也很符合 Compose 一贯的思路:把状态创建在合适的位置,上提给需要读取和操作它的组件;容器负责渲染与手势,外层则根据状态作出响应。

相关推荐
拾光Ծ2 小时前
【MySQL】对表数据的操作:增删查改(CRUD)
android·数据库·sql·mysql
一笑的小酒馆12 小时前
Android中的tcp通信
android
默阳123415 小时前
2026年Android中高级面试题专栏(Android进阶篇)
android
古法安卓15 小时前
Android-日志系统源码解析
android·java·android studio
恋猫de小郭16 小时前
Android 17 + OkHttp 5.5.0 ,全新 ECH 下你的 HTTPS 域名可以请求时被安全隐藏
android·前端·flutter
小强闯江湖16 小时前
不只是让 AI 写代码:ViewCompose 把 Android UI 做成了可编译、可渲染的闭环
android·人工智能·kotlin
hunterandroid18 小时前
StateFlow 与 SharedFlow 的边界:状态与事件的正确建模
android·前端
YF021118 小时前
Android遥控器对频详解
android·android things
码农coding19 小时前
android12 WindowManagerService窗口的布局过程
android·源码