Welcome!

Java IoT Authors: Pat Romanski, Elizabeth White, Liz McMillan, Yeshim Deniz, Mehdi Daoudi

Related Topics: Java IoT

Java IoT: Article

Working with Swing

Working with Swing

One of the strengths of Java is the abundance of standard APIs for doing everything from enterprise-level data access to manipulating data structures, sending and receiving e-mail and building GUIs. This broad sweep of APIs makes choosing how to implement the various parts of an application much simpler, but it also presents a problem: How do you best use the API?

Javadocs – the raw API documentation – are never enough for this purpose because they concentrate on each class or interface as a separate entity from the other classes. API specifications aren't much better because they deal with the minutiae of the API and with being a correct specification of how everything works. The most useful tool I've found for any newcomer to an API is the Java Tutorials (www.java.sun.com/docs/books/tutorial/index.html). These Tutorials guide you to correct and efficient use of an API rather than swamping you with detail.

This article extends the Tutorials and addresses some of the issues a developer might come across when writing an application that uses the Swing API. In it I will dive below the surface of the Swing and AWT toolkits to build a simple but useful component called a splash screen. I'm barely scratching the surface here, but you should learn some useful techniques and have a component that's useful in its own right.

What Is a Splash Screen?
A splash screen is the small window that appears while a program is loading. It usually has none of the decorations normally associated with a window, appears centrally positioned on the screen, and contains a graphic announcing the application and company and often some kind of progress indicator. Figure 1 provides an example.

My primary aim here is to produce a component that encapsulates most of the detail required to create such a window so it's easy for other people to include its functionality in their own applications. Essentially, it'll be an extension to the Swing Library.

Getting Started
First I need to select a component that will produce a top-level window with no decorations. A quick trawl through the API documentation reveals that JWindow is the class I should be using. Creating one of these is simple enough, but I'm going to add specialized functionality later so I may as well subclass JWindow so I can encapsulate that functionality in one place:

import javax.swing.JWindow;
public class SplashScreen extends JWindow {
...
}
Next I want to center the window on the screen. This involves a brief trip back into the AWT class libraries to use the Toolkit class. Ordinarily I wouldn't use this class directly, but it's the source of some useful information – in this case the screen dimensions. The Toolkit class itself is abstract, but can be used to locate an actual implementation. Once I have the implementation I can use it and information about the window size to position the window properly. To get the window size before it's visible, I just have to "pack" it. This code has to be executed after the window has been fully populated; the SplashScreen constructor would be a good place:
pack();
Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
setLocation((screenDim.width – getSize().width) / 2,
(screenDim.height – getSize().height) / 2);

Creating and showing my window now is simple. In the following example I pass in the application's main window as a parent:

SplashScreen ss = new SplashScreen(this);
ss.setVisible(true);
Once I'm finished with the window I'll need to remove it from the screen. I could do this with setVisible(false), but this will leave the window consuming resources (e.g., memory). As I'll never be showing it again, I may as well destroy it so that those resources are released. Merely removing my references to the window isn't sufficient as Swing has internal references that will prevent it from being garbage-collected. To remove Swing's references I need to call dispose() on the window, so I'll add a remove() function to my class that does this for me. This function will be made to perform other tasks later:
public void remove() {
dispose();
}
Filling in the Window
So far, I have a window with no contents, so I need to decide what contents to add and how to lay them out. Earlier I suggested that a splash screen should contain a graphic and a progress indicator. I'd also like to add a title as a separate component and lay out these components from top to bottom in the window. All products that my company ships will use the same layout for their splash screens, providing a consistent look across the whole product range, so I'm free to enforce the policy by encapsulating the code that produces it inside my class. If I wanted a more flexible splash screen, I could expose the standard JWindow functions to allow users to specify a layout manager and to add components as they want. To prevent my applications having to implement boilerplate code to create the splash screen, I'll implement as much as possible inside my class. For this reason my constructor will accept two JComponents for the title and progress indicator (more on why later) and the name of a graphic file to use as the main graphic:
public SplashScreen (JFrame parent,
String graphicName, JComponent title,
JComponent progress) {...}

Graphics is one area where the Java GUI libraries provide excellent facilities. My graphic file can be a JPEG, a GIF or even an animated GIF. All I have to do is create an ImageIcon, passing it the URL of a file containing the image:

ImageIcon i = new ImageIcon(url);
Resources
One issue: What location does this URL point to? I need a location that I can guarantee will exist wherever my application is installed. One of the best locations to fulfill this requirement is somewhere on the classpath. Even better would be to put the graphic in the same place as the rest of the classes that make up this specific application because then I can ship everything the application needs in one simple package.

This tells me where I should put the graphic, but I won't know where my application package will be installed. How can I discover what URL to use to actually load the graphic? Fortunately, the class loader will do this for me. Classloaders can be used to load anything off the classpath – not just classes. First I need to get a classloader – the one that was used to load me. This is easy enough to do. Every object has a method to get its class and hence its classloader. Then I use that classloader to obtain the URL of my graphic:

public URL getURL(String path) {
ClassLoader cl =
getClass().getClassLoader();
return cl.getResource(path);
}
Now I pass that URL to the constructor of ImageIcon as in the previous example.

This is a generally useful facility, so in the interests of reuse I should really encapsulate all this code in a class. ImageIcon has a constructor that takes a string – which it assumes is a filename. I could simply subclass ImageIcon and make the same constructor look for a resource of that name. If that fails, it can default to assuming the string is a filename. If I implement the other ImageIcon constructors too, I can use my new class wherever ImageIcon has been used already without breaking that code – plus I get the benefit of being able to distribute my application, along with the resources it needs, as one JAR file.

Now adding the resulting image to my display is simple. I just create a JLabel to hold it, then add the JLabel to my splash screen. I'll do this in my constructor so the code will look like this:

i = new ResourceImageIcon(path);
image = new JLabel(i);
add(image);
Careful analysis of running code shows I have a problem. The resources used by the instance of ImageIcon aren't completely released when I dispose of the window because ImageIcon caches the image data so that subsequent requests for the same image are faster. Worse, if the image is an animated GIF, the thread that performs the animation continues to run. To prevent this, I need to call flush() on the actual image. I may as well add this code to the remove function, which now becomes:
public void remove() {
i.getImage().flush();
dispose();
}

Swing and Threads
What about that progress indicator? I need to update the contents of one component, and this is one reason I passed in the components themselves rather than just, say, strings. If I make the progress indicator a JLabel, I can hold a reference to it in the object that created the SplashScreen instance and update its contents by calling setText(). I could use any other JComponent that I can dynamically change the contents of, but this will do for my purposes.

The only problem is that the Swing library isn't thread-safe once a component has been realized. In other words, I can't call setText() from any thread other than the Swing thread once I've made the splash screen visible. This would normally be okay because I'd be updating the display in response to a Swing event that would automatically ensure I was in the Swing thread. However, it's likely that I'll update my progress indicator in response to external events (connecting to servers, reading data, etc.), so how do I ensure that I call setText() from the right thread? Fortunately, Swing provides two functions that allow me to queue up work for it: SwingUtilities.invokeLater() and SwingUtilities.invokeAndWait(). The functions take a Runnable as an argument that's responsible for actually performing the work. The Runnable needs to have access to any data it needs at the time it runs. In the following example I call invokeAndWait() from a member function of a class called MainWin. The MainWin object holds a reference to the JLabel I'm using to display the message and to the text the Runnable will need to change the contents of the JLabel. The JLabel reference is called progress and the message reference is called status.

SwingUtilities.invokeAndWait (new Runnable() {
public void run() {
progress.setText(MainWin.this.status);
}
});

I'll have to use this mechanism whenever I want to update the splash screen once it's been made visible. This includes the call to "remove," which will remove it from the screen. I could put this code inside any function of SplashScreen that I think might be called from outside the Swing event loop. This would allow users of SplashScreen to call its methods without having to think about whether they're inside or outside the event loop. If I did this, I'd have to be very careful that I didn't cause deadlocks and that the data I needed didn't change between the user calling my function and Swing invoking the Runnable.

Tidying Up
So far so good, but the window still looks a little messy. I'd like to center the title and the progress indicator and make sure that the colors coordinate properly with the graphic. The code to do this appears in Listing 1. I also think a border should be added to the original wish list for the appearance of the splash screen – I can do this using the BorderFactory class. Plus the image needs to be centered and the components need to be laid out in the right order, running from top to bottom: title, image and then the progress indicator. The code to do all this appears in Listing 2.

Keeping Busy
Finally, I'd like to display a watch cursor while the pointer is over the window and prevent the user from interacting with it. Adding such a cursor can be done simply by adding the following line of code to the constructor for the splash screen:

setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

The easiest way to prevent user interaction with the splash screen is to make the glass pane visible. This pane, which is transparent, is a standard part of Swing windows. It's used to intercept events that would normally go to the window itself – for the splash screen I only need to intercept mouse events. The following code in the constructor of the splash screen will do it:

getGlassPane().addMouseListener(new MouseAdapter() {});
getGlassPane().setVisible(true);
For windows that already have the keyboard focus, I'd also have to intercept key events, but as the splash screen will never receive the focus, this code is sufficient.

Summary
I'm now satisfied that I have a component that fulfills my criteria for a splash screen. This useful component can be used straight out of the box, but more important – for the purposes of this article – it explores some of the real-world problems that GUI developers encounter when writing a Swing application. In particular:

  • Using the Toolkit to obtain information about the screen
  • Using a classloader to locate resources
  • Ensuring that all components are freed up when they're no longer needed
  • Accessing Swing components from other threads
  • Fine-tuning the appearance of a window
  • Putting a window into a "busy" state

More Stories By Paul Andrews

Paul Andrews has worked for over 20 years as a computer scientist in the IT industry and has been actively programming in Java since 1997. Paul specializes in the design of large distributed OO architectures for the implementation of secure e-commerce systems.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


IoT & Smart Cities Stories
The platform combines the strengths of Singtel's extensive, intelligent network capabilities with Microsoft's cloud expertise to create a unique solution that sets new standards for IoT applications," said Mr Diomedes Kastanis, Head of IoT at Singtel. "Our solution provides speed, transparency and flexibility, paving the way for a more pervasive use of IoT to accelerate enterprises' digitalisation efforts. AI-powered intelligent connectivity over Microsoft Azure will be the fastest connected pat...
There are many examples of disruption in consumer space – Uber disrupting the cab industry, Airbnb disrupting the hospitality industry and so on; but have you wondered who is disrupting support and operations? AISERA helps make businesses and customers successful by offering consumer-like user experience for support and operations. We have built the world’s first AI-driven IT / HR / Cloud / Customer Support and Operations solution.
Codete accelerates their clients growth through technological expertise and experience. Codite team works with organizations to meet the challenges that digitalization presents. Their clients include digital start-ups as well as established enterprises in the IT industry. To stay competitive in a highly innovative IT industry, strong R&D departments and bold spin-off initiatives is a must. Codete Data Science and Software Architects teams help corporate clients to stay up to date with the mod...
At CloudEXPO Silicon Valley, June 24-26, 2019, Digital Transformation (DX) is a major focus with expanded DevOpsSUMMIT and FinTechEXPO programs within the DXWorldEXPO agenda. Successful transformation requires a laser focus on being data-driven and on using all the tools available that enable transformation if they plan to survive over the long term. A total of 88% of Fortune 500 companies from a generation ago are now out of business. Only 12% still survive. Similar percentages are found throug...
Druva is the global leader in Cloud Data Protection and Management, delivering the industry's first data management-as-a-service solution that aggregates data from endpoints, servers and cloud applications and leverages the public cloud to offer a single pane of glass to enable data protection, governance and intelligence-dramatically increasing the availability and visibility of business critical information, while reducing the risk, cost and complexity of managing and protecting it. Druva's...
BMC has unmatched experience in IT management, supporting 92 of the Forbes Global 100, and earning recognition as an ITSM Gartner Magic Quadrant Leader for five years running. Our solutions offer speed, agility, and efficiency to tackle business challenges in the areas of service management, automation, operations, and the mainframe.
The Jevons Paradox suggests that when technological advances increase efficiency of a resource, it results in an overall increase in consumption. Writing on the increased use of coal as a result of technological improvements, 19th-century economist William Stanley Jevons found that these improvements led to the development of new ways to utilize coal. In his session at 19th Cloud Expo, Mark Thiele, Chief Strategy Officer for Apcera, compared the Jevons Paradox to modern-day enterprise IT, examin...
With 10 simultaneous tracks, keynotes, general sessions and targeted breakout classes, @CloudEXPO and DXWorldEXPO are two of the most important technology events of the year. Since its launch over eight years ago, @CloudEXPO and DXWorldEXPO have presented a rock star faculty as well as showcased hundreds of sponsors and exhibitors! In this blog post, we provide 7 tips on how, as part of our world-class faculty, you can deliver one of the most popular sessions at our events. But before reading...
DSR is a supplier of project management, consultancy services and IT solutions that increase effectiveness of a company's operations in the production sector. The company combines in-depth knowledge of international companies with expert knowledge utilising IT tools that support manufacturing and distribution processes. DSR ensures optimization and integration of internal processes which is necessary for companies to grow rapidly. The rapid growth is possible thanks, to specialized services an...
At CloudEXPO Silicon Valley, June 24-26, 2019, Digital Transformation (DX) is a major focus with expanded DevOpsSUMMIT and FinTechEXPO programs within the DXWorldEXPO agenda. Successful transformation requires a laser focus on being data-driven and on using all the tools available that enable transformation if they plan to survive over the long term. A total of 88% of Fortune 500 companies from a generation ago are now out of business. Only 12% still survive. Similar percentages are found throug...