在地鐵中刷抖音逞敷,由于網(wǎng)絡(luò)不通暢加載很慢,抖音會加載一個加載動畫灌侣,感覺很有意思推捐,于是分析了一下,自己寫了Demo侧啼,實(shí)現(xiàn)效果牛柒。
效果圖
douyinloading.gif
分析動畫
首先分析動畫堪簿,初始狀態(tài)是由兩個相切的圓形圖案組成。
將動畫拆解為兩部分看皮壁。
- 從藍(lán)色小球在左椭更,紅色小球在右作為起始位置,藍(lán)色小球向右移動闪彼,紅色小球向左移動(同時先縮小甜孤,在還原),兩個小球相交的部分變黑畏腕。
- 同第一步相同,只是兩小球變換位置茉稠。
進(jìn)行繪制
主要運(yùn)用Drawable動畫進(jìn)行繪制描馅。
public class DouYinLoadingDrawable extends Drawable implements Animatable {
}
首先準(zhǔn)備三個Paint,和三個Path而线。為了繪制動畫中的兩個圓形圖案和重合部分的圖案铭污。
private Paint leftBallPaint, rightBallPaint, coincideBallPaint;
private Path leftBallPath, rightBallPath, coincideBallPath;
繪制分為兩個過程,藍(lán)色小球向左和藍(lán)色小球向右膀篮,創(chuàng)建枚舉來控制動畫的過程嘹狞。
public enum Direction {
LEFT,
RIGHT
}
開始繪制,在ondraw()中進(jìn)行繪制誓竿。
@Override
public void draw(@NonNull Canvas canvas) {
if (mCurrentDirection == Direction.LEFT) {
canvas.save();
leftBallPath.reset();
leftBallPath.addCircle(centerX - radius + translate, centerY, radius, Path.Direction.CCW);
canvas.drawPath(leftBallPath, leftBallPaint);
canvas.restore();
canvas.save();
rightBallPath.reset();
rightBallPath.addCircle(centerX + radius - translate, centerY, radius * scale, Path.Direction.CCW);
canvas.drawPath(rightBallPath, rightBallPaint);
canvas.restore();
canvas.save();
coincideBallPath.reset();
coincideBallPath.op(leftBallPath, rightBallPath, Path.Op.INTERSECT);
canvas.drawPath(coincideBallPath, coincideBallPaint);
canvas.restore();
} else if (mCurrentDirection == Direction.RIGHT) {
canvas.save();
rightBallPath.reset();
rightBallPath.addCircle(centerX - radius + translate, centerY, 20, Path.Direction.CCW);
canvas.drawPath(rightBallPath, rightBallPaint);
canvas.restore();
canvas.save();
leftBallPath.reset();
leftBallPath.addCircle(centerX + radius - translate, centerY, 20 * scale, Path.Direction.CCW);
canvas.drawPath(leftBallPath, leftBallPaint);
canvas.restore();
canvas.save();
coincideBallPath.reset();
coincideBallPath.op(leftBallPath, rightBallPath, Path.Op.INTERSECT);
canvas.drawPath(coincideBallPath, coincideBallPaint);
canvas.restore();
}
}
進(jìn)行動畫部分代碼編寫躁劣,主要運(yùn)用ValueAnimator
- 首先開始向左部分動畫濒憋,向左部分動畫結(jié)束后,兩小球交換圓心左邊,重復(fù)向左部分動畫戳稽。
private void leftAnimation() {
//平移動畫
final ValueAnimator translateAnimator = ValueAnimator.ofFloat(0, 2 * radius);
translateAnimator.setStartDelay(200);
translateAnimator.setDuration(350);
translateAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
translateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
translate = (float) translateAnimator.getAnimatedValue();
invalidateSelf();
}
});
//縮放動畫
final ValueAnimator scaleAnimator = ValueAnimator.ofFloat(1, 0.5f, 1);
scaleAnimator.setStartDelay(200);
scaleAnimator.setDuration(350);
scaleAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
scaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scale = (float) scaleAnimator.getAnimatedValue();
invalidateSelf();
}
});
translateAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//每次動畫結(jié)束后,改變Direction的值试溯,進(jìn)行反向動畫
if (mCurrentDirection == Direction.LEFT)
mCurrentDirection = Direction.RIGHT;
else
mCurrentDirection = Direction.LEFT;
translate = 0;
leftAnimation();
}
});
animatorSet = new AnimatorSet();
animatorSet.playTogether(translateAnimator, scaleAnimator);
animatorSet.start();
}
動畫使用
在ImageView中設(shè)置drawable即可
DouYinLoadingDrawable douYinLoadingDrawable =new DouYinLoadingDrawable();
imageView.setImageDrawable(douYinLoadingDrawable);
douYinLoadingDrawable.start();