Android 畫筆Paint
了解Android Paint,一篇就夠。引用Aige《 自定義控件其實很簡單 》系列博客的話“很多時候你壓根不需要了解太多原理,只需站在巨人的丁丁上即可”,所謂前人種樹后人好乘涼,這里記錄下我的實踐結果。
我們可以通過Paint中setter方法來為畫筆設置屬性:
浩浩蕩蕩來將這些方法一一過一遍:
set
void set(Paint src)
為當前畫筆copy一個畫筆
setARGB
void setARGB(int a, int r, int g, int b)
設置Paint對象顏色,a代表透明度,r,g,b代表顏色值
插播:RGB與十六進制區別
一般在xml里定義顏色可以直接寫:
android:textColor="#FF6281"
但是在code代碼中就必須寫成這樣:
text.setTextColor(0xffff6281);
xml中透明度寫不寫無所謂,默認是ff不透明,但是代碼中用十六進制0x來表示,就必須跟上ff透明度,不然會默認00全透明。
setAlpha
void setAlpha(int a)
設置alpha透明度,范圍為0~255
setAntiAlias
void setAntiAlias(boolean aa)
是否抗鋸齒
setColor
void setColor(int color)
設置paint顏色
setColorFilter
ColorFilter setColorFilter (ColorFilter filter)
設置顏色過濾, ColorFilter 有三個子類去實現ColorMatrixColorFilter、LightingColorFilter和PorterDuffColorFilter
ColorMatrixColorFilter
public class PaintCanvas extends View {
private Paint mPaint;
//省略構造方法
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
// 生成色彩矩陣
ColorMatrix colorMatrix = new ColorMatrix(new float[]{
0.5F, 0, 0, 0, 0,
0, 0.5F, 0, 0, 0,
0, 0, 0.5F, 0, 0,
0, 0, 0, 1, 0,
});
mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
}
@Override
protected void onDraw(Canvas canvas) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.logo);
canvas.drawBitmap(bitmap, 0, 0, mPaint);
}
}
第一行表示的R(紅色)的向量,第二行表示的G(綠色)的向量,第三行表示的B(藍色)的向量,最后一行表示A(透明度)的向量,這一順序必須要正確不能混淆!這個矩陣不同的位置表示的RGBA值,其范圍在0.0F至2.0F之間,1為保持原圖的RGB值。每一行的第五列數字表示偏移值。
這是原圖效果,增加ColorMatrix,效果如下:
LightingColorFilter
只有一個構造方法, LightingColorFilter (int mul, int add) ,參數1:mul全稱是colorMultiply意為色彩倍增;參數2:add全稱是colorAdd意為色彩添加,這兩個值都是16進制的色彩值0xAARRGGBB。
// 設置顏色過濾,去掉綠色
mPaint.setColorFilter(new LightingColorFilter(0xFFFF00FF, 0x00000000));
效果如下:
PorterDuffColorFilter
也只有一個構造方法, PorterDuffColorFilter (int color, PorterDuff.Mode mode) ,參數1:16進制表示的顏色值;參數2:PorterDuff內部類Mode中的一個常量值,這個值表示混合模式。
// 設置顏色過濾,Color的值設為紅色,模式PorterDuff.Mode.DARKEN變暗
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DARKEN));
效果如下:
混合模式還有很多,不僅是應用于圖像色彩混合,還應用于圖形混合。
setDither
void setDither(boolean dither)
設定是否使用圖像抖動處理,會使繪制出來的圖片顏色更加平滑和飽滿,圖像更加清晰
setFakeBoldText
void setFakeBoldText(boolean fakeBoldText)
設置偽粗體文本
setFilterBitmap
void setFilterBitmap(boolean filter)
設置位圖進行濾波處理
setHinting
void setHinting (int mode)
Added in API level 14,設置暗示模式,HINTING_OFF 或 HINTING_ON
setLetterSpacing
void setLetterSpacing (float letterSpacing)
Added in API level 21,設置文本字母間距,默認0,負值收緊文本
setLinearText
void setLinearText(boolean linearText)
設置線性文本
setMaskFilter
MaskFilter setMaskFilter (MaskFilter maskfilter)
設置濾鏡的效果, MaskFilter 有兩個子類實現BlurMaskFilter, EmbossMaskFilter
BlurMaskFilter
設置畫筆模糊陰影效果
mPaint.setMaskFilter(new BlurMaskFilter(20f, BlurMaskFilter.Blur.SOLID));
參數1:模糊延伸半徑,必須>0;
參數2:有四種枚舉
NORMAL,同時繪制圖形本身內容+內陰影+外陰影,正常陰影效果
INNER,繪制圖形內容本身+內陰影,不繪制外陰影
OUTER,不繪制圖形內容以及內陰影,只繪制外陰影
SOLID,只繪制外陰影和圖形內容本身,不繪制內陰影
BlurMaskFilter繪制的Bitmap基本完全不受影響
四種枚舉效果如下:
EmbossMaskFilter
//Paint的setMaskFilter不被GPU支持,為了確保畫筆的setMaskFilter能供起效,我們需要禁用掉GPU硬件加速或AndroidManifest.xml文件中設置android:hardwareAccelerated為false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
//View從API Level 11才加入setLayerType方法
//設置軟件渲染模式繪圖
this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
//設置浮雕濾鏡效果,參數1:光源指定方向;參數2:環境光亮度,取值0-1,值越小越暗;參數3:鏡面高光反射系數,值越小反射越強;參數4:模糊延伸半徑
mPaint.setMaskFilter(new EmbossMaskFilter(new float[]{1, 1, 1}, 0.4f, 8f, 3f));
效果如下:
setPathEffect
PathEffect setPathEffect(PathEffect effect)
設置路徑效果, PathEffect 有6個子類實現ComposePathEffect, CornerPathEffect, DashPathEffect, DiscretePathEffect, PathDashPathEffect, SumPathEffect
具體代碼:
public class PaintCanvas extends View {
private Paint mPaint;
// 路徑對象
private Path mPath;
private PathEffect[] pathEffects = new PathEffect[7];
private float mPhase=5;
//省略構造方法
private void init() {
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setAntiAlias(true);
initPath();
}
private void initPath() {
// 實例化路徑
mPath = new Path();
// 定義路徑的起點
mPath.moveTo(10, 50);
// 定義路徑的各個點
for (int i = 0; i <= 30; i++) {
mPath.lineTo(i * 35, (float) (Math.random() * 100));
}
//什么都不處理
pathEffects[0] = null;
//參數1:線段之間的圓滑程度
pathEffects[1] = new CornerPathEffect(10);
//參數1:間隔線條長度(length>=2),如float[] {20, 10}的偶數參數20定義了我們第一條實線的長度,
//而奇數參數10則表示第一條虛線的長度,后面不再有數據則重復第一個數以此往復循環;參數2:虛實線間距
pathEffects[2] = new DashPathEffect(new float[]{20, 10}, mPhase);
//參數1:值越小雜點越密集;參數2:雜點突出的大小,值越大突出的距離越大
pathEffects[3] = new DiscretePathEffect(5.0f, 10.0f);
Path path = new Path();
path.addRect(0, 0, 8, 8, Path.Direction.CCW);
//定義路徑虛線的樣式,參數1:path;參數2:實線的長度;參數3:虛實線間距
pathEffects[4] = new PathDashPathEffect(path, 20, mPhase, PathDashPathEffect.Style.ROTATE);
pathEffects[5] = new ComposePathEffect(pathEffects[2], pathEffects[4]);
//ComposePathEffect和SumPathEffect都可以用來組合兩種路徑效果,具體區別(不知道如何描述)小伙伴們自己試試
pathEffects[6] = new SumPathEffect(pathEffects[4], pathEffects[3]);
}
@Override
protected void onDraw(Canvas canvas) {
/*
* 繪制路徑
*/
for (int i = 0; i < pathEffects.length; i++) {
mPaint.setPathEffect(pathEffects[i]);
canvas.drawPath(mPath, mPaint);
// 每繪制一條將畫布向下平移250個像素
canvas.translate(0, 250);
}
}
}
效果如下:
setRasterizer
Rasterizer setRasterizer(Rasterizer rasterizer)
設置光柵化,API21已過時
setShader
Shader setShader(Shader shader)
設置著色器, Shader 子類實現有BitmapShader, ComposeShader, LinearGradient, RadialGradient, SweepGradient
BitmapShader
對圖形進行渲染,構造方法:
BitmapShader (Bitmap bitmap,Shader.TileMode tileX,Shader.TileMode tileY)
tileX、tileY參數Shader.TileMode有三個:
CLAMP 重復最后一個顏色至最后
MIRROR 重復著色的圖像水平或垂直方向已鏡像方式填充會有翻轉效果
REPEAT 重復著色的圖像水平或垂直方向
設置tileX、tileY為Shader.TileMode.CLAMP
public class PaintCanvas extends View {
private Paint mPaint;
private Context mContext;
private Bitmap mBitmap;
private BitmapShader mShader;
// 省略構造方法
private void init() {
mBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.logo);
mShader= new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
mPaint.setShader(mBitmapShader);
canvas.drawCircle(500, 550, 500, mPaint);
}
}
效果如下:
設置tileX、tileY為Shader.TileMode.MIRROR
效果如下:
設置tileX、tileY為Shader.TileMode.REPEAT
效果如下:
LinearGradient
設置線性漸變效果,有兩個構造函數
//坐標(x0,y0)漸變直線的起點,坐標(x1,y1)漸變直線的終點,color0和color1分別表示了漸變的起始顏色和終止顏色,TileMode也有CLAMP 、REPEAT 和 MIRROR三個取值
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)
LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions,Shader.TileMode tile)
例子:
public class PaintCanvas extends View {
private Paint mPaint;
private Context mContext;
private Bitmap mBitmap;
private Shader mShader;
// 省略構造方法
private void init() {
mShader = new LinearGradient(0, 0, 500, 500, Color.BLUE, Color.GREEN,Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
mPaint.setShader(mBitmapShader);
canvas.drawCircle(500, 550, 400, mPaint);
}
}
效果如下:
設置REPEAT 和 MIRROR就不貼圖片了,小伙伴們可以自己試試看看效果。
RadialGrdient
設置光束從中心向四周發散的輻射漸變效果,構造方法:
//坐標(centerX,centerY)中心點坐標,radius圓的半徑,centerColor中心顏色,edgeColor圓的輪廓顏色,顏色逐漸從centerColor漸變到edgeColor,TileMode也有CLAMP 、REPEAT 和 MIRROR三個取值
RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, Shader.TileMode tileMode)
RadialGradient(float centerX, float centerY, float radius, int[] colors, float[] stops, Shader.TileMode tileMode)
例子:
public class PaintCanvas extends View {
private Paint mPaint;
private Context mContext;
private Shader mShader;
// 省略構造方法
private void init() {
mShader = new RadialGradient(500, 500, 400, Color.BLUE, Color.GREEN, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
mPaint.setShader(mBitmapShader);
canvas.drawCircle(500, 550, 400, mPaint);
}
}
效果如下:
設置REPEAT 和 MIRROR也不貼圖片了。
SweepGradient
設置繞著某中心點進行360度旋轉漸變效果,構造方法:
//坐標(cx,cy)決定了中心點的位置,會繞著該中心點進行360度旋轉。color0表示的是起點的顏色,color1表示的是終點的顏色
SweepGradient(float cx, float cy, int color0, int color1)
//坐標(cx,cy)決定了中心點的位置,colors顏色數組,position取值范圍為[0,1],0和1都表示3點鐘位置,0.25表示6點鐘位置,0.5表示9點鐘位置,0.75表示12點鐘位置,諸如此類
SweepGradient(float cx, float cy, int[] colors, float[] positions)
例子:
public class PaintCanvas extends View {
private Paint mPaint;
private Context mContext;
private Shader mShader;
// 省略構造方法
private void init() {
mShader = new SweepGradient(500, 500, Color.BLUE, Color.GREEN);
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
mPaint.setShader(mBitmapShader);
canvas.drawCircle(500, 550, 400, mPaint);
}
}
效果如下:
ComposeShader
混合,有兩個構造函數
//shaderA對應下層圖形,shaderB對應上層圖形
ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
例子:
public class PaintCanvas extends View {
private Paint mPaint;
private Context mContext;
private Bitmap mBitmap;
private Shader bitmapShader, linearGradient, composeShader;
// 省略構造方法
private void init() {
mBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.logo);
bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
linearGradient = new LinearGradient(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), Color.BLUE, Color.GREEN, Shader.TileMode.CLAMP);
//bitmapShader對應下層圖形,linearGradient對應上層圖形,像素顏色混合采用MULTIPLY模式
composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY);
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
mPaint.setShader(mBitmapShader);
canvas.drawCircle(500, 550, 400, mPaint);
}
}
效果如下:
setShadowLayer
void setShadowLayer(float radius, float dx, float dy, int shadowColor)
圖形添加一個陰影層效果
setStrikeThruText
void setStrikeThruText (boolean strikeThruText)
設置刪除線
setStrokeCap
void setStrokeCap (Paint.Cap cap)
當設置setStyle是Stroke或StrokeAndFill,設置筆刷的圖形樣式,如圓形樣式Cap.ROUND或方形樣式Cap.SQUARE
setStrokeJoin
void setStrokeJoin (Paint.Join join)
當設置setStyle是Stroke或StrokeAndFill,設置繪制時各圖形的結合方式,如影響矩形角的外輪廓
setStrokeMiter
void setStrokeMiter (float miter)
當設置setStyle是Stroke或StrokeAndFill,設置斜切
setStrokeWidth
void setStrokeWidth (float width)
當畫筆樣式為STROKE或FILL_OR_STROKE時,設置筆刷的粗細度
setStyle
void setStyle (Paint.Style style)
設置畫筆樣式,畫筆樣式分三種:
Paint.Style.STROKE:描邊
Paint.Style.FILL_AND_STROKE:描邊并填充
Paint.Style.FILL:填充
setSubpixelText
void setSubpixelText (boolean subpixelText)
有助于文本在LCD屏幕上的顯示效果
setTextAlign
void setTextAlign(Paint.Align align)
設置文本對齊
setTextScaleX
void setTextScaleX(float scaleX)
設置文本縮放倍數,1.0f為原始
setTextSize
void setTextSize(float textSize)
設置字體大小
setTextSkewX
void setTextSkewX (float skewX)
設置斜體文字,skewX為傾斜弧度,默認值0,大于0,向左斜,小于0,向右斜
setTypeface
Typeface setTypeface(Typeface typeface)
設置字體,Typeface包含了字體的類型,粗細,還有傾斜、顏色等。
mPaint.setTypeface(Typeface.SANS_SERIF);
setUnderlineText
void setUnderlineText(boolean underlineText)
設置下劃線
setXfermode
Xfermode setXfermode (Xfermode xfermode)
設置圖像混合模式, Xfermode 有個子類去實現PorterDuffXfermode
PorterDuffXfermode
構造方法 PorterDuffXfermode(PorterDuff.Mode mode) ,參數就是上面的提到的,圖形混合模式如圖:
Dst:先畫(下層)的圖形;Src:后畫(上層)的圖形,然而被網上這張圖片誤導了,解釋見 孫群博客 ,他也給了最終運行效果:
我一一運行確實是如此,這里貼出Mode 為Screen代碼:
public class PaintCanvas extends View {
private Paint mPaint;
private PorterDuffXfermode porterDuffXfermode;// 圖形混合模式
private Context mContext;
private Bitmap mBitmap;
//省略構造方法
private void init() {
mBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.logo);
mPaint = new Paint();
mPaint.setAntiAlias(true);
// 實例化混合模式
porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SCREEN);
}
@Override
protected void onDraw(Canvas canvas) {
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
//新建一個layer,放置在canvas默認layer的上部,產生的layer初始時是完全透明的
int layerId = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG);
//dst是先畫的圖形
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
//設置混合模式
mPaint.setXfermode(porterDuffXfermode);
//src是后畫的圖形
mPaint.setColor(0xFFFFCC44);
canvas.drawCircle(600, 600, 200, mPaint);
//還原混合模式
mPaint.setXfermode(null);
//或canvas.restore()把這個layer繪制到canvas默認的layer上去
canvas.restoreToCount(layerId);
}
}
最后
這篇文章真長,從開始寫到最后的校對,花了很長時間,每段代碼運行都截圖上傳。只能說實踐是檢驗真理的唯一標準,不一定知道每個原理,都必須知道每個是什么樣子的效果,記錄完成方便自己日后查找,也方便大家哦,如果您能讀到這篇文章的話。
來自:http://www.androidchina.net/5406.html