Search code examples
androiddrawingandroid-canvassurfaceview

How to draw smooth movement on surface


I am trying to achieve smooth movement of a drawn object.
Here I draw circle which by itselfe move in Hypotrochoid path. I am setting delay in 16 ms to get 60 frames per second and position every frame. However my circle still does not move smooth. Main Activity

Handler handler = new Handler();
    Runnable runnable = new Runnable(){

        @Override
        public void run(){
            masterMotion.tryDrawing();
            handler.postDelayed(runnable, 16);

        }
    };


    @Override
    public void surfaceCreated(SurfaceHolder holder){
        masterMotion = new MasterMotion(MainActivity.this, holder);
        handler.postDelayed(runnable, 16);

    }


    @Override
    public void surfaceChanged(SurfaceHolder holder, int frmt, int w, int h){
    }


    @Override
    public void surfaceDestroyed(SurfaceHolder holder){
        handler.removeCallbacks(runnable);
    }

Motion

public Motion(int type, int color, int xPos, int yPos, int radius, int totalHeight){
        this.type = type;
        this.color = color;

        this.radius = radius;
        xBottom = xPos;
        yBottom = yPos;
        xTop = xBottom;
        yTop =  (int) (radius * 0.2 - radius);
        xMid = xBottom;
        yMid = (int) (radius * 0.2 - radius + totalHeight / 2);
        xAlt = xBottom;
        yAlt=yBottom;
        switch(type){
            case 0:
                innerR = 20;
                hR = 10;
                hD = 2;
                break;


        }
    }


    public void drawMyStuff(final Canvas canvas, final Paint mPaint){
        updatePositions();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(color);
        canvas.drawCircle(xR, yR, radius, mPaint);

    }


    public void updatePositions(){
        xR =
            (float) (xAlt + (innerR - hR) * Math.cos(angle) + hD * Math.cos(
                                                                               ((innerR - hR) /
                                                                                    hR) *
                                                                                   angle
            ));
        yR =
            (float) (yAlt + (innerR - hR) * Math.sin(angle) + hD * Math.sin(
                                                                               ((innerR - hR) /
                                                                                    hR) *
                                                                                   angle
            ));
angle = (angle + 0.03) % (2 * Math.PI);

        if(stepCount>=0){
            xAlt+=stepX;
            yAlt+=stepY;
            stepCount--;
        }
    }


    public void goBottom(){
        mustMoove=true;
        direction =0;
        stepX = (xBottom-xAlt)/20;
        stepY = (yBottom - yAlt) /20;
        stepCount=20;

    }


    public void goMid(){
        mustMoove = true;
        direction = 1;
        stepX = (xMid - xAlt) / 100;
        stepY = (yMid - yAlt) / 100;
        stepCount=100;
    }


    public void goTop(){
        mustMoove = true;
        direction = 2;
        stepX = (xTop - xAlt) / 100;
        stepY = (yTop - yAlt) / 100;
        stepCount=100;
    }

}

Solution

  • The application does not determine the display's refresh rate. Attempts to "set" the rate to 60fps by sleeping for 16.7ms will produce jerky results. You should be using Choreographer to get notifications and timing info, and advancing your animation by actual time deltas rather than fixed-length frames.

    A discussion of game loops on Android can be found in the architecture doc.

    An illustration of smooth animation with Choreographer can be found in Grafika; try the "record GL app" activity, which animates smoothly even when dropping frames. It's also interesting to watch the "scheduled swap" activity, which can be used to demonstrate that 30fps looks smoother than 48fps on a 60fps display, despite the lower frame rate.

    Smooth animation with the GLSurfaceView "queue stuffing" approach can be seen in Android Breakout. It uses the time since the previous draw callback to determine how far to advance the animation, which is less accurate than Choreographer but close enough for most purposes.