Android自定義View之基本API(二)


在上一篇博客中介紹了自定義View的幾個常用類,在這一篇博客中接着介紹另外的一個常用類,Paint類:

Paint翻譯為“畫筆”,為繪圖定義各種參數:顏色、線條樣式、圖案樣式等。通常的繪圖思路是先定義Paint對象,指定繪圖參數,再通過Canvas對象進行圖形繪制,繪圖的結果因Paint的不同而不同。繪圖的方法定義在Canvas類中,Paint類用於指定繪圖的各種參數。

4.Paint類

Paint類用於定義繪圖時的參數,主要包含顏色、文本、圖形樣式、位圖模式、濾鏡等幾個方面。 通過控制這些參數,我們就可以控制Paint的樣式,繪制不同風格的文本、圖片等。

顏色是指繪圖時使用的顏色,在 Android 中顏色可以指定透明度,使用 16 進制來表示顏色時,格式通常為#AARRGGBB,其中,AA 表示透明度、RR 表示紅色、GG 表示綠色、BB 表示藍色,Color類定義了顏色信息,內置了常用顏色的int型常量,比如Color.RED 紅色,Color.BLUE 藍色,同時在Color類中定義的一個靜態方法parseColor(String colorString)將16進制裝換為color類型,比如:

int color = Color.parseColor("#FF552E");//將16進制的FF552E轉換成Android中直接使用的顏色值
① 創建一個Paint對象

// 創建一個畫筆
public Paint()
// 創建一個畫筆,並指定一個flags
public Paint(int flags)
// 從已有的畫筆對象創建一個畫筆
public Paint(Paint paint)

// 創建一個畫筆
Paint paint = new Paint();
paint.setFlags(Paint.ANTI_ALIAS_FLAG);// 設置抗鋸齒
// 下面這一行代碼等價於上面兩行代碼
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

② Paint類與顏色相關的方法

// 設置顏色
public native void setColor(intcolor);
// 設置透明度,a的范圍取 0~255 之間的整數
public nativevoid setAlpha(int a);
// 指定透明度、 紅、 綠、 藍定義一種顏色
public void setARGB(inta,intr,intg,intb)
// 獲取透明度
public native int getAlpha();
// 獲取顏色
public native void getColor(intcolor);
③ Paint類與文本相關的方法

// 設置文本大小,單位是 px,這個和我們平時使用的字體單位sp不同,所以最好進行轉換
public native void setTextSize(float textSize)
// 設置文本的對齊方式,可選值有Paint.Align.LEFT、Paint.Align.CENTER、Paint.Align.RIGHT等
public void setTextAlign(Paint.Align align)
// 設置文本的傾斜程度,skewx 取值於 -1~1 之間,正負表示傾斜的方向
public native void setTextSkewX(float skewx)
// 給文本添加下載線,underline 為 true 表示添加
public native void setUnderlineText(boolean underline)
// 設置文本的粗體樣式,bold 為 true 表示粗體
public native void setFakeBoldText(boolean bold)
// 為文本添加刪除線,strike 為 true 時表示添加
public native void setStrikeThruText(boolean strike)
④ Paint類與圖形樣式相關的方法

// 設置繪制的圖形是空心樣式還是實心樣式,默認為實心樣式
public void setStyle(Paint.Style style)
style的可選值有:
public static enum Style {
FILL
FILL_AND_STROKE,
STROKE
}
其中,FILL 表示實心樣式,對於閉合圖形來說,會用指定的顏色進行填充;STROKE表示空心樣式,繪制時只有線條而無填充效果;FILL_AND_STROKE 表示同時使用實心樣式和空心樣式。

// 當繪圖樣式為STROKE時,該方法用於指定線條連接處的拐角樣式,能使繪制的圖形更加平滑
public void setStrokeJoin(Paint.Join join)
可選值如下:
public static enum Join {
BEVEL,
MITER,
ROUND
}
Join三種類型區別可以用下圖表示:

// 該方法用於設置落筆時的樣式,控制我們的畫筆在離開畫板時留下的最后一點圖形,默認BUTT
public void setStrokeCap(Paint.Cap cap)
可選值如下:
public static enum Cap {
BUTT,
ROUND,
SQUARE
}
Cap三種類型區別可以用下圖表示:

// 設置畫筆粗細,參數為float類型,可以小於1
public native void setStrokeWidth(float width)
// 重置畫筆
public void reset()
下面的代碼是在canvas上面繪制一段文字和兩個不同樣式的矩形:

@Override
protected void onDraw(Canvas canvas) {
paint.setFlags(Paint.ANTI_ALIAS_FLAG);// 設置畫筆去鋸齒
paint.setColor(Color.parseColor("#FF0000")); // 設置畫筆顏色
paint.setTextSize(50);// 設置字體大小
paint.setTextSkewX(0.2f);// 設置字體傾斜
paint.setFakeBoldText(true); // 設置字體加粗
paint.setUnderlineText(true);// 設置給文字添加下划線
canvas.drawText("Android自定義控件", 100, 100, paint);// 繪制文字

paint.reset(); // 重置畫筆
paint.setFlags(Paint.ANTI_ALIAS_FLAG);// 設置畫筆去鋸齒
paint.setColor(Color.parseColor("#0000FF")); // 設置畫筆顏色
paint.setStyle(Paint.Style.STROKE);// 設置內容不填充
paint.setStrokeWidth(30);// 設置邊框寬度
paint.setStrokeJoin(Paint.Join.ROUND); // 設置Join
// paint.setStrokeCap(Paint.Cap.ROUND);// 設置Cap
canvas.drawRect(100, 150, 400, 350, paint);// 繪制一個矩形

paint.reset(); // 重置畫筆
paint.setFlags(Paint.ANTI_ALIAS_FLAG);// 設置畫筆去鋸齒
paint.setColor(Color.parseColor("#00FF00")); // 設置畫筆顏色
paint.setStyle(Paint.Style.FILL);// 設置內容填充
// paint.setStrokeCap(Paint.Cap.BUTT);// 設置Cap,樣式為STROKE時才有效
canvas.drawRect(450,150,850,350,paint);// 繪制一個矩形
}
結果如圖:


⑤ Paint類高級使用之陰影、漸變和位圖

首先說明:在繪圖中,有一個叫layer(層)的概念,默認情況下,我們的文字和圖形繪制在主層(mainlayer)上,其實也可以將內容繪制在新建的layer上。而上陰影就是在main layer的下面添加了一個陰影層(shaderlayer),可以為陰影指定模糊度、偏移量和陰影顏色。

陰影:

Paint類的setShadowLayer()方法,可以設置陰影效果:
public void setShadowLayer(float radius, float dx, float dy, int shadowColor)
radius:陰影半徑
dx:x方向陰影的偏移量
dy:y方向陰影的偏移量
shadowColor:陰影的顏色
陰影layer顯示陰影時, shader layer有兩種類:View.LAYER_TYPE_SOFTWARE、View.LAYER_TYPE_HARDWARE

layer的默認類型為LAYER_TYPE_HARDWARE,但陰影只能在View.LAYER_TYPE_SOFTWARE環境下工作,所以,我們如果需要顯示陰影想過,就要調用View類的public void setLayerType(int layerType, Paint paint)方法為Paint對象指定層的類型:
setLayerType(View.LAYER_TYPE_SOFTWARE, paint);

我在一個View的onDraw()方法中編寫了一下代碼:

@Override
protected void onDraw(Canvas canvas) {
// 初始化畫筆基本屬性
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.parseColor("#f0f5f9"));
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(5);
paint.setTextSize(100);

this.setLayerType(LAYER_TYPE_SOFTWARE, paint);// 設置圖層類型
paint.setShadowLayer(10, 5, 5, Color.RED);
canvas.drawText("Android", 50, 200, paint);
paint.setShadowLayer(5,3,3,Color.GREEN);
canvas.drawText("自定義組件",50,320,paint);

}
運行的到的結果如圖所示:

需要注意的是,一旦定義了陰影層,接下來的所有繪制都會帶陰影效果了,如果想取消陰影,請將setShadowLayer()方法的radius參數設置為0

漸變:

漸變(Gradient)是繪圖過程中顏色或位圖以特定規律進行變化,能增強物體的質感和審美情趣。生活中的漸變非常多,例如公路兩邊的電線桿、樹木、建築物的陽台、鐵軌的枕木延伸到遠方等等,很多的自然理象都充滿了漸變的形式特點。Android同樣對漸變進行了完善支持,通過漸變,可以繪制出更加逼真的效果。
Graphics2D漸變種類有:
線性漸變:LinearGradient
徑向漸變:RadialGradient
掃描漸變:SweepGradient
位圖漸變:BitmapShader
混合漸變:ComposeShader

定義漸變時,必須指定一個漸變區域,根據定義的漸變內容和漸變模式填滿該區域。每一種漸變都被定義成了一個類,他們都繼承自同一個父類——Shader。繪圖時,調用Paint類的setShader(Shader shader)方法指定一種漸變類型,繪制出來的繪圖填充區域都將使用指定的漸變顏色或位圖進行填充。

線性漸變、徑向漸變和掃描漸變屬於顏色漸變,指定2種或2種以上的顏色,根據顏色過渡算法自動計算出中間的過渡顏色,從而形成漸變效果,對於開發人員來說,無需關注中間的漸變顏色。


同時,這三種漸變有三種漸變模式可以選擇(A、B表示兩種顏色):
ABAB型:A、B兩種顏色重復變化,通過TileMode類的REPEAT常量來表示;
ABBA型:A、B兩種顏色鏡像變化,通過TileMode類的MIRROR常量來表示;
AABB型:A、B兩種顏色只出現一次,通過TileMode類的CLAMP常量來表示。


線性漸變:

線性漸變(LinearGradient)根據指定的角度、顏色和模式使用漸變顏色填充繪圖區域。我們必須定義兩個點(x0,y0)和(x1,y1),漸變的方向與這兩個點的連線垂直。

LinearGradient的構造方法如下:

publicLinearGradient(floatx0,floaty0,floatx1,floaty1,intcolor0,intcolor1,TileModetile):本方法用於兩種顏色的漸變,各參數意義如下:
x0、y0:用於決定線性方向的第一個點的坐標(x0,y0);
x1、y1:用於決定線性方向的第二個點的坐標(x1,y1);
color0:第一種顏色;
color1:第二種顏色;
tile:漸變模式

publicLinearGradient(floatx0,floaty0,floatx1,floaty1,intcolors[],floatpositions[],TileMode tile),這是一個功能更加強大的構造方法,我們來看看該構造方法參數的作用:
x0、y0:起始點的坐標
x1、y1:終止點的坐標
colors:多種顏色
positions:顏色的位置(比例)
TileMode:漸變模式
以下代碼都是以Shader.TileMode.CLAMP模式,並且都是2種漸變顏色,只是改變了漸變矩形的大小,能得到不同的效果
private void linerGradientTest(Canvas canvas) {        canvas.save();//保存畫布        Rect rect = new Rect(50, 50, 200, 200); // 創建矩形        // 以Shader.TileMode.CLAMP模式創建線性漸變        LinearGradient lg = new LinearGradient(rect.left, rect.top, rect.right, rect.bottom, Color.RED, Color.BLUE, Shader.TileMode.CLAMP);        paint.setShader(lg);//設置漸變        canvas.drawRect(rect, paint); // 畫矩形        Rect rect1 = new Rect(rect);// 根據rect來創建一個新的矩形        rect1.inset(70, 70);// 縮小漸變矩形        canvas.translate(rect.width() + 20, 0);// 將畫布水平平移20像素        LinearGradient lg1 = new LinearGradient(rect1.left, rect1.top, rect1.right, rect1.bottom, Color.RED, Color.BLUE, Shader.TileMode.CLAMP);        paint.setShader(lg1);        canvas.drawRect(rect, paint);        Rect rect2 = new Rect(rect); // 根據rect來創建一個新的矩形        rect1.inset(-70, -70); // 放大漸變矩形        canvas.translate(rect.width() + 20, 0);        LinearGradient lg2 = new LinearGradient(rect2.left, rect2.top, rect2.right, rect2.bottom, Color.RED, Color.BLUE, Shader.TileMode.CLAMP);        paint.setShader(lg2);        canvas.drawRect(rect, paint);        canvas.restore();// 還原畫布    }

下面的代碼分別使用了兩種漸變顏色和三種漸變顏色:

private void linerGradient(Canvas canvas) {
canvas.save();//保存畫布
Rect rect = new Rect(30, 30, 450, 450);
// 使用2種漸變顏色
LinearGradient lg = new LinearGradient(rect.left, rect.top + rect.height() / 2, rect.right, rect.top + rect.height() / 2, Color.RED, Color.BLUE, Shader.TileMode.CLAMP);
paint.setShader(lg);
canvas.drawRect(rect, paint);

canvas.translate(rect.width() + 20, 0);
// 使用3種漸變顏色
LinearGradient lg1 = new LinearGradient(rect.left, rect.top + rect.height() / 2, rect.right, rect.top + rect.height() / 2, new int[]{Color.RED, Color.BLUE,Color.YELLOW},new float[]{0.3f,0.6f,0.8f}, Shader.TileMode.CLAMP);
paint.setShader(lg1);
canvas.drawRect(rect, paint);

canvas.restore();// 還原畫布
}

徑向漸變

徑向漸變是以指定的點為中心,向四周以漸變顏色進行圓周擴散,和線性漸變一樣,支持兩種或多種顏色。
徑向漸變的主要構造方法如下:

public RadialGradient(float x, float y, float radius, int color0, int color1, TileMode tile),該構造方法支持兩種顏色,下面是參數的作用:
x、y:中心點坐標
radius:漸變半徑
color0:起始顏色
color1:結束顏色
TileMode:漸變模式
public RadialGradient(float x, float y, float radius, int colors[], float positions[], TileMode tile),該構造方法支持3種或3種以上顏色的漸變,各參數的作用如下:
x、y:中心點坐標
radius:漸變半徑
colors:多種顏色
positions:顏色的位置(比例)
TileMode:漸變模式

以下代碼定義了徑向漸變:

private void radiaGradientTest(Canvas canvas) {
canvas.save();
// 2種顏色圓形漸變
RadialGradient rg = new RadialGradient(200,200,180,Color.RED,Color.GREEN, Shader.TileMode.CLAMP);
paint.setShader(rg);
canvas.drawCircle(200, 200, 180, paint);

canvas.translate(420,0);
// 2種顏色矩形漸變
RadialGradient rg1 = new RadialGradient(200,200,180,Color.RED,Color.GREEN, Shader.TileMode.CLAMP);
paint.setShader(rg1);
canvas.drawRect(20, 0, 380, 360, paint);

canvas.translate(-420, 450);
// 3種顏色圓形漸變
RadialGradient rg2 = new RadialGradient(200,200,220,new int[]{Color.RED, Color.GREEN,Color.YELLOW},new float[]{0.3f,0.6f,0.8f}, Shader.TileMode.CLAMP);
paint.setShader(rg2);
canvas.drawCircle(200, 200, 180, paint);

canvas.translate(420,0);
// 3種顏色矩形漸變
RadialGradient rg3 = new RadialGradient(200,200,220,new int[]{Color.RED, Color.GREEN,Color.YELLOW},new float[]{0.3f,0.6f,0.8f}, Shader.TileMode.CLAMP);
paint.setShader(rg3);
canvas.drawRect(20,0,380,360,paint);
canvas.restore();
}
運行結果如圖:

掃描漸變

SweepGradient類似於軍事題材電影中的雷達掃描效果,固定圓心,將半徑假想為有形並旋轉一周而繪制的漸變顏色。
SweepGradient定義了兩個主要的構造方法:

publicSweepGradient(floatcx,floatcy,intcolor0,intcolor1)
支持兩種顏色的掃描漸變,參數的作用如下:
cx、cy:圓點坐標;
color0:起始顏色;
color1:結束顏色。
publicSweepGradient(floatcx,floatcy,intcolors[],floatpositions[])
支持多種顏色的掃描漸變,參數的作用如下:
cx、cy:圓點坐標;
colors:多種顏色;
positions:顏色的位置(比例)
以下代碼定義了掃描漸變:

private void sweepGradientTest(Canvas canvas) {
canvas.save();
SweepGradient sg = new SweepGradient(300,300,new int[]{Color.GREEN,Color.YELLOW,Color.BLUE,Color.GREEN}, null);
paint.setShader(sg);
canvas.drawCircle(300, 300, 280, paint);

SweepGradient sg1 = new SweepGradient(300,300,new int[]{Color.GREEN,Color.YELLOW,Color.BLUE,Color.GREEN}, new float[]{0.2f,0.5f,0.8f,0.9f});
paint.setShader(sg1);
canvas.drawCircle(300, 300, 280, paint);
canvas.restore();
}
運行結果如圖:


位圖漸變

位圖漸變其實就是在繪制的圖形中將指定的位圖作為背景,如果圖形比位圖小,則通過漸變模式進行平鋪,TileMode.CLAMP模式不平鋪,TileMode.REPEAT模式表示平鋪,TileMode.MIRROR模式也表示平鋪,但是交錯的位圖是彼此的鏡像,方向相反。可以同時指定水平和垂直兩個方向
的漸變模式。
BitmapShader只有一個構造方法:
public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY),參數如下:
bitmap:位圖;
tileX:x方向的重復模式;
tileY:y方向的重復模式。

以下代碼定義了一個水平和豎直方向都平鋪的位圖漸變:

private void bitmaGradientTest(Canvas canvas) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
BitmapShader bs = new BitmapShader(bmp,Shader.TileMode.REPEAT, Shader.TileMode.MIRROR);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShader(bs);
canvas.drawRect(new Rect(0, 0,1000, 1000), paint);
}

運行結果如圖:

混合漸變

混合漸變ComposeShader是將兩種不同的漸變通過位圖運算后得到的一種更加復雜的漸變。
ComposeShader有兩個構造方法:

publicComposeShader(ShadershaderA,ShadershaderB,Xfermodemode)
publicComposeShader(ShadershaderA,ShadershaderB,Modemode)
shaderA和shaderB是兩個漸變對象,mode為位圖運算類型,16種運算模式如圖(圖片來源網絡):

以下代碼定義了位圖漸變和掃描漸變相結合:

private void composeGradientTest(Canvas canvas) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
BitmapShader bs = new BitmapShader(bmp,Shader.TileMode.REPEAT, Shader.TileMode.MIRROR);//定義位圖漸變
// 定義掃描漸變
SweepGradient sg = new SweepGradient(500, 525, new int[]{Color.GREEN, Color.YELLOW, Color.BLUE, Color.GREEN}, null);
ComposeShader sc = new ComposeShader(bs,sg, PorterDuff.Mode.SRC_IN);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShader(sc);
canvas.drawRect(new Rect(0, 0,1000, 1000), paint);
}
運行結果如下圖:



位圖運算PorterDuffXfermode

位圖運算為位圖的功能繁衍ᨀ供了強大的技術基礎,增強了位圖的可塑性和延伸性,使很多看起來非常復雜的效果和功能都能輕易實現,比如圓形頭像、不規則圖片、橡皮擦、稀奇古怪的自定義進度條等等。

位圖運算模式定義在PorterDuff類的內部枚舉類型Mode中,對應了16個不同的枚舉值

publicenumMode{
/**[0,0]*/
CLEAR(0),
/**[Sa,Sc]*/
SRC(1),
/**[Da,Dc]*/
DST(2),
/**[Sa+(1-Sa)*Da,Rc=Sc+(1-Sa)*Dc]*/
SRC_OVER(3),
/**[Sa+(1-Sa)*Da,Rc=Dc+(1-Da)*Sc]*/
DST_OVER(4),
/**[Sa*Da,Sc*Da]*/
SRC_IN(5),
/**[Sa*Da,Sa*Dc]*/
DST_IN(6),
/**[Sa*(1-Da),Sc*(1-Da)]*/
SRC_OUT(7),
/**[Da*(1-Sa),Dc*(1-Sa)]*/
DST_OUT(8),
/**[Da,Sc*Da+(1-Sa)*Dc]*/
SRC_ATOP(9),
/**[Sa,Sa*Dc+Sc*(1-Da)]*/
DST_ATOP(10),
/**[Sa+Da-2*Sa*Da,Sc*(1-Da)+(1-Sa)*Dc]*/
XOR(11),
/**[Sa+Da-Sa*Da,
Sc*(1-Da)+Dc*(1-Sa)+min(Sc,Dc)]*/
DARKEN(16),
/**[Sa+Da-Sa*Da,
Sc*(1-Da)+Dc*(1-Sa)+max(Sc,Dc)]*/
LIGHTEN(17),
/**[Sa*Da,Sc*Dc]*/
MULTIPLY(13),
/**[Sa+Da-Sa*Da,Sc+Dc-Sc*Dc]*/
SCREEN(14),
/**Saturate(S+D)*/
ADD(12),
OVERLAY(15);

Mode(intnativeInt){
this.nativeInt=nativeInt;
}

/**
*@hide
*/
publicfinalintnativeInt;
}
具體使用哪一種模式才能達到效果,可以參考下圖(圖片來源網絡):


以下代碼通過位圖運算簡單實現了圓形圖片的制作:

public class CircleImage extends View {
private Paint paint;
private Bitmap bitmap;
private int width,height,centerX,centerY,radius;

public CircleImage(Context context) {
this(context, null);
}

public CircleImage(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public CircleImage(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

private void init() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);

bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.a); // 從資源文件中獲取圖片並變為Bitmap對象
// 獲取圖片的寬和高,並計算圓心位置
width = bitmap.getWidth();
height = bitmap.getHeight();
centerX = width / 2;
centerY = height / 2;
radius = Math.min(width,height) / 2;
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**
* Canvas類中的saveLayer()方法,表示出創建一個圖層入棧並返回該圖層所在棧中的位置,
* 而restoreToCount()方法表示將指定棧中位置的圖層繪制到canvas上面顯示。
*/
int saveLayer = canvas.saveLayer(0, 0, width, height, paint, Canvas.ALL_SAVE_FLAG);
// 畫一個圓,也就是需要顯示出來的圓
canvas.drawCircle(centerX,centerY,radius,paint);
//設置位圖模式為PorterDuff.Mode.SRC_IN
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, 0, 0, paint);
// 清空位圖模式
paint.setXfermode(null);
// 給圓形圖片畫一個3像素的邊框
paint.setStrokeWidth(3);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.parseColor("#65f692"));
canvas.drawCircle(centerX,centerY,radius,paint);

canvas.restoreToCount(saveLayer);
}
}
運行結果如圖所示:





注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com