欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java.lang.IllegalArgumentException: x + width must be 小于等于 bitmap.width()错误的分析,解决

程序员文章站 2022-05-20 22:13:51
...

场景

最近在写一个滑动验证登录的View,遇到了一个很有趣的BUG
java.lang.IllegalArgumentException: x + width must be 小于等于 bitmap.width()错误的分析,解决
我需要从背景图中1处扣除一个小图,放在2处。在抠图的时候,遇到了BUG。

java.lang.IllegalArgumentException: x must be < bitmap.width()

这个报错的意思是,当前我要扣的图尺寸要比原图比背景图大。这就很奇怪,小方块的尺寸明明比背景图小啊。

分析

记住,一切报错的问题的答案,在源码中一定可以找到!因此我们来看看是源码中哪里抛出了这个异常

public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height,
            @Nullable Matrix m, boolean filter) {

        checkXYSign(x, y);
        checkWidthHeight(width, height);
        if (x + width > source.getWidth()) {
            throw new IllegalArgumentException("x + width must be <= bitmap.width()");
        }
        if (y + height > source.getHeight()) {
            throw new IllegalArgumentException("y + height must be <= bitmap.height()");
        }
     
     后面的省略。。。。。。   

这段代码值得好好说下,因为每天在看线上崩溃日志时,偶尔能看到这里出错的场景。
首先是:

checkXYSign(x, y);
checkWidthHeight(width, height);

这两行主要就是校验当前Bitmap 的绘制的位置、尺寸是否是正常的。也就是说x、y、width、height 都必须要大于0,否则就会报错,例如:

throw new IllegalArgumentException("x must be >= 0");
throw new IllegalArgumentException("width must be > 0");

接下来就是今天的主角:

throw new IllegalArgumentException("x + width must be <= bitmap.width()");

x : 要创建的Bitmap的x 坐标;
width : 要创建的Bitmap的宽度。
Bitmap.width(): 原图的宽度

翻译下报错的意思,就是说,当前你要从主图中一个x,y的位置去截图一个width大小的图,但你要截取的图的位置不在或者超出了主图的范围,然后就报错了。

那什么情况会出现截图不在主图的范围 ?
答案:就是要绘制的Bitmap的宽、高要比View的宽、高小!(或者说不一致)
我们在给View 绘制Bitmap 时,会有这样一种操作:

mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bilibili);
canvas.drawBitmap(mBgBitmap,null,new Rect(0,0,mWidth,mHeight),mBgPaint);

这个操作其实就是讲图片的原图绘制到了View上,假设这张图是600* 200。而View的是1080 * 300。虽然你看到了这张图已经铺满了整个View,但这并不代表背景图的Bitmap的宽高就等于View的宽高!!!
如果此时,我从横坐标 x = 550 ,截取一张宽度100 的图。这个时候你会发现:
550 + 100 > 600 ; 即 x + width > bitmap.width()
然后就崩了。。。。。。

解决这个问题的办法,最好就是将背景的图的Bitmap 的大小设置成和View的大小一致,这样后面就好操作,也就不用考虑过多坐标的问题。

扩展

既然说到设置Bitmap 的大小,那么我在获取到 bitmap后(使用的是BitmapFactory.decodeResource)直接设置可以吗 ?

mBgBitmap.setWidth(1000);

答案是不行:你会看到

new IllegalStateException("only mutable bitmaps may be reconfigured");

继续从源码中找答案:

public void reconfigure(int width, int height, Config config) {
        checkRecycled("Can't call reconfigure() on a recycled bitmap");
        if (width <= 0 || height <= 0) {
            throw new IllegalArgumentException("width and height must be > 0");
        }
        if (!isMutable()) {
            throw new IllegalStateException("only mutable bitmaps may be reconfigured");
        }
        省略

我们在给Bitmap设置宽度时,最终会调用上面的代码,其中有句

!isMutable()

当前值是否可变,默认是不可变的,那啥时候可以变?
答案就是在使用createBitmap()方法创建Bitmap时,下面截图看最清晰
java.lang.IllegalArgumentException: x + width must be 小于等于 bitmap.width()错误的分析,解决
使用这种方式创建,一开始就设置Bitmap的相关属性是可变的。

总结

Bitmap 作为我们经常打交道的一个类,其源码非常有必要看!

问:你看源码的目的是啥?
答:两个目的。1:学习Android 源码里的一些功能的实现以及框架的设计思路。2、解BUG,BUG的产生就是你没有按照Android 设定的规则去写,看源码就是要了解规则。