由上边三篇文章,我们可以对自我绘制有个大概的思路:
第一步:开发者确定使用者在用包裹内容时候的默认View的整体大小
第二步:把这个默认的宽高在onMeasure中告诉父布局
第三步:为了防止使用者空间不够,需要在onSizeChanged方法中去矫正可以绘制的宽高。或者不去矫正,而是计算一个缩放比例,通过canvas.scale(scal, scal)去整体缩放绘制。怎么方便怎么来
第四步:分析需要绘制的内部所有元素需要用哪些绘制方法去绘制,开发者设置每个元素的默认大小,然后也去矫正一下,根据矫正后的整体大小和每个元素的大小,计算出每个元素的位置坐标(这里的计算全部以控件左上为坐标原点),然后绘制一个静态的效果。
以上四步:开发者确定了整体View的大小,内部元素的大小,通过这些确定的值,算出来内部元素的坐标。并且通过绘制方法绘制了静态的View。
第五步:添加属性动画(本质就是对值改变的操作),然后根据这些值的变化去实现动态绘制内部元素实现动画。(这里的动画是对View内部元素做改变)
到此一个完美的自定义View出来了!
当你看到需要绘制一个复杂的View的时候,很可能一开始没有思路,但是按照上边的步骤走,就可以快速定位到不会的地方,然后准确击破,完成自定义。
二绘制贝塞尔曲线
按照步骤来
步骤1:确定默认大小:
defult_Widht = 屏幕的宽度
defult_Height= 随便写,这里给150dp
步骤2:把这个宽高告诉父控件
setMeasuredDimension(resolveSize(defult_Widht.toInt(), widthMeasureSpec), resolveSize(defult_Height.toInt(), heightMeasureSpec))复制代码
步骤3:获取真正能绘制的区域
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { //去矫正宽度和高度 defult_Widht = w.toFloat() defult_Height = h.toFloat()}复制代码
步骤4:看绘制效果,分析元素的使用的绘制方法
这里只有一个元素,绘制曲线,填充即可,绘制类似这种曲线我们可以使用sin cos,或者贝塞尔曲线。这里线确定用去画。
看完对贝塞尔曲线原理文章的讲解之后我们知道我们需要计算起始点,控制点和终点,并且google给我们提供了绘制贝塞尔曲线的方法。
quadTo(float x1, float y1, float x2, float y2)
这条二次贝塞尔曲线的起点就是当前位置,而参数中的 x1, y1 和 x2, y2 则分别是控制点和终点的坐标。
现在就开始计算点。观察需要两个波形是从中心点去改变的,画个坐标图
蓝色的点坐标很轻易的算出来,分别是:
//起始点var startX1 = 0Fvar startY1 = defult_Height/2//控制点var controlX1 = defult_Widht/4var controlY1 = 0F//结束点,也是第二个曲线的起点var endX1 = defult_Widht/2var endY1 = defult_Height/2//控制点2var controlX2 = defult_Widht*3/4var controlY2 =defult_Height//结束点2var endX2 = defult_Widhtvar endY2= defult_Height/2复制代码
这是和什么鬼?为什么后半部分没有凹陷下去?
圆心是你画笔用的填充模式,只是填充的方向不对而已,我们可以把下半部分做成一个封闭的path,它就会填充下边
mWavePath.lineTo(width.toFloat(), height.toFloat())mWavePath.lineTo(0F,defult_Height)复制代码
步骤5:添加动画,让它动起来
改变哪些值能让他动起来呢?上边的曲线上的点,但是曲线上的路径我只给了系统关键点坐标,想要改变也不可能。
换个思维想一下,我们再屏幕外边也去绘制一个一模一样的一个,然后平移一次是一个动画周期怎么样?我觉得可以,那就试试。
类似这样的坐标图:
然后又是一些简单的计算:
注意
这里坐标原点的位置不变,屏幕的交点为坐标原点
每次绘制都需要吧path恢复到初始状态
源码地址: