Computer Science 2311
Fall 2007
Laboratory Assignment 10

Handed out: 11/12/07

Read this entire assignment before beginning to work on it.


The assignment

This assignment has you create a very simple graphics application. You will create a screen with one button that the user can push. The program will draw disks on the screen.

Your application should paint a disk (a filled circle) with diameter 20 pixels of a random color, and at a random position in the window. The random position should be chosen within limits so that the disk will be completely shown, and will not cover the button. There should be a few color options, such as red, blue, yellow and green. You can choose any set of three or more colors, as long as they are easy to distinguish from one another.

Each time the button is pushed, a color and position should be chosen at random. The old disk should be erased, and the new disk shown.


The Java library

For this assignment, you need to make extensive use of the Java library. The assignment tells you everything that you need. But if you want to, you can find out more from http://java.sun.com/j2se/1.5.0/docs/api/index.html. Select a class in the left-hand frame, and you will see the documention in the right-hand frame.


Class heading

The Swing library (javax.swing) has a class called JFrame that manages a window (by handling the frame around the window). You should create a class that extends JFrame, meaning that it automatically gets all of the variables and methods that are defined for class JFrame, as well as any variables and methods that you add. So not only does your class manage a window frame, but it does other things too, such as draw a button and circles. Start with the following heading, but select whatever name for your class you like.

  import javax.swing.*;
  import java.awt.*;
  import java.awt.event.*;

  public class Assignment10 extends JFrame
  {
    ...
  }


Variables

Your object will want to remember some information, such as the current disk color and the current disk position (x-coordinate and y-coordinate of the disk's center). So create variables to hold that information. The x- and y-coordinates are integers. The (0,0) position is in the upper-left corner of the window, with the x-coordinate getting larger as you move to the right, and the y-coordinate getting larger as you move downward. The color has type Color. Do not declare your variables to be static. If you have more than one of window frame, then each should remember its own information.

You will need a constructor for your class. Create constants telling the width and height (in pixels) of the window. For example, write the following for a 400x400 window.

  private static final int WINDOW_WIDTH  = 400;
  private static final int WINDOW_HEIGHT = 400;
Now begin the constructor by setting the width, height and title of the window.

  public Assignment10()
  {
    setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    setTitle("Graphics demo");
    ...
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }
where the ... stands for parts that you will add below. (The last line, setDefaultCloseOperation..., tells the frame to close the window when you hit the X button at the upper right corner. If you do not include that line, the X button will not do anything.)


Creating the interior of your window.

The JFrame object just manages the outer frame of the window. The interior is managed by a different class, JPanel. This section explains how to create the panel and how to put the panel into the window.

To create the panel, define constants PANEL_WIDTH and PANEL_HEIGHT in your class, telling how large the panel is (in pixels). For example, to make the panel have the same size as the window, say

  private static final int PANEL_WIDTH  = WINDOW_WIDTH;
  private static final int PANEL_HEIGHT = WINDOW_HEIGHT;
Now to create the panel, call it myPanel, and make it have a white background, do the following. (Add these to the constructor, so that the interior of the window is created when the window is created.)
  JPanel myPanel = new JPanel();
  myPanel.setLayout(new FlowLayout());
  myPanel.setBackground(Color.white);
  myPanel.setSize(PANEL_WIDTH, PANEL_HEIGHT);
Now add the panel to the window by adding it to the content pane of the window. Also do this in the constructor.
  Container contentPane = getContentPane();
  contentPane.setBackground(Color.white);
  contentPane.setLayout(new BorderLayout());
  contentPane.add(myPanel, BorderLayout.SOUTH);


Painting on the window

Write a paint method in your class with the following heading.

  public void paint(Graphics canvas)
This uses another class, Graphics, that is thought of as a canvas on which you can paint. What you paint will show up on the panel. Your class is a special kind of JFrame, and before you paint on the canvas, you should ask the JFrame to paint the background. Do that using line
  super.paint(canvas);
in the paint method. For now, that is all you will do. (Super is a way of refering to the thing that you are a special case of. That is, ask the JFrame class to do its paint operation.)


The main program

Your main program just creates an object of your class and makes it visible, so that you can see it on the screen.

  public static void main(String[] args) 
  {
    JFrame frame = new Assignment10();
    frame.setVisible(true);
  }
Now compile and run your program. All it will do is show the window, with nothing in it.


Drawing a disk

Now modify the paint method so that it draws a disk. If the Graphics object is called canvas, then canval.fillOval(x,y,width,height) will draw a filled oval with center (x,y) and the given width and height. To draw a red circle of diameter 20 pixels, you can say

  canvas.setColor(Color.red);
  canvas.fillOval(x, y, 20, 20);

The paint method will not be called unless you cause it to be called. To do that, add line

  frame.repaint();
to the end of the main program to repaint the window. Do not call paint directly. You need repaint to set up the canvas to paint on. Repaint will call paint for you.

Try the program now. You should see a disk painted in the window.


Action listeners

Shortly, you will add a button to your class. But you need to say what to do when the button is pushed. First, make your class a kind of ActionListener, meaning that it has a method called actionPerformed that performs actions in response to events such as button pushes. Modify your class heading as follows.

  public class Circle extends JFrame implements ActionListener
Now add an actionPerformed method that modifies the circle information and repaints. Here is a very simple one that remembers where the previous circle was, updates the center coordinates by adding 10 to each, and repaints the window.
  public void actionPerformed(ActionEvent e)
  {
    circlex += 10;
    circley += 10;
    repaint();
  }
The ActionEvent parameter tells what kind of event occurred. Since you will only have one kind of event, you can ignore it.


Adding a button

Now, in your constructor, create a button using the JButton class. Here is an example. It creates a green button labeled move.

  JButton button = new JButton("move");
  button.setBackground(Color.green);
You will want to make the button do something. If you write
  button.addActionListener(this);
then you are saying that, when this button is pushed, the program should perform the method called actionPerformed in the current object.

Finally, put the button on the panel.

  myPanel.add(button);
Do all of the above in the constructor, so that the button is created and added to the panel when the window is created.

Run your program. You should be able to push the button with the mouse and get a new disk drawn.


Choosing random numbers

You can use the Random class in the Java library to get pseudo-random numbers. It works similarly to your MyRandom class, but uses an algorithm that produces superior quality pseudo-random numbers. Add a variable of class Random to your class, and initialize it in your constructor.

  rand = new Random();
to create the pseudo-random number generator.

To get a pseudo-random integer from 0 to n-1, use rand.nextInt(n). For example, rand.nextInt(4) produces either 0 or 1 or 2 or 3, each equally likely.


Completing the assignment

Modify your program so that it chooses a random position and color for the circle. Some choices of colors are Color.black, Color.blue, Color.red, Color.cyan, Color.magenta, Color.yellow and Color.orange. You can find more in the documentation of the Color class.

Be sure that your disk does not cover the button. Think about how you can handle that.


Sprites

In situations where you need to do animations, it can be expensive to repaint the entire background whenever something moves. Sometimes you can get faster performance by only repainting the part that you need to.

Comment out the line

  super.paint();
from your paint method. Rerun the program. What happens?

If you just keep drawing disks without redrawing the background, more and more of your canvas will have disks painted on it. To avoid that, before drawing a new disk, erase the old one. You can erase it by painting it the same color as the background. (If the background is a complicated pattern, you need to remember what the pattern was.)

The paint method will need to know not only where the new disk is to be painted, but where the old disk is, so that it can erase the old disk. Just keep variables that tell you where the disk used to be, so that you can paint it the background color. For example, the actionPerformed method becomes

  public void actionPerformed(ActionEvent e)
  {
    oldcirclex = circlex;
    oldcircley = circley;
    circlex += 10;
    circley += 10;
    repaint();
  }
so that paint can use oldcirclex and oldcircley.

Try your modified program.


What to turn in

When you have completed your assignment, print it. Then ask either Niels or me to watch a demonstration. We will mark your printout as demonstrated.