Lesson 6 - Birthday Reminder in Java Swing - Wiring the layers
In the previous lesson, Birthday Reminder in Java Swing - Logic Layer, we finished the basis of the logic layer of our application. In today's tutorial, we're going to wire it to the form and make the application up and running.
Wiring the presentation and logic layers
Now we've both the presentation part of the application (forms) and the logic part (classes) ready. These 2 layers are strictly separated in applications. Otherwise, the code would be very confusing. You should never perform calculations, file manipulations, database queries, and similar things directly in the form code! We always create a class that provides the appropriate methods and we only use that class in the form. The logic stays in the class and the class shouldn't even know that the form exists. Therefore, for example, the class also shouldn't display any error messages but only throw exceptions in case of an error. It's the form what is responsible for displaying error messages to the user. The form is the part of the application that communicates with the user. No other does it.
If it crossed your mind that our simple calculator, which we created in first lessons of the course, was incorrectly designed, you're right. For the sake of simplicity, we've written the calculations directly into the button handling method. The right way would be having a class that computes the results, and we would then just call it from the form.
So now let's show how to do it right.
Wiring the presentation and logic
Let's move to the source code of the OverviewJFrame
form and add
a private
attribute of the PersonManager
type to the
class. We'll initialize it with a new PersonManager
instance:
private PersonManager personManager = new PersonManager();
The PersonManager
instance is created when the form is created,
and the form will then communicate with it to perform the tasks the user
wants.
In the form's constructor, we'll set the todayJLabel
to the
current date and set the JList
's model property to the
PersonManager
's model. That's how we bind the JList
to
the DefaultListModel
. From now on, it'll show its contents and if
something is added to the model, it'll also be reflected in the
JList
. If there's any person in the model, we'll set the selected
JList
item to the first index.
public OverviewJFrame() { initComponents(); todayJLabel.setText(Date.format(LocalDate.now())); personsJList.setModel(personManager.getModel()); if (!personManager.getPersons().isEmpty()) { personsJList.setSelectedIndex(0); } }
Adding and removing people
It's time for us to finally see something, so we're going to add some people.
First, we'll move to the code of the PersonJDialog
. We'll add a
private
attribute of the Person
type here and generate
a getter for it. This is where we'll load the person data from when we'll open
the dialog for the person the user has selected.
private Person person = null; public Person getPerson() { return person; }
Now we'll click the OK button and create there a person based on the data
that the user has entered. We'll save the person to a private
attribute.
If anything fails, we'll display a MessageDialog
with an error.
You've already encountered a try
-catch
block that
allows you to catch exceptions and somehow handle these errors instead of
letting the application crash.
private void okJButtonActionPerformed(java.awt.event.ActionEvent evt) { try { LocalDate birthday = Date.parse(birthdayJFormattedTextField.getText()); person = new Person(nameJTextField.getText(), birthday); dispose(); } catch (ParseException | IllegalArgumentException ex) { JOptionPane.showMessageDialog(null, "Error: " + ex.getMessage()); } }
It's the line:
person = new Person(nameJTextField.getText(), birthday);
which can throw an exception. As we throw the exception directly in the
person's constructor in case of invalid input, you can check it there. Another
exception can be caused by parsing the date, even if it actually never happens
because of the JFormattedTextField
. We placed the code which may
throw an exception in the try
block. Then we catch the exception in
the catch
block and display its message to the user. If everything
goes smoothly, the program won't even get into the catch
block. We
usually throw exceptions in logic classes and then catch them in forms.
Exceptions are described in more detail in the Exceptions in Java lesson. We use
dispose()
to close the current form.
We're done with this form so let's move back to the
OverviewJFrame
. This time, we'll open the design view and
double-click the addJButton
and removeJButton
buttons.
private void addJButtonActionPerformed(java.awt.event.ActionEvent evt) { PersonJDialog personJDialog = new PersonJDialog(this, true); personJDialog.setLocationRelativeTo(null); personJDialog.setVisible(true); Person newPerson = personJDialog.getPerson(); if (newPerson != null) { personManager.add(newPerson); } }
In the addJButton
's handling method, we create a new
PersonJDialog
instance. In its constructor, we set the parent form
to the current OverviewJFrame
instance (this
) and the
dialog as modal in the second parameter. When the dialog box is displayed using
setVisible(true)
, it'll block the rest of the application. The app
will only become active again if the dialog box is closed. This means that we
won't be able to work with the main form until we confirm or close the dialog
box. We usually do display dialogs like this to prevent the user from opening
the same dialog multiple times. Although we wouldn't mind if the user was using
the app while entering a new person and even opened another input dialog. We'll
load the person from the dialog box and if there's any (the user confirmed the
dialog by clicking the OK button and didn't cancel it), we add this person to
the manager. Because we use ListModel
, the person will be displayed
in the form's JList
immediately.
The handling method of the removeJButton
will look like
this:
private void removeJButtonActionPerformed(java.awt.event.ActionEvent evt) { Person selected = (Person)personsJList.getSelectedValue(); if (selected != null) { personManager.remove(selected); } }
The JList
may get initialized with the Type
Parameter set to String
which would then throw an error. If this is
your case go to the Design tab of the OverviewJFrame
, click on the
JList
and look for a Code tab located at the same side bar where
Properties and Events tabs are. There you'll find Type Parameters
option with the <String>
value set. Simply remove it and
you're good to go.
The condition that determines whether a JList
item is selected
is very important. As you can see, we get to the selected item via the
getSelectedValue()
property. The item is then cast to
Person
because it's of an Object
type (this is to make
the JList
universal). We'll pass this person to the remove method
on the manager. This method will also physically remove it from the
ListModel
.
You can now try the app to add and remove people. Every time you add someone,
they will immediately appear in the JList
thanks to bindings. The
JList
always shows what the object's toString()
method
returns. Thus, for persons, it displays their name.
Next time, Birthday Reminder in Java Swing - Completing the logic, we'll add more methods to the application's logic layer to complete it.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 30x (74.1 kB)
Application includes source codes in language Java