android 绘制几何背景,Android VIew绘图机制

本篇博客是我对View的绘图方面的理解和归纳,也不会说的非常的细,只是类似于一个思维导图的作用,因为有人写得更好了,我就没必要写了,况且写了还不一定如别人的。

本文大纲如下:

ab71d6d92605

大纲

View形状绘制

View颜色绘制

View几何变换和裁剪

View的绘制顺序

一、绘制形状

1)、Paint类的基本用法

paint.setAntiAlias(true); //抗锯齿功能,一般都会加入的

paint.setColor( ); // 设置画笔颜色

paint.setARGB(int a, int r, int g, int b) // 设置三原色和透明通路

paint.setAlpha(int a) // 设置透明通道

paint.setStrokeWidth(int a);//设置画笔宽度

paint.setStyle(Style.FILL);//设置填充样式

paint.setTextAlign(Align.CENTER); //设置文字对齐方式,取值:Align.CENTER、Align.LEFT或Align.RIGHT

paint.setTextSize(12); //设置文字大小

paint.setUnderlineText(true); //设置下划线

paint.setStrikeThruText(true); //设置带有删除线效果

paint.setTextScaleX(2); //只会将水平方向拉伸,高度不会变

Paint.Style 属性

描述

Paint.Style.FILL

填充内部

Paint.Style.FILL_AND_STROKE

填充内部和描边

Paint.Style.STROKE

仅描边

Paint.Cap 属性

描述

Paint.Cap.BUTT

线头形状平头

Paint.Cap.ROUND

线头形状圆头

Paint.Cap.SQUARE

线头形状方头

ab71d6d92605

Cap形状

Paint.Align属性

描述

Paint.Align.LEFT

内容居左显示

Paint.Align.CENTER

内容居中显示

Paint.Align.RIGHT

内容居右显示

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 绘制

①、通用函数

canvas.drawColor(Color.BLUE); // 设置画布颜色,即背景

canvas.drawRGB(255, 255, 0); // 设置画布颜色,即背景

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搭配使用可以达到不同的效果。如扇形和弧线等。

②、文字相关

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);

设定起始点

path.lineTo(10, 100)

连接到下一个点

path.rMoveTo(10, 10);

以在上一个结束点为基准点,x轴偏移10像素,y轴偏移10像素

path.rLineTo(10, 100)

连接到下一个点,下个点是以在上一个结束点为基准点,x轴偏移10像素,y轴偏移10像素

path.close();

形成一个闭环

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

非零环绕数原则,默认为WINDING

EVEN_ODD

交叉填充,两图形奇偶原则。一点重合了偶数次就不画

INVERSE_WINDING

与WINDING完全相反

INVERSE_EVEN_ODD

与EVEN_ODD完全相反

ab71d6d92605

WINDING

对于上图,WINDING规则下,画的方向会直接影响多个图形的重合区域,对于一点顺时针则+1,逆时针-1,如果相加等于0则不画。

INVERSE_WINDING和WINDING画的是完全相反的。INVERSE_EVEN_ODD和EVEN_ODD是完全相反的。

二、绘制颜色

1)、基本颜色

canvas.drawColor(Color.BLUE); // 设置画布颜色,即背景

canvas.drawRGB(255, 255, 0); // 设置画布颜色,即背景

paint.setColor( ); // 设置画笔颜色

paint.setARGB(int a, int r, int g, int b) // 设置三原色和透明通路

setShader(Shader shader) 设置 Shader

Shader称之着色器和渲染器,可以实现一些渐变和渲染效果。它的子类包括:

BitmapShader : 位图Shader

LinearShader : 线性Shader

RadialShader : 光束Shader

SweepShader : 梯度Shader

ComposeShader : 混合Shader

BitmapShader

构造函数:

public BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY) ;

其中TileMode 是图像的填充模式

CLAMP : 拉伸模式,图片最后一个像素的颜色铺满剩余的X或者Y轴

REPETA : 重复模式

MIRROR : 镜像模式

看一个关于图像中间去一个圆形图像的例子

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 : 用单一颜色和特定的复合模式对源像素进行着色

①、ColorMatrixColorFilter

在此之前先说明ColorMatrix这个类。主要属性是一个大小为20的float数组,即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(); : 设置旋转

colorMatrix.setSaturation(); : 设置饱和度,同时增加RGB值

colorMatrix.setScale(); : 色彩缩放

上面只给出了一些特定的矩阵效果,这个要求数学功底还可以,所以才有了LightingColorFilter

②、LightingColorFilter

唯一的构造方法 : LightingColorFilter(int mul, int add)// mul是用来相乘的,add运来相加

运算结果:

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

这是个比较强大的类。和PhotoShop的颜色叠加理念是一致的。

构造方法:

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与透明度有关

ab71d6d92605

源模式: 前缀为Mode.SRC 显示后加颜色,除了Mode.SRC_OUT透明外,其他显示RED

ab71d6d92605

目标模式:前缀为Mode.DST,除了Mode.DST_OUT透明外,其他显示目标图

ab71d6d92605

其他为叠加模式

ab71d6d92605

代码如下:

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);

}

/**

* 目标图像模式

* Mode.DST、Mode.DST_IN、Mode.DST_OUT、Mode.DST_OVER、Mode.DST_ATOP

* 除了Mode.DST_OUT显示完全透明图片以外,其它全部显示目标图像;

*/

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相关的模式,只有Mode.SRC_ATOP和SRC_IN能够实现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是很强大的,前提是弄清楚PorterDuff.Mode的模式特点,就是掌握了精髓。上面实例并没有涉及到透明度的问题,所以还算简单。不足的是叠加的只能是颜色不能是其他的如bitmap等。有关PorterDuff.Mode的模式算法,后面会细说。

3)、paint设置setXfermode

Xfermode 的唯一子类就是PorterDuffXfermode,使用Xfermode时需要增加离屏

给出一张官方的PorterDuff.Mode图

ab71d6d92605

PorterDuff.Mode

对应的相应算法如下:

其中

Sa : 源透明度

Da :目标透明度

Sc : 源Color

Dc : 目标Color

ab71d6d92605

PorterDuff.Mode公式算法

使用:准备叠加之前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实现刮刮卡的功能

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);

//先把手指轨迹画到目标Bitmap上

Canvas c = new Canvas(BmpDST);

c.drawPath(mPath,mBitPaint);

//然后把目标图像画到画布上

canvas.drawBitmap(BmpDST,0,0,mBitPaint);

//计算源图像区域

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、首先,目标图像和源图像混合,需不需要生成颜色的叠加特效,如果需要叠加特效则从颜色叠加相关模式中选择,有Mode.ADD(饱和度相加)、Mode.DARKEN(变暗),Mode.LIGHTEN(变亮)、Mode.MULTIPLY(正片叠底)、Mode.OVERLAY(叠加),Mode.SCREEN(滤色)

2、当不需要特效,而需要根据某一张图像的透明像素来裁剪时,就需要使用SRC相关模式或DST相关模式了。由于SRC相关模式与DST相关模式是相通的,唯一不同的是决定当前哪个是目标图像和源图像;

3、当需要清空图像时,使用Mode.CLEAR

小结:

一共有三处地方使用PorterDuff.Mode

PorterDuff.Mode

描述

ComposeShader

两个Shader组合方式,使用时不能使用硬件加速

PorterDuffColorFilter

增加一个单色的ColorFilter

Xfermode

两个绘制内容的叠加

三、canvas变换函数和裁剪

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;

// 构造一个背景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);

旋转(Rotate)

void rotate(float degrees)

void rotate (float degrees, float px, float py) // 以某一个point旋转

缩放(scale )

public void scale (float sx, float sy) // x和y轴的缩放比例

扭曲(skew)

void skew (float sx, float sy)

2)、canvas 裁剪

// 一些函数,裁剪出一个区域

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裁剪一块区域显示,来实现一段文字,不同颜色的功能

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这块区域

canvas.save();

canvas.clipRect(0,0,270,getHeight());

canvas.drawText(str,0,str.length(),0,baseLine,paint);

canvas.restore();

// 画一个宽度为270px~getWidth()这块区域

canvas.save();

paint.setColor(Color.GREEN);

canvas.clipRect(270,0,getWidth(),getHeight());

canvas.drawText(str,0,str.length(),0,baseLine,paint);

canvas.restore();

}

}

ab71d6d92605

结果

3)、canvas 保存与恢复

int save () // 会把当前的画布的状态进行保存,然后放入特定的栈中;

void restore() //会把栈中最顶层的画布状态取出来,并按照这个状态恢复当前的画布,并在这个画布上做画。

上个例子中也使用了canvas的保存和恢复。不进行保存和恢复,就会在上一个画布的基础上进行绘画,后面的文字就不会被显示出来。

4)、Matrix变换

步骤:

1、创建 Matrix 对象;

2、调用 Matrix 的 pre/postTranslate/Rotate/Scale/Skew() 方法来设置几何变换;

3、使用 Canvas.setMatrix(matrix) 或 Canvas.concat(matrix) 来把几何变换应用到 Canvas。

Canvas.setMatrix(matrix)是用新的matrix替代之前的matrix。Canvas.concat(matrix)是用新的和旧的matrix矩阵相乘。

5)、Camera三维变换

①、Camera.rotate*() 三维旋转

rotateX(deg) 沿X轴旋转deg角度

rotateY(deg) 沿Y轴旋转deg角度

rotateZ(deg) 沿Z轴旋转deg角度

rotate(x, y, z) 沿X/Y/Z轴旋转相应的角度

rotateX(deg)举例:

执行camera.rotateX(30)时,需要设置一个旋转轴心,否则会出现不对称问题;而且canvas的几何变换顺序是相反的,即后面的先执行。所以先写canvas.translate(centerX, centerY);

canvas.save();

camera.save(); // 保存 Camera 的状态

camera.rotateX(30); // 旋转 Camera 的三维空间

canvas.translate(centerX, centerY); // 旋转之后把投影移动回来

camera.applyToCanvas(canvas); // 把旋转投影到 Canvas

canvas.translate(-centerX, -centerY); // 旋转之前把绘制内容移动到轴心(原点)

camera.restore(); // 恢复 Camera 的状态

canvas.drawBitmap(bitmap, point1.x, point1.y, paint);

canvas.restore();

四、View绘制顺序

android是顺序的绘制View的,这会导致后面绘制的View会影响前面的View。即绘制顺序的不同,会有不同的界面效果。

从源码draw()里面看绘制流程:

drawBackground() 绘制背景,该方法不可重写

onDraw() 绘制主体内容

dispatchDraw() 绘制子View,需要调用setWillNotDraw(false)

onDrawFroeground() 绘制前景 API 23时候,才引用了该方法

ab71d6d92605

1、由于ViewGroup默认不会执行onDraw方法,需要调用View.setWillNotDraw(false)来强制执行onDraw

2、在尽可能的情况下,将代码放在onDraw()里面写,View的机制中会在不需要重新绘制的时候,跳过onDraw方法,从而提升效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值