Java ScreenShot
Screenshot      

Screenshot Core Java 2: Volume I - Fundamentals

Table of Contents
 7.  Graphics Programming


Displaying Information in a Panel

In this section, we'll show you how to display information inside a frame. For example, rather than displaying "Not a Hello, World program" in text mode in a console window as we did in , we will display the message in a frame, as shown in Screenshot-6.

Screenshot-6. A simple graphical program

Java graphics 07fig06.gif


It would be possible to draw the message string directly onto a frame, but that is not considered good coding practice. In Java, frames are really designed to be containers for components such as a menu bar and other user interface elements. You normally draw on another component, called a panel, which you add to the frame. The structure of a JFrame is surprisingly complex. Look at Screenshot-7 which shows the makeup of a JFrame. As you can see, four panes are layered in a JFrame. The root pane, layered pane, and glass pane are of no interest to us; they are required to organize the menu bar and content pane and to implement the look and feel. The part that most concerns Swing programmers is the content pane. When designing a frame, you add components into the content pane, using code such as the following:

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


Screenshot-7. The internal structure of a JFrame

Java graphics 07fig07.gif


Alternatively, if you just want to show a single Swing component in the frame, then you can set the content pane to that component:

JComponent c = . . .;
frame.setContentPane(c);


Java graphics notes_icon.gif

If you are familiar with AWT programming, you know that you used to call the add method to add components directly into an AWT Frame. In Swing, that is not possible. You must add all components into the content pane.

In our case, we want to add a single panel to the content pane onto which we will draw our message. Panels are implemented by the JPanel class. They are user interface elements with two useful properties:
  • They have a surface onto which you can draw;
  • They themselves are containers.

Thus, they can hold other user interface components such as buttons, sliders, and so on. However, there is no sense in adding a plain JPanel to the content pane—it doesn't do anything interesting. To make it more interesting, you use inheritance to create a new class and then override or add methods to get the extra functionality you want. In particular, to draw on a panel, you will need to:

  • Define a class that extends JPanel;
  • Override the paintComponent method in that class.

The paintComponent method is actually in JComponent—the parent class for all nonwindow Swing components. It takes one parameter of type Graphics. A Graphics object remembers a collection of settings for drawing images and text, such as the font you set or the current color. All drawing in Java must go through a Graphics object. It has methods that draw patterns, images, and text.

Java graphics notes_icon.gif

The Graphics parameter is similar to a device context in Windows or a graphics context in X11 programming.

Here's how to make a panel onto which you can draw:
class MyPanel extends JPanel
{
 public void paintComponent(Graphics g)
 {
 . . . // code for drawing will go here
 }
}


Each time a window needs to be redrawn, no matter what the reason, the event handler notifies the component. This causes the paintComponent methods of all components to be executed. Never call the paintComponent method yourself. It is called automatically whenever a part of your app needs to be redrawn, and you should not interfere with this automatic process. What sorts of actions trigger this automatic response? For example, painting occurs because the user increased the size of the window or minimized and then restored the window. If the user popped up another window and it covered an existing window and then made the overlaid window disappear, the app window that was covered is now corrupted and will need to be repainted. (The graphics system does not save the pixels underneath.) And, of course, when the window is displayed for the first time, it needs to process the code that specifies how and where it should draw the initial elements.

Java graphics exclamatory_icon.gif

If you need to force repainting of the screen, call the repaint method instead of paintComponent. The repaint method will cause paintComponent to be called for all components, with properly configured Graphics arguments.

As you saw in the code fragment above, the paintComponent method takes a single parameter of type Graphics. Measurement on a Graphics object for screen display is done in pixels. The (0, 0) coordinate denotes the top-left corner of the component on whose surface you are drawing. Displaying text (usually called rendering text) is considered a special kind of drawing. The Graphics class has a drawString method that has the following syntax:
g.drawString(text, x, y)


In our case, we want to draw the string "Not a Hello, World Program" in our original window, roughly one-quarter of the way across and halfway down. Although we don't yet know how to measure the size of the string, we'll start the string at coordinates (75, 100). This means the first character in the string will start at a position 75 pixels to the right and 100 pixels down. (Actually, it is the baseline for the text that is 100 pixels down—see below for more on how text is measured.) Thus, our paintComponent method looks like this:

class NotHelloWorldPanel extends JPanel
{
 public void paintComponent(Graphics g)
 {
 . . . // see below
 g.drawString("Not a Hello, World program",
 MESSAGE_X, MESSAGE_Y);
 }
 public static final int MESSAGE_X = 75;
 public static final int MESSAGE_Y = 100;
}


However, this paintComponent method is not complete. The NotHelloWorldPanel class extends the JPanel class, which has its own idea how to paint the panel, namely, to fill it with the background color. To make sure that the superclass does its part of the job, we must call super.paintComponent before doing any painting on our own.

class NotHelloWorldPanel extends JPanel
{
 public void paintComponent(Graphics g)
 {
 super.paintComponent(g);
 . . . // code for drawing will go here
 }
}


Java graphics notes_icon.gif

If you have programmed for the old AWT, you will have noticed quite a few differences. In the old AWT, you drew onto a Canvas, a subclass of Component just for drawing. In Swing, there is no special canvas class. You can draw onto any Swing component, but if you want to separate a drawing area from the remainder of your user interface, you should just render the drawing on the surface of a JPanel subclass. More importantly, you no longer use the paint method for drawing. In fact, if you override paint, then your program will not work correctly. You then interfere with the JComponent.paint method which carries out a number of complex actions, such as setting up the graphics context and image buffering. In Swing, you should always use the paintComponent method. In the old AWT, you may have defined the update method to call paint without erasing the window to avoid screen flicker. This is no longer necessary in Swing. The Swing components use buffering for flicker-free painting.

Example 7-3 shows the complete code.
Example 7-3 NotHelloWorld.java
 1. import javax.swing.*;
 2. import java.awt.*;
 3.
 4. public class NotHelloWorld
 5. {
 6. public static void main(String[] args)
 7. {
 8. NotHelloWorldFrame frame = new NotHelloWorldFrame();
 9. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
10. frame.show();
11. }
12. }
13.
14. /**
15. A frame that contains a message panel
16. */
17. class NotHelloWorldFrame extends JFrame
18. {
19. public NotHelloWorldFrame()
20. {
21. setTitle("NotHelloWorld");
22. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
23.
24. // add panel to frame
25.
26. NotHelloWorldPanel panel = new NotHelloWorldPanel();
27. Container contentPane = getContentPane();
28. contentPane.add(panel);
29. }
30.
31. public static final int DEFAULT_WIDTH = 300;
32. public static final int DEFAULT_HEIGHT = 200;
33. }
34.
35. /**
36. A panel that displays a message.
37. */
38. class NotHelloWorldPanel extends JPanel
39. {
40. public void paintComponent(Graphics g)
41. {
42. super.paintComponent(g);
43.
44. g.drawString("Not a Hello, World program",
45. MESSAGE_X, MESSAGE_Y);
46. }
47.
48. public static final int MESSAGE_X = 75;
49. public static final int MESSAGE_Y = 100;
50. }


javax.swing.JFrame 1.2

Java graphics api_icon.gif
  • Container getContentPane()

    returns the content pane object for this JFrame

java.awt.Component 1.0

Java graphics api_icon.gif
  • void repaint()

    causes a repaint of the component "as soon as possible."

  • public void repaint(int x, int y, int width, int height)

    causes a repaint of a part of the component "as soon as possible."

javax.swing.JComponent 1.2

Java graphics api_icon.gif
  • void paintComponent(Graphics g)

    Override this method to describe how your component needs to be painted.


Java ScreenShot
Screenshot      
Top
 

Comments