让应用具备折叠感知能力

借助展开的大显示屏和独特的折叠状态,我们能够在可折叠设备上打造全新用户体验。如需在 Jetpack Compose 中让应用具备折叠感知能力,请使用 Compose Material 3 Adaptive 库,它为可折叠设备的窗口功能(如折叠边、合页和设备姿态)提供了一些 API。如果应用具备折叠感知能力,就能调整其布局,避免将重要内容放在折叠边或合页区域,并将折叠边或合页用作自然分隔符。

了解设备是否支持桌面或图书折叠状态等配置,有助于决定是否支持不同的布局或提供特定功能。

窗口信息

在 Jetpack Compose 中,使用 collectFoldingFeaturesAsState() 可组合函数来观察窗口折叠信息。collectFoldingFeaturesAsState()WindowInfoTracker 中收集当前窗口折叠功能,并将其放入包含 FoldingFeature 对象列表的 Compose State 中。

由于 collectFoldingFeaturesAsState() 会返回一个 Compose State,因此每当设备折叠状态或姿态发生变化时,您的可组合函数都会自动重组,而无需手动进行生命周期管理或流收集:

@Composable
fun FoldAwareScreen() {
    val foldingFeatures by collectFoldingFeaturesAsState()

    // Access folding features for the current window.
    val foldFeature = foldingFeatures.firstOrNull()
    if (foldFeature != null) {
        // Adapt layout based on the fold feature.
    }
}

可折叠设备显示屏的功能

collectFoldingFeaturesAsState()DisplayFeature 的子类型)返回的 FoldingFeature 元素列表提供了有关可折叠设备显示屏的信息,其中包括以下属性:

  • state:设备的折叠状态,即 FLATHALF_OPENED

  • orientation:折叠边或铰链的方向,即 HORIZONTALVERTICAL

  • occlusionType:折叠边或铰链是否遮住了显示屏的一部分,即 NONEFULL

  • isSeparating:折叠边或铰链是否创建了两个逻辑显示区域,即 true 或 false

HALF_OPENED 的可折叠设备始终将 isSeparating 报告为 true,因为屏幕被分为两个显示区域。此外,当应用跨两块屏幕时,isSeparating 在双屏设备上始终为 true。

FoldingFeature bounds 属性(继承自 DisplayFeature)表示折叠功能的边界矩形(如折叠边或铰链)。可以使用边界将元素放到与功能相关的屏幕上:

@Composable
fun FoldFeatureDemo() {
    val foldingFeatures by collectFoldingFeaturesAsState()
    val foldFeature = foldingFeatures.firstOrNull()

    if (foldFeature != null) {
        val isSeparating = foldFeature.isSeparating
        val occlusionType = foldFeature.occlusionType
        val bounds = foldFeature.bounds
        // Use bounds and properties to position content.
    }
}

桌面姿态

利用 FoldingFeature 对象中包含的信息,您的应用可以支持桌面模式等姿势。在这种模式下,手机置于平面上、铰链处于水平位置,可折叠屏幕处于半开状态。

桌面姿势让用户无需拿着手机就能轻松操作手机。桌面姿势适合在观看媒体内容、拍照和进行视频通话时使用。

处于桌面姿态的视频播放器应用,视频播放位于屏幕的竖直部分,播放控件位于屏幕的水平部分。
图 1. 桌面姿势下的视频播放器应用 - 视频位于屏幕的竖直部分;播放控件位于水平部分。

使用 collectFoldingFeaturesAsState() 中的 FoldingFeature.StateFoldingFeature.Orientation 确定设备是否处于桌面模式:

@Composable
fun TabletopContent() {
    val foldingFeatures by collectFoldingFeaturesAsState()
    val foldFeature = foldingFeatures.firstOrNull()

    val isTableTopPosture = foldFeature != null &&
        foldFeature.state == FoldingFeature.State.HALF_OPENED &&
        foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL

    if (isTableTopPosture) {
        // Tabletop layout: separate content and controls across the fold.
    } else {
        // Standard layout.
    }
}

或者,您也可以通过检查 currentWindowAdaptiveInfoV2() 返回的 Posture 属性来确定窗口是否处于桌面姿态:

val adaptiveInfo = currentWindowAdaptiveInfoV2()
val isTabletop = adaptiveInfo.windowPosture.isTabletop

确定设备处于桌面模式后,应相应地更新应用布局。对于媒体应用,这通常意味着将播放媒体置于折线上方,控件和补充内容置于下方,从而提供免触摸观看或收听体验。

示例

图书模式

可折叠设备的另一种独特的折叠状态是图书模式。在这种模式下,设备处于半开状态,并且铰链是垂直的。图书模式非常适合阅读电子书。在可像装订版书籍一样打开的大屏可折叠设备上,图书姿势会采用双页布局,为您带来如同阅读纸质图书一般的体验。

如果您想在免触摸拍照时以不同的宽高比拍摄照片,也可以使用该模式进行摄影。

您可以采用与桌面姿势相同的技术实现图书姿势。检查折叠功能应用的屏幕方向是否为垂直,而非水平:

@Composable
fun BookContent() {
    val foldingFeatures by collectFoldingFeaturesAsState()
    val foldFeature = foldingFeatures.firstOrNull()

    val isBookPosture = foldFeature != null &&
        foldFeature.state == FoldingFeature.State.HALF_OPENED &&
        foldFeature.orientation == FoldingFeature.Orientation.VERTICAL

    if (isBookPosture) {
        // Book posture layout: two-page layout across the vertical fold.
    } else {
        // Standard single-page layout.
    }
}

窗口大小变化

应用的显示区域可能会因设备配置变更(例如设备折叠或展开、旋转,或在多窗口模式下调整大小)而发生变化。

如需检索当前的窗口大小类别并适应 Jetpack Compose 中的大小变化,请使用 Compose Material 3 Adaptive 库中的 currentWindowAdaptiveInfoV2() 可组合函数。currentWindowAdaptiveInfoV2() 返回一个 WindowAdaptiveInfo 实例,该实例提供当前窗口的 WindowSizeClass(默认支持大型和超大型断点),以及设备的 windowPosture

@Composable
fun AdaptiveApp() {
    val adaptiveInfo = currentWindowAdaptiveInfoV2()
    val windowSizeClass = adaptiveInfo.windowSizeClass

    when {
        windowSizeClass.isWidthAtLeastBreakpoint(
            WIDTH_DP_EXPANDED_LOWER_BOUND
        ) -> {
            // Expanded layout
        }
        windowSizeClass.isWidthAtLeastBreakpoint(
            WIDTH_DP_MEDIUM_LOWER_BOUND
        ) -> {
            // Medium layout
        }
        else -> {
            // Compact layout
        }
    }
}

请参阅使用窗口大小类别

其他资源

如需详细了解折叠感知布局和可折叠设备,请参阅以下资源:

示例

Codelab