Android自定义绘制1-1 Plus

Posted 呼啸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android自定义绘制1-1 Plus相关的知识,希望对你有一定的参考价值。

上一个文章是看着扔物线的视频写的。写玩之后,发现他的文章还有很多内容。尴尬。

接着写呗。

自定义绘制知识的4个级别

1.Canvas的drawXXX()系列方法以及Paint类的一些常见方法。

canvas的drawXXX()是自定义绘制的最基本操作。掌握了这些方法,才知道怎么绘制内容。例如,怎么画圆,怎么画方块,怎么画文字。这些内容再配合Paint的一些常见方法对绘制内容的颜色和风格进行简单配置,就能应付大部分绘制需求了。

2.Paint的完全攻略

Paint可以做的事,不仅仅是设置颜色,阴影,粗细。它可以做的风格真的非常多,非常细。比如双线性过滤,拐角形状,特效。

3.Canvas对绘制的辅助--范围裁切和几何变化。

大多数的时候,他们并不会被用到,但当被用到的时候,都是很酷的功能。

以后再也不要怕设计师设计多炫酷的功能了。

4.使用不同的绘制方法来控制绘制顺序。

 有人说,绘制顺序,我多整几个view给他弄出来,但这样通常都有性能问题。所以我们用绘制顺序解决的也就是性能问题。因为用绘制顺序,一个view就能搞定。

第一个系列的内容,canvas.drawXXX以及paint最基本的使用。

1.一切的开始onDraw()

drawXXX()系列的方法和Paint的基础掌握了,就能够应付简单的绘制需求。它们主要包括:

1.Canvas类下的所有draw打头的方法。例如drawCircle(),drawBitmap()

2.Pain类的几个常用方法,比如:

paint.style 绘制模式
paint.color 设置绘制颜色
paint.strokeWidth 设置线条宽度
paint.isAntiAlias 抗锯齿开关

Canvas.drawColor 颜色填充

这是最基本drawXXX方法,是在整个绘制区,统一涂上指定的颜色。

canvas.drawColor(resources.getColor(R.color.black))

这个就直接把绘制区变黑。

也可以通过透明度设置遮罩。

 类似的还有drawRGB和drawARGB,效果都是一样的。

这类颜色填充方法一般用于在绘制之前设置底色,或者在绘制之后设置半透明蒙版。

drawCircle(float cx, float cy, float radius, @NonNull Paint paint)

前面两个参数是圆心的坐标。第三个是圆的半径。他们共同构成了这个圆的基本信息。第四个参数是Paint.它提供除了基本信息以外的所有风格信息。

android中,每个View都有一个自己的坐标系。彼此之间是不影响的。坐标系的原点就是View左上角的那个点。所以当我们设置绘制这个圆的时候,比如我们设置圆中心的坐标是300,300,那么它就是这样的:

 如果你想画的圆不是实心的,而是空心的,可以这样:

paint.style = Paint.Style.STROKE

 这里style有三种模式,fill,stroke,fill_and_stroke。分别是填充,描边,填充和描边。

你在stroke和fill_and_stroke模式下,你还可以设置线条的宽度:

paint.strokeWidth = 20f

 在绘制的时候,为了让图形和文字的边缘更圆滑,往往需要开启抗锯齿:

paint.isAntiAlias = true

那么抗锯齿这么好,为什么不默认开启呢?

实质上,抗锯齿的发生是因为图形分辨率过低,导致人眼察觉出了画面中的像素颗粒而已。

那么,为什么开启抗锯齿,就看起来圆滑了呢?因为抗锯齿的原理是,修改图形边缘的颜色。而让图形看起来更加圆滑。

 可以看到,未开启抗锯齿的时候,所有像素的颜色同样都是黑色。而开启后,边缘的颜色被略微改变了。所以,抗锯齿,会造成图形的失真。

除了drawCircle,还可以画别的图形。

 另外,他还有另外两个重载方法,drawRect(RectF rect, Paint paint) 和 drawRect(Rect rect, Paint paint) ,可以让你直接填写rectF或者rect来绘制矩形。

canvas.drawPoint(50f,50f,paint)

再来看这个drawPoint,就是画点,x和y是点的坐标。点的大小可以通过strokeWidth来设置。

点的形状可以通过strokeCap来设置。

 strokeCap除了ROUND之外还可以用BUTT,和SQUARE

 有点像FILL模式下的drawCircle和drawRect。效果是一样的,看自己喜欢,用哪个。

那么为什么要搞两个API呢?因为drawPoint可以画多个点。

 这里使用drawPoints,可以传递一个数组,这个数组里每两个为一对坐标。

offset参数,是指在数组里跳过几个数字,后面的count就是绘制几个点数。

 paint.style = Paint.Style.FILL
        paint.strokeWidth = 20f
        canvas.drawOval(50f,50f,300f,200f,paint)

paint.style = Paint.Style.STROKE下:

另外,他还有一个重载方法,drawOval(rect:RectF,paint) 也可以使用RectF来绘制。

可以使用drawLine来绘制线:

canvas.drawLine(50f,50f,200f,500f,paint)

 因为这个直接不是封闭图形,所以style对他没有影响。

也可以通过drawLines绘制多个点。也就是drawLine的复制版。

val floatArray = floatArrayOf(20f,20f,50f,50f,100f,100f,120f,300f,200f,400f,200f,100f)
canvas.drawLines(floatArray,paint)

 接着,再来看drawRoundRect,也就是带圆角的矩形。

canvas.drawRoundRect(100f,100f,200f,200f,10f,10f,paint)

接着,再来看drawArc。这个是绘制弧形或者扇形的。

drawArc是使用一个椭圆来描述弧形的。

paint.style = Paint.Style.STROKE
canvas.drawArc(100f,100f,200f,200f,-100f,100f,true,paint)

 

首先,前面4个参数是椭圆的4个边界点的坐标,startAngle是弧形的起始角度。X轴的正向是0度的位置,逆时针为负角度,顺时针为正角度。sweepAngle是弧形划过的角度,注意这里的划过,是指在起始角度顺时针划。userCenter表示起始弧度和划过之后的终点位置是否连接中心点。如果连接了,那就是弧形。如果不连接,那就是扇形。

canvas.drawArc(100f,100f,200f,200f,-100f,100f,false,paint)

 

 我们再来看下fill模式下。

paint.style = Paint.Style.FILL
canvas.drawArc(100f,100f,200f,200f,-100f,100f,true,paint)

paint.style = Paint.Style.FILL
canvas.drawArc(100f,100f,200f,200f,-100f,100f,false,paint)

 

 以上就是cavans所有的简单图形绘制,除了简单图形绘制,还可以使用path绘制复杂的自定义图形。

 drawPath(Path path,Paint paint)

这个方法有点复杂的。

前面说到的方法都是绘制给定的图形。而drawPath可以绘制自定义的图形。像涂鸦板都是用这个方法去做。

这里的path就是描述路径对象。

比如绘制心形:

Path可以描述直线,二次曲线,三次曲线,圆,椭圆,矩形,弧形,圆角矩形,把这些图形结合起来,就可以绘制出很多复杂的图形。

Path有两类方法,一类是直接描述路径,另一类就是辅助的设置或者计算。

 Path的第一类方法:直接描述路径

这一类的方法还可以再细分为2组:添加子图和画线(直线或者曲线)

第一组:addXXx---添加子图形

addCircle(float x, float y, float radius, Direction dir)

添加圆

前三个参数是圆的基本信息,最后一个参数是圆的路径的方向。

不过对于普通情况,填逆时针或者顺时针。没有影响。我们一般需要选择填充图形或者描边。

并且在图形出现相交的时候,我们需要判断填充范围。比如:

 

在使用Path.addCircle新增一个圆之后,就可以使用canvas.drawPath来画出来。

path.addCircle(300f,300f,200f,Path.Direction.CW)
        canvas.drawPath(path,paint)

 除了addCircle,还有别的addOval,添加椭圆,addRect添加矩形,addRoundRect添加圆角矩形,addPath(path)添加另一个path.

上面这几个方法的用法都和addCircle差不多。

第二组方法:xxxTo()-----画线(直线或者曲线)

和第一组方法的区别是第一组是添加完整的封闭图形(addPath除外),这一组只是添加一条线。

lineTo(float x,float y)/ rLineTo(floatx, float y)画直线

从当前位置向目标位置画一条直线。x y 是目标位置的坐标。这两个方法的区别是lineTo的参数是绝对目标,而rLineTo的的参数是相对当前位置的相对坐标。前缀r的意思就是relative,相对的。

当前位置:所谓当前位置指的就是最后一次调用画Path的方法的终点位置。初始值为原点(0,0)

paint.style = Paint.Style.STROKE
        path.lineTo(100f,100f) //由当前位置(0,0)向(100,100)画一条直线
        path.rLineTo(100f,0f) //由当前位置(0,0)向正右方100像素的位置画一条直线
        canvas.drawPath(path,paint)

quadTo(float x1, float y1, float x2, float y2) / rQuadTo(float dx1, float dy1, float dx2, float dy2)画二次贝塞尔曲线

这条贝塞尔曲线的起点就是当前位置,而参数中的x1,y1,x2,y2分别是控制点和终点的坐标。

cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) / rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3)  画三次贝塞尔曲线

和上面二次贝塞尔曲线同理

moveTo(float x, float y)/ rMoveTo(float x, float y)移动到目标位置

不论是直线还是而塞尔曲线,都是以当前位置作为起点,而不能指定起点。但是咱们可以通过moveTo(x, y)或者rMoveTo(x,y)来改变当前位置,从而达到间接的设置这些方法的起点。

 paint.style = Paint.Style.STROKE
        path.lineTo(100f,100f) //由当前位置(0,0)向(100,100)画一条直线
        path.moveTo(200f,100f)//移动
        path.lineTo(200f,0f)//画竖线
        canvas.drawPath(path,paint)

 moveTo虽然不能添加图形,但是他可以设置起点。所以这是一个非常重要的辅助方法。

arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo) / arcTo(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean forceMoveTo) / arcTo(RectF oval, float startAngle, float sweepAngle)画弧形

这个方法和canvas.drawArc比起来。少了一个参数useCenter,但是多了一个参数,forceMoveTo.

为啥会这样呢?因为arcTo只用来画弧形,而不能用来画扇形。所以这个参数就没用了。

那么这个forceMoveTo又是啥意思

绘制是抬一下笔过去,还是拖着笔迹过去。区别在于是否留下移动的痕迹。

paint.style = Paint.Style.STROKE
        path.lineTo(100f,100f) //由当前位置(0,0)向(100,100)画一条直线
        path.arcTo(100f,100f,300f,300f,-90f,90f,true) //强制移动到弧形起点(无痕迹)
        canvas.drawPath(path,paint)

path.arcTo(100f,100f,300f,300f,-90f,90f,false) //留下痕迹

 

addArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle) / addArc(RectF oval, float startAngle, float sweepAngle)

这又是一个弧形方法。一个addArc,一个arcTo,区别在哪?

其实非常简单,这里少了一个参数 foreceMoveTo,默认为true了。

close()封闭当前子图形

什么是封闭呢,就是由当前位置向当前子图形的起点,画一条线。

先看一下没有封闭的:

 paint.style = Paint.Style.STROKE
        path.moveTo(100f,100f)
        path.lineTo(200f,100f) //由当前位置(0,0)向(100,100)画一条直线
        path.lineTo(150f,150f)

如果这个时候我们封闭呢?

paint.style = Paint.Style.STROKE
        path.moveTo(100f,100f)
        path.lineTo(200f,100f) //由当前位置(0,0)向(100,100)画一条直线
        path.lineTo(150f,150f)
        path.close()

 

 大家可能会想到我这个时候,使用lineTo起点也是可以办到的,是的,这个时候和Lineto是完全等价的。

所谓子图形,就是一次不间断的连线。

另外,当style为fill,或者为fill_and_stroke的时候,会自动连线。

Path方法第二类:辅助的设置或计算。

这类方法的使用场景比较少。不必学太多。只学期中一个比较常用的:

setFillType(FillType fillType)

前面有说过,path.setFillType是用来设置图形自相交时的填充算法的。

FillType的取值一共有4个:

1.EVEN_ODD

2.WINDING 默认值

3.INVERSE_EVEN_ODD

4.INVERSE_WINDING

两个前面带有INVERSE前缀的, 是1和2的反色版本。

WINDING是全填充,EVEN_ODD是交叉填充。

这个是简单粗暴版本,也就是通常情况下的。

drawBitmap(Bitmap bitmap, float left, float top, Paint paint)画Bitmap

绘制Bitmap对象,期中left,top是要把bitmap要绘制到的位置坐标。

drawText(String text, float x, float y, Paint paint) 绘制文字

界面里所有显示的内容都是绘制出来的,包括文字。drawText这个方法就是绘制文字的。

参数text就是用来绘制的字符串,x,y是绘制的起点坐标。

以上是关于Android自定义绘制1-1 Plus的主要内容,如果未能解决你的问题,请参考以下文章

Android自定义View实现可拖拽的进度条

Android轮盘控件-自定义

Android自定义View

Android自定义View

Android 自定义View绘制汉堡菜单

android自定义View: 饼状图绘制