Android 大图显示策略优化显示(一)

在 Android 开发中,使用 BitmapFactory.Options 分两步操作是高效加载和缩放大图的常用方法。首先获取图片的实际宽高,然后根据目标尺寸计算出合适的采样率 inSampleSize,最终只将缩放后的图片加载到内存中以避免内存溢出。

📌‌实现步骤与核心方法‌

‌1. 预读取图片边界信息 ‌

通过设置 options.inJustDecodeBounds = true,可以解析出图片的原始宽高,而无需将整个图片加载到内存中。

复制代码
val options = BitmapFactory.Options().apply {
    inJustDecodeBounds = true // 开启边界模式
}
// 解码资源,现在只会填充 options.outWidth 和 options.outHeight
BitmapFactory.decodeResource(resources, R.drawable.your_image, options)
val imageWidth = options.outWidth
val imageHeight = options.outHeight

‌2. 计算采样率 inSampleSize ‌

根据你指定的目标尺寸(例如 reqWidth 和 reqHeight)和原始尺寸,计算出一个合适的 inSampleSize。采样率必须是 2 的幂(如 1, 2, 4, 8...),且数值越大,最终图片越小。

复制代码
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
    // 图片的实际高度和宽度
    val (height: Int, width: Int) = options.run { outHeight to outWidth }
    var inSampleSize = 1

    // 当实际尺寸大于目标尺寸时,通过乘以2来尝试更大的采样率
    if (height > reqHeight || width > reqWidth) {
        val halfHeight: Int = height / 2
        val halfWidth: Int = width / 2
        while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2
        }
    }
    return inSampleSize
}
val reqWidth = // ... 你的目标宽度(像素)
val reqHeight = // ... 你的目标高度(像素)
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)

‌3. 加载最终位图 ‌

将 inJustDecodeBounds 设置回 false 并应用计算出的 inSampleSize,再次解码资源以获取缩放后的位图。

复制代码
options.inJustDecodeBounds = false // 关闭边界模式,开始解码图片
val bitmap: Bitmap? = BitmapFactory.decodeResource(resources, R.drawable.your_image, options)
// 将 bitmap 设置给 ImageView

💡‌注意事项‌

  • 获取到的 inSampleSize 是 int 类型,代表的是宽高的缩放因子。例如,inSampleSize 为 2 时,生成的位图宽度为原始宽度的 1/2,高度为原始高度的 1/2,总像素数为原来的 1/4。
  • 这种采样缩放是‌向下采样 ‌,即只能缩小不能放大。如果需要放大、精确裁剪或有其他复杂需求,需要在获取位图后使用 Bitmap.createScaledBitmap 等方法进行进一步处理

以上是加载大图片压缩图片的步骤。这样实现的话,图片并不是最佳的显示效果。毕竟 要是原图的 1倍,2倍,4倍,不是最佳的显示效果。

只要知道,控件如ImageView 的最大显示宽高。把控件计算成符合原图片宽高比例的效果即可。再把图片赋值到ImageView上即可。并且scaleType 要是 fitXY的属性。

复制代码
   <ImageView
                            android:id="@+id/igvTarget"
                            android:layout_width="match_parent"
                            android:layout_height="match_parent"
                            android:scaleType="fitXY"
                            android:src="@drawable/windmill"></ImageView>

具体实现代码可以采用以下方式:

复制代码
val options = BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(resources, R.drawable.windmill, options)
        val maxHeight = binding.layoutLine.layoutParams.height
        val maxWidth = binding.layoutLine.layoutParams.width
        val scaleResult =
            ImageUtils.calculateScaleParameters(
                maxWidth,
                maxHeight,
                options.outWidth,
                options.outHeight
            )
        binding.igvTarget.layoutParams.height = scaleResult.newHeight
        binding.igvTarget.layoutParams.width = scaleResult.newWidth
//        binding.igvTarget.setImageResource(R.drawable.windmill)
        Glide.with(this@PhotoGuidanceActivity).load(R.drawable.windmill).into(binding.igvTarget)


fun calculateScaleParameters(
            maxWidth: Int,
            maxHeight: Int,
            bitmapWidth: Int,
            bitmapHeight: Int
        ): ScaleResult {


            // 如果图片高度低于要求的高度,直接展示图片
            if (bitmapHeight <= maxHeight && bitmapWidth <= maxWidth) {
                return ScaleResult(
                    newWidth = bitmapWidth,
                    newHeight = bitmapHeight,
                    scaleX = 1.0f,
                    scaleY = 1.0f,
                    shouldScale = false
                )
            }

            // 计算缩放比例
            val scaleX = maxWidth.toFloat() / bitmapWidth
            val scaleY = maxHeight.toFloat() / bitmapHeight

            // 选择较小的缩放比例以保持图片比例
            val scale = minOf(scaleX, scaleY)

            // 计算缩放后的尺寸
            val newWidth = (bitmapWidth * scale).toInt()
            val newHeight = (bitmapHeight * scale).toInt()

            return ScaleResult(
                newWidth = newWidth,
                newHeight = newHeight,
                scaleX = scale,
                scaleY = scale,
                shouldScale = true
            )
        }
相关推荐
倒头就睡的小比特5 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!5 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
千里马学框架5 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台5 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone5 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
猎头南楼5 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
致远ccc5 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
旖旎夜光5 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark5 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her15 天前
并查集-听课笔记
笔记·算法·并查集