Compose Material3 SearchBar拦截尾随按钮点击,如何禁止该拦截?
问题
使用Compose Material3的SearchBar组件时,编写了如下代码:
SearchBar( query = query, onQueryChange {}, onSearch = {}, active = active, onActiveChange = {}, trailingIcon = { IconButton(onClick = {}) { Icon( painterResource(R.drawable.ic_filter_24), contentDescription = "Filter", ) } }, ) {}
点击筛选按钮本应打开对应菜单,但同时会触发SearchBar的onActiveChange使其展开,不符合预期。查看SearchBar内部代码发现,其BasicTextField通过pointerInput拦截了自身表面的所有点击事件(包括子组件trailingIcon的点击):
BasicTextField( value = query, onValueChange = onQueryChange, modifier = modifier .height(InputFieldHeight) .fillMaxWidth() .focusRequester(focusRequester) .pointerInput(Unit) { awaitEachGesture { // Must be PointerEventPass.Initial to observe events before the text field // consumes them in the Main pass awaitFirstDown(pass = PointerEventPass.Initial) val upEvent = waitForUpOrCancellation(pass = PointerEventPass.Initial) if (upEvent != null) { onActiveChange(true) } } }, decorationBox = @Composable { innerTextField -> TextFieldDefaults.TextFieldDecorationBox( interactionSource = interactionSource, trailingIcon = trailingIcon?.let { trailing -> { Box(Modifier.offset(x = -SearchBarIconOffsetX)) { trailing() } } }, ) } )
需要绕过该拦截,让trailingIcon单独处理点击事件,阻止事件向下传递给SearchBar。
解决方案
方法1:在IconButton中消费点击事件
给IconButton添加自定义pointerInput,在事件传递的初始阶段直接消费事件,阻止其到达SearchBar的拦截逻辑:
SearchBar( query = query, onQueryChange {}, onSearch = {}, active = active, onActiveChange = {}, trailingIcon = { IconButton( onClick = { /* 此处处理筛选菜单的打开逻辑 */ }, modifier = Modifier.pointerInput(Unit) { awaitEachGesture { awaitFirstDown(pass = PointerEventPass.Initial) waitForUpOrCancellation(pass = PointerEventPass.Initial) consumeAllChanges() // 消费事件,终止传递 } } ) { Icon( painterResource(R.drawable.ic_filter_24), contentDescription = "Filter", ) } }, ) {}
方法2:在onActiveChange中判断点击来源
通过跟踪图标的交互状态,在onActiveChange回调中跳过激活逻辑:
val iconInteractionSource = remember { MutableInteractionSource() } SearchBar( query = query, onQueryChange {}, onSearch = {}, active = active, onActiveChange = { shouldActivate -> // 检查图标是否处于按下状态 val isIconPressed = iconInteractionSource.interactions.any { it is PressInteraction.Press } if (!isIconPressed) { // 仅在未点击图标时执行SearchBar激活逻辑 } }, trailingIcon = { IconButton( onClick = { /* 处理筛选菜单 */ }, interactionSource = iconInteractionSource ) { Icon( painterResource(R.drawable.ic_filter_24), contentDescription = "Filter", ) } }, ) {}
方法3:自定义SearchBar组件(进阶)
如果上述方法无法满足需求,可以参考官方SearchBar的实现,自行封装搜索栏组件,移除全局点击拦截逻辑,仅在输入区域添加激活逻辑。
内容的提问来源于stack exchange,提问作者proninyaroslav
相关产品推荐
相关产品推荐

