編者:李國帥
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的更新發展,以及所有規範的變遷,都在不停的完善。