编者:李国帅
qq:9611153 微信lgs9611153
时间 2020/6/17
背景原因:
android绘图的方法有很多种,比如opengl,sdl, canvas,surfaceview canvas等,不过对于简单绘图功能,使用canvas就足够了。
几天前,遇到了一个问题,以前在android6.0设备上能够正常绘图的程序在android9.0手机上突然就不行了。
解决这个问题的过程中依次排除了函数兼容性错误,硬件加速等错误,最后才锁定问题。
所需资源:
Android,canvas绘图
解决过程:
首先修改了明显的语法错误:
if(Build.VERSION.SDK_INT >= 26){
m_memCanvas.clipRect(left, top, right, bottom);
}else {
m_memCanvas.clipRect(left, top, right, bottom, Region.Op.UNION);
}
Canvas.clipRect:在手机屏幕上裁剪出一块区域来,起点是从屏幕的左上角开始。
更改了也没有效果,说明这不是简单的兼容性问题。
然后,发现还是有问题,图像显示不正常。
查了网上的资料,有人说是硬件加速的问题,于是添加了相关语句。
this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);//禁止硬件加速 sdk 29
还有其他禁止硬件加速的方法,都试了,依然如故,说明还是没有找对方法。
禁止加速不行,那就是其他原因。
也不是canvas函数不起作用,因为新编写的例子都是正常的。
仔细观察,发现canvas执行drawPath的时候是能够显示一个小区域。所以可能是与clipRect函数有关。
于是打印出clipRect的区域和path中lineto画线的区域,一切都是正常对应的,不存在绘制到无效区域的情况。
到底是为什么呢?
仔细观察对比正常绘图与不正常绘图,发现当前代码中,每次进行裁剪之前进行了canvas.save();但是在绘图之后却没有调用restore()。
是这个原因吗?
save() : 用来保存Canvas的状态,save()方法之后的代码,可以调用Canvas的平移、放缩、旋转、裁剪等操作!
restore():用来恢复Canvas之前保存的状态(可以想成是保存坐标轴的状态),防止save()方法代码之后对Canvas执行的操作,继续对后续的绘制会产生影响,通过该方法可以避免连带的影响
通过测试,在加入restore()之后,发现在android 9上面依然是可以显示绘图的。
正确的做法:
使用裁剪clip绘图的流程必须是:
canvas.save()----canvas.clipRect----canvas.draw--- canvas.restore();
整个流程一个步骤都不能少。
canvas.save();
canvas.clipRect(object_x, object_y, object_x + object_width, object_y + object_height);
canvas.drawBitmap(boosPlaneBomb, object_x, object_y - y, paint);
canvas.restore();
正确的流程才能保证正常的功能和愿景。
总结:
说到底,这依然是个兼容性问题。
对于老版本的android sdk,如果没有调用canvas.restore()直接调用canvas.clipRect会使用新的裁剪区域,但是新版本不行。
所谓的一朝天子一朝臣,不同的SDK版本采用不同规范(语言的,业务的),排除方式。
当系统或者底层sdk发生变化的时候,可能会引起依赖于他们的上层软件发生相应的变化。
这就是为什么以前的软件用得好好的,升级之后突然就不行了的原因。
32位计算机的时候,16位程序不能用了;系统升级后,许多系统工具不能用了。语言升级后,许多代码不能用了。
不要老是想后看,世界总是滚滚向前。
语言升级问题
语言也是不断的发展,严谨程度,开发效率,易用程度都在变化。
语言开发包原来的语法,用法可能不严谨,不规范。语言升级后,基于此的代码也可能出现兼容性问题。
如在VC6中
for(int i=0;i<10;i++);
for(i=0;i<10;i++);
是正确的的,可以编译的,但是在新版本VC中则是错误的。这个问题编译过directshow源码的朋友应该很清楚。
那些成熟的应用广泛的sdk错误很少,但是并不代表没有错误,而且随着sdk的更新发展,以及所有规范的变迁,都在不停的完善。