COMP 310 |
Dynamically Modifying GUIs |
![]() ![]() ![]() |
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.
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: 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:
|
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!
© 2017 by Stephen Wong