文档管理中心
最佳实践多设备开发一次开发,多端部署多设备功能开发相机硬件差异

相机硬件差异

概述

在移动端应用开发中,相机页面的多设备适配一直是开发者面临的一大难题。由于不同设备的屏幕尺寸、相机镜头、折叠形态以及系统特性等方面存在较大差异,相机界面的开发往往会遇到一系列兼容性问题,影响用户体验。

本文介绍如何将手机相机页面(含预览、拍摄和查看照片功能)适配至双折叠、Pura X、三折叠和平板等多种设备形态。在基础相机功能(预览、拍照、查看照片)之上,适配折叠屏和平板设备时,需要重点关注以下核心问题:

通过断点实现多套页面布局

“两个宽度相近的窗口,页面布局应相同”。首先,根据这条原则,确认要适配窗口的宽度范围,手机、双折叠、Pura X、三折叠、平板设备共涉及到3种横向断点:sm、md和lg,因此应用首次开发时需要单独设计3种页面布局。

“对于高度相对宽度较小的窗口,呈现横向窗口或类方形窗口时,页面布局需进行差异化设计”。其次根据这条规则,因为Pura X外屏独特的小方形窗口形态,布局会与手机略有差异。UX设计图如下:

断点

横向断点sm,纵向断点md

横向断点sm,纵向断点lg

横向断点md

横向断点lg

UX图

开发步骤

  1. 分析三种横向断点对应的UX设计。sm断点的相机按钮布局分布在上下两侧,需要单独实现,Pura X外屏与手机布局的差异通过纵向断点进行区分;md、lg断点的相机按钮布局相同,分布在左右两侧,可以使用一套代码实现。使用Stack组件,展示相机预览画面上的控制按钮,并使用断点控制在三折叠不同使用状态下的显示与隐藏。相机页面中XComponent组件宽高均可设置为100%,预览画面的大小可通过设置XComponent组件对应Surface区域的宽高实现,代码可参考设置多设备上相机预览画面比例
    Stack() {
      // Camera View.
      Column() {
        XComponent({
          type: XComponentType.SURFACE,
          controller: this.xComponentController
        }) {}
        // ...
      }
      .width('100%')
      .height(this.isHalfFolded ? this.creaseRegion[0] : '')
      .layoutWeight(this.isHalfFolded ? 0 : 1)
      // ...
    
      // Shooting button view.
      Stack() {
        // Setting view for sm.
        Column() {
          // ...
        }
        .width(this.heightBp === HeightBreakpoint.HEIGHT_MD ? 30 : 48)
        .height('100%')
        .margin({ right: 16 })
        .padding({ top: this.heightBp === HeightBreakpoint.HEIGHT_MD ? 16 : 108 })
        .visibility(this.widthBp === WidthBreakpoint.WIDTH_SM ? Visibility.Visible : Visibility.None)
    
        // Choose music for sm.
        Row() {
          // ...
        }
        .width('100%')
        .height(this.heightBp === HeightBreakpoint.HEIGHT_MD ? 28 : 40)
        .position({
          x: 0,
          y: this.heightBp === HeightBreakpoint.HEIGHT_MD ? 16 : 28
        })
        .justifyContent(FlexAlign.Center)
        .visibility(this.widthBp === WidthBreakpoint.WIDTH_SM ? Visibility.Visible : Visibility.None)
    
        // Shooting button for sm.
        Column() {
          // ...
        }
        .visibility(this.widthBp === WidthBreakpoint.WIDTH_SM ? Visibility.Visible : Visibility.None)
        .height(this.heightBp === HeightBreakpoint.HEIGHT_MD ? 96 : 132)
        .width('100%')
        .margin({ bottom: this.heightBp === HeightBreakpoint.HEIGHT_MD ? 16 : 84 })
    
        // Setting view for md/lg.
        Column() {
          // ...
        }
        .width(this.widthBp === WidthBreakpoint.WIDTH_MD ? 144 : 152)
        .height('100%')
        .justifyContent(FlexAlign.Start)
        .padding({ left: this.heightBp === HeightBreakpoint.HEIGHT_MD ? 24 : 32 })
        .position({ x: 0, y: 0 })
        .alignItems(HorizontalAlign.Start)
        .visibility(this.widthBp === WidthBreakpoint.WIDTH_MD || this.widthBp === WidthBreakpoint.WIDTH_LG ?
          Visibility.Visible : Visibility.None)
    
        // Shooting button for md/lg.
        Column() {
          // ...
        }
        .width(92)
        .height('100%')
        .justifyContent(FlexAlign.Center)
        .padding({ right: 16 })
        .visibility(this.widthBp === WidthBreakpoint.WIDTH_MD || this.widthBp === WidthBreakpoint.WIDTH_LG ?
          Visibility.Visible : Visibility.None)
      }
      .height('100%')
      .width('100%')
      .alignContent(Alignment.BottomEnd)
      .visibility(this.isHalfFolded ? Visibility.None : Visibility.Visible)
      // ...
    }
    .height('100%')
    .width('100%')
    .alignContent(this.widthBp === WidthBreakpoint.WIDTH_MD ? (this.isHalfFolded ? Alignment.Top : Alignment.Start) :
      Alignment.Center)
  2. 相机页面在不同设备上横竖屏旋转,请参考为多设备配置旋转策略。以本应用为例,横向断点为sm时不支持旋转;md、lg时支持旋转。因此,当窗口宽度、高度的最小值大于等于600vp时,窗口支持旋转。
    onWindowSizeChange: (windowSize: window.Size) => void = (windowSize: window.Size) => {
      this.setOrientation(this.uiContext!.px2vp(windowSize.width), this.uiContext!.px2vp(windowSize.height));
      // ...
    }
    
    setOrientation(width: number, height: number): void {
      // When the minimum value of window width and height is greater than the md breakpoint threshold, rotation is supported.
      if (Math.min(width, height) >= 600) {
        this.windowData?.setPreferredOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED).catch((error: BusinessError) => {
          hilog.error(0x0000, `MultiDeviceCamera`,
            `Set window orientation failed. Code: ${error.code}, message: ${error.message}`);
        });
      } else {
        this.windowData?.setPreferredOrientation(window.Orientation.PORTRAIT).catch((error: BusinessError) => {
          hilog.error(0x0000, `MultiDeviceCamera`,
            `Set window orientation failed. Code: ${error.code}, message: ${error.message}`);
        });
      }
    }
    
    // ...
    onWindowStageCreate(windowStage: window.WindowStage): void {
      // ...
      windowStage.loadContent('pages/Index', (err) => {
        // ...
        windowStage.getMainWindow().then((data: window.Window) => {
          // ...
          // Monitor window size changes and update breakpoints.
          data.on('windowSizeChange', this.onWindowSizeChange);
          let rect: window.Rect = data.getWindowProperties().windowRect;
          this.setOrientation(this.uiContext.px2vp(rect.width), this.uiContext.px2vp(rect.height));
        }).catch((err: BusinessError) => {
          hilog.error(0x0000, 'testTag', `Error occured, error code: ${err.code}, error message: ${err.message}`);
        })
      });
    }

选择相机设备

完整的相机页面,除了实现页面的按钮组件,还需要实现相机预览画面。在创建相机预览对象之前,首先要使用createCameraInput()确认使用的相机设备。

开发步骤

  1. 手机、双折叠、Pura X、三折叠、平板均配有后置相机和前置相机。相机页面显示时,根据isFront变量选择对应CameraPosition的相机,默认使用后置相机。
    onPageShow(): void {
      abilityAccessCtrl.createAtManager().requestPermissionsFromUser(this.context, this.permissions).then(() => {
        setTimeout(() => {
          // After obtaining permission, load the camera preview stream and ensure it is consistent with the aspect ratio of the surface.
          // ...
          if (this.isFront) {
            this.cameraUtil.cameraShooting(this.surfaceId, this.context!, camera.CameraPosition.CAMERA_POSITION_FRONT);
          } else {
            this.cameraUtil.cameraShooting(this.surfaceId, this.context!, camera.CameraPosition.CAMERA_POSITION_BACK);
          }
        }, 200);
      }).catch((err: BusinessError) => {
        hilog.error(0x0000, 'testTag', `Failed to requestPermissionsFromUser. Code: ${err.code}, message: ${err.message}`);
      })
    }

    CameraPosition是相对的而不是固定的,例如Pura X外屏的相机。

    • 在折叠态时CameraPosition为CAMERA_POSITION_FRONT,效果图如下:

    • 在展开态时CameraPosition为CAMERA_POSITION_BACK,效果图如下:

  2. 其他需要重置预览流的场景需要开发者单独处理。折叠状态切换(例如双折叠的折叠态切换至半折叠态),会导致显示屏幕变化,需要重新选择相机设备。所以在display.on('foldStatusChange')中判断变化前后的折叠状态,并根据变化前使用的相机位置,选择变化后使用前置相机或后置相机。
    onFoldStatusChange: (foldStatus: display.FoldStatus) => void = (foldStatus: display.FoldStatus) => {
      if (foldStatus === display.FoldStatus.FOLD_STATUS_HALF_FOLDED) {
        let orientation: display.Orientation = display.getDefaultDisplaySync().orientation;
        // Determine the page layout that has entered half folded status and prohibit portrait orientation.
        if (this.widthBp === WidthBreakpoint.WIDTH_MD && (orientation === display.Orientation.LANDSCAPE ||
          orientation === display.Orientation.LANDSCAPE_INVERTED)) {
          this.isHalfFolded = true;
          this.windowUtil.setMainWindowOrientation(window.Orientation.LANDSCAPE);
          this.cameraUtil.setHalfFoldedRect(this.windowUtil.getWindowSize());
        } else {
          if (this.oldFoldStatus === display.FoldStatus.FOLD_STATUS_FOLDED) {
            if (this.isFront) {
              this.cameraUtil.cameraShooting(this.surfaceId, this.context!, camera.CameraPosition.CAMERA_POSITION_FRONT);
            } else {
              this.cameraUtil.cameraShooting(this.surfaceId, this.context!, camera.CameraPosition.CAMERA_POSITION_BACK);
            }
          }
        }
        return;
      }
      // ...
      // Exit the half folded status page.
      if (this.isHalfFolded) {
        this.isHalfFolded = false;
        this.cameraUtil.setXComponentRect(this.windowUtil.getWindowSize());
      } else {
        if (foldStatus === display.FoldStatus.FOLD_STATUS_FOLDED ||
          (this.oldFoldStatus === display.FoldStatus.FOLD_STATUS_EXPANDED_WITH_SECOND_HALF_FOLDED &&
            foldStatus === display.FoldStatus.FOLD_STATUS_EXPANDED) ||
          (this.oldFoldStatus === display.FoldStatus.FOLD_STATUS_EXPANDED &&
            foldStatus === display.FoldStatus.FOLD_STATUS_EXPANDED_WITH_SECOND_HALF_FOLDED)) {
          if (this.isFront) {
            this.cameraUtil.cameraShooting(this.surfaceId, this.context!, camera.CameraPosition.CAMERA_POSITION_FRONT);
          } else {
            this.cameraUtil.cameraShooting(this.surfaceId, this.context!, camera.CameraPosition.CAMERA_POSITION_BACK);
          }
        }
      }
      // ...
    }
    
    aboutToAppear(): void {
      try {
        display.on('foldStatusChange', this.onFoldStatusChange);
      } catch (error) {
        let err = error as BusinessError;
        hilog.error(0x0000, 'MultiDeviceCamera', `Failed to obtain fold status. Code: ${err.code}, message: ${err.message}`);
      }
      // ...
    }

    需要注意,PuraX外屏只存在前置相机。如果在内屏使用的是后置相机,切换外屏后,后置相机将不再可用,则返回可用相机列表中默认的相机。

    getCamera(cameras: Array<camera.CameraDevice>, cameraPosition: camera.CameraPosition): number {
      // Choose front or rear camera.
      for (let i: number = 0; i < cameras.length; ++i) {
        if (cameras[i].cameraPosition === cameraPosition) {
          if (cameraPosition === camera.CameraPosition.CAMERA_POSITION_BACK) {
            AppStorage.setOrCreate('isFront', false);
          }
          if (cameraPosition === camera.CameraPosition.CAMERA_POSITION_FRONT) {
            AppStorage.setOrCreate('isFront', true);
          }
          return i;
        }
      }
      hilog.error(0x0000, 'testLog', `Failed to find the camera with the corresponding position.`);
      if (cameras[0].cameraPosition === camera.CameraPosition.CAMERA_POSITION_BACK) {
        AppStorage.setOrCreate('isFront', false);
      }
      if (cameras[0].cameraPosition === camera.CameraPosition.CAMERA_POSITION_FRONT) {
        AppStorage.setOrCreate('isFront', true);
      }
      return 0;
    }

设置多设备上相机预览画面比例

选择相机之后,需要通过createPreviewOutput()创建预览输出对象,绑定至XComponent组件展示预览画面,实现流程可参考拍照实践。在开发多设备上相机预览画面时,需要通过以下步骤避免压缩、拉伸、异常旋转的问题。

XComponent组件对应Surface区域的宽高比,取决于用户预览时设备的屏幕顺时针旋转角度。如果display.rotation为0°或180°,则Surface与相机预览流宽高比互为倒数;如果display.rotation为90°或270°,则Surface与相机预览流宽高比一致。

以获取预览流宽高比4:3为例(Pura X折叠态为1:1),多设备不同状态的Surface宽高比如下表:

  

Pura X折叠态

手机/双折叠折叠态/三折叠F态/Pura X展开态

双折叠展开态/三折叠M态

三折叠G态

平板

设备方向

以充电口朝右为例

display.rotation=270

以充电口朝下为例

display.rotation=0

以充电口朝下为例

display.rotation=0

以充电口朝下为例

display.rotation=0

以充电口朝下为例

display.rotation=0

Surface宽高比

1:1

3:4

3:4

3:4

3:4

设备方向

页面不支持旋转

页面不支持旋转

以充电口朝右为例

display.rotation=270

以充电口朝右为例

display.rotation=270

以充电口朝右为例

display.rotation=270

Surface宽高比

4:3

4:3

4:3

多设备不同状态的预览效果图如下表:

Pura X折叠态

手机/双折叠折叠态/三折叠F态/Pura X展开态

双折叠展开态/三折叠M态

三折叠G态

平板

页面不支持旋转

页面不支持旋转

开发步骤

  1. 在窗口横竖屏旋转时,实现setXComponentSurfaceRect()方法重新设置XComponent组件中surface区域的宽和高,确保与多设备相机的预览流、拍照流宽高比一致。
    setXComponentRect(windowSize: window.Size): void {
      try {
        // Initialize the width and height of the surface to match the full screen of the window.
        let rect: SurfaceRect = {
          surfaceWidth: windowSize.width,
          surfaceHeight: windowSize.height
        };
        let widthBp: WidthBreakpoint = this.uiContext!.getWindowWidthBreakpoint();
        let heightBp: HeightBreakpoint = this.uiContext!.getWindowHeightBreakpoint();
        let displayRotation: number = display.getDefaultDisplaySync().rotation * 90;
        if (widthBp === WidthBreakpoint.WIDTH_SM && heightBp === HeightBreakpoint.HEIGHT_MD) {
          this.xComponentController!.setXComponentSurfaceRect(rect);
          return;
        }
        if (AppStorage.get('isHalfFolded')) {
          this.setHalfFoldedRect(windowSize);
          return;
        }
        if (displayRotation === 0 || displayRotation === 180) {
          if (windowSize.height * 3 / 4 > windowSize.width) {
            rect.surfaceHeight = windowSize.width / 3 * 4;
          } else {
            rect.surfaceWidth = windowSize.height / 4 * 3;
          }
          if (widthBp === WidthBreakpoint.WIDTH_MD && heightBp === HeightBreakpoint.HEIGHT_MD) {
            rect.offsetX = 0;
            rect.offsetY = 0;
          }
        }
        if (displayRotation === 90 || displayRotation === 270) {
          if (windowSize.width * 3 / 4 > windowSize.height) {
            rect.surfaceWidth = windowSize.height / 3 * 4;
          } else {
            rect.surfaceHeight = windowSize.width / 4 * 3;
          }
        }
        this.xComponentController!.setXComponentSurfaceRect(rect);
      } catch (error) {
        let err = error as BusinessError;
        hilog.error(0x0000, 'MultiDeviceCamera', `Failed to set XComponent rect. Code: ${err.code}, message: ${err.message}`);
      }
    }
  2. 在窗口横竖屏旋转时,会刷新窗口尺寸,需要调用setXComponentRect()方法刷新预览画面比例,避免预览画面异常压缩或拉伸。
    export default class EntryAbility extends UIAbility {
      // ...
      isFirstTime: boolean = true;
      onWindowSizeChange: (windowSize: window.Size) => void = (windowSize: window.Size) => {
        // ...
        if (!this.isFirstTime) {
          this.cameraUtil!.setXComponentRect(this.windowUtil!.getWindowSize());
        } else {
          this.isFirstTime = false;
        }
      }
      // ...
      onWindowStageCreate(windowStage: window.WindowStage): void {
        // ...
        windowStage.loadContent('pages/Index', (err) => {
          // ...
          windowStage.getMainWindow().then((data: window.Window) => {
            // ...
            // Monitor window size changes and update breakpoints.
            data.on('windowSizeChange', this.onWindowSizeChange);
            // ...
          }).catch((err: BusinessError) => {
            hilog.error(0x0000, 'testTag', `Error occured, error code: ${err.code}, error message: ${err.message}`);
          })
        });
      }
      // ...
    }
  3. 最后调用setXComponentSurfaceRotation()设置XComponent的Surface区域在屏幕旋转时锁定方向,确保相机预览画面旋转时的用户体验。
    XComponent({
      type: XComponentType.SURFACE,
      controller: this.xComponentController
    }) {}
    .onLoad(() => {
      // ...
      // Set surface to lock direction when screen rotates.
      this.xComponentController.setXComponentSurfaceRotation({ lock: true });
    })

设置拍照旋转角度

在横竖屏拍照场景下,需确保图片始终正向显示,避免出现照片方向异常(如旋转90°或倒置)。

开发步骤

capture()方法中,通过重力传感器获取当前拍照的角度,并区分后置相机与前置相机,设置拍照时的旋转角度rotation。

capture(): void {
  let rotation: number = 0;
  let isFront: boolean | undefined = AppStorage.get('isFront');
  try {
    // Obtain the angle of the gravity sensor during shooting and set the shooting rotation angle.
    sensor.once(sensor.SensorId.GRAVITY, (data: sensor.GravityResponse) => {
      if (Math.abs(data.z) > OVERLOOKING_GRAVITY_OF_Z_AXIS) {
        rotation = this.lastRotation;
      }
      else {
        let degree: number = this.getCalDegree(data.x, data.y, data.z);
        if ((degree >= 0 && degree <= 30) || degree >= 300) {
          rotation = camera.ImageRotation.ROTATION_0;
        } else if (degree > 30 && degree <= 120) {
          if (isFront) {
            // Use ROTATION_270 when degree range is (30, 120] for front camera.
            rotation = camera.ImageRotation.ROTATION_270;
          } else {
            // Use ROTATION_90 when degree range is (30, 120] for back camera.
            rotation = camera.ImageRotation.ROTATION_90;
          }
        } else if (degree > 120 && degree <= 210) {
          // Use ROTATION_180 when degree range is (120, 210].
          rotation = camera.ImageRotation.ROTATION_180;
        } else if (degree > 210 && degree <= 300) {
          if (isFront) {
            // Use ROTATION_90 when degree range is (210, 300] for front camera.
            rotation = camera.ImageRotation.ROTATION_90;
          } else {
            // Use ROTATION_270 when degree range is (210, 300] for back camera.
            rotation = camera.ImageRotation.ROTATION_270;
          }
        };
      }

      let setting: camera.PhotoCaptureSetting = {
        quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,
        rotation: rotation,
        mirror: isFront
      }
      this.photoOutput?.capture(setting);
    })
  } catch (error) {
    let err = error as BusinessError;
    hilog.error(0x0000, 'MultiDeviceCamera', `Capture failed. Code: ${err.code}, message: ${err.message}`);
  }
}

实现悬停态相机页面

悬停态对应折叠状态为FOLD_STATUS_HALF_FOLDED。在进入悬停态时,可以设计特殊的用户体验,UX效果图如下:

开发步骤

使用display.on('foldStatusChange')监听折叠状态的变化。当折叠状态为半折叠且屏幕显示方向为横屏或反向横屏时,记录悬停状态isHalfFolded为true,重新计算Xcomponent的Surface显示区域宽高并调整旋转策略,展示悬停态的相机页面布局;否则,记录悬停态状态为false。

onFoldStatusChange: (foldStatus: display.FoldStatus) => void = (foldStatus: display.FoldStatus) => {
  if (foldStatus === display.FoldStatus.FOLD_STATUS_HALF_FOLDED) {
    let orientation: display.Orientation = display.getDefaultDisplaySync().orientation;
    // Determine the page layout that has entered half folded status and prohibit portrait orientation.
    if (this.widthBp === WidthBreakpoint.WIDTH_MD && (orientation === display.Orientation.LANDSCAPE ||
      orientation === display.Orientation.LANDSCAPE_INVERTED)) {
      this.isHalfFolded = true;
      this.windowUtil.setMainWindowOrientation(window.Orientation.LANDSCAPE);
      this.cameraUtil.setHalfFoldedRect(this.windowUtil.getWindowSize());
    } else {
      if (this.oldFoldStatus === display.FoldStatus.FOLD_STATUS_FOLDED) {
        if (this.isFront) {
          this.cameraUtil.cameraShooting(this.surfaceId, this.context!, camera.CameraPosition.CAMERA_POSITION_FRONT);
        } else {
          this.cameraUtil.cameraShooting(this.surfaceId, this.context!, camera.CameraPosition.CAMERA_POSITION_BACK);
        }
      }
    }
    return;
  }
  if (this.widthBp !== WidthBreakpoint.WIDTH_SM) {
    this.windowUtil.setMainWindowOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED);
  }
  // Exit the half folded status page.
  if (this.isHalfFolded) {
    this.isHalfFolded = false;
    this.cameraUtil.setXComponentRect(this.windowUtil.getWindowSize());
  } else {
    if (foldStatus === display.FoldStatus.FOLD_STATUS_FOLDED ||
      (this.oldFoldStatus === display.FoldStatus.FOLD_STATUS_EXPANDED_WITH_SECOND_HALF_FOLDED &&
        foldStatus === display.FoldStatus.FOLD_STATUS_EXPANDED) ||
      (this.oldFoldStatus === display.FoldStatus.FOLD_STATUS_EXPANDED &&
        foldStatus === display.FoldStatus.FOLD_STATUS_EXPANDED_WITH_SECOND_HALF_FOLDED)) {
      if (this.isFront) {
        this.cameraUtil.cameraShooting(this.surfaceId, this.context!, camera.CameraPosition.CAMERA_POSITION_FRONT);
      } else {
        this.cameraUtil.cameraShooting(this.surfaceId, this.context!, camera.CameraPosition.CAMERA_POSITION_BACK);
      }
    }
  }
  this.oldFoldStatus = foldStatus;
}

常见问题

按钮大小异常、被截断

问题现象

在不同设备上显示按钮大小异常或被截断。

可能原因

未设计不同设备对应的多套相机页面布局。

解决方案

针对不同横向断点设计多套相机页面布局并实现。详情请参考通过断点实现多套页面布局

大屏相机页面的可操作组件不支持旋转

问题现象

在三折叠G态、双折叠展开态、平板等大屏幕,相机页面的可操作组件不支持旋转,不易操作。

可能原因

未设置窗口支持旋转。

解决方案

相机页面的横向断点为sm时,不支持旋转;为md、lg时,支持旋转。因此当窗口宽度、高度的最小值大于等于600vp时,窗口支持旋转。详情请参考通过断点实现多套页面布局

预览画面压缩、拉伸

问题现象

预览画面的显示内容被压缩或拉伸。

可能原因

相机预览对象绑定XComponent组件时,未正确设置Surface区域宽高的值,导致宽高比与预览流旋转后的宽高比不一致。

解决方案

设置XComponent组件对应Surface区域的宽高比,使其与预览流旋转后的宽高比保持一致。详情请参考设置多设备上相机预览画面比例

拍完照片显示角度异常

问题现象

拍摄完成后,照片的实际内容与预期方向不符,可能发生90°、180°或270°的旋转,导致用户需要手动调整才能正常查看。

可能原因

拍照时旋转了设备,且未设置正确的拍照角度。

解决方案

设置拍照时的旋转角度。详情请参考设置拍照旋转角度

折叠屏切换折叠状态时出现黑屏

问题现象

在折叠屏开合,切换折叠状态后,相机预览页面出现黑屏。

可能原因

切换折叠状态过程,导致折叠前选择的相机不再可用,预览画面黑屏。

解决方案

折叠状态切换过程窗口尺寸会变化,通过监听窗口尺寸变化,重新选择相机生成预览流。详情请参考选择相机设备

在 最佳实践 多设备开发 中进行搜索
请输入您想要搜索的关键词