javaandroidanimationcustom-painting

android rotate animation reduce calculations and make animation smooth


I'm trying to draw a custom rotating animation. This is the output of my code.

enter image description here

Basically every circle has small sub circles. Only issue is, sometimes animation skip some frames and animation looks broken. My guess is since this calculation is time consuming some frames are missing.

My question is what is the best way to improve this and keep animation smooth. Is it possible to just calculate for first frame and rotate the canvas so we can reduce the calculations?

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);    
    int startRadius = 20;
    int endRadius = 400;
    int circleCount = 20;
    int circleDistance = (int) (((endRadius - startRadius) / (float) circleCount));

    for (int i = 0; i < circleCount; i++) {
        int radius = startRadius + i * circleDistance;
        canvas.drawCircle(center.x, center.y, radius, mPaint);

        int subCircles=i+1;
        double angleSegment = Math.PI * 2 / subCircles;
        for (int segment = 0; segment < subCircles; segment++) {
            double angle = angleSegment * segment + shiftAngle;
            double x = Math.cos(angle) * radius;
            double y = Math.sin(angle) * radius;
            canvas.drawCircle((float) (x + center.x), (float) (y + center.y), 6, pointPaint);
        }

    }
}

Animator

public void startAnim() {
    rotateAnim = ValueAnimator.ofInt(0, 100);
    rotateAnim.setDuration(1000);
    rotateAnim.setInterpolator(new LinearInterpolator());
    rotateAnim.addUpdateListener(animation -> {
        shiftAngle += Math.PI / 180;
        View.this.invalidate();
    });
    rotateAnim.setRepeatCount(ValueAnimator.INFINITE);
    rotateAnim.start();
}

Solution

  • You can precalculate the center coordinates of the small circles at shiftAngle = 0 and store them in arrays. You can draw them first and then rotate them Math.PI / 180 with the following formula.

     newX = Math.cos(Math.PI / 180) * x - Math.sin(Math.PI / 180) * y;
     newY = Math.sin(Math.PI / 180) * x + Math.cos(Math.PI / 180) * y;
    

    Since Math.sin(Math.PI / 180) and Math.cos(Math.PI / 180) are constants. They can be calculated outside the loop in advance.

    You can push sin and cos calculations out of the loop by doing this.