- A Really Simple GUI
- Coding a Single-Threaded Java GUI
- The Problem with Having Just One Thread
- Multithreaded GUI Issues to Look Out For
- Conclusion
Coding a Single-Threaded Java GUI
Listing 1 illustrates an excerpt from the main code that produces Figure 1. Apart from the exit confirmation dialog, all the code is contained in a single class called ThreadBareGui.java.
Listing 1 A Single-Threaded Java GUI Class
public ThreadBareGui() { setSize(WIDTH, HEIGHT); setTitle("Single-threaded GUI"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); box = new JPanel(); add(box, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout()); JButton startButton = new JButton(FillScreenButton); startButton.addActionListener(this); buttonPanel.add(startButton); JButton exitButton = new JButton(ExitScreenButton); exitButton.addActionListener(this); buttonPanel.add(exitButton); add(buttonPanel, BorderLayout.SOUTH); } public void actionPerformed(ActionEvent e) { String guiActionCommand = e.getActionCommand(); if (guiActionCommand.equals(FillScreenButton)) { fillWithObjects(); } else { if (guiActionCommand.equals(ExitScreenButton)) { ConfirmDialog confirm = new ConfirmDialog(); if (confirm.activateDialog() != 0) return; } System.exit(0); } }
The constructor in Listing 1 is pretty standard Swing code and is concerned with creating the graphical elements of the GUI. This type of code is easy enough to understand so I won't go into any detail on it, apart from saying that it sets up the container window, defines a few controls, and then waits for user interaction as illustrated in Figure 1.
The interesting stuff in Listing 1 happens in the actionPerformed() method. This method receives the user-driven GUI events via a parameter of type ActionEvent. This parameter can be queried to determine the origin of the event and, looking at the actionPerformed() method code, this is exactly what the method does by calling the e.getActionCommand() method. If the user presses the Fill Screen button, then the fillWithObjects() method is called. The latter is the code that displays the little black rectangles you saw in Figure 2.
Listing 2 illustrates the code that produces the screen filling.
Listing 2 The Screen-Filling Code
public void fillWithObjects() { Graphics g = box.getGraphics(); for (int y = 0; y < FILL_HEIGHT; y = y + OBJECT_SIZE) { for (int x = 0; x < FILL_WIDTH; x = x + OBJECT_SIZE) { g.fillRect(x, y, OBJECT_SIZE, OBJECT_SIZE); justWait(PAUSE); } } } public void justWait(int milliseconds) { try { Thread.sleep(milliseconds); } catch(InterruptedException e) { System.out.println("Unexpected interrupt " + e.toString()); System.exit(0); } }
If instead of pressing the Fill Screen button in Figure 1, the user presses the Exit button, an instance of ConfirmDialog is created, and the associated method activateDialog() is invoked. The latter invocation results in the display illustrated in Figure 3 that gives you the choice of exiting the program or canceling the exit operation. Listing 3 illustrates some of the code from the ConfirmDialog class.
Listing 3 The ConfirmDialog Class
public class ConfirmDialog implements LayoutManager { static String[] optionNames = { "OK", "Cancel" }; static String dialogTitle = "Warning"; Dimension origin = new Dimension(0, 0); JButton dialogButton; JPanel dialogPanel; JPanel mainPanel; JScrollPane tableAggregate; public ConfirmDialog() { mainPanel = new JPanel(); try { createDialog(); } catch (Exception e) { System.out.println("Exception occurred during dialog."); e.printStackTrace(); } mainPanel.setLayout(this); } /** * Renders a JDialog using JOptionPane containing the dialogPanel. */ public int activateDialog() { if(JOptionPane.showOptionDialog(tableAggregate, dialogPanel, dialogTitle, JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, optionNames, optionNames[0]) == 0) { return 0; } else { return -1; } }
In Listing 3, the constructor code creates the basic dialog element, and the activateDialog() method displays it on the screen. The main merit of using the code in Listing 3 is that it provides the required application exit policy—i.e., it makes certain that the user wants to exit before terminating the application. The Listing 3 code is called only if the user presses the Exit button. If the user selects any of the other two program exit mechanisms (by clicking the title bar Close button), then the Listing 3 code is not called. This is pretty much in line with the normal use of these program exit controls. You can check this out when you use any of the many GUI-based desktop applications.
However, applications such as word processors should always give you the option of saving your work before exiting—so there are exceptions but if you open a word processor (such as Microsoft Word) and without opening a document press the Close button, you won't be prompted to save your work. The latter is because in this case Word deems that there is no work to save, which is the correct operation.
It's issues like these that make GUI development a fiddly process. The good thing is you can build your own GUI efforts on a solid foundation by carefully studying the many excellent examples in modern desktop software.
Enough about exiting the program—what about the big long wait while the screen is filling in Figure 2?