如何阻止点击穿透自定义的Compose底部弹出层遮罩?
Description
我希望我的 CustomSheet 能正确覆盖屏幕、阻止下面屏幕的所有交互。我以为这是位于顶部的任意Composable的默认行为。然而,在这种情况下,尽管整块屏幕都已被覆盖,我仍然可以点击 HomeScreen 上的按钮。你有什么想法吗?
Code details
我的 CustomSheet 包装器:
val visibleState = remember {
MutableTransitionState(false)
}.apply {
targetState = visible
}
if (visibleState.currentState || visibleState.targetState || !visibleState.isIdle) {
Box(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.scrim)
)
AnimatedVisibility(
modifier = Modifier.align(Alignment.BottomCenter),
visibleState = visibleState,
enter = slideInVertically(
initialOffsetY = { it },
animationSpec = tween(durationMillis = 500, delayMillis = 250)
),
exit = slideOutVertically(
targetOffsetY = { it },
animationSpec = tween(500)
)
) {
Box(
modifier = modifier
.clip(shape)
.background(color = backgroundColor)
){
content()
}
}
}
}
CustomSheet:
@Composable
fun CustomSheet(
viewModel: ChangelogViewModel = hiltViewModel(),
navController: NavController
) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
// display only in homeScreen
val isHomeScreen = currentDestination?.hierarchy?.any {
it.route in listOf(
NavigationBarEnum.HOME.route.name(),
)
} == true
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
var delayedVisible by remember { mutableStateOf(false) }
LaunchedEffect(uiState.changelog, uiState.visible, isHomeScreen) {
val showChangelog = uiState.changelog != null && uiState.visible && isHomeScreen
if (showChangelog) {
delay(ANIMATION_LONG_DELAY_DURATION.toLong())
delayedVisible = true
} else {
delayedVisible = false
}
}
uiState.changelog?.let { changelogItem ->
CustomSheetWrapper(
visible = delayedVisible,
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.85f),
) {
ChangelogContent(
currentVersion = changelogItem.version,
changelog = changelogItem.changelog,
onSubmit = {
viewModel.onChangelogDismissed()
})
}
}
}
我直接在 MainActivity 里把它连接起来:
NavHost(navController = navController)
CustomSheet(navController = navController)
然而,下面是位于顶部的 HomeScreen,里面有一个按钮(该按钮被 CustomSheet 完全遮挡):
@Composable
fun ClickableCard(data: Data?, onClick: () -> Unit) {
val painter = if (diocese != null) painterResource(diocese.image)
else painterResource(R.drawable.example)
Card(
shape = RoundedCornerShape(Dimens.roundedCornersLarge),
elevation = CardDefaults.elevatedCardElevation(Dimens.roundedCornersLarge),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
modifier = Modifier
.fillMaxWidth()
.aspectRatio(16f / 9f)
) {}
}
解决方案
在Jetpack Compose中,只有background() 修饰符的Box不会消费指针事件。background会绘制像素,但对输入系统保持“透明”,因此点击会穿透到Box下方绘制的内容。父级内部的叠放顺序由声明顺序决定。后面的同级会绘制在上方,但绘制顺序并不会自动拦截触摸事件。
向覆盖层Box添加任一
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = { /* dismiss */ },
)
或
.pointerInput(Unit) {
awaitPointerEventScope {
while (true) { awaitPointerEvent() }
}
}
,最好也应用到sheets内容Box上,这样触摸就会被吸收,而不是穿透到下方的屏幕。
完整示例:
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.scrim)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = onScrimClick, // or {} to just absorb
),
)
指针事件会被分发到一个可组合项层级。当一个新的指针首次触发指针事件时,系统开始对 "eligible" 可组合项进行命中测试。一个可组合项在具备指针输入处理能力时才被视为符合条件。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。