views:

2996

answers:

15

I'm writing a GUI for an application using Swing, and in the interests of code maintenance and readability, I want to follow a consistent pattern throughout the whole system.

Most of the articles and books (or at least book sections) that I've read appear to provide plenty of examples on how to create and arrange various components, but ignore the bigger picture of writing a full GUI.

What are your best tips for application GUI design, and what patterns do you follow when designing or refactoring a GUI application?

+8  A: 

Use layout managers. You might think it's simpler just to position everything with hard coded positions now (especially if you use a graphical layout tool), but when it comes time to update the gui, or internationalize it, your successors will hate you. (Trust me on this, I was the guy saying to use the layout managers from the start, and the successor to the guy who ignored me.)

Paul Tomblin
+9  A: 

Avoid using GUI layout designers (builders). Later on it will make your code much cleaner and easier to maintain.

Dev er dev
It's true that if you start your GUI with a builder, you are pretty much committed to using that builder throughout the whole life of the GUI. This is sometimes acceptable, sometimes not.
Eddie
Yes it's true. Different builders store metadata in different formats which are not mutually compatibel. The only exception is Instantiations Swing Designer which works with the code directly.All of them create code which is not meant to be edited by hand (unreadable) which will bite you later.
Dev er dev
I disagree. If you know Swing well any code generated by a GUI builder will be readable enough. Problem is that by using a GUI builder you will not get to know Swing well. Good separation is of course necessary.
willcodejavaforfood
+3  A: 

mvc is your friend.

emeraldjava
+3  A: 

Get into the habit of having your callbacks spawn off threads to do the actual work, and then you won't be having frozen GUIs when one of your callbacks turns into a time consuming monster.

Paul Tomblin
Better yet, use SwingWorker.
Dev er dev
Yes, SwingWorker is one of many ways to spawn off a thread.
Paul Tomblin
Dont forget SwingUtilities invoker later if that thread needs to update the GUI afterwards :)
willcodejavaforfood
Take a look at FoxTrot: http://foxtrot.sourceforge.net/
Gary Kephart
+2  A: 

Definitely put the GUI in one class and the logic in another class or multiple classes -- to the greatest extent possible. If you use the MVC (Model-View-Controller) pattern, this will happen automatically. If you don't do this, the GUI will quickly become unmaintainably complicated.

Eddie
+2  A: 

Try not to code the text into your app. Swing guis can be pretty easily written to be data driven, consider defining your GUI in an xml file (including the component names and positions/layout attributes).

I worked on systems that had a LOT of property sheets (which are just piles of controls, page after page of them)--without making it data driven, it's virtually impossible to maintain or internationalize.

If you decide to use a GUI builder, never modify the code it outputs if you can possibly avoid it--it's better to bind to the GUI from an external class. Think about what will happen if you have to do it without the builder--will it be difficult to port? Impossible?

Understand the gotchas in swing--only modifying GUI components from the AWT thread, returning the AWT thread as quickly as possible (spawn a new thread if you have to do anything that takes over 100ms),

Try your best to keep your code DRY--It can be a real programming challenge with Swing GUIs--Again, data driven code is the only way I've found to not constantly repeat code like new JButton("...");

If your data is property-sheet based, seriously consider creating a binding mechanism to tie your controls to your data. A good goal for DRY code would be 0 (ZERO) control-specific lines of code per control to get a piece of data from your database to your GUI, have the user edit it and get it back to your DB. This means that you should be able to add a new control by doing nothing but modifying your data.

Bill K
+5  A: 

This is a more abstract high-level answer about what your GUI represents, not the mechanics of it..

Depending on your task, it may be kind of difficult to make it so your user can conceptually grasp what the GUI is doing. I've done some pretty tricky work involving GUIs, and my most successful approaches have been those that took a complex set of controls and put them into a layout that the user expected.

For instance I wrote a system to manage 2 devices one at either end of a T1 line (kinda like modems). The controls were really hard to comprehend--fields like "create loopback, test far end signal, test near end bit patterns, sending various bit patterns, ..." (this is a huge oversimplification, it was a lot worse than this)

I had to really understand the problem, so I went to a Tech Support rep who helped customers with this problem all the time. He showed me a diagram in the manual and stepped me through what the different controls did on that diagram.

I took the diagram, re-created it using graphics (just a simple line-drawing for the most part, but it showed both ends and the connections between them), then used regions of the graphics to represent controls AND feedback (color changes). You could visually see that a signal was going out. When you turned on a loopback at the far end, you could see that the line looped the signal back to it's outgoing line, then you could see the color change as your near-end started getting the pattern that it was sending out it's other line.

The "Controls" were significantly more convoluted than this, but the GUI reduced it to EXACTLY what the customer needed to understand the problem.

After this we had customers coming back to us telling us that they had never been able to figure this stuff out before, but now they totally get it!

This presentation was infinitely more important than the wiring of the GUI implementation.

Bill K
+2  A: 

Have a look at the application framework API ( https://appframework.dev.java.net/ and http://java.sun.com/developer/technicalArticles/javase/swingappfr/. It's a great API to build your swing application. e.g. : all the styles (color, font,icons...) are defined in a simple config file.

Pierre
+12  A: 

Never derive from JDialog, JFrame or JInternalFrame for defining your forms, dialogs...

Rather derive from JPanel. This will bring you the follwing advantages:

  • possibility to later change from a JFrame to a JDialog for instance (because user changed his mind)
  • you can reuse one panel instance from one JDialog to another (JDialog are generally not reusable because they are constructed with a reference to their "parent", a frame or another dialog)
  • you can later on change replace JDialog with a more functional subclass from a 3rd-party framework.
jfpoilpret
Along with this I would define a 'view' interface.
willcodejavaforfood
What should your 'view' interface do?
mklhmnn
+2  A: 

You're not supposed to extend JFrame, JDialog, JPanel, JButton, Janything classes (although certain extensions to table behaviour are only available if you extend it). You can extend JComponent if you feel like doing custom component. If are supposed to implement models (e.g. by extending abstract models), listeners (e.g. by extending adapters), but that's it. You don't need/have to extend swing components usually, and you better not do it, as it makes your code tied to implementation of superclass.

Peter Štibraný
+3  A: 

Avoid inherit when composition would be easier.

For instance I have seen a lot like this:

public class CustomerSupportApp extends JFrame { 
     JList<Customer> customers;
     OtherBusinessComponent importantComponent;

     etc. etc

}

This is mixing business logic with presentation. It only makes changes from difficult to impossible.

Better is:

public class CustomerSupportApp { 
     JList<Customer> customers;
     OtherBusinessComponent importantComponent;
     // The app HAS-A frame but not IS-A frame
     JFrame frame;
     etc. etc
}
OscarRyz
In your case it is obvious, because application != frame, but there are other examples, e.g. `FooFrame extends BarFrame`, where inheritance also is not appropriate, e.g. because they have too less in common.
mklhmnn
+2  A: 

Avoid spawning too many threads when user clicks action button multiple times. Disable button on first click, spawn your action in background thread, and when done, enable button again. This may not be problem for short running tasks.

Peter Štibraný
A: 

I think that the main problem you are going to be faced with is testability of your gui application.

So regarding maintainability and ease of unit testing I am leaning towards the "Presenter first" idiom instead of Model View Controller (MVC) and other derivatives that instruct you to have the view knowing of the actual application logic (Model). The best resource is the web site of the group that introduced it as a thought.

Since using an approach like that is going to take a lot of boilerplate code to initialize the various elements of your application I would also suggest in using a dependency injection framework. I have settled with Guice.

Yorgos Pagles
+5  A: 

I think a good working knowledge of concurrency is often understated. You really need to be familiar with Swing's threading policy and general synchronization techniques to build a responsive GUI and an efficient backend.

Zach Scrivena
+1  A: 

Make heavy use of the MVC pattern. Here's a simple example of what I mean:

class Person
{
  String firstName;
  String lastName;
  // and getters and setters...
}

class PersonSwingModel
{
  private Person person;
  private javax.swing.text.PlainDocument firstName;
  private javax.swing.text.PlainDocument lastName;
  // and getters and setters...
  // Create some method like init() that initializes PlainDocument values
  // to attributes in model.
}

class SavePersonAction extends AbstractAction
{
  private PersonSwingModel model;
  // and getters and setters...
}

class PersonSwingView extends JFrame
{
  private PersonSwingModel model;
  private javax.swing.JTextField firstName;
  private javax.swing.JTextField lastName;
  private SavePersonAction savePersonAction; // hook up to JButton/JMenuItem
  // and getters and setters...
  // Create some method like init() which binds PlainDocument to JTextField
  // and Actions to JButtons or JMenuItems
}

I see some people disagree with extending JFrame or JPanel. I don't. Works for me.

Also, use LayoutManagers. GridBagLayout is very powerful. If you use it, define some GridBagConstraints constants (like LABEL_GBC and FIELD_GBC) and keep reusing them.

Gary Kephart