Window:追踪折叠与窗口特征 · AndroidX 源码指南
AAndroidX 源码指南
自适应布局
自适应布局 · androidx.window

Window:追踪折叠与窗口特征Window 1.6.0-alpha05

用 WindowInfoTracker 的 Flow 观察 WindowLayoutInfo 和 FoldingFeature。

最后更新 2026-08-01

复制即用

val tracker = WindowInfoTracker.getOrCreate(activity)

activity.lifecycleScope.launch {
    activity.repeatOnLifecycle(Lifecycle.State.STARTED) {
        tracker.windowLayoutInfo(activity).collect { layoutInfo ->
            val fold = layoutInfo.displayFeatures
                .filterIsInstance<FoldingFeature>()
                .firstOrNull()
            renderFor(fold)
        }
    }
}

关键windowLayoutInfo持续变化的 Flow,不是一次性设备检测。收集必须绑定可见生命周期(repeatOnLifecycle),避免页面不可见后仍持有 Activity。

FoldingFeature 属性

val fold = ... as FoldingFeature

fold.state          // FLAT / HALF_OPENED
fold.orientation    // HORIZONTAL / VERTICAL
fold.occlusionType  // NONE / FULL(遮挡屏幕)
fold.isSeparating   // 是否分隔成两个 pane
fold.bounds         // 折痕区域

判断原则

  • 有折痕不必然要分两个 pane——先看 isSeparating(是否分隔内容)。
  • occlusionType 判断是否遮挡(铰链遮挡屏幕时内容要避开)。
  • bounds 决定实际分隔边界在哪。
  • 普通直板设备的 displayFeatures 为空——正常结果,不是错误。

分隔布局示例

@Composable
fun FoldAwareLayout(fold: FoldingFeature?, content: @Composable () -> Unit) {
    if (fold == null || !fold.isSeparating) {
        // 单面板:正常内容
        content()
        return
    }

    // 折叠分隔:双面板布局
    val bounds = fold.bounds
    if (fold.orientation == FoldingFeature.Orientation.VERTICAL) {
        Row(Modifier.fillMaxSize()) {
            Box(Modifier.weight(1f).size(width = bounds.width.toDp())) { /* 左 */ }
            Box(Modifier.weight(1f)) { /* 右 */ }
        }
    }
}

常见陷阱

  • 当成一次性查询windowLayoutInfo 是 Flow,窗口变化(旋转/折叠展开)会重发。
  • 不在 repeatOnLifecycle:后台仍收集,持有 Activity。
  • 把有折痕=必须分栏isSeparating=false 时折痕不分割内容。
  • 忽略 occlusion:铰链遮挡屏幕时内容显示在遮挡区。
  • Compose 忘 API:Compose 优先用 currentWindowAdaptiveInfoV2(),这个底层 API 给自定义场景。

复制即用

class MainActivity : ComponentActivity() {

    private val tracker by lazy { WindowInfoTracker.getOrCreate(this) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                tracker.windowLayoutInfo(this@MainActivity).collect { info ->
                    val fold = info.displayFeatures.filterIsInstance<FoldingFeature>().firstOrNull()
                    updateLayout(fold)
                }
            }
        }
    }
}

要点

  • WindowInfoTracker.windowLayoutInfo 是持续 Flow;绑定可见生命周期收集。
  • FoldingFeatureisSeparating/occlusionType/bounds 决定布局。
  • 直板设备 displayFeatures 为空是正常。
  • Compose 场景优先 currentWindowAdaptiveInfoV2()

相关页面