如何将CameraX拍摄的图片保存到共享存储以在相册中访问
如何将CameraX拍摄的图片保存到共享存储并在系统相册显示
要实现将CameraX拍摄的图片保存到共享存储并让系统相册识别,你需要修改takePhoto方法的实现,改用基于MediaStore的文件保存逻辑,而不是仅在内存中处理Bitmap。以下是具体的修改步骤和完整代码:
1. 权限适配
确保你的应用拥有必要的存储权限:
- Android 9(API 28及以下):需要申请
WRITE_EXTERNAL_STORAGE权限 - Android 10+(API 29及以上):无需申请写入权限,直接通过
MediaStore插入媒体文件即可
在hasRequiredPermissions()方法中补充权限检查逻辑:
private fun hasRequiredPermissions(): Boolean { return if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED } else { // Android 10+ 无需写入权限,只需确保相机权限已获取 ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED } }
2. 构建MediaStore保存参数
创建方法生成图片的保存路径和元数据,确保系统相册能识别:
private fun createImageContentValues(): ContentValues { val displayName = "CameraX_${System.currentTimeMillis()}.jpg" return ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, displayName) put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/CameraX") put(MediaStore.MediaColumns.IS_PENDING, 1) } } }
3. 修改takePhoto方法,使用文件保存回调
替换原来的OnImageCapturedCallback,改用takePicture的重载方法,传入OutputFileOptions并处理保存结果:
private fun takePhoto( controller: LifecycleCameraController, onPhotoTaken: (Bitmap, Uri) -> Unit // 新增Uri参数,返回保存后的图片路径 ) { if (!hasRequiredPermissions()) { return } // 1. 生成MediaStore内容参数 val contentValues = createImageContentValues() val imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) ?: run { Log.e("Camera", "Failed to create image URI") return } // 2. 构建OutputFileOptions val outputOptions = ImageCapture.OutputFileOptions.Builder(contentResolver, imageUri, contentValues).build() // 3. 调用takePicture保存到文件 controller.takePicture( outputOptions, ContextCompat.getMainExecutor(applicationContext), object : ImageCapture.OnImageSavedCallback { override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { // 4. 处理图片旋转(保持你原来的翻转逻辑) val imageBitmap = MediaStore.Images.Media.getBitmap(contentResolver, imageUri) val matrix = Matrix().apply { if (controller.cameraSelector == CameraSelector.DEFAULT_BACK_CAMERA) { postScale(1f, 1f) } else { // 前置摄像头水平翻转 postScale(-1f, 1f) } } val rotatedBitmap = Bitmap.createBitmap( imageBitmap, 0, 0, imageBitmap.width, imageBitmap.height, matrix, true ) // 5. 将旋转后的Bitmap写回文件 contentResolver.openOutputStream(imageUri)?.use { outputStream -> rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream) } // 6. 通知系统相册刷新(Android 10+ 需要移除IS_PENDING标记) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { contentValues.clear() contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0) contentResolver.update(imageUri, contentValues, null, null) } else { // 低版本发送广播刷新相册 sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, imageUri)) } // 回调返回处理后的Bitmap和保存路径 onPhotoTaken(rotatedBitmap, imageUri) imageBitmap.recycle() rotatedBitmap.recycle() } override fun onError(exception: ImageCaptureException) { Log.e("Camera", "Couldn't save photo: ", exception) // 保存失败时删除临时Uri contentResolver.delete(imageUri, null, null) } } ) }
关键说明
- MediaStore优势:直接通过系统媒体库保存,无需手动创建文件夹,系统相册会自动识别
- 图片旋转处理:保持了你原来的前后摄像头翻转逻辑,确保图片方向正确
- 相册刷新:Android 10+ 通过更新
IS_PENDING标记通知系统,低版本发送广播触发扫描 - 资源回收:及时回收Bitmap避免内存泄漏
内容的提问来源于stack exchange,提问作者garrettlynchirl
相关产品推荐
相关产品推荐

