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

Kotlin Uri转缩放Bitmap后图片侧向旋转问题排查

问题根因

图片旋转的核心错误是解码流程没有处理图片EXIF元数据中存储的朝向信息:

  • 手机拍摄、部分来源的图片会在EXIF信息中标记拍摄时的设备朝向,系统图片组件、常规图片加载库(包括你使用的AsyncImage)直接加载原始Uri时,会自动读取该参数校正图片朝向。
  • 你使用的BitmapFactory.decodeStream方法解码图片流时,会直接丢弃所有EXIF元数据,仅按照图像传感器输出的原始像素方向生成Bitmap,后续将这个未校正方向的Bitmap存储为文件再加载,自然会出现侧向旋转的问题。
修复方法

在解码得到原始Bitmap后,先从原始Uri读取EXIF方向参数,将Bitmap旋转到正确角度后再执行后续存储逻辑:

  • 先引入AndroidX的ExifInterface依赖,用于读取图片EXIF信息,在模块级build.gradle的dependencies块中添加:
implementation "androidx.exifinterface:exifinterface:1.3.7"
  • 替换原有的decodeUri方法,增加方向校正逻辑:
fun decodeUri(c: Context, uri: Uri?, requiredSize: Int): Bitmap? {
    return try {
        // 计算采样缩放比例,逻辑与原代码一致
        val boundsOptions = BitmapFactory.Options().apply {
            inJustDecodeBounds = true
        }
        c.contentResolver.openInputStream(uri!!).use { stream ->
            BitmapFactory.decodeStream(stream, null, boundsOptions)
        }
        var widthTmp = boundsOptions.outWidth
        var heightTmp = boundsOptions.outHeight
        var scale = 1
        while (true) {
            if (widthTmp / 2 < requiredSize || heightTmp / 2 < requiredSize) break
            widthTmp /= 2
            heightTmp /= 2
            scale *= 2
        }

        // 解码得到原始Bitmap
        val decodeOptions = BitmapFactory.Options().apply {
            inSampleSize = scale
        }
        val rawBitmap = c.contentResolver.openInputStream(uri).use { stream ->
            BitmapFactory.decodeStream(stream, null, decodeOptions)
        } ?: return null

        // 读取EXIF方向参数
        val exif = c.contentResolver.openInputStream(uri).use { stream ->
            ExifInterface(stream!!)
        }
        val orientation = exif.getAttributeInt(
            ExifInterface.TAG_ORIENTATION,
            ExifInterface.ORIENTATION_NORMAL
        )
        val rotateAngle = when (orientation) {
            ExifInterface.ORIENTATION_ROTATE_90 -> 90f
            ExifInterface.ORIENTATION_ROTATE_180 -> 180f
            ExifInterface.ORIENTATION_ROTATE_270 -> 270f
            else -> 0f
        }
        if (rotateAngle == 0f) return rawBitmap

        // 旋转校正Bitmap
        val rotateMatrix = Matrix().apply {
            postRotate(rotateAngle)
        }
        val correctedBitmap = Bitmap.createBitmap(
            rawBitmap,
            0,
            0,
            rawBitmap.width,
            rawBitmap.height,
            rotateMatrix,
            true
        )
        rawBitmap.recycle()
        return correctedBitmap
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}
额外说明
  • 代码中使用use扩展函数处理输入流,可以自动关闭流避免资源泄漏,比原代码手动管理流更可靠。
  • 校正完成后及时回收原始未旋转的Bitmap,避免不必要的内存占用。
  • 不需要修改Composable和bitmapToFile、resizeImage的原有逻辑,替换decodeUri方法后即可解决旋转问题。

内容的提问来源于stack exchange,提问作者Raj Narayanan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 00:45:46