如何检测设备是否为折叠屏?

解读

在国内面试中,这道题考察的是“对非常规屏幕形态的系统级适配能力”。折叠屏手机(华为 Mate X 系列、荣耀 Magic V、OPPO Find N、vivo X Fold 等)已经批量上市,厂商对“大屏利用率”和“连续交互体验”有 KPI 要求,因此面试官希望候选人:

  1. 能第一时间给出“官方推荐 API”,避免用反射或民间字段;
  2. 知道在哪些生命周期回调里判断,防止因窗口大小变化而重复弹窗或重建任务;
  3. 了解国内 ROM 差异化(例如华为 EMUI 的超大屏模式、小米 MIUI 的平行窗口)对判断结果的影响;
  4. 能把“折叠屏检测”与“窗口大小类、Configuration、Jetpack WindowManager”串成体系,体现架构思维。

知识点

  1. Jetpack WindowManager 1.0+(androidx.window:window)——Google 官方折叠屏适配库,提供 WindowInfoRepository、 FoldingFeature、DeviceState。
  2. FoldingFeature 的 isSeparating、orientation、state(FLAT / HALF_OPENED)、occlusionType(FULL / NONE)。
  3. Configuration.uiMode、screenLayout、smallestScreenWidthDp 的“传统字段”局限:只能拿到“屏幕大或小”,无法区分“折叠/展开/半折”。
  4. Activity 生命周期与 WindowMetricsCalculator:在 onStart() 之后监听、在 onStop() 之前移除,防止泄漏。
  5. 国内 ROM 的“平行视窗”“超大屏强制缩放”开关:可能让 smallestScreenWidthDp 失真,需要结合 FoldingFeature 二次校验。
  6. 权限:Jetpack WindowManager 不需要额外权限,但后台监听窗口变化会被系统节流,需绑定前台生命周期。
  7. Kotlin Flow/Channel 与 LiveData 的桥接:实现“窗口状态驱动 UI”的 MVVM 案例。

答案

给出可直接写进简历项目的“官方方案”代码骨架,并口头补充面试话术:

// 1. 依赖
implementation("androidx.window:window:1.2.0")

// 2. 在 Activity/Fragment 中
class MainActivity : AppCompatActivity() {
    private lateinit var windowInfoRepo: WindowInfoRepository
    private val scope = lifecycleScope + Dispatchers.Main.immediate

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        windowInfoRepo = WindowInfoRepository.create(this)
        // 3. 注册折叠状态回调
        scope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                windowInfoRepo.windowLayoutInfo
                    .collect { layoutInfo ->
                        val foldingFeature = layoutInfo.displayFeatures
                            .filterIsInstance<FoldingFeature>()
                            .firstOrNull()
                        val isFolded = when {
                            foldingFeature == null -> false               // 非折叠屏
                            foldingFeature.state == FoldingFeature.State.FLAT -> false
                            foldingFeature.isSeparating -> true           // 半折或全折
                            else -> false
                        }
                        // 4. 通知 UI 层刷新
                        viewModel.onFoldStateChanged(isFolded)
                    }
            }
        }
    }
}

面试话术: “国内项目里我会优先使用 Jetpack WindowManager,它在华为、荣耀、OPPO、vivo 等折叠屏机型上已内置适配,无需反射。通过 FoldingFeature.isSeparating 可以准确知道屏幕是否被铰链分隔,再叠加 smallestScreenWidthDp 判断展开后的真实宽度,就能完成‘折叠/展开/半折’三态适配。整个监听绑定在 lifecycleScope,防止泄漏,也符合 MVVM 单向数据流。”

拓展思考

  1. 多窗口/分屏场景:FoldingFeature 的 occlusionType 会返回 FULL,表示铰链遮挡区域,此时可自动把 RecyclerView 的 item 间距增大,避免内容被“折痕”截断。
  2. 桌面模式(Samsung DeX、华为电脑模式):虽然屏幕物理尺寸变大,但 FoldingFeature 为 null,需要额外检测 Configuration.uiMode 是否包含 UI_MODE_TYPE_DESK,防止误判成“展开”。
  3. 自动化测试:使用 androidx.test.uiautomator + WindowManagerShellHelper 模拟 HALF_OPENED 状态,验证折叠动画期间是否发生重启或闪屏。
  4. 性能:折叠瞬间会触发 Configuration change,如果 Activity 未声明 android:configChanges="screenSize|smallestScreenSize|screenLayout",会默认重建;在大项目里建议把 configChanges 交给 ViewModel + SavedStateHandle 处理,避免瞬间双重初始化。
  5. 隐私合规:国内应用常把“设备形态”埋点到大数据平台,需脱敏(只传 0/1/2 枚举),避免明文上传 FoldingFeature 原始对象,防止被认定为“读取硬件敏感信息”。