前言
Canvas的使用一般用于继承View的自定义View中,主要为我们提供了4类方法
- 第一是以drawXXX为主的绘制方法;
- 第二是以clipXXX为主的裁剪方法;
- 第三是以scale、skew、translate和rotate组成的Canvas变换方法;
- 最后一类则是以saveXXX和restoreXXX构成的画布锁定和还原
drawXXX
Paint
setStyle(Style style)
setStyle设置画图样式,有三种选项
- Paint.Style.FILL:填充内部
- Paint.Style.FILL_AND_STROKE :填充内部和描边
- Paint.Style.STROKE :描边
Paint mPaint = new Paint();
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(30f);
Path path3 = new Path();
RectF mRect = new RectF(100, 100, 500, 300);
path3.addRect(mRect, Path.Direction.CCW);
canvas.drawPath(path3, mPaint);
Paint.Style.FILL 和 Paint.Style.FILL_AND_STROKE ,下面是描边
setStrokeJoin(Join join)
setStrokeJoin设置画矩形的时候是否是锐角,有三种选项
mPaint.setStrokeJoin(Paint.Join.ROUND);
- Paint.Join.ROUND
- Paint.Join.MITER
- Paint.Join.BEVEL
setStrokeCap(Cap cap)
注意同样长度下,线帽大小导致长度不一样
- Paint.Cap.SQUARE
- Paint.Cap.ROUND
- Paint.Cap.BUTT
Paint mPaint = new Paint();
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(50);
mPaint.setStrokeCap(Paint.Cap.SQUARE);//方形
mPaint.setStrokeJoin(Paint.Join.BEVEL);//直线
Path path = new Path();
path.moveTo(100, 100);
path.lineTo(300, 100);
canvas.drawPath(path, mPaint);
mPaint.reset();//重置
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(50);
mPaint.setStrokeCap(Paint.Cap.ROUND);//圆形
mPaint.setStrokeJoin(Paint.Join.BEVEL);//直线
Path path1 = new Path();
path1.moveTo(100, 200);
path1.lineTo(300, 200);
canvas.drawPath(path1, mPaint);
mPaint.reset();//重置
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(50);
mPaint.setStrokeCap(Paint.Cap.BUTT);//没有
mPaint.setStrokeJoin(Paint.Join.BEVEL);//直线
Path path2 = new Path();
path2.moveTo(100, 300);
path2.lineTo(300, 300);
canvas.drawPath(path2, mPaint);
setTextAlign(Align align)
不设置情况下默认Paint.Align.LEFT
- Paint.Align.LEFT
- Paint.Align.CENTER
- Paint.Align.RIGHT
int left = 200;
int textSize = UIHelper.Dp2Px(mContext, 14);
TextPaint FontPaint = new TextPaint();
FontPaint.setTextSize(textSize);
float textWidth = FontPaint.measureText("我爱中国");
Paint mPaint = new Paint();
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(5);
mPaint.setTextSize(textSize);
mPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("我爱中国", left, 100, mPaint);
//画的是上面蓝色的横线
Path path = new Path();
path.moveTo(left, 50);
path.lineTo(400, 50);
canvas.drawPath(path,mPaint);
mPaint.reset();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(5);
mPaint.setTextSize(textSize);
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("我爱中国", left, 200, mPaint);
//画的是竖着的红线
Path mPath = new Path();
mPath.moveTo(left + textWidth / 2, 50);
mPath.lineTo(left + textWidth / 2, 350);
canvas.drawPath(mPath, mPaint);
mPaint.reset();
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(5);
mPaint.setTextSize(textSize);
mPaint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText("我爱中国", left, 300, mPaint);
setLetterSpacing
int left = 200;
int textSize = UIHelper.Dp2Px(mContext, 14);
TextPaint FontPaint = new TextPaint();
FontPaint.setTextSize(textSize);
Paint mPaint = new Paint();
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(5);
mPaint.setTextSize(textSize);
mPaint.setTextAlign(Paint.Align.LEFT);
mPaint.setLetterSpacing(2.0f);
canvas.drawText("我爱中国", left, 100, mPaint);
setStrikeThruText(true);
Path
lineTo、rLineTo
rLineTo(100,200):以上面点向左移动100,下平移200
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - 描边
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path path3 = new Path();
path3.moveTo(100, 100);
path3.lineTo(100, 200);
canvas.drawPath(path3, mPaint);
Path path4 = new Path();
path4.moveTo(100, 100);
path4.rLineTo(100, 200);//以上面点向左移动100,下平移200
canvas.drawPath(path4, mPaint);
moveTo、rMoveTo
path3.rMoveTo(100,100);实际开始位置为(200 ,300) //当前点坐标为上一个点的位置加上rMoveTo中的偏移量(100+100,200+100)所以位置为(200 ,300),中间就会断开
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - 描边
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path path3 = new Path();
path3.moveTo(100, 100);
path3.lineTo(100, 200);
path3.rMoveTo(100,100);//(200 ,300) //第三个点坐标为上一个点的位置加上偏移量(100+100,200+100)
path3.lineTo(200,400);
canvas.drawPath(path3, mPaint);
addArc
RectF(100, 100, 200, 200):距离上下左右位置
addArc(arcRecF, 0, 270):画弧线为0-270度,3点钟方向为0度。
Path path3 = new Path();
RectF arcRecF = new RectF(100, 100, 200, 200);
path3.addArc(arcRecF, 0, 270);
canvas.drawPath(path3, mPaint);
Path path4 = new Path();
RectF arcRecF2 = new RectF(100, 300, 300, 400);
path4.addArc(arcRecF2, 0, 270);
canvas.drawPath(path4, mPaint);
addCircle
addCircle(300,300,100, Path.Direction.CCW),圆的圆心坐标(300,300),半径100。Path.Direction.CCW:逆时针,Path.Direction.CW :顺时针
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - 描边
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path path=new Path();
path.addCircle(300,300,100, Path.Direction.CCW);//逆时针 CW
canvas.drawPath(path, mPaint);
addOval
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - 描边
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path path = new Path();
RectF arcRecF = new RectF(100, 100, 300, 200);
path.addOval(arcRecF, Path.Direction.CCW);
canvas.drawPath(path, mPaint);
addRect、addRoundRect
addRoundRect(arcRecF1,25,25,Path.Direction.CCW):圆角矩形的圆角的x、y半径
addRoundRect(arcRecF2,a,Path.Direction.CCW),radii:8个值的数组,4对[X,Y]半径这个值绝对不能null
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - 描边
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path path = new Path();
RectF arcRecF = new RectF(100, 100, 300, 200);
path.addRect(arcRecF,Path.Direction.CCW);
canvas.drawPath(path, mPaint);
Path path1=new Path();
RectF arcRecF1 = new RectF(100, 300, 300, 400);
path1.addRoundRect(arcRecF1,25,25,Path.Direction.CCW);
RectF arcRecF2 = new RectF(100, 500, 300, 600);
float a[]={10,10,10,10,25,25,25,25};
path1.addRoundRect(arcRecF2,a,Path.Direction.CCW);
canvas.drawPath(path1, mPaint);
path.approximate(0.5f)
用一系列线段近似。这将返回包含点组件的数组的float []。按顺序,每个点有三个组件
0.5f:Path上一行的可接受错误。通常这将是0.5,因此误差小于半个像素。值为0或更大
- 沿点所在路径长度的分数
- 点的x坐标
- 点的y坐标
画的图一共有三个节点, float[] pointComponents 一共9条数据,3条一组,打印出来的刚好是三个节点的所在百分比,以及x、y坐标
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - 描边
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path path3 = new Path();
path3.moveTo(100, 100);
path3.lineTo(100, 200);
path3.lineTo(200, 200);
canvas.drawPath(path3, mPaint);
float[] pointComponents = path3.approximate(0.5f);
Log.i("LOG", "pointComponents.size:" + pointComponents.length);
for (int i = 0; i < pointComponents.length; i++) {
Log.i("LOG", "data:" + pointComponents[i]);
}
arcTo
参数forceMoveTo很重要,false:连在一起,true:不连着
arcTo:其实是画一个圆弧然后和前面的路径连在一起用的
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - 描边
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path path3 = new Path();
path3.moveTo(100, 100);
path3.lineTo(100, 200);
RectF arcRecF = new RectF(100, 100, 300, 200);
path3.arcTo(arcRecF, 0, 270, false);
RectF arcRecF2 = new RectF(350, 100, 550, 200);
path3.arcTo(arcRecF2, 0, 270, true);
canvas.drawPath(path3, mPaint);
computeBounds
computeBounds(mComputeRect, true): 后面一个参数好像没什么用,方法主要的作用是,计算路径控制点的边界,并将答案写入边界。看看下面打印,left变成了圆的left,也就是边界被重新计算了。
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - 描边
mPaint.setStrokeWidth(10); // 边框宽度 - 10
RectF mComputeRect = new RectF();
Path mEndPath = new Path();
mEndPath.addCircle(200, 200, 100, Path.Direction.CW);
mEndPath.addRect(new RectF(200, 300, 500, 500), Path.Direction.CW);
mEndPath.computeBounds(mComputeRect, true);
Log.i("LOG", "-------" + mComputeRect.left + "," + mComputeRect.top + "," + mComputeRect.right + ","
+ mComputeRect.bottom);
canvas.drawPath(mEndPath, mPaint);
offset
就是进行移动,这个是向移动500
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - 描边
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path path3 = new Path();
path3.moveTo(100, 100);
path3.lineTo(100, 200);
path3.offset(500,0,path3);
canvas.drawPath(path3, mPaint);
op
此方法用于对两个Path对象做相互运算,类似于:与、或、异或之类
- Path.Op.UNION
- Path.Op.DIFFERENCE
- Path.Op.INTERSECT
- Path.Op.REVERSE_DIFFERENCE
- Path.Op.XOR
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.FILL); // 填充模式 - :填充内部
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path path1 = new Path();
path1.addCircle(150, 150, 100, Path.Direction.CW);
Path path2 = new Path();
path2.addCircle(200, 200, 100, Path.Direction.CW);
path1.op(path2, Path.Op.UNION);
canvas.drawPath(path1, mPaint);
canvas.drawPath(path1, mPaint);
quadTo、rQuadTo
从最后一个点添加二次贝塞尔曲线,如果没有先前的点,则自动插入moveTo(0,0)。 曲线第一个坐标(300, 0,) 第二个(500, 300)
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - :填充内部
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path mPath=new Path();
mPath.moveTo(100, 300);
mPath.quadTo(300, 0, 500, 300);
mPath.quadTo(700, 600, 900, 300);
canvas.drawPath(mPath, mPaint);
rQuadTo与quadTo相同,但坐标被认为是相对于此轮廓上的最后一个点。如果没有先前的点,则自动插入moveTo(0,0)
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - :填充内部
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path mPath=new Path();
mPath.moveTo(100,300);
mPath.rQuadTo(100,-100,200,0);
mPath.rQuadTo(100,100,200,0);
canvas.drawPath(mPath, mPaint);
cubicTo、rCubicTo
以下只是三阶贝塞尔曲线只是示范,相比二阶贝塞尔曲线,三阶贝塞尔曲线需要看相关动图了解,下面的示例并非常规使用,只是想实现同样的曲线
cubicTo : 从最后一点添加一个三次贝塞尔曲线,接近控制点(x1,y1)和(x2,y2),并以(x3,y3)结束。如果没有对此轮廓进行moveTo()调用,则第一个点自动设置为(0,0)。
Path mPath=new Path();
mPath.moveTo(100, 300);
mPath.cubicTo(100, 300,300, 0, 500, 300);
mPath.cubicTo(500, 300,700, 600, 900, 300);
canvas.drawPath(mPath, mPaint);
rCubicTo:与cubicTo相同,但坐标被认为是相对于此轮廓上的当前点。如果没有先前的点,则自动插入moveTo(0,0)。
(100, 300)最左边坐标,(100, -300)为峰顶坐标,(200, 300)为谷底坐标,(300, 0)最右侧坐标
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - :填充内部
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path mPath = new Path();
mPath.moveTo(100, 300);
mPath.rCubicTo(100, -300, 200, 300, 300, 0);
canvas.drawPath(mPath, mPaint);
同样是rCubicTo
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - :填充内部
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path mPath = new Path();
mPath.moveTo(100, 300);//(0,0)
mPath.rCubicTo(100, -300, 200, 0, 200, 0);
mPath.rCubicTo(100, 300, 200, 0, 200, 0);
canvas.drawPath(mPath, mPaint);
setLastPoint
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - :填充内部
mPaint.setStrokeWidth(10); // 边框宽度 - 10
Path mPath=new Path();
mPath.moveTo(100, 100);
mPath.lineTo(100, 200);
mPath.lineTo(200, 200);
mPath.setLastPoint(200,300);
mPath.lineTo(100,400);
canvas.drawPath(mPath, mPaint);
方法 | 作用 |
---|---|
close() | 闭合当前路径 (系统会自动从起点到终点绘制一条直线,使当前路径闭合) |
path.reset() | 清除掉path里的线条和曲线,但是不会改变它的fill-type(后面setFillType再说) |
path.rewind() | 清除掉path里的线条和曲线,但是会保留内部的数据结构以便重用 |
path.set(Path src) | 用src的内容替换原path的内容,和硬件加速有关,开启的话有可能出不来效果 |
isInverseFillType() | 是否是 逆 填充模式:WINDING 和 EVEN_ODD 返回false,INVERSE_WINDING 和 INVERSE_EVEN_ODD 返回true |
toggleInverseFillType() | 切换相反的填充模式,如WINDING ->INVERSE_WINDING |
isEmpty() | path是否为空,如果path不包含任何线条和曲线,则返回true,否则返回false |
isRect(RectF rect) | 如果path指定的是一个rect,则返回true,否则返回false,如果返回true & rect 不为null,则将该rect设置为path 的区域 |
setFillType(Path.FillType ft) | 有几种取值和和op类似:于:与、或、异或之类 |
clipXXX
clipPath是使用路径对Canvas进行裁剪,clipRect是使用范围进行裁剪,如果过你使用Path连成框也可以行,
canvas设置绿色的部分就是从原画布上扣下来的,注意Region.Op.INTERSECT参数,就是取和Path重合部分
Path path = new Path();
path.moveTo(100, 100);
path.lineTo(100, 200);
path.lineTo(200, 250);
path.lineTo(300, 200);
path.close();
canvas.drawPath(path, mPaint);
canvas.clipPath(path,Region.Op.INTERSECT);
canvas.drawColor(Color.GREEN);
DIFFERENCE(0),
INTERSECT(1):重合部分
UNION(2),
XOR(3),
REVERSE_DIFFERENCE(4),
REPLACE(5)
同时使用clipRect,clipPath取两个的重合部分
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
canvas.drawRect(new RectF(100, 100, 200, 200), mPaint);
canvas.drawCircle(200, 150, 50, mPaint);
canvas.clipRect(new RectF(100, 100, 200, 200));
Path path = new Path();
path.addCircle(200, 150, 50, Path.Direction.CCW);
canvas.clipPath(path, Region.Op.INTERSECT);
canvas.drawColor(Color.GREEN);
DIFFERENCE
UNION
XOR
REVERSE_DIFFERENCE
REPLACE
Canvas变换
对于Canvas变换,你要知道一点:Canvas的变换都是以屏幕左上为原点坐标
。如果平移前后都画一次图,那么就相当于产生了两个画布,而平移后的图有可能超出屏幕,显示不完全
translate
Paint paint = new Paint();
paint.setStrokeWidth(10);
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(new Rect(0, 0, mViewWidth, 200), mPaint);
canvas.translate(100, 100);
canvas.drawRect(new Rect(0, 0, mViewWidth, 200), paint);
scale
下面蓝色区域就是缩小后的canvas区域,我看到
canvas.scale(0.75f, 0.75f);
Paint paint = new Paint();
paint.setStrokeWidth(10);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(new Rect(0,0,mViewWidth,mViewHeight),paint);
paint.setColor(Color.GREEN);
canvas.drawRect(new Rect(0, 0, mViewWidth, 200), mPaint);
canvas.translate(100, 100);
canvas.drawRect(new Rect(0, 0, mViewWidth, 200), paint);
skew
skew(错切)
对于skey(float sx,float sy),sx和sy分别表示将画布在x和y方向上倾斜相应的角度对应的tan值。
当sx=1时,即将画布在x方向上旋转45度,其实就是x轴保持方向不变,y轴逆时针旋转45度。
当sy=1时,即将画布在y方向上旋转45度,其实就是y轴保持方向不变,x轴顺时针旋转45度。
当sx、sy都改变时,两者都进行相应的移动。最后一张图中,将画布往x方向旋转45端,y方向旋转60度,可见,x轴顺时针旋转了60度,y逆时针旋转了45度。
private Paint mPaint;
private int mViewWidth, mViewHeight;// 控件宽高
private float curValue = 0f;
public CanvasView(Context context) {
this(context, null);
}
public CanvasView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CanvasView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
initAnimator();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
/*
* 获取控件宽高
*/
mViewWidth = w;
mViewHeight = h;
}
private void init() {
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(10);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// sx 将画布在x方向上倾斜相应的角度,sx倾斜角度的tan值,其实就是将y逆时针旋转相应的角度
// sy 将画布在y方向上倾斜相应的角度,sx倾斜角度的tan值,其实就是将x顺时针旋转相应的角度
canvas.skew(curValue, 0f);
canvas.drawRect(new Rect(0, 0, 200, 200), mPaint);
}
private void initAnimator() {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(1000);
animator.setStartDelay(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
curValue = (float) animation.getAnimatedValue();
invalidate();
}
});
animator.start();
}
rotate
旋转角度也是以左上角为方向,不过可以设置旋转点 canvas.rotate(curValue,mViewWidth/2,mViewHeight/2)
这个就是按屏幕中间的旋转
private Paint mPaint;
private int mViewWidth, mViewHeight;// 控件宽高
private int curValue = 0;
public CanvasView(Context context) {
this(context, null);
}
public CanvasView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CanvasView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
initAnimator();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
/*
* 获取控件宽高
*/
mViewWidth = w;
mViewHeight = h;
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 旋转画布
canvas.rotate(curValue);
/*
* 绘制一个红色矩形
*/
mPaint.setColor(Color.RED);
canvas.drawRect(mViewWidth / 2F - 200, mViewHeight / 2F - 200, mViewWidth / 2F + 200, mViewHeight / 2F + 200, mPaint);
mPaint.setColor(Color.BLUE);
canvas.drawRect(mViewWidth / 2F - 200, mViewHeight / 2F - 100, mViewWidth / 2F + 150, mViewHeight / 2F + 100, mPaint);
}
private void initAnimator() {
ValueAnimator animator = ValueAnimator.ofInt(0, 30);
animator.setDuration(1000);
animator.setStartDelay(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
curValue = (int) animation.getAnimatedValue();
invalidate();
}
});
animator.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas)
/*
* 绘制一个红色矩形
*/
mPaint.setColor(Color.RED);
canvas.drawRect(mViewWidth / 2F - 200, mViewHeight / 2F - 200, mViewWidth / 2F + 200, mViewHeight / 2F + 200, mPaint);
mPaint.setColor(Color.BLUE);
// 旋转画布
canvas.rotate(curValue);
canvas.drawRect(mViewWidth / 2F - 200, mViewHeight / 2F - 100, mViewWidth / 2F + 150, mViewHeight / 2F + 100, mPaint);
}
画布锁定和还原
save、restore
//在屏幕中间画一条线
Path path = new Path();
path.moveTo(mViewWidth / 2, mViewHeight / 2-100);
path.lineTo(mViewWidth / 2, mViewHeight / 2);
canvas.drawPath(path, mPaint);
canvas.rotate(curValue,mViewWidth/2,mViewHeight/2);
canvas.drawPath(path, mPaint);
//如果此时我想在屏幕上方画一个矩形,发现其跟着canvas旋转了
canvas.drawRect(new Rect(0,0,200,500),mPaint);
这里就用到save、restore;在要做旋前保存画布状态,然后在旋转后将画布还原,也就是说画布的状态是只针对这一次操作。
Path path = new Path();
path.moveTo(mViewWidth / 2, mViewHeight / 2-100);
path.lineTo(mViewWidth / 2, mViewHeight / 2);
canvas.drawPath(path, mPaint);
canvas.save();
canvas.rotate(curValue,mViewWidth/2,mViewHeight/2);
canvas.drawPath(path, mPaint);
canvas.restore();
//如果此时我想在屏幕上方画一个矩形,发现其跟着canvas旋转了
canvas.drawRect(new Rect(0,0,200,500),mPaint);
saveLayer
public int saveLayer //新建图层
public void restoreToCount(int saveCount) //切换到指定图层的上一级图层
public int getSaveCount() //获取图层数量
下面代码一共有2个画布,在这里我们要理解 Canvas,画布和图层的关系。
Canvas就像一个画板,画板上有一个原画布,一般情况先我们都是在原画布画布上画东西,我们每 canvas.drawxxx的时候就会生成一个新的图层,我们也可以使用canvas.saveLayer生成新的画布,在创建画布的时候一定要大小适量,新的画布也具有图层的概念,但是新生成的画布,在画东西的时候,坐标的基准点还是屏幕的左上角。在多个画布中,我们可以使用canvas.restoreToCount回到某一个画布。
要理解这个下面将代码一个一个屏蔽后打开就明白,并且一定要理解Canvas、画布和图层的关系,关于canvas.saveLayer中Canvas.ALL_SAVE_FLAG其他参数自行了解
private Paint mPaint;
private int mViewWidth, mViewHeight;// 控件宽高
private float curValue = 0f;
private int OFFSET = 100;//偏移量
private Context mContext;
public CanvasView(Context context) {
this(context, null);
}
public CanvasView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CanvasView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init();
initAnimator();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
/*
* 获取控件宽高
*/
mViewWidth = w;
mViewHeight = h;
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(10);
mPaint.setStyle(Paint.Style.FILL);
float size = UIHelper.dp2px(mContext, 15);
mPaint.setTextSize(size);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLUE);
//创建一个画布,
int saveCount = canvas.saveLayer(OFFSET, OFFSET, mViewWidth - OFFSET, mViewHeight - OFFSET, mPaint, Canvas.ALL_SAVE_FLAG);
//设置画布的颜色为GREEN
canvas.drawColor(Color.GREEN);
//再创建一个画布
int saveCount1 = canvas.saveLayer(OFFSET * 2, OFFSET * 2, mViewWidth - OFFSET * 2, mViewHeight - OFFSET * 2, mPaint, Canvas.ALL_SAVE_FLAG);
//设置画布的颜色为BLACK
canvas.drawColor(Color.BLACK);
//在BLACK画布上画一个圆, 注意这个圆不能显示完全,因为画布的大小有限制,左边点的位置还是相对于屏幕坐上角
canvas.drawCircle(300, 300, 300, mPaint);
//创建一个新画笔
Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
mPaint.setColor(Color.WHITE);
mPaint.setStrokeWidth(10);
mPaint.setStyle(Paint.Style.FILL);
//还是在黑色画布上画一个矩形
canvas.drawRect(new Rect(0, 0, 350, 350), mPaint);
//切换到黑色画布之前,就是绿色画布
canvas.restoreToCount(saveCount1);
mPaint.setColor(Color.CYAN);
//在绿色画布上再画一个矩形
canvas.drawRect(new Rect(0, 0, 300, 300), mPaint);
Toast.makeText(mContext, " canvas.getSaveCount()" + canvas.getSaveCount(), Toast.LENGTH_LONG).show();
}
android 自定义View之SVG
Android Canvas基础详解
讲解Canvas中的一些重要方法
gif制作