Simple Single Bouncing Ball

Notes from : http://code.tutsplus.com/tutorials/android-sdk-achieving-movement–mobile-5463

Movement.java

package com.jamesfroggatt.movement.app;

import android.app.Activity;
import android.os.Bundle;


public class Movement extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(new MovementView(this));
    }
}

MovementView.java

package com.jamesfroggatt.movement.app;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.SurfaceHolder;
import android.view.SurfaceView;


// our SurfaceView Object
// Notice the implementation of SurfaceHolder.Callback = This is what allows the
// view to detect things like the surface being created, changed or destroyed
public class MovementView extends SurfaceView implements SurfaceHolder.Callback {
    private int xPos;
    private int yPos;

    private int xVel;
    private int yVel;

    private int width;
    private int height;

    private int circleRadius;
    private Paint circlePaint;

    UpdateThread updateThread;

    // MovementView contructor:
    // 1) Call the super() method to give us our SurfaceView to work with.
    // 2) Link the class up with the SurfaceHolder.Callback
    // 3) Initialize variables regarding the circle
    // 4) Set the speed of movement in each direction
    public MovementView(Context context) {
        super(context);
        getHolder().addCallback(this);

        circleRadius = 20;
        circlePaint = new Paint();
        circlePaint.setColor(Color.BLUE);

        xVel = 4;
        yVel = 4;
    }

    // This is the function that will be called to draw the circle on each frame.
    // It accomplishes two very simple tasks:
    // 1) Repaint the Canvas all black to cover the previous frame.
    // 2) Draw the new circle at the new set of coordinates.
    @Override
    protected void onDraw(Canvas canvas) {

        // you can always experiment by removing this line if you wish!
        canvas.drawColor(Color.WHITE);

        canvas.drawCircle(xPos, yPos, circleRadius, circlePaint);

       // canvas.drawCircle(xPos+10, yPos+10, circleRadius, circlePaint);
    }

    // This function is also called every frame and accomplishes two tasks:
    // 1) handle the simple physics of the movement
    // 2) Update the position of the ball and make it 'bounce' if it has hit the edge
    // What may be confusing about this step is how the app determines which way to
    // bounce the ball. The basic concept is that if the ball makes contact with the side,
    // it reverses in the X direction. If the ball hits the top or bottom, it reverses
    // in the Y direction. Also, it is important that when a collision is detected,
    // we immediately set the ball to rest exactly on that wall, not behind it as
    // it already might be. This prevents the ball getting stuck in a loop behind the wall.

    public void updatePhysics() {

        xPos += xVel;
        yPos += yVel;

        if (yPos - circleRadius < 0 || yPos + circleRadius > height) {
            // the ball has hit the top or the bottom of the canvas

            if (yPos - circleRadius < 0) {
                // the ball has hit the top of the canvas
                yPos = circleRadius;
            } else {
                // the ball has hit the bottom of the canvas
                yPos = height - circleRadius;
            }

            // reverse the y direction of the ball
            yVel *= -1;
        }

        if (xPos - circleRadius < 0 || xPos + circleRadius > width) {
            // the ball has hit the sides of the canvas
            if (xPos - circleRadius < 0) {
                // the ball has hit the left of the canvas
                xPos = circleRadius;
            } else {
                // the ball has hit the right of the canvas
                xPos = width - circleRadius;
            }

            // reverse the x direction of the ball
            xVel *= -1;
        }
    }

    // This method is called when the workable surface area for the app is first created.
    // You should note that it is never good practice to assume a screen size. Instead,
    // use whatever means necessary to determine width/height information during the app
    // runtime. This method accomplishes the following tasks:
    // 1) Grab the rectangle bounding area of the canvas and pass the width
    // and height to the respective variables.
    // 2) Set the initial position of the ball to the top center of the screen.
    // 3) Create the UpdateThread and start it.
    // From the point that the UpdateThread begins running, the ball will begin to move:
    public void surfaceCreated(SurfaceHolder holder) {

        Rect surfaceFrame = holder.getSurfaceFrame();
        width = surfaceFrame.width();
        height = surfaceFrame.height();

        xPos = width / 2;
        yPos = circleRadius;

        updateThread = new UpdateThread(this);
        updateThread.setRunning(true);
        updateThread.start();
    }

    // This method is not used in our app but is required by the SurfaceHolder.Callback
    // implementation.
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
    {

    }

    // This method deals with exiting the app and shutting down the thread. If we tried
    // to shut down the app without dealing with the thread, the user would see a
    // very annoying error dialog everytime they attempt to close the app.
    // This small piece of code will let the thread finish the current task
    // before closing, hence eliminating the error.
    public void surfaceDestroyed(SurfaceHolder holder) {

        boolean retry = true;

        updateThread.setRunning(false);
        while (retry) {
            try {
                updateThread.join();
                retry = false;
            } catch (InterruptedException e) {

            }
        }
    }
}

UpdateThread.java

package com.jamesfroggatt.movement.app;

import android.graphics.Canvas;
import android.view.SurfaceHolder;

public class UpdateThread extends Thread {
    private long time;
    private final int fps = 20;
    private boolean toRun = false;
    private MovementView movementView;
    private SurfaceHolder surfaceHolder;

    // UpdateThread constructor: The main purpose of this constructor is
    // to populate the surfaceHolder variable which will eventually be
    // used to provide a reference of the Canvas.
    public UpdateThread(MovementView rMovementView) {
        movementView = rMovementView;
        surfaceHolder = movementView.getHolder();
    }

    // This method serves one simple, but essential purpose: to give the
    // thread permission to run or not to run.
    public void setRunning(boolean run) {
        toRun = run;
    }

    // This is the main method of the Thread. The code in this method
    // dictates what is done with each 'tick' of the thread.
    // This is the list of tasks it performs:
    // 1) Check if it has permission to run.
    // 2) If so, check if the required time has passed to keep in line with the
    // FPS (frames per second) value.
    // 3) If so, set the canvas to empty.
    // 4) Get a reference to the canvas and lock it to prepare for drawing.
    // 5) Update the physics of the ball.
    // 6) Draw the ball in the new position.
    // 7) If it is safe to do so, lock and update the canvas.
    @Override
    public void run() {

        Canvas c;
        while (toRun) {

            long cTime = System.currentTimeMillis();

            if ((cTime - time) <= (1000 / fps)) {

                c = null;
                try {
                    c = surfaceHolder.lockCanvas(null);

                    movementView.updatePhysics();
                    movementView.onDraw(c);
                } finally {
                    if (c != null) {
                        surfaceHolder.unlockCanvasAndPost(c);
                    }
                }
            }
            time = cTime;
        }
    }
}

Leave a Reply