先上图:
再附上:源码地址
欢迎大家star~
上面的自定义动画效果,有以下三个关键点:
1,背景颜色变化;
2,中心位置风车的旋转;
3,四周小圈圈,吸收进中心位置;
下面详细介绍它们实现方式:
实际整体控制动画的流畅性,是利用属性动画辅助的:
mAnim = ValueAnimator.ofInt(0, 100);
mAnim.setDuration(30 * 1000);
mAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float progress = animation.getAnimatedFraction();
// ...省略代码
invalidate();
}
});
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
// 背景色渐变
// ...省略代码
// 风扇 旋转
// ...省略代码
// 冲击的 圈圈
// ...省略代码
// 底部文案
// ...省略代码
}
利用属性动画,在 onAnimationUpdate 监听里面,invalidate 重走 onDraw 重绘;
背景色变化
mAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float progress = animation.getAnimatedFraction();
// ...省略代码
mCurrentScore = (int) ((mTargetScore - mCurrentScore) * progress + mCurrentScore);
mBgColor = mArgbEvaluator.evaluate((float) mCurrentScore/100, CleanColorHelper.Colors.RED, CleanColorHelper.Colors.GREEN);
invalidate();
}
});
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
// 背景色
canvas.drawColor(mBgColor);
// 背景色上盖一层 渐变
if (mLGradient == null) {
mLGradient = new LinearGradient(0, 0, getWidth(), getHeight(), 0x88ffffff, 0x00ffffff, Shader.TileMode.REPEAT);
}
mPaint.setShader(mLGradient);
canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
// 风扇 旋转
// ...省略代码
// 冲击的 圈圈
// ...省略代码
// 底部文案
// ...省略代码
}
风车旋转
// ...省略代码
mScanBitmap = BitmapUtils.getBitmapFromResourceWithHighQuality(getContext().getResources(), R.drawable.manage_icon_scan_fs, 268, 268);
mAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float progress = animation.getAnimatedFraction();
// ...省略代码
// 每次旋转15度
mScanAngle += 15;
invalidate();
}
});
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
// 背景色
// ...省略代码
// 风扇 旋转
if (mScanBitmap != null) {
mPaint.setShader(null);
mPaint.setAlpha((int) (255));
mPaint.setStyle(Paint.Style.FILL);
float scanAngle = mScanAngle % 360;
int centerBmX = mScanBitmap.getWidth() / 2;
int centerBmY = mScanBitmap.getHeight() / 2;
// 把风车图片移到中心
mBmMatrix.setTranslate(centerX - centerBmX, centerY - centerBmY);
// 以图片中心,旋转角度
mBmMatrix.preRotate(scanAngle, centerBmX, centerBmY);
canvas.drawBitmap(mScanBitmap, mBmMatrix, mPaint);
}
// 冲击的 圈圈
// ...省略代码
// 底部文案
// ...省略代码
}
冲击的圈圈
// 随机生成某个方向的 圈圈
private ScoreAnimationView.Circle obtainNewCircle(int where) {
ScoreAnimationView.Circle circle = new ScoreAnimationView.Circle();
circle.where = where;
int random = DensityUtils.dip2px(-30f) + RandomUtils.getRandomInt(DensityUtils.dip2px(60f));
if (where == 0) {
circle.x = DensityUtils.dip2px(-100f) + random * 2;
circle.y = DensityUtils.dip2px(-150f) + random;
circle.radius = DensityUtils.dip2px(30f) + random / 6;
circle.color = ScoreAnimationView.MC;
} else if (where == 1) {
circle.x = DensityUtils.dip2px(120f) + random;
circle.y = DensityUtils.dip2px(-150f) + random / 2;
circle.radius = DensityUtils.dip2px(25f) + random / 6;
circle.color = ScoreAnimationView.MC;
} else if (where == 2) {
circle.x = DensityUtils.dip2px(-110f) + random;
circle.y = DensityUtils.dip2px(150f) + random * 2;
circle.radius = DensityUtils.dip2px(26f) + random / 6;
circle.color = ScoreAnimationView.MC;
} else if (where == 3) {
circle.x = DensityUtils.dip2px(100f) + random / 2;
circle.y = DensityUtils.dip2px(150f) + random;
circle.radius = DensityUtils.dip2px(20f) + random / 6;
circle.color = ScoreAnimationView.MC;
} else if (where == 4) {
circle.x = DensityUtils.dip2px(-200f) + random / 2;
circle.y = DensityUtils.dip2px(10f) + random / 2;
circle.radius = DensityUtils.dip2px(15f) + random / 6;
circle.color = ScoreAnimationView.MC;
} else if (where == 5) {
circle.x = DensityUtils.dip2px(200f) + random / 2;
circle.y = DensityUtils.dip2px(-10f) + random / 2;
circle.radius = DensityUtils.dip2px(18f) + random / 6;
circle.color = ScoreAnimationView.MC;
}
return circle;
}
/**
* 添加新的一组圈圈,到屏幕里;
* mCurrentNextTime 会逐渐变大的,每间隔 NEXT_NEW_TIME 的时间,会增加一组圈圈,显得看着连贯冲击感;
*/
private void addNewCircles() {
if (mCurrentNextTime >= NEXT_NEW_TIME
&& mCurrentNextTime < NEXT_NEW_TIME + LOOP_TIME) {
mCurrentNextTime = 0;
mCircles.add(obtainNewCircle(0));
mCircles.add(obtainNewCircle(1));
mCircles.add(obtainNewCircle(2));
mCircles.add(obtainNewCircle(3));
mCircles.add(obtainNewCircle(4));
mCircles.add(obtainNewCircle(5));
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
// 背景色
// ...省略代码
// 风扇 旋转
// ...省略代码
// 冲击的 圈圈
Iterator<ScoreAnimationView.Circle> it = mCircles.iterator();
while (it.hasNext()) {
ScoreAnimationView.Circle circle = it.next();
// 每个圈圈都有自己的进度,大于1了,就可以回收删除了;
if (circle.progress > 1) {
it.remove();
} else {
mPaint.setShader(null);
mPaint.setColor(circle.color);
mPaint.setAlpha((int) (125 * (1f - circle.progress)));
float x = centerX + circle.x * (1f - circle.progress);
float y = centerY + circle.y * (1f - circle.progress);
float radius = circle.radius * (1.2f - circle.progress);
canvas.drawCircle(x, y, radius, mPaint);
circle.progress += LOOP_TIME;
}
}
addNewCircles();
mCurrentNextTime += LOOP_TIME;
// 底部文案
// ...省略代码
}