求助:使用Android Camera API保存原始尺寸图片失败,代码排查
解决Fragment中相机拍照仅保存低分辨率图片的问题
嘿,这个问题我之前也踩过坑!核心原因是你大概率用了默认的相机Intent启动方式,没有指定输出文件路径——Android的ACTION_IMAGE_CAPTURE默认只会返回一张缩略图,而不是原始尺寸的照片。要保存原图,必须让相机直接把照片写入你指定的文件里,而不是通过Intent的data返回。
下面是具体的解决步骤和代码示例:
1. 先创建用于保存原图的文件(适配Android版本)
首先要处理不同Android版本的存储规则:Android 10及以上推荐用FileProvider来规避权限问题,Android 9及以下可以直接创建文件。这里给出兼容性更好的FileProvider适配示例:
// 在Fragment中创建保存照片的方法 private fun createImageFile(): File? { // 获取应用私有存储目录,无需额外读写权限 val storageDir = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES) return try { // 用时间戳生成唯一文件名,避免重复 File.createTempFile( "JPEG_${System.currentTimeMillis()}_", ".jpg", storageDir ) } catch (ex: IOException) { ex.printStackTrace() null } }
2. 配置FileProvider(必须步骤)
在AndroidManifest.xml里添加FileProvider的声明:
<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文件:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="my_images" path="Pictures" /> </paths>
3. 启动相机并指定输出Uri
启动相机时,必须添加EXTRA_OUTPUT参数,把刚才创建的文件Uri传进去,让相机直接把原图写入这个文件:
private val REQUEST_CAMERA_PERMISSION = 1001 private val REQUEST_IMAGE_CAPTURE = 1002 private var mCurrentPhotoPath: String? = null // 启动相机的方法 private fun launchCamera() { // 先检查相机权限(Android 6+需要动态申请) if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ) { ActivityCompat.requestPermissions( requireActivity(), arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION ) return } val imageFile = createImageFile() ?: return mCurrentPhotoPath = imageFile.absolutePath // 通过FileProvider获取文件Uri val imageUri = FileProvider.getUriForFile( requireContext(), "${requireContext().packageName}.fileprovider", imageFile ) val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) // 关键:指定输出Uri,让相机直接写入原图 takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri) // 授予相机临时读写权限 requireContext().grantUriPermission( "com.android.camera", imageUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION ) startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE) }
4. 在onActivityResult中处理原图
注意:因为已经指定了输出文件,data参数会是null,直接读取之前保存的mCurrentPhotoPath对应的文件即可,这就是原始尺寸的照片:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) { mCurrentPhotoPath?.let { path -> val imageFile = File(path) if (imageFile.exists()) { // 添加到RecyclerView的ArrayList yourPhotoList.add(imageFile) yourAdapter.notifyDataSetChanged() // 存入Photos表 savePhotoToDatabase(imageFile) } } } }
额外推荐:用新的Activity Result API(更简洁)
如果你的项目基于AndroidX,建议弃用startActivityForResult,改用ActivityResultContracts.TakePicture,代码更简洁且兼容性更好:
// 初始化ActivityResultLauncher private val takePictureLauncher = registerForActivityResult(ActivityResultContracts.TakePicture()) { success -> if (success) { mCurrentPhotoPath?.let { path -> val imageFile = File(path) // 后续处理和上面一致 yourPhotoList.add(imageFile) yourAdapter.notifyDataSetChanged() savePhotoToDatabase(imageFile) } } } // 启动相机的方法改成这样 private fun launchCameraWithNewAPI() { // 权限检查同上... val imageFile = createImageFile() ?: return mCurrentPhotoPath = imageFile.absolutePath val imageUri = FileProvider.getUriForFile( requireContext(), "${requireContext().packageName}.fileprovider", imageFile ) takePictureLauncher.launch(imageUri) }
这样处理后,你就能拿到原始分辨率的照片了,再也不会只有缩略图啦。
内容的提问来源于stack exchange,提问作者mmmmm
相关产品推荐
相关产品推荐

