一、概述
本周学习计划就顺利的进行到了我们的画图阶段,在本周的学习过程中主要是对画图中的三个比较重要的 API 进行了学习,这三个 API 分别是 Canvas 、Paint 和 Path,其实学习这些内容的原因都是为了之后的自定义 view 做准备的,我们都知道我们的自定义 view 里面的东西都是需要通过我们自己去绘制的,所以下面我们简单介绍一下我们常用的 API 和我在学习中遇到的问题
二、Canvas
Canvas 相信做过 Android 开发的都应该不陌生,我们通常叫他画布,其实如果我们想在 view 上面画一些内容,归根结底都是要通过 Canvas 的。那在说 Canvas 之前,我首先要说的一个东西就是坐标系,我们都知道,我们如果想绘制内容在 view 首先要知道绘制的位置,那这个位置就是通过坐标系去制定的。
坐标系:
坐标系分为两种,一种是 Canvas 坐标系,一种是绘图坐标系。
Canvas 坐标系:view 的左上角为0,0 ,坐标原点向右为x的正半轴,原点的向下为 y 的正半轴
绘图坐标系:
Canvas的drawXXX方法中传入的各种坐标指的都是绘图坐标系中的坐标,而非Canvas坐标系中的坐标。默认情况下,绘图坐标系与Canvas坐标系完全重合,即初始状况下,绘图坐标系的坐标原点也在View的左上角,从原点向右为x轴正半轴,从原点向下为y轴正半轴。但不同于Canvas坐标系,绘图坐标系并不是一成不变的,可以通过调用Canvas的translate方法平移坐标系,可以通过Canvas的rotate方法旋转坐标系,还可以通过Canvas的scale方法缩放坐标系,而且需要注意的是,translate、rotate、scale的操作都是基于当前绘图坐标系的,而不是基于Canvas坐标系,一旦通过以上方法对坐标系进行了操作之后,当前绘图坐标系就变化了,以后绘图都是基于更新的绘图坐标系了。也就是说,真正对我们绘图有用的是绘图坐标系而非Canvas坐标系。
那坐标系说的差不多了,下面切入我们的正题 Canvas
相关API
这里我只说几个当时学习的时候不是很理解的 API,其他常用的 API 在 https://developer.android.google.cn/index.html 官方文档上面看一下,前提是你英文比较好。
canvas.save 表示的是我们画布的当前状态,这个状态包括旋转,平移等。
canvas.restore 表示的回到我们上个 saver 保存的状态
drawOval 绘制椭圆四个参数的意义如下图
drawCircle 绘制圆形 需要传递圆心坐标和 radius
drawArc 绘制椭圆弧:
钟表的3点位置对应着0度,如果传入的startAngle小于0或者大于等于360,那么用startAngle对360进行取模后作为起始绘制角度。
useCenter是个boolean值,如果为true,表示在绘制完弧之后,用椭圆的中心点连接弧上的起点和终点以闭合弧;如果值为false,表示在绘制完弧之后,弧的起点和终点直接连接,不经过椭圆的中心点。就会把起点和终点连接成一条直线
drawRoundRect 绘制圆角矩形
drawRect 绘制矩形有两个重写方法都是四个参数,意思分别是两个点与x轴的距离,两个点与 y 轴的距离
drawpoint 绘制点需要设置 paint 宽度
drawBitmap 绘制 bitmap
clipRect 剪切一个矩形区域
clipRegion 剪切执行区域
rotate 对画布进行旋转
scale 对画布进行缩放
skew 对画布进行倾斜
translate(float dx,float dy) 对画布进行平移用正负数却分上下左右
public void drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint)
该方法有两个功能:1.只绘制原有bitmap对象的一部分,2.还可以将要绘制的bitmap缩放到指定的区域。
1、只绘制原有bitmap对象的一部分
我们知道Bitmap是一个矩形,其是有宽度和高度的,也就说以bitmap对象本身作为坐标系(原点在bitmap左上角),我们可以构建一个Rect对象,如果满足left为0,top为0,right为bitmap的宽度,bottom为bitmap的高度,那么就说名我们要绘制整个Bitmap。但是有时候我们只想绘制Bitmap的一部分,例如我们上面的图中所示,我们想只绘制Android图像的头部区域怎么办呢?办法是我们构建一个Rect对象,定义我们要绘制Bitmap的哪些部位。
比如我们通过代码srcRect.bottom = (int)(0.33 * bitmap.getHeight())指定了我们只绘制bitmap对象头部1/3的位置,即Android图像的头部,这样我们用该指定的srcRect绘制bitmap时只绘制了其头部位置。需要特别注意的是,srcRect中left、top、right、bottom的值都是以Bitmap本身的局部坐标系为基础的。
2、将要绘制的bitmap缩放到指定的区域
有时候我们需要将原有的bitmap进行放大或缩小,如上图所示,我们将原有图片放大了,这怎么做呢?我们需要指定RectF类型的参数dstRectF,以便告诉Android将srcRect中定义的bitmap缩放到哪里。即Android会将srcRect中定义的bitmap缩放到dstRectF区域范围内。需要注意的是,此处的dstRecF是绘图坐标系中的坐标,不是Bitmap本身的局部坐标系。我们在代码中保证了dstRecF的长宽比与srcRect中的长宽比相同,这样不会导致图片长宽比例变形,效果见上图中的第二个放大的图形。
那在 Canvas 的基础中我就总结了以上的内容,我个人认为 Canvas 中的 API 都是比较依赖坐标系的,只要我们把坐标系弄明白了,那么我们对 Canvas 的使用就会很熟练了
三、Paint
我们都知道在 Canvas 的 API 中都需要传递一个 Paint 参数,那这个 Paint 参数作用就如它的名字,就是画笔的意思,那我们画出来的东西是什么颜色,是线条还是什么都是由这个 Paint 决定的,我们绘制的界面是否是色彩斑斓的是由 Paint 决定的,我们绘制的轮廓是由 Canvas 决定的,所以我们的 Paint 也是很重要的,下面看一下我在了解中遇到的一些不太理解的 API:
1、setAntiAlias 设置是否抗锯齿,如果你做过 UI 你会知道如果不设置这个把图片放大会有锯齿,如果设置了会显得平滑一些
2、setStrokeJoin 设置画笔转弯处的链接风格
3、setShadowLayer 设置 Paint 阴影,这个主要就是用在字体上面
4、setStrokeMiter(float miter) 设置连接处的倾斜度,如果你绘制三条线段,这个效果会凸显出来
5、setXfermode(Xfermode xfermode) 设置重叠方式
private Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
我们的 PorterDuff.Mode 系统给我们提供了 18 中,具体什么效果可以自行调试
PorterDuffXfermode:
用于实现新绘制的像素与Canvas上对应位置已有的像素按照混合规则进行颜色混合。
6、setShader(Shader shader)
//设置着色器,用来给图像着色的,绘制出各种渐变效果,有BitmapShader,ComposeShader,LinearGradient,RadialGradient,SweepGradient几种,通过这个可以绘制很多渐变的效果,常用的是我们的雷达扫描,或者一些渐变的 UI
7、setColorFilter(ColorFilter filter)
//设置画笔颜色过滤器,有ColorMatrixColorFilter,LightingColorFilter,PorterDuffColorFilter几种,这个以后再单独分析
8、measureText(char[] text, int index, int count),measureText(String text, int start, int end),measureText(String text),measureText(CharSequence text, int start, int end)//这
几个就是测量字体的长度了
9、setStrokeCap(Paint.Cap.BUTT);这个方法就是设置绘制的线的两端的形态,两端形态可以是圆形(ROUND),正方形(BUTT),
正方形()
Paint.Cap.BUTT
当用BUTT作为帽端时,所绘制的线段恰好在起点终点位置处戛然而止,两端是方形,上图中第一条加粗的线段就是用BUTT作为帽端绘制的。
Paint.Cap.ROUND
当用ROUND作为帽端时,所绘制的线段的两端端点会超出起点和终点一点距离,并且两端是圆形状,上图中第二条加粗的线段就是用ROUND作为帽端绘制的。
Paint.Cap.SQUARE
当用SQUARE作为帽端时,所绘制的线段的两端端点也会超出起点和终点一点距离,两端点的形状是方形,上图中最后一条加粗的线段就是用SQUARE作为帽端绘制的。
10、paint.setStyle 前面几个在设置之前我们都要考虑这个 API
Paint.Style.FILL、Paint.Style.STROKE和Paint.Style.FILL_AND_STROKE。
当style为FILL时,绘制是填充面,FILL是Paint默认的style;
当style为STROKE时,绘制的是图形的轮廓线;
当style为FILL_AND_STROKE时,同时绘制填充面和轮廓线,不过这种情况用的不多,因为填充面和轮廓线是用同一种颜色绘制的,区分不出轮廓线的效果。
四、path
我们都知道,在 Canvas 中有一个方法教 drawpath,里面有一个参数为 Path,那 Path 很简单,就是路径的意思,那 Canvas 就是按照 path 中的路径去绘制的,那这么说来 Path 就很好理解了,其实 Path 中的 API 和 Canvas 中的有些相似,无非就是 Canvas 是直接画上去了,Path 是把长方形,圆形等加入 Path ,这里就不做过多的说明了,下面看说一下 Path 的几个子类,ComposePathEffect,CornerPathEffect,DashPathEffect,DiscretePathEffect,PathDashPathEffect,SumPathEffect几种。
我们可以通过 new 的方法创建它的子类,这些子类的作用就是绘制的 Path 的效果,有可能是虚线或者其他线段,可以自行调试。
API
1、path.arcTo 绘制弧线
注意:绘制之后会回到原点,如果arcTo 绘制起点和终点不在同一个点,那么他会在起点和终点绘制一条直线,
不过我们可以的通过 moveTo 方法把我们的起点和终点重合,当用 arcTo 绘制之后,我们如果想从它绘制的终点开始,我们需要通过 MoveTo 方法移动到我们上次绘制的终点
2、close:关闭当前的轮廓如果当前的点和第一个点不在一起,那么将会绘制一条直线,也就是封闭线段
3、addPath:在我们的 path 中加入新的 path,里面的 xy 值是 path 的位移值
4、set:重制这个 path 的内容,参数中的 path 值设置到我们的 path 中不是添加是替换
5、offset:把 path 的内容偏移,面对的是整个路径
6、reset:清空 path 里面的全部内容,设置为 empty
判断方法:
7、isConvex:判断是否为凸面 。 //在 API 21 之后才能使用
8、isEmpty:判断 path 是否为空
9、isRect:判断是否为矩形
上面都是一些比较好理解的 API,下面说一下在 Path 中最感兴趣的东西,贝塞尔曲线,如果不了解贝塞尔曲线的请点击
这里,那 Path 中提供了方法来让我们绘制贝塞尔曲线
那我说一下贝塞尔曲线主要是做什么的:
一阶贝塞尔曲线就是一条直线没什么好说的
二阶贝塞尔曲线在我看来就是两个定点和一个数据点能确定一条曲线,通过改变这个数据点的位置,可以生成贝塞尔曲线的动画三阶贝塞尔曲线也是两个固定的点,但是数据点变成了两个,两个点都可以动,当数据点发生变化的时候,曲线也会发生变化,我们可以通过控制两个点的坐标去形成动画
三阶段贝塞尔曲线是做小球移动效果的,然后三阶曲线可以画出圆形三阶的c 的值为c = 0.552284749
二阶可以通过 quadTo 去绘制,三阶的可以通过 cubicTo 去绘制,如果你的数学功底非常好,那么你将会很容易的使用贝塞尔曲线,而且会非常容易的绘制出优美的曲线
总结:
今天的内容到这里就差不多了,说的东西都是一些大致的轮廓,其实每个点深入学习都会有很多内容去学习,所以让我们一起加油吧,本文如有什么错误欢迎指出,共同进步,谢谢