# 平行视界

#### 概述

平行视界是针对应用在未适配分栏布局的场景下，通过标准化配置实现宽屏、大屏设备分栏显示的系统级兼容方案。开启平行视界并分栏显示时，应用会在一个窗口中同时显示两个页面，默认情况下，两页按1:1平分窗口，如下图所示。其中左侧固定为主页，页面路由的跳转只发生在右侧，右侧显示跳转后的详情类页面。从API 23版本开始，支持开发者自配置。

平行视界适用于办公、邮箱、IM即时通讯、电商等需要频繁切换页面的应用。当前平行视界支持两种路由模式：导航模式和购物模式。以购物类应用在双折叠上运行为例：

*   导航模式：左侧主页（商品分类页）保持不变，右侧展示所选分类的商品或具体商品详情，后续相关操作均在右侧进行；
*   购物模式：页面路由跳转时右侧页面始终向左推入，屏幕右侧展示路由栈**栈顶页面**，左侧展示路由栈**次栈顶页面**。

| 路由模式 | 导航模式 | 购物模式 |
| --- | --- | --- |
| 效果图 |  |  |

本文面向中高级开发者，在开始学习之前，请完成以下前置操作：

1.  环境要求：安装最新版DevEco Studio，确保SDK版本不低于6.1.0(23)。
2.  知识储备：了解鸿蒙开发基础，掌握分栏布局、设置组件导航和页面路由等相关知识。

本文主要内容如下：

1.  开发步骤：针对API 23及以上版本，详细介绍平行视界的完整适配流程。
2.  配置内容说明：深入讲解平行视界核心配置文件中各关键参数的含义、取值范围及配置规则，为开发者提供清晰、规范的配置指引。
3.  典型开发场景：以Navigation路由实现的购物类应用为载体，围绕平行视界高频应用场景，提供可复用的场景化解决方案。
4.  常见问题：梳理平行视界开发中的高频问题，剖析问题产生的核心原因，并给出解决方案及避坑建议，帮助开发者高效解决实战中的各项问题。
5.  示例代码：提供可直接运行的项目代码，供开发者下载，结合本文内容配合使用。

#### 开发步骤

1.  增加配置文件。
    
    在profile目录下创建兼容方案的配置文件easy\_go.json（示例文件名，可自行命名）。在module.json5配置文件中添加easyGo字段，并指向引用的easy\_go.json配置文件。当前仅支持在entry模块下配置，配置后应用级生效。
    
2.  在easy\_go.json配置文件中，配置平行视界的相关属性，详情可参考配置内容说明。

#### 配置内容说明

easy\_go.json是一个标准的Object类型JSON文件，整体结构分为两层。第一层配置设备类型；第二层配置对应设备类型下的显示模式。

#### 设备类型

一层配置，设置平行视界在不同设备类型下的表现。

```
{ 
  "common": {},
  "phone": {},
  "tablet": {}
}
```

| 字段名 | 说明 | 可选 | 字段类型 |
| --- | --- | --- | --- |
| common | 通用设备配置，为所有设备类型提供基础默认配置。 | 否 | displayModeOptions |
| phone | phone类型设备上生效的配置，配置后common配置在phone类型设备上不再生效。 | 是 | displayModeOptions |
| tablet | tablet类型设备上生效的配置，配置后common配置在tablet类型设备上不再生效。 说明： 自由多窗模式下，暂不支持平行视界。 | 是 | displayModeOptions |

#### 显示模式

二层配置displayModeOptions字段，设置平行视界的显示模式，内部字段说明如下：

| 字段名 | 说明 | 可选 |
| --- | --- | --- |
| wideWindowMode | 控制应用在比直板机宽的长方形宽屏窗口（>= 600vp，宽/高 > 1.2）上的显示方式。 | 否 |
| squareWindowMode | 控制应用在比直板机宽的方形宽屏窗口（>= 600vp，高/宽 <= 1.2，宽/高 <= 1.2）上的显示方式。 | 是 |
| routerSplitOptions | 平行视界使用Router分栏时的配置。 | 是 |
| navigationSplitOptions | 平行视界使用Navigation分栏时的配置。 | 是 |

wideWindowMode/squareWindowMode内部字段说明：

| 字段名 | 说明 |
| --- | --- |
| navigationSplit | 代表应用路由由navigation组件实现，必须配合navigationSplitOptions字段一起使用。 |
| routerSplit | 代表应用路由由router模块实现，必须配合routerSplitOptions字段一起使用。 |
| original | 关闭窗口显示模式的所有兼容运行行为。 |

routerSplitOptions/navigationSplitOptions内部可包含的字段说明：

| 字段名 | 说明 | 数据类型 | 可选 |
| --- | --- | --- | --- |
| homePage | 主页名称，不配置时系统采用默认的策略进行主页识别。 说明： 对于Navigation路由，如果将NavDestination做为主页，则配置为NavDestination的name；如果将Navigation首页作为主页，则配置为"navBar"。建议将Navigation实际主页配置为homePage。 对于Router路由，配置为页面绝对路径，由配置文件中pages列表提供，例如"pages/Index"。 | 字符串 | 是 |
| relatedPage | 关联页名称，不配置时没有关联页能力。 说明： 内容的格式和homePage保持一致。必须有homePage才能配置relatedPage。不支持传递参数，建议配置无需动态参数的静态页面作为关联页。 | 字符串 | 是 |
| enableReducedContainerSize | 是否开启虚拟容器能力，默认值为false。 - false：lpx单位、页面中横向断点、窗口宽度尺寸及屏幕宽度尺寸将使用原始尺寸进行计算。 - true： lpx单位、页面中横向断点、窗口宽度尺寸及屏幕宽度尺寸均使用原始尺寸的缩小比例，按照右侧页面尺寸计算。 说明： 开启后在平行视界分栏时，整个应用内生效。平行视界退出分栏时（如：进入全屏页）自动失效。 | 布尔值 | 是 |
| fullScreenPages | 支持全屏显示的页面数组，跳转到数组中的页面时，退出分栏显示。默认值为空数组。 说明： 数组中每一项内容的格式和homePage保持一致，且不能和homePage、relatedPage重复。 | 字符串数组 | 是 |
| supportLandscapeFullscreen | 应用主动请求横屏时是否全屏显示，默认值为true。 - true： 当应用主动请求横屏时，会退出分栏全屏显示。 - false： 当应用主动请求横屏时，仍然保持平行视界效果。 | 布尔值 | 是 |
| wideSplit | 比直板机宽的长方形窗口（>= 600vp，宽/高>1.2）上的配置参数，包含字段：ratio。 从API version 26开始，支持该标签。 | 对象 | 是 |
| squareSplit | 比直板机宽的方形窗口（>= 600vp，高/宽 <= 1.2，宽/高 <= 1.2）上的配置参数，包含字段：ratio。 从API version 26开始，支持该标签。 | 对象 | 是 |
| mode | 路由模式，类型为非空整数，默认值为1。 - 0：购物模式。 - 1：导航模式。 从API version 26开始，支持该标签。 | 数值 | 是 |
| transPages | 过渡页面，在购物模式下生效，配置此标签的页面将固定在右侧显示，不支持右推左。默认值为空数组。 说明： NavDestinationMode为DIALOG类型的NavDestination页面，默认属于过渡页面。 数组中每一项内容的格式和homePage保持一致，且不能和homePage、relatedPage、fullScreenPages重复。 从API version 26开始，支持该标签。 | 字符串数组 | 是 |
| splitDividerColor | 分割线颜色，支持配置深浅色模式，包含可选字段：light和dark。 从API version 26开始，支持该标签。 | 对象 | 是 |
| drawableRectHook | WindowProperties.drawableRect是否开启页面级容器能力，默认值为false - false：Window.drawableRect将使用原始尺寸进行计算。 - true：Window.drawableRect将使用原始尺寸的缩小比例，按照右侧页面尺寸计算。 从API version 26开始，支持该标签。 | 布尔值 | 是 |
| enableInSplitScreen | 是否支持在窗口分屏场景进入平行视界，默认值为false。 - false：分屏时始终不进入平行视界。 - true：分屏时窗口宽高若满足条件则进入平行视界。 从API version 26开始，支持该标签。 | 布尔值 | 是 |

navigationSplitOptions中可额外包含如下几个字段：

| 字段名 | 说明 | 数据类型 | 可选 |
| --- | --- | --- | --- |
| homeNavigationId | 分栏Navigation的id（组件通用属性），不配置时使用最外层Navigation进行分栏。 推荐开发者配置为做全局路由的Navigation的id，如果配置为其他Navigation，可能会导致布局异常。 | 字符串 | 是 |
| disablePlaceholder | 是否隐藏占位页，默认值为false。 - false：不隐藏占位页。 - true：隐藏占位页。 | 布尔值 | 是 |
| disableDivider | 是否隐藏分割线，默认值为false。 - false：不隐藏分割线。 - true：隐藏分割线。 | 布尔值 | 是 |

wideSplit/squareSplit内部字段说明：

| 字段名 | 说明 | 数据类型 | 可选 |
| --- | --- | --- | --- |
| ratio | 左右页面比例，数据格式为"正整数 \| 正整数"，范围限制在1:2到2:1之间，未配置时默认为1:1，配置超出范围时默认为范围边界值。 从API version 26开始，支持该标签。 | 字符串 | 是 |

splitDividerColor内部字段说明：

| 字段名 | 说明 | 数据类型 | 可选 |
| --- | --- | --- | --- |
| light | 浅色模式颜色，类型为十六进制字符串，格式为#AARRGGBB。 从API version 26开始，支持该标签。 | 字符串 | 是 |
| dark | 深色模式颜色，类型为十六进制字符串，格式为#AARRGGBB。 从API version 26开始，支持该标签。 | 字符串 | 是 |

*   routerSplitOptions和navigationSplitOptions不能同时存在。开启平行视界后，混用Router和Navigation会导致部分行为异常，因此不建议在启用平行视界的应用中混用两种路由框架。
*   分割线颜色默认支持深浅色模式。如果应用不适配深色模式，需要配置light和dark为相同颜色。

#### 配置示例

1.  Router路由下，为通用设备配置平行视界效果。
    
    *   通过common为通用设备配置平行视界效果。
    *   将wideWindowMode及squareWindowMode字段设置为"routerSplit"，代表应用路由由router模块实现。
    *   配置routerSplitOptions字段，按需设置主页、关联页、全屏页等字段。
    
    ```
    {
      "common": {
        "displayModeOptions": {
          "wideWindowMode": "routerSplit",
          "squareWindowMode": "routerSplit",
          "routerSplitOptions": {
            "homePage": "pages/Index",
            "relatedPage": "pages/CategoryPage",
            "fullScreenPages": [
              "pages/FullScreenImagePage"
            ],
            "supportLandscapeFullscreen": true,
            "enableReducedContainerSize": true
          }
        }
      }
    }
    ```
    
2.  Navigation路由下，为通用设备配置平行视界效果。
    
    *   通过common为通用设备配置平行视界效果。
    *   将wideWindowMode及squareWindowMode字段设置为"navigationSplit"，代表应用路由由navigation组件实现。
    *   配置navigationSplitOptions字段，按需设置主页、关联页、全屏页、路由模式等字段。在平板/三折叠等长方形窗口下设置左右屏幕比例为1：2，在双折叠等方形窗口下设置左右屏幕比例为1：1。
    
    ```
    {
      "common": {
        "displayModeOptions": {
          "wideWindowMode": "navigationSplit",
          "squareWindowMode": "navigationSplit",
          "navigationSplitOptions": {
            "homePage": "navBar",
            "relatedPage": "CategoryPage",
            "fullScreenPages": [
              "FullScreenImagePage"
            ],
            "supportLandscapeFullscreen": true,
            "enableReducedContainerSize": true,
            "wideSplit": {
              "ratio": "1 | 2"
            },
            "squareSplit": {
              "ratio": "1 | 1"
            },
            "mode": 0,
            "splitDividerColor": {
              "light": "#33FFFFFF",
              "dark": "#33000000"
            },
            "drawableRectHook": true,
            "enableInSplitScreen": true
          }
        }
      }
    }
    ```
    
3.  以Navigation路由为例，为tablet设备单独关闭平行视界效果。
    
    *   增加tablet设备配置。
    *   将wideWindowMode、squareWindowMode字段设置为"original"，关闭窗口显示模式的所有兼容运行行为。
    
    ```
    {
      "common": {
        "displayModeOptions": {
          "wideWindowMode": "navigationSplit",
          "squareWindowMode": "navigationSplit",
          "navigationSplitOptions": {
            "homePage": "CategoryPage",
            "fullScreenPages": [
              "FullScreenImagePage"
            ],
            "supportLandscapeFullscreen": true,
            "enableReducedContainerSize": false
          }
        }
      },
      "tablet": {
        "displayModeOptions": {
          "wideWindowMode": "original",
          "squareWindowMode": "original"
        }
      }
    }
    ```
    

#### 典型开发场景

本章以Navigation路由实现的购物类应用为载体，介绍平行视界适配开发中的四个典型场景，并讲解如何通过easy\_go.json配置文件进行配置。

#### 配置主页与关联页

**场景描述**

默认情况下，系统会将应用的某个页面识别为主页，但是该机制在有些场景下不能准确识别主页。建议开发者主动配置主页，需要的话，还可以配置关联页。

例如在购物类应用中，开发者可以将首页设置为主页，首个商品分类页设置为启动关联页。

| 场景 | 同时配置主页和关联页 | 只配置主页 |
| --- | --- | --- |
| 效果图 |  |  |

**实现原理**

平行视界提供配置主页及关联页能力。通过homePage属性设置主页，通过relatedPage属性设置关联页。

**开发步骤**

1.  在easy\_go.json配置文件中，将homePage属性设置为"navbar"，表示将Navigation首页设置为主页。将relatedPage属性设置为"CategoryPage"，表示将分类页设置为关联页。具体配置文件如下：

```
{
  "common": {
    "displayModeOptions": {
      "wideWindowMode": "navigationSplit",
      "squareWindowMode": "navigationSplit",
      "navigationSplitOptions": {
        "homePage": "navBar",
        "relatedPage": "CategoryPage"
      }
    }
  }
}
```

#### 路由跳转过程请求全屏

**场景描述**

在商品详情页，为清晰呈现商品细节，点击商品图片后，需将图片全屏展示。

**实现原理**

平行视界提供了全屏页能力，支持通过fullScreenPages字段指定全屏页。配置后，对应页面展示时，将暂时退出分栏模式，切换为全屏显示。页面隐藏时，恢复分栏显示。

**开发步骤**

1.  在平行视界配置文件的fullScreenPages字段中，加入图片浏览页。

```
{
  "common": {
    "displayModeOptions": {
      ...
      "fullScreenPages": [
         "FullScreenImagePage"
      ],
    }
  }
}
```

#### 路由跳转过程请求横屏显示

**场景描述**

1.  在商品详情页中，“参数对比”、“配置表”类图片往往需要横屏展示，以解决竖屏模式下文字过小等问题。

**实现原理**

平行视界支持通过配置supportLandscapeFullscreen字段实现横屏时全屏显示页面。在easy\_go.json配置文件中配置该字段值为true后，当应用请求横屏时，会退出分栏，全屏展示页面。

**开发步骤**

1.  在easy\_go.json配置文件中，将supportLandscapeFullscreen属性设置为true，配置后，当页面申请横屏时，将退出分栏模式，切换为全屏显示。

```
{
  "common": {
    "displayModeOptions": {
      ...
      "supportLandscapeFullscreen": true
    }
  }
}
```

#### 开启虚拟容器能力

**场景描述**

在开启平行视界并分栏显示时，一个窗口内部会同时显示两个页面，两个页面默认按照1:1平分窗口大小。如果页面内的元素布局使用窗口宽度，可能会导致UI元素超出页面范围被截断，开发者可以开启虚拟容器能力，此时lpx单位、页面中横向断点、窗口宽度尺寸将使用原始尺寸的一半进行计算。

**开发步骤**

1.  在easy\_go.json配置文件中，将enableReducedContainerSize属性设置为true，获取平行视界后的断点。

```
{
  "common": {
    "displayModeOptions": {
      ...
      "enableReducedContainerSize": true
    }
  }
}
```

#### 常见问题

#### UI元素超出页面范围，页面元素被截断

**问题描述**

开启平行视界并分栏显示时，页面内的元素超出页面范围，导致被截断，如下图所示：

**原因分析**

在开启平行视界并分栏显示时，一个窗口内部会同时显示两个页面，两个页面默认按照1:1平分窗口大小。如果页面内的元素布局使用窗口宽度，可能会导致UI元素超出页面范围。

**解决方案**

建议采取以下策略：

1.  优先使用系统组件的自适应布局能力：自适应布局。
2.  开启虚拟容器能力，在平行视界配置文件中，将enableReducedContainerSize设置为true。此时，lpx单位、页面中横向断点、窗口宽度尺寸和屏幕宽度尺寸将使用原始尺寸的一半进行计算。
3.  使用页面大小获取接口和无感监听能力：
    
    on('navDestinationUpdate')：监听NavDestination组件的状态变化，API 23版本及以上，返回的当前NavDestination组件状态中会包含NavDestination组件的大小。
    
    on('routerPageUpdate')： 监听Router中page页面的状态变化，API 23版本及以上，返回的当前Router页面的信息中会包含routerPage页面的大小。
    
    onRouterPageSizeChange： 当可见的Router页面大小发生变化时，会触发该回调函数。
    
    onNavDestinationSizeChange：当可见的NavDestination大小发生变化时，会触发该回调函数。
    

#### 组件复用场景下，同一资源只在一个页面中显示

**问题描述**

左右两个分栏页面中，同一资源只在一页面中显示，如下图所示：

**原因分析**

如果左右两个分栏页面利用ArkUI的组件复用机制（例如：NodeContainer）共用了同一个UI资源，会导致对应的资源同一时刻只能在一个页面中显示。

**解决方案**

建议改为两边页面使用独立的UI资源，互不影响，详情请参考自定义组件复用开发实践。

#### 使用页面级窗口策略不生效

**问题描述**

平行视界场景下，使用NavDestination组件提供preferredOrientation属性设置页面方向时，未生效。

**原因分析**

平行视界场景下，会同时显示左右两个页面，两个页面方向需要一致。

**解决方案**

推荐使用窗口级方案，通过window提供的setPreferredOrientation接口设置窗口策略。

#### 应用设置一镜到底等转场效果不生效

**问题描述**

平行视界场景下，应用设置的一镜到底等转场效果不生效。

**原因分析**

应用接入平行视界后，部分场景动效会被系统屏蔽或修改。如：

1.  Router类型的平行视界，系统会屏蔽所有自定义转场。
2.  Navigation类型的平行视界，在主页首次跳转到二级页面或者二级页面返回到首页时，系统会屏蔽所有转场，默认无动画跳转；购物模式场景下，系统会屏蔽自定义转场，默认右推左或左推右实现平移转场。

**解决方案**

平行视界场景下，涉及到以上转场场景时，建议关闭自定义转场，遵循系统默认效果。

#### 示例代码

平行视界