封面预览
前言
使用百度贴吧客户端的时候发发现加载的小动画挺有意思的,于是自己动手写写看。想学习自定义View以及自定义动画的小伙伴一定不要错过哦。
读者朋友需要有最基本的canvas绘图功底,比如画笔Paint的简单使用、Path如何画直线等简单的操作,不熟悉也没关系,下文带大家撸代码的时候会简单的讲一下。
此篇文章用到如下知识点:
好了,开始正文!
一、准备工作
1、效果图
loading小球
2、动画拆解
直观的看我们要实现三个方面
1)、波浪动画(蓝色部分)
2)、不规则的文字(白色的半个“贴”字)
3)、控件显示部分限制成圆形
3、技术分析
1)、波浪动画
要实现波浪动画,首先要绘制出波浪的形状,其次再让他动起来。波浪线看起来有点像是正弦或者余弦函数,但是Android的Path并没有提供绘制正余弦图形的函数,但是提供了一个功能更强大的曲线——贝塞尔曲线,贝塞尔曲线分为二阶、三阶及多阶,本案例里使用的是二次贝塞尔曲线,如下图所示,二阶贝塞尔曲线需要三个点才可以确定
二阶贝塞尔曲线
我们来看一下Android里贝塞尔曲线的源码:
- /* @param x1 The x-coordinate of the control point on a quadratic curve
- * @param y1 The y-coordinate of the control point on a quadratic curve
- * @param x2 The x-coordinate of the end on a quadratic curve
- * @param y2 The y-coordinate of the end point on a quadratic curve
- */
- public void quadTo(float x1, float y1, float x2, float y2) {
- isSimplePath = false;
- native_quadTo(mNativePath, x1, y1, x2, y2);
- }
由注解可以看出来quadTo(float x1, float y1, float x2, float y2)的四个参数分别是控制点的x,y坐标,结束点的x,y坐标,少了一个开始点呀!不要着急,开始点是Path路径的上一次结束的点,如果你的Path没有绘制过路径,那么Path的最后一个点坐标就是(0,0)如果想自己定义起始点位置,就用Path.moveTo(float x, float y)即可。
但是每次都需要指定具体的控制点和结束点既麻烦又容易出错,那么就需要rQuadTo(float dx1, float dy1, float dx2, float dy2)出马了,rQuadTo跟quadTo的区别在于rQuadTo使用的是相对起始点的坐标,而不是具体的坐标点,举个例子,如下代码效果等价:
- //使用quadTo
- Path path=new Path();
- path.moveTo(100,100);
- path.quadTo(150,0,200,100);
-
- //使用rQuadTo
- Path path=new Path();
- path.moveTo(100,100);
- path.rQuadTo(50,-100,100,0);
此时画笔最后的落点都为(200,100)。
画波浪线的技术难点解决了那么如何让波浪动起来呢,想动起来肯定需要波浪在水平方向移动,那么我们需要画一个很长很长的波浪让他移动,这样就实现了上下起伏效果,但是这样需要画无数多条贝塞尔曲线,肯定不行,这时就用到万能的数学理论——周期函数了,如果我们绘制两个周期的贝塞尔曲线,每次只让它显示一个周期,然后等第二周期显示结束的时候再从头开始,这样就造成了无限周期的假象,如下图
初始位置为1,向右前进,当走到2位置的时候重置成3的位置,即1原始的位置,如此往复就成了绵绵不绝的波浪了
绵延原理
做成效果如下:黄色区域就是要显示的区域,蓝色竖线是波浪线两个周期的总长度
连绵不绝的波浪线
2)、不规则的文字
我们可以看到圆球里的“贴”字在波浪区域显示的是白色,波浪区域之外显示的是蓝色,Android并不支持给文字部分区域着色的功能,那么我们只能靠控制显示区域让文字只显示特定形状,强大的Canvas正好有画布裁剪功能,通过裁剪画布就能控制绘制区域,画布的裁剪可以用Canvas.clipPath(Path path)实现,传入一个闭合的Path既可以随心所欲裁剪画布,裁剪示意图如下
裁剪文字
利用波浪形闭合路径讲画布裁剪成波浪形,那么在此接下来的Canvas绘制操的内容只能在这个波浪形区域里显示,这样就解决了文字的部分区域显示问题。那么接下来我们只用在相同位置绘制相同字体、字号不同色的文字即可实现一个文字显示两种颜色了(注意:实际操作的时候,被裁剪的文字要盖在未被裁减的文字的上边,即先在画布裁剪之前绘制蓝色的“贴”字,然后再裁剪画布再在裁剪后的画布上绘制白色的“贴”)
3)、控件显示部分限制成圆形
经过2)的分析,将显示部分限制在圆形区域里不是易如反掌吗,使用一个圆形的Path裁剪画布即可。感兴趣的同学也可以尝试BitmapShader或者Xfermode来将显示区域变成圆形
好了,最主要的步骤都分析完了,上一张图更直观地展示一下绘制流程
整体分析图
图中可以看出波浪形的闭合Path有两个作用,一个是负责裁剪画布,一个是负责绘制蓝色,其实只用第一个功能即可,此处只是方便分解步骤。
二、代码实现
文章只贴出主要代码,完整代码文末提供链接
既然是自定义控件,那就要有通用性比如下边的效果:
各种颜色的球球
loading小球需文字和颜色都可以改变,所以我们要给自己的控件添加这两个属性。首先在“res/values/”路径下新建一个attrs.xml文件,在里边定义如下属性:
- <declare-styleable name="Wave">
- <attr name="color" format="color"/>
- <attr name="text" format="string"/>
- </declare-styleable>
接下来开始自定义View
复写三个构造函数,将单参数和双参数的构造函数的super方法都改为this,保证无论调用哪个构造方法都会跳到三个参数的构造方法中,这样就可以偷懒只用在三个参数的构造方法里初始化各种参数了
- public class Wave extends View {
- public Wave(Context context) {
- this(context,null