第六章 一張白紙好作畫—Canvas畫布(5),canvas畫布
6.4.8地區 android.graphics.Region與Region.Op
在Canvas的繪畫時,我們可能碰到止需要顯示半個矩形,或者顯示一部分圖片,那麼我們就要用到Canvas的設定地區的方法,有clipRect(Rect rect,Region.Op op)、clipRegion(Region region)這兩個方法。Region表示的是一個地區和Rect不同的是,它可以表示的一個不規則的樣子,可以是橢圓、多邊形等等,當然Region也可以表示一個矩形,而Rect僅僅是矩形。
同樣Region的boolean contains(int x, int y) 成員可以判斷一個點是否在該地區內。
Region.Op是多個地區疊加效果的參數。
public enum Op { DIFFERENCE(0),//DIFFERENCE 第一個中不同於第二個的部分顯示出來 INTERSECT(1),//INTERSECT 取兩者交集,預設的方式 UNION(2),//UNION 取全集 XOR(3),//XOR 補集,就是全集的減去交集的剩餘部分顯示 REVERSE_DIFFERENCE(4),//第二個不同於第一個的部分顯示 REPLACE(5);//REPLACE 顯示第二個的 } |
下面我們來解讀下SDK中的ApiDemos(com.example.android.apis.graphics.Region.java)這個樣本。6-4所示。
圖6-4 Region的樣本
它主要是將兩個Rect放在同一個Region中,根據不同的Region.Op來製作出的,有顏色的地區為有效區,不同的顏色表示合并後產生的不同Rect。
核心代碼如下:
// 定義兩個Rect(矩形) mRect1.set(10, 10, 100, 80); mRect2.set(50, 50, 130, 110); // 定義一個Region,用來儲存兩個Rect的集合 Region rgn = new Region(); // 將mRect1添加進Region中 rgn.set(mRect1); // 將mRect2添加進Region中,注意這裡的第二個參數,他就是要傳進去得效果的標示,詳細參見上面的Region.Op說明。 rgn.op(mRect2, op); // Region的迭代器,可以講一個Region分解成不同的Rect,通過iter.next(Rect r)方法來把每個矩形提取出來。 RegionIterator iter = new RegionIterator(rgn); Rect r = new Rect (); while (iter.next(r)) { canvas.drawRect(r, mPaint); } |
6.4.9千姿百態矩陣變換 android.graphics.Matrix
對前面的基礎知識有所瞭解後,我們就可以來看android.graphics.Matrix類,該類表示一個轉換矩陣,它確定如何將一個座標空間的點映射到另一個座標空間。通過設定Matrix對象的屬性並將其應用於Canvas對象或Bitmap對象,我們可以對該對象執行各種圖形轉換。這些轉換函式包括平移(x和y重新置放)、旋轉、縮放和傾斜,達到很炫的效果。
matrix 對象被視為具有如下內容的 3 x 3 的矩陣:
在傳統的轉換矩陣中,u、v和w屬性具有其它功能。Matrix類只能在二維空間中操作,因此始終假定屬性值u和v為0.0,屬性值w為1.0。換句話說,矩陣的有效值如下:
您可以擷取和設定 Matrix 對象的全部六個其它屬性的值:a、b、c、d、tx 和 ty。
Matrix類支援四種主要的轉換函式類型:平移、縮放、旋轉和傾斜。對於這些函數中的三種,有特定的方法,如表6-8中所述。
方法 |
矩陣值 |
顯示結果 |
說明 |
Translate(tx,ty) |
|
|
平移(置換), 將映像向右移動tx像素,向下移動ty 像素。 |
scale(sx, sy) |
|
|
縮放, 調整映像的大小,方法是將每個像素的位置在 x 軸方向上乘以sx並在 y 軸方向上乘以sy。 |
rotate(q) |
|
|
旋轉, 將映像旋轉一個以弧度為單位的角度q。 |
Skew(skx, sky) |
|
|
傾斜, 以平行於X軸或Y軸的方向逐漸滑動映像。skx 值充當乘數,控制沿x 軸滑動的距離;sky控制沿y軸滑動的距離。 |
表6-8 Matrix類支援的四種主要的轉換函式
下面的代碼簡單的實現了圖片的倒影鏡像
Matrix mMatrix = new Matrix(); mMatrix.setScale(1.0f, -1.0f); canvas.drawBitmap(mBitmap, mMatrix, null); |
上述的四種操作的方法,每種操作方法都有三種介面setXX、preXX、postXX。setXX將使整個matrix的值為設定的值。preXX是將新的變換矩陣左乘原來的矩陣,而postXX是將新的變換矩陣右乘原來的變換矩陣。
經驗分享: 在組合matrix中preTranslate、setTranslate、postTranslate是有很大區別的。抽象的說pre方法是向前“生長”,post方法是向後“生長”,下面還是通過2個例子來說明: matrix.preScale(0.5f, 1); matrix.preTranslate(10, 0); matrix.postScale(0.7f, 1); matrix.postTranslate(15, 0); 則座標變換經過的4個變換過程依次是: translate(10, 0) -> scale(0.5f, 1) -> scale(0.7f, 1) -> translate(15, 0), 所以對matrix方法的調用順序是很重要的,不同的順序往往會產生不同的變換效果。pre方法的調用順序和post方法的互不影響,即以下的方法調用和前者在真實座標變換順序裡是一致的。 matrix.postScale(0.7f, 1); matrix.preScale(0.5f, 1); matrix.preTranslate(10, 0); matrix.postTranslate(15, 0); 而matrix的set方法則會對先前的pre和post操作進行刷除,而後再設定它的值,比如下列的方法調用: matrix.preScale(0.5f, 1); matrix.postTranslate(10, 0); matrix.setScale(1, 0.6f); matrix.postScale(0.7f, 1); matrix.preTranslate(15, 0); 其座標變換順序是translate(15, 0) -> scale(1, 0.6f) -> scale(0.7f, 1). 另外可以注意這個方法Matrix.mapRect(RectF rect);對RectF矩形進行變換。 |
矩陣一般應用在變換view的時候,那麼很多時候我們將需要將一些點或矩形,進行轉換,Android的Matrix為我們提供了很方便的方法來進行計算。下面我們來看個例子:
float[] p1 = {1000f,100f}; float[] p2 = {1000f,100f}; // 下面是一個正向的過程 // 原始變換矩陣 Matrix m1 = new Matrix(); // m1的逆矩陣 Matrix m2 = new Matrix(); Log.d("test111 ", ""+p1[0]+","+p1[1]); m1.postTranslate(100, 300); m1.postScale(0.6f, 0.3f); m1.postRotate(45.f); // 這個過程是將p1{1000f,100f}這個點通過了m1的轉換,變成了一個新的點p1,這時候p1已經變成了轉換後的點了。 m1.mapPoints(p1); Log.d("test222 ", ""+m1.toString()); Log.d("test333 ", ""+p1[0]+","+p1[1]); // 下面是一個逆向的過程 // 將p1經過轉換的點,賦值給p2 p2 = p1; Log.d("test444 ", ""+p2[0]+","+p2[1]); // 這裡將m1進行了逆向,然後存放在m2裡 boolean temp = m1.invert(m2); Log.d("test555 ", ""+m2.toString()); Log.d("test666 ", ""+temp); // 這裡轉換過的點可以理解為轉換後的點,通過逆向矩陣m2得到最原始的點的位子,並存放在p2裡。 m2.mapPoints(p2); Log.d("test777 ", ""+p2[0]+","+p2[1]); |
圖6-5顯示了啟動並執行結果。
圖6-5 矩陣逆向例子的結果
經驗分享: 通過上面的例子我們可以看到,你可能在一個時候只需要用到一部分,及矩陣正向的邏輯,或矩陣逆向的邏輯。需要注意的是下面2個方法: 1)m1.mapPoints(p1);//這個過程是將p1{1000f,100f}這個點通過了m1的轉換,變成了一個新的點p1,這時候p1已經變成了轉換後的float數組了。 2)m1.invert(m2);//這裡將m1進行了逆向,然後存放在m2裡 |