You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Camera2中CameraViewfinder预览画面拉伸问题求助

问题:CameraViewfinder 预览垂直拉伸,fillCenter 未按预期裁剪校正

我正使用Camera2框架开发应用内相机,采用CameraViewfinder实现预览,按文档描述它应该自动处理方向与比例校正。但设置3072x4080(3:4)分辨率并请求Surface后,预览画面出现垂直拉伸。我配置了fillCenter缩放类型,其文档说明“若相机预览比例与CameraViewfinder容器比例不匹配,可能会裁剪取景器”,但实际未出现裁剪,反而拉伸变形。

我尝试过获取取景器比例并匹配最接近的分辨率,但预览仍失真,请问该如何修正?

请求预览Surface的代码

surfaceRequest = ViewfinderSurfaceRequest.Builder(
    resolution = resolution // Size(3072, 4080)
).build()
surfaceRequest?.let { request ->
    val surfaceListenableFuture: ListenableFuture<Surface> =
        binding.viewFinder.requestSurfaceAsync(request)

    Futures.addCallback(
        surfaceListenableFuture, object : FutureCallback<Surface> {
            override fun onSuccess(result: Surface?) {
                // use surface
            }

        },
        ContextCompat.getMainExecutor(requireContext())
    )
}

CameraViewfinder布局代码

<androidx.camera.viewfinder.CameraViewfinder
    android:id="@+id/view_finder"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginBottom="92dp"
    android:layout_marginTop="64dp"
    app:scaleType="fillCenter" />

注:取景器嵌套在FrameLayout中,因相机UI有上下栏设置了边距;对比我的相机预览与Google相机预览,前者画面垂直拉伸。


解决方案

1. 计算取景器实际显示比例

设置的上下边距会改变取景器的实际可用尺寸,CameraViewfinder可能基于原始match_parent尺寸计算比例,而非边距后的实际尺寸。需手动获取实际宽高计算比例:

binding.viewFinder.post {
    val actualWidth = binding.viewFinder.width
    val actualHeight = binding.viewFinder.height
    val viewfinderRatio = actualWidth.toFloat() / actualHeight.toFloat()
    // 基于此比例匹配相机预览分辨率
}

2. 筛选匹配比例的相机预览分辨率

不要硬编码3072x4080,从Camera2支持的分辨率中筛选与取景器实际比例最接近的选项:

// 已获取相机Characteristics的前提下
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
val configMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
val supportedSizes = configMap.getOutputSizes(SurfaceTexture::class.java)

val targetRatio = actualWidth.toFloat() / actualHeight.toFloat()
var bestSize: Size? = null
var minDiff = Float.MAX_VALUE

for (size in supportedSizes) {
    val sizeRatio = size.width.toFloat() / size.height.toFloat()
    val diff = abs(sizeRatio - targetRatio)
    if (diff < minDiff) {
        minDiff = diff
        bestSize = size
    }
}

// 使用匹配到的最佳分辨率创建SurfaceRequest
surfaceRequest = bestSize?.let { ViewfinderSurfaceRequest.Builder(it).build() }

3. 对齐相机预览方向

Camera2预览方向需与屏幕旋转角度匹配,否则会导致比例计算偏差:

// 获取屏幕旋转角度
val screenRotation = requireActivity().windowManager.defaultDisplay.rotation
// 转换为相机适配的方向
val cameraOrientation = when (screenRotation) {
    Surface.ROTATION_0 -> 90
    Surface.ROTATION_90 -> 0
    Surface.ROTATION_180 -> 270
    Surface.ROTATION_270 -> 180
    else -> 90
}

// 配置CaptureRequest时设置方向
val previewRequest = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
    set(CaptureRequest.JPEG_ORIENTATION, cameraOrientation)
    addTarget(surface) // surface为从CameraViewfinder获取的预览Surface
}

4. 显式设置缩放类型

确保fillCenter缩放类型生效,可在代码中显式设置:

binding.viewFinder.scaleType = CameraViewfinder.ScaleType.FILL_CENTER

内容的提问来源于stack exchange,提问作者Pramath Pancholi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 02:50:18