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

Android Kotlin使用FileProvider调用相机后ImageView黑屏无响应问题

问题解决:Fragment中调用相机后显示图片黑屏无响应

核心问题分析

你当前的代码存在两个致命问题:

  1. 启动相机后立即解码图片,此时相机还未完成拍摄,photoFile中没有有效图像数据,甚至可能为空。
  2. 直接解码原图会导致内存溢出,引发界面黑屏无响应。

分步解决方案

1. 修正相机调用逻辑,在拍摄完成后再加载图片

必须在相机返回成功结果的回调中处理图片加载,不能提前执行解码操作。

2. 对图片进行采样压缩,避免内存不足

相机拍摄的原图分辨率极高,直接加载会占用大量内存,必须按ImageView的尺寸压缩后再显示。

3. 确保FileProvider配置正确

如果FileProvider的权限或路径配置错误,相机无法写入图片文件,也会导致后续加载失败。


完整修正代码

第一步:定义全局变量与相机启动代码

private var photoFile: File? = null
private const val FILE_NAME = "temp_photo"
private const val REQUEST_CODE_CAMERA = 3

// 启动相机的方法
private fun startCamera() {
    val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    // 获取应用私有图片目录
    activity?.getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.let { storageDir ->
        try {
            // 创建临时图片文件
            photoFile = File.createTempFile(FILE_NAME, ".jpg", storageDir)
            photoFile?.let { file ->
                // 获取FileProvider的Uri
                val fileProviderUri = FileProvider.getUriForFile(
                    requireContext(),
                    "com.example.an0nym0us.fileprovider", // 需与AndroidManifest中配置一致
                    file
                )
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileProviderUri)
                startActivityForResult(takePictureIntent, REQUEST_CODE_CAMERA)
            }
        } catch (e: IOException) {
            e.printStackTrace()
            // 处理文件创建失败的情况
        }
    }
}

第二步:重写onActivityResult处理返回结果

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    // 确认相机拍摄成功
    if (requestCode == REQUEST_CODE_CAMERA && resultCode == Activity.RESULT_OK) {
        photoFile?.let { file ->
            // 按ImageView尺寸压缩加载图片
            val targetWidth = yourImageView.width
            val targetHeight = yourImageView.height
            val bitmap = decodeSampledBitmapFromFile(file.absolutePath, targetWidth, targetHeight)
            yourImageView.setImageBitmap(bitmap)
        }
    }
}

第三步:添加图片压缩工具方法

// 采样压缩解码图片
private fun decodeSampledBitmapFromFile(path: String, reqWidth: Int, reqHeight: Int): Bitmap {
    val options = BitmapFactory.Options()
    // 先获取图片原始尺寸,不加载到内存
    options.inJustDecodeBounds = true
    BitmapFactory.decodeFile(path, options)
    
    // 计算合适的采样率
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
    
    // 解码图片到内存
    options.inJustDecodeBounds = false
    return BitmapFactory.decodeFile(path, options)
}

// 计算采样率,确保压缩后图片尺寸不超过目标尺寸
private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
    val height = options.outHeight
    val width = options.outWidth
    var inSampleSize = 1
    
    if (height > reqHeight || width > reqWidth) {
        val halfHeight = height / 2
        val halfWidth = width / 2
        
        while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
            inSampleSize *= 2
        }
    }
    return inSampleSize
}

第四步:检查FileProvider配置

在AndroidManifest.xml中添加FileProvider声明:

<application>
    ...
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.example.an0nym0us.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目录需手动创建):

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 对应getExternalFilesDir(Environment.DIRECTORY_PICTURES)的路径 -->
    <external-files-path
        name="my_pictures"
        path="Pictures" />
</paths>

第五步:权限检查

Android 6.0及以上系统需动态申请相机权限:

// 检查并申请相机权限
private fun checkCameraPermission() {
    if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA)
        != PackageManager.PERMISSION_GRANTED
    ) {
        ActivityCompat.requestPermissions(
            requireActivity(),
            arrayOf(Manifest.permission.CAMERA),
            100
        )
    } else {
        startCamera()
    }
}

// 处理权限申请结果
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == 100 && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        startCamera()
    } else {
        // 处理权限被拒绝的情况
    }
}

关键注意事项

  • 必须等待相机返回RESULT_OK后再加载图片,否则文件内容无效。
  • 永远不要直接加载原图,采样压缩是避免内存溢出的必要操作。
  • FileProvider的authorities必须和代码、配置文件完全一致,否则相机无法写入文件。

内容的提问来源于stack exchange,提问作者Joshua Perez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 04:40:29