I’m not a big fan of GUI builders: They typically autogenerate bucket-loads of code that then locks in your whole development team to using one IDE. Also, this code is often unreadable (check the code generated when using Matisse under Netbeans).
My recommendations for GUI design / debugging would be:
- Add a
main
method to each panel (or “top-level” component) implementation, allowing other developers to easily determine what a component looks like. - Favour the use of
Action
s overActionListener
s and register these actions with eachJComponent
‘sActionMap
. This allows them to be “extracted” and added to other parts of the UI (e.g.JToolBar
) whilst still having their state controlled by the “owning”JComponent
(i.e. loose coupling). - Use assert to ensure that all UI component modifications are occurring on the Event Dispatch thread; e.g.
assert SwingUtilities.isEventDispatchThread()
. - To debug strange layout behaviour consider painting a component’s background in red!
- Centralise the capturing and reporting of workflow events and exceptions. For example, I typically implement a
TaskManager
class that is registered with my UI’s status bar. Any background processing (performed withinSwingWorker
s) is passed a handle to aTask
created by theTaskManager
. Interracting with the Task (by callingsetDescription(String)
,setThrowable(Throwable)
,cancel()
) causes the status bar to be updated. It also causes the glass pane to be displayed for “global” tasks … but this is all decoupled / hidden from the individual SwingWorkers. - Do not use the
Observer
/Observable
classes, but instead favourChangeListener
,PropertyChangeListener
or your own custom listener implementation for propagating events.Observer
passes anObject
as it’s event, forcing client code to check the type using instanceof and to perform downcasts, making code unreadable and making relationships between classes less clear. - Favour the use of
JTable
overJList
, even in situations where your table only has one column.JList
has some nasty features in its API including the fact that you need to provide a prototype value for it to calculate its size correctly. - Never use
DefaultTableModel
as it typically results in you storing your “model” data in two places: In your actual business objects and also within the 2D array thatDefaultTableModel
sits on. Instead, simply subclassAbstractTableModel
– It’s very easy to do this and means your implementation can simply delegate through to the data structure (e.g.List
) storing your data.