适用于折叠设备的 Jetpack 窗口管理器

Jetpack 窗口管理器提供了可用于所有可折叠设备的标准 API。 它包含两个重要的类:

  • DisplayFeature - 标识连续平面(如铰链或折叠)的中断。 窗口管理器将从布局更改回调返回显示功能的集合。
  • FoldingFeature - 提供有关设备的特定特征的信息。 虽然 Surface Duo 只有一个折叠特征,但其他设备可能有多个。

类似的指南位于 Android Foldable Codelab 中。 有关详细信息,请参阅 Android 文档中的开发可折叠设备。也可在 GitHub 上找到 Android 团队示例Jetpack 发行说明记录了窗口管理器更新时的更改。

提示

Surface Duo 双屏库中的控件和帮助程序类可与窗口管理器结合使用。 按照说明将正确的包添加到应用项目中。

若要直接在代码中使用窗口管理器,请按照以下说明进行操作:

添加依赖项

  1. 确保顶级 build.gradle 文件中有 mavenCentral() 存储库:

    allprojects {
        repositories {
            google()
            mavenCentral()
         }
    }
    
  2. 请确保在模块级别的 build.gradle 文件中将 compileSdkVersiontargetSdkVersion 设置为 API 31 或更新版本:

    android { 
        compileSdkVersion 31
    
        defaultConfig { 
            targetSdkVersion 31
        } 
        ... 
    }
    
    
  3. 在模块级别的 build.gradle 文件中添加以下依赖项:

    dependencies {
        implementation "androidx.window:window:1.0.0"
        implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
    }
    

在 Kotlin 代码中使用窗口管理器

访问 Kotlin 项目中的窗口管理器属性时,请务必设置正确的信息流。 否则,可能会收到过少或过多的事件更新,并且应用性能可能会受到影响。

若要初始化和使用 WindowInfoTracker 对象,请执行以下步骤:

  1. 在 MainActivity 类中,为 WindowInfoTracker 创建变量。 确保将 import androidx.window.layout.WindowInfoTracker 添加到文件顶部。

    class MainActivity : AppCompatActivity() {
        private lateinit var windowInfoTracker: WindowInfoTracker
    
  2. 初始化活动的 onCreate 方法中的 WindowInfoTracker,设置流以从 windowLayoutInfo 属性收集信息。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        // Initialize the window manager
        windowInfoTracker = WindowInfoTracker.getOrCreate(this@MainActivity)
    
        // Set up a flow
        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                windowInfoTracker.windowLayoutInfo(this@MainActivity)
                    .collect { 
                        // Check FoldingFeature properties here
                    }
            }
        }
    }
    

    确保将这些导入项也添加到文件顶部:

    import androidx.lifecycle.Lifecycle
    import androidx.lifecycle.lifecycleScope
    import androidx.lifecycle.repeatOnLifecycle
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.flow.collect
    import kotlinx.coroutines.launch
    
  3. 添加代码以检查 WindowLayoutInfo 流中的折叠特征属性。 运行此代码时,活动将更新为当前设备状态并显示特征(如果跨折叠或铰链显示)。

    在下面的代码片段中,活动根据 FoldingFeature 属性显示不同的文本。

    此示例有一个称为 layout_change_text 的 TextView,显示封闭类型和任何检测到的折叠特征的 isSeparating 值。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        windowInfoTracker = WindowInfoTracker.getOrCreate(this@MainActivity)
    
        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                windowInfoTracker.windowLayoutInfo(this@MainActivity)
                    .collect { newLayoutInfo ->
                        layout_change_text.text = "No display features detected"
                        for (displayFeature : DisplayFeature in newLayoutInfo.displayFeatures) {
                            if (displayFeature is FoldingFeature && displayFeature.occlusionType == FoldingFeature.OcclusionType.NONE) {
                                layout_change_text.text = "App is spanned across a fold, " +
                                    "isSeparating = ${displayFeature.isSeparating}"
                            }
                            if (displayFeature is FoldingFeature && displayFeature.occlusionType == FoldingFeature.OcclusionType.FULL) {
                                layout_change_text.text = "App is spanned across a hinge, " +
                                    "isSeparating = ${displayFeature.isSeparating}"
                            }
                        }
                    }
            }
        }
    }
    

折叠特征属性

WindowLayoutInfo 类具有一个 DisplayFeature 项的集合,其中的一个或多个项可以是 FoldingFeature 类的实例。

折叠特征具有以下属性:

  • bounds - 折叠特征的边界矩形的坐标
  • occlusionType - 如果折叠特征隐藏内容(FULLNONE
  • orientation - 折叠特征的方向(HORIZONTALVERTICAL
  • state - 折叠特征的角度(HALF_OPENEDFLAT
  • isSeparating - 如果折叠特征将显示区域分隔为两个不同的部分

可以查询这些属性,以决定如何在配置更改后调整布局。

isSeparating

在决定放置控件的位置或要显示的内容窗格数时,请使用 isSeparating 属性。 此字段可确保应用在所有可折叠设备上提供最佳用户体验:

  • 对于双屏设备,当应用跨铰链时,这始终为 true
  • 对于其他可折叠设备,仅当状态是 HALF_OPENED 时(例如设备处于桌面状态时)这为 true

使用 isSeparating 属性决定是让应用的 UI 布局适应可折叠设备,还是在没有分隔时使用默认 UI:

private fun updateCurrentLayout(newLayoutInfo: WindowLayoutInfo) {
   for (displayFeature in newLayoutInfo.displayFeatures) {
       val foldFeature = displayFeature as? FoldingFeature
       foldFeature?.let {
           if (it.isSeparating) {
               // The content is separated by the FoldingFeature.
               // Here is where you should adapt your UI.
           } else {
               // The content is not separated.
               // Users can see and interact with your UI properly.
           }
       }
   }
}

若要查看有关如何使用此字段的更详细示例,请查看此 isSeparating 示例

Google 还在其大屏幕和可折叠设备指南中提供了与此属性相关的文档示例

示例

surface-duo-jetpack-window-manager-samples GitHub 存储库包含大量 Kotlin 示例,它们演示使用 Jetpack 窗口管理器和传统视图系统生成的不同的双屏用户体验模式。

surface-duo-compose-samples GitHub 存储库还包含双屏 Kotlin 示例,该示例使用 Jetpack 窗口管理器,但在这些示例中,UI 是使用 Jetpack Compose 生成的。

Java API

请参阅 Jetpack 窗口管理器与 Java 博客文章和该 Java 示例,了解如何通过 WindowInfoTrackerCallbackAdapter 访问 WindowInfoTracker 类。

资源