先上圖:
再附上:源碼地址
歡迎大家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;
// 底部文案
// ...省略代碼
}