Canvas之translate、scale、rotate、skew方法講解!

yangxi_001發表於2017-06-09

前面說Canvas大致可以分為三類:

1. save、restore 等與層的儲存和回滾相關的方法;

2. scale、rotate、clipXXX 等對畫布進行操作的方法;

3. drawXXX 等一系列繪畫相關的方法;


前面主要講了drawBitmap方法,並舉了一個星球浮動的栗子,在那個例子中,星球有大有小,需要移動,有時候可能需求上還需要旋轉或錯切,有了這些需求,我們就需要使用到與Canvas相關的translate、scale、rotate、skew這幾個方法,平移、縮放、旋轉、錯切,這四個詞聽起來是如此的熟悉,我們在做一些基本動畫的時候經常會與這幾個詞打交道,現在我們一個個看下當把這幾個傢伙和Canvas(畫布)結合能產生什麼效果;

當然在看之前得先明確兩個基本概念:

1.Canvas 的左上角是(0,0);

2.基於左上角往右 X 為正,往下 Y 為正,反之為負;


一、canvas.translate() - 畫布的平移:

首先我們們在畫布上畫一個400 X 400 紅色的矩形

[html] view plain copy
  1. canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  

此時整個畫布的左上角出現了一個紅色的矩形(為了更清楚,藍色打個底)該矩形大小為400 X 400 ,效果如下:


接下來我們canvas.translate( )玩玩

[html] view plain copy
  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     super.onDraw(canvas);  
  4.     canvas.drawColor(Color.BLUE);  
  5.     canvas.translate(100, 100);  
  6.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  7. }  

看下效果:

此時可以看到,雖然是繪製同樣的矩形,但矩形在畫布上的位置已經向右和向下各移動了100px;

既然如此,這個時候如果我們再將canvas 平移(translate)(100,100),再繪製一個同樣的矩形會出現什麼情況呢?會與之前的矩形重疊嗎?我們們拭目以待:

[html] view plain copy
  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     super.onDraw(canvas);  
  4.     canvas.drawColor(Color.BLUE);  
  5.     canvas.translate(100, 100);  
  6.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  7.     canvas.translate(100, 100);  
  8.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  9. }  


從效果上看,兩次translate 進行了疊加,繪製第二個矩形的時候畫布已經偏移了(200,200);

好了,瞭解到這裡,我們們利用canvas.translate( )一起來做個小栗子,繪製一個生活中比較常用的刻度尺;

我們們先從網上找個用於參考的刻度尺圖片:

從圖上看,刻度尺的元素有:外框、刻度線(不同的數值刻度線長短不一)、數字

所以我們所要做的就是對上面的元素在onDraw裡分別繪製:

[html] view plain copy
  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     super.onDraw(canvas);  
  4.     // 繪製外框  
  5.     drawOuter(canvas);  
  6.     // 繪製刻度線  
  7.     drawLines(canvas);  
  8.     // 繪製數字  
  9.     drawNumbers(canvas);  
  10. }  
我們們先簡單分析一下,刻度尺有個外框,外框距離左右都有一定的邊距,第一根和最後一根刻度線距離邊框也有一定的邊距,其餘刻度線之間距離相同,另外一些特殊的刻度線長短不一;

有了上面的分析,我們們一個一個來,先繪製外框,外框也就是一個矩形,只需要確定邊框的位置和大小,然後使用canvas.drawRect( )繪製即可:

我們們先定義幾個需要的資料,為了螢幕適配,資料均為dp:

[html] view plain copy
  1. // 刻度尺高度  
  2. private static final int DIVIDING_RULE_HEIGHT = 70;  
  3. // 距離左右間  
  4. private static final int DIVIDING_RULE_MARGIN_LEFT_RIGHT = 10;  
  5.   
  6. // 第一條線距離邊框距離  
  7. private static final int FIRST_LINE_MARGIN = 5;  
  8. // 打算繪製的釐米數  
  9. private static final int DEFAULT_COUNT = 9;  
然後將以上資料轉為對應畫素值:

[html] view plain copy
  1. private void initData() {  
  2.     mDividRuleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,  
  3.             DIVIDING_RULE_HEIGHT, mResources.getDisplayMetrics());  
  4.     mHalfRuleHeight = mDividRuleHeight / 2;  
  5.   
  6.     mDividRuleLeftMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,  
  7.             DIVIDING_RULE_MARGIN_LEFT_RIGHT, mResources.getDisplayMetrics());  
  8.     mFirstLineMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,  
  9.             FIRST_LINE_MARGIN, mResources.getDisplayMetrics());  
  10.   
  11. }  
有了以上資料,則可以確定外邊框的Rect為:

[html] view plain copy
  1. mOutRect = new Rect(mDividRuleLeftMargin, top, mTotalWidth - mDividRuleLeftMargin,  
  2.                 mRuleBottom);  

接下來看刻度線的繪製,根據釐米可以計算出中間的格數,根據釐米佔用螢幕寬度和所佔格數可以計算出每一格所佔螢幕寬度:

[html] view plain copy
  1. mLineInterval = (mTotalWidth - 2 * mDividRuleLeftMargin - 2 * mFirstLineMargin)  
  2.                 / (DEFAULT_COUNT * 10 - 1);  

有了每一格所佔寬度,我們只需要在繪製刻度線的時候不斷將畫布右移對應寬度即可:

[html] view plain copy
  1. /**  
  2.  * 繪製刻度線  
  3.  * @param canvas  
  4.  */  
  5. private void drawLines(Canvas canvas) {  
  6.     canvas.save();  
  7.     canvas.translate(mLineStartX, 0);  
  8.     int top = mMaxLineTop;  
  9.     for (int i = 0; i <= DEFAULT_COUNT * 10; i++) {  
  10.         if (i % 10 == 0) {  
  11.             top = mMaxLineTop;  
  12.         } else if (i % 5 == 0) {  
  13.             top = mMiddleLineTop;  
  14.         } else {  
  15.             top = mMinLineTop;  
  16.         }  
  17.   
  18.         canvas.drawLine(0, mRuleBottom, 0, top, mLinePaint);  
  19.         canvas.translate(mLineInterval, 0);  
  20.   
  21.     }  
  22.     canvas.restore();  
  23.   
  24. }  
由於刻度尺上分三種長短的刻度線,我們也做對應處理,10的整數倍的刻度線最長,5的整數倍的刻度線中等長度,其餘較短;

此時繪製出的刻度尺效果為:


此時刻度尺的基本樣子就出來了,對應文字大家有興趣可以自己加上;

俗話說,條條大路通羅馬,我們除了使用canvas.translate ,還能不能使用別的方式進行實現呢,答案當然是可以,比如在繪製的時候根據for迴圈裡的 i 值也可以直接計算出每一根刻度線的位置,然後直接進行繪製,相比之下,這兩種方式的優劣大家也可以自行比較一下,好了,canvas.translate() 就說這麼多;


二、canvas.scale( ) - 畫布的縮放:

關於scale,Android 提供了以下兩個介面:

[html] view plain copy
  1. /**  
  2.  * Preconcat the current matrix with the specified scale.  
  3.  *  
  4.  * @param sx The amount to scale in X  
  5.  * @param sy The amount to scale in Y  
  6.  */  
  7. public native void scale(float sx, float sy);  
  8.   
  9. /**  
  10.  * Preconcat the current matrix with the specified scale.  
  11.  *  
  12.  * @param sx The amount to scale in X  
  13.  * @param sy The amount to scale in Y  
  14.  * @param px The x-coord for the pivot point (unchanged by the scale)  
  15.  * @param py The y-coord for the pivot point (unchanged by the scale)  
  16.  */  
  17. public final void scale(float sx, float sy, float px, float py) {  
  18.     translate(px, py);  
  19.     scale(sx, sy);  
  20.     translate(-px, -py);  
  21. }  
我們先看下scale(float sx , float sy),我們還是以上面的正方形作為栗子,呼叫canvas.scale(float sx , float sy)之後看下效果;

[html] view plain copy
  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     super.onDraw(canvas);  
  4.     canvas.drawColor(Color.BLUE);  
  5.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  6.     canvas.scale(0.5f, 0.5f);  
  7.     mPaint.setColor(Color.YELLOW);  
  8.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  9. }  
我們將畫布在x,y方向上均縮放為 0.5 倍,使用預設基準點(原點 0,0),效果如下:


效果就相當於用個釘子釘在(0,0)處,然後把矩形的x,y縮放為一半,我們再來看看第二個介面scale(float sx , float sy, float px,float py):

前兩個引數為將畫布在x、y方向上縮放的倍數,而px和py 分別為縮放的基準點,從原始碼上可以非常清楚的看出和scale(float sx , float sy)的差別:

[html] view plain copy
  1. translate(px, py);  
  2. scale(sx, sy);  
  3. translate(-px, -py);  
即先將畫布平移px,py,然後scale,scale結束之後再將畫布平移回原基準點;

我們再在之前的基礎上繪製一個同樣的矩形,x , y 均縮放為 0.5 倍,縮放中心為矩形的中心:

[html] view plain copy
  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     super.onDraw(canvas);  
  4.     canvas.drawColor(Color.BLUE);  
  5.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  6.   
  7.     // 儲存畫布狀態  
  8.     canvas.save();  
  9.     canvas.scale(0.5f, 0.5f);  
  10.     mPaint.setColor(Color.YELLOW);  
  11.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  12.     // 畫布狀態回滾  
  13.     canvas.restore();  
  14.   
  15.     canvas.scale(0.5f, 0.5f, 200, 200);  
  16.     mPaint.setColor(Color.BLACK);  
  17.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  18. }  

一起來看下效果:


效果就相當於用個釘子釘在矩形的中心,然後進行縮放;

根據上面android 的實現,我們其實可以使用以下程式碼實現同樣的效果:

[html] view plain copy
  1. // 先將畫布平移到矩形的中心  
  2. canvas.translate(200, 200);  
  3. // 將畫布進行縮放  
  4. canvas.scale(0.5f, 0.5f);  
  5. // 將畫布移回原基準點  
  6. canvas.translate(-200, -200);  
  7. mPaint.setColor(Color.BLACK);  
  8. canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  

到此為止,我們也就瞭解了對畫布的縮放,基於canvas.scale(),我們一起完成一個小例子:


上面是網路上找的一張讓人產生視覺誤差的靜態圖,我們模擬繪製出上面的效果;

思路非常的簡單:

1. 繪製一個和螢幕等寬的正方形;

2. 將畫布以正方形中心為基準點進行縮放;

3. 在縮放的過程中繪製原正方形;

注:每次繪製都得使用canvas.save()  和 canvas.restore()進行畫布的鎖定和回滾,以免除對後面繪製的影響(後面會單獨講)

先初始化畫筆,注意此時畫筆需要設定成空心:

[html] view plain copy
  1. /**  
  2.  * 初始化畫筆  
  3.  */  
  4. private void initPaint() {  
  5.     mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  6.     // 將畫筆設定為空心  
  7.     mPaint.setStyle(Style.STROKE);  
  8.     // 設定畫筆顏色  
  9.     mPaint.setColor(Color.BLACK);  
  10.     // 設定畫筆寬度  
  11.     mPaint.setStrokeWidth(mLineWidth);  
  12. }  
然後迴圈的將畫布縮放的同時繪製原正方形:

[html] view plain copy
  1. /**  
  2.  * 繪製正方形  
  3.  *   
  4.  * @param canvas  
  5.  */  
  6. private void drawSquare(Canvas canvas) {  
  7.     for (int i = 0; i < TOTAL_SQUARE_COUNT; i++) {  
  8.         // 儲存畫布  
  9.         canvas.save();  
  10.         float fraction = (float) i / TOTAL_SQUARE_COUNT;  
  11.         // 將畫布以正方形中心進行縮放  
  12.         canvas.scale(fraction, fraction, mHalfWidth, mHalfHeight);  
  13.         canvas.drawRect(mSquareRect, mPaint);  
  14.         // 畫布回滾  
  15.         canvas.restore();  
  16.     }  
  17. }  
一起來看下繪製的效果:

其實最終效果和網上找的還是有點小差別的,由於畫布的縮放,越小的時候畫筆寬度越細,而原圖是所有的都一樣寬度,但似乎畫筆寬度縮放之後效果更佳,哈哈 ... ... 


三、canvas.rotate( ) - 畫布的旋轉:

canvas.rotate( )和canvas.scale()可以類比起來看,如果理解了canvas.scale( ),那麼canvas.rotate( )將會非常簡單實用;

簡單來講,canvas.rotate( )即是將畫布進行旋轉,和canvas.scale( )類似的是,它也有兩個可以使用的方法:

[html] view plain copy
  1. /**  
  2.  * Preconcat the current matrix with the specified rotation.  
  3.  *  
  4.  * @param degrees The amount to rotate, in degrees  
  5.  */  
  6. public native void rotate(float degrees);  
  7.   
  8. /**  
  9.  * Preconcat the current matrix with the specified rotation.  
  10.  *  
  11.  * @param degrees The amount to rotate, in degrees  
  12.  * @param px The x-coord for the pivot point (unchanged by the rotation)  
  13.  * @param py The y-coord for the pivot point (unchanged by the rotation)  
  14.  */  
  15. public final void rotate(float degrees, float px, float py) {  
  16.     translate(px, py);  
  17.     rotate(degrees);  
  18.     translate(-px, -py);  
  19. }  

兩個方法的區別也是在於基準點的選取,預設是以原點作為基準點,另一個則是以傳入的x,y 作為基準點,是不是和scale 一模一樣,我們們一起來rotate一下:

我們們先轉轉左上角的矩形,轉多少度呢?先來個90度玩玩吧;

[html] view plain copy
  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     super.onDraw(canvas);  
  4.     canvas.drawColor(Color.BLUE);  
  5.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  6.     mPaint.setColor(Color.YELLOW);  
  7.     canvas.rotate(90);  
  8.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  9. }  
我們的預期是螢幕上有個旋轉了的騷黃色矩形,一起來看看;


擦,黃色的矩形呢?

由於基準點是原點,我們直接旋轉了90 度,所以已經將矩形旋轉出螢幕,當然看不到了,我們將角度調小一點,改為45 度:

[html] view plain copy
  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     super.onDraw(canvas);  
  4.     canvas.drawColor(Color.BLUE);  
  5.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  6.     mPaint.setColor(Color.YELLOW);  
  7.     canvas.rotate(45);  
  8.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  9. }  

此時我們可以可以清楚的看到黃色的矩形是紅色矩形繞原點(0,0)旋轉45度之後的結果;


我們再將旋轉基準點改為矩形中心看看:

[html] view plain copy
  1. canvas.rotate(45,200,200);  

可以看到現在黃色矩形是紅色矩形繞著中心旋轉後的結果:


到這裡,我們已經瞭解了canvas.rotate(float degrees)和 canvas.rotate(float degrees,float px , float py)的使用,同樣也應該清楚後者的實現如下:

[html] view plain copy
  1. translate(px, py);  
  2. rotate(degrees);  
  3. translate(-px, -py);  

好了,我們再利用canvas.rotate()完成個鬧鐘錶盤的小例子:

鬧鐘錶盤其實和刻度尺類似,只是一個是在一條直線上繪製,一個是在一個圓周上繪製,說到底都是確定一個位置繪製刻度線;

既然是圓周,最簡單的方式莫過於在鬧鐘的12點鐘處劃線,通過canvas的旋轉繪製到對應圓周處,我們一起實現一下:

整個圓周是360 度,每隔 30 度為一個整時間刻度,整刻度與刻度之間有四個短刻度,劃分出5個小段,每個段為6度,有了這些分析,我們則可以採用如下程式碼進行繪製:

[html] view plain copy
  1. /**  
  2.  * 繪製刻度  
  3.  *   
  4.  * @param canvas  
  5.  */  
  6. private void drawLines(Canvas canvas) {  
  7.     for (int i = 0; i <= 360; i++) {  
  8.         if (i % 30 == 0) {  
  9.             mLineBottom = mLineTop + mLongLineHeight;  
  10.             mLinePaint.setStrokeWidth(mLineWidth);  
  11.         } else {  
  12.             mLineBottom = mLineTop + mShortLineHeight;  
  13.             mLinePaint.setStrokeWidth(mHalfLineWidth);  
  14.         }  
  15.   
  16.         if (i % 6 == 0) {  
  17.             canvas.save();  
  18.             canvas.rotate(i, mHalfWidth, mHalfHeight);  
  19.             canvas.drawLine(mLineLeft, mLineTop, mLineLeft, mLineBottom, mLinePaint);  
  20.             canvas.restore();  
  21.         }  
  22.     }  
  23. }  

此時效果如下:


整體程式碼如下:

[html] view plain copy
  1. /**  
  2.  * 鬧鐘錶盤  
  3.  *   
  4.  * @author AJian  
  5.  */  
  6. public class RotateClockView extends View {  
  7.   
  8.     private static final int LONG_LINE_HEIGHT = 35;  
  9.     private static final int SHORT_LINE_HEIGHT = 25;  
  10.     private Paint mCirclePaint, mLinePaint;  
  11.     private DrawFilter mDrawFilter;  
  12.     private int mHalfWidth, mHalfHeight;  
  13.   
  14.     // 圓環線寬度  
  15.     private int mCircleLineWidth, mHalfCircleLineWidth;  
  16.     // 直線刻度線寬度  
  17.     private int mLineWidth, mHalfLineWidth;  
  18.     // 長線長度  
  19.     private int mLongLineHeight;  
  20.     // 短線長度  
  21.     private int mShortLineHeight;  
  22.     // 刻度線的左、上位置  
  23.     private int mLineLeft, mLineTop;  
  24.   
  25.     // 刻度線的下邊位置  
  26.     private int mLineBottom;  
  27.     // 用於控制刻度線位置  
  28.     private int mFixLineHeight;  
  29.   
  30.     public RotateClockView(Context context) {  
  31.         super(context);  
  32.         mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG  
  33.                 | Paint.FILTER_BITMAP_FLAG);  
  34.   
  35.         mCircleLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8,  
  36.                 getResources().getDisplayMetrics());  
  37.         mHalfCircleLineWidth = mCircleLineWidth;  
  38.         mLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4,  
  39.                 getResources().getDisplayMetrics());  
  40.         mHalfLineWidth = mLineWidth / 2;  
  41.   
  42.         mFixLineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4,  
  43.                 getResources().getDisplayMetrics());  
  44.   
  45.         mLongLineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,  
  46.                 LONG_LINE_HEIGHT,  
  47.                 getResources().getDisplayMetrics());  
  48.         mShortLineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,  
  49.                 SHORT_LINE_HEIGHT,  
  50.                 getResources().getDisplayMetrics());  
  51.         initPaint();  
  52.     }  
  53.   
  54.     private void initPaint() {  
  55.         mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  56.         mCirclePaint.setColor(Color.RED);  
  57.         // 將畫筆設定為空心  
  58.         mCirclePaint.setStyle(Style.STROKE);  
  59.         // 設定畫筆寬度  
  60.         mCirclePaint.setStrokeWidth(mCircleLineWidth);  
  61.   
  62.         mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  63.         mLinePaint.setColor(Color.RED);  
  64.         mLinePaint.setStyle(Style.FILL_AND_STROKE);  
  65.         // 設定畫筆寬度  
  66.         mLinePaint.setStrokeWidth(mLineWidth);  
  67.     }  
  68.   
  69.     @Override  
  70.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  71.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  72.     }  
  73.   
  74.     @Override  
  75.     protected void onDraw(Canvas canvas) {  
  76.         canvas.setDrawFilter(mDrawFilter);  
  77.         super.onDraw(canvas);  
  78.         // 繪製錶盤  
  79.         drawCircle(canvas);  
  80.         // 繪製刻度  
  81.         drawLines(canvas);  
  82.     }  
  83.   
  84.     /**  
  85.      * 繪製刻度  
  86.      *   
  87.      * @param canvas  
  88.      */  
  89.     private void drawLines(Canvas canvas) {  
  90.         for (int i = 0; i <= 360; i++) {  
  91.             if (i % 30 == 0) {  
  92.                 mLineBottom = mLineTop + mLongLineHeight;  
  93.                 mLinePaint.setStrokeWidth(mLineWidth);  
  94.             } else {  
  95.                 mLineBottom = mLineTop + mShortLineHeight;  
  96.                 mLinePaint.setStrokeWidth(mHalfLineWidth);  
  97.             }  
  98.   
  99.             if (i % 6 == 0) {  
  100.                 canvas.save();  
  101.                 canvas.rotate(i, mHalfWidth, mHalfHeight);  
  102.                 canvas.drawLine(mLineLeft, mLineTop, mLineLeft, mLineBottom, mLinePaint);  
  103.                 canvas.restore();  
  104.             }  
  105.         }  
  106.     }  
  107.   
  108.     /**  
  109.      * 繪製錶盤  
  110.      *   
  111.      * @param canvas  
  112.      */  
  113.     private void drawCircle(Canvas canvas) {  
  114.         canvas.drawCircle(mHalfWidth, mHalfHeight, mHalfWidth - mHalfCircleLineWidth, mCirclePaint);  
  115.     }  
  116.   
  117.     @Override  
  118.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  119.         super.onSizeChanged(w, h, oldw, oldh);  
  120.         mHalfWidth = w / 2;  
  121.         mHalfHeight = h / 2;  
  122.   
  123.         mLineLeft = mHalfWidth - mHalfLineWidth;  
  124.         mLineTop = mHalfHeight - mHalfWidth + mFixLineHeight;  
  125.     }  
  126. }  

同樣的,有興趣的同學可以自己補上文字;


四、canvas.skew( ) - 畫布的錯切: 

[html] view plain copy
  1. /**  
  2.  * Preconcat the current matrix with the specified skew.  
  3.  *  
  4.  * @param sx The amount to skew in X  
  5.  * @param sy The amount to skew in Y  
  6.  */  
  7. public native void skew(float sx, float sy);  

這個方法只要理解了兩個引數即可:

float sx:將畫布在x方向上傾斜相應的角度,sx為傾斜角度的tan值;

float sy:將畫布在y軸方向上傾斜相應的角度,sy為傾斜角度的tan值;

注意,這裡全是傾斜角度的tan值,比如我們打算在X軸方向上傾斜45度,tan45=1;

先在X 軸上傾斜45 度,我們一起看看:

[html] view plain copy
  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     super.onDraw(canvas);  
  4.     canvas.drawColor(Color.BLUE);  
  5.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  6.   
  7.     // x 方向上傾斜45 度  
  8.     canvas.skew(1, 0);  
  9.     mPaint.setColor(0x8800ff00);  
  10.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  11. }  
效果如下:


再在y軸上傾斜45度看看:

[html] view plain copy
  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     super.onDraw(canvas);  
  4.     canvas.drawColor(Color.BLUE);  
  5.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  6.   
  7.     // y 方向上傾斜45 度  
  8.     canvas.skew(0, 1);  
  9.     mPaint.setColor(0x8800ff00);  
  10.     canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);  
  11. }  
此時效果如下:



關於Canvas(畫布)的translate(平移)、scale(縮放) 、rotate(旋轉) 、skew(錯切)就說這麼多,這些方法都不復雜,而靈活的使用往往能解決繪製中很多看似複雜的問題,所以重在理解,並在看到與之相關的效果時能夠及時恰當的進行關聯。


當然對Canvas的操作往往使用Matrix(後面會單獨講)也能達到同樣的效果,想看例子可參考 一個絢麗的loading動效分析與實現!


原始碼下載連結


轉自:http://blog.csdn.net/tianjian4592/article/details/45234419

相關文章