In the previous chapter, you were introduced to Swing and learned about some of the Swing components. This chapter takes the introduction to the next level. You'll learn about Swing windows, menus, toolbars, tables, trees, and other GUI components. You'll learn how Swing events are handled and how the JApplet class supports the development of applets that use Swing components. You'll also learn how to convert your existing applications and applets to Swing. When you finish this chapter, you'll be able to begin writing your own Swing-based applets and applications.
Chapter 6, "GUI Building," covered GUI building using the component and container classes of the AWT. Swing GUI building is very similar to AWT GUI building, except that you have many more component classes with which to work. For the most part, everything you learned in Chapter 6 carries over to Swing. However, Swing provides a number of enhancements you'll need to know about in order to maximize Swing's potential. The following subsections describe the classes used for Swing GUI building and point out the enhancements provided by Swing.
Just as AWT provides a Window class hierarchy, so does Swing. Swing's window classes are extensions of the AWT Window class hierarchy. The JWindow class extends the AWT Window class. The JFrame class extends the AWT Frame class and the JDialog class extends the AWT Dialog class.
The JWindow, JFrame, and JDialog classes differ from their AWT counterparts in that they use a separate content pane for adding and laying out GUI components. This content pane is a Container object that is accessed via the getContentPane() method. The content pane is part of a JRootPane object that contains other panes used for overlaying components and intercepting mouse and keyboard events. You'll learn how to use the content pane to build a Swing GUI in the examples of this chapter.
Swing menus, like Swing windows, are analogous to their AWT counterparts. The JMenuBar, JMenu, JMenuItem, JCheckBoxMenuItem, and JRadioButtonMenuItem classes are used in the same manner as the AWT MenuBar, Menu, MenuItem, and CheckboxMenuItem classes but with one very important difference. The Swing menu classes are all subclasses of the JComponent class, and therefore, of the Component class. This means that Swing menus, unlike their AWT counterparts, are first-class components and can be used with any Container classes. The JPopupMenu class is analogous to the AWT PopupMenu class. Another nice feature provided by Swing menus is the capability to use icon images in menus. An image can be added to a menu item via its constructor.
The SwingWin program, shown in Listing 13.1, demonstrates the use of Swing's menus. The program's opening display is shown in Figure 13.1. Select the New menu item from the File menu, as shown in Figure 13.2. The menu item's label is displayed in the text field, as shown in Figure 13.3. Try experimenting with the menu items of the File and Edit menus.
FIGURE 13.1. The SwingWin opening window.
FIGURE 13.2. Selecting the New menu item from the File menu.
FIGURE 13.3. The name of the menu item is displayed in the text field.
The Special menu is shown in Figure 13.4. Note that it contains two checkboxes and two menu items. The second checkbox is already checked. Check the first checkbox and then pull down the menu to see its effect. As you can see in Figure 13.5, multiple checkboxes may be checked.
FIGURE 13.4. The Special menu.
FIGURE 13.5. Multiple checkboxes can be checked.
Check the first radio button and then pull down the Special menu to verify that it has been checked. Now check the second radio button. Only one radio button can be checked at a time. Figure 13.6 shows that the checking of the second radio button causes the first radio button to become unchecked.
FIGURE 13.6. Only a single radio button may be checked.
import java.awt.*; import java.awt.event.*; import com.sun.java.swing.*; import com.sun.java.swing.event.*; public class SwingWin extends JFrame { public static int WIDTH = 300; public static int HEIGHT = 300; public static String TITLE = "SwingWin"; Container frameContainer; // Swing components JTextField textField = new JTextField(50); JMenuBar menuBar = new JMenuBar(); JMenu fileMenu = new JMenu("File"); JMenuItem fileNew = new JMenuItem("New"); JMenuItem fileOpen = new JMenuItem("Open"); JMenuItem fileSave = new JMenuItem("Save"); JMenuItem fileExit = new JMenuItem("Exit"); JMenu editMenu = new JMenu("Edit"); JMenuItem editCut = new JMenuItem("Cut"); JMenuItem editCopy = new JMenuItem("Copy"); JMenuItem editPaste = new JMenuItem("Paste"); JMenu specialMenu = new JMenu("Special"); JCheckBoxMenuItem specialCheck1 = new JCheckBoxMenuItem("Check 1"); JCheckBoxMenuItem specialCheck2 = new JCheckBoxMenuItem("Check 2",true); JSeparator separator = new JSeparator(); JRadioButtonMenuItem specialRadio1 = new JRadioButtonMenuItem("Radio 1"); JRadioButtonMenuItem specialRadio2 = new JRadioButtonMenuItem("Radio 2"); ButtonGroup buttonGroup = new ButtonGroup(); public SwingWin() { super(TITLE); buildGUI(); setupEventHandlers(); setSize(WIDTH,HEIGHT); show(); } void buildGUI() { setupMenuBar(); layoutComponents(); } void setupMenuBar() { fileMenu.add(fileNew); fileMenu.add(fileOpen); fileMenu.add(fileSave); fileMenu.add(fileExit); editMenu.add(editCut); editMenu.add(editCopy); editMenu.add(editPaste); specialMenu.add(specialCheck1); specialMenu.add(specialCheck2); specialMenu.add(separator); buttonGroup.add(specialRadio1); buttonGroup.add(specialRadio2); specialMenu.add(specialRadio1); specialMenu.add(specialRadio2); menuBar.add(fileMenu); menuBar.add(editMenu); menuBar.add(specialMenu); setJMenuBar(menuBar); } public void layoutComponents() { frameContainer = getContentPane(); frameContainer.setLayout(null); textField.setBounds(100,100,100,20); frameContainer.add(textField); } void setupEventHandlers() { addWindowListener(new WindowHandler()); fileNew.addActionListener(new MenuItemHandler()); fileOpen.addActionListener(new MenuItemHandler()); fileSave.addActionListener(new MenuItemHandler()); fileExit.addActionListener(new MenuItemHandler()); editCut.addActionListener(new MenuItemHandler()); editCopy.addActionListener(new MenuItemHandler()); editPaste.addActionListener(new MenuItemHandler()); specialCheck1.addItemListener(new ItemHandler()); specialCheck2.addItemListener(new ItemHandler()); specialRadio1.addItemListener(new ItemHandler()); specialRadio2.addItemListener(new ItemHandler()); } public static void main(String[] args) { SwingWin app = new SwingWin(); } public class WindowHandler extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } } public class MenuItemHandler implements ActionListener { public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); if(cmd.equals("Exit")) System.exit(0); else textField.setText(cmd); } } public class ItemHandler implements ItemListener { public void itemStateChanged(ItemEvent e) { AbstractButton button = (AbstractButton) e.getItem(); String label = button.getText(); if(button.isSelected()) label += " true"; else label += " false"; textField.setText(label); } } }
The first thing you should notice about SwingWin is that it extends the JFrame class instead of the Frame class of java.awt. It then declares constants and variables for use in the program. Objects of classes JMenuBar, JMenu, JMenuItem, JCheckBoxMenuItem, and JRadioButtonMenuItem are declared in a manner similar to their AWT counterparts. Note that the JSeparator class is used to implement menu separators. For objects of the JRadioButtonMenuItem class to implement mutually exclusive button selection, they are added to an object of the ButtonGroup class.
The SwingWin() constructor invokes buildGUI() to build the program's GUI and setupEventHandlers() to connect events to their event handling code.
The setupMenuBar() method is invoked by buildGUI() to set up the program's menu bar. Menu items are added to menus and the menus are then added to the menu bar. In addition, radio buttons are added to their button group. Finally, the menu bar is set using the setJMenuBar() method of the JFrame class.
NOTE: You can use the Swing menu classes to rewrite the MyMenu class of Chapter 9, "Creating Window Applications."
The layoutComponents() method invokes the getContentPane() method of the JFrame class to get the frame's container. Unlike AWT frames, Swing frames have a container that is separate from the frame itself. The container's layout is set to a null layout and the text field is centered within the container.
The setupEventHandlers() method sets up event handlers for the window and each of the menu items. Note that a separate menu handler is used for the checkbox and radio button menu items. The actionPerformed() method of the MenuItemHandler class handles the selection of a menu item by displaying the menu item's label in the text field. The itemStateChanged() method of the ItemHandler class handles the selection of a checkbox or radio button by displaying the checkbox or button's label and selection state in the text field.
The JPanel class is the Swing analog of the AWT Panel class. It, like all other subclasses of JComponent, provides the capability to add a border. You'll learn how to use borders with a panel in the upcoming SwingBorder program of Listing 13.2.
Swing containers support all of the AWT layouts, including the null layout. In addition, the following layouts are supported by Swing:
With the exception of BoxLayout and OverlayLayout, you'll probably use the layouts of the AWT.
One of the most useful features provided by Swing is the capability to add icons to components, such as labels, buttons, menu items, and so on. The Icon interface defines the methods that must be implemented by icon classes. The ImageIcon class provides a default implementation of this interface. ImageIcon objects can be constructed from image files, URLs that point to image files, or AWT Image objects. You'll define a border from an Image object in the SwingBorder program of Listing 13.2.
The com.sun.java.swing.border package provides the Border interface, which defines the methods that need to be implemented by all border classes. The AbstractBorder class implements the Border interface and is the superclass of the Swing border classes. Its subclasses include:
The use of these classes is covered in the next example.
The SwingBorder program, shown in Listing 13.2, illustrates the use of Swing's pre-defined borders. The opening window of SwingBorder is shown in Figure 13.7. Select Bevel from the Border menu and the window's border changes to a beveled border as shown in Figure 13.8. Note that the beveling can be changed from a lowered bevel to a raised bevel.
FIGURE 13.7. The SwingBorder opening display.
Select Compound from the Border menu and a compound blue and red border is displayed, as shown in Figure 13.9. The compound border consists of two line borders, one of each color. Figure 13.10 shows an the empty border that is displayed when you select Empty from the Border menu. An empty border is used to provide vertical and horizontal margins, without displaying anything in those margins.
FIGURE 13.8. A beveled border.
FIGURE 13.9. A compound border.
FIGURE 13.10. An empty border.
Selecting Etched from the Border menu results in the Etched border shown in Figure 13.11. Figure 13.12 shows the lined border that results from selecting Line from the border menu.
Probably the most interesting border is the matte border, shown in Figure 13.13. This border results from selecting matte from the Border menu. The border was created using a phone icon.
Figure 13.14 shows the raised soft beveled border that results from selecting Soft Bevel from the Border menu. Figure 13.15 provides an example of a titled border.
FIGURE 13.11. An etched border.
FIGURE 13.12. A lined border.
FIGURE 13.13. A matte border.
FIGURE 13.14. A soft beveled border.
FIGURE 13.15. A titled border.
import java.awt.*; import java.awt.event.*; import com.sun.java.swing.*; import com.sun.java.swing.event.*; import com.sun.java.swing.border.*; public class SwingBorder extends JFrame { public static int WIDTH = 300; public static int HEIGHT = 300; public static String TITLE = "SwingBorder"; Container frameContainer; // Swing components JPanel panel = new JPanel(); JMenuBar menuBar = new JMenuBar(); JMenu fileMenu = new JMenu("File"); JMenuItem fileExit = new JMenuItem("Exit"); JMenu borderMenu = new JMenu("Border"); String[] borderTypes = {"Bevel","Compound","Empty","Etched", "Line","Matte","SoftBevel","Titled"}; JRadioButtonMenuItem[] borders = new JRadioButtonMenuItem[borderTypes.length]; AbstractBorder[] border = {new BevelBorder(BevelBorder.LOWERED), new CompoundBorder(new LineBorder(Color.blue,10), new LineBorder(Color.red,5)), new EmptyBorder(10,10,10,10), new EtchedBorder(), new LineBorder(Color.blue,10), new MatteBorder(new ImageIcon("phone.gif")), new SoftBevelBorder(BevelBorder.RAISED), new TitledBorder("TitledBorder")}; ButtonGroup buttonGroup = new ButtonGroup(); public SwingBorder() { super(TITLE); buildGUI(); setupEventHandlers(); setSize(WIDTH,HEIGHT); show(); } void buildGUI() { setupMenuBar(); layoutComponents(); } void setupMenuBar() { fileMenu.add(fileExit); for(int i=0;i<borderTypes.length;++i) { borders[i] = new JRadioButtonMenuItem(borderTypes[i]); buttonGroup.add(borders[i]); borderMenu.add(borders[i]); } menuBar.add(fileMenu); menuBar.add(borderMenu); setJMenuBar(menuBar); } public void layoutComponents() { frameContainer = getContentPane(); frameContainer.setLayout(new BorderLayout()); frameContainer.add("Center",panel); } void setupEventHandlers() { addWindowListener(new WindowHandler()); fileExit.addActionListener(new MenuItemHandler()); for(int i=0;i<borders.length;++i) borders[i].addItemListener(new ItemHandler()); } public static void main(String[] args) { SwingBorder app = new SwingBorder(); } public class WindowHandler extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } } public class MenuItemHandler implements ActionListener { public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); if(cmd.equals("Exit")) System.exit(0); } } public class ItemHandler implements ItemListener { public void itemStateChanged(ItemEvent e) { JRadioButtonMenuItem button = (JRadioButtonMenuItem) e.getItem(); String label = button.getText(); for(int i=0;i<borderTypes.length;++i) { if(label.equals(borderTypes[i])) { panel.setBorder(border[i]); repaint(); } } } } }
SwingBorder begins by declaring the constants and variables used to implement the borders. It uses an object of the JPanel class to display the border. This object is assigned to the panel variable. The borderTypes array is used to create the menu items of the Border menu. These menu items are implemented as objects of the JRadioButtonMenuItem class. An array of AbstractBorder objects is created to provide examples of each border type. These borders are implemented by the following objects:
The setupMenuBar() method creates each of the JRadioButtonMenuItem objects from the borderTypes array, adds the buttons to their button group, and then adds them to the Border menu. The setupEventHandlers() method sets up the event handlers for the window and menu items. The Border menu items are assigned objects of the ItemHandler class.
The itemStateChanged() method of the ItemHandler class retrieves the label of the JRadioButtonMenuItem that is selected. The border of the JPanel object referenced by panel is set to the selected border object and the repaint() method is invoked to bring the border into effect.
The JToolTip class provides the capability to add popup text boxes that are displayed when the mouse is held over a component. Those components that support tool tips allow tool tips to be specified in their constructors. The setToolTipText() method of the JComponent class can also be used to specify a component's tool tip.
The JToolBar class provides the capability to use moveable and dockable toolbars with Swing. Objects of this class are containers for other Swing or AWT components. Typical JToolBar objects contain JButton objects that are constructed with image icons. The addSeparator() method is used to add a separator to a toolbar.
The SwingBar program, shown in Listing 13.3, illustrates Swing's toolbar and tool tip capabilities. The program's opening display is shown in Figure 13.16. A toolbar is positioned at the top of the window, underneath the menu bar. Position your mouse over the first button and the New tool tip is displayed, as shown in Figure 13.17. The toolbar is moveable. Drag it by the left side to another position within the SwingBar window as shown in Figure 13.18.
FIGURE 13.16. The SwingBar opening window.
FIGURE 13.17. Tool tips are assigned to the toolbar's buttons.
FIGURE 13.18. The toolbar can be moved around the SwingBar window.
import java.awt.*; import java.awt.event.*; import com.sun.java.swing.*; import com.sun.java.swing.event.*; public class SwingBar extends JFrame { public static int WIDTH = 400; public static int HEIGHT = 400; public static String TITLE = "SwingBar"; Container frameContainer; // Swing components JToolBar toolBar = new JToolBar(); String[] iconFiles = {"new.gif","open.gif","save.gif","cut.gif", "copy.gif","paste.gif"}; String[] buttonLabels = {"New","Open","Save","Cut","Copy","Paste"}; ImageIcon[] icons = new ImageIcon[iconFiles.length]; JButton[] buttons = new JButton[buttonLabels.length]; JMenuBar menuBar = new JMenuBar(); JMenu fileMenu = new JMenu("File"); JMenuItem fileExit = new JMenuItem("Exit"); public SwingBar() { super(TITLE); buildGUI(); setupEventHandlers(); setSize(WIDTH,HEIGHT); show(); } void buildGUI() { setupMenuBar(); layoutComponents(); } void setupMenuBar() { fileMenu.add(fileExit); menuBar.add(fileMenu); setJMenuBar(menuBar); } public void layoutComponents() { frameContainer = getContentPane(); frameContainer.setLayout(new BorderLayout()); for(int i=0;i<buttonLabels.length;++i) { icons[i] = new ImageIcon(iconFiles[i]); buttons[i] = new JButton(icons[i]); buttons[i].setToolTipText(buttonLabels[i]); if(i==3) toolBar.addSeparator(); toolBar.add(buttons[i]); } frameContainer.add("North",toolBar); } void setupEventHandlers() { addWindowListener(new WindowHandler()); fileExit.addActionListener(new MenuItemHandler()); } public static void main(String[] args) { SwingBar app = new SwingBar(); } public class WindowHandler extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } } public class MenuItemHandler implements ActionListener { public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); if(cmd.equals("Exit")) System.exit(0); } }
}
The SwingBar program creates a JToolBar object and assigns it to the toolBar variable. The iconFiles array identifies the filenames of the toolbar's button icons. The buttonLabels array identifies the tool tips of the toolbar's buttons. The icons array is used to hold the images of the toolbar's buttons. The buttons array contains the actual buttons.
The layoutComponents() method creates the image icons from their image files, creates the buttons from their icons, and sets the tool tips of each button. A separator is added between the third and fourth buttons. The buttons are then added to the JToolBar object.
The JLabel and JButton classes provide Swing analogs to the AWT Label and Button classes. The Swing implementation provides the advantage of being able to use icons along with text. The JLabel() and JButton() constructors allow an icon to be specified. In addition, both classes support the setIcon() method for setting an icon after the object has been constructed.
The JTextComponent, JTextField, and JTextArea classes are the Swing analogs of the AWT TextComponent, TextField, and TextArea classes. In addition, Swing provides the TextPane class for working with text documents that can be marked up with different text styles.
The JComboBox and JList classes provide the capability to present the user with a list of text or graphic selections. The JComboBox class implements a drop-down list, similar to a Motif option list. The JList class is a single or multiple selection list with a multi- element view. JList objects are typically added to a JScrollPane object so the list can be scrolled. You'll see examples of JComboBox and JList objects in the Calendar example of Listing 13.4.
You were introduced to sliders and progress bars in Chapter 12, "Introducing Swing." The JSlider and JProgressBar classes do not have AWT analogs. Both classes support horizontal and vertical orientations. The JProgressBar class is typically used to display the progress of a task, such as the loading of an image. The JSlider class is used to adjust or monitor the value of a variable within its allowed interval.
The JScrollPane greatly simplifies the use of scrollbars. The getViewport() method returns a JViewport object to which components may be added. In most cases, you simply need to add components to the JViewport object and they are automatically scrolled. You'll see an example of this in the Calendar program of Listing 13.4.
The JTable class is another Swing component that does not have an AWT analog. JTable provides a very flexible capability for creating and displaying tables. It allows tables to be constructed from arrays, vectors of objects, or from objects that implement the TableModel interface.
The JTableModel interface defines methods for objects that specify a table's contents. The AbstractTableModel class provides a default implementation of the JTableModel interface. This class is typically extended to provide a custom table model implementation. You'll see an example of using the AbstractTableModel class in the Calendar program of Listing 13.4.
The JTable class provides the capability to edit tables. The setCellEditor() method allows an object of the TableCellEditor interface to be identified as a table's cell editor.
The Calendar program, shown in Listing 13.4, illustrates the use of Swing tables. The program's opening display is shown in Figure 13.19. It consists of a combo box, list, and a table. When you select a year from the combo box, the calendar displayed by the table is updated. Select 1999 from the combo box and the calendar is updated, as shown in Figure 13.20. The selection of a month from the list also results in the calendar table being updated. Select December from the list to view the calendar for the end of this millennium, as shown in Figure 13.21.
FIGURE 13.19. The Calendar opening display.
FIGURE 13.20. Selecting a year from the combo box results in a new calendar.
FIGURE 13.21. Selecting a month from the list also results in a new calendar.
import java.awt.*; import java.awt.event.*; import com.sun.java.swing.*; import com.sun.java.swing.event.*; import com.sun.java.swing.table.*; import java.util.Date; public class Calendar extends JFrame { public static int WIDTH = 600; public static int HEIGHT = 400; public static String TITLE = "Calendar"; Container frameContainer; // Swing components String[] years = {"1998","1999","2000","2001", "2002","2003","2004","2005"}; JComboBox comboBox = new JComboBox(years); String[] months = {"January","February","March","April","May", "June","July","August","September","October","November", "December"}; JList list = new JList(months); JScrollPane scrollPane = new JScrollPane(list); CalendarModel model = new CalendarModel(); JTable table = new JTable(model); JMenuBar menuBar = new JMenuBar(); JMenu fileMenu = new JMenu("File"); JMenuItem fileExit = new JMenuItem("Exit"); public Calendar() { super(TITLE); buildGUI(); setupEventHandlers(); setSize(WIDTH,HEIGHT); show(); } void buildGUI() { setupMenuBar(); layoutComponents(); } void setupMenuBar() { fileMenu.add(fileExit); menuBar.add(fileMenu); setJMenuBar(menuBar); } public void layoutComponents() { frameContainer = getContentPane(); frameContainer.setLayout(null); comboBox.setBounds(10,10,100,30); comboBox.setSelectedIndex(0); comboBox.addItemListener(new ComboHandler()); scrollPane.setBounds(200,10,150,100); list.setSelectedIndex(3); list.addListSelectionListener(new ListHandler()); table.setBounds(10,150,550,200); model.setMonth(comboBox.getSelectedIndex()+1998, list.getSelectedIndex()); frameContainer.add(comboBox); frameContainer.add(scrollPane); table.setGridColor(Color.black); table.setShowGrid(true); frameContainer.add(table); } void setupEventHandlers() { addWindowListener(new WindowHandler()); fileExit.addActionListener(new MenuItemHandler()); } public static void main(String[] args) { Calendar app = new Calendar(); } class CalendarModel extends AbstractTableModel { String[] days = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; int[] numDays = {31,28,31,30,31,30,31,31,30,31,30,31}; String[][] calendar = new String[7][7]; public CalendarModel() { for(int i=0;i<days.length;++i) calendar[0][i]=days[i]; for(int i=1;i<7;++i) for(int j=0;j<7;++j) calendar[i][j]=" "; } public int getRowCount() { return 7; } public int getColumnCount() { return 7; } public Object getValueAt(int row, int column) { return calendar[row][column]; } public void setValueAt(Object value,int row, int column) { calendar[row][column] = (String) value; } public void setMonth(int year,int month) { for(int i=1;i<7;++i) for(int j=0;j<7;++j) calendar[i][j]=" "; java.util.GregorianCalendar cal = new java.util.GregorianCalendar(); cal.set(year,month,1); int offset = cal.get(java.util.GregorianCalendar.DAY_OF_WEEK)-1; offset += 7; int num = daysInMonth(year,month); for(int i=0;i<num;++i) { calendar[offset/7][offset%7]=Integer.toString(i+1); ++offset; } } public boolean isLeapYear(int year) { if(year % 4 ==0) return true; return false; } public int daysInMonth(int year,int month) { int days = numDays[month]; if(month==1 && isLeapYear(year)) ++days; return days; } } public class WindowHandler extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } } public class ComboHandler implements ItemListener { public void itemStateChanged(ItemEvent e) { model.setMonth(comboBox.getSelectedIndex()+1998, list.getSelectedIndex()); table.repaint(); } } public class ListHandler implements ListSelectionListener { public void valueChanged(ListSelectionEvent e) { model.setMonth(comboBox.getSelectedIndex()+1998, list.getSelectedIndex()); table.repaint(); } } public class MenuItemHandler implements ActionListener { public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); if(cmd.equals("Exit")) System.exit(0); } } }
}
The Calendar program uses the years and months arrays to identify the years and months supported by the calendar. A JComboBox object is created from the years array and a JList object is created from the months array. The JList object is used to create a ScrollPane object that supports list scrolling. The CalendarModel class is an inner class that is used to implement the table's model. An object of this class is created and assigned to the model variable. A JTable object is created from the CalendarModel object and assigned to the table variable.
The layoutComponents() method sets the layout of the frame's container to a null layout. It then sets the bounds, selected index, and event handlers for the combo box and list. The table's bounds are set and then the table's model is updated based on the year selected in the combo box and month selected in the list. This is accomplished using the setMonth() method of the CalendarModel class.
The CalendarModel class is used as the table's model. It extends the AbstractTableModel class. It declares the days array for use in the table's column headings and the numDays array to keep track of the number of days in each month. The calendar array is set to a seven-row by seven-column array. It is used to keep track of the table's contents. The CalendarModel() constructor sets the column headings and then blanks the table's contents.
The getRowCount(), getColumnCount(), getValueAt(), and setValueAt() methods override those of the AbstractTableModel class.
The setMonth() method updates the calendar array with the contents of the calendar for the specified year and month. It blanks out the array's contents and creates a GregorianCalendar object for the first day of the month. It then uses the get() method to determine the day of the week associated with the first day of the month. The daysInMonth() method returns the number of days in the specified month. Using this information, setMonth() is able to fill in the array's contents.
The isLeapYear() method is used to identify leap years and the daysInMonth() method returns the number of days in a month adjusted for leap years.
One of the most interesting new classes provided by Swing is the JTree class. This class implements a tree structure that can be used to display hierarchical data. The TreeNode interface defines methods that are to be implemented by the nodes of a JTree object. The DefaultMutableTreeNode class provides a default implementation of the TreeNode interface. Trees are created by creating objects of the TreeNode interface and then adding them together (via the add() method). When all of the TreeNode objects have been added together, the resulting TreeNode object is passed to the JTree constructor.
The default rendering of a JTree object uses a folder icon to identify tree nodes that have child nodes and a file icon to identify tree leaves. The setCellRenderer() method of the JTree class is used to identify an alternative tree rendering. The setCellRenderer() method takes an object of the TreeCellRenderer interface as a parameter. The following example shows how to use a custom tree rendering object.
The SwingTree program, shown in Listing 13.5, illustrates the use of Swing trees. Its opening window is shown in Figure 13.22. A JTree object is used to display the teams of the National Basketball association, arranged by conference and division. Click the circles before the conferences and divisions to expand the tree, as shown in Figure 13.23. Note that scrollbars appear when you expand the tree to the bottom of its display area. Click any team and the team's win/loss record is displayed in the text field at the bottom of the window as shown in Figure 13.24.
FIGURE 13.22. The SwingTree initial display.
FIGURE 13.23. Expand the tree and scrollbars are displayed.
FIGURE 13.24. Click a team and the team's record is displayed.
import java.awt.*; import java.awt.event.*; import com.sun.java.swing.*; import com.sun.java.swing.event.*; import com.sun.java.swing.tree.*; public class SwingTree extends JFrame { public static int WIDTH = 400; public static int HEIGHT = 400; public static String TITLE = "SwingTree"; Container frameContainer; // Swing components JTextField textField = new JTextField(); JScrollPane scrollPane = new JScrollPane(); JTree tree; Renderer renderer = new Renderer(); DefaultMutableTreeNode nba = new DefaultMutableTreeNode("National Basketball Association"); DefaultMutableTreeNode western = new DefaultMutableTreeNode("Western Conference"); DefaultMutableTreeNode pacific = new DefaultMutableTreeNode("Pacific Division Teams"); DefaultMutableTreeNode lalakers = new DefaultMutableTreeNode("Los Angeles (Lakers)"); DefaultMutableTreeNode seattle = new DefaultMutableTreeNode("Seattle"); DefaultMutableTreeNode phoenix = new DefaultMutableTreeNode("Phoenix"); DefaultMutableTreeNode portland = new DefaultMutableTreeNode("Portland"); DefaultMutableTreeNode sacramento = new DefaultMutableTreeNode("Sacramento"); DefaultMutableTreeNode goldengate = new DefaultMutableTreeNode("San Francisco"); DefaultMutableTreeNode laclippers = new DefaultMutableTreeNode("Los Angeles (Clippers)"); DefaultMutableTreeNode midwest = new DefaultMutableTreeNode("Midwest Division Teams"); DefaultMutableTreeNode utah = new DefaultMutableTreeNode("Utah"); DefaultMutableTreeNode sanantonio = new DefaultMutableTreeNode("San Antonio"); DefaultMutableTreeNode houston = new DefaultMutableTreeNode("Houston"); DefaultMutableTreeNode minnesota = new DefaultMutableTreeNode("Minnesota"); DefaultMutableTreeNode vancouver = new DefaultMutableTreeNode("Vancouver"); DefaultMutableTreeNode dallas = new DefaultMutableTreeNode("Dallas"); DefaultMutableTreeNode denver = new DefaultMutableTreeNode("Denver"); DefaultMutableTreeNode eastern = new DefaultMutableTreeNode("Eastern Conference"); DefaultMutableTreeNode atlantic = new DefaultMutableTreeNode("Atlantic Division Teams"); DefaultMutableTreeNode miami = new DefaultMutableTreeNode("Miami"); DefaultMutableTreeNode ny = new DefaultMutableTreeNode("New York"); DefaultMutableTreeNode nj = new DefaultMutableTreeNode("New Jersey"); DefaultMutableTreeNode washington = new DefaultMutableTreeNode("Washington"); DefaultMutableTreeNode orlando = new DefaultMutableTreeNode("Orlando"); DefaultMutableTreeNode boston = new DefaultMutableTreeNode("Boston"); DefaultMutableTreeNode philadelphia = new DefaultMutableTreeNode("Philadelphia"); DefaultMutableTreeNode central = new DefaultMutableTreeNode("Central Division Teams"); DefaultMutableTreeNode chicago = new DefaultMutableTreeNode("Chicago"); DefaultMutableTreeNode indiana = new DefaultMutableTreeNode("Indiana"); DefaultMutableTreeNode charlotte = new DefaultMutableTreeNode("Charlotte"); DefaultMutableTreeNode atlanta = new DefaultMutableTreeNode("Atlanta"); DefaultMutableTreeNode cleveland = new DefaultMutableTreeNode("Cleveland"); DefaultMutableTreeNode detroit = new DefaultMutableTreeNode("Detroit"); DefaultMutableTreeNode milwaukee = new DefaultMutableTreeNode("Milwaukee"); DefaultMutableTreeNode toronto = new DefaultMutableTreeNode("Toronto"); JMenuBar menuBar = new JMenuBar(); JMenu fileMenu = new JMenu("File"); JMenuItem fileExit = new JMenuItem("Exit"); public SwingTree() { super(TITLE); buildGUI(); setupEventHandlers(); setSize(WIDTH,HEIGHT); show(); } void buildGUI() { setupMenuBar(); setupTree(); layoutComponents(); } void setupMenuBar() { fileMenu.add(fileExit); menuBar.add(fileMenu); setJMenuBar(menuBar); } void setupTree() { nba.add(western); nba.add(eastern); western.add(pacific); western.add(midwest); eastern.add(atlantic); eastern.add(central); pacific.add(lalakers); pacific.add(laclippers); pacific.add(goldengate); pacific.add(seattle); pacific.add(phoenix); pacific.add(portland); pacific.add(sacramento); midwest.add(utah); midwest.add(sanantonio); midwest.add(houston); midwest.add(minnesota); midwest.add(vancouver); midwest.add(dallas); midwest.add(denver); atlantic.add(miami); atlantic.add(ny); atlantic.add(nj); atlantic.add(washington); atlantic.add(orlando); atlantic.add(boston); atlantic.add(philadelphia); central.add(chicago); central.add(indiana); central.add(charlotte); central.add(atlanta); central.add(cleveland); central.add(detroit); central.add(milwaukee); central.add(toronto); tree = new JTree(nba); } public void layoutComponents() { frameContainer = getContentPane(); frameContainer.setLayout(new BorderLayout()); tree.setCellRenderer(renderer); tree.addTreeSelectionListener(new TreeHandler()); scrollPane.getViewport().add(tree); frameContainer.add("Center",scrollPane); frameContainer.add("South",textField); } void setupEventHandlers() { addWindowListener(new WindowHandler()); fileExit.addActionListener(new MenuItemHandler()); } public static void main(String[] args) { SwingTree app = new SwingTree(); } public class WindowHandler extends WindowAdapter { public void windowClosing(WindowEvent e) { System.exit(0); } } public class MenuItemHandler implements ActionListener { public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); if(cmd.equals("Exit")) System.exit(0); } } public class TreeHandler implements TreeSelectionListener { public void valueChanged(TreeSelectionEvent e) { TreePath path = e.getPath(); String text = path.getPathComponent( path.getPathCount()-1).toString(); if(path.getPathCount()>3) { text += ": "; text += Integer.toString((int)(Math.random()*50))+" Wins "; text += Integer.toString((int)(Math.random()*50))+" Losses"; } textField.setText(text); } } class Renderer extends JLabel implements TreeCellRenderer { public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { setText(value.toString()+" "); return this; } } }
The SwingTree program uses a TextField object to display team selections, a ScrollPane object to support scrolling for the tree, a JTree object to implement the tree, and a Renderer object to control the way the tree is rendered. The Renderer class is declared as an inner class.
The nodes and leaves of the tree are implemented as DefaultMutableTreeNode objects. These objects are created with the names of NBA teams and organizational groupings. The setupTree() method connects the DefaultMutableTreeNode objects into the nodes of the tree. A new JTree object is created at the end of setupTree() method. This object is created from the tree's root node and assigned to the tree variable.
The layoutComponents() method lays out the frame's container using a BorderLayout object. The setCellRenderer() method sets the rendering object of the tree to the Renderer object referenced by the renderer variable. An object of the TreeHandler class is used to handle the selection of elements of the tree. The tree is then added to the view port of a scroll pane.
The valueChanged() method of the TreeHandler class handles the selection of elements of the tree by using the getPathComponent() method to get the path selected by the user and the getPathCount() method to identify the length of this path. The last element of the path is set as the text of the text field. If the element is a team (in other words, at the fourth level of the tree), random win/loss statistics are displayed along with the team's name.
Swing events are handled using the event delegation model introduced by JDK 1.1. The event inheritance model of JDK 1.0 cannot be used with Swing. The com.sun.java.swing.event package defines a number of event listening interfaces and event classes for use with Swing components. In addition, many Swing components also use AWT events. The Swing events defined in the com.sun.java.swing.event package are the following:
The com.sun.java.swing.event package defines event listener interfaces for the above events. The next chapter covers the Model View Controller (MVC) used by Swing components.
The JApplet class is the Swing analog of the Applet class. JApplet is similar to JFrame in that it supports a separate content pane. This container is accessed via the getContentPane() method. If you ever wished you could use menus in an applet, you'll love the JApplet class. It provides the capability to use a menu bar with an applet via the setJMenuBar method. The menu bar must be an object of the JMenuBar class.
The CalendarApplet applet, shown in Listing 13.6, is an applet conversion of the Calendar application of Listing 13.4. CalendarApplet illustrates the menu feature of the JApplet class. The calendar.htm file of Listing 13.7 is used to display CalendarApplet using the appletviewer.
Figure 13.25 shows the opening display of CalendarApplet. Note the addition of the Year and Month menus at the top of the applet display area. The inability to include menus in applets was a shortcoming of the Applet class. This shortcoming is removed by JApplet. Select the year 2002 from the Year pull-down menu. The calendar is updated to display a calendar for April 2002, as shown in Figure 13.26. Note that the year displayed by the combo box is also updated.
FIGURE 13.25. The CalendarApplet initial display.
FIGURE 13.26. Selecting a menu item from the Year menu causes the calendar and combo box to be updated.
Select March from the Month menu. The calendar is updated to display March 2002, as shown in Figure 13.27. Also note that the list identifies the month of March as being selected.
FIGURE 13.27. Selecting a menu item from the Month menu causes the calendar and list to be updated.
import java.awt.*; import java.awt.event.*; import com.sun.java.swing.*; import com.sun.java.swing.event.*; import com.sun.java.swing.table.*; import java.util.Date; public class CalendarApplet extends JApplet { public static int WIDTH = 600; public static int HEIGHT = 400; Container frameContainer; // Swing components String[] years = {"1998","1999","2000","2001", "2002","2003","2004","2005"}; JComboBox comboBox = new JComboBox(years); String[] months = {"January","February","March","April","May", "June","July","August","September","October","November", "December"}; JList list = new JList(months); JScrollPane scrollPane = new JScrollPane(list); CalendarModel model = new CalendarModel(); JTable table = new JTable(model); JMenuBar menuBar = new JMenuBar(); JMenu yearMenu = new JMenu("Year"); JMenu monthMenu = new JMenu("Month"); JMenuItem[] yearMenuItems = new JMenuItem[years.length]; JMenuItem[] monthMenuItems = new JMenuItem[months.length]; public CalendarApplet() { buildGUI(); setupEventHandlers(); setSize(WIDTH,HEIGHT); } void buildGUI() { setupMenuBar(); layoutComponents(); } void setupMenuBar() { for(int i=0;i<years.length;++i) { yearMenuItems[i] = new JMenuItem(years[i]); yearMenu.add(yearMenuItems[i]); } for(int i=0;i<months.length;++i) { monthMenuItems[i] = new JMenuItem(months[i]); monthMenu.add(monthMenuItems[i]); } menuBar.add(yearMenu); menuBar.add(monthMenu); setJMenuBar(menuBar); } public void layoutComponents() { frameContainer = getContentPane(); frameContainer.setLayout(null); comboBox.setBounds(10,10,100,30); comboBox.setSelectedIndex(0); comboBox.addItemListener(new ComboHandler()); scrollPane.setBounds(200,10,150,100); list.setSelectedIndex(3); list.addListSelectionListener(new ListHandler()); table.setBounds(10,150,550,200); model.setMonth(comboBox.getSelectedIndex()+1998, list.getSelectedIndex()); frameContainer.add(comboBox); frameContainer.add(scrollPane); table.setGridColor(Color.black); table.setShowGrid(true); frameContainer.add(table); } void setupEventHandlers() { for(int i=0;i<yearMenuItems.length;++i) yearMenuItems[i].addActionListener(new YearMenuItemHandler()); for(int i=0;i<monthMenuItems.length;++i) monthMenuItems[i].addActionListener(new MonthMenuItemHandler()); } class CalendarModel extends AbstractTableModel { String[] days = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; int[] numDays = {31,28,31,30,31,30,31,31,30,31,30,31}; String[][] calendar = new String[7][7]; public CalendarModel() { for(int i=0;i<days.length;++i) calendar[0][i]=days[i]; for(int i=1;i<7;++i) for(int j=0;j<7;++j) calendar[i][j]=" "; } public int getRowCount() { return 7; } public int getColumnCount() { return 7; } public Object getValueAt(int row, int column) { return calendar[row][column]; } public void setValueAt(Object value,int row, int column) { calendar[row][column] = (String) value; } public void setMonth(int year,int month) { for(int i=1;i<7;++i) for(int j=0;j<7;++j) calendar[i][j]=" "; java.util.GregorianCalendar cal = new java.util.GregorianCalendar(); cal.set(year,month,1); int offset = cal.get(java.util.GregorianCalendar.DAY_OF_WEEK)-1; offset += 7; int num = daysInMonth(year,month); for(int i=0;i<num;++i) { calendar[offset/7][offset%7]=Integer.toString(i+1); ++offset; } } public boolean isLeapYear(int year) { if(year % 4 ==0) return true; return false; } public int daysInMonth(int year,int month) { int days = numDays[month]; if(month==1 && isLeapYear(year)) ++days; return days; } } public class ComboHandler implements ItemListener { public void itemStateChanged(ItemEvent e) { model.setMonth(comboBox.getSelectedIndex()+1998, list.getSelectedIndex()); table.repaint(); } } public class ListHandler implements ListSelectionListener { public void valueChanged(ListSelectionEvent e) { model.setMonth(comboBox.getSelectedIndex()+1998, list.getSelectedIndex()); table.repaint(); } } public class YearMenuItemHandler implements ActionListener { public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); int year = (new Integer(cmd)).intValue() - 1998; comboBox.setSelectedIndex(year); model.setMonth(comboBox.getSelectedIndex()+1998, list.getSelectedIndex()); table.repaint(); } } public class MonthMenuItemHandler implements ActionListener { public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand(); int month = 0; for(int i=0;i<months.length;++i) { if(cmd.equals(months[i])) { month = i; break; } } list.setSelectedIndex(month); model.setMonth(comboBox.getSelectedIndex()+1998, list.getSelectedIndex()); table.repaint(); } } }
The CalendarApplet class is based on the Calendar class of Listing 13.4. Instead of extending JFrame, CalendarApplet extends JApplet. The only other significant difference between CalendarApplet and Calendar is the use of the Year and Month menus. These menus are set up in the setupMenuBar() and setupEventHandlers() methods. Note that separate classes are used to handle the Year and Month menu items.
The actionPerformed() method of the YearMenuItemHandler class determines the year that was selected and sets the corresponding index in the combo box. The table's model is updated based upon the indexes of the combo box and list. The repaint() method is invoked to cause the table to be redisplayed.
The actionPerformed() method of the MonthMenuItemHandler class is similar to that of the YearMenuItemHandler class. The month that was selected is determined and the corresponding index in the list is set. The table's model is then updated and the repaint() method is invoked.
<HTML> <HEAD> <TITLE>Calendar Applet</TITLE> </HEAD> <BODY> <APPLET CODE="CalendarApplet.class" HEIGHT=400 WIDTH=600> </APPLET> </BODY> </HTML>
Because Swing provides GUI components that are analogous to AWT components, it is easy to convert applications and applets to Swing. Applications are converted to Swing by replacing the Frame class with the JFrame class and using getContentPane() to access the frame's container. GUI components that were added to the Frame are added to the frame container. Applets are converted in a similar manner with the JApplet class replacing the Applet class. Most AWT GUI components can be converted to Swing by simply preceding the AWT class name with the letter `J'.
Once you convert your AWT components to Swing components you may want to substitute new Swing components, such as sliders, trees, and tables to reduce the complexity of your user interface. You may also want to use icons with buttons, labels, and other components to make the interface more attractive and usable. Finally, you should experiment with borders and other Swing features that contribute to your application's or applet's look and feel.
In this chapter you learned about Swing windows, menus, toolbars, tables, trees, and other GUI components. You learned about Swing events and how the JApplet class supports the development of applets that use Swing components. You also learned how to convert your existing applications and applets to Swing. In the next chapter, Chapter 14, "Changing the Look and Feel of Your Swing Components" you'll learn about Swing's pluggable look and feel capabilities.
© Copyright, Macmillan Computer Publishing. All rights reserved.