安卓的graphics提供了2D圖形各種繪製工具,如Canvas(畫布), color filters(顏色過濾器), points(點), rectangles(矩形)等,利用這些工具可以直接在介面上進行繪製。 本文主要講的是自定義View時我們經常用到的Canvas和Paint,像平時畫畫一樣,我們需要畫布和畫筆,而Canvas就是畫布,Paint就是畫筆.
Canvas官網地址: https://developer.android.com/reference/android/graphics/Canvas.html Paint官網地址: https://developer.android.com/reference/android/graphics/Paint.html
###先來看Paint,Paint常用方法一覽:
Paint.setAntiAlias(boolean flag);//設定抗鋸齒效果 設定true的話邊緣會將鋸齒模糊化
Paint.setDither(boolean flag);//設定防抖動,設定true的話圖片看上去會更柔和點
Paint.setColor(int color);//設定畫筆顏色
###TODO
Paint.setARGB(int a, int r, int g, int b); //設定畫筆的ARGB值
Paint.setAlpha(int alpha);//設定畫筆的Alpha值
Paint.setStyle(); //設定畫筆的style (三種:FILL填充 FILL_AND_STROKE填充加描邊 STROKE描邊 )
Paint.setStrokeWidth(float width);//設定描邊寬度
Paint.setXfermode(Xfermode xfermode);//設定圖形重疊時的處理方式,如合併,取交集或並集,經常用來製作橡皮的擦除效果
Paint.setShader(Shader shader);//設定影像效果,使用Shader可以繪製出各種漸變效果
Paint.setShadowLayer(float radius ,float dx,float dy,int color);//在圖形下面設定陰影層,產生陰影效果,radius為陰影的半徑,dx和dy為陰影在x軸和y軸上的距離,color為陰影的顏色
//下面寫文字的時候經常用到的
Paint.setTextSize(float textSize);//設定畫筆文字大小
Paint.measureText(String text);//測試文字的長度
Paint.setTextAlign(Paint.Align align);// CENTER(文字居中) LEFT(文字左對齊) RIGHT(文字右對齊)
複製程式碼
先來看上面Paint的幾個主要方法,結合程式碼和效果圖:
- ####Paint.setStyle(); //設定畫筆的style
Paint.Style.FILL //填充 Paint.Style.FILL_AND_STROKE //填充加描邊 Paint.Style.STROKE //描邊 複製程式碼
測試虛擬碼:
複製程式碼
Paint mPaint= new Paint(); mPaint.setColor(Color.RED);//畫筆顏色為紅色 mPaint.setStrokeWidth(80); //描邊寬度為80(為了區分效果,特意設定特別大)
float radius = 100f; // 填充 mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(400, 500, radius, mPaint); // 描邊 mPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(400, 200, radius, mPaint); // 描邊加填充 mPaint.setStyle(Paint.Style.FILL_AND_STROKE); canvas.drawCircle(400, 900, radius, mPaint);
來看效果圖:

如圖,根據測試,**設定FILL_AND_STROKE模式時在其圓的外圍的描邊寬度並不是StrokeWidth的寬度,而是StrokeWidth/2的寬度.**
* ####Paint.setShader(Shader shader)//設定影像效果
[Shader](https://developer.android.com/reference/android/graphics/Shader.html)是著色器,用來給影像著色,Shader 是基類基類,它有5個已知的子類:
[BitmapShader](https://developer.android.com/reference/android/graphics/BitmapShader.html),
[ComposeShader](https://developer.android.com/reference/android/graphics/ComposeShader.html),
[LinearGradient](https://developer.android.com/reference/android/graphics/LinearGradient.html),
[RadialGradient](https://developer.android.com/reference/android/graphics/RadialGradient.html),
[SweepGradient](https://developer.android.com/reference/android/graphics/SweepGradient.html)
在講這5個子類之前,先了解一個列舉Shader.TileMode,它裡面有三個值:{CLAMP,REPEAT,MIRROR}:
######Shader.TileMode.CLAMP:
如果shader繪製範圍大於原有的範圍時,會用原有影像四邊的顏色填充剩餘空間。
######Shader.TileMode.REPEAT:
在水平和豎直方向重複shader影像。
######Shader.TileMode.MIRROR:
在水平和豎直方向重複shader影像,這一點和REPEAT相似,不同的是MIRROR模式下相鄰的兩個影像互為映象。
接下來結合例子分別來看一下Shader的5個子類和Shader.TileMode的使用姿勢。
先來看下原圖(**用我家兩隻貓咪鎮樓!**)

#####BitmapShader:
BitmapShader本質上就是繪製一個bitmap,並用這個bitmap對需要繪製的圖形進行填充。
複製程式碼
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
BitmapShader初始化時的三個引數:
BitmapShader引數| 備註
:----:|:----:
bitmap| 用來填充圖形的Bitmap
tileX| X軸Bitmap用Shader.TileMode模式填充
tileY| Y軸Bitmap用Shader.TileMode模式填充
示例:
複製程式碼
BitmapShader shader = new BitmapShader(bitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.MIRROR); mPaint.setShader(shader); canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
效果圖:

X軸用TileMode.CLAMP模式,即用bitmap的右邊緣去填充X軸其餘空間,
Y軸用TileMode.MIRROR模式,即在用相鄰兩張影像互為映象的方式填充整個Y軸其餘空間。
X軸和Y軸分別換一下引數模式:
複製程式碼
BitmapShader shader = new BitmapShader(bitmap, BitmapShader.TileMode.MIRROR, BitmapShader.TileMode.REPEAT); mPaint.setShader(shader); canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
效果圖:

X軸用TileMode.MIRROR模式,即用相鄰兩張影像互為映象的方式填充整個X軸其餘空間,
Y軸用TileMode.REPEAT模式,即用相同的影像重複填充整個Y軸其餘空間。
#####LinearGradient:
複製程式碼
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,TileMode tile)
LinearGradient是沿一條直線用來建立線性漸變效果,(x0,y0),(x1,y1)分別是起始座標和終止座標,color0,color1分別是起始顏色和終止顏色,tile為
Shader.TileMode(CLAMP,REPEAT,MIRROR)模式中的一個。
LinearGradient引數| 備註
:----:|:----:
x0| 漸變線起始座標的X座標
y0| 漸變線起始座標的Y座標
x1| 漸變線終止座標的X座標
y1| 漸變線終止座標的Y座標
color0| 漸變線起始顏色
color1| 漸變線終止顏色
tile| 漸變線用Shader.TileMode模式填充
示例:
複製程式碼
LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, Color.GREEN, Color.YELLOW, Shader.TileMode.MIRROR); mPaint.setShader(linearGradient); canvas.drawRect(200, 200, 600, 600, mPaint);
效果圖:

下面修改一下程式碼,擴大一下繪製範圍:
複製程式碼
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
並且分別在LinearGradient建構函式中設定Shader.TileMode為CLAMP,REPEAT,MIRROR:
效果圖:

LinearGradient還有另外一個建構函式:
複製程式碼
LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],TileMode tile)
和第一個建構函式不同的是colors和positions,可以傳多個color及對應的position進行線性漸變。
LinearGradient引數| 備註
:----:|:----:
colors| 用colors陣列線性填充
positions|每個position取值範圍[0,1],並且和colors陣列中對應位置的color一一對應
複製程式碼
int[] colors = {Color.GREEN, Color.GRAY, Color.RED, Color.BLUE}; float[] positions = {0f, 0.5f, 0.75f, 1f}; LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, colors, positions, Shader.TileMode.CLAMP); mPaint.setShader(linearGradient); canvas.drawRect(200, 200, 600, 600, mPaint);
效果圖:

同樣修改程式碼擴大一下範圍並且修改Shader.TileMode模式:
複製程式碼
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
效果圖:

#####RadialGradient:
RadialGradient也用來建立漸變效果,和LinearGradient 不同的是,LinearGradient 是線性漸變,而RadialGradient是徑向漸變,也就是從中心向四周發散漸變,RadialGradient也有兩個建構函式,先看第一個:
複製程式碼
RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode)
(centerX,centerY)是圓心座標,radius是圓半徑,centerColor是圓中心顏色,edgeColor是圓邊緣顏色。
RadialGradient引數| 備註
:----:|:----:
centerX| 圓中心的X軸座標
centerY|圓中心的Y軸座標
radius| 圓半徑
centerColor|圓中心顏色
edgeColor| 圓邊緣顏色
tileMode|徑向漸變Shader.TileMode模式填充
示例:
複製程式碼
RadialGradient gradient = new RadialGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, Color.GREEN, Color.BLACK, Shader.TileMode.CLAMP); mPaint.setShader(gradient); canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, mPaint);
效果圖:

因為RadialGradient範圍和canvas範圍是一樣大小,所以RadialGradient建構函式最後一個引數Shader.TileMode不起作用,同樣的,我們來擴大canvas範圍:
複製程式碼
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, mPaint);
我們將圓的半徑擴大至螢幕寬度的一半,然後看效果圖:

再來看RadialGradient的另一個建構函式:
複製程式碼
RadialGradient(float centerX, float centerY, float radius,int colors[], float stops[],TileMode tileMode)
和上一個不同的是colors和stops,單獨列一下:
RadialGradient引數| 備註
:----:|:----:
colors| color陣列分佈在圓的中心和邊緣之間
stops|取值範圍在[0.0f,1.0f],並且和colors陣列中對應位置的color一一對應,如果為null,顏色均勻的分佈在中心和邊緣之間
#####SweepGradient:
SweepGradient用來建立圍繞一箇中心點360度沿順時針旋轉漸變效果:
複製程式碼
SweepGradient(float cx, float cy, int color0, int color1)
SweepGradient引數| 備註
:----:|:----:
cx| 圓中心的X軸座標
cy|圓中心的Y軸座標
color0| 開始旋轉起始顏色,起始點在3點鐘方向,順時針
color1| 結束旋轉終止顏色,終止點也在3點鐘方向
示例:
複製程式碼
SweepGradient gradient = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, Color.GREEN, Color.RED); mPaint.setShader(gradient); canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, mPaint);
效果圖:

修改一下canvas形狀
複製程式碼
canvas.drawRect(0, (getMeasuredHeight() - getMeasuredWidth()) / 2, getMeasuredWidth(), (getMeasuredHeight() + getMeasuredWidth()) / 2, mPaint);
效果圖:

SweepGradient另一個建構函式:
複製程式碼
SweepGradient(float cx, float cy, int colors[], float positions[])
和前面不同的是colors和positions:
SweepGradient引數| 備註
:----:|:----:
colors| color陣列順時針分佈
positions|每個position取值範圍[0,1],並且和colors陣列中對應位置的color一一對應
示例:
複製程式碼
int[] colors = {Color.GREEN, Color.YELLOW, Color.BLACK, Color.BLUE, Color.RED}; float[] positions = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f}; SweepGradient gradient = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, colors, positions); mPaint.setShader(gradient); canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, mPaint);
效果圖:

#####ComposeShader:
ComposeShader結合Xfermode模式,是兩個Shader的組合模式,ComposeShader有兩個建構函式:
複製程式碼
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
Xfermode可以用於實現新繪製的畫素與Canvas上對應位置已有的畫素按照混合規則進行顏色混合,Xfermode 有三個子類:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,前兩個已廢棄,PorterDuffXfermode初始化時需要傳入PorterDuff.Mode即:PorterDuffXfermode(PorterDuff.Mode mode),所以上面第一個建構函式是第二個建構函式的一種情況,我們只看第一個建構函式就可以了:
ComposeShader引數| 備註
:----:|:----:
shaderA| 目標畫素DST
shaderB|源畫素SRC
mode|新繪製的畫素與Canvas上對應位置已有的畫素按照混合規則進行顏色混合
* ####Paint.setShadowLayer(float radius ,float dx,float dy,int color);
//在圖形下面設定陰影層,產生陰影效果:
setShadowLayer引數| 備註
:----:|:----:
radius | radius為陰影半徑,半徑越大,陰影面積越大,越模糊;反之,半徑越小,陰影面積越小,也越清晰,radius=0時,陰影消失
dx|dx為陰影在x軸上的偏移值
dy|dy為陰影在y軸上的偏移值
color|color為陰影的顏色
示例:
複製程式碼
Paint paint = new Paint(); paint.setColor(Color.RED); paint.setShadowLayer(20, 0, 0, Color.YELLOW); paint.setTextSize(200); canvas.drawText("Hello World", 200, 300, paint);
效果圖:

修改一下:
複製程式碼
paint.setShadowLayer(20,50, 50, Color.YELLOW);
其他程式碼不變,效果圖:

可以看到陰影偏移量起始座標(x,y)從(0,0)變成了(50,50),即陰影位置從(0,0)移動到了(50,50)的位置,再改一下:
複製程式碼
paint.setShadowLayer(1,50, 50, Color.YELLOW);
其他程式碼不變,效果圖:

陰影半徑radius從20變成1,可以看到陰影清晰了很多,再來改一下:
複製程式碼
paint.setShadowLayer(0,50, 50, Color.YELLOW);
其他程式碼不變,效果圖:

陰影半徑radius變成0時,陰影消失,接著看下面程式碼:
複製程式碼
Paint paint = new Paint(); paint.setColor(Color.GREEN); paint.setShadowLayer(30, 0, 0, Color.BLACK); canvas.drawCircle(400, 800, 100, paint);
效果圖:

**納尼?我們預期的黑邊陰影腫麼沒有出現?What a fucking day!別急,只要給paint加一句:**
複製程式碼
setLayerType(LAYER_TYPE_SOFTWARE, paint);
然後來看下效果圖:

終於看到黑色陰影了,為毛要加setLayerType呢,google工程師給出解釋,連結:
http://stackoverflow.com/questions/17410195/setshadowlayer-android-api-differences
Paint先介紹到這裡,接下篇:
[Android自定義View工具:Paint&Canvas(二)](http://www.jianshu.com/p/adbe33e887be)
複製程式碼