1# 虚拟屏的创建和使用 (ArkTS) (仅对系统应用开放)
2
3## 场景介绍
4
5虚拟屏是系统中的一种抽象,允许应用创建一个虚拟显示设备,不依赖于物理显示器。它通过Surface提供一个渲染目标,应用可以将图像、视频等内容渲染到这个虚拟显示设备上。
6
7## 接口说明
8
9以下为虚拟屏的常用接口。更多接口说明请参考[ @ohos.screen (屏幕)(系统接口)](../reference/apis-arkui/js-apis-screen-sys.md)。
10
11| 接口名                                                       | 描述                                         |
12| ------------------------------------------------------------ | -------------------------------------------- |
13| createVirtualScreen(options:VirtualScreenOption): Promise<Screen> | 创建虚拟屏幕,使用Promise异步回调。          |
14| setVirtualScreenSurface(screenId:number, surfaceId: string): Promise<void> | 设置虚拟屏幕的Surface,使用Promise异步回调。 |
15| makeMirror(mainScreen:number, mirrorScreen:Array<number>): Promise<number> | 将屏幕设置为镜像模式,使用Promise异步回调。  |
16| stopMirror(mirrorScreen:Array<number>): Promise<void> | 停止屏幕的镜像模式,使用Promise异步回调。    |
17| destroyVirtualScreen(screenId:number): Promise<void>   | 销毁虚拟屏幕,使用Promise异步回调。          |
18
19## 开发步骤
20
211. 创建虚拟屏。
22
23   - 定义用于创建虚拟屏的各项参数。
24
25   - 通过createVirtualScreen()创建虚拟屏。
26
27   需特别注意的是,使用createVirtualScreen()创建虚拟屏时,需要申请`ohos.permission.CAPTURE_SCREEN`权限,配置方式请参见[申请使用受限权限](../security/AccessToken/declare-permissions-in-acl.md)。
28
292. 将渲染目标SurfaceID绑定在虚拟屏上。
30
31   为了使虚拟屏能够准确呈现所需的图像内容,需要将渲染目标的SurfaceID绑定到虚拟屏上。具体操作分为两步:
32
33   - 首先,通过getXComponentSurfaceId()方法获取Surface的唯一标识符SurfaceID。SurfaceID作为一个关键标识,承载着与屏幕图像信息相关的重要数据,借助它可以获取并灵活设置当前屏幕的各种图像属性,例如图像的分辨率、像素格式等。
34
35   - 然后,使用setVirtualScreenSurface()方法将获取到的SurfaceID与虚拟屏进行关联设置,从而确保虚拟屏能够正确接收和处理对应的图像数据。
36
373. 通过makeMirror()创建物理屏的镜像,并映射到虚拟屏上。
38
39   在成功创建虚拟屏之后,应用程序可依据具体的业务需求,通过调用makeMirror()方法来开启物理屏的镜像功能。该方法会将物理屏幕上正在显示的内容完整地复制,并映射到之前创建的虚拟屏上,实现物理屏与虚拟屏之间的实时同步显示。
40
414. 停止镜像。
42
43   当不再需要物理屏与虚拟屏之间的镜像同步功能时,可通过调用stopMirror()方法来停止镜像。这一操作将终止物理屏内容向虚拟屏的映射过程,使虚拟屏恢复到独立状态。
44
455. 销毁虚拟屏。
46
47   当整个业务流程结束,不再需要使用虚拟屏时,应及时释放相关资源,以避免内存泄漏和系统资源的浪费。通过调用destroyVirtualScreen接口,可安全、有效地将虚拟屏销毁,释放其所占用的系统资源。
48
49```ts
50import { BusinessError } from '@kit.BasicServicesKit';
51import { screen } from '@kit.ArkUI';
52
53@Entry
54@Component
55struct VirtualScreen {
56  xComponentController: XComponentController = new XComponentController();
57
58  build() {
59    RelativeContainer() {
60      Column() {
61        XComponent({
62          type: XComponentType.SURFACE,
63          controller: this.xComponentController
64        })
65      }
66      Button('虚拟屏')
67        .onClick(() => {
68          // screenVirtualScreen用于存储创建的虚拟屏对象
69          let screenVirtualScreen: screen.Screen | null = null;
70          class VirtualScreenOption {
71            name: string = '';
72            width: number = 0;
73            height: number = 0;
74            density: number = 0;
75            surfaceId: string = '';
76          }
77          // option定义创建虚拟屏所需的参数
78          let option: VirtualScreenOption = {
79            name: 'screen01',
80            width: 1080,
81            height: 2340,
82            density: 2,
83            surfaceId: ''
84          };
85          // 创建虚拟屏
86          screen.createVirtualScreen(option, (err: BusinessError, data: screen.Screen) => {
87            const errCode: number = err.code;
88            if (errCode) {
89              console.error(`Failed to create the virtual screen. Code:${err.code},message is ${err.message}`);
90              return;
91            }
92            screenVirtualScreen = data;
93            console.info('Succeeded in creating the virtual screen. Data: ' + JSON.stringify(data));
94            // 获取surfaceId
95            let surfaceId = this.xComponentController.getXComponentSurfaceId();
96            screen.setVirtualScreenSurface(screenVirtualScreen.id, surfaceId, (err: BusinessError) => {
97              const errCode: number = err.code;
98              if (errCode) {
99                console.error(`Failed to set the surface for the virtual screen. Code:${err.code},message is ${err.message}`);
100                return;
101              }
102              console.info('Succeeded in setting the surface for the virtual screen.');
103            });
104            let mirrorScreenIds: Array<number> = [screenVirtualScreen.id];
105            // 获取当前所有屏幕
106            screen.getAllScreens((err: BusinessError, data: Array<screen.Screen>) => {
107              const errCode: number = err.code;
108              if (errCode) {
109                console.error(`Failed to get all screens. Code:${err.code},message is ${err.message}`);
110                return;
111              }
112              // 通过makeMirror创建物理屏的镜像,并映射到虚拟屏上
113              let mainScreenId = data.find(item => item.sourceMode === 0)?.id;
114              screen.makeMirror(mainScreenId, mirrorScreenIds, (err: BusinessError, data: number) => {
115                const errCode: number = err.code;
116                if (errCode) {
117                  console.error(`Failed to set screen mirroring. Code:${err.code},message is ${err.message}`);
118                  return;
119                }
120                console.info('Succeeded in setting screen mirroring. Data: ' + JSON.stringify(data));
121              });
122              // 停止镜像
123              screen.stopMirror(mirrorScreenIds, (err: BusinessError) => {
124                const errCode: number = err.code;
125                if (errCode) {
126                  console.error(`Failed to stop mirror screens. Code:${err.code},message is ${err.message}`);
127                  return;
128                }
129                console.info('Succeeded in stopping mirror screens.');
130              });
131              // 销毁虚拟屏
132              screen.destroyVirtualScreen(mirrorScreenIds[0], (err: BusinessError) => {
133                const errCode: number = err.code;
134                if (errCode) {
135                  console.error(`Failed to destroy the virtual screen. Code:${err.code},message is ${err.message}`);
136                  return;
137                }
138                console.info('Succeeded in destroying the virtual screen.');
139              });
140            });
141          });
142        })
143    }
144    .height('100%')
145    .width('100%')
146  }
147}
148```