COMP 310

Dynamically Modifying GUIs

    Current Home  Java Resources  Eclipse Resources

It is very common to want to make a GUI that is dynamically modifiable enable it to adapt to changing situations or to present different user capabilities in different situations or to augment the GUI as the system dynamically changes.  

Whenever one is modifying the GUI, always remember

ALL GUI MODIFICATIONS MUST TAKE PLACE ON THE JAVA GUI ("AWT EVENT DISPATCH") THREAD!

This is not a problem if all your code takes place on the GUI thread, but one must take special care if the code that wants to do the modifications is not on the GUI thread, or perhaps is unknown as to whether it is or is not on the GUI thread.  

To transfer operation over to the GUI thread, use the SwingUtilities.invokeLater(Runnable r) (non-blocking) or SwingUtilities.invokeAndWait(Runnable r) (blocking) methods.   The SwingUtilities.isEventDispatchThread() can be used to determine whether the current thread is or is not the GUI thread.  invokeLater() can be called from any thread, even the GUI thread because it is non-blocking, but invokeAndWait() must be called from a non-GUI thread or it will deadlock the GUI, so isEventDispatchThread() must be checked first to insure that one is not on the GUI thread.

Techniques for Adding General Components to a GUI

Recommendation: Restrict the components being created to JComponents for maximum capabilities and safety.

There are many ways to provide services that allow other parts of the system to dyanamically add components, typically JComponents, to the GUI in a GUI-controlled manner.  Let's compare and contrast 3 different techniques for implementing the service provided by the GUI to the user's code to add the user's component to the GUI:

Technique: 

Pros:

Cons:

Notes:

Recommended: Give the GUI a factory that creates the desired component to add:  
void buildComponent(IComponentFactory fac)

where IComponentFactory is a factory that instantiates a JComponent,  e.g. java.util.function.Supplier<JComponent>.
Simplest method overall.   No GUI threading issues on the user side, any thread can be used.   Simple GUI thread dispatching on the GUI side due to the void return.   Seemingly more complicated as it requires the definition of the factory interface.  Note that the code of the factory is really the same as the code in the other two techniques below for instantiating the desired component, it's just in a different place.    The use of lambda expressions can help keep the code relatively smaller however plus help with making sure the factory has access to everything it needs (via the lambda's closure). The void return means that the entire process of preparing the GUI, instantiating the desired component using the supplied factory, and installing the resultant component can all be easily dispatched onto the GUI thread using invokeLater() with no thread synchronization needed. 
Get a container fro the GUI to put components into:
Container getContainer()
Deceptively simple looking code that can be incorporated into the process that is instantiating the desired component to add. No guarantees that the method is invoked on the GUI thread, so GUI side code must must check the thread and then do thread synchronization if it is not the GUI thread in order to return the container (created on the GUI thread) back on the original thread.  User side code must also be on the GUI thread when adding components to the container.   Effectively couples the GUI's container generation to the user's component instantiation process.
Add an already instantiated component to the GUI:
void addComponent(JComponent comp)
Deceptively simple looking code which separates the user's instantiation of the component from its installation.
No thread synchronization on the GUI side needed due to void return.  
Requires that the user's code properly instantiate the component on the GUI thread which might not be the thread that is calling addComponent(), so thread synchronization may be needed on the user's side. By always using SwingUtilities.invokeLater() to add the component to the GUI, this technique can be called from any thread.

 

Don't get mislead by a seemingly "simpler" implementation by the latter two options above!   The surrounding threading issues actually make their code MORE complicated and much more difficult in the end.   The "factory" method completely releases the caller side from the GUI threading issues and is a very simple dispatch to the GUI event thread on the implementation (local system) side.   So in the end, it is actually the simplest of all the options!

 

Back to Java GUIs


© 2017 by Stephen Wong