Android CameraX相机应用拍照后如何添加TextView水印并自动保存
CameraX拍摄图片叠加界面TextView水印的修复方案
问题核心成因
- 拍摄流程逻辑分支错位:现有代码直接将ImageCapture返回的原始图像字节流写入存储,完全跳过了Canvas绘制水印的环节,最终存盘的始终是未加工的原始照片
- 坐标映射逻辑不匹配:界面上的水印TextView坐标是相对于预览控件的布局坐标,和相机输出的全尺寸Bitmap坐标系没有做比例换算,就算执行了绘制逻辑,水印也会落在图片可视区域外
- 图像方向未校正:相机传感器输出的原始图像默认存在角度偏移,没有完成旋转、镜像校正的话,绘制的水印会随旋转错位被裁剪
- 异步流程执行顺序错乱:绘制水印、保存新Bitmap的代码写在了原始图存储逻辑的return分支后方,因为代码提前返回,这部分逻辑根本没有被执行到
逐点修复实现
- 替换原始图像直接存盘的逻辑,优先完成图像的方向校正
不要直接把ImageProxy的字节流丢给MediaStore,先通过CameraX提供的扩展方法拿到经过旋转、镜像校正的可编辑Bitmap:imageCapture.takePicture( ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageCapturedCallback() { override fun onCaptureSuccess(image: ImageProxy) { // 校正后的原始Bitmap val baseBitmap = image.toBitmap().rotate( degrees = image.imageInfo.rotationDegrees.toFloat(), flip = image.imageInfo.lensFacing == CameraSelector.LENS_FACING_FRONT ) // 后续绘制逻辑写在这里 image.close() } } ) - 实现预览布局坐标到输出Bitmap坐标的比例换算
拿到界面上水印TextView的真实布局偏移量,按照预览View和输出Bitmap的尺寸比例,计算出水印在Bitmap上的实际绘制位置:val scaleX = baseBitmap.width.toFloat() / previewView.width val scaleY = baseBitmap.height.toFloat() / previewView.height // 拿到TextView在父布局内的真实坐标 val watermarkOffsetX = watermarkTv.left * scaleX val watermarkOffsetY = watermarkTv.top * scaleY - 完成Canvas水印绘制
新建可变Bitmap作为绘制载体,完全复用TextView自身的字体大小、颜色属性,保证绘制出来的水印和界面上显示的视觉效果完全一致:val resultBitmap = baseBitmap.copy(Bitmap.Config.ARGB_8888, true) val canvas = Canvas(resultBitmap) val watermarkPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = watermarkTv.currentTextColor textSize = watermarkTv.textSize } canvas.drawText( watermarkTv.text.toString(), watermarkOffsetX, watermarkOffsetY, watermarkPaint ) - 替换存盘对象
把原来直接存原始图的逻辑,改为将绘制完成的resultBitmap写入MediaStore,完成最终带水印的图片保存
额外避坑提示
- 确保拍摄回调里的
ImageProxy在逻辑处理完成后及时调用close(),避免CameraX管线阻塞 - 如果预览View有边缘裁剪的逻辑,坐标换算时要同步计算裁剪偏移量,保证水印位置和界面显示完全对齐
- 存盘前要判断当前应用是否已经获得了媒体存储权限,避免因为权限缺失导致带水印的图片写入失败
内容的提问来源于stack exchange,提问作者Georgi Tasev
相关产品推荐
相关产品推荐

