Don’t Block the GUI #92
Chapter 12, Miscellany
|
459
HACK
SwingUtilities.invokeLater(new Runnable( ) {
public void run( ) {
//label.setText("You have " + new_count + " unread messages.");
if(new_count > 0) {
label.setFont(new Font("WebDings",
Font.PLAIN,40));//label.getFont().getSize( )));
label.setText(""+(char)0xf099);
} else {
label.setFont(new Font("WingDings",
Font.PLAIN,40));//label.getFont().getSize( )));
label.setText(""+(char)0x2709);
}
System.out.println("unread messages = " + new_count);
}
});
This code will set the label to character 0xf099 in the WebDings font (the
envelope with a lightning bolt) if there is at least one unread email. If there
are no unread emails, it will use character
0x2709 in the WingDings font (a
plain envelope).
Java doesn’t support all of the glyphs in certain custom
fonts, so be sure to test your applications before going into
production whenever you use non-ASCII fonts.
H A C K
#92
Don’t Block the GUI Hack #92
Thread your heavy lifting so the event-dispatch thread stays responsive.
Practically every AWT and Swing book you’ll ever see keeps things simple
by responding to button clicks, menu selections, and other actions by doing
something in the event listener. That’s probably good for helping you learn
the various GUI widgets, but it sets you up for a really bad habit: putting
increasingly long-lasting calls in your event callbacks.
This is bad because the thread that calls
actionPerformed( ), valueChanged( ),
and other event-based methods is the same thread that services GUI events
throughout AWT and Swing. The AWT Event Dispatch Thread is responsi-
ble for polling for events, dispatching them to listeners, and for repainting
everything. If you block it on some long-lasting call—such as database or
network access, intense calculation, etc.—then mouse clicks and key-presses
won’t be processed, menus won’t be available, portions of your GUI may
not get repainted if they become obscured by other windows, etc. Oh, and
the user will hate you. Just so you know.
460
|
Chapter 12, Miscellany
#92 Don’t Block the GUI
HACK
The trick, then, is to keep heavy lifting out of the event-dispatch thread.
There’s a very straightforward way to do this in Java: move complicated pro-
cessing to its own thread, and let event dispatching continue immediately
after starting this new thread. Then you just have to deal with cleanup when
the launched thread finishes up.
AWTBlockDemo
, shown in Example 12-8, offers a test bed for exhibiting and
fixing the problem. It offers a
JTextField along with two JButtons: Load
(blocking) and Load (non-blocking). A menu also offers the blocking and
non-blocking load as
JMenuItem
s, along with a Quit menu item.
The text field takes a URL. When you click one of the load buttons or menu
items, it loads the file at that address into the text area. The text area is pre-
populated with the address for
java.awt.Component in Sun’s JavaDoc, a nice
300 KB file that will take a little while to load, even with a fast network
connection.
If your network is really fast, you can put some Thread.
sleep( )
calls in the code to simulate a slower network.
The code for the demo is shown in Example 12-8.
Example 12-8. Demonstration of both blocking and not blocking the AWT event-dispatch
thread during lengthy actions
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.net.*;
public class AWTBlockDemo extends JFrame {
JButton blockButton, dontBlockButton;
JMenuItem blockMenuItem, dontBlockMenuItem, quitMenuItem;
JTextField urlField;
JTextArea contentArea;
final static String DEFAULT_URL =
"http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Component.html";
Thread loaderThread;
public AWTBlockDemo ( ) {
super ("AWT Thread Blocking");
initMainLayout( );
initMenus( );
initActions( );
}
Don’t Block the GUI #92
Chapter 12, Miscellany
|
461
HACK
private void initMainLayout( ) {
urlField = new JTextField (DEFAULT_URL, 60);
JPanel topPanel = new JPanel ( );
topPanel.setLayout (new BoxLayout (topPanel, BoxLayout.Y_AXIS));
topPanel.add (urlField);
JPanel buttonPanel = new JPanel( );
blockButton = new JButton ("Load (blocking)");
dontBlockButton = new JButton ("Load (non-blocking)");
buttonPanel.add (blockButton);
buttonPanel.add (dontBlockButton);
topPanel.add (buttonPanel);
contentArea = new JTextArea (25, 60);
JScrollPane scroller =
new JScrollPane (contentArea,
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
getContentPane().setLayout(new BorderLayout( ));
getContentPane( ).add (topPanel, BorderLayout.NORTH);
getContentPane( ).add (scroller, BorderLayout.CENTER);
}
private void initMenus( ) {
JMenuBar bar = new JMenuBar( );
JMenu fileMenu = new JMenu ("File");
blockMenuItem = new JMenuItem ("Load (blocking)");
dontBlockMenuItem = new JMenuItem ("Load (non-blocking)");
fileMenu.add (blockMenuItem);
fileMenu.add (dontBlockMenuItem);
fileMenu.addSeparator( );
quitMenuItem = new JMenuItem ("Quit");
fileMenu.add (quitMenuItem);
bar.add (fileMenu);
setJMenuBar (bar);
}
private void initActions( ) {
quitMenuItem.addActionListener (new QuitAction( ));
BlockingLoadAction blocker = new BlockingLoadAction( );
blockButton.addActionListener (blocker);
blockMenuItem.addActionListener (blocker);
NonBlockingLoadAction nonBlocker = new NonBlockingLoadAction( );
dontBlockButton.addActionListener (nonBlocker);
dontBlockMenuItem.addActionListener (nonBlocker);
}
public static void main (String[] args) {
AWTBlockDemo awtbd = new AWTBlockDemo( );
awtbd.pack( );
awtbd.setVisible (true);
}
Example 12-8. Demonstration of both blocking and not blocking the AWT event-dispatch
thread during lengthy actions (continued)

Get Swing Hacks now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.