• Android-认识Bitmap


    Android-认识Bitmap

    学习自

    • Android开发艺术探索

    例行废话

    在Android的各种APP中都被离不开各种各样的图片,有的图片很大,有的图片很小不管这样图片都是一种很吃内存的资源,而在Android中每个APP所持有的资源是非常有限的,所以我们要尽可能的“抠门”一点。本着能省则省的原则,有一个 300 x 300 的图片现在在一个100 x 100 的ImageView中是一个完全不必要的事情。所以我们为了更节省资源和避免OOM,我们必须对图片进行处理。在Android中 Bitmap 就代表一个图片资源。

    BitmapFactory和Options

    我们记载位图资源都是通过 BitmapFactory 进行加载的,这个类提供了以下的方法。

    方法名 描述
    decodeResource 从资源中加载图片
    decodeByteArray 从byte数组中加载图片
    decodeFile 从文件中加载图片
    decodeStream 从流中加载资源

    Options

    Options类是BitmapFactory的一个嵌套类,所有的对Bitmap进行特殊处理的操作同时通过这个类的参数和属性达成的。其中一个Bitmap所占用的内存的大小是由下面的几个参数影响的。

    • inPreferredConfig Bitmap的色彩模式,不同的色彩模式,每个像素所占据的字节的数量是不一样的。
    • inSampleSize 采样率,采样率越大,Bitmap的宽和高就越小,所占用的内存也就越小。

    inPreferredConfig

    通过 inPreferredConfig 属性可以设置Bitmap加载的色彩模式,主要有以下几种色彩模式

    1. ALPHA_8 每个像素占据1byte
    2. ARGB_4444 每个像素占据2byte
    3. ARGB_8888 每个像素占据4byte
    4. RGB_565 每个像素占据2byte

    ARGB_8888 的显式效果最好,同时也是Android默认的色彩模式,但是也最为消耗内存。
    假设一张1024 x 1024,模式为ARGB_8888的图片,那么它占有的内存就是:
    1024 x 1024 x 4 byte

    //ARGB_8888 模式加载的Bitmap
    val bitmap = BitmapFactory.decodeResource(resources, R.drawable.wallbackground)
    Log.e("TAG", "Width:${bitmap.width} Helight:${bitmap.height}")
    Log.e("TAG", "Original:" + getBitmapSize(bitmap))
    
    //Log信息如下
    //Width:5040 Helight:2835
    //Size:57153600
    
    //RGB_565 这里本想使用 ARGB_4444 的测彩模式的,但是打印出来的一直却和模式的ARGB_8888相同
    //可能也跟具体的设备有关系吧,所以这里就是用了 RGB_565 的色彩模式来验证一下不同的色彩模式
    //占中的内存大小不同
    val options = BitmapFactory.Options()
    options.inPreferredConfig = Bitmap.Config.RGB_565
    val bitmap = BitmapFactory.decodeResource(resources, R.drawable.wallbackground,options)
    Log.e("TAG", "Width:${bitmap.width} Helight:${bitmap.height}")
    Log.e("TAG", "Size:" + getBitmapSize(bitmap))
    
    //Log信息如下
    //Width:5040 Helight:2835
    //Size:28576800
    
    fun getBitmapSize(bitmap: Bitmap): Int {
        return bitmap.rowBytes * bitmap.height
    }
    

    inSampleSize 采样率

    采样率可以Bitmap内存优化的重头戏,上面也提到了,当使用一个大图加载到一个小图中的时候这种情况是完全不需要的,比如说 400 x 400 图片要显示在 200 x 200 的ImageView上的时候,那么只需要记载200 x 200 大小的图片就行了,而如果直接加载 400 x 400 的图片就凭白地浪费了一倍的空间。所以我们需要通过设置Bitmap的采样率将图片缩小。当然最后经过采样后的图片最好不要小于 ImageView 的大小,否则就会造成拉伸效果。

    现假设要加载一个400 x 400 的图片,采样率默认是 1 也就是加载原图,如果采样变为 2 那么 图片的宽高都会 / 2 所以所占据的内存也就只有原图的 1/4(1/采样率的平方) 了。采样率越大图片的宽高越小,占用的内存也就越小。 在官方文档中推荐将采样率设置为2的N次幂(1,2,3,8..),比如说如果采样率设为 3 那么采样率就是 2 ,但是这真是一个参考,在一些机型上的运行效果并不是如此。

    NOTE:

    1. 经过采样后的Bitmap的大小不应该小于 ImageView的大小。
    2. 关于采样率的计算的最终结果并不会完全地精确,会有一些上下的浮动。
    val bitmap = BitmapFactory.decodeResource(resources, R.drawable.wallbackground)
    Log.e("TAG", "Size:" + getBitmapSize(bitmap))
    //Log: Size:57153600
    
    val options = BitmapFactory.Options()
    options.inSampleSize = 2
    val bitmap = BitmapFactory.decodeResource(resources, R.drawable.wallbackground, options)
    Log.e("TAG", "Size:" + getBitmapSize(bitmap))
    //Log: 3573360
    
    fun getBitmapSize(bitmap: Bitmap): Int {
        return bitmap.rowBytes * bitmap.height
    }
    //Log Size:14293440
    

    优化Bitmap加载

    我想通过上面的介绍你已经大概知道了如果优化Bitmap了吧

    1. 在加载图片的时候首先获取到图片的宽和高和ImageView的宽和高(在这一步并不会加载Bitmap仅仅会获取Bitmap的宽和高)
    2. 根据图片的宽和高来和ImageView的采样率
    3. 以指定的采样率加载Bitmap

    页面的布局文件如下

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="cn.shycoder.studybitmap.MainActivity">
    
        <Button
            android:id="@+id/btnLoadImg"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="Load Img" />
    
        <ImageView
            android:id="@+id/iv"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_gravity="center_horizontal" />
    </LinearLayout>
    

    Activity 的代码

    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
        }
    
        fun onClick(view: View) {
            loadImageFromResource(iv, R.drawable.wallbackground)
        }
    
        /**
         * 从资源中得到一个合适大小的Bitmap
         * */
        private fun loadImageFromResource(imageView: ImageView, resId: Int) {
            Log.e("TAG", "ImageView Width:${imageView.width} Height: ${imageView.height}")
    
            //1. 获取Bitmap的宽高
            val options = BitmapFactory.Options()
            // 通过设置此选项,加载Bitmap的时候,仅仅会获取宽和高并不会真正地加载Bitmap
            options.inJustDecodeBounds = true
            BitmapFactory.decodeResource(this.resources, resId, options)
    
            //2. 计算对应的图片的采样率
            val inSampleSize = this.calculateInSampleSize(options, imageView.width, imageView.height)
    
            //3.根据采样率加载图片
            //记得取消这个选项
            options.inJustDecodeBounds = false
            options.inSampleSize = inSampleSize
            val bitmap = BitmapFactory.decodeResource(this.resources, resId, options)
            imageView.setImageBitmap(bitmap)
        }
    
        private fun calculateInSampleSize(options: BitmapFactory.Options, requireWidth: Int,
                                          requireHeight: Int): Int {
            val width = options.outWidth
            val height = options.outHeight
            var inSampleSize = 1
            //如果Bitmap的大小是大于ImageView的大小的
            if (width > requireWidth && height > requireHeight) {
                //采样率的增加
                while ((width / (inSampleSize + 1) > requireWidth)
                        && (height / (inSampleSize + 1) > requireHeight)) {
                    inSampleSize += 1
                }
            }
    
            return inSampleSize
        }
    }
    
    

    图片对比

    // 原图所占内存:60466176
    // 经过采样压缩后:15148960:
    

    Snag_484e1caf

  • 相关阅读:
    7款纯CSS3实现的炫酷动画应用
    9款基于HTML5/SVG/Canvas的折线图表应用
    8款耀眼的jQuery/HTML5焦点图滑块插件
    10款很酷的HTML5动画和实用应用 有源码
    13款精彩实用的最新jQuery插件
    9款超绚丽的HTML5/CSS3应用和动画特效
    8款最受欢迎的HTML5/CSS3应用及源码
    Zookeeper可以干什么
    MySQL数据库优化
    SQL语句的执行过程
  • 原文地址:https://www.cnblogs.com/slyfox/p/9846579.html
Copyright © 2020-2023  润新知