更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680
本篇文章将从自定义View利器Canvas和Paint来进行详解
一、Canvas
为了后文更为方便的讲解Canvas的常用方法的使用,我们先来做一些准备工作,创建一个自定义View框架,先初始化一下Paint画笔,并设置相关方法:
1.1 绘制圆弧和扇形
Canvas提供drawArc()方法,通过传递不同的参数可用来绘制圆弧和扇形,此方法有两个重载方法,详细参数如下:
- drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
- left:扇形或圆弧所占区域的左边界线x坐标
- top:扇形或圆弧所占区域的上边界线y坐标
- right:右边界线x坐标
- bottom:下边界线y坐标
- startAngle:扇形或圆弧的起始角度
- sweepAngle:扫过的角度
- userCenter:此参数可以理解为true就是画扇形,false就是画圆弧
- paint:画笔
- drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
此方法第一个参数是一个RectF类,也是边界,就是把一个方法的left,top,right,bottom封装到了RectF类中,剩余参数与上一个方法一致。
接下来用着两个重载方法分别绘制两个90°的扇形和两个90°的圆弧:
绘制效果如下图所示,另外需要说明的一点是,drawArc的第五个参数startAngle中的角度,0°是指坐标系中第四象限中与x重合的角度,顺时针方向代表角度增大的方向,如下图中红色线条所示。
1.2 绘制Bitmap
在Canvas中提供了drawBitmap方法,此方法可以让我们直接获取一张图片绘制到画布上,有了它可以让我们的自定义View锦上添花,同时也让我们实现一些复杂效果有了一个更加方便的途径。下面是drawBitmap的几个比较常用的重载方法:
- drawBitmap(Bitmap bitmap, float left, float top, Paint paint)
- bitmap:Bitmap资源文件
- left和top:代表了图片左上角落入的位置坐标。
- top:看2
- paint:画笔
- drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
- src:在Bitmap图片上截取一部分作为绘制源,可null
- det:将绘制目标拉伸平铺到det指定的矩形中
-
drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
同第二个重载方法,几乎一毛一样。 -
drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)
- matrix:Matrix的参数传入是的drawBitmap功能变得异常强大,让此方法有意思了许多,通过matrix可以实现图片的平移(postTranslate())、缩放(postScale())、旋转(postRotate())、错切(postSkew())等等花式炫酷效果,由于Matrix的用法稍微多一些,篇幅限制,这里就先一带而过了,感兴趣的朋友可以自行探索。
在onDraw方法中drawBitmap的以上重载方法,注意在使用完Bitmap之后记得用Bitmap.recycle()来回收掉资源,以防止oom。
效果如下图(红框内)。
1.4 绘制圆形
- drawCircle(float cx, float cy, float radius, Paint paint)
- cx:圆心x坐标
- cy:圆心y坐标
- radius:半径
效果如下图:
1.5 绘制点
- drawPoint(float x, float y, Paint paint)
- x:点的x坐标
- y:点的y坐标
- drawPoints(float[] pts, Paint paint) 绘制一组点
- pts:float数组,两位为一组,两两结合代表x、y坐标,例如:pts[0]、pts[1]代表第一个点的x、y坐标,pts[2]、pts[3]代表第二个点的x、y坐标,依次类推。
- drawPoints(float[] pts, int offset, int count, Paint paint) 绘制一组点
- pts:float数组,两位为一组,两两结合代表x、y坐标,例如:pts[0]、pts[1]代表第一个点的x、y坐标,pts[2]、pts[3]代表第二个点的x、y坐标,依次类推。
- offset:代表数组开始跳过几个只开始绘制点,注意这里不是指数组的下标,而是代表跳过几个值。
- count:在跳过offset个值后,处理几个值,注意这里的count不是代表点的个数,而是代表数组中值的个数。
效果如下图:
1.6 绘制椭圆
- drawOval(float left, float top, float right, float bottom, Paint paint)
- left
- top
- right
- bottom
在left、top、right、bottom围成的区域内绘制一个椭圆。
- drawOval(RectF oval, Paint paint)
- 将第一个重载方法的left、top、right、bottom封装到RectF类中,与扇形的重载方法异曲同工。
效果如下图:
1.7 绘制矩形
- drawRect(float left, float top, float right, float bottom, Paint paint)
- drawRect(Rect r, Paint paint)
- drawRect(RectF rect, Paint paint)
drawRect的参数非常好理解,这里就不啰嗦了,直接上代码看效果:
注:这里的rectF2即上文绘制椭圆时创建的RectF对象。
1.8 绘制圆角矩形
- drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)
- drawRoundRect(RectF rect, float rx, float ry, Paint paint)
drawRoundRect是绘制圆角矩形,用法和drawRect类似,唯一不同的是多了两个参数:
- rx:x轴方向的圆角弧度
- ry:y轴方向的圆角弧度
上代码,看效果:
这里为了突出两个方向的圆角弧度,特地将rx和ry设置差距比较大,效果如下图:
1.9 绘制直线
- drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
- drawLines(float[] pts, int offset, int count, Paint paint)
- drawLines(float[] pts, Paint paint)
drawLine和drawLines一个是绘制一个点,一个是绘制一组点,其中drawLines中的float数组中四个值为一组点,其用法可以参照drawPoints。
效果如下图:
1.10 drawPath() 绘制不规则图形
上面的这些Canvas方法固然已经很强大了,但是我们如果想要绘制一些不规则的图形怎么办,这时候就要用到强大的drawPath()方法了,通过对Path进行设置不同的坐标、添加不同图形,最后传入drawPath方法中可以绘制出复杂的且不规则的形状。以下是drawPath的方法及参数:
- drawPath(Path path, Paint paint)
这里的关键参数就是Path,Path类的方法较多,大部分用法类似,这里挑几个说一下:
- Path类
- addArc(RectF oval, float startAngle, float sweepAngle) 往path里面添加一个圆弧
- addCircle(float x, float y, float radius, Path.Direction dir) 添加一个圆形
- addOval(RectF oval, Path.Direction dir) 添加一个椭圆
- addRect(RectF rect, Path.Direction dir) 添加一个矩形
- lineTo(float x, float y) 连线到坐标(x,y)
- moveTo(float x, float y) 将path绘制点移动到坐标(x,y)
- close() 用直线闭合图形,调用此方法后,path会将最后一处点与起始用直线连接起来,path起始点为moveTo()方法的坐标上,如果没有调用moveTo()起始点将默认为(0,0)坐标。
- ...
接下来使用drawPath绘制一个楼梯:
效果如下图:
再用drawPath方法绘制一个Android小机器人:
效果图如下:
最后,上文中Canvas示例的全部代码如下:
完整效果图如下:
其实Canvas除了可以绘制图形之外,还可以绘制文字,Canvas的绘制文字的方法有drawText()、drawTextOnPath()、drawTextRun()等方法,在绘制文字是和Paint的结合更为紧密,所以讲绘制文字的方法放在下文和Paint一起讲可能效果会更好一些,好了,废话不多说了,接下来咱们就开始Paint的篇章。
二、 Paint
为了更为清晰的讲解Paint的用法,先来新建一个自定义类,暂叫PaintStudyView,接下来创建一个它的大体骨架,在此类中定义了一些变量,变量的意义请见注释:
2.1 Canvas的绘制文字的相关方法:
2.1.1drawText()的重载方法
drawText() 是Canvas的绘制文字中的最长用的方法,它只能按照从左至右的普通方式来绘制文字。
- drawText(String text, float x, float y, Paint paint)
- text:待绘制的文字内容
- x:文字绘制位置的x坐标
- y:文字绘制位置的y坐标
- paint:Paint画笔,可以通过Paint.setTextAlign()来决定文字的方位,有:Paint.Align.LEFT(居左),Paint.Align.RIGHT(居右),Paint.Align.CENTER(居中)三个位置。
- drawText(String text, int start, int end, float x, float y, Paint paint)
- start:代表从text中的第几个字符开始截取绘制,包含第start个字符。
- end:代表截取到text的第几个字符,不包含第end个字符。
例如:我是一个自定义View的控件,start=1,end=6,截取后为:是一个自定
下面两个重载方法可以参考第二个很容易就能理解:
- drawText(CharSequence text, int start, int end, float x, float y, Paint paint)
- drawText(char[] text, int index, int count, float x, float y, Paint paint)
以下示例说明了文字的不同位置,同时也说明了第二个和第四个重载方法对字符串截取时的用法:
效果图如下:
其中的红点为额外添加的参考坐标,目的是为了突出setTextAlign中参数的位置。
2.1.2drawTextOnPath()的重载方法
drawTextOnPath() 由方法名字我们就可以看出来他可以按照Path的走向来绘制文字,例如我们在path中传入一个圆弧,那么绘制出来的文字走向就是圆弧状的,是不是很酷,来看一下它的重载方法:
- drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint)
- text:同drawText的第一个参数。
- path:Path参数,用法在前文已经说过了。
- hOffset:水平方向的偏移量。
- vOffset:垂直方向的偏移量。
关键点:有一点一定要提的就是,这里的hOffset是相对于path路径的水平偏移量,而vOffset也是相对于path路径的垂直偏移量,这么说可能还有点不清楚,结合下面的示例来说明,请仔细体会这里的意思:
如下是效果图,注意看图片中的红色部分,红色的线是用代码绘制出来的path参考线,红色的箭头是path的水平和垂直方向的走向,结合下图可以更好的理解drawTextOnPath的hOffset和vOffset参数。
- drawTextOnPath(char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint)
这个方法的套路想必不用解释了。
2.1.3drawTextRun()的重载方法
- drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, float x, float y, boolean isRtl, Paint paint)
- drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint paint)
drawTextRun()可以文字的是从左到右还是从右到左的顺序来绘制,其中倒数第二个参数isRtl就是用来控制方向的,true就是倒序绘制,false就是正序绘制,其他的参数就没啥好说的了,这个方法用法比较简单,这里就不贴代码了。另外这个方法是在API 23才开始添加的,使用时要注意。
到目前为止,Canvas的常用用法基本介绍完了,接下来就可以着重来看Paint的使用了,Paint和Canvas两者是不可分离的,两者协作,相辅相成。所以在下面的用法示例中不免要用到Canvas的相关方法。
2.2 使用Paint测量文字的尺寸,定位文字
我们在开发自定义控件时,免不了要精确定位文字的文字,例如必须把文字放在某个区域的正中间,或者必须让一行文字的几何中心精确的处于某个点上,这时我们如果不懂这里的窍门可能就要盲目的试位置了,这样一点一点试出来的位置很不可靠,可能换个屏幕尺寸位置就不对了,接下来怎么来看看怎么样用最优雅的姿势来精确的定位文字。
其实在水平方向的定位还比较好说,直接使用Paint.setTextAlign()就能搞定大多需求,主要是在水平方向上稍稍复杂一点,想要定位位置,首先需要先获取文字的高度,要用到Paint的以下两个方法:
- float ascent():根据文字大小获取文字顶端到文字基线的距离(返回的是负值)
- float descent():根据文字大小获取文字底部到文字基线的距离(返回的事正值)
有了这两个方法那就非常好办了,首先用代码结合效果图说明一下基线、ascent、descent和文字的关系:
效果图如下,它们之间的关系注意看图片里面的说明。(注:在这里感谢园友在截图中指出的一处错误,现已修正)
接下来就让文字的中心落在参考点上:
效果图如下,仔细看参考点(红点)和文字的位置:
2.3利用Paint.setShader()(着色器)绘制渐变色
使用 setShader() 方法可以添加渐变颜色也可以使用图片作为背景,其参数是一个Shader类,传入不同的Shader子类可以实现不同的渐变效果或者添加背景图片,其子类有一下几种:
- LinearGradient:线性渐变
- RadialGradient:放射状渐变
- SweepGradient:扫描渐变
- BitmapShader:添加背景图片
- ComposeShader:多种Shader组合
上面接个Shader的子类在使用方式上都差不多,这里只用LinearGradient为例说明一下,并注意对LinearGradient构造器的最后一个参数传入不同的参数对应的效果图:
效果图如下:
除了以上这些,Paint的用法还有很多很多,一时半会也列不完,博主在这也只能抛砖引玉,感兴趣的朋友可查看官方API自行探索。
更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680
原文链接 https://www.cnblogs.com/codingblock/p/8227598.html