Android图片处理之加载大图策略

在Android开发中常常遇到加载大图的需求,而手机分配的内存有限,所以如果直接加载到手机经常会oom,因此我们需要解决这类问题。

 加载缩略图

简单介绍一下BitmapFactory,它是android加载图片的工厂,里面有一个常用的内部类options,这个类很重要,一般对Bitmap压缩就是使用这个类,这个类可以认为是对图片解码时做的一些设置,具体请看官方文档。这里简单的介绍一些常用的属性。

1.inJustDecodeBounds,这个是压缩常用字段,设置为true的时候可以查询bitmap的信息却不占用内存,返回的bitmap为Null,在设置完相对参数后需要设置为false才能得到bitmap对象。

2.inSampleSize这个是压缩的倍数,1相当于和原来一样,2是压缩原来的二分之一,是宽高都变为原来的二分之一。则图像的压缩倍数则是长和宽的倍数的乘积,也就是4。这个最小值为1,小于1的话则会设置成1。设置值如果不是2的倍数则会向下取2的倍数(5取4,3取2,2到1之间的话就不往下取直接为2),不过这是区分不同Android版本的。

以下则用代码来了解以下

public class BitmapUtil {

    private static final String TAG = "lmf";
    
    
    public static Bitmap creatImage(Context context,int res){

        BitmapFactory.Options options = new BitmapFactory.Options();
        //设置为true获取原图大小而且不加载到内存
        options.inJustDecodeBounds = true;
        //这时候加载图片并不占用内存,只是获得原图信息而已;
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),res,options);
        
        if (bitmap != null){
            Log.d("lmf", "creatImage: " +bitmap.getByteCount());
        }else {
            Log.d(TAG, "creatImage: bitmap == null");
        }
        options.inJustDecodeBounds = false;
        options.inSampleSize = 2;
        bitmap = BitmapFactory.decodeResource(context.getResources(),res,options);
        Log.d(TAG, "creatImage: options.inJustDecodeBounds = false;"+bitmap.getByteCount());
        
        return bitmap;
    }
}
而调用地方的代码则
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "lmf";
    private Bitmap bitmap;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImageView imageView = findViewById(R.id.image);
        bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.daily_pre);
        Log.d(TAG, "onCreate: 未压缩的bitmap:"+bitmap.getByteCount());
        
        bitmap = BitmapUtil.creatImage(this,R.drawable.daily_pre);
        
        imageView.setImageBitmap(bitmap);

    }
}

打印结果则为:

07-01 15:31:15.895 5465-5465/com.example.limaofang.bitmapdemo D/lmf: onCreate: 未压缩的bitmap:595980
07-01 15:31:15.896 5465-5465/com.example.limaofang.bitmapdemo D/lmf: creatImage: bitmap == null

07-01 15:31:15.899 5465-5465/com.example.limaofang.bitmapdemo D/lmf: creatImage: options.inJustDecodeBounds = false;149152

为什么会打印出bitmap == null这个结果呢?

因为inJustDecodeBounds这个参数设置为true的话,它只会解析图片的信息而不会把bitmap加载到内存,所以会打印出bitmap==null,从上述打印结果来看,当inSampleSize设置2时候,内存的确为原来的4分之一。


二加载高清原图(不允许压缩)

但如果我们遇到需要展示很大的图片又不允许压缩,那该怎么办呢?这时候我们应该要用到一个类BitmapRegionDecoder。

BitmapRegionDecoder主要用于显示指定图片的某一块矩形区域,如果你需要指定显示某一块区域,这个类将非常适合。

  • BitmapRegionDecoder 提供了一系列的 newInstance 方法来构造对象,支持传入文件路径,文件描述符,文件的 inputstrem 等。
BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
  • 上述解决了传入我们需要处理的图片,那么接下来就是显示指定的区域。
    bitmapRegionDecoder.decodeRegion(rect, options);

    参数一很明显是一个rect,参数二是BitmapFactory.Options,你可以控制图片的inSampleSize,inPreferredConfig等。

    下面是一个简单的核心代码:

    InputStream is = getAssets().open("big_pic.jpg");
    //获得图片的宽、高
    BitmapFactory.Options tmpOptions = new BitmapFactory.Options();
    tmpOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(inputStream, null, tmpOptions);
    int width = tmpOptions.outWidth;
    int height = tmpOptions.outHeight;
    //设置显示图片的中心区域
    BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Bitmap.Config.RGB_565;
    Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width / 2 - 200, height / 2 - 200, width / 2 + 200, height / 2 + 200), options);
    mImageView.setImageBitmap(bitmap);
    上述代码显示区域为中间400*400的区域。

自定义大图显示控件

根据上述分析,自定义大图控件思路就十分清晰了。

  • 提供一个设置图片的入口
  • 重写 onTouchEvent,在里面根据用户移动的手势,去更新显示区域的参数
  • 每次更新区域参数后,调用 invalidate,onDraw 里面去regionDecoder.decodeRegion 拿到 bitmap,去 draw
由于时间原因,自定义控件留待下一篇博客去完善。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值