如何解决LazyColumn始终吞噬鼠标点击(无条目时也生效)的问题?
我用Jetpack Compose写了一个全局通知区域,放在窗口角落。现在遇到的问题是:LazyColumn不管有没有条目,都会吞噬所有鼠标点击。我希望只有LazyColumn里已显示的条目才响应点击,空白区域的点击要透传到底层内容。
我必须用LazyColumn,因为它的.animateItem()能实现条目放置动画。如果去掉fillMaxHeight()修饰符,条目变更时的高度动画会失效,没法和.animateItem()配合实现流畅效果。
注意:第一个Row里可以放任意嵌套Composable,这里只是示例。通知区域必须显示在所有内容上方。
我试过用Column搭配AnimatedVisibility,虽然能正常工作,但会让通知可见性逻辑变复杂,而且按顺序关闭通知时还有小问题(每个通知都要启动一个LaunchedEffect(Unit)),所以不想用这个方案。
附代码示例:
import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.window.singleWindowApplication fun main() = singleWindowApplication { Box(modifier = Modifier.fillMaxSize()) { Row( modifier = Modifier .background(Color.LightGray) .fillMaxSize() .clickable { println("clicked") } ) { } Row(horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth()) { LazyColumn( modifier = Modifier .background(Color.DarkGray) .fillMaxHeight() .width(300.dp) ) { items(listOf("a", "b", "c")) { Text(it, modifier = Modifier .padding(10.dp) .clickable { println("clicked text") }) } } } } }
问题根源是LazyColumn作为可滚动组件,默认会消费所有触摸事件(包括点击),再加上你给它设置了background填充整个高度,导致空白区域也会拦截点击。下面提供两种适配不同场景的解决方案:
场景1:不需要滚动通知区域
如果通知数量不多,不需要滚动,只需给LazyColumn添加userScrollEnabled = false,禁用它的滚动事件消费能力,空白区域的点击就会透传到底层:
import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.window.singleWindowApplication fun main() = singleWindowApplication { Box(modifier = Modifier.fillMaxSize()) { Row( modifier = Modifier .background(Color.LightGray) .fillMaxSize() .clickable { println("clicked") } ) { } Row(horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth()) { LazyColumn( modifier = Modifier .background(Color.DarkGray) .fillMaxHeight() .width(300.dp), userScrollEnabled = false // 禁用滚动事件消费 ) { items(listOf("a", "b", "c")) { Text(it, modifier = Modifier .padding(10.dp) .clickable { println("clicked text") }) } } } } }
场景2:需要保留滚动功能
如果通知数量多需要滚动,就把LazyColumn的背景移到每个条目外层,让空白区域透明,这样点击会透传到底层,同时保留fillMaxHeight()来支持动画:
import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.Text import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.window.singleWindowApplication fun main() = singleWindowApplication { Box(modifier = Modifier.fillMaxSize()) { Row( modifier = Modifier .background(Color.LightGray) .fillMaxSize() .clickable { println("clicked") } ) { } Row(horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth()) { LazyColumn( modifier = Modifier .fillMaxHeight() .width(300.dp), // 移除全局背景,改为透明 verticalArrangement = Arrangement.Top ) { items(listOf("a", "b", "c")) { // 给每个条目单独设置背景 Box( modifier = Modifier .fillMaxWidth() .background(Color.DarkGray) ) { Text(it, modifier = Modifier .padding(10.dp) .clickable { println("clicked text") }) } // 可选:添加条目间距 Spacer(modifier = Modifier.height(4.dp)) } } } } }
如果需要整个通知区域的背景是深灰(包括条目间的空白),可以把所有条目放在一个Box里,再用LazyColumn加载,这样Box会包裹所有内容,空白区域依然透明:
// 仅修改LazyColumn部分 LazyColumn( modifier = Modifier .fillMaxHeight() .width(300.dp), verticalArrangement = Arrangement.Top ) { item { Box( modifier = Modifier .fillMaxWidth() .background(Color.DarkGray) ) { Column { listOf("a", "b", "c").forEach { Text(it, modifier = Modifier .padding(10.dp) .clickable { println("clicked text") }) Spacer(modifier = Modifier.height(4.dp)) } } } } }
内容的提问来源于stack exchange,提问作者Dima

