相机取景器

相机取景器

注意:本页介绍的是 Camera2 软件包。除非您的应用需要 Camera2 中的特定低级功能,否则我们建议您使用 CameraX。CameraX 和 Camera2 都支持 Android 5.0(API 级别 21)及更高版本。

在 Android 设备上,摄像头和相机预览并不总是采用相同的屏幕方向。

相机位于设备上的固定位置,而无论设备是否

是手机、平板电脑或计算机。当设备屏幕方向改变时,

改变摄像头方向。

因此,相机应用通常假定设备的屏幕方向和相机预览的宽高比呈固定关系。当手机处于纵向模式时,相机预览会被假定为高度大于宽度。当手机(和摄像头)旋转为横屏时,相机预览的宽度应大于高度。

但是,这些假设受到了可折叠设备等新外形规格设备的挑战。

设备,

以及各种显示模式

多窗口模式

和

多显示屏。

可折叠设备会在不更改屏幕方向的情况下更改显示大小和宽高比。多窗口模式会将相机应用限制在

调整相机预览,而不考虑设备的屏幕方向。

多显示屏模式支持使用辅助显示屏,辅助显示屏的屏幕方向可能与主显示屏不同。

摄像头方向

通过

Android 兼容性定义

规定摄像头图像传感器“必须朝向正确方向,以便长

摄像头的尺寸与屏幕的长度方向一致也就是说,

设备处于横屏模式时,摄像头必须以

横向模式。无论设备的自然

屏幕方向;也就是说,它适用于以横屏为主的设备

纵向主要设备。”

摄像头到屏幕的排列可将摄像头的显示区域最大化

取景器。此外,图像传感器通常会以

横向宽高比,4:3 最常见。

图 1. 手机和相机传感器的典型关系

屏幕方向。

摄像头传感器的自然屏幕方向为横向。在图 1 中,前置摄像头的传感器(朝向与显示屏相同方向的摄像头)相对于手机旋转了 270 度,以符合 Android 兼容性定义。

为了向应用公开传感器旋转,camera2 API 包含 SENSOR_ORIENTATION 常量。对于大多数手机和平板电脑,设备会报告前置摄像头的传感器方向为 270 度,后置摄像头的传感器方向为 90 度(从设备背面观察的视角),以使传感器的长边与设备的长边对齐。笔记本电脑摄像头通常会报告

传感器方向为 0 度或 180 度。

注意: 自然(或原生)屏幕方向是设备的典型屏幕方向

或传感器——适用于手机为纵向、适用于平板电脑和笔记本电脑、为横向、横向

。

由于相机图像传感器会在传感器的自然屏幕方向(横向)输出其数据(图像缓冲区),因此必须将图像缓冲区旋转 SENSOR_ORIENTATION 指定的度数,才能让相机预览在设备的自然屏幕方向下显示为竖屏。对于前置摄像头,旋转方向为逆时针;对于后置摄像头,旋转方向为顺时针。

例如,对于图 1 中的前置摄像头,摄像头传感器生成的图像缓冲区如下所示:

必须逆时针旋转 270 度,使预览的方向与设备方向一致:

后置摄像头会生成与上述缓冲区相同的方向的图片缓冲区,但 SENSOR_ORIENTATION 为 90 度。因此,缓冲区会顺时针旋转 90 度。

设备旋转

设备旋转角度是指设备从自然旋转角度

屏幕方向。例如,处于横屏模式的手机有一个设备

旋转 90 度或 270 度,具体取决于旋转方向。

摄像头传感器图像缓冲区的旋转角度必须与

设备旋转角度(以及传感器朝向角度),

相机预览会保持竖直

屏幕方向计算

相机预览的正确屏幕方向会考虑传感器方向和设备旋转。

传感器图像缓冲区的总旋转度数可使用以下公式计算:

rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360

其中,sign 为前置摄像头的 1,后置摄像头的 -1。

对于前置摄像头,图像缓冲区逆时针旋转(从

传感器的自然方向)。对于后置摄像头,传感器

图像缓冲区将顺时针旋转。

表达式 deviceOrientationDegrees * sign + 360 会将后置摄像头的设备旋转方向从逆时针旋转转换为顺时针旋转(例如,将逆时针 270 度旋转转换为顺时针 90 度旋转)。模数

运算将结果缩放到小于 360 度(例如,缩放到 540)

旋转角度为 180)。

不同的 API 会以不同的方式报告设备旋转:

Display#getRotation()

提供设备的逆时针旋转角度(从用户所在位置开始)

视图)。此值会按原样插入上述公式。

OrientationEventListener#onOrientationChanged()

返回设备顺时针旋转的角度(从用户的角度)。

将该值取负,以便在上述公式中使用。

前置摄像头

图 2. 相机预览和传感器(手机旋转了 90 度)

横向模式。

下面是图 2 中相机传感器生成的图像缓冲区:

缓冲区必须逆时针旋转 270 度才能针对传感器进行调整

方向(请参阅上文的镜头方向):

然后,系统会再逆时针旋转缓冲区 90 度,以考虑设备旋转,从而使相机预览在图 2 中显示正确的方向:

以下是镜头向右变为横向的样子:

图 3. 相机预览和传感器(手机旋转了 270 度)

(或 -90 度)转换为横向。

以下是图像缓冲区:

缓冲区必须逆时针旋转 270 度才能针对传感器进行调整

屏幕方向:

然后将缓冲区再逆时针旋转 270 度

设备旋转:

后置摄像头

后置摄像头的传感器方向通常为 90 度(从设备背面观察)。确定相机预览的方向时,传感器图像缓冲区会按传感器旋转量顺时针旋转(而不是像前置摄像头那样逆时针旋转),然后图像缓冲区会按设备旋转量逆时针旋转。

图 4. 横向放置的手机,后置摄像头朝下(旋转 270 度或 -90 度)。

以下是图 4 中相机传感器的图像缓冲区:

必须顺时针旋转缓冲区 90 度,以调整传感器方向:

然后,将缓冲区逆时针旋转 270 度,以考虑设备旋转:

宽高比

显示宽高比会在设备屏幕方向发生变化时发生变化,但也会在可折叠设备折叠和展开时、在多窗口环境中调整窗口大小时,以及在应用在辅助显示屏上打开时发生变化。

当界面动态更改屏幕方向(无论设备是否更改屏幕方向)时,相机传感器图像缓冲区必须调整方向和缩放比例,以匹配取景器界面元素的方向和宽高比。

在新型设备、多窗口或多显示屏环境中,如果您的设备是

应用假定相机预览的方向与设备相同

(纵向或横向)您的预览方向可能不正确,或为横向

不正确,或者两者兼有。

图 5. 可折叠设备从纵向宽高比切换为横向宽高比,但摄像头传感器保持纵向方向。

在图 5 中,应用错误地假定设备旋转了 90

逆时针角度;因此,应用将预览画面旋转相同的幅度。

图 6. 可折叠设备从纵向宽高比切换为横向宽高比,但摄像头传感器保持纵向方向。

在图 6 中,应用未调整图片缓冲区的宽高比,以便其能够正确缩放以适应相机预览界面元素的新尺寸。

屏幕方向固定的相机应用在可折叠设备上通常会出现问题,

笔记本电脑等其他大屏设备:

图 7. 笔记本电脑上固定屏幕方向的纵向应用。

在图 7 中,相机应用的界面是横向的,因为该应用的屏幕方向仅限于纵向。取景器图像已正确朝向

相对于相机传感器的位置。

嵌入式纵向模式

不支持多窗口模式的相机应用

(resizeableActivity="false")

并限制它们的屏幕方向

(screenOrientation="portrait"

或 screenOrientation="landscape")

在大屏设备上可以置于边衬区纵向模式,以便正确定位

相机预览

以竖屏方式插入纵向模式信箱模式(边衬区)且仅支持纵向模式的应用

即使显示屏宽高比为横向也是如此。

即使应用仅以横向模式显示,在横屏状态下仍会显示信箱模式

显示宽高比为纵向。相机图片会旋转以与应用界面对齐,剪裁以匹配相机预览的宽高比,然后缩放以填充预览。

当相机图像传感器的宽高比与应用的主要 activity 的宽高比不匹配时,系统会触发嵌入式纵向模式。

图 8. 已开启边衬区人像模式下固定屏幕方向的纵向应用

笔记本电脑

在图 8 中,仅限纵向的相机应用已旋转,以便在笔记本电脑显示屏上竖屏显示界面。由于纵向应用和横向显示屏之间的宽高比不同,应用会进入信箱模式。相机预览图片已旋转,以补偿应用界面的旋转(由于采用内嵌纵向模式),并且图片已剪裁和缩放以适应纵向屏幕方向,从而缩小了视野范围。

旋转、剪裁、缩放

为显示屏上的仅支持人像的相机应用调用边衬区人像模式

横向宽高比的图片:

图 9. 笔记本电脑上的固定纵向应用。

应用在纵向模式下进入信箱模式:

相机图片会旋转 90 度,以适应

app:

系统会将图片剪裁为相机预览的宽高比,然后缩放以填充预览(视野范围会缩小):

在可折叠设备上,摄像头传感器的方向可以是纵向

而显示屏的宽高比为横向:

图 10. 展开的设备,其中包含仅限纵向的相机应用,以及

摄像头传感器和显示屏宽高比的不同。

由于相机预览会旋转以调整传感器方向,因此图片在取景器中会正确显示,但仅限纵向的应用会横向显示。

插入竖屏模式只需要在纵向模式下让应用进入信箱模式

正确定位应用和相机预览:

API

从 Android 12(API 级别 31)开始,应用还可以明确控制边衬区纵向

我们将使用

SCALER_ROTATE_AND_CROP

CaptureRequest 的属性

类。

默认值为

SCALER_ROTATE_AND_CROP_AUTO,

使系统能够调用边衬区人像模式

SCALER_ROTATE_AND_CROP_90

是上述插入人像模式的行为。

并非所有设备都支持所有 SCALER_ROTATE_AND_CROP 值。如需获取支持的值列表,请参阅 CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES。

注意 :仅限具有相机硬件抽象层的设备

(HAL),它支持

SCALER_ROTATE_AND_CROP API 可以启用边衬区人像模式。

CameraX

注意:Jetpack CameraX 向后兼容 Android 5.0(API 级别 21)。

Jetpack CameraX 库

创建可适应传感器方向和

完成一项简单的任务。

PreviewView 布局元素会创建相机预览,并自动根据传感器方向、设备旋转和缩放进行调整。PreviewView 会保持

将

FILL_CENTER

缩放类型,将图片居中,但可能会根据尺寸对其进行剪裁

(位于 PreviewView 中)。如需将相机图片设置为信箱模式,请将缩放类型设置为 FIT_CENTER。

如需了解使用 PreviewView 创建相机预览的基础知识,请参阅实现预览。

如需查看完整的示例实现,请参阅 GitHub 上的 CameraXBasic 代码库。

相机取景器

注意:CameraViewfinder 库向后兼容 Android 5.0(API 级别 21)。

与预览用例类似,CameraViewfinder 库提供了一组工具来简化相机预览的创建。它不依赖于 CameraX Core,因此您可以将其无缝集成到

现有的 Camera2 代码库。

您可以使用 CameraViewfinder widget 来显示 Camera2 的摄像头画面,而不是直接使用 Surface。

CameraViewfinder 在内部使用 TextureView 或 SurfaceView

显示相机画面,并对其应用所需的转换

正确显示取景器。

这包括校正其宽高比、缩放比例和旋转角度。

如需从 CameraViewfinder 对象请求 Surface,您需要:

创建 ViewfinderSurfaceRequest。

此请求包含对界面分辨率和摄像头设备的要求

信息来自 CameraCharacteristics。

正在呼叫requestSurfaceAsync()

将请求发送到 Surface 提供程序,该提供程序可以是 TextureView 或

SurfaceView 并获取 ListenableFuture 为 Surface。

正在呼叫markSurfaceSafeToRelease()

通知 Surface 提供程序不需要 Surface,并且

可以释放资源

Kotlin

fun startCamera(){

val previewResolution = Size(width, height)

val viewfinderSurfaceRequest =

ViewfinderSurfaceRequest(previewResolution, characteristics)

val surfaceListenableFuture =

cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest)

Futures.addCallback(surfaceListenableFuture, object : FutureCallback {

override fun onSuccess(surface: Surface) {

/* create a CaptureSession using this surface as usual */

}

override fun onFailure(t: Throwable) { /* something went wrong */}

}, ContextCompat.getMainExecutor(context))

}

Java

void startCamera(){

Size previewResolution = new Size(width, height);

ViewfinderSurfaceRequest viewfinderSurfaceRequest =

new ViewfinderSurfaceRequest(previewResolution, characteristics);

ListenableFuture surfaceListenableFuture =

cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest);

Futures.addCallback(surfaceListenableFuture, new FutureCallback() {

@Override

public void onSuccess(Surface result) {

/* create a CaptureSession using this surface as usual */

}

@Override public void onFailure(Throwable t) { /* something went wrong */}

}, ContextCompat.getMainExecutor(context));

}

SurfaceView

SurfaceView是

在相机预览无法获得相机预览时,

需要处理,没有动画效果。

SurfaceView 会自动旋转相机传感器图像缓冲区,以匹配显示屏方向,同时考虑传感器方向和设备旋转。不过,系统会缩放图像缓冲区以适应 SurfaceView

尺寸而不考虑宽高比

您必须确保图像缓冲区的纵横比与纵横比匹配

SurfaceView的宽高比,这可以通过缩放内容

位于组件的 SurfaceView 中,

onMeasure()

方法:

(computeRelativeRotation() 源代码位于

详见下文的相对旋转)。

Kotlin

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {

val width = MeasureSpec.getSize(widthMeasureSpec)

val height = MeasureSpec.getSize(heightMeasureSpec)

val relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees)

if (previewWidth > 0f && previewHeight > 0f) {

/* Scale factor required to scale the preview to its original size on the x-axis. */

val scaleX =

if (relativeRotation % 180 == 0) {

width.toFloat() / previewWidth

} else {

width.toFloat() / previewHeight

}

/* Scale factor required to scale the preview to its original size on the y-axis. */

val scaleY =

if (relativeRotation % 180 == 0) {

height.toFloat() / previewHeight

} else {

height.toFloat() / previewWidth

}

/* Scale factor required to fit the preview to the SurfaceView size. */

val finalScale = min(scaleX, scaleY)

setScaleX(1 / scaleX * finalScale)

setScaleY(1 / scaleY * finalScale)

}

setMeasuredDimension(width, height)

}

Java

@Override

void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int width = MeasureSpec.getSize(widthMeasureSpec);

int height = MeasureSpec.getSize(heightMeasureSpec);

int relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees);

if (previewWidth > 0f && previewHeight > 0f) {

/* Scale factor required to scale the preview to its original size on the x-axis. */

float scaleX = (relativeRotation % 180 == 0)

? (float) width / previewWidth

: (float) width / previewHeight;

/* Scale factor required to scale the preview to its original size on the y-axis. */

float scaleY = (relativeRotation % 180 == 0)

? (float) height / previewHeight

: (float) height / previewWidth;

/* Scale factor required to fit the preview to the SurfaceView size. */

float finalScale = Math.min(scaleX, scaleY);

setScaleX(1 / scaleX * finalScale);

setScaleY(1 / scaleY * finalScale);

}

setMeasuredDimension(width, height);

}

如需详细了解如何以相机预览的形式实现 SurfaceView,请参阅

相机方向。

TextureView

TextureView 性能不如

SurfaceView - 工作量更大,但 TextureView 可以帮您最大限度地

控制相机预览。

TextureView 会根据传感器方向旋转传感器图像缓冲区,但

不处理设备旋转或预览缩放。

缩放和旋转可以编码为矩阵转换。要了解如何

正确缩放和旋转 TextureView,请参阅

在相机应用中支持可调整大小的 Surface

相对旋转

相机传感器的相对旋转角度是

使摄像头传感器输出与设备方向对齐。

SurfaceView 和 TextureView 等组件使用相对旋转来确定预览图片的 x 和 y 缩放比例。它还用于指定传感器图像缓冲区的旋转。

通过

CameraCharacteristics

和

Surface 类可用于计算

摄像头传感器的相对旋转:

Kotlin

/**

* Computes rotation required to transform the camera sensor output orientation to the

* device's current orientation in degrees.

*

* @param characteristics The CameraCharacteristics to query for the sensor orientation.

* @param surfaceRotationDegrees The current device orientation as a Surface constant.

* @return Relative rotation of the camera sensor output.

*/

public fun computeRelativeRotation(

characteristics: CameraCharacteristics,

surfaceRotationDegrees: Int

): Int {

val sensorOrientationDegrees =

characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!

// Reverse device orientation for back-facing cameras.

val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) ==

CameraCharacteristics.LENS_FACING_FRONT

) 1 else -1

// Calculate desired orientation relative to camera orientation to make

// the image upright relative to the device orientation.

return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360

}

Java

/**

* Computes rotation required to transform the camera sensor output orientation to the

* device's current orientation in degrees.

*

* @param characteristics The CameraCharacteristics to query for the sensor orientation.

* @param surfaceRotationDegrees The current device orientation as a Surface constant.

* @return Relative rotation of the camera sensor output.

*/

public int computeRelativeRotation(

CameraCharacteristics characteristics,

int surfaceRotationDegrees

){

Integer sensorOrientationDegrees =

characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);

// Reverse device orientation for back-facing cameras.

int sign = characteristics.get(CameraCharacteristics.LENS_FACING) ==

CameraCharacteristics.LENS_FACING_FRONT ? 1 : -1;

// Calculate desired orientation relative to camera orientation to make

// the image upright relative to the device orientation.

return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360;

}

窗口指标

不应使用屏幕尺寸来确定摄像头的尺寸

取景器;相机应用可能会在屏幕的某一部分运行,

在移动设备上处于多窗口模式,在 ChromeOS 中则设为空闲模式。

WindowManager#getCurrentWindowMetrics()(在 API 级别 30 中添加)会返回应用窗口的大小,而不是屏幕的大小。Jetpack WindowManager 库方法

WindowMetricsCalculator#computeCurrentWindowMetrics()

和

WindowInfoTracker#currentWindowMetrics()

提供类似的支持,向后兼容 API 级别 14。

注意:Display 的 getRealSize() 和 getRealMetrics() 方法自 API 级别 31 起已弃用,这些方法不支持多窗口模式或自由形式模式。要获取显示密度,请使用

Configuration#densityDpi

而非 getRealMetrics()。

旋转 180 度

设备旋转 180 度(例如,从自然屏幕方向旋转到

自然屏幕方向倒置)不会触发

onConfigurationChanged()

回调。因此,相机预览可能会上下倒置。

如需检测 180 度旋转,请实现 DisplayListener,并在 onDisplayChanged() 回调中通过调用 Display#getRotation() 检查设备旋转。

专属资源

在 Android 10 之前,只有多窗口环境中可见的顶层 activity 处于 RESUMED 状态。这会让用户感到困惑

系统不会提供关于哪个 activity 已恢复的指示。

Android 10(API 级别 29)引入了多项恢复功能,其中所有可见 activity

处于 RESUMED 状态。可见 activity 仍可进入 PAUSED

状态,例如,透明 activity 位于 activity 之上

该 activity 不可聚焦,例如在画中画模式下(请参阅

画中画支持)。

应用使用摄像头、麦克风或任何专有的

API 级别 29 或更高级别上的单例资源必须支持多项恢复。例如,如果有三个已恢复的 activity 想要使用摄像头,则只有其中一个能够访问此独占资源。每个 activity 都必须实现

onDisconnected()

回调,以知晓更高的优先级对相机的抢先访问

活动。

注意:设置 resizeableActivity="false" 并不能保证对专属资源的访问权限,因为在辅助显示屏上运行的其他 activity 或应用可能具有更高的优先级。此外,

从 Android 12(API 级别 31)开始,resizeableActivity="false" 不会阻止

activity 进入多窗口模式,而在该模式下,其他 activity 可能会进入多窗口模式

资源的优先级。

如需了解详情,请参阅多恢复。

其他资源

如需查看 Camera2 示例,请参阅 Camera2Basic 应用

。

如需了解 CameraX 预览用例,请参阅 CameraX 实现预览。

有关 CameraX 相机预览示例实现,请参阅

CameraXBasic

代码库。

如需了解 ChromeOS 上的相机预览,请参阅相机方向。

如需了解如何针对可折叠设备进行开发,请参阅了解可折叠设备。

相关文章

如何回复对方的早上好(100句)
彩票365官网下载安装

如何回复对方的早上好(100句)

📅 07-15 👁️ 8934
3、苹果十三玩吃鸡怎么样
彩票365官网下载安装

3、苹果十三玩吃鸡怎么样

📅 08-09 👁️ 9480
无限流量卡到底是不是真的?三大避坑指南+运营商对比
365bet网上娱乐网址

无限流量卡到底是不是真的?三大避坑指南+运营商对比

📅 07-18 👁️ 8379