java.lang.IllegalArgumentException: width and height must be > 0

来源:互联网 发布:三维设计软件 编辑:程序博客网 时间:2024/05/24 00:33

遨游在代码的海洋中,难免会遇到一个大浪拍晕你。今天就遇到一个关于图片压缩方面的坑,且听我细细道来~~
想直接看解决方法的直接目录跳转 正确代码

问题出现场景:

项目里有处地方需要把下载下来的图片进行压缩,这里图片下载使用的是Glide,下载完会自动转成bitmap,所以只需要对bitmap进行压缩处理就好了。对于bitmap的处理对于做过多个项目的老手(不管你信不信,反正我是信了)来说我直接反手就是一段代码。

错误代码:

1public static Bitmap ResizeBitmap(Bitmap bitmap, int scale) {2int width = bitmap.getWidth();3int height = bitmap.getHeight();4、        Matrix matrix = new Matrix();5、        matrix.postScale(1 / scale, 1 / scale);6、        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);7、        bitmap.recycle();8return resizedBitmap;    }

上述代码是从我的知识小金库中拿出来的,没想到一上来就反手给我一个Crash,报错完整如下:

java.lang.IllegalArgumentException: width and height must be > 0   at android.graphics.Bitmap.createBitmap(Bitmap.java:933)   at android.graphics.Bitmap.createBitmap(Bitmap.java:912)   at android.graphics.Bitmap.createBitmap(Bitmap.java:843)   at zz.xx.xx.common.view.seekbar.MarkSeekBar.ResizeBitmap(MarkSeekBar.java:223)   at xxxx.MarkSeekBar$1$1.onResourceReady(MarkSeekBar.java:206)   at xxxx.MarkSeekBar$1$1.onResourceReady(MarkSeekBar.java:203)   at com.bumptech.glide.request.GenericRequest.onResourceReady(GenericRequest.java:525)   at com.bumptech.glide.request.GenericRequest.onResourceReady(GenericRequest.java:507)   at android.os.Handler.dispatchMessage(Handler.java:98)   at android.os.Looper.loop(Looper.java:145)   at android.app.ActivityThread.main(ActivityThread.java:5942)   at java.lang.reflect.Method.invoke(Native Method)   at java.lang.reflect.Method.invoke(Method.java:372)   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1388)   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1183)

看见报错楼主是毫不慌张的,因为很明显宽高没有获取到,对于一个有经验的老手(可能是个假的)来说,开始分析,因为这是在自定义View里面,会不会是view没有绘制出来所以获取不到(很多情况下view的宽高获取不到都是这个原因),但是很快被否定了,因为这里根本不关view什么事,我们获取的是bitmap,为了稳妥起见,还是打了断点,发现width 和height的值是有的,而且大于0!!!我擦嘞,那它报错是几个意思?冷静下来。没办法只能debug了,发现在如上“错误代码”的第6行报错了(自己手动标的行数),接着只能从源码着手。

下面贴出Bitmap createBitmap()方法精简源码:

下述三个方法分别在Bitmap源码的 728行、874行、855行,有条件的可以自行查看。

代码一、

    public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,            Matrix m, boolean filter) {        int neww = width;        int newh = height;        Canvas canvas = new Canvas();        Bitmap bitmap;        Paint paint;        ...        RectF dstR = new RectF(0, 0, width, height);        ...        ...            RectF deviceR = new RectF();            m.mapRect(deviceR, dstR);            neww = Math.round(deviceR.width());            newh = Math.round(deviceR.height());            bitmap = createBitmap(neww, newh, transformed ? Config.ARGB_8888 : newConfig,           transformed || source.hasAlpha());        return bitmap;    }

因为报错说的是width和height的值有问题,所以我们只需要跟踪width和height都做了哪些操作,接着看源码,开始width和height就被赋值neww和newh,接着做了m.mapRect操作,这里是做了个矩形转换操作。接着是Math.round的数学操作,这个也不用管,最后调用方法createBitmap,我们跟着跳进去看代码如下:

代码二、

private static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) {        return createBitmap(null, width, height, config, hasAlpha);    }

这里又调用了createBitmap(null, width, height, config, hasAlpha) 方法,接着往里跳:

代码三、

private static Bitmap createBitmap(DisplayMetrics display, int width, int height,            Config config, boolean hasAlpha) {        if (width <= 0 || height <= 0) {            throw new IllegalArgumentException("width and height must be > 0");        }        Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true);        if (display != null) {            bm.mDensity = display.densityDpi;        }        ...        ...        return bm;    }

到了这里我们终于找到了异常报错的地方throw new IllegalArgumentException !!!回想整个流程发现并没有什么地方做了什么特殊处理。然后陷入沉思;没办法只能再从头想一遍,上述“错误代码”最复杂的一个方法我们已经分析完了,就剩下matrix.postScale(1 / scale, 1 / scale); 这一段了,我们还是跳入源码:

/**     * Postconcats the matrix with the specified scale.     * M' = S(sx, sy) * M     */    public boolean postScale(float sx, float sy) {        native_postScale(native_instance, sx, sy);        return true;    }

方法很简单,也是只调了一个native方法;但是我们发现了一个问题,这个方法接收的参数是float类型!!!因为Java属于强语言,不会自动转换类型,所以大概可以猜出这里至少是一个问题,抱着最后的希望将参数改为float类型:

正确代码:

public static Bitmap ResizeBitmap(Bitmap bitmap, int scale) {        int width = bitmap.getWidth();        int height = bitmap.getHeight();        Matrix matrix = new Matrix();        matrix.postScale(1f / scale, 1f / scale);        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);        bitmap.recycle();        return resizedBitmap;    }

结果不用说了,自然是这里的问题!

折腾一圈最后才发现问题其实很简单,只是一开始方向错了,但这个过程中至少我们学会了怎么去分析一个问题(至少安慰下自己 -_-)。

这里对上面提到过的View获取宽高失败的问题也提一下,之所以会获取失败是因为获取宽高的时候我们的view还没绘制完成,所以获取失败;这里解决方法是做一个延时操作:

View获取宽高

View.post(new Runnable() {    @Override    public void run() {       //获取宽高    }});

或者对视图进行监听:

ViewTreeObserver vto = view.getViewTreeObserver();        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                view.getViewTreeObserver().removeOnGlobalLayoutListener(this);                //获取宽高            }        });

还可以通过测量的方式,不过分不同情况:

match_parent 的情况: 直接放弃,无法 measure 出具体的宽高

具体的数值( dp/px )

比如宽高都是 100px ,如下 measure :int widthMeasureSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);int heightMeasureSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);view.measure(widthMeasureSpec, heightMeasureSpec);

wrap_content 的情况:

int widthMeasureSpec = MeasureSpec.makeMeasureSpec( (1 << 30) - 1, MeasureSpec.AT_MOST);int heightMeasureSpec = MeasureSpec.makeMeasureSpec( (1 << 30) - 1, MeasureSpec.AT_MOST);view.measure(widthMeasureSpec, heightMeasureSpec);

注意到(1 << 30) - 1,通过分析MeasureSpec的实现可以知道,view的尺寸使用30位二进制表示的,也就是说最大是30个1即 2^30 - 1,也就是(1 << 30) - 1,在最大化模式下,我们用view理论上能支持的最大值去构造MeasureSpec是合理的。

好了,今天的坑就说到这里了,有什么不对的地方欢迎指出。

阅读全文
0 0
原创粉丝点击