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

如何在Jetpack Compose中调用系统原生相机应用拍摄并存储照片

Jetpack Compose 调用系统原生相机拍照存储实现

首先纠正一个常见误区:废弃的是旧版Camera框架的内置相机实现API,跨应用调用系统相机的Intent协议从来没有被废弃,也和你用View还是Compose没有关系。Camera2、CameraX是用来在应用内搭建自定义相机界面的,如果你只是想拉起系统自带相机拍全分辨率照片、存到指定路径,完全不需要引入这两个库,用Compose适配的Activity Result API就能实现,全程不需要写View相关代码。

1. 前置配置

首先在AndroidManifest.xml里加必要配置:

  • 权限声明:
    • Android 13及以上:如果需要读取拍摄的照片展示,声明android.permission.READ_MEDIA_IMAGES
    • Android 12及以下:声明android.permission.READ_EXTERNAL_STORAGE
    • Android 9及以下:额外声明android.permission.WRITE_EXTERNAL_STORAGE
  • 配置FileProvider:Android 7.0以上禁止直接向外部应用传递file://格式的文件路径,必须通过FileProvider生成可访问的content:// Uri,配置如下:
<application>
    <!-- 其他应用配置 -->
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
</application>

在res目录下新建xml文件夹,创建file_paths.xml配置允许FileProvider访问的路径:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 配置应用专属图片目录为可访问路径 -->
    <external-files-path
        name="my_app_photos"
        path="Pictures/" />
</paths>

2. Compose中实现拍照逻辑

直接用AndroidX提供的rememberLauncherForActivityResult注册拍照回调,不需要依赖旧版View体系的onActivityResult:

@Composable
fun CameraCaptureScreen() {
    val context = LocalContext.current
    // 持有拍摄照片的目标Uri
    var capturedPhotoUri by remember { mutableStateOf<Uri?>(null) }
    // 注册系统相机拍照结果接收器
    val takePictureLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.TakePicture()
    ) { isSuccess ->
        if (isSuccess) {
            // 拍照成功,capturedPhotoUri就是全分辨率照片的存储地址,可直接加载、上传、处理
            capturedPhotoUri?.let { uri ->
                // 直接调用图片加载库加载即可,比如AsyncImage(model = uri, ...)
            }
        }
        // 失败/用户取消的场景按需处理即可
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Button(onClick = {
            // 生成照片存储文件
            val photoFile = File(
                context.getExternalFilesDir(Environment.DIRECTORY_PICTURES),
                "CAPTURE_${System.currentTimeMillis()}.jpg"
            )
            // 生成FileProvider可用的content Uri
            capturedPhotoUri = FileProvider.getUriForFile(
                context,
                "${context.packageName}.fileprovider",
                photoFile
            )
            // 拉起系统相机
            takePictureLauncher.launch(capturedPhotoUri!!)
        }) {
            Text("调用系统相机拍照")
        }

        // 拍照成功后展示照片
        capturedPhotoUri?.let { uri ->
            AsyncImage(
                model = uri,
                contentDescription = "拍摄的照片",
                modifier = Modifier.size(300.dp)
            )
        }
    }
}

3. 可选:保存照片到系统公共相册

如果需要让拍摄的照片在系统相册全局可见,不需要存在应用专属目录,可以直接把输出Uri指定为MediaStore公共图片集合的地址,不需要额外配置FileProvider:

// 创建公共目录图片Uri的方法
fun createPublicPhotoUri(context: Context): Uri {
    val contentValues = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, "IMG_${System.currentTimeMillis()}.jpg")
        put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/MyApp")
        }
    }
    return context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)!!
}

把这个方法生成的Uri直接传给takePictureLauncher.launch()即可,系统相机会直接把照片写入公共相册目录,Android 10及以上不需要申请存储权限。

注意:很多旧教程提到调用相机后只返回缩略图,是因为没有给Intent传入输出Uri参数导致的,只要传入了指定的输出Uri,系统相机默认会写入全分辨率原图,不会返回缩略图,这个行为在所有主流Android版本上保持一致。

内容的提问来源于stack exchange,提问作者Raj Narayanan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 08:33:21