自定义View绘图篇(一)-Canvas

前言

View的绘制相当于我们平时根据需求去画出各种图案,而Canvas 和 Paint 就像我们平时画画需要的画笔和画纸一样,也就是我们的 Paint(画笔)和 Canvas(画纸,通常称为画布),所以凡是跟要画的东西设置相关的,比如颜色、大小、宽度、样式、透明度等都是在 Paint 中设置的。而凡是跟要画的成品,比如想画一个矩形、圆形、文字、路径等都是通过 Canvas 操作的。

一、基本方法

Canvas我们可以看做一个坐标系,坐标系是左正下正,因此坐标中心就是画布左上角

使用方法:初始化画笔,重写 View 的 onDraw 方法,在 onDraw 方法中通过 Canvas 来实现我们想要实现的效果,最后在布局中使用:

public class CustomView extends View {

    private Paint paint;

    public CustomView(Context context) {
        this(context, null);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void init() {
        //初始化画笔
        paint = new Paint();
        //设置抗锯齿
        paint.setAntiAlias(true);
        //设置画笔宽度
        paint.setStrokeWidth(5);
        //设置画笔颜色
        paint.setColor(Color.RED);
        //设置画笔样式
        paint.setStyle(Paint.Style.STROKE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }
}

1、画圆

参数:

  • cx:圆心点的 X 轴坐标
  • cy:圆心点的 Y 轴坐标
  • radius:圆的半径
  • paint:画笔

2、画矩形

参数:

  • RectF:矩形辅助类

    常用构造函数RectF(float left, float top, float right, float bottom),即由四个点构造一个矩形

  • left、top、right、bottom顾名思义即距离左边、上边、右边、下边的距离。

canvas.drawRect(new RectF(50,60,200,200),paint);

3、画圆角矩形

drawRoundRect (RectF rect, float rx, float ry, Paint paint) 
drawRoundRect (float left, float top, float right, float bottom, float rx, float ry, Paint paint)

参数:

  • rect:要画的矩形
  • rx:生成圆角的椭圆的 X 轴半径
  • ry:生成圆角的椭圆的 Y 轴半径

4、画椭圆

5、画圆弧

drawArc (RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter, Paint paint)

参数:

  • RectF oval:生成圆弧的矩形
  • float startAngle :圆弧开始的角度,以 X 轴正方向为 0 度 ,顺时针为正角度
  • float sweepAngle:圆弧持续的角度
  • boolean useCenter:是否显示弧的两边,true,显示,false 不显示,只有一条弧线
paint.setColor(Color.RED);
RectF rectF=new RectF(100, 400, 300, 600);
canvas.drawRect(rectF, paint);
paint.setColor(Color.BLUE);
canvas.drawArc(rectF, 0, 90, false, paint);

paint.setColor(Color.RED);
RectF rectF1=new RectF(350, 400, 500, 600);
canvas.drawRect(rectF1, paint);
paint.setColor(Color.BLUE);
canvas.drawArc(rectF1, 0, 90, true, paint);

6、画直线

参数:

  • float startX:开始点 X 坐标
  • float startY:开始点 Y 坐标
  • float stopX:结束点 X 坐标
  • float stopY:结束点 Y 坐标
  • pts:坐标数组,每四个为一组,不够则舍弃
  • offset:要跳过坐标点数组中几个值才开始画(必须是4的倍数)
  • count:至少为2,offset 之后数组的大小。

7、画图像

参数:

  • bitmap:要画在画布上的位图
  • matrix:构建的矩阵作用于将要画出的位图【后面整理】
  • src:可为 null,表示画整个位图,否则只花出位图的一块矩形区域图
  • dst:定义的一个矩形范围,位图会平移或缩放来将自身放入矩形内
Rect src = new Rect(20, 20, 40, 40);//取bitmap上src区域的部分图像
Rect dst = new Rect(100, 100, 200, 200);//填满矩形
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
   canvas.drawBitmap(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher),src,dst,mPaint);

8、画背景

drawColor(int color, PorterDuff.Mode mode) ;
drawRGB(int r, int g, int b);

9、画点

drawPoint(s)

10、画文字

绘制出文本的基本样式(粗细、大小、颜色、下划线等),可以查看Paint的一些属性

11、画路径

重点方法

  • moveTo(float x1,float y1):直线的开始点,即将直线路径的绘制点定在(x1,y1)的位置;
  • lineTo(float x2,float y2):直线的结束点,又是下一次绘制路径的开始点,lineTo() 可以一直调用
  • close():调用 close() 方法会自动将路径的首尾连接起来,形成闭环。

11.1、基本用法

Path path=new Path();
path.moveTo(100, 200);
path.lineTo(200, 200);
path.lineTo(100, 400);
path.lineTo(0, 400);
path.close();
canvas.drawPath(path, paint);

11.2、图形路径

创建不同的图形路径。


参数:

  • dir:Direction 由2个取值(Path.Direction.CCW:创建逆时针方向的矩形路径;Path.Direction.CW:创建顺时针方向的矩形路径)
 private void init() {
        //初始化画笔
        paint = new Paint();
        paint.setTextSize(30);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(3);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        String text = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
        Path CCWPath = new Path();
        RectF rect = new RectF(100, 200, 350, 400);
        CCWPath.addRect(rect, Path.Direction.CCW);
        canvas.drawPath(CCWPath, paint);
        canvas.drawTextOnPath(text, CCWPath, 0, 0, paint);

        Path CWPath = new Path();
        RectF rect1 = new RectF(400, 200, 650, 400);
        CWPath.addRect(rect1, Path.Direction.CW);
        canvas.drawPath(CWPath, paint);
        canvas.drawTextOnPath(text, CWPath, 0, 0, paint);
    }

ps:贝塞尔曲线后面具体整理

二、图形裁切与变换

常用方法

  • Canvas.save();保存状态
  • Canvas.restore();恢复保存状态

1、图形裁切

1.1、clipRect()

canvas.clipRect(left, top, right, bottom);  
canvas.drawBitmap(bitmap, x, y, paint);  

示例:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.city, null);
canvas.save();
canvas.clipRect(50, 150, 500, 400);
canvas.drawBitmap(bitmap,0, 0, paint);
canvas.restore();

1.2、clipPath()

canvas.clipPath(path);
clipPath(Path path, Region.Op op);//不支持硬件加速

参数:

  • op:
    DIFFRENCE是第一次不同于第二次的部分显示A-B
    REPLAC是显示第二次的B
    REVERSE_DIFFRENCE是第二次不同于第一次的部分显示
    INTERSECT是交集显示
    UNION是全部显示A+B
    XOR是补集(全集减去交集剩余部分)显示

示例:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.city, null);
canvas.save();
Path path=new Path();
path.addCircle(300,300,100,Path.Direction.CCW);
canvas.clipPath(path);
canvas.drawBitmap(bitmap,0, 0, paint);
canvas.restore();

2、变换

2.1、Canvas二维变换

canvas内部还是由Matrix 实现

注意:Canvas的几何变换是倒序的,即操作顺序与代码相反

  • 平移

    Canvas.translate(float dx, float dy)

    参数:

    • dx:横向位移距离
    • dy:纵行位移距离
  • 旋转

    Canvas.rotate(float degrees, float px, float py)

    参数:

    • degrees:旋转角度,方向是顺时针为正向
    • px:轴心x坐标
    • py:轴心y坐标
  • 缩放

    Canvas.scale(float sx, float sy, float px, float py)

    参数:

    • sx:横向缩放倍数
    • sy:纵向缩放倍数
    • px:缩放轴心x坐标
    • py:缩放轴心y坐标
  • 错切

    skew(float sx, float sy)

    参数:

    • sx:x 方向错切系数
    • sy: y 方向错切系数
    引自百度

2.2、Matrix 二维变换

前面说到canvas内部还是由Matrix 实现,因此Matrix 也可以实现上述的变换,所不同的是:Matrix可以自定义变换顺序,自定义变换规则

逆序变换:preTranslate()、preRotate()、preScale()、preSkew()

正序变换:postTranslate()、postRotate()、postScale()、postSkew()

2.3、Camera三维变换

Camera 的三维变换有三类:旋转、平移、移动相机,坐标系如下图,通过camera将物体进行投影,实现三维变换。

常用操作:

  • 旋转:rotateX(deg) rotateY(deg) rotateZ(deg) rotate(x, y, z)
  • 移动:translate(float x, float y, float z)
  • 设置虚拟相机的位置:setLocation(x, y, z)---->移动相机可以实现图片的放大缩小

注意:三维旋转是按照中心点旋转,为了方便操作,我们可以通过通过canvas的translate方法,将画布移动至原点进行操作,然后在移动回去,记得顺序是反的,因为canvas是逆序操作的。

最后:简单实现下面简单的效果,github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容