概述
當寫一個應用時,恰當的決定你的圖形需求是很重要的.不同的圖形任務對應不同的技術.例如,一個靜態應用的圖形和動畫的實現肯定與一個交互式遊戲非常不同.這裡,我們將討論一些在android上繪制圖形時的操作以及它們最適合應用的任務.
Canvas和Drawables
Android提供瞭一系列View部件來為大多數用戶界面提供通常的功能.你也可以擴展這些部件來修改它們的外觀和行為.另外,你可以使用Canvas類的方法來繪制你自己的2D顯示或為那些像紋理按鈕或逐幀顯示的動畫之類的東西或創建Drawable對象.
硬件加速
從Android3.0開始,你可以硬加速大多數以CanvasAPI完成的繪畫工作來大幅提高它們的性能.
OpenGL
Android支持OpenGLES 1.0和2.0,Android框架API和(NDK)都同樣支持.在CanvasAPI不支時使用框架API添加一些圖形增強功能是被推薦的或者你不在意高性但期望是平臺無關的.使用框架API比NDK性能要低,所以對於很多圖形交互應用比如遊戲,使用NDK是最好的(重點註意盡管你使用框架API依然獲得足夠的性能.比如,Google主體應用是全用框架API開發的).當你的有很多原生代碼要移植到android時NDK中的OpenGL還是很有用處的.
Canvas
Android框架APIs提供瞭一系列2D繪畫APIs使你可以在一個canvas上畫出你自己的圖形或修改已存在的View來定制它們的外觀.當畫2D圖形時,典型情況下,你將使用以下兩方法之一:
a.把你的圖形或動畫繪制到你的layout中的一個View上.你的圖形的繪制被系統的標準View繪制過程所處理— 你隻需定義進入View的圖形即可.
b.在一個Canvas中直接繪制圖形.用此方法,你需親自調用恰當的類的onDraw()方法(把它傳給你的Canvas),或Canvas的draw…()方法們中的一個(比如drawPicture()).在這樣做時,你也可以任意控制動畫.
選項"a,"畫到View上,是當你想畫不需動態改變的簡單圖形並且不是高性能要求遊戲的一部分時的最佳的選擇.例如,你應該在你想顯示一個靜態圖形或預先定義的動畫時畫到View上.
選項"b,"畫到Canvas上,當你的應用需要定期地重畫自己時是更好的選擇.像視頻遊戲這樣的應用應畫到Canvas上,有不止一種方法來這樣做:
在你的UIActivity線程中,你創建一個自定義View組件,然後調用invalidate()然後處理onDraw()回調.
或者,在不同的線程中,你管理一個SurfaceView並且以最快的可能速度執行向Canvas繪畫的動作(你不需要執行invalidate()).
用Canvas繪畫
當你正在寫一個應用,在其中你想執行特殊的繪畫並且/或者控制圖形動畫,你應該使用Canvas作畫.一個其實隻是一個中間層,隻是一個接口,它代表瞭實際的圖形繪制到的表面— 它承受瞭所有的繪畫調用.通過Canvas,你的繪制實際執行到一個後臺Bitmap上,這個Bitmap被放在窗口中.
在響應繪畫事件的onDraw()回調方法中,提供給你瞭Canvas,於是你隻需把你的繪制調用傳給它就行瞭.當處理一個SurfaceView對象時,你可以從SurfaceHolder.lockCanvas()獲取一個Canvas.然而,如果你需要創建一個新的Canvas,那麼你必須定義實際繪制所在的Bitmap.Bitmap是Canvas永遠所需要的.你可以像下面這樣創建一個新的Canvaslike this:
Bitmap b =Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas c =new Canvas(b);
現在你的Canvas將畫到所定義的Bitmap上.使用Canvas畫完後,你可以使用任一個Canvas.drawBitmap(Bitmap,…)方法把你的Bitmap移到另一個Canvas.推薦你最終還是使用View.onDraw()或SurfaceHolder.lockCanvas()提供給你的Canvas作畫.
Canvas類有很多繪制方法可以使用,比如drawBitmap(…),drawRect(…),drawText(…)等等.也有其它類也具有draw()方法.例如,你有一個Drawable對象要放到Canvas上,Drawable具有它自己的draw()方法,你隻需把你的Canvas作為一個參數傳給這個方法即可.
畫到View
如果你的應用不需要大量的處理運算或保證幀率(可能是一個棋類遊戲,一個貪吃蛇遊戲或其它慢動畫的應用),那麼你應考慮創建一個自定義的View組件然後在它的View.onDraw()中使用Canvas繪制.這樣做的方便之處是Android框架將提供給你一個預定義好的Canvas.
要這樣做,從View派生(或從其子孫派生)然後定義onDraw()回調方法.這個方法將被Android框架調用,要求你的View畫出它自己的樣子.這是你使用Canvas執行所有繪制動作的地方.
Android框架將僅在需要時才調用onDraw().每次你的應用準備好被繪制時,你必須調用invalidate()來請求你的View無效.這表明瞭你想讓你的View被繪制於是Android將調用你的onDraw()方法(盡管這不能保證回調方法會被立即執行).
在你的View組件的onDraw()中,使用給你的Canvas來進行所有的繪制工作:使用各種Canvas.draw…()方法,或把你的Canvas作為參數調用其它類的draw()方法.一旦你的onDraw()結束,Android框架將使用你的Canvas來畫一個Bitmap,Bitmap被系統管理.
註:為瞭在主Acitivity所在線程之外的線程請求view無效,你必須調用postInvalidate().
可以去SDK例程目錄:<your-sdk-directory>/samples/Snake/中看貪吃蛇遊戲例子.
畫到SurfaceView
SurfaceView是View的一種特殊子類,它在View派生樹中提供一個專門的繪畫接口,其目的是把這個繪畫接口提供給一個應用的第二個線程,於是應用不需等待系統的View派生類準備好作畫再進行其它工作,而是另外的線程引用瞭一個SurfaceView,SurfaceView可以按自己的速度畫到自己的Canvas上.
要使用它,你首先要從SurfaceView派生創建一個新的類.這個類要實現SurfaceHolder.Callback回調接口.這個接口將把後臺表面的信息通知給你,比如當表面被創建,改變或銷毀.這些事件都是很重要的,因為你可以從它們知道何時你可以開始作畫,你是否需要根據新表面的屬性進行調整,和什麼時候應該停止繪畫並且可能要殺死一些任務.在你的SurfaceView類中也是定義你的第二個線程類的好地方,這個線程類將執行所有對你的Canvas的作畫過程.
你應該通過一個SurfaceHolder來操作你的表面對象而不是直接操作它.所以,當你的SurfaceView初始化後,通過調用getHolder()獲取SurfaceHolder.你然後還應該調用addCallback()(把this傳給它)來通知SurfaceHolder你想接收SurfaceHolder回調(從SurfaceHolder.Callback).最後在你的SurfaceView類中重寫SurfaceHolder.Callback的每一個方法.
為瞭在你的第二個線程中畫到表面的Canvas上,你必須把你的SurfaceHandler傳給第二線程並且用lockCanvas()獲取Canvas.你現在可以用Canvas做畫瞭.一旦你完成瞭繪畫,調用unlockCanvasAndPost(),把你的Canvas對象傳給它,現在,表面將按你給它的來繪制Canvas.每次你想作畫,就執行這個canvas加鎖和解鎖的步驟.
註:每次你從SurfaceHolder取得Canvas,Canvas的上一次的狀態將保留.你必須每次都完全重畫你的表面.例如,你可以通過drawColor()填充顏色或通過drawBitmap()設置一個背景圖像來清空Canvas的上一次狀態.否則,你將會看到你上次作畫的痕跡.
我看例子,可以去看LunarLander遊戲,在SDKsamples 文件夾下:<your-sdk-directory>/samples/LunarLander/.或者,瀏覽在SampleCode一文隻瀏覽源碼(尚未出此章,敬請期待).
摘自 nkmnkm的專欄