Saturday, July 19, 2008

Very Basic Java Graphics: 3 Examples

We're going to build up a graphics app from something so basic it doesn't really work, like the example from Java Graphics--Start with a JFrame and building to something more sophisticated. In the JFrame article and in A Most Basic Graphics App I've avoided the use of comments in the code to keep it shorter. In these examples I'm going to include comments in the code to describe what's happening. So don't be scared of the length of these examples, most of what's there is just comments, which Java ignores. They're there just for humans.
/* BasicFrame.java

This is a really simple graphics program.
It opens a frame on the screen with a single
line drawn across it.

It's not very polished, but it demonstrates
a graphical program as simply as possible.mag-27Apr2008
*/

// Import the basic graphics classes.
import java.awt.*;
import javax.swing.*;

public class BasicFrame extends JFrame{

// Create a constructor method
public BasicFrame(){
// All we do is call JFrame's constructor.
// We don't need anything special for this
// program.
super();
}

// The following methods are instance methods.
/* Create a paint() method to override the one in JFrame.
This is where the drawing happens.
We don't have to call it in our program, it gets called
automatically whenever the frame needs to be redrawn,
like when it it made visible or moved or whatever.*/
public void paint(Graphics g){
g.drawLine(10,10,150,150); // Draw a line from (10,10) to (150,150)
}

public static void main(String arg[]){
// create an identifier named 'window' and
// apply it to a new BasicFrame object
// created using our constructor, above.
BasicFrame frame = new BasicFrame();

// Use the setSize method that our BasicFrame
// object inherited to make the frame
// 200 pixels wide and high.
frame.setSize(200,200);

// Make the window show on the screen.
frame.setVisible(true);
}
}

This example draws a line on the JFrame. The window decorations take up some of our drawing space, so the title bar may cover some of our line. With some JVMs the background area of the JFrame won't be cleared before you draw. Also, when you close the window using the close button, the application doesn't shut down. For many applications on Mac OS X this is normal behavior, but on other OSes, and even many applications under OS X it's normal to expect an application to close down completely if you click the close button on the last open window (for applications that have multiple windows) or the close button on the main window for other applications.

So here's our next example, where we take care of that last problem:
/* CloseFrame.java
This is a really simple graphics program.
It opens a frame on the screen with a single
line drawn across it.

We're starting to add a little bit of polish
here--we make the program close nicely when
the close box is clicked, rather than just
sort of hanging around half-dead.
mag-28Apr2008
*/

// Import the basic graphics classes.
import java.awt.*;
import javax.swing.*;

public class CloseFrame extends JFrame{

// Create a constructor method
public CloseFrame(){
// All we do is call JFrame's constructor.
// We don't need anything special for this.
super();
}

// The following methods are instance methods.

/* Create a paint() method to override the one in JFrame.
This is where the drawing happens.
We don't have to call it in our program,
it gets called automatically whenever the
frame needs to be redrawn, like when it is
made visible or moved or whatever.
*/
public void paint(Graphics g){
g.drawLine(10,10,150,150); // Draw a line from (10,10) to (150,150)
}

public static void main(String arg[]){
BasicFrame frame = new BasicFrame();
// This uses a constant EXIT_ON_CLOSE that's a member of JFrame.
// The constant is passed to the setDefaultCloseOperation method
// of our frame object, which is a CloseFrame object,
// which inherits the method from its parent JFrame.
// It makes the program exit (close completely) when we click
// the close button.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200,200);frame.setVisible(true);
}
}

The magic line that makes the application exit when we click the close button is the one that reads: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);. We can tell that EXIT_ON_CLOSE is a type of variable called a "constant" because the name follows the convention of being written in all capital letters. So we're not YELLING when we say "EXIT_ON_CLOSE" we're using the value stored in EXIT_ON_CLOSE as part of the JFrame class.
A JFrame has this constant available because it implements the interface WindowConstants. By implementing this interface, in essence, the JFrame class of objects promise to do the right thing if one of these constants is passed to a method that handles it, in this case setDefaultCloseOperation.

Our final example for this entry goes one big step further:
/* BasicJPanel.java
This is a somewhat more sophisticated drawing program.
It uses a new child of JPanel as the drawing surface
for a JFrame, to avoid the problems with drawing
directly on a JFrame.

A custom JPanel child class called BasicJPanel is created
with its own paintComponent method, which contains our
drawing code.

A generic JFrame is then created to hold the BasicJPanel
object, the BasicJPanel is created, made into the JPanel's
content pane, and our paintComponent method is called
automatically. *Whew!*
mag-28Apr2008
*/

// Import the basic graphics classes.
import java.awt.*;
import javax.swing.*;

public class BasicJPanel extends JPanel{

// Create a constructor method
public BasicJPanel(){
super();
}

// The following methods are instance methods.

/* Create a paintComponent() method to override the one in
JPanel.This is where the drawing happens. We don't have
to call it in our program, it gets called automatically
whenever the panel needs to be redrawn, like when it is
made visible or moved or whatever.
*/
public void paintComponent(Graphics g){
g.drawLine(10,10,150,150); // Draw a line from (10,10) to (150,150)
}

public static void main(String arg[]){
JFrame frame = new JFrame("BasicJPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200,200);

// Create a new identifier for a BasicJPanel called "panel",
// then create a new BasicJPanel object for it to refer to.
BasicJPanel panel = new BasicJPanel();

// Make the panel object the content pane of the JFrame.
// This puts it into the drawable area of frame, and now
// we do all our drawing to panel, using paintComponent(), above.
frame.setContentPane(panel);
frame.setVisible(true);
}
}

Here, we've added a JPanel inside the drawable area of our JFrame. This means that if we start drawing at 0,0 we'll actually be drawing somewhere we can see it, it won't be hidden by the window decorations.

The other big change is the change of our drawing method's name from paint() to paintComponent(). This is because we changed from a JFrame to a JPanel. If you take a look at JPanel in the API Specification you'll see that it extends JComponent. This family of objects uses paintComponent() to manage drawing.

This last example is a good starting point for any graphical operation. In future examples, I'll be extending it further to add additional features.