You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 09:37:56