Android Kotlin:ActivityResultContracts.TakePicture()空图片删除方案
解决相机取消拍照时生成空图片的问题
核心问题原因
你当前的代码提前创建了MediaStore的图片记录(也就是newUri在启动相机前就通过insert生成了),所以即使用户取消拍照,这条空记录已经存在于MediaStore中,对应生成0KB的空文件。
解决方案
调整逻辑:仅在确认相机权限通过、即将启动相机时才创建MediaStore记录;用状态变量持有当前拍照的Uri,在相机回调中判断拍照是否成功,失败则删除空记录。
修改后的完整代码
@Composable fun CameraCaptureButton(context: Context) { // 用状态变量持有当前待拍照的Uri,避免回调中无法获取 val currentCameraUri = remember { mutableStateOf<Uri?>(null) } // 相机启动器:回调参数isSuccess表示拍照是否成功 val cameraLauncher = rememberLauncherForActivityResult(ActivityResultContracts.TakePicture()) { isSuccess -> currentCameraUri.value?.let { uri -> if (!isSuccess) { // 拍照取消/失败时删除空文件 runCatching { context.contentResolver.delete(uri, null, null) } } // 清空状态,避免重复处理 currentCameraUri.value = null } } // 相机权限请求启动器 val permissionLauncher = rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> if (isGranted) { createCameraUriAndLaunch(context, currentCameraUri, cameraLauncher) } } Button( modifier = Modifier.size(90.dp, 60.dp), onClick = { val permissionCheckResult = ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) if (permissionCheckResult == PackageManager.PERMISSION_GRANTED) { createCameraUriAndLaunch(context, currentCameraUri, cameraLauncher) } else { permissionLauncher.launch(Manifest.permission.CAMERA) } } ) { Icon( painter = painterResource(id = R.drawable.camera), contentDescription = "拍照按钮", tint = MaterialTheme.colorScheme.tertiary ) } } // 抽离创建MediaStore Uri并启动相机的逻辑 private fun createCameraUriAndLaunch( context: Context, currentUri: MutableState<Uri?>, launcher: ActivityResultLauncher<Uri> ) { val dUri = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) } else { MediaStore.Images.Media.EXTERNAL_CONTENT_URI } val fileName = "ride_map-camera-${System.currentTimeMillis()}.jpg" val newImageDetails = ContentValues().apply { put(MediaStore.Images.Media.DISPLAY_NAME, fileName) put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg") // 可选:指定保存到DCIM下的自定义文件夹(Android 10+支持) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { put(MediaStore.Images.Media.RELATIVE_PATH, "DCIM/RideMap") } } // 插入MediaStore记录,获取Uri val newUri = context.contentResolver.insert(dUri, newImageDetails) newUri?.let { currentUri.value = it launcher.launch(it) } }
关键修改点说明
- 延迟创建MediaStore记录:把
newUri的创建从Compose节点初始化移到权限通过后的点击逻辑中,避免提前生成空记录占用存储。 - 用状态变量持有Uri:通过
mutableStateOf保存当前启动相机的Uri,确保回调中能拿到要删除的目标文件。 - 回调中清理空文件:当拍照回调返回
isSuccess = false时,调用contentResolver.delete()删除对应的Uri,彻底清理空文件。 - 异常安全处理:用
runCatching包裹删除操作,避免删除时因权限或文件不存在等问题抛出异常。 - 可选路径优化:添加
RELATIVE_PATH指定自定义保存文件夹,让照片分类更规整,方便用户查找。
内容的提问来源于stack exchange,提问作者Ruineie
相关产品推荐
相关产品推荐

