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

如何修改MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA默认拍照保存目录并实现连拍

实现自定义目录下的连续拍照(基于MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)

嘿,这个需求我之前折腾过,刚好能给你捋清楚怎么实现。首先得明确:MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA是唤起系统相机的连续拍照模式,但默认会把照片丢去DCIM/Camera。要让它存到你指定的Pictures/mypic,核心是提前在MediaStore媒体库里创建好自定义路径的媒体条目,引导系统相机用这个条目来保存照片。

下面是具体的实现步骤,分版本处理更稳妥:

1. 先搞定权限

  • Android 12及以下:得申请WRITE_EXTERNAL_STORAGE权限,Android 6.0+别忘了动态申请哈。
  • Android 13及以上:不用写权限了,但如果要读已拍的照片,得加READ_MEDIA_IMAGES。不过单纯唤起系统拍照的话,本身不需要额外权限。

动态申请权限的代码示例(Kotlin):

private const val PERMISSION_REQUEST_CODE = 1001

// 检查并申请权限
fun checkStoragePermission(context: Activity) {
    if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(context,
            arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
            PERMISSION_REQUEST_CODE)
    }
}

2. 为自定义目录创建媒体库条目

这一步是关键,相当于在系统媒体库中提前“占位”,告诉相机照片要存在哪里。分Android 10+(分区存储)和低版本处理:

Android 10及以上(分区存储时代)

不能直接写绝对路径了,用RELATIVE_PATH指定相对路径:

fun createCustomMediaUri(context: Context): Uri? {
    val contentValues = ContentValues().apply {
        // 给照片起个唯一名字,用时间戳避免重复
        put(MediaStore.Images.Media.DISPLAY_NAME, "mypic_${System.currentTimeMillis()}.jpg")
        put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
        // 指定自定义路径:Pictures/mypic
        put(MediaStore.Images.Media.RELATIVE_PATH, "${Environment.DIRECTORY_PICTURES}/mypic")
        // 标记为待处理,防止其他应用提前访问
        put(MediaStore.Images.Media.IS_PENDING, 1)
    }

    // 插入到媒体库,拿到对应的Uri
    return context.contentResolver.insert(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        contentValues
    )
}

Android 9及以下(传统存储)

可以直接指定绝对路径,先创建目录再生成条目:

fun createCustomMediaUriLegacy(context: Context): Uri? {
    // 创建自定义目录
    val customDir = File(
        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
        "mypic"
    )
    if (!customDir.exists()) customDir.mkdirs()

    // 创建照片文件
    val photoFile = File(customDir, "mypic_${System.currentTimeMillis()}.jpg")
    val contentValues = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, photoFile.name)
        put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
        put(MediaStore.Images.Media.DATA, photoFile.absolutePath)
    }

    return context.contentResolver.insert(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        contentValues
    )
}

3. 唤起连续拍照并绑定自定义路径

INTENT_ACTION_STILL_IMAGE_CAMERA本身不直接支持传Uri,但很多系统相机认MediaStore.EXTRA_OUTPUT这个参数,会把连续拍摄的照片都存到我们指定的目录下。

唤起相机的代码:

fun startContinuousCapture(context: Context) {
    // 根据系统版本选择对应的Uri创建方法
    val customUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        createCustomMediaUri(context)
    } else {
        createCustomMediaUriLegacy(context)
    }

    customUri?.let { uri ->
        val intent = Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA).apply {
            // 传递我们创建的Uri,引导相机存到指定目录
            putExtra(MediaStore.EXTRA_OUTPUT, uri)
            // 可选参数:触发快速连续拍照模式,部分相机支持
            putExtra("android.intent.extra.quickCapture", true)
        }
        // 检查有没有能处理这个Intent的相机应用
        if (intent.resolveActivity(context.packageManager) != null) {
            context.startActivity(intent)
        }
    }
}

4. 监听照片拍摄完成(可选)

如果需要在照片拍好后自动处理(比如上传、压缩),可以注册ContentObserver监听媒体库的变化:

class ImageCaptureObserver(private val context: Context, handler: Handler) : ContentObserver(handler) {
    override fun onChange(selfChange: Boolean, uri: Uri?) {
        super.onChange(selfChange, uri)
        uri?.let {
            // 照片已保存,这里可以获取照片信息
            val cursor = context.contentResolver.query(it, null, null, null, null)
            cursor?.use { c ->
                if (c.moveToFirst()) {
                    val photoPath = c.getString(c.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
                    val fileName = c.getString(c.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME))
                    // 这里做你的后续处理,比如显示、上传...
                }
            }
        }
    }
}

// 在Activity里注册Observer
private lateinit var imageObserver: ImageCaptureObserver

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    imageObserver = ImageCaptureObserver(this, Handler(Looper.getMainLooper()))
    contentResolver.registerContentObserver(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        true, // 监听子目录变化
        imageObserver
    )
}

// 记得在销毁时注销,避免内存泄漏
override fun onDestroy() {
    super.onDestroy()
    contentResolver.unregisterContentObserver(imageObserver)
}

关于Camera Folder这类应用的实现逻辑

其实它们的核心就是我上面说的:提前在MediaStore中创建自定义目录的媒体条目,然后唤起相机时通过Intent参数引导相机使用这个路径。有些还会监听系统相机的广播或者媒体库变化,批量处理拍摄后的照片,确保所有照片都落到指定目录里。

⚠️ 注意:不同品牌的系统相机可能对EXTRA_OUTPUT的支持有差异,如果遇到某些机型不生效的情况,那可能得考虑自定义相机实现(用CameraX或者Camera2 API),这样能完全控制拍照流程和保存路径,但开发成本会高一些。

内容的提问来源于stack exchange,提问作者ashraf abdalghani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:31:38