本篇博客是我對View的繪圖方面的理解和歸納,也不會說的非常的細(xì)煞抬,只是類似于一個思維導(dǎo)圖的作用霜大,因為有人寫得更好了,我就沒必要寫了革答,況且寫了還不一定如別人的战坤。
本文大綱如下:
- View形狀繪制
- View顏色繪制
- View幾何變換和裁剪
- View的繪制順序
一、繪制形狀
1)蝗碎、Paint類的基本用法
- paint.setAntiAlias(true); //抗鋸齒功能,一般都會加入的
- paint.setColor( ); // 設(shè)置畫筆顏色
- paint.setARGB(int a, int r, int g, int b) // 設(shè)置三原色和透明通路
- paint.setAlpha(int a) // 設(shè)置透明通道
- paint.setStrokeWidth(int a);//設(shè)置畫筆寬度
- paint.setStyle(Style.FILL);//設(shè)置填充樣式
- paint.setTextAlign(Align.CENTER); //設(shè)置文字對齊方式旗扑,取值:Align.CENTER蹦骑、Align.LEFT或Align.RIGHT
- paint.setTextSize(12); //設(shè)置文字大小
- paint.setUnderlineText(true); //設(shè)置下劃線
- paint.setStrikeThruText(true); //設(shè)置帶有刪除線效果
- paint.setTextScaleX(2); //只會將水平方向拉伸,高度不會變
Paint.Style 屬性 | 描述 |
---|---|
Paint.Style.FILL | 填充內(nèi)部 |
Paint.Style.FILL_AND_STROKE | 填充內(nèi)部和描邊 |
Paint.Style.STROKE | 僅描邊 |
Paint.Cap 屬性 | 描述 |
---|---|
Paint.Cap.BUTT | 線頭形狀平頭 |
Paint.Cap.ROUND | 線頭形狀圓頭 |
Paint.Cap.SQUARE | 線頭形狀方頭 |
Paint.Align屬性 | 描述 |
---|---|
Paint.Align.LEFT | 內(nèi)容居左顯示 |
Paint.Align.CENTER | 內(nèi)容居中顯示 |
Paint.Align.RIGHT | 內(nèi)容居右顯示 |
- paint.setShadowLayer (float radius, float dx, float dy, int color) // 添加陰影臀防,傾斜度眠菇,x軸偏離值,y軸偏離值袱衷,陰影顏色捎废。如TextView等也可以在xml布局中屬性有android:shadowDx等等,原理一致
- paint.drawPath(@NonNull Path path, @NonNull Paint paint) //畫條路徑
2)致燥、Canvas 繪制
①登疗、通用函數(shù)
- canvas.drawColor(Color.BLUE); // 設(shè)置畫布顏色,即背景
- canvas.drawRGB(255, 255, 0); // 設(shè)置畫布顏色嫌蚤,即背景
- canvas.drawLine (float startX, float startY, float stopX, float stopY, Paint paint) // 畫一條直線
- void drawLines (float[] pts, Paint paint) // 多條直線
- void drawPoint (float x, float y, Paint paint) // 畫條點
- void drawPoints (float[] pts, Paint paint)
- void drawRect (float left, float top, float right, float bottom, Paint paint) // 畫矩形
- void drawRect (RectF rect, Paint paint)
- void drawRect (Rect r, Paint paint)
- void drawRoundRect (RectF rect, float rx, float ry, Paint paint) // 圓角矩形
- void drawCircle (float cx, float cy, float radius, Paint paint) // 圓形
- void drawOval (RectF oval, Paint paint) // 橢圓
- void drawArc (RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) // 畫條弧線
注意:useCenter和Paint.Style搭配使用可以達(dá)到不同的效果辐益。如扇形和弧線等。
②脱吱、文字相關(guān)
- void drawText (String text, float x, float y, Paint paint)
- void drawText (CharSequence text, int start, int end, float x, float y, Paint paint) // 畫一個Text出來智政,start和end代表畫那些字符,x代表x軸位置箱蝠,y代表基線续捂。
基線算法:
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
int dy = (fm.top + fm.bottom)/2;
int baseLine = getHeight()/2 - dy;`
- void drawText (String text, int start, int end, float x, float y, Paint paint)
- void drawText (char[] text, int index, int count, float x, float y, Paint paint)
- void drawPosText (String text, float[] pos, Paint paint) // 指定每個文字的位置
- void drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint) // 沿著一個路徑繪制
3)、Path基本用法
Path用法 | 描述 |
---|---|
path.moveTo(10, 10); | 設(shè)定起始點 |
path.lineTo(10, 100) | 連接到下一個點 |
path.rMoveTo(10, 10); | 以在上一個結(jié)束點為基準(zhǔn)點宦搬,x軸偏移10像素牙瓢,y軸偏移10像素 |
path.rLineTo(10, 100) | 連接到下一個點,下個點是以在上一個結(jié)束點為基準(zhǔn)點间校,x軸偏移10像素一罩,y軸偏移10像素 |
path.close(); | 形成一個閉環(huán) |
path.addRect(RectF rect, Direction dir) | 增加一個矩形路徑,Direction枚舉代表生成的方向 |
void addCircle (float x, float y, float radius, Path.Direction dir) | 圓形路徑 |
void addOval (RectF oval, Path.Direction dir) | 橢圓路徑 |
void addArc (RectF oval, float startAngle, float sweepAngle) | 弧形路徑 |
void quadTo | 二階貝塞爾曲線 |
void cubicTo | 三階貝塞爾曲線 |
Path.Direction屬性 | 描述 |
---|---|
CW | 順時針開始繪制 |
CCW | 逆時針開始繪制 |
只有在兩個圖形相交的時候撇簿,才會用到FillType屬性聂渊,用于判斷相交的部分是否需要顯示
Path.FillType屬性 | 描述 |
---|---|
WINDING | 非零環(huán)繞數(shù)原則差购,默認(rèn)為WINDING |
EVEN_ODD | 交叉填充,兩圖形奇偶原則汉嗽。一點重合了偶數(shù)次就不畫 |
INVERSE_WINDING | 與WINDING完全相反 |
INVERSE_EVEN_ODD | 與EVEN_ODD完全相反 |
對于上圖欲逃,WINDING規(guī)則下,畫的方向會直接影響多個圖形的重合區(qū)域饼暑,對于一點順時針則+1稳析,逆時針-1,如果相加等于0則不畫弓叛。
INVERSE_WINDING和WINDING畫的是完全相反的彰居。INVERSE_EVEN_ODD和EVEN_ODD是完全相反的。
二撰筷、繪制顏色
1)陈惰、基本顏色
- canvas.drawColor(Color.BLUE); // 設(shè)置畫布顏色,即背景
- canvas.drawRGB(255, 255, 0); // 設(shè)置畫布顏色毕籽,即背景
- paint.setColor( ); // 設(shè)置畫筆顏色
- paint.setARGB(int a, int r, int g, int b) // 設(shè)置三原色和透明通路
- setShader(Shader shader) 設(shè)置 Shader
Shader稱之著色器和渲染器抬闯,可以實現(xiàn)一些漸變和渲染效果。它的子類包括:
- BitmapShader : 位圖Shader
- LinearShader : 線性Shader
- RadialShader : 光束Shader
- SweepShader : 梯度Shader
- ComposeShader : 混合Shader
BitmapShader
構(gòu)造函數(shù):
public BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY) 关筒;
其中TileMode 是圖像的填充模式
- CLAMP : 拉伸模式溶握,圖片最后一個像素的顏色鋪滿剩余的X或者Y軸
- REPETA : 重復(fù)模式
- MIRROR : 鏡像模式
看一個關(guān)于圖像中間去一個圓形圖像的例子
public class TelescopeView extends View {
private Paint mPaint;
private Bitmap mBitmap,mBitmapBG;
private int mDx = -1, mDy = -1;
public TelescopeView(Context context) {
super(context);
init();
}
public TelescopeView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TelescopeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mPaint = new Paint();
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.game);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDx = (int) event.getX();
mDy = (int) event.getY();
postInvalidate();
return true;
case MotionEvent.ACTION_MOVE:
mDx = (int) event.getX();
mDy = (int) event.getY();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mDx = -1;
mDy = -1;
break;
}
postInvalidate();
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mBitmapBG == null){
mBitmapBG = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvasbg = new Canvas(mBitmapBG);
canvasbg.drawBitmap(mBitmap,null,new Rect(0,0,getWidth(),getHeight()),mPaint);
mPaint.setShader(new BitmapShader(mBitmapBG, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
}
if (mDx != -1 && mDy != -1) {
canvas.drawCircle(mDx, mDy, 150, mPaint);
}
}
}
2)、使用ColorFilter
ColorFilter 是一個顏色過濾的基類蒸播,它的子類分別是
- ColorMatrixColorFilter : 色彩矩陣過濾
- LightingColorFilter : ColorMatrixColorFilter的簡化版
- PorterDuffColorFilter : 用單一顏色和特定的復(fù)合模式對源像素進(jìn)行著色
①睡榆、ColorMatrixColorFilter
在此之前先說明ColorMatrix這個類。主要屬性是一個大小為20的float數(shù)組袍榆,即45的舉證肉微,這是個45階的矩陣,RGBA和一個啞元蜡塌。啞元是一個附加值碉纳。增加一些分量。
如:
[ a, b, c, d, e,
f, g, h, i, j,
k, l, m, n, o,
p, q, r, s, t ]
上面矩陣和RGBA這個矩陣相乘就會得到一個運算后的新的RGBA
R = a*R + b*G + c*B + d*A + e;
G = f*R + g*G + h*B + i*A + j;
B = k*R + l*G + m*B + n*A + o;
A = p*R + q*G + r*B + s*A + t;
基本使用如下馏艾,對透明通道減半操作:
// 生成色彩矩陣
ColorMatrix colorMatrix = new ColorMatrix(new float[]{
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 0.5, 0,
});
mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
通過ColorMatrix矩陣的值的不同劳曹,得到的新的顏色也是不一致的。
1琅摩、過濾Blue通道的顏色
colorMatrix = new ColorMatrix(new float[]{
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0,
})
2铁孵、顏色取反
colorMatrix = new ColorMatrix(new float[]{
-1,0,0,0,255,
0,-1,0,0,255,
0,0,-1,0,255,
0,0,0,1,0
});
3、色彩的縮放運算
colorMatrix = new ColorMatrix(new float[]{
1.2f, 0, 0, 0, 0,
0, 1.2f, 0, 0, 50,
0, 0, 1.2f, 0, 0,
0, 0, 0, 1.2f, 0,
});
4房资、色彩反色 (紅綠反色)
colorMatrix = new ColorMatrix(new float[]{
0,1,0,0,0,
1,0,0,0,0,
0,0,1,0,0,
0,0,0,1,0
});
ColorMatrix類也給出了一些基本的運算:
- colorMatrix.setRotate(); : 設(shè)置旋轉(zhuǎn)
- colorMatrix.setSaturation(); : 設(shè)置飽和度蜕劝,同時增加RGB值
- colorMatrix.setScale(); : 色彩縮放
上面只給出了一些特定的矩陣效果,這個要求數(shù)學(xué)功底還可以,所以才有了LightingColorFilter
②岖沛、LightingColorFilter
唯一的構(gòu)造方法 : LightingColorFilter(int mul, int add)// mul是用來相乘的暑始,add運來相加
運算結(jié)果:
R = (r*mul.R+add.R)%255;
G = (g*mul.G+add.G)%255;
B = (b*mul.B+add.B)%255;
用法比較簡單:
mPaint.setColorFilter(new LightingColorFilter(0x00ff00, 0x000000));
③、PorterDuffColorFilter
這是個比較強(qiáng)大的類婴削。和PhotoShop的顏色疊加理念是一致的廊镜。
構(gòu)造方法:
public PorterDuffColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode)
一個附加顏色和一個模式。
PorterDuff.Mode 模式種類
Mode.SRC
Mode.DST
Mode.SRC_OVER
Mode.DST_OVER
Mode.SRC_IN
Mode.DST_IN
Mode.SRC_OUT 透明
Mode.DST_OUT
Mode.SRC_ATOP
Mode.DST_ATOP
Mode.CLEAR
Mode.XOR
* Mode.DARKEN 變暗
* Mode.LIGHTEN 變亮
* Mode.MULTIPLY 正片疊底
* Mode.SCREEN 濾色
* Mode.OVERLAY 疊加
* Mode.ADD 飽和度相加
大體分為下面幾種模式唉俗,源顏色為Color.RED舉例
-
清除模式 : Mode.CLEAR 和 Mode.XOR嗤朴,什么都不會顯示喻鳄,Mode.XOR與透明度有關(guān)
-
源模式: 前綴為Mode.SRC 顯示后加顏色煎源,除了Mode.SRC_OUT透明外,其他顯示RED
-
目標(biāo)模式:前綴為Mode.DST耐朴,除了Mode.DST_OUT透明外衡楞,其他顯示目標(biāo)圖
-
其他為疊加模式
代碼如下:
public class SelfViewTwo extends View {
private Paint mPaint;
private Bitmap mBmp;
public SelfViewTwo(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mBmp = BitmapFactory.decodeResource(getResources(), R.drawable.game);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setAntiAlias(true);
drawPorterDuffFilter5(canvas);
}
private void drawPorterDuffFilter(Canvas canvas){
int width = 300;
int height = width * mBmp.getHeight()/mBmp.getWidth();
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.ADD));//飽和度相加
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(350,0);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DARKEN));//變暗
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(-350,350);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.LIGHTEN));//變亮
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(350,0);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY));//正片疊底
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(-350,350);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.OVERLAY));//疊加
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(350,0);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SCREEN));//濾色
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
}
/**
* 清空模式
* PorterDuff.Mode.CLEAR 和 PorterDuff.Mode.XOR 不會顯示的
*/
private void drawPorterDuffFilter2(Canvas canvas){
int width = 500;
int height = width * mBmp.getHeight()/mBmp.getWidth();
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(550,0);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.CLEAR));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(-550,550);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.XOR));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
}
/**
* 目標(biāo)圖像模式
* Mode.DST吱雏、Mode.DST_IN、Mode.DST_OUT寺酪、Mode.DST_OVER坎背、Mode.DST_ATOP
* 除了Mode.DST_OUT顯示完全透明圖片以外替劈,其它全部顯示目標(biāo)圖像寄雀;
*/
private void drawPorterDuffFilter3(Canvas canvas){
int width = 500;
int height = width * mBmp.getHeight()/mBmp.getWidth();
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(550,0);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DST));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(-550,550);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DST_IN));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(550,0);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DST_OUT));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(-550,550);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DST_OVER));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(550,0);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DST_ATOP));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
}
/**
* 源圖模式
* Mode.SRC、Mode.SRC_IN陨献、Mode.SRC_OUT盒犹、Mode.SRC_OVER、Mode.SRC_ATOP
* 除了Mode.SRC_OUT顯示完全透明圖片以外眨业,其它全部顯示源圖像急膀;
*/
private void drawPorterDuffFilter4(Canvas canvas){
int width = 500;
int height = width * mBmp.getHeight()/mBmp.getWidth();
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(550,0);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(-550,550);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(550,0);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_OUT));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(-550,550);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_OVER));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(550,0);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_ATOP));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
}
/**
* SRC相關(guān)的模式,只有Mode.SRC_ATOP和SRC_IN能夠?qū)崿F(xiàn)SetTint的功能
*/
private void drawPorterDuffFilter5(Canvas canvas){
int width = 100;
int height = width * mBmp.getHeight()/mBmp.getWidth();
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(150,0);
mPaint.setColorFilter(new PorterDuffColorFilter(0xffff00ff, PorterDuff.Mode.SRC));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(150,0);
mPaint.setColorFilter(new PorterDuffColorFilter(0xff00f0ff, PorterDuff.Mode.SRC_ATOP));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(150,0);
mPaint.setColorFilter(new PorterDuffColorFilter(0xfff0f0ff, PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(150,0);
mPaint.setColorFilter(new PorterDuffColorFilter(0xffffff00, PorterDuff.Mode.SRC_OVER));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
canvas.translate(150,0);
mPaint.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.SRC_ATOP));
canvas.drawBitmap(mBmp,null,new Rect(0,0,width,height),mPaint);
}
}
總的來說龄捡,PorterDuffColorFilter是很強(qiáng)大的卓嫂,前提是弄清楚PorterDuff.Mode的模式特點,就是掌握了精髓聘殖。上面實例并沒有涉及到透明度的問題晨雳,所以還算簡單。不足的是疊加的只能是顏色不能是其他的如bitmap等奸腺。有關(guān)PorterDuff.Mode的模式算法餐禁,后面會細(xì)說。
3)突照、paint設(shè)置setXfermode
Xfermode 的唯一子類就是PorterDuffXfermode帮非,使用Xfermode時需要增加離屏
對應(yīng)的相應(yīng)算法如下:
其中
Sa : 源透明度
Da :目標(biāo)透明度
Sc : 源Color
Dc : 目標(biāo)Color
使用:準(zhǔn)備疊加之前paint先增加一個mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN))即可。
canvas.drawBitmap(dstBmp, 0, 0, mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
mPaint.setXfermode(null);
如PorterDuff.Mode.SRC_OUT實現(xiàn)刮刮卡的功能
public class GuaGuaCardView_SRCOUT extends View{
private Paint mBitPaint;
private Bitmap BmpDST,BmpSRC,BmpText;
private Path mPath;
private float mPreX,mPreY;
public GuaGuaCardView_SRCOUT(Context context, AttributeSet attrs) {
super(context, attrs);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
mBitPaint = new Paint();
mBitPaint.setColor(Color.RED);
mBitPaint.setStyle(Paint.Style.STROKE);
mBitPaint.setStrokeWidth(45);
BmpText = BitmapFactory.decodeResource(getResources(),R.drawable.guaguaka_text,null);
BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.guaguaka_pic,null);
BmpDST = Bitmap.createBitmap(BmpSRC.getWidth(), BmpSRC.getHeight(), Bitmap.Config.ARGB_8888);
mPath = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(BmpText,0,0,mBitPaint);
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
//先把手指軌跡畫到目標(biāo)Bitmap上
Canvas c = new Canvas(BmpDST);
c.drawPath(mPath,mBitPaint);
//然后把目標(biāo)圖像畫到畫布上
canvas.drawBitmap(BmpDST,0,0,mBitPaint);
//計算源圖像區(qū)域
mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
canvas.drawBitmap(BmpSRC,0,0,mBitPaint);
mBitPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
mPath.moveTo(event.getX(),event.getY());
mPreX = event.getX();
mPreY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
float endX = (mPreX+event.getX())/2;
float endY = (mPreY+event.getY())/2;
mPath.quadTo(mPreX,mPreY,endX,endY);
mPreX = event.getX();
mPreY =event.getY();
break;
case MotionEvent.ACTION_UP:
break;
}
postInvalidate();
return super.onTouchEvent(event);
}
}
- 1、首先末盔,目標(biāo)圖像和源圖像混合筑舅,需不需要生成顏色的疊加特效,如果需要疊加特效則從顏色疊加相關(guān)模式中選擇庄岖,有Mode.ADD(飽和度相加)豁翎、Mode.DARKEN(變暗),Mode.LIGHTEN(變亮)隅忿、Mode.MULTIPLY(正片疊底)心剥、Mode.OVERLAY(疊加),Mode.SCREEN(濾色)
- 2背桐、當(dāng)不需要特效优烧,而需要根據(jù)某一張圖像的透明像素來裁剪時,就需要使用SRC相關(guān)模式或DST相關(guān)模式了链峭。由于SRC相關(guān)模式與DST相關(guān)模式是相通的畦娄,唯一不同的是決定當(dāng)前哪個是目標(biāo)圖像和源圖像;
- 3弊仪、當(dāng)需要清空圖像時熙卡,使用Mode.CLEAR
小結(jié):
一共有三處地方使用PorterDuff.Mode
PorterDuff.Mode | 描述 |
---|---|
ComposeShader | 兩個Shader組合方式,使用時不能使用硬件加速 |
PorterDuffColorFilter | 增加一個單色的ColorFilter |
Xfermode | 兩個繪制內(nèi)容的疊加 |
三励饵、canvas變換函數(shù)和裁剪
1)驳癌、幾何變換
- 平移(translate)
// 畫筆
Paint paint = new Paint();
paint.setTextSize(50);
paint.setColor(Color.RED);
String str = "Android View";
// 基線
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
int dy = (fm.top + fm.bottom)/2;
int baseLine = getHeight()/2 - dy;
// 構(gòu)造一個背景bitmap
Bitmap bitmap = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas1 = new Canvas(bitmap);
canvas1.translate(100,100);
canvas1.drawText(str,0,str.length(),0,baseLine,paint);
canvas.drawBitmap(bitmap,null,new Rect(0,0,getWidth(),getHeight()),paint);
旋轉(zhuǎn)(Rotate)
void rotate(float degrees)
void rotate (float degrees, float px, float py) // 以某一個point旋轉(zhuǎn)縮放(scale )
public void scale (float sx, float sy) // x和y軸的縮放比例扭曲(skew)
void skew (float sx, float sy)
2)、canvas 裁剪
// 一些函數(shù)役听,裁剪出一個區(qū)域
boolean clipPath(Path path)
boolean clipPath(Path path, Region.Op op)
boolean clipRect(Rect rect, Region.Op op)
boolean clipRect(RectF rect, Region.Op op)
boolean clipRect(int left, int top, int right, int bottom)
boolean clipRect(float left, float top, float right, float bottom)
boolean clipRect(RectF rect)
boolean clipRect(float left, float top, float right, float bottom, Region.Op op)
boolean clipRect(Rect rect)
boolean clipRegion(Region region)
boolean clipRegion(Region region, Region.Op op)
下面通過clip裁剪一塊區(qū)域顯示颓鲜,來實現(xiàn)一段文字,不同顏色的功能
public class Test extends View {
public Test(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
protected void onDraw(Canvas canvas) {
// 畫筆
Paint paint = new Paint();
paint.setTextSize(50);
paint.setColor(Color.RED);
String str = "Android View Administrator";
// 基線
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
int dy = (fm.top + fm.bottom)/2;
int baseLine = getHeight()/2 - dy;
// 畫一個寬度為0~270px這塊區(qū)域
canvas.save();
canvas.clipRect(0,0,270,getHeight());
canvas.drawText(str,0,str.length(),0,baseLine,paint);
canvas.restore();
// 畫一個寬度為270px~getWidth()這塊區(qū)域
canvas.save();
paint.setColor(Color.GREEN);
canvas.clipRect(270,0,getWidth(),getHeight());
canvas.drawText(str,0,str.length(),0,baseLine,paint);
canvas.restore();
}
}
3)典予、canvas 保存與恢復(fù)
- int save () // 會把當(dāng)前的畫布的狀態(tài)進(jìn)行保存甜滨,然后放入特定的棧中;
- void restore() //會把棧中最頂層的畫布狀態(tài)取出來瘤袖,并按照這個狀態(tài)恢復(fù)當(dāng)前的畫布衣摩,并在這個畫布上做畫。
上個例子中也使用了canvas的保存和恢復(fù)捂敌。不進(jìn)行保存和恢復(fù)艾扮,就會在上一個畫布的基礎(chǔ)上進(jìn)行繪畫,后面的文字就不會被顯示出來黍匾。
4)栏渺、Matrix變換
步驟:
1、創(chuàng)建 Matrix 對象锐涯;
2磕诊、調(diào)用 Matrix 的 pre/postTranslate/Rotate/Scale/Skew() 方法來設(shè)置幾何變換;
3、使用 Canvas.setMatrix(matrix) 或 Canvas.concat(matrix) 來把幾何變換應(yīng)用到 Canvas霎终。
Canvas.setMatrix(matrix)是用新的matrix替代之前的matrix滞磺。Canvas.concat(matrix)是用新的和舊的matrix矩陣相乘。
5)莱褒、Camera三維變換
①击困、Camera.rotate*() 三維旋轉(zhuǎn)
- rotateX(deg) 沿X軸旋轉(zhuǎn)deg角度
- rotateY(deg) 沿Y軸旋轉(zhuǎn)deg角度
- rotateZ(deg) 沿Z軸旋轉(zhuǎn)deg角度
- rotate(x, y, z) 沿X/Y/Z軸旋轉(zhuǎn)相應(yīng)的角度
rotateX(deg)舉例:
執(zhí)行camera.rotateX(30)時,需要設(shè)置一個旋轉(zhuǎn)軸心广凸,否則會出現(xiàn)不對稱問題;而且canvas的幾何變換順序是相反的阅茶,即后面的先執(zhí)行。所以先寫canvas.translate(centerX, centerY);
canvas.save();
camera.save(); // 保存 Camera 的狀態(tài)
camera.rotateX(30); // 旋轉(zhuǎn) Camera 的三維空間
canvas.translate(centerX, centerY); // 旋轉(zhuǎn)之后把投影移動回來
camera.applyToCanvas(canvas); // 把旋轉(zhuǎn)投影到 Canvas
canvas.translate(-centerX, -centerY); // 旋轉(zhuǎn)之前把繪制內(nèi)容移動到軸心(原點)
camera.restore(); // 恢復(fù) Camera 的狀態(tài)
canvas.drawBitmap(bitmap, point1.x, point1.y, paint);
canvas.restore();
四谅海、View繪制順序
android是順序的繪制View的脸哀,這會導(dǎo)致后面繪制的View會影響前面的View。即繪制順序的不同扭吁,會有不同的界面效果撞蜂。
從源碼draw()里面看繪制流程:
- drawBackground() 繪制背景,該方法不可重寫
- onDraw() 繪制主體內(nèi)容
- dispatchDraw() 繪制子View侥袜,需要調(diào)用setWillNotDraw(false)
- onDrawFroeground() 繪制前景 API 23時候蝌诡,才引用了該方法
1、由于ViewGroup默認(rèn)不會執(zhí)行onDraw方法枫吧,需要調(diào)用View.setWillNotDraw(false)來強(qiáng)制執(zhí)行onDraw
2浦旱、在盡可能的情況下,將代碼放在onDraw()里面寫由蘑,View的機(jī)制中會在不需要重新繪制的時候闽寡,跳過onDraw方法代兵,從而提升效率尼酿。