This example develops a useful class for displaying various figures. The class enables the user to set the figure type and specify whether the figure is filled, and displays the figure on a panel. The UML diagram for the class is shown in Figure 13.9. The panel can display lines, rectangles, round-cornered rectangles, and ovals. Which figure to display is decided by the type property. If the filled property is true, the rectangle, round-cornered rectangle, and oval are filled in the panel.
The UML diagram serves as the contract for the FigurePanel class. The user can use the class without knowing how the class is implemented. Let us begin by writing a program in Listing 13.4 that uses the class to display six figure panels, as shown in Figure 13.10.
1 import java.awt.*; 2 import javax.swing.*; 3 4 public class TestFigurePanel extends JFrame { 5 public TestFigurePanel() { 6 setLayout( new GridLayout( 2 , 3 , 5 , 5 )); 7 add( new FigurePanel(FigurePanel.LINE)); 8 add( new FigurePanel(FigurePanel.RECTANGLE)); 9 add( new FigurePanel(FigurePanel.ROUND_RECTANGLE)); 10 add( new FigurePanel(FigurePanel.OVAL)); 11 add( new FigurePanel(FigurePanel.RECTANGLE, true )); 12 add( new FigurePanel(FigurePanel.ROUND_RECTANGLE, true )); 13 } 14 15 public static void main(String[] args) { 16 TestFigurePanel frame = new TestFigurePanel(); 17 frame.setSize( 400 , 200 ); 18 frame.setTitle( "TestFigurePanel" ); 19 frame.setLocationRelativeTo( null ); // Center the frame 20 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 21 frame.setVisible( true ); 22 } 23 } |
The FigurePanel class is implemented in Listing 13.5. Four constants, LINE , RECTANGLE , ROUND_RECTANGLE , and OVAL , are declared in lines 6 “9. Four types of figures are drawn according to the type property (line 37). The setColor method (lines 39, 44, 53, 62) sets a new color for the drawing.
1 import java.awt.*; 2 import javax.swing.JPanel; 3 4 public class FigurePanel extends JPanel { 5 // Define constants 6 public static final int LINE = 1 ; 7 public static final int RECTANGLE = 2 ; 8 public static final int ROUND_RECTANGLE = 3 ; 9 public static final int OVAL = 4 ; 10 11 private int type = 1 ; 12 private boolean filled; 13 14 /** Construct a default FigurePanel */ 15 public FigurePanel() { 16 } 17 18 /** Construct a FigurePanel with the specified type */ 19 public FigurePanel( int type) { 20 this .type = type; 21 } 22 23 /** Construct a FigurePanel with the specified type and filled */ 24 public FigurePanel( int type, boolean filled) { 25 this .type = type; 26 this .filled = filled; 27 } 28 29 /** Draw a figure on the panel */ 30 public void paintComponent(Graphics g) { 31 super .paintComponent(g); 32 33 // Get the appropriate size for the figure 34 int width = getSize().width; 35 int height = getSize().height; 36 37 switch (type) { 38 case LINE: // Display two cross lines 39 g.setColor(Color.BLACK); 40 g.drawLine ( 10 , 10 , width - 10 , height - 10 ); 41 g.drawLine (width - 10 , 10 , 10 , height - 10 ); 42 break ; 43 case RECTANGLE: // Display a rectangle 44 g.setColor(Color.BLUE); 45 if (filled) 46 g.fillRect (( int )( . 1 * width), ( int )( . 1 * height), 47 ( int )( . 8 * width), ( int )( . 8 * height)); 48 else 49 g.drawRect (( int )( . 1 * width), ( int )( . 1 * height), 50 ( int )( . 8 * width), ( int )( . 8 * height)); 51 break ; 52 case ROUND_RECTANGLE: // Display a round-cornered rectangle 53 g.setColor(Color.RED); 54 if (filled) 55 g.fillRoundRect (( int )( . 1 * width), ( int )( . 1 * height), 56 ( int )( . 8 * width), ( int )( . 8 * height), 20 , 20 ); 57 else 58 g.drawRoundRect (( int )( . 1 * width), ( int )( . 1 * height), 59 ( int )( . 8 * width), ( int )( . 8 * height), 20 , 20 ); 60 break ; 61 case OVAL: // Display an oval 62 g.setColor(Color.BLACK); 63 if (filled) 64 g.fillOval (( int )( . 1 * width), ( int )( . 1 * height), 65 ( int )( . 8 * width), ( int )( . 8 * height)); 66 else 67 g.drawOval (( int )( . 1 * width), ( int )( . 1 * height), 68 ( int )( . 8 * width), ( int )( . 8 * height)); 69 } 70 } 71 72 /** Set a new figure type */ 73 public void setType( int type) { 74 this .type = type; 75 repaint(); 76 } 77 78 /** Return figure type */ 79 public int getType() { 80 return type; 81 } 82 83 /** Set a new filled property */ 84 public void setFilled( boolean filled) { 85 this .filled = filled; 86 repaint(); 87 } 88 89 /** Check if the figure is filled */ 90 public boolean isFilled() { 91 return filled; 92 } 93 94 /** Specify preferred size */ 95 public Dimension getPreferredSize() { 96 return new Dimension( 80 , 80 ); 97 } 98 } |
The repaint method (lines 75, 86) is defined in the Component class. Invoking repaint causes the paintComponent method to be called. The repaint method is invoked to refresh the viewing area. Typically, you call it if you have new things to display.
Caution
The paintComponent method should never be invoked directly. It is invoked either by the JVM whenever the viewing area changes or by the repaint method. You should override the paintComponent method to tell the system how to paint the viewing area, but never override the repaint method. |
Note
The repaint method lodges a request to update the viewing area and returns immediately. Its effect is asynchronous, and if several requests are outstanding, it is likely that only the last paintComponent will be done. |
The getPreferredSize() method (lines 95 “97), defined in Component , is overridden in FigurePanel to specify the preferred size for the layout manager to consider when laying out a FigurePanel object. This property may or may not be considered by the layout manager, depending on its rules. For example, a component uses its preferred size in a container with a FlowLayout manager, but its preferred size may be ignored if it is placed in a container with a GridLayout manager. It is a good practice to override getPreferredSize() in a subclass of JPanel to specify a preferred size, because the default preferred size for a JPanel is by .