Android自定义相机拍照:如何仅保留界面圆形显示区域的图像?
嘿,这个问题我之前做自定义相机的时候也踩过坑!你界面上的圆形覆盖层只是视觉上的遮罩,相机本身还是会拍摄完整的传感器区域,所以得额外处理才能拿到你要的圆形区域图像。下面给你两种实用的方案,按需选就行:
方案一:拍照后裁剪(兼容性拉满,新手友好)
这是最稳妥的方式,不管你用的是旧的Camera API还是新的Camera2,都能搞定。核心思路是:先拿到相机拍的完整图像,再把它裁剪成和你界面上圆形区域对应的部分。
具体步骤:
建立预览与拍照图像的坐标映射
相机预览的尺寸(比如1920×1080)和最终拍照的图像尺寸(比如4032×3024)比例大概率不一样,而且竖屏拍照时图像还会旋转,所以得先把你界面上圆形遮罩的坐标,转换成拍照图像上的对应位置:- 先获取你相机预览View(SurfaceView/TextureView)的实际宽高,还有圆形遮罩相对于这个View的left、top、宽高
- 计算缩放比例:
x缩放比 = 拍照图像宽度 / 预览View宽度,y缩放比 = 拍照图像高度 / 预览View高度 - 如果是竖屏,记得先把拍照图像旋转到正确方向,再用上面的比例计算坐标
对Bitmap进行圆形裁剪
把拍照得到的图像转成Bitmap后,用Canvas和Path来裁剪出圆形区域,代码示例:
// 假设originalBitmap是拍照得到的原始图像Bitmap // circleLeft、circleTop是圆形遮罩在预览View上的左上角坐标,circleWidth是遮罩直径 float scaleX = (float) originalBitmap.getWidth() / previewView.getWidth(); float scaleY = (float) originalBitmap.getHeight() / previewView.getHeight(); // 计算原始图像中对应圆形区域的矩形 RectF srcRect = new RectF( circleLeft * scaleX, circleTop * scaleY, (circleLeft + circleWidth) * scaleX, (circleTop + circleWidth) * scaleY ); // 创建和圆形遮罩等大的空白Bitmap Bitmap croppedBitmap = Bitmap.createBitmap(circleWidth, circleWidth, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(croppedBitmap); // 绘制圆形裁剪路径 Path circlePath = new Path(); circlePath.addCircle(circleWidth/2f, circleWidth/2f, circleWidth/2f, Path.Direction.CW); canvas.clipPath(circlePath); // 把原始图像的对应区域画到新Bitmap上 canvas.drawBitmap(originalBitmap, srcRect, new RectF(0, 0, circleWidth, circleWidth), null); // 现在croppedBitmap就是你要的圆形图像了
方案二:用Camera2 API直接指定拍照区域(高效进阶)
如果你的App只需要兼容Android 5.0(API 21)及以上,那可以用Camera2 API让相机硬件直接拍摄你要的区域,省去后期裁剪的步骤,性能更好。
关键是在创建拍照请求时,设置SCALER_CROP_REGION参数,指定传感器上的裁剪区域:
// 先通过CameraCharacteristics获取传感器的有效尺寸 CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId); Rect sensorActiveSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); // 把你界面上的圆形区域转换成传感器坐标系统的矩形(这里需要处理方向和比例转换,具体逻辑根据你的布局调整) Rect cropRect = calculateSensorCropRect(circleArea, sensorActiveSize, previewSize, displayRotation); // 创建拍照请求 CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); // 设置裁剪区域 captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, cropRect); // 其他必要设置:自动对焦、闪光灯、输出目标等 // ... // 发送拍照请求 cameraCaptureSession.capture(captureBuilder.build(), captureCallback, null);
这里的calculateSensorCropRect需要你自己实现,核心是把界面上的坐标转换为相机传感器的横屏坐标,还要保证裁剪区域的比例和拍照输出的图像比例一致,不然相机可能会自动调整。
几个必踩的坑提醒
- 图像方向问题:竖屏拍照时,相机输出的图像通常是横的,一定要先旋转到正确方向再处理,不然裁剪出来的区域会歪
- 内存溢出:如果拍照图像尺寸很大,直接转Bitmap可能会OOM,建议用
BitmapFactory.Options.inSampleSize先缩小图像再裁剪 - 遮罩与实际区域对齐:测试时可以给遮罩加个边框,对比裁剪后的图像,确保坐标转换没有错误
内容的提问来源于stack exchange,提问作者Achy97
相关产品推荐
相关产品推荐

