自定义一个View,让该View执行普通的Animation动画,利用Animation来当计时器,控制整个动画流程,收到每个进度变化时,先计算每个动画元素的大小、位置、颜色等逻辑,再刷新View来显示(动画元素在draw方法中自绘)。
A.开始动画,创建一个Animation,设置好动画时间后,利用applyTransformation的回调来控制动画进度,interpolatedTime表示进度,取值范围为0到1;
progressAnimation = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
setProgress(interpolatedTime);
}
};
progressAnimation.setDuration(ANIMATION_TIME_OPENING);
this.startAnimation(progressAnimation);
B.处理动画元素的逻辑
private void setProgress(float progress){
//处理各个动画元素的基本逻辑
// 比如计算小球的位置和大小,x、y表示小球的实时位置,radius表示实时大小
// x = (int) (origin_x + (des_x_one - origin_x) * progress);
// y = (int) (origin_y + (des_y_one - origin_y) * progress);
// radius = (int) (origin_radius + (des_radius_one - origin_radius) * progress);
//刷新view
this.invalidate();
}
C. 在View的onDraw方法中绘制动画元素
protected void onDraw(Canvas canvas) {
//以小球为例
//绘制小球,x,y坐标,radius半径
canvas.drawCircle( x, y, radius, mPaint);
}
总共分为5个部分,在公共时间轴之内完成动画,公共时间轴的进度为从0到1;
1. 4个小球
分为3个阶段,往外走(0-0.3),往中心走(0.3-0.7),再往外走(0.7-1)
其中每个阶段有个私有时间轴的概念,会把公共轴映射到私有轴上
比如从位置A到位置B,公共轴为(0-0.3),映射到私有轴(0-1)上,这样就能在(0-0.3)的时间内从A位移到B;
2. 2个中间的大球
从时间轴0.7开始绘制,内圈的时间轴为(0.7-0.9),外圈的时间轴为(0.7-1),这样两个圈一前一后有膨胀的感觉,同样也是需要将公共轴的进度映射到私有轴上
3. 分数
时间轴为(0.7-1),映射到私有轴(0-1)后,比如分数从0分到35分,每次计算一个分值并绘制,就能实现一个分数动态上涨的效果
4. 大球和小球的颜色
沿用公共轴的进度,每个进度算一个颜色值并绘制;颜色计算的基本原理为有N个色值,每个色值对应一个分数值,比如35分处于N[3]到N[4]色值之间,那么最终色值为N[3]到N[4]之间的一个渐变色;
5. 描述文案
略
运动的分为3个部分,由于要求动画的连贯性,故在一个Animation中完成,duration设置较长时间(30s),自己划分时间周期,如将1划分为30个时间周期,一个周期的时间为1/30,在不同的时间周期内完成不同的动作;
1. 4个小球
4小球分为两组,两两绕相同轨道转动;运动阶段1为缩小、加速过程,阶段2为匀速转动等待清理结果响应,阶段3为放大、减速过程;
2. 背景色
3. 分数
计算小球运动的类
private class CircleItemSmall {
int x;
int y;
int alpha;
float radius;
int color;
float origin_x;
float origin_y;
float des_x_one;
float des_y_one;
float des_x_two = 0;
float des_y_two = 0;
float des_x_three;
float des_y_three;
float origin_radius;
float des_radius_one;
float des_radius_two;
float des_radius_three;
int origin_alpha;
int des_alpha;
float progress_step_one = 0.6f;
float progress_step_two = 0.85f;
boolean isUp;//是否显示是前面
int pos = 0;//0右上 1左上 2左下 3右下
double cos_del;//椭圆倾斜角
double sin_del;//椭圆倾斜角
double a;//椭圆长轴
float oval_center_x;
float oval_center_y;
public CircleItemSmall(float x, float y, float des_x_one, float des_y_one, float des_x_three, float des_y_three
, float radius, float des_radius_one , float des_radius_two, float des_radius_three
, int originAlpha, int desAlpha){
this.origin_x = x;
this.origin_y = y;
this.des_x_one = des_x_one;
this.des_y_one = des_y_one;
this.des_x_three = des_x_three;
this.des_y_three = des_y_three;
calPos();
this.origin_radius = radius;
this.des_radius_one = des_radius_one;
this.des_radius_two = des_radius_two;
this.des_radius_three = des_radius_three;
this.origin_alpha = originAlpha;
this.des_alpha = desAlpha;
}
private void calPos(){
if(des_x_three > 0 && des_y_three < 0){
oval_center_x = (mSmallFinalDesPos[0][0] + mSmallFinalDesPos[2][0]) / 2;
oval_center_y = (mSmallFinalDesPos[0][1] + mSmallFinalDesPos[2][1]) / 2;
a = Math.sqrt((mSmallFinalDesPos[0][0] - oval_center_x) * (mSmallFinalDesPos[0][0] - oval_center_x)
+ (mSmallFinalDesPos[0][1] - oval_center_y) * (mSmallFinalDesPos[0][1] - oval_center_y));
cos_del = ( mSmallFinalDesPos[0][0] - oval_center_x )/ a;
sin_del = ( mSmallFinalDesPos[0][1] - oval_center_y) / a;
pos = 0;
}else if(des_x_three < 0 && des_y_three < 0){
oval_center_x = (mSmallFinalDesPos[1][0] + mSmallFinalDesPos[3][0]) / 2;
oval_center_y = (mSmallFinalDesPos[1][1] + mSmallFinalDesPos[3][1]) / 2;
a = Math.sqrt((mSmallFinalDesPos[1][0] - oval_center_x) * (mSmallFinalDesPos[1][0] - oval_center_x)
+ (mSmallFinalDesPos[1][1] - oval_center_y) * (mSmallFinalDesPos[1][1] - oval_center_y));
cos_del = -( mSmallFinalDesPos[1][0] - oval_center_x ) / a;
sin_del = -( mSmallFinalDesPos[1][1] - oval_center_y) / a;
pos = 1;
}else if(des_x_three < 0 && des_y_three > 0){
oval_center_x = mSmallCircleList.get(0).oval_center_x;//减小计算
oval_center_y = mSmallCircleList.get(0).oval_center_y;
a = mSmallCircleList.get(0).a;
cos_del = -( mSmallFinalDesPos[2][0] - oval_center_x ) / a;
sin_del = -( mSmallFinalDesPos[2][1] - oval_center_y ) / a;
pos = 2;
}else if(des_x_three > 0 && des_y_three > 0){
oval_center_x = mSmallCircleList.get(1).oval_center_x;//减小计算
oval_center_y = mSmallCircleList.get(1).oval_center_y;
a = mSmallCircleList.get(1).a;
cos_del = ( mSmallFinalDesPos[3][0] - oval_center_x ) / a;
sin_del = ( mSmallFinalDesPos[3][1] - oval_center_y ) / a;
pos = 3;
}
}
public void handlerProgress(float progress){
if(state == STATE_OPENING) {
if (progress <= progress_step_one) {
handlerAnimStepOne(progress);
} else if (progress <= progress_step_two) {
handlerAnimStepTwo(progress);
} else {
handlerAnimStepThree(progress);
}
handlerAlpha(progress);
}else if(state == STATE_SCAN || state == STATE_CLEAN){
handlerScaleAnim(loop, ratio);
handlerOvalAnim(loop, ratio);
}else if( state == STATE_CLEAN_ENDING){
handlerCleanEndScaleAnim(loop, ratio);
handlerOvalAnim(loop, ratio);
}
if(progress == 1){
reset();
}
}
private void reset() {
x = (int) des_x_three;
y = (int) des_y_three;
radius = des_radius_three;
alpha = des_alpha;
}
private void handlerCleanEndScaleAnim(int loop, float ratio) {
if(current_rotate_loop == ANIMATION_ROTATE_LOOP_COUNT) {
radius = (int) (origin_radius + (des_radius_three - origin_radius) * ratio);
}
}
private void handlerScaleAnim(int loop, float ratio) {
if(loop < ANIMATION_ROTATE_LOOP_COUNT) {
radius = (int) (des_radius_three + (origin_radius - des_radius_three) * ratio);
}
}
private void handlerOvalAnim(int loop, float ratio) {
if(current_rotate_loop > ANIMATION_ROTATE_LOOP_COUNT){//收到结束消息后循环完一周停止运动
return;
}
double angle;
if(pos == 0 || pos == 3) {
angle = 2 * Math.PI * ratio;
if(angle < Math.PI){
isUp = false;
}else{
isUp = true;
}
}else{
angle = Math.PI + 2 * Math.PI * ratio;
if(angle < 2 * Math.PI){
isUp = true;
}else{
isUp = false;
}
}
double cos_angle = Math.cos(angle);
double sin_angle = Math.sin(angle);
x = (int) (a * cos_angle * cos_del + a / 3 * sin_angle * sin_del + oval_center_x);
y = (int) (a * cos_angle * sin_del + a / 3 * sin_angle * cos_del + oval_center_y);
}
private void handlerAlpha(float progress) {
alpha = (int) (origin_alpha + (des_alpha - origin_alpha) * progress);
}
private void handlerAnimStepOne(float progress){
float ratio = progress / progress_step_one;
x = (int) (origin_x + (des_x_one - origin_x) * ratio);
y = (int) (origin_y + (des_y_one - origin_y) * ratio);
radius = (int) (origin_radius + (des_radius_one - origin_radius) * ratio);
}
private void handlerAnimStepTwo(float progress){
float ratio = ( progress - progress_step_one ) / (progress_step_two - progress_step_one);
x = (int) (des_x_one + (des_x_two - des_x_one) * ratio);
y = (int) (des_y_one + (des_y_two - des_y_one) * ratio);
radius = (int) (des_radius_one + (des_radius_two - des_radius_one) * ratio);
}
private void handlerAnimStepThree(float progress){
float ratio = ( progress - progress_step_two ) / (1 - progress_step_two);
x = (int) (des_x_two + (des_x_three - des_x_two) * ratio);
y = (int) (des_y_two + (des_y_three - des_y_two) * ratio);
radius = (int) (des_radius_two + (des_radius_three - des_radius_two) * ratio);
}
}
颜色计算方法
public class CleanColorHelper {
/** 颜色列表,依次是红橙绿 */
private static final int[] COLORS = {
// 红
0xffff5252, //
0xfffe6530, //
0xffff8d22, //
0xffffb619, //
// 黄
0xfffcd038, //
0xffb5eb12, //
0xff7ad845, //
// 绿
0xff54d36f,
//蓝
0xff32a7de
};
/**
* 调用者控制变色的节奏
*
* @param percent 百分数
*/
public static int manualUpdateColor(Colors startColor, Colors endColor, int percent) {
if (percent > 100) {
percent = 100;
} else if (percent < 0) {
percent = 0;
}
// 8->0 30%
final int startColorIdx = getColorIdx(startColor);
final int endColorIdx = getColorIdx(endColor);
final float newPosition = startColorIdx + (float) (endColorIdx - startColorIdx) * percent / 100;
int showColor ;
if (newPosition == startColorIdx || newPosition == endColorIdx) {
showColor = COLORS[(int) newPosition];
} else {
int currentIdx ;
int nextIdx ;
float offset ;
if (endColorIdx < startColorIdx) {
currentIdx = (int) newPosition + 1;
nextIdx = currentIdx - 1;
offset = currentIdx - newPosition;
} else {
currentIdx = (int) newPosition;
nextIdx = currentIdx + 1;
offset = newPosition - currentIdx;
}
final int currentColor = COLORS[currentIdx];
final int nextColor = COLORS[nextIdx];
showColor = getShowColor(currentColor, nextColor, offset);
}
return showColor;
}
private static int getShowColor(int colorStart, int colorEnd, float offset) {
final int startRed = Color.red(colorStart);
final int startGreen = Color.green(colorStart);
final int startBlue = Color.blue(colorStart);
final int endRed = Color.red(colorEnd);
final int endGreen = Color.green(colorEnd);
final int endBlue = Color.blue(colorEnd);
final int showRed = (int) (startRed + offset * (endRed - startRed));
final int showGreen = (int) (startGreen + offset * (endGreen - startGreen));
final int showBlue = (int) (startBlue + offset * (endBlue - startBlue));
return Color.argb(255, showRed, showGreen, showBlue);
}
private static int getColorIdx(Colors color) {
switch (color) {
case RED:
return 0;
case ORANGE:
return 4;
case GREEN:
return 8;
}
return 8;
}
public static enum Colors {
RED, ORANGE, GREEN
}
}