Android APP完整基础教程(14)图形系统-Bitmap类及拓展

49 篇文章 15 订阅

1 Bitmap类解读

1.1 Bitmap简介

Bitmap(位图)本质上就是一张图片的内容在内存中的表达形式。它将图片的内容看做是由存储数据的有限个像素点组成;每个像素点存储该像素点位置的ARGB值。Android可以将所有的图片资源的内容以Bitmap对象的形式加载到内存中,再通过ImageView的setImageBitmap(Bitmap b)方法即可展示该Bitmap对象所表示的图片内容。

为什么不直接通过XML引用资源,还要使用bitmap?

如果是需要展示项目中的图片资源文件,我们只需要调用ImageView的setImageResource(int id)方法并传入该图片资源的id(一般为R.drawable.xxx)即可。但是如果是需要展示手机本地的某张图片或者网络上的某个图片资源,我们就需要使用一些通用的图形API了,因此会涉及Bitmap。

1.2 Bitmap关键类

Bitmap关键类主要有:Bitmap、BitmapDrawable和Bitmapfactory。

@1 Bitmap <==> BitmapDrawable

Bitmap代表位图,BitmapDrawable封装的就是一个Bitmap对象。Bitmap转换成BitmapDrawable对象 和 BitmapDrawable转换成Bitmap 的方法如下:

//通过Bitmap对象 来获取 BitmapDrawable对象
BitmapDrawable bitmapDrawable = new BitmapDrawable(null,bitmap1);
//通过BitmapDrawable对象 来获取 Bitmap对象
Bitmap bitmap2 = bitmapDrawable.getBitmap();

@2 Bitmap常用方法如下所示:

Bitmap是静态类,最常用的就是Bitmap的createBitmap方法,关于该方法android提供有:

关于创建Bitmap对象的方法集详细解读参照官方文档:Android Bitmap.createBitmap 方法集合

关于回收Bitmap对象的方法主要有2个关键方法,如下所示:

boolean isRecycled(); //判断是否回收 
void recycle(); //回收

@3 BitmapFactory常用方法如下所示:

BitmapFactory的decode系列静态方法也是用于创建Bitmap对象的,关于该方法android提供有:

 关于BitmapFactory获取Bitmap对象的方法集参照官方文档:Android BitmapFactory方法集合

@4 实战总结(查看assets目录下的图片查看器)

这里以一个案例形式展示,关键代码如下所示:

public class MainActivity extends AppCompatActivity {
    private static String TAG = "MainActivity";
    private Button btn_next;
    private ImageView imageView;
    private String[] images;
    private int currentImageIndex = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_next = findViewById(R.id.btn_next);
        imageView = findViewById(R.id.imageView);

        try {
            images = getAssets().list("");
        } catch (IOException e) {
            e.printStackTrace();
        }

        btn_next.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //数组越界处理,过界归零
                if (currentImageIndex >= images.length) {
                    currentImageIndex = 0;
                }

                //1 如果该索引无效,一直找到下一个有效的Index索引
                while(!(images[currentImageIndex].endsWith(".png") || images[currentImageIndex].endsWith(".PNG"))){
                    currentImageIndex++;
                    if(currentImageIndex>=images.length){
                        currentImageIndex = 0;
                    }
                }

                InputStream assetFiles = null;
                try {
                    //2 打开资源对应输入流
                    assetFiles = getAssets().open(images[currentImageIndex++]);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //3 获取BitmapDrawable并做回收处理
                BitmapDrawable bitmapDrawable = (BitmapDrawable) imageView.getDrawable();
                if (bitmapDrawable != null && !bitmapDrawable.getBitmap().isRecycled()) {
                    bitmapDrawable.getBitmap().recycle();
                }
                //4 改变imageView显示图片
                imageView.setImageBitmap(BitmapFactory.decodeStream(assetFiles));
            }
        });
    }
}

注意:

  • 经测试,如果目录下有5张图片,但images.length为7,且index为1-5时有效,6,7时无效。
  • 在判定是否为图片格式,这里仅考虑png格式,其他格式可根据需要酌情添加。

@5 关于Bitmap、BitmapDrawable和Bitmapfactory更多解读,查看官方文档如下:

1.3 使用Bitmap时防止OOM的有效方法

@1 高效压缩图片:均基于公式:Bitmap所占内存 = 长度 x 宽度 x 一个像素点占用的字节数。

  • 改变宽高:可以通过Bitmap.Options选项来合理调整采样率,采样率越大,图片越小,失真越严重,因此要找到合适的采样率。也可以通过缩放的方式来设置。
  • 改变像素:可以通过图片质量参数quality在保持像素数量的前提下改变图片的位深及透明度等,来达到压缩图片的目的。也可以通过编码方式来设置。

@2 使用缓存技术:常用缓存策略有LruCache(内存缓存,其中LRU是Least Recently Used的缩写)和DiskLruCache(存储缓存)

  • LRU是最近最少使用算法,核心思想是当缓存快满时,会淘汰近期最少使用的缓存目标。DiskLruCache用于实现存储设备缓存,即磁盘缓存,核心思想是通过将缓存对象写入文件系统从而实现缓存效果。

@3 及时回收:

  • 如果APP中引用了大量的Bitmap对象,又不需要同时显示所有图片,可以将暂时不用到的Bitmap对象 及时回收掉
  • 对于一些明确知道图片使用情况的场景可以使用完就执行recycle操作
  • 帧动画,加载一张,画一张,释放一张!使用时加载,不显示时直接置null或recycle。

2 ImageDecoder类解读

ImageDecoder不仅可以解码JPEG、PNG等静态图片,还可以解码GIF、WEBP等动画图片。

@1 ImageDecoder常用方法如下所示:

可以看到主要分为两类,也是解码图片的两个关键步骤:

  • 第一步,createSource系列方法,来创建Source。
  • 第二步,decodeXXX系列方法,通过Source来获取Bitmap对象/Drawable对象(注意:这里可以传入一个监听器 OnHeaderDecodedListener 来获得解码图片的信息)

@2 实战总结(同上,依然是查看assets目录下的图片查看器)

这里以ImageDecoder的方式来编写代码,关键代码如下所示:

public class MainActivity extends AppCompatActivity {
    private static String TAG = "MainActivity";
    private Button btn_next;
    private ImageView imageView;
    private String[] images;
    private int currentImageIndex = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_next = findViewById(R.id.btn_next);
        imageView = findViewById(R.id.imageView);

        try {
            images = getAssets().list("");
        } catch (IOException e) {
            e.printStackTrace();
        }

        btn_next.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //数组越界处理,过界归零
                if (currentImageIndex >= images.length) {
                    currentImageIndex = 0;
                }

                //判定是否为图片格式,这里仅考虑png和gif格式,其他格式可根据需要添加
                //如果该索引无效,一直找到下一个有效的Index索引
                while(!(images[currentImageIndex].endsWith(".png")
                        || images[currentImageIndex].endsWith(".PNG")
                        || images[currentImageIndex].endsWith(".gif")
                        || images[currentImageIndex].endsWith(".GIF")
                )){
                    currentImageIndex++;
                    if(currentImageIndex>=images.length){
                        currentImageIndex = 0;
                    }
                }
                //关键步骤1 ImageDecoder创建Source
                ImageDecoder.Source src = ImageDecoder.createSource(getAssets(),images[currentImageIndex++]);
                Drawable drawable = null;
                try {
                    //关键步骤2 ImageDecoder通过Source获取drawable
                    drawable = ImageDecoder.decodeDrawable(src, new ImageDecoder.OnHeaderDecodedListener() {
                        @Override
                        public void onHeaderDecoded(@NonNull ImageDecoder decoder, @NonNull ImageDecoder.ImageInfo info, @NonNull ImageDecoder.Source source) {
                            //监听后 可以根据需求获取解码图片的信息
                            Log.d(TAG, "onHeaderDecoded process");
                        }
                    });
                    imageView.setImageDrawable(drawable);
                    //如果是动态图片的显示,直接调用start方法来播放动画
                    if(drawable instanceof AnimatedImageDrawable){
                        ((AnimatedImageDrawable) drawable).start();
                    }
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        });
    }
}

说明:与上一个相同功能的demo相比,ImageDecoder这里支持了动态图片的显示,同时在代码编写上会更简洁些。

@3 关于ImageDecoder更多解读,查看官方文档:Android ImageDecoder类详细解读

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值