Introduction
Swing is the primary Java GUI widget toolkit. It is easy to use but there are things that can be improved , for example assumptions of common defaults or settings, also the use of certain types of collections when binding properties to objects.
I've find some of these improvement areas and created a solution called SwingHelper as a library. The goal of this library is to set commons defaults when using Swing components in order to re-use code and make faster and standard method invocations.
This article is created with the goals of learn basics about Swing and the usage of the SwingHelper open-source library.
Background
As written on the introduction, Swing is the primary Java GUI, but it is not the only one available for Java to create desktop applications, we can find AWT, SWT, SwingX, JavaFX, Apache Pivot, and many others.
AWT was the first Java GUI, and Swing is its successor. Swing was created to provide rich components completely written on Java, which means that they are not implemented by platform-specific code, it is also known as “lightweight” elements, the ability of Swing to render their own components by painting them using Java2D APIs and not depending of native user interface toolkits.
The reason why I choose Swing to work on my projects is because it is completely written on Java and it is included on the JVM so no extra libs are needed, and the main reason for me is that I can create OS independent applications using the Look and Feel that I want, that can be one of the Java's default that looks the same on all platforms, or my favorite, using the same as current OS, all in one pure jar that is executable in any platform, literally “write once, run anywhere”.
Using the code
What is SwingHelper?
It is an open-source library written on Java, it is a set of common invocations to components that reduce the complexity assuming default values. Also it implements some AbstractModel to bind data with components just in seconds.
Downloading the library
SwingHelper is open-source, it means you can download it for free and add it to your project. If you want to improve something you can download the source code and check how it works.
Source code are hosted at:
https://code.google.com/p/sidereal-libraries/
Since Google Code recently changed
their download policy, the binary releases can be found at:
https://drive.google.com/folderview?id=0B_QgKAk4BATMaTUxNWNpVlNvcVU&usp=sharing
The current version is 1.1
The SwingHelper class and methods
Basically what you can do with this
class is:
-Set the Look and Feel of the whole application. Note: The System Look and Feel is set as default instead
of the Metal one.
Examples:
SwingHelper.setLookAndFeel(SwingHelper.LookAndFeel.NIMBUS);
SwingHelper.setLookAndFeel(SwingHelper.LookAndFeel.MOTIF);
SwingHelper.setLookAndFeel(SwingHelper.LookAndFeel.METAL);
SwingHelper.setLookAndFeel(SwingHelper.LookAndFeel.SYSTEM);
-Change the themes for Metal Look and Feel
Examples:
SwingHelper.setMetalTheme(SwingHelper.MetalTheme.DEFAULT_METAL);
SwingHelper.setMetalTheme(SwingHelper.MetalTheme.OCEAN);
-Center a window
Example:
SwingHelper.centerWindow(customerJDialog);
-Display an error messages
Example:
SwingHelper.showErrorMessage("Error title", "This is an error message.");
-Display a warning messages
Example:
SwingHelper.showWarningMessage("Warning title", "This is a warning message.");
-Display an information messages
Example:
SwingHelper.showInformationMessage("Information title", "This is an information message.");
-Display an Input Dialog asking for a non-empty value
Example:
String result =
SwingHelper.askForRequiredTextUsingInputDialog("Text request", "Write your name:");
-Display a Confirmation Dialog with Yes/No or Yes/No/Cancel or OK/Cancel buttons
Examples:
String title = "Click on a button to confirm";
String message = "Click on YES button";
String result =
SwingHelper.showConfirmationDialogWithYesNoButtons(title, message);
if(result.equals("yes")){
}
-Display a File Chooser asking for a folder
Example:
String title = "Choose a folder"
String startDirectory = "target/test-classes/com/diegohp/swing/";
File result = SwingHelper.chooseDirectory(title, startDirectory);
-Display a File Chooser asking for a file
Example:
String title = "Choose any file";
File result = SwingHelper.chooseFile(title);
-Display a File Chooser asking for a file with certain extension
String title = "Choose a text file";
String fileExtension = ".txt";
File result = SwingHelper.chooseFile(title, fileExtension);
All of these features have several parameters combinations (some of them didn't appear in the above examples), so you can choose the one that you need for each situation.
The most common parameters are: title (of the window), startDirectory (the folder that a file chooser opens as default), fileExtension (the extension of the file to be selected), startFile (the file that a file chooser selects as default), lookAndFeel (one of the options of an enum of the same name with all the needed information), metalTheme (one of the options of an enum of the same name with all the needed information), message (the message displayed on message window).
The ListComboBoxModel class
What you can do with this class is:
-Have a class that extends from AbstractListModel and implements MutableComboBoxModel. All methods are implemented, objects of this class are ready to use out of the box
- AbstractListModel gives to the class the ability to use a List as a container of data (in this case, an ArrayList with parametrized type)
- MutableComboBoxModel gives to the class the ability to change its data even after the UI component was displayed
Example:
We are going to create a window that asks to the user the selection of a language.
Let's declare a JDialog with a Combo Box and a button:
public class LanguageSelectorJDialog extends javax.swing.JDialog{
private javax.swing.JComboBox jComboBoxLanguages;
private javax.swing.JButton jButtonSelect;
}
Now use ListComboBoxModel to create the model of the Combo Box:
private ListComboBoxModel<String> languagesListComboBoxModel;
Note: in this case the parametrized type of the container was set to <String>, but it can be anything you want, Integer, Long, even your custom classes for example pojos that overrides the toString() method (what the component reads to put in the UI) to return something useful to the user.
Next, in the constructor of the JDialog we are going to populate the model data:
this.languagesListComboBoxModel.addElement("English");
this.languagesListComboBoxModel.addElement("Español");
Now we are going to implemented the logic of the click on the select button:
private void jButtonSelectActionPerformed(java.awt.event.ActionEvent evt) {
String selected = (String) this.languagesListComboBoxModel.getSelectedItem();
if (selected.equals("English")){
Locale.setDefault(Locale.ENGLISH);
System.out.println("Language set to English");
} else {
Locale.setDefault(new
Locale("es"));
System.out.println("Language set to Spanish");
}
this.dispose();
}
This is a pretty easy example of a usage of the ListComboBoxModel class, but remember the ability of the parametrized type. In a more complex example we need to use instead of a <String> a custom class called Language that has a name (English, Spanish) and a number of language (345, 874) and the list of languages are not limited to only 2, they can be more based on records of a data base. Fist you need to override the toString() method of the Language class to return for example just the name (since the user is not going to understand the number of language), then you just need to add all the list of languages to the model and when the user selects a language and click on select button, the model return you the whole object selected. With this feature you avoid to create an auxiliary list as an index of the objects that fits the old model limitations and start a search based on the value selected on the real objects list, saving you time, memory, processor time and unneeded code.
What you can see with this is something like this:
The ListTableModel class
As the ListComboBoxModel, this class shares the same objetives:
-Have a class that extends from AbstractTableModel, it implements all the methods as much as it can, leaving just one method to be implemented, getValueAt(int row, int col), which is very easy, you just need to return the value of the corresponding column. When trying to implement an AbstractTableModel you get just a 40% of the class, using ListTableModel you get a 99% of the implementation with a flexible and mutable logic.
-It has a List of Strings as variable to populate with the columns names, so you can change them even after the component was displayed
- Implemented the usage of a List as a container of data (in this case, an ArrayList with parametrized type), very useful when displaying and editing information in a table
Example:
For this scenario we are going to create a window with a table of costumers, when a costumer is clicked, a message is displayed with information and a count of views is incremented in the table.
First, let's create an entity class of Customer:
public class Customer {
private Long id;
private String name;
private Integer views;
}
Now, let's create a JDialog with a table, a model and an object of Customer to keep the selected customer:
public class CustomerTableModelJDialog extends javax.swing.JDialog {
private javax.swing.JTable jTableCustomers;
private ListTableModel<Customer> customersListTableModel;
private Customer customerSelected;
}
Once declared the model, we need to create its instance by implementing the remaining method. In this case our first column will display the Id of the customers, the seconds displays the name and the third displays the numbers of views, so based on that we implement the method to return the respective value of the object:
this.customersListTableModel = new ListTableModel<Customer>() {
@Override
public Object getValueAt(int row, int col) {
Customer customer = this.objects.get(row);
if (col == 0) {
return customer.getId();
}
if (col == 1) {
return customer.getName();
}
if (col == 2) {
return customer.getViews();
}
return customer.getId() + " -> " + customer.getName();
}
};
Now we need to declare the names of the headers of the table, following our order we set it on this way:
List<String> columnsNames = new ArrayList<String>();
columnsNames.add("Id");
columnsNames.add("Name");
columnsNames.add("Views");
this.customersListTableModel.setColumnNames(columnsNames);
The next step is to create a listener to capture the value changed moment and implement some specific logic when a customer is selected:
this.jTableCustomers.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent lse) {
if(!lse.getValueIsAdjusting()){
ListSelectionModel model = jTableCustomers.getSelectionModel();
if(model.getLeadSelectionIndex() >= 0){
customerSelected = ((ListTableModel<Customer>)jTableCustomers.getModel()).getObjectAt(model.getLeadSelectionIndex());
customerSelected.setViews(customerSelected.getViews() + 1);
customersListTableModel.fireTableDataChanged();
SwingHelper.showInformationMessage("Clicked on a Customer",
"Customer's Id is " + customerSelected.getId() + " and
name is " + customerSelected.getName());
}
}
}
});
Once the model is ready, let's set it to the table:
this.jTableCustomers.setModel(this.customersListTableModel);
In order to populate the table, we need to create some customer, add them to the table and update the changes:
List<Customer> customers = new
ArrayList<Customer>();
Customer c1 = new Customer();
c1.setId(123456789L);
c1.setName("Harry Mason");
c1.setViews(0);
Customer c2 = new Customer();
c2.setId(987654321L);
c2.setName("James Sunderland");
c2.setViews(0);
customers.add(c1);
customers.add(c2);
this.customersListTableModel.addObjectList(customers);
this.customersListTableModel.fireTableDataChanged();
And that's it. You will see something like this:
If you want to check all the code of these examples, download the source code and check out the test classes, they contain all the examples. The project was created with Maven so it should be easy to run the test when building the code.
Points of Interest
-I was a teacher of Java and I used
Swing to teach how to create desktop applications. Swing is easy to
learn for my students and very useful. I've been creating desktop
apps since then for personal and professional projects. Before that I
had used other solutions for this, but after trying Swing I found it
very interesting and easier in some way, and also the ability to
create one executable that can run using the system interface in
Windows, Mac and Linux at the same time with a single double click,
was priceless.
-Swing doesn't need of native libraries
and doesn't need to be re-compilated for each OS, it makes it very
portable, light and easy to distribute. Give it a try using Netbeans
and test the visual designer, I bet you are going to found it
intuitive.
-SwingHelper is a library with code
that I noticed I started to use frequently, so I put it on the same
class and tried to optimized it. My goal with this library is to add
more functionality that I will collect and publish in future
releases.
History
Oct 2 2013, Created the article using
the SwingHelper 1.1 release