view-Canvas

前言

Canvas的使用一般用于继承View的自定义View中,主要为我们提供了4类方法

  1. 第一是以drawXXX为主的绘制方法;
  2. 第二是以clipXXX为主的裁剪方法;
  3. 第三是以scale、skew、translate和rotate组成的Canvas变换方法;
  4. 最后一类则是以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);

  1. Paint.Join.ROUND
  2. Paint.Join.MITER
  3. Paint.Join.BEVEL

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

setStrokeCap(Cap cap)

注意同样长度下,线帽大小导致长度不一样

  1. Paint.Cap.SQUARE
  2. Paint.Cap.ROUND
  3. 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对象做相互运算,类似于:与、或、异或之类

  1. Path.Op.UNION
  2. Path.Op.DIFFERENCE
  3. Path.Op.INTERSECT
  4. Path.Op.REVERSE_DIFFERENCE
  5. 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制作

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值