概述
當寫一個應用時,恰當的決定你的圖形需求是很重要的.不同的圖形任務對應不同的技術.例如,一個靜態應用的圖形和動畫的實現肯定與一個互動式遊戲非常不同.這裡,我們將討論一些在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的專欄