Java Swing Tips

This post collects some tips for Java AWT/Swing features that I found less than obvious in my recent programming. I’m using Oracle OpenJDK 10 on Windows 10, but all Oracle Java Documentation links refer to Java SE 8 as that is the target version of my projects.

2021-03-31: Moved all JComboBox tips to an enhanced and corrected follow-up post, and added some more tips on other components.

Border Insets

The Border implementations returned by BorderFactory do not allow specifying insets in addition to the (non-empty) Border itself, but there is a simple mechanism for this: create a CompoundBorder with a visible outer border and an empty inner border taking up the desired inset space.

File Chooser Issues

The JFileChooser class has some surprising defects: save dialogs don’t ask for confirmation before overwriting a file; the extension of the selected file is not automatically added or updated when a FileNameExtensionFilter selection changes; and extensions specified in that filter cannot contain any dots, i.e. something like .xml.gz is impossible.

There are no built-in options to change these behaviors. You have to write custom code to address them, either after the JFileChooser has closed or through action listeners and/or custom subclasses.

Mouse Event Handling

To handle multiple mouse events, it’s common and convenient to derive from the general classes MouseAdapter or MouseInputAdapter. But while such classes may contain handlers for all kinds of mouse events, those handlers will not actually be called unless you also add your adapter-derived instance to your component using the corresponding specific listener. That is, you must supply it separately to addMouseListener, addMouseMotionListener, and/or addMouseWheelListener as needed. I frequently forget the latter two and then marvel why mouse motion and mouse wheel events aren’t being handled, even though I had implemented them on my adapter-derived class.

Relative Window Locations

The (J)Window class has a handy method setLocationRelativeTo that centers the window on the screen or over another component. One important point here is that the centering location is calculated immediately, based on the currently known size of the window to be centered. Therefore you should place your call to setLocationRelativeTo after any calls to pack and/or setSize, or the window will appear at the wrong location.

Resize Event Handling

To lay out components manually in a container without a layout manager, you’ll need to handle the componentResized event on the container and reposition and/or resize your components as desired. Importantly, once you’ve done that you also need to call revalidate() and repaint() on the components, or the positions and/or sizes may not be updated correctly. The “may” is important: in my experience the layout works sometimes even without revalidate/repaint, so it’s easy to forget these calls and become puzzled over the resulting intermittent layout failures.

Some Popup Tips

The documentation for PopupFactory.getPopup is somewhat confusing. The optional owner parameter is described as “Component mouse coordinates are relative to, may be null.” This seems to imply that when an owner is present the x and y parameters would be relative to it, but that is not the case. Their documentation correctly states that both are absolute screen coordinates, and that is the case regardless of any owner.

Also note that the contract for the PopupFactory class, described at the top of the page, is quite serious. You must indeed call Popup.hide on any popup obtained from a factory before you request another popup from the same factory, or else a myriad of popup remnants will be smeared across the screen!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.