It is beyond the capacity of a single chapter in a book to fully cover the components, and I have no intention of trying. For the most part, the javadocs (You have been using them, right?) are sufficient for describing the many properties and methods of the various components. So, this chapter will conclude by combining the learn-by-example approach with your own ability to use the javadocs to deepen your understanding.
Example 12.8 is a larger code example that creates a JFrame containing most of the JComponents listed in tables 12-9 through 12-16. JOptionPane and JColorChooser will be incorporated into the program in the next chapter when we begin to customize the application’s behavior. Except for lines 67 - 77 which make brief use of the List, Vector and Arrays class, nothing in the application is beyond the scope of this or previous chapters. You may safely ignore these lines as you read through the code or, if you would like, browse ahead to the chapter on Collections. Please read through the code, then compile and run it. Figure 12-41 shows the results of running example 12.8. Click buttons, type text into the fields, resize the window and generally experiment to get an idea of how much behavior the AWT and the Swing components provide by default. Consult tables 12-9 through 12-16 and figure 12-42 to make sure you know which component is which and what it does. Finally, study the output of the call to TreePrinterUtils.printLayout (line 100) and figure 12-43 to solidify your understanding of the application’s layout.
Example 12.8: chap12.MainFrame.java
1 package chap12; 2 3 import java.awt.BorderLayout; 4 import java.awt.Color; 5 import java.awt.GridLayout; 6 import java.util.Arrays; 7 import java.util.List; 8 import java.util.Vector; 9 10 import javax.swing.ButtonGroup; 11 import javax.swing.JButton; 12 import javax.swing.JCheckBox; 13 import javax.swing.JComboBox; 14 import javax.swing.JFrame; 15 import javax.swing.JLabel; 16 import javax.swing.JList; 17 import javax.swing.JMenu; 18 import javax.swing.JMenuBar; 19 import javax.swing.JMenuItem; 20 import javax.swing.JPanel; 21 import javax.swing.JPasswordField; 22 import javax.swing.JRadioButton; 23 import javax.swing.JScrollPane; 24 import javax.swing.JSlider; 25 import javax.swing.JTextArea; 26 import javax.swing.JTextField; 27 import javax.swing.JToggleButton; 28 import javax.swing.border.EtchedBorder; 29 import javax.swing.border.LineBorder; 30 import javax.swing.border.TitledBorder; 31 32 import utils.TreePrinterUtils; 33 34 public class MainFrame extends JFrame { 35 protected JPanel displayOptionsPanel; 36 protected JButton bgColorButton; 37 protected JButton defaultColorButton; 38 39 protected JToggleButton lockingToggleButton; 40 protected JTextArea textArea; 41 protected JComboBox fontStyleComboBox; 42 protected JSlider fontSizeSlider; 43 protected JLabel sliderLabel; 44 protected JLabel eventLabel; 45 46 protected JList saladList; 47 protected JTextField chosenItemTextField; 48 49 protected JPasswordField secretCodeField; 50 51 protected JMenuItem menuItem1; 52 protected JMenuItem menuItem2; 53 protected JMenuItem menuItem3; 54 protected JMenuItem menuItem4; 55 56 protected JCheckBox vegetablesCheckBox; 57 protected JCheckBox fruitsCheckBox; 58 protected JCheckBox nutsCheckBox; 59 protected JCheckBox cheesesCheckox; 60 61 protected JRadioButton titleBorderRadioButton; 62 protected JRadioButton lineBorderRadioButton; 63 protected JRadioButton etchedBorderRadioButton; 64 protected JRadioButton bevelBorderRadioButton; 65 protected JRadioButton noBorderRadioButton; 66 67 protected List vegetables = 68 Arrays.asList( 69 new String[] { "Tomatoes", "Lettuce", "Cucumbers", "Olives" }); 70 protected List fruits = 71 Arrays.asList(new String[] { "Apples", "Oranges", "Dates" }); 72 protected List nuts = 73 Arrays.asList(new String[] { "Walnuts", "Almonds", "Peanuts" }); 74 protected List cheeses = 75 Arrays.asList( 76 new String[] { "Jack Cheese", "Cheddar Cheese", "Jalapeno Cheese" }); 77 protected Vector saladListItems = new Vector(vegetables); 78 79 public MainFrame() { 80 super("Introduction to Swing GUIs"); 81 JMenuBar menuBar1 = createMenuBar(); 82 setJMenuBar(menuBar1); 83 84 JPanel contentPane = new JPanel(); 85 setContentPane(contentPane); 86 contentPane.setLayout(new BorderLayout()); 87 88 JPanel centerPanel = createCenterPanel(); 89 contentPane.add(centerPanel, BorderLayout.CENTER); 90 91 JPanel northPanel = createNorthPanel(); 92 contentPane.add(northPanel, BorderLayout.NORTH); 93 94 JPanel southPanel = createSouthPanel(); 95 contentPane.add(southPanel, BorderLayout.SOUTH); 96 97 setSize(600, 600); 98 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 99 setVisible(true); 100 TreePrinterUtils.printLayout(this); 101 } 102 private JMenuBar createMenuBar() { 103 JMenuBar menuBar = new JMenuBar(); 104 JMenu menu1 = new JMenu("Menu 1"); 105 menuBar.add(menu1); 106 menuItem1 = new JMenuItem("Menu Item 1"); 107 menu1.add(menuItem1); 108 menuItem2 = new JMenuItem("Menu Item 2"); 109 menu1.add(menuItem2); 110 JMenu menu2 = new JMenu("Menu 2"); 111 menuBar.add(menu2); 112 menuItem3 = new JMenuItem("Menu Item 3"); 113 menu2.add(menuItem3); 114 menuItem4 = new JMenuItem("Menu Item 4"); 115 menu2.add(menuItem4); 116 return menuBar; 117 } 118 private JPanel createCenterPanel() { 119 JPanel centerPanel = new JPanel(new BorderLayout()); 120 JPanel centerNorthPanel = new JPanel(new GridLayout(0, 2)); 121 eventLabel = 122 new JLabel("Textarea events will display here.", JLabel.CENTER); 123 eventLabel.setBorder(new LineBorder(Color.red)); 124 eventLabel.setOpaque(true); 125 eventLabel.setBackground(Color.yellow); 126 eventLabel.setForeground(Color.red); 127 centerNorthPanel.add(eventLabel, BorderLayout.CENTER); 128 lockingToggleButton = new JToggleButton("Lock Text Area"); 129 centerNorthPanel.add(lockingToggleButton, BorderLayout.WEST); 130 centerPanel.add(centerNorthPanel, BorderLayout.NORTH); 131 132 textArea = new JTextArea(30, 60); 133 textArea.setFont(textArea.getFont().deriveFont((float)24)); 134 textArea.setLineWrap(true); 135 textArea.setWrapStyleWord(true); 136 centerPanel.add(new JScrollPane(textArea), BorderLayout.CENTER); 137 return centerPanel; 138 } 139 private JPanel createNorthPanel() { 140 JPanel northPanel = new JPanel(new BorderLayout()); 141 142 displayOptionsPanel = createDisplayOptionsPanel(); 143 northPanel.add(displayOptionsPanel, BorderLayout.WEST); 144 145 JPanel saladOptionsPanel = createSaladOptionsPanel(); 146 northPanel.add(saladOptionsPanel, BorderLayout.CENTER); 147 148 return northPanel; 149 } 150 private JPanel createDisplayOptionsPanel() { 151 displayOptionsPanel = new JPanel(); 152 displayOptionsPanel.setBorder(new TitledBorder("Panel Options")); 153 154 JPanel colorButtonPanel = new JPanel(new GridLayout(0, 1)); 155 bgColorButton = new JButton("Choose Background Color"); 156 bgColorButton.setToolTipText("Click to select background color."); 157 colorButtonPanel.add(bgColorButton); 158 defaultColorButton = new JButton("Default Background Color"); 159 defaultColorButton.setToolTipText( 160 "Click to restore background color to its default."); 161 colorButtonPanel.add(defaultColorButton); 162 displayOptionsPanel.add(colorButtonPanel); 163 164 JPanel radioPanel = new JPanel(new GridLayout(0, 1)); 165 radioPanel.setBorder(new TitledBorder("Borders")); 166 167 noBorderRadioButton = new JRadioButton("No Border"); 168 radioPanel.add(noBorderRadioButton); 169 titleBorderRadioButton = new JRadioButton("TitleBorder"); 170 titleBorderRadioButton.setSelected(true); 171 radioPanel.add(titleBorderRadioButton); 172 lineBorderRadioButton = new JRadioButton("LineBorder"); 173 radioPanel.add(lineBorderRadioButton); 174 etchedBorderRadioButton = new JRadioButton("EtchedBorder"); 175 radioPanel.add(etchedBorderRadioButton); 176 bevelBorderRadioButton = new JRadioButton("BevelBorder"); 177 radioPanel.add(bevelBorderRadioButton); 178 ButtonGroup buttonGroup = new ButtonGroup(); 179 buttonGroup.add(noBorderRadioButton); 180 buttonGroup.add(titleBorderRadioButton); 181 buttonGroup.add(lineBorderRadioButton); 182 buttonGroup.add(etchedBorderRadioButton); 183 buttonGroup.add(bevelBorderRadioButton); 184 displayOptionsPanel.add(radioPanel); 185 186 return displayOptionsPanel; 187 } 188 private JPanel createSaladOptionsPanel() { 189 JPanel saladOptionsPanel = new JPanel(new BorderLayout()); 190 saladOptionsPanel.setBorder(new TitledBorder("Salad Options")); 191 192 JPanel checkBoxPanel = new JPanel(new GridLayout(0, 1)); 193 checkBoxPanel.setBorder(new TitledBorder("Ingredients")); 194 vegetablesCheckBox = new JCheckBox("Vegetables"); 195 vegetablesCheckBox.setSelected(true); 196 checkBoxPanel.add(vegetablesCheckBox); 197 fruitsCheckBox = new JCheckBox("Fruits"); 198 checkBoxPanel.add(fruitsCheckBox); 199 nutsCheckBox = new JCheckBox("Nuts"); 200 checkBoxPanel.add(nutsCheckBox); 201 cheesesCheckBox = new JCheckBox("Cheeses"); 202 checkBoxPanel.add(cheesesCheckBox); 203 saladOptionsPanel.add(checkBoxPanel, BorderLayout.WEST); 204 205 saladList = new JList(vegetables.toArray()); 206 JScrollPane scrollPane1 = new JScrollPane(saladList); 207 saladOptionsPanel.add(scrollPane1, BorderLayout.CENTER); 208 209 chosenItemTextField = new JTextField(); 210 saladOptionsPanel.add(chosenItemTextField, BorderLayout.SOUTH); 211 return saladOptionsPanel; 212 } 213 private JPanel createSouthPanel() { 214 JPanel southPanel = new JPanel(); 215 216 JPanel fontPanel = new JPanel(new BorderLayout()); 217 fontPanel.setBorder(new TitledBorder("Font")); 218 fontStyleComboBox = 219 new JComboBox(new String[] { "Plain", "Bold", "Italic", "Bold+Italic" }); 220 fontPanel.add(fontStyleComboBox, BorderLayout.WEST); 221 222 fontSizeSlider = new JSlider(1, 200, 24); 223 fontPanel.add(fontSizeSlider, BorderLayout.CENTER); 224 sliderLabel = new JLabel(String.valueOf(fontSizeSlider.getValue())); 225 fontPanel.add(sliderLabel, BorderLayout.EAST); 226 southPanel.add(fontPanel); 227 228 JPanel codePanel = new JPanel(); 229 codePanel.setBorder(new EtchedBorder()); 230 231 codePanel.add(new JLabel("Enter Secret Code:")); 232 secretCodeField = new JPasswordField(10); 233 codePanel.add(secretCodeField); 234 southPanel.add(codePanel); 235 return southPanel; 236 } 237 public static void main(String[] arg) { 238 MainFrame frame = new MainFrame(); 239 } 240 }
Figure 12-41: MainFrame GUI
Figure 12-42: Visual Guide to the Components in MainFrame
Figure 12-43: MainFrame Layout
Use the following commands to compile and execute the example. From the directory containing the src folder:
javac –d classes -sourcepath src src/chap12/MainFrame.java java –cp classes chap12.MainFrame
Following is the console output from running the MainFrame application.
MainFrame [0, 0, 600, 600] (BorderLayout: 1 child) +--JRootPane [4, 24, 592, 572] (JRootPane$RootLayout: 2 children) +--JPanel [0, 0, 592, 572] +--JLayeredPane [0, 0, 592, 572] (null: 2 children) +--JMenuBar [0, 0, 592, 23] (DefaultMenuLayout: 2 children) | +--JMenu [0, 1, 53, 21] | +--JMenu [53, 1, 53, 21] +--JPanel [0, 23, 592, 549] (BorderLayout: 3 children) +--JPanel [0, 193, 592, 295] (BorderLayout: 2 children) | +--JPanel [0, 0, 592, 26] (GridLayout: 2 children) | | +--JLabel [0, 0, 296, 26] | | +--JToggleButton [296, 0, 296, 26] | +--JScrollPane [0, 26, 592, 269] (ScrollPaneLayout$UIResource: 3 children) | +--JViewport [1, 1, 574, 266] (ViewportLayout: 1 child) | | +--JTextArea [0, 0, 574, 960] | +--JScrollPane$ScrollBar [575, 1, 15, 266] (MetalScrollBarUI: 2 children) | | +--MetalScrollButton [0, 251, 15, 15] | | +--MetalScrollButton [0, 0, 15, 15] | +--JScrollPane$ScrollBar [0, 0, 0, 0] (MetalScrollBarUI: 2 children) | +--MetalScrollButton [0, 0, 0, 0] | +--MetalScrollButton [0, 0, 0, 0] +--JPanel [0, 0, 592, 193] (BorderLayout: 2 children) | +--JPanel [0, 0, 320, 193] (FlowLayout: 2 children) | | +--JPanel [10, 73, 182, 52] (GridLayout: 2 children) | | | +--JButton [0, 0, 182, 26] | | | +--JButton [0, 26, 182, 26] | | +--JPanel [197, 26, 113, 146] (GridLayout: 5 children) | | +--JRadioButton [5, 21, 103, 24] | | +--JRadioButton [5, 45, 103, 24] | | +--JRadioButton [5, 69, 103, 24] | | +--JRadioButton [5, 93, 103, 24] | | +--JRadioButton [5, 117, 103, 24] | +--JPanel [320, 0, 272, 193] (BorderLayout: 3 children) | +--JPanel [5, 21, 99, 147] (GridLayout: 4 children) | | +--JCheckBox [5, 21, 89, 30] | | +--JCheckBox [5, 51, 89, 30] | | +--JCheckBox [5, 81, 89, 30] | | +--JCheckBox [5, 111, 89, 30] | +--JScrollPane [104, 21, 163, 147] (ScrollPaneLayout$UIResource: 3 children) | | +--JViewport [1, 1, 160, 144] (ViewportLayout: 1 child) | | | +--JList [0, 0, 160, 144] (null: 1 child) | | | +--CellRendererPane [0, 0, 0, 0] (null: 1 child) | | | +--DefaultListCellRenderer$UIResource [-160, -18, 0, 0] | | +--JScrollPane$ScrollBar [0, 0, 0, 0] (MetalScrollBarUI: 2 children) | | | +--MetalScrollButton [0, 0, 0, 0] | | | +--MetalScrollButton [0, 0, 0, 0] | | +--JScrollPane$ScrollBar [0, 0, 0, 0] (MetalScrollBarUI: 2 children) | | +--MetalScrollButton [0, 0, 0, 0] | | +--MetalScrollButton [0, 0, 0, 0] | +--JTextField [5, 168, 262, 20] +--JPanel [0, 488, 592, 61] (FlowLayout: 2 children) +--JPanel [20, 5, 311, 51] (BorderLayout: 3 children) | +--JComboBox [5, 21, 87, 25] (MetalComboBoxUI$MetalComboBoxLayoutManager: 2 children) | | +--MetalComboBoxButton [0, 0, 87, 25] | | +--CellRendererPane [0, 0, 0, 0] (null: 1 child) | | +--BasicComboBoxRenderer$UIResource [-61, -18, 0, 0] | +--JSlider [92, 21, 200, 25] | +--JLabel [292, 21, 14, 25] +--JPanel [336, 13, 235, 34] (FlowLayout: 2 children) +--JLabel [7, 9, 106, 16] +--JPasswordField [118, 7, 110, 20]
Because of the greater complexity of this program’s interface, the code for creating the interface is broken into six methods: createMenuBar(), createCenterPanel(), createNorthPanel(), createSouthPanel(), createDisplayOption-sPanel() and createSaladOptionsPanel().
In lines 121 - 122, the second parameter to the JLabel constructor instructs the JLabel to center whatever text it has to display. Our previous programs didn’t set this parameter on the JLabels so they defaulted to JLabel.LEFT.
In line 124, we set the JLabel’s “opaque” property to true. JLabels are transparent by default, which means that they don’t paint their background. This allows whatever is behind to show through. To make the label’s background yellow, we must set it to yellow, and then call this line to ensure that its background is painted whenever the label is painted.
Line 133 sets the font of the textarea to a 24 point version of whatever the textarea’s default font was.
Lines 136 and 206 make use of JScrollPanes. One JScrollPane contains the textArea and another contains the list so that when the textArea or list contains more text or items than can be displayed, they will become scrollable. JScrollPane is a priceless tool, making a difficult task trivial.
In lines 152, 165, 190, 193 and 217, TitledBorders are responsible for the text and gray outline around these panels. Pretty neat, huh?
Lines 178 – 183 utilize a typical pattern for organizing radio buttons. Radio buttons are usually arranged in groups where only one radio button can be selected at a time. Neither a component nor a container, ButtonGroup can be thought of as a helper class that gives this typical behavior to a group of radio buttons or any other two-state buttons.
In the JSlider constructor (line 222), the first two integer parameters determine the minimum and maximum values the slider can have. The third integer parameter sets the slider’s initial value.
In line 224, we initialize the label’s text to match the slider’s initial value. In the next chapter, sliding the slider will actually update the label.
The integer parameter in the JPasswordField constructor (line 232) sets the number of columns of the field. JPasswordField inherits the columns property from JTextField. Surprisingly, the number of columns has no effect on how many characters the field can contain. It affects the preferred size of the field but not in a very accurate manner. Often the number of columns is a moot point because a layout manager will usually determine the size of its components. In this application, however, the textfield’s container uses FlowLayout which, as you may recall, always sets a component to its preferred size. So, it was important to set the number of columns to a sensible value.
For the first time, we used TreePrinterUtils.printLayout() to print the entire window contents rather than just the content pane’s contents. In this expanded (and complete) textual view of the window’s layout, the content pane is listed on the sixth line. It is the second child of a JLayeredPane, which is the second child of a JRootPane, which is the only child of the JFrame. Until now, this chapter intentionally presented a simplified view of the structure of top-level Swing containers. You may never need to know more, but if you would like a complete explanation, please consult the
javadocs for JRootPane. Also of interest in the textual view is the structure of the JMenuBar which doesn’t show the four JMenuItems we added (Why not?). Finally take a look at the structure of the JScrollPanes and the structure of the JComboBox.
The program only uses BorderLayout, FlowLayout and GridLayout, but through the use of multiple nested JPanels, these were enough to produce a complex interface. Components that will be related to each other in the next chapter were generally placed in the same JPanel. The basic logic for which layout manager to use in each JPanel was:
1. If only one of the components in a group should resize when the container resizes, BorderLayout was chosen and that component was placed in the CENTER position. The Panel including the JTextArea was an obvious candidate for BorderLayout. BorderLayout happens to be my favorite layout manager due to its simplicity of use and the way it handles the CENTER component. You can see that I used it in several containers.
2. If the components in a group should be sized uniformly then GridLayout was chosen. GridLayout was an obvious choice for JRadioButton and JCheckBox groups.
3. If the components were to remain at their preferred sizes and not resize when the container is resized, Flow-Layout was chosen. FlowLayout is not one of my favorites but it had its usefulness in a couple panels, too.
Technically and artistically, there are many ways to design an interface. From the technical perspective, I chose multiple containers and simple layout managers because it seemed like an elegant solution to me. However, the entire GUI could possibly have been constructed from one container and a very complicated GridBagLayout. From the artistic perspective, of course, beauty and usability are subjective qualities and I make no claims to the interface’s beauty or usability. Its purpose was to put as many different components into one window as seemed reasonable for a chapter on GUIs, and to serve as the basis for the next chapter. The interface meets this objective. If you don’t like it artistically, design your own. (I really mean that).