可折叠设备布局

重要

本文介绍的功能和指南为公共预览版,在正式发布之前可能会有重大修改。 Microsoft 不对此处提供的信息作任何明示或默示的担保。

重要

只有在将活动根视图附加到窗口后,才可看到此视图的内容。 之所以是这种情况是因为实现基于窗口管理器库,并且窗口管理器仅在视图附加到窗口后才会正常工作。

FoldableLayout 组件是一种自定义布局,当应用程序在可折叠设备上运行时,该组件可以不同方式定位其子元素。 该组件可以将屏幕拆分为两个区域,具体取决于折叠功能的位置和方向。 拆分屏幕后,可以并排保留两个子容器,也可以将一个子容器置于另一个容器之上。

单屏布局 双屏布局
宽屏模式中单个屏幕上的应用 宽屏模式中横跨两个屏幕的应用
竖屏模式中单个屏幕上的应用 竖屏模式中横跨两个屏幕的应用

XML 属性

  • single_screen_layout_id - 接收要在单屏模式下显示的布局 ID。
  • dual_screen_start_layout_id - 接收要在双屏模式的启动容器中显示的布局 ID。
  • dual_screen_end_layout_id - 接收要在双屏模式的启动容器中显示的布局 ID。

当应用程序在可折叠设备上运行时,FoldableLayout 还可以创建单个容器。

我们添加了四个新属性,可帮助你创建所需的 UI:

  • dual_portrait_single_layout_id - 接收双纵向单容器的布局 ID。
  • is_dual_portrait_single_container - 创建空的双纵向单容器。
  • dual_landscape_single_layout_id - 接收双横向单容器的布局 ID。
  • is_dual_landscape_single_container - 创建空的双横向单容器。

smallestScreenSize 支持

当某项活动过渡到新的屏幕模式但未重新创建它(因为在清单文件中设置了标志 smallestScreenSize)时,布局将检测新配置并自动调整容器的大小,甚至根据 FoldableLayout 配置添加或删除第二个容器。

Android Studio 预览版

  • tools_hinge_color - 在布局预览中选择铰链颜色。
  • tools_screen_mode - 在布局预览中选择屏幕模式。

如果将 xml 文件中的片段添加到 FoldableLayout,将需要以下属性来预览片段 UI:

  • show_in_single_screen
  • show_in_dual_screen_start
  • show_in_dual_screen_end
  • show_in_dual_portrait_single_container
  • show_in_dual_landscape_single_container

容器 ID

容器具有 ID:

  • first_container_id
  • second_container_id

无论屏幕方向或设置的布局特殊行为是什么,如果布局仅显示一个容器,它的 ID 将是 first_container_id。 如果它显示两个容器,则将是 second_container_id

例如:

  • 如果应用程序处于单屏模式,则容器 ID 将是 first_container_id
  • 如果已完成双纵向中向双屏模式的转换,且我们已设置 dual_portrait_single_layout_idis_dual_portrait_single_container,则双屏模式中将仅有一个容器,它的 ID 将仍是 first_container_id
  • 如果已完成向双横向的转换,但我们没有设置属性 dual_landscape_single_layout_idis_dual_landscape_single_container,则 ID 为 first_container_idsecond_container_id 的两个容器都会存在。

在代码中创建 FoldableLayout

findViewById<FrameLayout>(R.id.parent).addView(
    FoldableLayout(this, FoldableLayout.Config().apply {
        singleScreenLayoutId = R.layout.single_screen
        dualScreenStartLayoutId = R.layout.dual_screen_start
        dualScreenEndLayoutId = R.layout.dual_screen_end
        dualLandscapeSingleLayoutId = R.layout.single_screen
    })
)

替换 FoldableLayout 配置

下面的代码将放弃旧配置,将其替换为新配置,并使用新配置来显示视图。

findViewById<FoldableLayout>(R.id.surface_duo_layout)
    .newConfigCreator()
    .singleScreenLayoutId(R.layout.single_screen)
    .dualScreenStartLayoutId(R.layout.dual_screen_start)
    .dualScreenEndLayoutId(R.layout.dual_screen_end)
    .reInflate()

更新 FoldableLayout 配置

下面的代码将用所选属性更新当前配置,并显示视图:

findViewById<FoldableLayout>(R.id.surface_duo_layout)
    .updateConfigCreator()
    .dualScreenStartLayoutId(R.layout.dual_screen_start)
    .reInflate()

如何使用布局组件

若要创建应用程序,可以使用某个活动或某个包含片段的活动来处理 UI。 也可在布局资源文件中声明片段,或者直接在活动中创建片段。 稍后我们将讨论组件如何处理这些情况。

流程图

使用活动

在这里,我们只使用一个活动来处理 UI,通过这种方式了解组件的工作原理。

  1. 首先,必须将 FoldableLayout 添加到活动的 *.xml 文件中。

    <com.microsoft.device.dualscreen.layouts.FoldableLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/enlightened_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:animateLayoutChanges="true"
        app:single_screen_layout_id="@layout/single_screen_layout"
        app:dual_screen_start_layout_id="@layout/single_screen_layout"
        app:dual_screen_end_layout_id="@layout/dual_screen_end_layout"/>
    
  2. 然后,创建三个适用于不同屏幕模式的布局。

  3. 将布局链接到活动。

    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
        }
    }
    

使用在资源文件中声明的片段

在这里,我们将了解组件如何使用 *.xml 文件中声明的片段。

  1. 首先,必须将 FoldableLayout 添加到活动的 *.xml 文件中。

    <com.microsoft.device.dualscreen.layouts.FoldableLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:single_screen_layout_id="@layout/single_screen_layout"
        app:dual_screen_start_layout_id="@layout/dual_screen_start_layout"
        app:dual_screen_end_layout_id="@layout/dual_screen_end_layout" />
    
  2. 接下来,将片段声明到 singlescreenlayout.xmldualscreenstartlayout.xmldualscreenendlayout.xml 文件中。

    <fragment
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/single_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.microsoft.device.display.samples.contentcontext.MapPointListFragment" />
    
  3. 将布局链接到活动。

    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
        }
    }
    

使用在活动中创建的片段

最后,我们将了解组件如何使用通过 getSupportFragmentManager().beginTransaction() 添加的片段。

  1. 首先,必须将 FoldableLayout 添加到活动的 *.xml 文件中。

    <com.microsoft.device.dualscreen.layouts.FoldableLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    

    这会为视图创建容器,但不会将其放大。

  2. 可以使用容器 ID 访问 FoldableLayout 的容器:

    • first_container_id
    • second_container_id
  3. 接下来,请访问片段管理器状态处理程序库,因为在处理屏幕模式转换时需要特别谨慎地处理片段。

查看绑定

重要

请使用 findViewById,如下面的代码片段中所示。 请勿尝试使用 Google View 绑定

由于当 WindowManager 包含有关窗口布局的可用信息时 FoldableLayout 内容会膨胀,因此无法检索子实例并使用它。 为了绑定子视图,首先需要确保 FoldableLayout 内容已能够使用。 为此,应使用以下方法:

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    private val contentChangedListener = object : FoldableLayout.ContentChangedListener {
        override fun contentChanged(screenMode: ScreenMode?) {
            // Here, you can retrieve the child instance
            val child = binding.foldableLayout.findViewById<View>(R.id.child_view)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }

    override fun onResume() {
        super.onResume()
        binding.foldableLayout.addContentChangedListener(contentChangedListener)
    }

    override fun onPause() {
        super.onPause()
        binding.foldableLayout.removeContentChangedListener(contentChangedListener)
    }
}

其中:

  • FoldableLayout.ContentChangedListener 是在 FoldableLayout 内容膨胀后将要调用的回调。

  • FoldableLayout.addContentChangedListener 将注册给定的 FoldableLayout.ContentChangedListener 回调。

  • foldableLayout.removeContentChangedListener 将取消注册给定的 FoldableLayout.ContentChangedListener 回调。

重要

需要从 ActivityFragment 中将此代码添加到 onResumeonPause 方法中,以避免内存泄漏。