Android中如何为CameraX设置宽高比以支持1:1/4:3/16:9拍摄
CameraX实现1:1、4:3、16:9比例拍照
核心思路
CameraX的ImageCapture和Preview用例都支持通过setTargetAspectRatio()指定宽高比,切换比例时只需更新对应参数并重新绑定相机用例即可。若需要严格保证输出图片比例,可在拍照后对原始图片进行裁剪。
实现步骤
1. 初始化相机用例
先定义全局变量保存当前比例和ImageCapture实例,然后初始化相机绑定预览和拍照用例:
private lateinit var imageCapture: ImageCapture private var currentAspectRatio = AspectRatio.RATIO_4_3 // 默认4:3比例 private fun startCamera() { val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext()) cameraProviderFuture.addListener({ val cameraProvider = cameraProviderFuture.get() // 配置预览用例,匹配当前比例 val preview = Preview.Builder() .setTargetAspectRatio(currentAspectRatio) .build() .also { it.setSurfaceProvider(binding.viewFinder.surfaceProvider) } // 配置拍照用例,设置目标比例和低延迟模式 imageCapture = ImageCapture.Builder() .setTargetAspectRatio(currentAspectRatio) .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build() // 选择后置摄像头 val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA try { // 先解绑所有用例,再重新绑定 cameraProvider.unbindAll() cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture) } catch(exc: Exception) { Log.e("CameraX", "绑定相机用例失败", exc) } }, ContextCompat.getMainExecutor(requireContext())) }
2. 切换宽高比
提供切换方法,更新当前比例后重新初始化相机即可:
fun switchAspectRatio(ratio: Int) { when(ratio) { AspectRatio.RATIO_1_1, AspectRatio.RATIO_4_3, AspectRatio.RATIO_16_9 -> { currentAspectRatio = ratio startCamera() } } } // 调用示例: // switchAspectRatio(AspectRatio.RATIO_1_1) // 切换为1:1 // switchAspectRatio(AspectRatio.RATIO_16_9) // 切换为16:9
3. 拍照后裁剪(可选)
若相机输出的原始图片比例与目标比例有偏差(比如部分设备传感器比例限制),可在拍照完成后裁剪图片到指定比例:
private fun takePhoto() { val outputFile = createImageFile() val outputOptions = ImageCapture.OutputFileOptions.Builder(outputFile).build() imageCapture.takePicture( outputOptions, ContextCompat.getMainExecutor(requireContext()), object : ImageCapture.OnImageSavedCallback { override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { // 裁剪图片到当前目标比例 cropToTargetRatio(Uri.fromFile(outputFile), currentAspectRatio) } override fun onError(exc: ImageCaptureException) { Log.e("CameraX", "拍照失败", exc) } } ) } private fun cropToTargetRatio(imageUri: Uri, aspectRatio: Int) { val bitmap = MediaStore.Images.Media.getBitmap(requireContext().contentResolver, imageUri) val targetWidth: Int val targetHeight: Int // 根据目标比例计算裁剪尺寸 when(aspectRatio) { AspectRatio.RATIO_1_1 -> { val size = min(bitmap.width, bitmap.height) targetWidth = size targetHeight = size } AspectRatio.RATIO_4_3 -> { targetHeight = if(bitmap.width * 3 > bitmap.height *4) bitmap.height else bitmap.width *3 /4 targetWidth = targetHeight *4 /3 } AspectRatio.RATIO_16_9 -> { targetHeight = if(bitmap.width *9 > bitmap.height *16) bitmap.height else bitmap.width *9 /16 targetWidth = targetHeight *16 /9 } else -> { targetWidth = bitmap.width targetHeight = bitmap.height } } // 计算裁剪偏移量,保证居中裁剪 val xOffset = (bitmap.width - targetWidth) / 2 val yOffset = (bitmap.height - targetHeight) / 2 val croppedBitmap = Bitmap.createBitmap(bitmap, xOffset, yOffset, targetWidth, targetHeight) // 保存裁剪后的图片(需处理权限和文件存储逻辑) saveCroppedBitmap(croppedBitmap) } // 辅助方法:创建临时图片文件 private fun createImageFile(): File { val storageDir = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES) return File.createTempFile( "JPEG_${System.currentTimeMillis()}_", ".jpg", storageDir ) }
注意事项
- 预览布局需适配比例:建议使用
CameraX PreviewView,并设置scaleType为ScaleType.FIT_CENTER或ScaleType.CENTER_CROP,避免画面拉伸。 - 权限处理:确保已申请
CAMERA和WRITE_EXTERNAL_STORAGE(或使用MediaStore)权限。
内容的提问来源于stack exchange,提问作者Dkathayat1
相关产品推荐
相关产品推荐

