1. 程式人生 > >第六章 一張白紙好作畫—Canvas畫布(4)

第六章 一張白紙好作畫—Canvas畫布(4)

6.4.4路徑android.graphics.Path

當我們的需求是一個不規則的圖形的時候,Canvas的drawRect等方法就不行了,這裡就要用到drawPath(Path path, Paint paint)方法來按路徑繪畫一個形狀。Canvas還有一個方法clipPath(Path path)。這個方法用於按照設計的路徑來設定Canvas中的有效區域。

下面我們就介紹下路徑類,它是一個多個點和圖形的集合。

Path的構造方法比較簡單,如下:

Path path1 = new Path();  //構造方法 

下面我們畫一個封閉的原型路徑,我們使用Path類的addCircle方法。

path1.addCircle(10,10,50,Direction.CW);

解釋下此方法:

voidaddCircle(float x, float y, float radius, Direction dir)

引數 x是x軸水平位置;引數y是y軸垂直位置;引數radius是圓形的半徑;引數dir是繪製的方向,CW為順時針方向,而CCW是逆時針方向。

同樣我們也可以自由的新增一些點和線,而組成一個三角形。

Path path2 = new Path();

// 將路徑的起始點移到90,330

path2.moveTo(90, 330);

// 從90,330畫一條直線到150,330

path2.lineTo(150,330);

// 從150,330畫一條直線到120,270

path2.lineTo(120,270);

// 關閉當前的輪廓。這樣就形成一個三角形了

path2.close();

結合Canvas類中的繪製方法drawPath()和drawTextOnPath(),我們可以在onDraw()中加入如下程式碼。

// 這裡pathPaint為路徑的畫筆的顏色

canvas.drawPath(path1, pathPaint);

// 將文字繪製到路徑中去

canvas.drawTextOnPath("Android", path2,0,15, textPaint);

下面,我們的onDraw()方法中演示瞭如何繪製路徑。

@Override 

protected void onDraw(Canvas canvas) { 

Paint pathPaint =new Paint(); 

Paint textPaint =new Paint(); 

// 路徑的畫刷為紅色

pathPaint.setColor(Color.Red); 

// 設定paint的style為FILL:實心

pathPaint.setStyle(Paint.Style.FILL);

// 路徑上的文字為藍色 

textPaint.setColor(Color.Blue);

Path path1 = new Path();

Path path2 = new Path(); 

// 省略部分程式碼

canvas.drawPath(path1, pathPaint); 

// 在路徑上繪製文字

canvas.drawTextOnPath("Android", path2,0,15, textPaint); 

解釋下方法:

voiddrawTextOnPath (String text, Path path, float hOffset, float vOffset, Paintpaint) 

引數text,為需要在路徑上繪製的文字內容;引數path,將文字繪製到哪個路徑;引數hOffset,距離路徑開始的距離;引數vOffset,離路徑的上下高度,該引數型別為float浮點型,除了精度為8位小數外,可以為正或負,當為正時文字在路徑的圈裡面,為負時在路徑的圈外面;引數paint,最後仍然是一個Paint物件用於制定Text本文的顏色、字型、大小等屬性。

有關路徑類常用的其它方法如表6-5所示。

方法

返回值

說明

addArc(RectF oval, float startAngle, float sweepAngle)

void   

為路徑新增一個多邊形

addCircle(float x, float y, float radius, Path.Direction dir)

void   

給路徑新增圓圈

addOval(RectF oval, Path.Direction dir)

void   

新增橢圓形

addRect(RectF rect, Path.Direction dir)

void   

新增一個區域

addRoundRect(RectF rect, float[] radii, Path.Direction dir)

void   

新增一個圓角區域

isEmpty()

boolean 

判斷路徑是否為空

transform(Matrix matrix)

void   

應用矩陣變換

transform(Matrix matrix, Path dst)

void   

應用矩陣變換並將結果放到新的路徑中,即第二個引數。

表6-5 Path類常用的其它方法

6.4.5路徑的高階效果android.graphics.PathEffect

一條直線是否太單調了,下面我們來看看路徑的高階效果,如圖6-2所示。是不是很炫,這些效果其實都是使用PathEffect類實現的。

圖6-2 路徑的高階效果

PathEffect對於繪製Path基本圖形特別有用,它可以應用到任何Paint中從而影響線條繪製的方式。使用PathEffect,可以改變一個形狀的邊角的外觀並且控制輪廓的外表。SDK附帶的ApiDemos(com.example.android.apis.graphics.PathEffects.java)示例給出瞭如何應用每一種效果的指導說明。圖6-2就是ApiDemos中的PathEffects的效果圖。

Android包含了多個PathEffect,包括:

1)CornerPathEffect  可以使用圓角來代替尖銳的角從而對基本圖形的形狀尖銳的邊角進行平滑。

2)DashPathEffect  可以使用DashPathEffect來建立一個虛線的輪廓(短橫線/小圓點),而不是使用實線。你還可以指定任意的虛/實線段的重複模式。

3)DiscretePathEffect  與DashPathEffect相似,但是添加了隨機性。當繪製它的時候,需要指定每一段的長度和與原始路徑的偏離度。

4)PathDashPathEffect  這種效果可以定義一個新的形狀(路徑)並將其用作原始路徑的輪廓標記。

複雜的效果可以在一個Paint中使用多個Path Effect組合而成的一個PathEffect。

5)SumPathEffect  順序地在一條路徑中新增兩種效果,這樣每一種效果都可以應用到原始路徑中,並且兩種效果結合起來。SumPathEffect (first, second) = first(path) + second(path)

6)ComposePathEffect  組合兩種效果,結果為先使用第一種效果,然後在這種效果的基礎上應用第二種效果。ComposePathEffect (outer ,inner)= Outer(inner(path))。

7)DiscretePathEffect 將路徑劃分成指定長度的線段,然後把每條線段隨機偏移原來的位置。

物件形狀的PathEffect的改變會影響到形狀的區域。這就能夠保證應用到相同形狀的填充效果將會繪製到新的邊界中。

上面效果圖的核心程式碼如下:

// phase 指定的是虛線上虛實偏移,每次加1,相當於交換虛處和實處的位置。

// 這樣通過不停的重新整理就可以達到虛實不斷變換給人以動畫的效果。

private static void makeEffects(PathEffect[] e, float phase) {

e[0] = null;

e[1] = new CornerPathEffect(10);

e[2] = new DashPathEffect(new float[] {10, 5, 5, 5}, phase);

e[3] = new PathDashPathEffect(makePathDash(), 12, phase,

                    PathDashPathEffect.Style.TRANSLATE);

e[4] = new PathDashPathEffect(makePathDash(), 12, phase,

                    PathDashPathEffect.Style.ROTATE);

e[5] = new PathDashPathEffect(makePathDash(), 12, phase,

                    PathDashPathEffect.Style.MORPH);

e[6] = new ComposePathEffect(e[2], e[1]);

e[7] = new SumPathEffect(e[2], e[1]);

e[8] = new ComposePathEffect(e[5], e[1]);

e[9] = new SumPathEffect(e[5], e[1]);

}

// 製造一個形狀,PathDashPathEffect顯示的單位形狀

private static Path makePathDash() {

Path p = new Path();

p.moveTo(4, 0);

p.lineTo(0, -4);

p.lineTo(8, -4);

p.lineTo(12, 0);

p.lineTo(8, 4);

p.lineTo(0, 4);

return p;

}

6.4.6點類 android.graphics.Point和android.graphics.PointF

看過了Canvas畫出的線條,那麼我們來看看組成線條的基礎,點(Point類)。

Point類有兩個屬性,分別是:X 座標和 y 座標。

建構函式有三種。

Point() //構造一個點

Point(int x,int y) //傳入x和y座標構造一個點

Point(Point p) //傳入一個Point物件構造一個點

主要方法如表6-6所示。

方法

返回值

說明

set(x,y)

void

重新設定一下 x,y 的座標

offset(int dx,int dy)

void

給座標一個補償值,值可以使正的也可以是負的

negate()

void

否定座標值

表6-6 Point類常用的方法

Point類和android.graphics.PointF類似,不同點是前者座標值的型別是 int 型,而後者的座標值是float 型。

除此之外 PointF 類多加了 幾個方法,比如:

public final float length();//返回(0,0)點到該點的距離。

public static float length(float x,float y);//返回(0,0)點到(x,y) 點的距離。

經驗分享:

說到座標點,那麼我們就不得不說下手機螢幕的座標系,手機的座標系和一般的物理座標系不同,手機螢幕座標系的原點(0,0)在螢幕的左上角,沿左沿邊和上沿邊,x,y數值依次增加。如下圖6-3所示。

圖6-3手機座標點示意圖

6.4.7形狀類android.graphics.Rect和android.graphics.RectF

矩形,繪圖上比較常用的幾種形狀之一。RectF這個類包含一個矩形的四個單精度浮點座標。矩形通過上、下、左、右4個邊的座標來表示一個矩形。這些座標值屬性可以被直接訪問,用width()和height()方法可以獲取矩形的寬和高。

RectF一共有四個構造方法.

RectF() //構造一個無參的矩形

RectF(float left,float top,float right,float bottom) //構造一個指定了4個引數的矩形

RectF(RectF r) //根據指定的RectF物件來構造一個RectF物件(複製一個Rect F)

RectF(Rect r) //根據給定的Rect物件來構造一個RectF物件

RectF提供了很多方法,下面介紹幾個方法,如表6-7所示。

方法

返回值

說明

contain(RectF r)

boolean

判斷一個點或矩形是否在此矩形內,如果在這個矩形內或者和這個矩形等價則返回true

offset(float dx, float dy)

void

平移dx,dy距離

offsetTo(float newLeft, float newTop)

void

平移到新的位置

inset(float dx, float dy)

void

縮小2*dx,2*dy

表6-7 RectF類常用的方法

經驗分享:

Android.graphics.Rect類, 這個類同android.graphics.RectF很相似,不同的地方是Rect類的座標是用整型表示的,而RectF的座標是用單精度浮點型表示的。

獲取Matrix中的X的縮放比例:

public void getValues(float[] values);

// 陣列values是一個size>9的陣列,values [Matrix.MSCALE_X]就為縮放比例。其他引數也在其中如:Matrix。

public static final int MSCALE_X = 0;

public static final int MSKEW_X  = 1;

public static final int MTRANS_X = 2;

public static final int MSKEW_Y  = 3;

public static final int MSCALE_Y = 4;

public static final int MTRANS_Y = 5;

public static final int MPERSP_0 = 6;

public static final int MPERSP_1 = 7;

public static final int MPERSP_2 = 8;