- Android和PHP開發最佳實踐(第2版)
- 黃雋實
- 419字
- 2018-12-31 20:29:09
2.8 Android圖形界面
前面介紹了Android應用界面(Android UI)的相關內容,不過對于一些游戲應用來說,這些UI控件往往派不上用場。此外,一些特殊的Android應用也有可能會使用到比較底層的圖形類庫,因此,本節我們就來學習Android的圖形系統。
Android系統中的圖形大致可以分為2D圖形和3D圖形兩類,2D圖形的類庫在android.graphics包下,本節將會重點介紹;3D圖形的類庫在android.opengl包下,由于這部分內容和游戲開發關系比較緊密,這部分內容將被放在本書第13章中介紹,感興趣的朋友可以提前參考13.1.4節中的內容。
2.8.1 畫筆(Paint)
首先,讓我們來想象一下,當我們繪畫的時候,最重要的兩樣東西是什么?答案應該沒有什么懸念,那就是畫筆和畫布。實際上,在Android系統中繪制圖形的原理是相同的,我們同樣需要先使用程序構造一把畫筆(Paint),然后在畫布(Canvas)上進行繪畫。
Android系統中的畫筆類,即android.graphics包下的Paint類,該類包含了一系列的方法與屬性,用于構造繪制圖形用的畫筆。我們把常用的方法歸納到表2-5中。
表2-5 畫筆類常用方法

以上方法常用于畫筆初始化的配置邏輯中,接下來讓我們來學習Paint畫筆類的使用范例,如參考代碼清單2-28所示。
代碼清單 2-28
public class TestPaintView extends View { ... private Paint mPaint = new Paint(); ... public void onDraw(Canvas canvas) { super.onDraw(canvas); // 設置畫筆 mPaint.setAntiAlias(true); mPaint.setColor(Color.RED); mPaint.setAlpha(200); mPaint.setStyle(Paint.Style.FILL); // 繪制矩形 canvas.drawRect(100, 100, 150, 150, mPaint); } ... }
以上視圖類TestPaintView繼承自View基類,主要的繪制邏輯在onDraw方法中,即使用定制好的實心畫筆繪制一個紅色的矩形,這里我們可以學習到使用Paint畫筆類的正確方法。此外,我們還需要注意,這里在使用setColor方法設置畫筆顏色的時候,用到了Color類的預定義顏色常量,我們將這些常用的顏色常量歸納到表2-6中。
表2-6 畫筆類顏色常量

2.8.2 畫布(Canvas)
設置好畫筆和顏色,就可以開始在畫布上繪畫了,這時我們就需要用到畫布類,即Canvas類。該類包含了一系列的方法與屬性,用于設置畫布的外觀,我們把常用的方法歸納到表2-7中。
Canvas類中常用繪制方法的用法比較簡單,Android系統已經在View類的onDraw方法中默認傳入了canvas對象,我們可以根據需要使用不同的draw方法繪制出不同的圖形。比如,代碼清單2-29中就使用了drawRect方法繪制了一個矩形。
表2-7 畫布類常用方法

然而,游戲應用的畫布中通常不只有一個圖形,通常需要對其中的某些圖形進行特殊處理,比如旋轉、變形等,此時需要先使用save方法來保存畫布,圖形處理完畢之后再調用restore方法來重置、重繪,使用范例如代碼清單2-29所示。
代碼清單 2-29
public class TestCanvasView extends View { ... private Paint mPaint = new Paint(); ... public void onDraw(Canvas canvas) { super.onDraw(canvas); // 設置畫布顏色 canvas.drawColor(Color.BLACK); // 設置畫筆 mPaint.setAntiAlias(true); // 剪裁畫布 canvas.clipRect(0, 0, 200, 200); // 保存畫布 canvas.save(); // 繪制一個矩形 canvas.rotate(10.0f); mPaint.setColor(Color.RED); canvas.drawRect(100, 100, 150, 150, mPaint); // 重置畫布 canvas.restore(); // 繪制另一個矩形 mPaint.setColor(Color.BLUE); canvas.drawRect(100, 0, 200, 100, mPaint); } ... }
以上程序繪制了兩個矩形。其中,紅色的矩形繞著屏幕左上方的頂點順時間旋轉了10°。這里涉及Canvas畫布坐標系的知識,我們將在2.8.3節中介紹。另外,我們還可以學習到如何對Canvas畫布進行設置、保存、旋轉、重置等一系列的操控過程。學習了以上Paint和Canvas類的編程技巧之后,開發者就可以在Android應用和游戲中方便地繪圖了。
2.8.3 基礎幾何圖形
前面我們已經學習了畫筆(Paint)和畫布(Canvas)的基礎知識,接下來我們就可以使用這些工具來畫圖了。實際上,在前面的代碼范例中,我們已經介紹了如何使用Canvas對象的drawRect方法來繪制矩形,但是大家可能還不清楚方法中參數值的含義,因此我們先來熟悉Canvas畫布的坐標系,如圖2-12所示。

圖2-12 Canvas坐標系
從以上的坐標系示意圖中,我們可以看出以下幾個要點。其一,Canvas畫布的坐標原點位于整張畫布的左上方,點坐標為“(0,0)”;其二,屏幕橫向的是X軸,縱向的是Y軸,屏幕內的點坐標都是正數;其三,以矩形為例,我們可以看到繪圖方法(drawRect)中的left、top、right、bottom等參數的含義,其他方法中的類似參數的含義都可以依此類推。
另外,在使用Canvas進行繪圖的時候還要注意,畫布是按照程序邏輯的先后順序進行渲染的,因此底部圖形的渲染邏輯放在前面,渲染邏輯在后面的圖形則會層層覆蓋上去,使用范例請參考代碼清單2-30。
代碼清單 2-30
public class TestGraphicsView extends View { ... private Paint mPaint = new Paint(); ... public void onDraw(Canvas canvas) { super.onDraw(canvas); // 設置畫布顏色 canvas.drawColor(Color.BLACK); // 設置畫筆 mPaint.setAntiAlias(true); // 畫圓形 mPaint.setColor(Color.YELLOW); canvas.drawCircle(160, 160, 120, mPaint); // 畫矩形 mPaint.setColor(Color.RED); canvas.drawRect(80, 80, 240, 240, mPaint); // 畫橢圓 mPaint.setColor(Color.GREEN); RectF rectf = new RectF(); rectf.left = 90; rectf.top = 100; rectf.right = 230; rectf.bottom = 220; canvas.drawOval(rectf, mPaint); // 畫多邊形 Path path = new Path(); path.moveTo(160, 110); path.lineTo(160-40, 110+80); path.lineTo(160+40, 110+80); path.close(); mPaint.setColor(Color.BLUE); canvas.drawPath(path, mPaint); ... } ... }
在上述代碼中,TestGraphicsView類的onDraw方法中依次繪制了圓形、矩形、橢圓和多邊形,運行結果如圖2-13所示,我們可以很清楚地看到這些基礎幾何圖形的顯示效果以及圖形渲染的先后順序。

圖2-13 基礎幾何圖形
基礎幾何圖形的繪制是Android圖形系統的基礎知識。在此基礎之上,我們可以把Android UI控件結合到一起,開發出豐富多彩的應用UI界面。當然,我們還可以運用View控件的刷新機制完成一些簡單的圖形動畫,相關內容將在2.8.4節中介紹。
2.8.4 常見圖形變換
常見的圖形變換包括位移、旋轉、縮放、傾斜等,其中,位移變換在開發者掌握了畫布坐標系等基礎概念的情況下,實現起來是比較簡單的;然而,旋轉、縮放以及傾斜變換則涉及變換矩陣(Matrix)的概念,這里需要特別解釋一下。
Android系統中的變換矩陣實際上是一個3×3的矩陣,專門用于控制圖形變換,矩陣中的每個數值都有其特定的含義。Android SDK中的Matrix類位于android.graphics包下,我們可以通過setValue方法直接設置旋轉矩陣的二維數組,但是這種用法比較難懂,更簡單的用法是使用Matrix類提供的方法來控制旋轉矩陣,比如setRotate方法就用于設定旋轉的角度。代碼清單2-31就展示了Matrix類的用法。
代碼清單 2-31
public class TestImageView extends View implements Runnable { private Bitmap star = null; private int starWidth = 0; private int starHeight = 0; private float starAngle = 0.0f; private Matrix starMatrix = new Matrix(); public TestImageView(Context context) { super(context); // 加載資源 Resources res = this.getResources(); star = BitmapFactory.decodeResource(res, R.drawable.star); // 獲取原始圖片寬高 starWidth = star.getWidth(); starHeight = star.getHeight(); // 開始重繪視圖 new Thread(this).start(); } public void onDraw(Canvas canvas) { super.onDraw(canvas); // 重置旋轉矩陣 starMatrix.reset(); // 設置旋轉角度 starMatrix.setRotate(starAngle); // 重繪旋轉的圖形 Bitmap starBitmap = Bitmap.createBitmap(star, 0, 0, starWidth, starHeight, starMatrix, true); canvas.drawBitmap(starBitmap, 0, 0, null); } @Override public void run() { while (!Thread.currentThread().isInterrupted()) { try { Thread.sleep(100); starAngle++; // 旋轉角度 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 通知主線程更新圖像 this.postInvalidate(); } } }
上述代碼中的TestImageView類是一個完整的重繪畫布視圖的例子。首先,該類繼承自View基類,同時還包含了一個線程類的run方法,在該方法的邏輯中,每100ms進行一次重繪,即調用postInvalidate方法通知主線程更新圖像。其次,在TestImageView類的構造方法中,主要包含了資源初始化的邏輯,這里程序加載了一個五星形狀的圖像資源文件。另外,在onDraw方法中,我們可以看到starMatrix變換矩陣的常見用法之一,即通過setRotate方法設置旋轉的角度。該程序最終的運行效果,就是畫出了一個繞著屏幕左上方順時針旋轉的五角星,如圖2-14所示。
當然,我們還可以讓圖像繞著某個中心點旋轉,這也不是問題,我們只需要對onDraw方法的邏輯稍做修改即可,修改過的邏輯實現如代碼清單2-32所示。
代碼清單 2-32
... public void onDraw(Canvas canvas) { super.onDraw(canvas); // 重置旋轉矩陣 starMatrix.reset(); // 設置旋轉中心 float transX = 100; float transY = 100; float pivotX = starWidth/2; float pivotY = starHeight/2; starMatrix.setRotate(starAngle, pivotX, pivotY); starMatrix.postTranslate(transX, transY); // 重繪旋轉的圖形 canvas.drawBitmap(star, starMatrix, null); } ...
要讓圖形繞著其中心旋轉,首先要使用setRotate方法設置圖形的旋轉中心,然后再使用postTranslate方法把圖形平移到相應的位置,即坐標(transX,transY)。該實例的運行效果如圖2-15所示,我們可以看到屏幕上出現了一個不斷自轉的五角星。

圖2-14 旋轉的五角星

圖2-15 自轉的五角星
當然,除了旋轉之外,常見的圖形變換還包括大小變換、傾斜變換等,限于篇幅,這里就不做介紹了,有興趣的讀者可以參考Matrix類文檔中的preScale、postScale、preSkew、postSkew等方法。這里我們還需要注意的是pre和post系列方法的區別,帶有pre前綴的方法表示此變換邏輯需要應用在所有變換邏輯之前,而帶有post前綴的方法則表示此變換邏輯會依次往后排列,因此代碼清單2-28中的旋轉邏輯也可以使用代碼清單2-33中的代碼替代。
代碼清單 2-33
... public void onDraw(Canvas canvas) { ... starMatrix.setTranslate(transX, transY); starMatrix.preRotate(starAngle, pivotX, pivotY); ... } ...
- C語言程序設計案例教程(第2版)
- 劍指JVM:虛擬機實踐與性能調優
- Vue.js快跑:構建觸手可及的高性能Web應用
- iOS開發實戰:從零基礎到App Store上架
- OpenCV for Secret Agents
- The DevOps 2.4 Toolkit
- 名師講壇:Java微服務架構實戰(SpringBoot+SpringCloud+Docker+RabbitMQ)
- Java設計模式及實踐
- Java應用開發技術實例教程
- OpenResty完全開發指南:構建百萬級別并發的Web應用
- Mastering AWS Security
- 新印象:解構UI界面設計
- Java并發編程之美
- Python Programming for Arduino
- C#程序設計基礎與實踐