Android中TextureView(相机预览)叠加等比图片及CameraX比例异常问题
我之前也碰到过一模一样的报错,这个问题的核心是CameraX无法找到匹配你指定宽高比例的相机硬件配置,大概率是你获取TextureView尺寸的时机不对,或者手动计算比例时出了问题。下面是针对性的解决方案:
第一步:先确保ConstraintLayout布局的1:1比例设置正确
首先要保证TextureView和叠加的ImageView确实是严格的1:1比例,避免布局层面就出现比例偏差:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 相机预览的TextureView,完全匹配ImageView的尺寸 --> <TextureView android:id="@+id/textureView" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintTop_toTopOf="@id/overlayImageView" app:layout_constraintBottom_toBottomOf="@id/overlayImageView" app:layout_constraintStart_toStartOf="@id/overlayImageView" app:layout_constraintEnd_toEndOf="@id/overlayImageView" /> <!-- 叠加的1:1图片 --> <ImageView android:id="@+id/overlayImageView" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintDimensionRatio="1:1" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" android:src="@drawable/your_overlay_image" /> </androidx.constraintlayout.widget.ConstraintLayout>
这里关键是给ImageView设置app:layout_constraintDimensionRatio="1:1",让它保持宽高比1:1,然后TextureView通过约束完全绑定到ImageView,确保两者尺寸完全一致。
第二步:避免在View未测量完成时获取尺寸
你之前的错误很可能是在onCreate或onStart阶段就获取TextureView的尺寸——这时候View还没完成测量,得到的宽高都是0,用这个无效值创建的Rational会导致CameraX找不到匹配的相机。
正确的做法是等待View完成布局后再获取尺寸,比如用ViewTreeObserver监听布局完成事件:
Kotlin 示例
override fun onResume() { super.onResume() // 等待TextureView完成布局后再配置CameraX textureView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { // 移除监听,避免重复触发 textureView.viewTreeObserver.removeOnGlobalLayoutListener(this) // 此时可以获取到正确的尺寸 val viewWidth = textureView.measuredWidth val viewHeight = textureView.measuredHeight setupCameraX(viewWidth, viewHeight) } }) } private fun setupCameraX(width: Int, height: Int) { // 优先用CameraX提供的AspectRatio常量,避免手动计算出错 val aspectRatio = AspectRatio.RATIO_1_1 // 创建PreviewConfig val previewConfig = PreviewConfig.Builder() .setTargetAspectRatio(aspectRatio) .setTargetRotation(windowManager.defaultDisplay.rotation) .build() // 创建Preview实例并绑定到TextureView val preview = Preview(previewConfig) preview.setOnPreviewOutputUpdateListener { output -> // 更新TextureView的SurfaceTexture textureView.surfaceTexture = output.surfaceTexture } // 绑定CameraX生命周期 CameraX.bindToLifecycle(this, preview) }
Java 示例
@Override protected void onResume() { super.onResume(); textureView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { textureView.getViewTreeObserver().removeOnGlobalLayoutListener(this); int viewWidth = textureView.getMeasuredWidth(); int viewHeight = textureView.getMeasuredHeight(); setupCameraX(viewWidth, viewHeight); } }); } private void setupCameraX(int width, int height) { AspectRatio aspectRatio = AspectRatio.RATIO_1_1; PreviewConfig previewConfig = new PreviewConfig.Builder() .setTargetAspectRatio(aspectRatio) .setTargetRotation(getWindowManager().getDefaultDisplay().getRotation()) .build(); Preview preview = new Preview(previewConfig); preview.setOnPreviewOutputUpdateListener(output -> textureView.setSurfaceTexture(output.getSurfaceTexture())); CameraX.bindToLifecycle(this, preview); }
第三步:为什么不用手动计算的Rational?
CameraX的AspectRatio类已经封装了相机硬件支持的常见比例(比如1:1、4:3、16:9),用这些常量比你手动用View尺寸创建Rational更可靠——因为即使你的View是1:1,某些设备的相机可能不支持精确的像素比例,但CameraX会自动匹配最接近的硬件支持比例,避免抛出异常。
如果一定要手动计算比例,要确保宽高都不为0,并且用AspectRatio.of(width, height)来创建,而不是直接new Rational:
val aspectRatio = AspectRatio.of(width, height)
额外检查项
- 确保已经申请了相机权限(
android.permission.CAMERA),虽然这个错误不是权限导致的,但权限缺失会引发其他相机初始化问题 - 如果你同时使用了其他UseCase(比如ImageCapture),要保证所有UseCase的比例一致,否则CameraX也可能抛出冲突异常
内容的提问来源于stack exchange,提问作者Raghav Sharma

