The Abstract Window Toolkit (AWT) has been upgraded with the new Merlin release. Many of the changes are modest but nonetheless important enough to merit mention. This month, we'll take a look at four new capabilities:
- The ability to discover space used by screen adornments.
- The capitalization of constants in the
Colorclass. - The support for reacting to mouse wheel events.
- The ability to differentiate between mouse buttons, keypad keys, and shift keys better.
To demonstrate the new AWT features, we'll create a program that sizes the screen to the full size (minus desktop adornments like the toolbar) and uses the new color constants to set the background. You'll be able to change background colors by either rotating the mouse wheel or using the left and right shift keys.
Listing 1 provides the basic shell of the program. Unfortunately, there is still a bug in the mouse wheel support capabilities to be used (see Resources for details). You'll need to add a JButton to a JScrollPane to the frame in order for that to work. Because of that bug, the shell is a little more complex than necessary.
Listing 1. Shell to create frame that is closable
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class AwtTest {
// See bug 4475240
static JButton button = new JButton();
static JScrollPane pane = new JScrollPane(button);
public static void main(String args[]) {
JFrame frame = new JFrame("AwtTest");
// See bug 4475240
frame.getContentPane().add(button, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
}
|
In a previous column, I mentioned the ability to maximize a Frame with setExtendedState()
. If you set the state to Frame.MAXIMIZED_BOTH, your frame is fully maximized. If, however, you don't want the frame maximized, but you do want it sized to fill the user's desktop area, less any desktop adornments, you can now ask for the Insets used by those adornments (see Listing 2). This information is provided by the getScreenInsets() method of Toolkit. If you subtract the insets from the screen size, you have the size your window should be. To position it properly, you'll need to use the top and left insets, too.
Listing 2. Size the screen
private static void sizeScreen(JFrame frame) {
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
GraphicsConfiguration config = frame.getGraphicsConfiguration();
Insets insets = kit.getScreenInsets(config);
screenSize.width -= (insets.left + insets.right);
screenSize.height -= (insets.top + insets.bottom);
frame.setSize(screenSize);
frame.setLocation(insets.left, insets.top);
}
|
Just add a call to sizeScreen() into your main() method.
Section 6.8 of the Java Language Specification includes naming conventions for items like packages, methods, fields, and constants. The subsection on constant names (6.8.5) states that constants shouldn't use lowercase letters, but since the beginning, all the color constants in the Color class, like red, green, and blue, have been specified in all lowercase. Merlin sets the record straight and now provides the same constants in all uppercase, too. The old names aren't deprecated, yet, but you can use the newer names with your 1.4 programs.
For the test program, we'll create an array of color constants and a counter to loop through them, then provide a changeBackground() method that loops through them, in one of two directions (see Listing 3).
Listing 3. Change the background color
private static final Color colors[] = {
Color.BLACK,
Color.BLUE,
Color.CYAN,
Color.DARK_GRAY,
Color.GRAY,
Color.GREEN,
Color.LIGHT_GRAY,
Color.MAGENTA,
Color.ORANGE,
Color.PINK,
Color.RED,
Color.WHITE,
Color.YELLOW
};
static int colorCounter;
private static final int UP = 1;
private static final int DOWN = 2;
private static void changeBackground(JFrame frame, int direction) {
// See bug 4475240
// w/o bug, change background of getContentPane()
button.setBackground(colors[colorCounter]);
// Update counter based on direction
if (direction == UP) {
colorCounter++;
} else {
--colorCounter;
}
// Wrap colors
if (colorCounter == colors.length) {
colorCounter = 0;
} else if (colorCounter < 0) {
colorCounter = colors.length-1;
}
}
|
Add a call to the changeBackground() method to your main() routine.
The naming conventions were also updated in some other classes, like GridBagLayout, which included several protected methods like AdjustForGravity() that have now been modified to begin with a lowercase letter.
Detecting mouse wheel movement
One feature that Java developers have clamored for since the last century is support for mouse wheel rotation. With the Merlin release, you can now attach a MouseWheelListener to any Component and respond accordingly. Other than the previously mentioned bug, it works nicely. The JScrollPane component even comes with a pre-registered listener, so the pane will scroll when it has focus and the user moves the wheel.
The listener has a single method, mouseWheelMoved(), which gets a MouseWheelEvent argument. From the event, you can find out the amount to scroll with getScrollAmount(), whether it is a unit or block scroll with getScrollType(), the direction and number of wheel rotations with getWheelRotation, and, for convenience, the units to scroll with getUnitsToScroll().
In the test program, we'll attach a wheel listener that has the background color loop through the array of colors based upon the direction, as shown in Listing 4. Documentation states that mouse wheel events propagate up the container hierarchy, but the bug prevents this from happening so you'll need to attach the listener to the button. Be sure to add the listener in the main() method.
Listing 4. Attach wheel listener
private static void attachMouseWheelListener(final JFrame frame) {
MouseWheelListener listener = new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent e) {
int count = e.getWheelRotation();
int direction = (Math.abs(count) > 0) ? UP : DOWN;
changeBackground(frame, direction);
}
};
button.addMouseWheelListener(listener);
}
|
This last feature is larger than you might first think, as the test program uses only a small part of the added capabilities. Basically, Merlin adds the ability to differentiate between different versions of the same key (keypad and regular) and different sequences of keyboard keys and mouse buttons. For instance, previously, there was no way to tell the difference between a shift-click and clicking a different button; both produced the same mouse event. With the help of the following new constants in the InputEvent class, that's all changed:
-
ALT_DOWN_MASK -
CTRL_DOWN_MASK -
META_DOWN_MASK -
SHIFT_DOWN_MASK -
BUTTON1_DOWN_MASK -
BUTTON2_DOWN_MASK -
BUTTON3_DOWN_MASK -
BUTTON1_CHANGED_MASK -
BUTTON2_CHANGED_MASK -
BUTTON3_CHANGED_MASK
If all you want to find out is which button is down, the following methods are of more use:
-
isButton1Down() -
isButton2Down() -
isButton3Down()
The MouseEvent also provides a getButton() method to discover which button changed state.
All these methods and constants should make responding to button events easier.
Dfferentiating between different versions of the same key requires changes to the KeyEvent class. You can ask the event for the key location with getKeyLocation(); you get back one of the following constants:
-
KEY_LOCATION_LEFT -
KEY_LOCATION_NUMPAD -
KEY_LOCATION_RIGHT -
KEY_LOCATION_STANDARD -
KEY_LOCATION_UNKNOWN
By asking where the location of a key is, you can differentiate between keypad and standard keys as well as left and right shift keys.
For the test program, we'll simply have a key listener loop up or down based on whether the left or right shift key is pressed, as shown in Listing 5. We must be sure to check which key is pressed first, then check for location. And, of course, add a call to the routine in the main() method.
Listing 5. Attach key listener
private static void attachKeyListener(final JFrame frame) {
KeyListener listener = new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
int location = e.getKeyLocation();
int direction = (location == KeyEvent.KEY_LOCATION_LEFT) ?
UP : DOWN;
changeBackground(frame, direction);
}
}
};
button.addKeyListener(listener);
}
|
Listing 6 provides a complete example for you to try out all these new capabilities. When the mouse wheel listener bug is fixed, you'll be able to remove the use of the button and scroll pane.
Listing 6. Complete example
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class AwtTest {
private static final Color colors[] = {
Color.BLACK,
Color.BLUE,
Color.CYAN,
Color.DARK_GRAY,
Color.GRAY,
Color.GREEN,
Color.LIGHT_GRAY,
Color.MAGENTA,
Color.ORANGE,
Color.PINK,
Color.RED,
Color.WHITE,
Color.YELLOW
};
static int colorCounter;
private static final int UP = 1;
private static final int DOWN = 2;
// See bug 4475240
static JButton button = new JButton();
static JScrollPane pane = new JScrollPane(button);
private static void sizeScreen(JFrame frame) {
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
GraphicsConfiguration config = frame.getGraphicsConfiguration();
Insets insets = kit.getScreenInsets(config);
screenSize.width -= (insets.left + insets.right);
screenSize.height -= (insets.top + insets.bottom);
frame.setSize(screenSize);
frame.setLocation(insets.left, insets.top);
}
private static void changeBackground(JFrame frame, int direction) {
// See bug 4475240
// w/o bug, change background of getContentPane()
button.setBackground(colors[colorCounter]);
// Update counter based on direction
if (direction == UP) {
colorCounter++;
} else {
--colorCounter;
}
// Wrap colors
if (colorCounter == colors.length) {
colorCounter = 0;
} else if (colorCounter < 0) {
colorCounter = colors.length-1;
}
}
private static void attachMouseWheelListener(final JFrame frame) {
MouseWheelListener listener = new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent e) {
int count = e.getWheelRotation();
int direction = (Math.abs(count) > 0) ? UP : DOWN;
changeBackground(frame, direction);
}
};
// See bug 4475240
// w/o bug, add to frame
button.addMouseWheelListener(listener);
}
private static void attachKeyListener(final JFrame frame) {
KeyListener listener = new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
int location = e.getKeyLocation();
int direction = (location == KeyEvent.KEY_LOCATION_LEFT) ?
UP : DOWN;
changeBackground(frame, direction);
}
}
};
// See bug 4475240
// w/o bug, add to frame
button.addKeyListener(listener);
}
public static void main(String args[]) {
JFrame frame = new JFrame("AwtTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// See bug 4475240
frame.getContentPane().add(button, BorderLayout.CENTER);
sizeScreen(frame);
changeBackground(frame, UP);
attachMouseWheelListener(frame);
attachKeyListener(frame);
frame.show();
}
}
|
| Name | Size | Download method |
|---|---|---|
| j-mer1106awttest.zip | 1 KB | HTTP |
Information about download methods
- Participate in the discussion forum.
- Section 6.8 of the
Java Language Specification
defines naming conventions previously ignored by some system classes.
- Read about the
MouseWheelListenerbug in Bug Report 4475240. - Read about the request for mouse wheel support Bug Report 4289845.
- Bertrand Portier discusses the new (and significantly improved) AWT focus model and provides tips for migrating your code to the new model in his article "Java 2 gets a new focus subsystem" (developerWorks, October 2001).
- Read the complete collection of Merlin tips by John Zukowski.
- Find more Java resources on the developerWorks Java technology zone.

John Zukowski conducts strategic Java consulting with JZ Ventures, Inc. and serves as the resident guru for a number of jGuru's community-driven Java FAQs. His latest books are Java Collections and Definitive Guide to Swing for Java 2 (2nd ed) from Apress. You can contact John at jaz@zukowski.net.




