import java.applet.*; import java.awt.*; import java.awt.event.*; /** * This class defines the applet, and sets up the Model and View objects. * If we had user controls (such as Start and Stop buttons, or a speed * control), this is where they would be defined and handled.

* This example is more complex than it needs to be, because it enforces * a strict separation between the model (the part that does the actual * computation), the display, and the controls in the user interface (of * which there are none). This is called the MVC (Model-View-Controller) * pattern, and while it may be overkill for this example, MVC provides an * excellent basis for more complex animations. */ public class Controller extends Applet { // Create the model (the ball) and the view (the animation) Thread animation; Model model = new Model(); View view = new View(); /** * Lays out the applet components, starts a new thread for the * view to do its animation, and gives the model, view, and * controller access to one another. */ public void init() { // Lay out components setLayout(new BorderLayout()); this.add(BorderLayout.CENTER, view); // Give the view its own thread in which to do the animation animation = new Thread(view); animation.start(); // Get applet parameters and put them where they belong view.delay = Integer.valueOf(getParameter("delay")).intValue(); // Tell the view about the model and the controller view.model = model; view.controller = this; } /** * Finds the size of the canvas (this can't be done in init() * because the canvas isn't ready yet) and gives the animation * permission to run. */ public void start() { model.xLimit = view.getSize().width - model.BALL_SIZE; model.yLimit = view.getSize().height - model.BALL_SIZE; view.alive = true; view.okToRun = true; } /** * Withdraws permission for the animation to run. */ public void stop() { view.okToRun = false; } /** * Tells the animation thread to stop running. */ public void destroy() { view.alive = false; } } // ------------------------------------------------------------------------------------- /** * This class models the action of a bouncing ball. It is important to note * that this class is independent of the GUI. For this animation, the * Model does need to know the size of the box that the ball is bouncing * around in, and this is set from outside. Similarly, it provides a * makeOneStep() method so that the model can be controlled from outside the * class; but neither of these requires the model to know anything at all * about the rest of the program */ class Model { final int BALL_SIZE = 20; int xPosition = 0; int yPosition = 0; int xLimit, yLimit; int xDelta = 4; int yDelta = 3; /** * Moves the ball one step, by adding xDelta and yDelta to its xPosition * and yPosition, respectively, and checking for bounces off the "walls". */ void makeOneStep() { xPosition += xDelta; if(xPosition < 0 || xPosition >= xLimit) { xDelta = -xDelta; xPosition += xDelta; } yPosition += yDelta; if(yPosition < 0 || yPosition >= yLimit) { yDelta = -yDelta; yPosition += yDelta; } } } // ------------------------------------------------------------------------------------- /** * This class runs in a separate (animation) thread and controls all the * painting. */ class View extends Canvas implements Runnable { Controller controller; Model model; Dimension size = getSize(); int delay = 1000; int stepNumber = 0; boolean alive, okToRun; /** * Sets up an (almost) infinite loop to do the animation, and every * delay milliseconds asks the model to update itself, then draws the * result. */ public void run() { // "alive" will be true until the Applet is destroyed while(alive) { // "okToRun" will be true if the Applet is started and not stopped if(okToRun) { model.makeOneStep(); repaint(); // Note: uses Graphics for this Canvas, not for the Applet } // Control the speed of execution try { Thread.sleep(delay); } catch(InterruptedException e) {} } } /** * Paints a background, then paints other things on top of it. */ public void update(Graphics g) { background(g); paint(g); } /** * Repaints the canvas based on information that it gets from the model. */ public void paint(Graphics g) { // Draw new ball g.setColor(Color.red); g.fillOval(model.xPosition, model.yPosition, model.BALL_SIZE, model.BALL_SIZE); // Display steps and position in the status line; // this is a good place to put debugging information. if(++stepNumber % 10 == 0) controller.showStatus("Step " + stepNumber + ", x = " + model.xPosition + ", y = " + model.yPosition); } /** * Paints a background on the given Graphics. */ void background(Graphics g) { int width = getSize().width; int height = getSize().height; int squareSize = 2 * model.BALL_SIZE; g.setColor(Color.blue); g.fillRect(0, 0, width, height); g.setColor(Color.green); for (int x = 0; x < width; x += 2 * squareSize) { for (int y = 0; y < height; y += 2 * squareSize) { g.fillRect(x, y, squareSize, squareSize); g.fillRect(x + squareSize, y + squareSize, squareSize, squareSize); } } } }