Introduction to graphics

Java has extensive graphics capabilities. You can use these for

Much effort has been expended in improving Java graphics as this was an area of much frustration in earlier releases. The basic graphics package is something called the Abstract Window Toolkit (AWT). The improved graphics sits on top of a library known as ``Swing''. Our text makes frequent comparisons between the two. The legacy of AWT still influences the way people go about learning and using Java graphics.

Modern graphics and GUI's are a very successful application area of object oriented programming and a study of them may help you feel more comfortable with OO.

Graphics programs are somewhat unique and have patterns which you will find unusual at first. Once you learn them, they are often repeated and you can ignore the ``boiler plate''.

A very important aspect of GUI programming is event handling. We will cover aspects of this this week.

Look and feel

Swing is part of the Java Foundation Classes (JFC). It is a ``non-peer-based'' GUI toolkit in which the user interface elements (scrollbars, menus, text fields) are ``painted on'' to a uniform background.

Java lets you change the ``look and feel'' of these GUI elements to be similar to Mac, Windows and X-Motif. There is also a custom Java LAF known as ``Metal''. (example: PlafTest.java)

Hello World

Study the pattern of the following code which puts up a simple, titled frame:

/* example 7-1 of HC */

import javax.swing.*;

public class SwingFrame
{
    public static void main(String[] args)
    {
       HelloFrame frame = new HelloFrame();
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       frame.show();
    }
}

class HelloFrame extends JFrame
{
    public HelloFrame()
    {
        setTitle("Hello");
        setSize(WIDTH, HEIGHT);

   }

    public static final int WIDTH = 600;
    public static final int HEIGHT = 400;
}

This draws a top level graphics window. It uses a JFrame class which is a Swing extension of the AWT Frame class (Swing adds a J). It is one of the few ``non-painted'' graphics components.

Frames are examples of containers and we will study ways of inserting graphics objects into them.

Note the patterns in the sample code:

Frame positioning

Many of the useful JFrame methods are inherited from other classes further up the hierarchy:

 
Figure 1: Figure 7-5 of HC vol 1

>From the Frame class the following methods are useful

Other methods are available from the Component and Window classes (particularly methods to resize and position the frame):

Note that the coordinates in JFrame methods are with respect to the whole screen. In other swing classes they are usually with respect to a specific container.

It is possible to get the dimensions of the screen you are using by creating an object of class Toolkit and calling its getScreenSize() method. This returns the width and height as fields in a dimension object. The pattern is

        Toolkit kit = Toolkit.getDefaultToolkit();
        Dimension screenSize = kit.getScreenSize();
        int screenHeight = screenSize.height;
        int screenWidth = screenSize.width;
(where we note that the height and width are public variables!).

The example program CenteredFrame.java shows this in action. Note that you can also read in your own icon for the image on the top left of the window.

See pages 307-309 of HC for other API notes. Also see the main Java API documentation (and remember to follow the links through to super classes).

Displaying information in a panel

Although it is possible to draw directly onto a JFrame this is considered to be bad programming practice. The thing most swing programmers do is to add components to the content pane which is part of the JFrame. (See fig 7-7.) The thing to do is to call the getContentPane() method of your JFrame to create a Container object and then to add Components to this.

        Container contentPane = getContentPane();
        Component c = ...;
        contentPane.add(c);

Our hello world program adds an object which extends JPanel:

        Container contentPane = getContentPane();
        HelloPanel panel = new HelloPanel();
        contentPane.add(panel);

What are panels? They are

Why do you extend JPanel? Because the basic panel is very boring. You need to override its paintComponent method so that the system will draw interesting objects.

Here is the complete program.

/* example 7-3 of HC */

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

public class SwingHelloWorld
{
    public static void main(String[] args)
    {
       HelloFrame frame = new HelloFrame();
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       frame.show();
    }
}

class HelloFrame extends JFrame
{
    public HelloFrame()
    {
        setTitle("Hello");
        setSize(WIDTH, HEIGHT);

        // add panel to frame
        HelloPanel panel = new HelloPanel();
        Container contentPane = getContentPane();
        contentPane.add(panel);
    }

    public static final int WIDTH = 600;
    public static final int HEIGHT = 400;
}

/**
A panel that displays a message
*/
class HelloPanel extends JPanel
{
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.drawString("Hello World!", MESSAGE_X, MESSAGE_Y);
    }

    public static final int MESSAGE_X = 75;
    public static final int MESSAGE_Y = 200;
}

2D shapes

Although the previous example was a complicated form of saying hello, we have now set the stage to actually draw pictures! All of the action in our following examples will take place within the paintComponent method of our custom JPanel class.

Unfortunately, the old Graphics class has very limited methods for drawing shapes (you could not vary line thicknesses or rotate shapes amongst other shortcomings). Java now has a powerful Java2D library which you access by casting your Graphics object to a Graphics2D object and then accessing its methods.

The Java2D library implements geometric shapes in an object oriented fashion. There are classes to represent points, lines, rectangles and ellipses and they build on one another in an object-oriented way. They implement the Shape interface. In order to draw something, you create such an object and then pass it to the draw method of your Graphics2D object.

See figure 7-10 for an example of relationships between some shape classes.

 
Figure 2: Figure 7-10 of HC vol 1

Heavy weather

HC make heavy weather of the difference between float and double arguments of shape objects. We will step through some of these during lectures, but I suggest that you try to remember to call them with constants ending with a capital F. (Alternatively, use the Rectangle2D.Double type objects as they do.

Colours

Java2d has some great colours which you can set using the setPaint method. You select a colour, draw and then select a new colour.

For example, the following code fills in an ellipse and a rectangle.

/* Example 7.5 of Core Java Vol 1 */

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

public class FillTest
{
    public static void main(String[] args)
    {
        FillFrame frame = new FillFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.show();
    }
}

/* A frame that contains a panel with drawings */
class FillFrame extends JFrame
{
    public FillFrame()
    {
        setTitle("FillTest");
        setSize(WIDTH,HEIGHT);
        // add panel to frame
        FillPanel panel = new FillPanel();
        Container contentPane = getContentPane();
        contentPane.add(panel);
    }
    public static final int WIDTH = 400;
    public static final int HEIGHT = 400;
}

/* A panel that displays filled rectangles and ellipses */
class FillPanel extends JPanel
{
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;
        
        // draw a rectangle
        double leftX = 100;
        double topY = 100;
        double width = 200;
        double height = 150;

        Rectangle2D rect = new Rectangle2D.Double(leftX,
                                  topY, width, height);
        g2.setPaint(Color.red);
        g2.fill(rect);

        // draw the enclosed ellipse
        Ellipse2D ellipse = new Ellipse2D.Double();
        ellipse.setFrame(rect);
        g2.setPaint(new Color(0, 128, 128)); // dull blue green
        g2.fill(ellipse);
    }
}

Fonts

As well as having access to named fonts, Java allows you to specify logical font names which are always mapped to actual fonts which exist on the client machine.

You use fonts by creating a font object and calling setFont.

        String message = "Hello World!";
        Font f = new Font("Serif",Font.BOLD, 36);
        g2.setFont(f);
        g2.drawString(message, x, y);

Remember that in one of our early lab exercises we put effort into centering text. It is possible to very exactly center your text taking its actual font dimensions into account. This is the point of our last example. See Fig 7-13, and the discussion nearby, to understand the details.

/* Example 7.6 of Core Java Vol 1 */

import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;

public class FontTest
{
    public static void main(String[] args)
    {   FontFrame frame = new FontFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.show();
    }
}

/* A frame with a text message panel */

class FontFrame extends JFrame
{
    public FontFrame()
    {
        setTitle("FontTest");
        setSize(WIDTH,HEIGHT);
        // add panel to frame
        FontPanel panel = new FontPanel();
        Container contentPane = getContentPane();
        contentPane.add(panel);
    }
    public static final int WIDTH = 400;
    public static final int HEIGHT = 400;
}

/* A panel that shows a centered message in a box. */
class FontPanel extends JPanel
{
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;

        String message = "Hello World!";
        Font f = new Font("Serif",Font.BOLD, 36);
        g2.setFont(f);

        // measure the size of the message
        FontRenderContext context = g2.getFontRenderContext();
        Rectangle2D bounds = f.getStringBounds(message, context);

        // set (x,y) to top left corner of text
        double x = (getWidth()-bounds.getWidth())/2.;
        double y = (getHeight()-bounds.getHeight())/2.;

        // add ascent to y to reach baseline
        double ascent = -bounds.getY();
        double baseY = y + ascent;

        // draw the message
        g2.drawString(message, (int)x, (int)baseY);
        
        // draw the baseline
        g2.setPaint(Color.gray);
        g2.draw(new Line2D.Double(x, baseY,
                 x+ bounds.getWidth(), baseY));

        //draw the enclosing rectangle
        Rectangle2D rect = new Rectangle2D.Double(x,y,
              bounds.getWidth(), bounds.getHeight());
        g2.draw(rect);
        }
    }

 
Figure 3: Figure 7-13 of HC vol 1



Henry Gardner
2001-03-21