前言
本文将实现早期58同城一个带有弹跳效果的加载动画,且结合图形变换(圆形变正方形、正方形变三角形等)实现一种动态、富有表现力的加载效果。
最终效果如下:
1. 效果分析
- 形状切换: 显示一个不断变化的形状,例如圆形、正方形、三角形之间的变换。
- 弹跳效果: 加载图标会有上下弹跳的动画,增加动感。
- 阴影效果: 动画中随着形状变化,添加一个阴影效果,随着形状变化而收缩或放大。
效果展示:
- 初始状态: 显示一个圆形图标,伴随初始弹跳。
- 动画过程: 随着动画的进行,圆形变为正方形,再变为三角形,每次形状变换后都会触发一次上下弹跳。
- 动画结束: 当动画完成时,图标将继续上下弹跳,直至用户操作或停止。
2. 结构分析
由上面GIF图可知,加载动画组合布局分为 上 中 下 三部分:分别是上面一个ShapeView 负责切换不同形状,中间一个View 用于模拟阴影效果,底部是加载文字,至于所有动效全部由各种动画实现。
为了方便控制上中下三部分内容,需要自定义一个LoadingView,用于包含这三部分View,其内部实现各个View的动画效果。
3. 技术实现
3.1 ShapeView
用于绘制三种图形:圆形、矩形、三角形。且具有形状切换功能,比如 先绘制圆形,圆形展示完绘制矩形,矩形展示完绘制三角形,三角形展示完再绘制圆,首尾相连。
首先我们要确保ShapeView 是个正方形,才能保证绘制出的三种图形都比较规则。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//确保是正方形
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int size = Math.min(width, height);
setMeasuredDimension(size, size);
}
接着,要定义三种图形的枚举类,方便切换使用:
public void exchangeShape(){
switch (currentShapeType){
case CIRCLE:
currentShapeType = ShapeType.SQUARE;
break;
case SQUARE:
currentShapeType = ShapeType.TRIANGLE;
break;
case TRIANGLE:
currentShapeType = ShapeType.CIRCLE;
break;
}
invalidate();
}
public ShapeType getCurrentShapeType() {
return currentShapeType;
}
public enum ShapeType{
CIRCLE, //圆形
SQUARE, //正方形
TRIANGLE //三角形
}
然后,在onDraw中绘制三种图形,其中圆和矩形最简单,有现成的方法canvas.drawCircle
和 canvas.drawRect
在此不做介绍了,三角形需要介绍一下,因为三角形要使用Path
进行绘制,且为了美观要绘制一个等边三角形:
要绘制三角形,我们首先要知道三角形的三个顶点的坐标,才能进行绘制,由于ShapeView
我们已经保证是正方形了,所以顶点坐标就是(getWidth() / 2,0)
,左边顶点坐标不能是(0,getHeight())
因为这样绘制的三角形就是等边三角形了,影响美观,而要实现等边三角形的话就要重新计算Y轴坐标,由上面草图可知,大三角形分成开两个小三角形,我们知道底边和斜边是1/2的关系,由三角定理可知,一份二份根号三份,所以长的直角边就是根号三的getWidth()
,此时我们就拿到了三个顶点坐标了。
if (mPath == null) {
mPath = new Path();
mPath.moveTo(getWidth() / 2,0);
mPath.lineTo((float) 0, (float) ((getHeight() / 2) * Math.sqrt(3)));
mPath.lineTo(getWidth(),(float) ((getHeight() / 2) * Math.sqrt