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

Android摄影App中Bitmap无法获取最大分辨率,ImageView显示画质骤降求助

解决Android上传Fragment中图片显示画质损失的问题

嘿,我太懂这种糟心的感觉了——辛辛苦苦拍的高清图,放到ImageView里就糊成马赛克,大概率是你拿到的是系统返回的压缩缩略图,而非真正的原图!下面给你拆解具体的解决办法,分相机和相册两种场景来处理:

一、相机Intent:别依赖返回的data,直接指定原图保存路径

很多人习惯用Intent(MediaStore.ACTION_IMAGE_CAPTURE)后,从返回的data里拿Bitmap,但这个Bitmap是系统压缩后的缩略图,尺寸通常只有几百像素,画质自然差。正确的做法是让相机直接把原图保存到你指定的文件里:

1. 配置FileProvider(Android 7.0+必备)

首先在AndroidManifest.xml里注册FileProvider:

<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>

然后在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>

2. 启动相机并指定原图保存路径

private lateinit var photoFile: File

private fun takePhoto() {
    // 创建保存原图的文件
    photoFile = createImageFile()
    val photoUri = FileProvider.getUriForFile(
        requireContext(),
        "${requireContext().packageName}.fileprovider",
        photoFile
    )
    // 启动相机Intent,指定输出路径
    val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri)
    startActivityForResult(takePictureIntent, REQUEST_CODE_CAMERA)
}

// 生成唯一的图片文件
private fun createImageFile(): File {
    val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
    val storageDir = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)
    return File.createTempFile(
        "JPEG_${timeStamp}_", /* prefix */
        ".jpg", /* suffix */
        storageDir /* directory */
    ).apply {
        // 保存文件路径,后续可以用来读取
        absolutePath
    }
}

二、相册Intent:通过ContentResolver获取原图

从相册选图时,data.getData()返回的Uri可能指向缩略图,也可能是原图的Uri,但直接解码的话要注意避免OOM,最好配合合理的缩放:

1. 处理相册返回的结果

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode == Activity.RESULT_OK) {
        when (requestCode) {
            REQUEST_CODE_GALLERY -> {
                data?.data?.let { uri ->
                    // 获取ImageView的目标尺寸,避免加载过大的Bitmap导致OOM
                    val targetWidth = imageView.width
                    val targetHeight = imageView.height
                    // 通过ContentResolver读取原图并缩放
                    val originalBitmap = decodeSampledBitmapFromUri(uri, targetWidth, targetHeight)
                    imageView.setImageBitmap(originalBitmap)
                    // 或者用Glide等图片库更省心:
                    // Glide.with(this).load(uri).centerCrop().into(imageView)
                }
            }
            REQUEST_CODE_CAMERA -> {
                // 读取相机保存的原图文件并缩放
                val targetWidth = imageView.width
                val targetHeight = imageView.height
                val originalBitmap = decodeSampledBitmapFromFile(photoFile.absolutePath, targetWidth, targetHeight)
                imageView.setImageBitmap(originalBitmap)
            }
        }
    }
}

2. 实现Bitmap的高效缩放(避免OOM)

// 从Uri解码并缩放Bitmap
private fun decodeSampledBitmapFromUri(uri: Uri, reqWidth: Int, reqHeight: Int): Bitmap {
    val inputStream = requireContext().contentResolver.openInputStream(uri)
    // 先获取Bitmap的尺寸,不加载到内存
    val options = BitmapFactory.Options().apply {
        inJustDecodeBounds = true
        BitmapFactory.decodeStream(inputStream, null, this)
        inputStream?.close()
    }
    // 计算缩放比例
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
    // 加载缩放后的Bitmap
    options.inJustDecodeBounds = false
    val newInputStream = requireContext().contentResolver.openInputStream(uri)
    return BitmapFactory.decodeStream(newInputStream, null, options)!!.apply {
        newInputStream?.close()
    }
}

// 从文件解码并缩放Bitmap
private fun decodeSampledBitmapFromFile(path: String, reqWidth: Int, reqHeight: Int): Bitmap {
    val options = BitmapFactory.Options().apply {
        inJustDecodeBounds = true
        BitmapFactory.decodeFile(path, this)
    }
    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
        // 找到最大的inSampleSize,使得缩放后的尺寸不小于目标尺寸
        while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
            inSampleSize *= 2
        }
    }
    return inSampleSize
}

三、ImageView的额外配置

别忘了给ImageView设置合适的属性,让图片显示更清晰:

<ImageView
    android:id="@+id/imageView"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:adjustViewBounds="true"
    android:scaleType="centerCrop"
    android:antiAlias="true" />

android:antiAlias="true"可以开启抗锯齿,让边缘更平滑;scaleType根据你的需求选centerCrop或fitCenter。


内容的提问来源于stack exchange,提问作者alfonso claros

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:03:42