一歩ずつあなたにSlackのLoadingアニメーションを書くことを教えます。


プロジェクト住所:https://github.com/JeasonWong/SlackLoadingView
慣例によって,まず効果を上げる.

図が大きい。
最初にこのアニメを見た後の考えを言います。
+2本の平行線を使って、直線式y=kx+bを使います。
+もう二本の平行線は、前の二本の平行線の傾きと掛け合わせて-1となります。つまり、k 1*k 2=-1となります。
+線を円周運動にすると、k値がどんどん変化します。
+そして簡単な線の長さの変化です。
多くの人が私と同じような考えを持っていると思いますが、トイレに行ったら複雑だと思います。
トイレに行った後の考えを言います。
線が斜めだと思わないでください。普通の線分です。LineToでできます。(startXとstopXは同じです。Yだけ違います。)
線の垂直がより簡単で、直接的にCanvasが反転します。
アニメ全体の円周運動もキャンバス反転です。
線の単一度の変化は依然として属性アニメーションを使用しています。
アニメが始まる前からキャンバスを全部回転させます。
これはとても簡単です。
アニメーションを四つのステップに分けました。
キャンバスの回転と線の変化アニメーション(Canvas Rotate Line Change)
キャンバス回転動画(Canvas Rotate)
キャンバス回転円変化動画(Canvas Rotate Circule Change)
線変化動画(Line Change)
詳細は先にメンバー変数といくつかの初期化を紹介します。
メンバー変数

//    
 private final int STATUS_STILL = 0;
 //    
 private final int STATUS_LOADING = 1;
 //      
 private final int MAX_LINE_LENGTH = dp2px(getContext(), 120);
 //      
 private final int MIN_LINE_LENGTH = dp2px(getContext(), 40);
 //      
 private final int MAX_DURATION = 3000;
 //      
 private final int MIN_DURATION = 500;

 private Paint mPaint;
 private int[] mColors = new int[]{0xB07ECBDA, 0xB0E6A92C, 0xB0D6014D, 0xB05ABA94};
 private int mWidth, mHeight;
 //      
 private int mDuration = MIN_DURATION;
 //     
 private int mEntireLineLength = MIN_LINE_LENGTH;
 //   
 private int mCircleRadius;
 //    
 private List<Animator> mAnimList = new ArrayList<>();
 //Canvas      
 private final int CANVAS_ROTATE_ANGLE = 60;
 //      
 private int mStatus = STATUS_STILL;
 //Canvas    
 private int mCanvasAngle;
 //    
 private float mLineLength;
 //  Y   
 private float mCircleY;
 //     
 private int mStep;

初期化

 private void initView() {
  mPaint = new Paint();
  mPaint.setAntiAlias(true);
  mPaint.setColor(mColors[0]);
 }

 private void initData() {
  mCanvasAngle = CANVAS_ROTATE_ANGLE;
  mLineLength = mEntireLineLength;
  mCircleRadius = mEntireLineLength / 5;
  mPaint.setStrokeWidth(mCircleRadius * 2);
  mStep = 0;
 }

一、キャンバスの回転と線の変化アニメーション(Canvas Rotate Line Change)

 /**
  * Animation1
  *   1
  * Canvas Rotate Line Change
  *            
  */
 private void startCRLCAnim() {

  Collection<Animator> animList = new ArrayList<>();

  ValueAnimator canvasRotateAnim = ValueAnimator.ofInt(CANVAS_ROTATE_ANGLE + 0, CANVAS_ROTATE_ANGLE + 360);
  canvasRotateAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   @Override
   public void onAnimationUpdate(ValueAnimator animation) {
    mCanvasAngle = (int) animation.getAnimatedValue();
   }
  });

  animList.add(canvasRotateAnim);

  ValueAnimator lineWidthAnim = ValueAnimator.ofFloat(mEntireLineLength, -mEntireLineLength);
  lineWidthAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   @Override
   public void onAnimationUpdate(ValueAnimator animation) {
    mLineLength = (float) animation.getAnimatedValue();
    invalidate();
   }
  });

  animList.add(lineWidthAnim);

  AnimatorSet animationSet = new AnimatorSet();
  animationSet.setDuration(mDuration);
  animationSet.playTogether(animList);
  animationSet.setInterpolator(new LinearInterpolator());
  animationSet.addListener(new AnimatorListener() {
   @Override
   public void onAnimationEnd(Animator animation) {
    Log.d("@=>", "  1  ");
    if (mStatus == STATUS_LOADING) {
     mStep++;
     startCRAnim();
    }
   }
  });
  animationSet.start();

  mAnimList.add(animationSet);
 }

第一歩は二つのアニメが同時に進行することに関連していますので、アニメイトセットを使っています。この種類はとても強くて、N個のアニメを同時に行うことができます。N個のアニメを順番に実行することもできます。
ここで言えば、実は私の4つのアニメは順番に行われていますが、各アニメには同時進行のアニメがあります。説明のために、アニメイト・エンドをモニターしてアニメーションの実行順序をコントロールしています。実は直接にplaySequentiallyを使うことができます。
上のアニメは二つのことをしました。
1、キャンバスを回して、カンバンからROTATE.ANGLE+0はCANVES_に転送します。ROTATE.ANGLE+360、CANVES_ROTATE.ANGLEはキャンバスの初期傾斜角度です。
2、線の長さが変化して、mEnttireline Lengthから-mEnitireline Lengthまで。
対応するonDraw方法:

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  switch (mStep % 4) {
   case 0:
    for (int i = 0; i < mColors.length; i++) {
     mPaint.setColor(mColors[i]);
     drawCRLC(canvas, mWidth / 2 - mEntireLineLength / 2.2f, mHeight / 2 - mLineLength, mWidth / 2 - mEntireLineLength / 2.2f, mHeight / 2 + mEntireLineLength, mPaint, mCanvasAngle + i * 90);
    }
    break;
   ...
  }

 }

 ...

 private void drawCRLC(Canvas canvas, float startX, float startY, float stopX, float stopY, @NonNull Paint paint, int rotate) {
  canvas.rotate(rotate, mWidth / 2, mHeight / 2);
  canvas.drawArc(new RectF(startX - mCircleRadius, startY - mCircleRadius, startX + mCircleRadius, startY + mCircleRadius), 180, 180, true, mPaint);
  canvas.drawLine(startX, startY, stopX, stopY, paint);
  canvas.drawArc(new RectF(stopX - mCircleRadius, stopY - mCircleRadius, stopX + mCircleRadius, stopY + mCircleRadius), 0, 180, true, mPaint);
  canvas.rotate(-rotate, mWidth / 2, mHeight / 2);
 }

機転がいいですか?drawCRLCは三つのことをしました。
1、キャンバスは回転してまた戻ってきます。
2、半円を描く(なぜ半円を描くのですか?丸を描きませんか?ここに思考問題を残してください。
3、線を引く
これでアニメが完成します。
二、キャンバス回転動画(Canvas Rotate)

 /**
  * Animation2
  *   2
  * Canvas Rotate
  *       
  */
 private void startCRAnim() {
  ValueAnimator canvasRotateAnim = ValueAnimator.ofInt(mCanvasAngle, mCanvasAngle + 180);
  canvasRotateAnim.setDuration(mDuration / 2);
  canvasRotateAnim.setInterpolator(new LinearInterpolator());
  canvasRotateAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   @Override
   public void onAnimationUpdate(ValueAnimator animation) {
    mCanvasAngle = (int) animation.getAnimatedValue();
    invalidate();
   }
  });
  canvasRotateAnim.addListener(new AnimatorListener() {
   @Override
   public void onAnimationEnd(Animator animation) {
    Log.d("@=>", "  2  ");
    if (mStatus == STATUS_LOADING) {
     mStep++;
     startCRCCAnim();
    }
   }
  });
  canvasRotateAnim.start();

  mAnimList.add(canvasRotateAnim);
 }

 ...

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  switch (mStep % 4) {
   ...
   case 1:
    for (int i = 0; i < mColors.length; i++) {
     mPaint.setColor(mColors[i]);
     drawCR(canvas, mWidth / 2 - mEntireLineLength / 2.2f, mHeight / 2 + mEntireLineLength, mPaint, mCanvasAngle + i * 90);
    }
    break;
   ...
  }

 }

 ...

 private void drawCR(Canvas canvas, float x, float y, @NonNull Paint paint, int rotate) {
  canvas.rotate(rotate, mWidth / 2, mHeight / 2);
  canvas.drawCircle(x, y, mCircleRadius, paint);
  canvas.rotate(-rotate, mWidth / 2, mHeight / 2);
 }

アニメ1の下地があると、これは簡単すぎて、簡単にCanvasを回転させます。
三、カンバスの回転円変化動画(Canvas Rotate Circule Change)

 /**
  * Animation3
  *   3
  * Canvas Rotate Circle Change
  *           
  */
 private void startCRCCAnim() {
  Collection<Animator> animList = new ArrayList<>();

  ValueAnimator canvasRotateAnim = ValueAnimator.ofInt(mCanvasAngle, mCanvasAngle + 90, mCanvasAngle + 180);
  canvasRotateAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   @Override
   public void onAnimationUpdate(ValueAnimator animation) {
    mCanvasAngle = (int) animation.getAnimatedValue();
   }
  });

  animList.add(canvasRotateAnim);

  ValueAnimator circleYAnim = ValueAnimator.ofFloat(mEntireLineLength, mEntireLineLength / 4, mEntireLineLength);
  circleYAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   @Override
   public void onAnimationUpdate(ValueAnimator animation) {
    mCircleY = (float) animation.getAnimatedValue();
    invalidate();
   }
  });

  animList.add(circleYAnim);

  AnimatorSet animationSet = new AnimatorSet();
  animationSet.setDuration(mDuration);
  animationSet.playTogether(animList);
  animationSet.setInterpolator(new LinearInterpolator());
  animationSet.addListener(new AnimatorListener() {
   @Override
   public void onAnimationEnd(Animator animation) {
    Log.d("@=>", "  3  ");
    if (mStatus == STATUS_LOADING) {
     mStep++;
     startLCAnim();
    }
   }
  });
  animationSet.start();

  mAnimList.add(animationSet);
 }

 ...

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  switch (mStep % 4) {
   ...
   case 2:
    for (int i = 0; i < mColors.length; i++) {
     mPaint.setColor(mColors[i]);
     drawCRCC(canvas, mWidth / 2 - mEntireLineLength / 2.2f, mHeight / 2 + mCircleY, mPaint, mCanvasAngle + i * 90);
    }
    break;
   ...
  }

 }

 ...

 private void drawCRCC(Canvas canvas, float x, float y, @NonNull Paint paint, int rotate) {
  canvas.rotate(rotate, mWidth / 2, mHeight / 2);
  canvas.drawCircle(x, y, mCircleRadius, paint);
  canvas.rotate(-rotate, mWidth / 2, mHeight / 2);
 }

アニメ3は二つのことをしました。
1、回転キャンバス
2、CircureのY座標を変化させて、中へ縮める効果を達成します。
四、線変化アニメ(Line Change)

 /**
  * Animation4
  *   4
  * Line Change
  *       
  */
 private void startLCAnim() {
  ValueAnimator lineWidthAnim = ValueAnimator.ofFloat(mEntireLineLength, -mEntireLineLength);
  lineWidthAnim.setDuration(mDuration);
  lineWidthAnim.setInterpolator(new LinearInterpolator());
  lineWidthAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
   @Override
   public void onAnimationUpdate(ValueAnimator animation) {
    mLineLength = (float) animation.getAnimatedValue();
    invalidate();
   }
  });
  lineWidthAnim.addListener(new AnimatorListener() {
   @Override
   public void onAnimationEnd(Animator animation) {
    Log.d("@=>", "  4  ");
    if (mStatus == STATUS_LOADING) {
     mStep++;
     startCRLCAnim();
    }
   }
  });
  lineWidthAnim.start();

  mAnimList.add(lineWidthAnim);
 }

 ...

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  switch (mStep % 4) {
   ...
   case 3:
    for (int i = 0; i < mColors.length; i++) {
     mPaint.setColor(mColors[i]);
     drawLC(canvas, mWidth / 2 - mEntireLineLength / 2.2f, mHeight / 2 + mEntireLineLength, mWidth / 2 - mEntireLineLength / 2.2f, mHeight / 2 + mLineLength, mPaint, mCanvasAngle + i * 90);
    }
    break;
  }

 }

 ...

 private void drawLC(Canvas canvas, float startX, float startY, float stopX, float stopY, @NonNull Paint paint, int rotate) {
  canvas.rotate(rotate, mWidth / 2, mHeight / 2);
  canvas.drawArc(new RectF(startX - mCircleRadius, startY - mCircleRadius, startX + mCircleRadius, startY + mCircleRadius), 0, 180, true, mPaint);
  canvas.drawLine(startX, startY, stopX, stopY, paint);
  canvas.drawArc(new RectF(stopX - mCircleRadius, stopY - mCircleRadius, stopX + mCircleRadius, stopY + mCircleRadius), 180, 180, true, mPaint);
  canvas.rotate(-rotate, mWidth / 2, mHeight / 2);
 }

アニメ4は線の変化だけしました。
このように全体のSlackのLoadingアニメーションは完成して、とても簡単ですか?
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。