Csci 210 Lab 2: Drawing

(Laura Toma adapted from Eric Chown)

Overview

In this lab you will build an interactive program that you can use to draw simple shapes. You'll use rectangles, ovals and lines and learn about some graphical features in Java, like buttons and panels. This is the sort of lab that looks much harder than it actually is, because of all the technical requirements of drawing in Java. Most of the work will be to read the documentation pages of the various components you will be using, and to understand how to put them together.

Some doc sites that you'll find useful:

and the class notes on Java GUIs: Graphics.pdf.

Background

To define GUIs in Java we will use the Swing library. Swing builds on a different graphics library in Java called awt. Applications created with awt depend on the platform, while applications created using Swing components have the same feel accross platforms (Macs, Windows, linux, etc).

A program can use the Swing package by including the statement:

import javax.swing.*;
at the head of your file. All classes defined in Swing have an extra capital J in front. For example we will use JFrame class (whereas in awt it would simply be a Frame). We will get to the definition of a JFrame in a moment.

In Java, when you want to draw something, or build a graphical interface, you need a "place" to put things. For example you might need a place to do the drawing, another place to put buttons, another to write text to the user, etc. A JFrame is one way to do this. JFrames are examples of Components in Java.

As the name suggests, we can put Components together to form more complex objects. Some components stand on their own (buttons, text areas), other are used to group/hold together components.

Component hierarchy: A GUI consists of components arranged in a hierarchy (one contained in another); for e.g. a window may contain a panel which contains a few buttons and labels, which in turn...and so on. The application has a top-level container that holds all the individual components together into a window. Swing provides a few top-level containers (JFrame, JDialog, JApplet). We will be using JFrame.

Events: One of the things we will want to do with Components is to have users interact with them. To do this we need to track things like where the mouse is, when it has been clicked, if a button was clicked, etc. In Java all of these things are called events. When a class wants to pay attention to an event it needs to register as an event listener to that event. For e.g., if a class wants to be notified of the mouse events, it needs to register as a mouse listener.

Outcome:

In this lab we will start to learn how to use and create Components, how to put them together and create listeners to use them with. Since this is our first experience with "real" Java, and this class is not about Java graphics, we will stick to fairly simple Components (namely JFrames, JButtons, JPanels, etc).

Your finished program for this lab should have a number of basic capabilities. It should be able to draw Rectangles, Ovals, and Lines using a color chosen by the user. Essentially, you'll specify the location and size of a Rectangle, for example, by using the mouse directly. For example, to draw a Rectangle you would hit the Rectangle button, then press the mouse to select one corner of the rectangle, drag the mouse to the opposite corner of the rectangle and let go. When you let go the rectangle should appear. We will proceed, as always, in incremental steps.

Creating a Window

The first thing we need to do is to make a window. The simplest way to do this is to make our program extend the JFrame class.

import javax.swing.*
public class MyDrawingProgram extends JFrame 

Our new class will require a constructor (this is often where a lot of the work of an interactive graphical program takes place). Remember, the constructor is a method that is declared like this:

public MyDrawingProgram() 
This is a version of the constructor with no parameters. Usually the first line in the constructor should be a call to the constructor of the super-class (JFrame). The JFrame constructor takes a string parameter which is used as the window's title. The corresponding call would be
super("My drawing window!");

JFrames have lots of other useful methods built in that you inherit with the extends call. These include setSize(int x, int y) to set its size, and setVisible(boolean value) to either display it or hide it. One thing to be careful about - you can't actually draw anything within the constructor (afterall the JFrame is still being constructed!).

For starters, try creating a program that extends the JFrame class. The constructor should call the parent constructor, size the window and show it (as a rule, the setVisible(true) command should be the last statement in a JFrame constructor). Or take a look at Graphics1.java.

Responding to the Mouse

You may already have some experience dealing with the mouse from 101. In this lab we will do more of the same, but again will do this in "real" Java. We will not use the ObjectDraw library.

In many cases Java provides unimplemented class definitions in an interface. We will talk about these more in class. The point of interfaces is that they leave the details of the implementation to the programmer. In the same time, the system knows how to interact with your program even though it does not know how you implement things. For example, it knows how to let your program know about Mouse events: since your program promises to implement all mouse functions, the mouse knows which functions to call on your program (though it will not know what exactly your methods do).

The first kind of interface that we are going to use is a MouseListener.

public class MyClass extends JFrame implements MouseListener 

MouseListener is defined in java.awt.event, so you'll need an import like this:

import java.awt.event.*;

The MouseListener interface calls for five methods that you will have to include in your class. The signatures of these are as follows:

mouseClicked(MouseEvent e)
mousePressed(MouseEvent e)
mouseReleased(MouseEvent e)
mouseEntered(MouseEvent e)
mouseExited(MouseEvent e)

All methods required by MouseListener interface have a MouseEvent as a parameter. When the mouse is clicked (and if your object is a listener to the mouse), a MouseEvent is passed to the object. A MouseEvent actually contains much more information than a simple location, but for our purposes we'll just worry about getting the location out. MouseEvents have getX()and getY() methods.

In this lab you'll need three of the methods. The mousePressed event will be used to initiate drawing. The mouseReleased event will be used to terminate a drawing command. The mouseClicked will be used to reset the drawing window. Essentially the user is to use the mouse to draw lines, ovals, and rectangles. The mousePressed command determines the starting point of the object to be drawn, while the mouseReleased command determines its final point (therefore its height and width if appropriate). Despite the fact that you only need three of these methods you still need to define all five. The implements keyword says that you will do so.

Finally, it is not enough to implement the MouseListener. You must also make sure that you add it to the JFrame (this seems strange since it is being defined as part of the JFrame). Basically, just add the following line to your constructor

addMouseListener(this);

You've added your class as a mouse listener!

Take a look at GraphicsAndMouse.java which contains everything discussed above. Compile it and play with it so that you get a feel for what the mouse events are and what are the functions that handle them.

Buttons

One of the issues we have to face in this program is selecting what it is we want to draw. We'll give the user choices by using JButtons. JButtons are another kind of Java component. You interact with them through the use of a listener. In this program we are going to attach our buttons to a JPanel, and then attach that JPanel to our JFrame. This might seem a bit excessive, but it is helpful to ensure that our buttons are placed nicely.

JButtons can be declared like any other variable. Once declared, the trick is to attach them to something (like a JPanel) and to define a listener, such that they respond to being pressed. To do this you'll need code very much like the following:

private JButton rectangleButton, lineButton;
JPanel buttonArray;

rectangleButton = new JButton("Rectangle");
lineButton = new JButton("Line");
buttonArray = new JPanel();

buttonArray.add(rectangleButton);
buttonArray.add(lineButton);

//define what happens when pressing the rectangle button
rectangleButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) {
      drawRectangles();
   }
   });
//define what happens when pressing the line button
lineButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) {
      drawLines();
   }
   });

//add the button panel to content panel of the frame
getContentPane().add("South", buttonArray);

In the example two buttons would be created. When the user pressed the "Rectangle" button, a method called drawRectangles() would be called (it would be up to you to define this method). The last line of the code adds the JPanel to the JFrame and tells Java that the panel should appear at the bottom of the frame (you could also use the other directions). This is our first example of the Java layout manager.

For this program you should define a number of buttons in order to choose the kind of object to draw as well as its color. So, for example, the drawLines() method might set a global variable objectType to a value corresponding to a framedRect. Note: the "draw" buttons shouldn't actually draw anything, they should set some variables such that the next press and drag combination do the drawing (e.g. to draw a rectangle I would first press the Rectangle button and then I would press the mouse at one corner and release it at the far corner).

Drawing: The paint method and the painting mechanism

There are many ways to get a Java to draw what you want. However, good programming style requires that all the Java drawing be done in a method called paint. This is a method inherited by any java.awt.Component (therefore also by a JFrame).
    public void paint(Graphics g)

This method is called on each component by the system and by the user:

When this method is invoked, the Graphics object parameter is pre-configured with the appropriate state for drawing on this particular component.

In general, programs should avoid placing drawing code at any point where it might be invoked outside the scope of the paint callback. Why? Because such code may be invoked at times when it is not appropriate to paint -- for instance, before the component is visible or has access to a valid Graphics object. It is not recommended that programs invoke paint() directly. Instead, whenever the application needs to trigger painting, it should call repaint().

For example, you could override the paint() method as follows:

public void paint(Graphics g) {
    g.setColor(Color.red); 
    g.drawRect(x, y, width, height);
    g.fillRect(x, y, width, height);
    g.setColor(Color.red);  // change the pen color to red
    g.drawLine(x, y, x1, y1);
    g.fillOval(x, y, width, height);

}
Whenever something changes (for example, when width or height change), you need to call repaint(), which will trigger a call to paint() (it is not recommended that programs invoke paint() directly).

All of the graphics stuff comes from java.awt. So you'll need an import statement like

 import java.awt.*; 

If you want to read more about the painting mechanism in Java, take a look at Painting in AWT and Swing. You will not understand everything (actually you will understand very little), but try to get a feeling for how the painting mechanism works --- it will save you a lot of trouble later. The key is that Java will invoke the paint method when it's time to paint (look at the section on painting guidelines).

Putting it together

When it is complete, your program should work as an interactive drawing tool. The user should be able to press buttons to select what they want to draw, or the current drawing color. Clicking the mouse should reset the drawing window. One way to do this is with the clearRect(int x, int y, int width, int height) graphics method).

Your program should have a nice, easy to use interface. Experiment with different layouts of the buttons, for example. Keep in mind that you can use multiple JPanels, and that even a single JPanel can have its own layout.

Your program must contain a main method so that I can test it from the console.

The most frequent error in a program like this comes from not worrying about what can go wrong. To really think like a computer scientist you need to consider all of the things that can go wrong in a program. For this program a common thing that can go wrong is that the user can specify the rectangle "backwards". E.g. the bottom right corner first, then the upper left second. Be sure that all possible pairs of corners work.

Style

Half of the lab grade is for functionality, the other half for style. For max score your program should follow programming guideline (for e.g., Writing clear code).

Possible extensions

Grading policy

The grading policy for this lab is the following:

Functionality: 60 points

Note: part of functionality is to handle various relative positions for the mouse presses.

Style: 40 points

What to hand in

  1. Email me the Java file as attachment;
  2. Hand in hard copy, and sign on the front page that you followed course rules.