CIT 591 Bouncing Ball
Fall 2008, David Matuszek

Here is the Bouncing Ball program, as presented on this PowerPoint presentation. I have made some changes in the code, marked in red, to convert the example from an applet into an application.

Normal Java style is to put each class in a separate file. For convenience in downloading and running, I have put all three classes into a single file. This file should be named Controller.java and put into the default package.

import java.awt.*;
import java.awt.event.*;
import java.util.Observable;
import java.util.Observer;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.*;

public class Controller extends JFrame { // Changed from JApplet
    JPanel buttonPanel = new JPanel();
    JButton runButton = new JButton("Run");
    JButton stopButton = new JButton("Stop");
    Timer timer;

    Model model = new Model();
    View view = new View(model); // View must know about Model
    
    // Added main method to convert applet into application
    public static void main(String[] args) {
        Controller c = new Controller();
        c.init();
        c.setSize(300, 300);
        c.setVisible(true);
        c.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    public void init() {
        layOutComponents();
        attachListenersToComponents();

        // Connect model and view
        model.addObserver(view);
    }
    
    private void layOutComponents() {
        setLayout(new BorderLayout());
        this.add(BorderLayout.SOUTH, buttonPanel);
        buttonPanel.add(runButton);
        buttonPanel.add(stopButton);
        stopButton.setEnabled(false);
        this.add(BorderLayout.CENTER, view);
    }
    
    private void attachListenersToComponents() {
        runButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                runButton.setEnabled(false);
                stopButton.setEnabled(true);
                timer = new Timer(true);
                timer.schedule(new Strobe(), 0, 40); // 25 times a second
            }
        });
        stopButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                runButton.setEnabled(true);
                stopButton.setEnabled(false);
                timer.cancel();
            }
        });
    }

    private class Strobe extends TimerTask { // An inner class

        public void run() {
            model.setLimits(view.getWidth(), view.getHeight());
            model.makeOneStep();
        }
    }
}

//---------------------------------------------------------------------

class Model extends Observable {
    public final int BALL_SIZE = 20;
    private int xPosition = 0;
    private int yPosition = 0;
    private int xLimit, yLimit;
    private int xDelta = 6;
    private int yDelta = 4;

    public void setLimits(int xLimit, int yLimit) {
        this.xLimit = xLimit - BALL_SIZE;
        this.yLimit = yLimit - BALL_SIZE;
    }

    public int getX() {
        return xPosition;
    }

    public int getY() {
        return yPosition;
    }

    public void makeOneStep() {
        // Do the work
        xPosition += xDelta;
        if (xPosition < 0 || xPosition >= xLimit) {
            xDelta = -xDelta;
            xPosition += xDelta;
        }
        yPosition += yDelta;
        if (yPosition < 0 || yPosition >= yLimit) {
            yDelta = -yDelta;
            yPosition += yDelta;
        }
        // Notify observers
        setChanged();
        notifyObservers();
    }
}

//---------------------------------------------------------------------

class View extends JPanel implements Observer { // changed from Panel
    Model model;

    View(Model model) {
        this.model = model;
    }

    public void paint(Graphics g) {
    
        // Added because of differences between Panel and JPanel
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, getWidth(), getHeight());
        
        g.setColor(Color.red);
        g.fillOval(model.getX(), model.getY(),
                model.BALL_SIZE, model.BALL_SIZE);
    }

    public void update(Observable obs, Object arg) {
        repaint();
    }
}