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