如何修改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

