2020.8.20
一.引言
自定义控件分为自定义View和ViewGroup两种,一个控件的创建都会经历onMeasure、onLayout、onDraw三个阶段,但View和ViewGroup各有特色
- 自定义View
1.onMeasure
2.onDraw - 自定义ViewGroup
1.onMeasure
2.onLayout
对于View,不需要重写onLayout方法,ViewGroup不需要重写onDraw
二.以自定义View为例重写构造方法
此处以自定义View为例,首先我们需要创建一个类,让它继承View,并添加构造方法,通常添加一下三种构造方法
class testView:View {
//在代码中创建
constructor(context: Context):super(context){}
//在xml中创建
constructor(context: Context,attr: AttributeSet):super(context,attr){}
//需要使用到style
constructor(context: Context,attr: AttributeSet,style:Int):super(context,attr,style){}
}
三.重写onDraw进行绘制主体内容
按照流程,应先重写onMeasure方法,但本片文章主要介绍自定义绘制,onMeasure将在另外一篇文章中详写
绘画的4个基础内容
- Bitmap 图片
- Canvas 绘制类->可以理解成一块画板
提供了各种绘制方法 - Paint 画笔->设置画笔的样式
- Path 用于设置绘制的路径
1.绘制背景颜色
override fun onDraw(canvas: Canvas?) {
setBackgroundColor(Color.MAGENTA)
}
除了使用系统提供的颜色外,还可以使用自己手动创建的颜色
通过Color.parseColor("#B990E7")来实现
具体使用实例:setBackgroundColor(Color.parseColor("#B990E7"))
2.画圆
drawCircle(圆心x,圆心y,raduis,paint)
需要提前准备好圆心坐标以及半径和画笔
注:onDraw方法会被多次调用,所以尽量不要在这个方法里面创建对象(变量),最好设置为属性
- onSizeChanged()->此方法是在onMeasure之后调用,也就是说,我们可以在此获取控件的宽高,这点很重要,比如我们需要绘制一个图形,该图形的大小以空间的宽高为参考,所以我们在此方法中对图形绘制所需要的尺寸进行赋值
var cx = 0f
var cy = 0f
var radius = 0f
var mWidth = 0
var mHeight = 0
val mPaint = Paint().apply {
color = Color.MAGENTA
style = Paint.Style.STROKE
strokeWidth=3f
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
mWidth = measuredWidth
mHeight = measuredHeight
//获取半径大小
radius = if (mWidth>mHeight) mHeight/2f else mWidth/2f
//设置圆心
cx = mWidth/2f
cy = mHeight/2f
}
override fun onDraw(canvas: Canvas?) {
canvas?.drawCircle(cx,cy,radius,mPaint)
}
- 画笔-Paint
color 设置颜色
style 设置填充方式
Paint.Style.STROKE 描边
Paint.Style.FILL 全填充
Paint.Style.FILL_AND_STROKE 填充+描边
strokeWidth 设置画笔粗细(float类型) - 设置画笔颜色
1.除了使用系统提供的和前面写到的Color.parseColor("#B990E7")来设置颜色外,还可以使用setARGB()来设置,四个参数,A代表透明度(255不透明,0全透明),RGB即对应了红绿蓝三原色的深度,范围都是0-255
setARGB(100,175,202,253)
2.着色器shader
子类1:LinearGradient-线性渐变
起始点(x0,y0),终点(x1,y1),color0-color1渐变色
tile:Shader.TileMode->CLAMP 边缘色拉伸
REPEAT 重复
MIRROR 倒影
子类2:BitmapShader-用一张图片作为颜色进行绘制
子类3:RadialGradient-发射式
子类3:SweepGradient-扫射式
使用示例
shader=LinearGradient(
mWidth/2f,0f,mWidth/2f,mHeight.toFloat(),
Color.GREEN,Color.MAGENTA,Shader.TileMode.CLAMP
)
----------------------------------------------------------------------------------------
val img = BitmapFactory.decodeResource(resources,R.drawable.wiwi)
shader=BitmapShader(img,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP)
小贴士:将画笔颜色设置为图片搭配画圆可以实现圆形头像
3.drawBitmap
将一张图片绘制到控件中,这和上面的画笔以图片为颜色是不相同的,画笔以图片为颜色绘制是平铺的,不可设置位置,不够灵活,并且依赖于你选择绘制的形状,而drawBitmap是独立的元素将图片添加到控件中,各有各的好处
private val pic:Bitmap by lazy {
BitmapFactory.decodeResource(resources,R.drawable.alipay)
}
canvas?.drawBitmap(pic,0f,0f,mPaint)
通过设置左边和上边与控件的距离来控制绘制的区域
4.drawPath与drawLine
画一条线只需要起点和终点的坐标即可
drawLine(startX,startY,endX,endY,paint),但如果需要连续画线的话,drawLine就显得太笨拙了,让代码更加冗杂,此时就需要用到drawPath
- drawPath(path,paint)
绘制路径只需要设置path,并且path还可动态修改 - Path() 路径
moveTo(x,y) 移动到某一点,并作为下一条线的起点
lineTo(x,y) 画到某点,作为终点
5.绘制贝塞尔曲线
drawPath不仅可以画直线,还可画曲线,只需要设置路径即可
- quadTo(x1,y1,x2,y2)
(x1,y1)为控制点,(x2,y2)是终点,这是2阶贝塞尔,该方法同moveTo一样在path内部设置 -
cubicTo(x1,y1,x2,y2,x3,y3)
中间过程多一个控制点的坐标,就会多一个阶数
绘制曲线可以设计出很多新奇的图案,只需计算好控制点的坐标即可
6.画圆弧
画圆弧有两种方式,一是使用path路径中的arcTo,二是drawArc()
两者都需要确定绘制圆弧的矩形区域以及扫过的角度(圆弧对应的角度)
不同之处,arcTo在path内部,不需要设置画笔,圆心的确定使用moveTo,因此arcTo依赖于drawPath,而drawArc是独立的
可以看到drawArc画出来的效果更好,绘制圆弧有个前提,你给出的区域必须是一个正方形区域rect
- arcTo
val path = Path().apply {
moveTo(500f,400f)
val rect = RectF(100f,10f,900f,800f)
arcTo(rect,0f,-90f)
}
-
drawArc(left,top,right,bottom,startAngle,sweepAngle,是否使用中心点,paint)
下图是使用中心点的效果
可以根据自己的需求来设置是否使用中心点
同理画圆也可以通过path路径来设置
- addCircle(x,y,radius,方向)
CW 顺时针
CCW 逆时针
addCircle(600f,400f,100f,Path.Direction.CW)
7.画圆角矩形
为了给后面的小Demo做铺垫,这里介绍一下绘制圆角矩形
drawRoundRect(left,top,right,bottom,rx,ry)
这里重点说明rx,ry两个参数,它们是用来控制四角的弧度
小结:基本的绘制如上文所示,自定义动画是建立在自定义控件的基础上的,可以说自定义控件就是用来自定义动画,手动实现系统没有的控件以及动画效果,所以接下来会将两者融合详讲,并以项目来巩固知识点