You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose中监听SystemBars显示并自动隐藏

监听SystemBars显示状态并自动隐藏的Jetpack Compose实现方案

问题描述

想要监听SystemBars的显示状态,以便在一段时间后自动隐藏,回到沉浸式视图。可以手动隐藏和显示系统栏,但无法有效监听其显示状态,初始实现方法效果不佳。

初始实现代码:

// readerScreenBars是一个mutableStateOf(true/false)变量,用于切换栏的显示状态

while (!readerScreenBars) { // 用于监听应用栏是否隐藏

    if (!readerScreenBars && systemUiController.isSystemBarsVisible){ // 如果系统栏可见且应用栏隐藏

        LaunchedEffect(Unit) {
            delay(4.seconds)
            systemUiController.isSystemBarsVisible = false

        break
    }

}

问题场景演示:
系统栏显示状态变化演示

解决方案

你的初始实现存在核心问题:在Compose的声明式UI体系中使用while循环会打破重组机制,且LaunchedEffect的使用方式不符合Compose生命周期规则。以下是正确的实现方式:

实现思路

  1. 借助Android原生ViewTreeObserver.OnGlobalLayoutListener监听根View布局变化,通过判断系统栏的WindowInsets间接获取其可见状态
  2. 用Compose的State变量维护系统栏可见状态,触发合法的UI重组
  3. 结合LaunchedEffect在满足条件时执行延迟隐藏逻辑,完全遵循Compose生命周期

完整代码示例

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalView
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import kotlinx.coroutines.delay

@Composable
fun ReaderScreen(
    systemUiController: WindowInsetsControllerCompat
) {
    var readerScreenBars by remember { mutableStateOf(true) }
    var isSystemBarsVisible by remember { mutableStateOf(true) }
    val view = LocalView.current

    // 监听系统栏可见状态变化
    DisposableEffect(view) {
        val listener = ViewTreeObserver.OnGlobalLayoutListener {
            val windowInsets = ViewCompat.getRootWindowInsets(view)
            val systemInsets = windowInsets?.getInsets(WindowInsetsCompat.Type.systemBars())
            // 通过系统栏的inset高度判断是否可见
            isSystemBarsVisible = (systemInsets?.top ?: 0) > 0 || (systemInsets?.bottom ?: 0) > 0
        }
        view.viewTreeObserver.addOnGlobalLayoutListener(listener)

        // 组件销毁时移除监听,避免内存泄漏
        onDispose {
            view.viewTreeObserver.removeOnGlobalLayoutListener(listener)
        }
    }

    // 延迟隐藏系统栏逻辑
    LaunchedEffect(readerScreenBars, isSystemBarsVisible) {
        // 当需要隐藏应用栏且系统栏当前可见时,执行延迟隐藏
        if (!readerScreenBars && isSystemBarsVisible) {
            delay(4000)
            systemUiController.hide(WindowInsetsCompat.Type.systemBars())
            isSystemBarsVisible = false // 同步本地状态与实际系统栏状态
        }
    }

    // 页面内容布局
    Surface(
        modifier = Modifier.fillMaxSize(),
        color = MaterialTheme.colorScheme.background
    ) {
        // ... 添加你的页面内容,比如切换readerScreenBars状态的按钮等
    }
}

代码说明

  • DisposableEffect:负责添加和移除原生View的监听,确保组件销毁时清理资源,避免内存泄漏
  • 系统栏可见性判断:通过WindowInsets获取系统栏的上下inset高度,当任一方向inset大于0时,判定系统栏处于显示状态
  • LaunchedEffect:监听readerScreenBars和isSystemBarsVisible的状态变化,仅在两个条件同时满足时执行延迟隐藏逻辑,完全符合Compose重组规则
  • 状态同步:隐藏系统栏后手动更新isSystemBarsVisible,保证本地状态与实际系统栏状态一致

额外优化(处理用户触摸显示系统栏场景)

如果需要支持用户触摸屏幕临时显示系统栏,再自动隐藏的逻辑,可以在页面布局中添加触摸监听:

// 在Surface中添加触摸监听
Surface(
    modifier = Modifier
        .fillMaxSize()
        .pointerInput(Unit) {
            detectTapGestures(
                onTap = {
                    if (!readerScreenBars) {
                        systemUiController.show(WindowInsetsCompat.Type.systemBars())
                        isSystemBarsVisible = true
                    }
                }
            )
        },
    color = MaterialTheme.colorScheme.background
) {
    // ... 页面内容
}

内容的提问来源于stack exchange,提问作者X-Black...

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 10:53:19