Our next example application is a simple GUI application that implements a very simple (and highly insecure) password manager.
The password manager has three sections. In the top third of the screen users can enter user name/password combinations for sites. The middle section displays a list of all of the sites that the user has stored passwords for. The bottom third allows users to view user name/password combinations for sites they have selected from the list.
The password data is stored in a simple text file. Clicking the Save button saves changes back to that file.
The first class we will need for this application is a Site class, which stores user name/password combinations for the sites we want to work with.
package edu.lawrence.guipasswordmanager; import java.io.PrintWriter; import java.util.Scanner; public class Site { private String name; private String user; private String password; public Site() {} public Site(String name,String user,String password) { this.name = name; this.user = user; this.password = password; } public void readFrom(Scanner input) { name = input.next(); user = input.next(); password = input.next(); } public void writeTo(PrintWriter output) { output.println(name); output.println(user); output.println(password); } public String getName() { return name; } public String getUser() { return user; } public String getPassword() { return password; } @Override public String toString() { return name; } }
The GUI for our application includes a ListView component, which will display the list of Site objects that the application manages. JavaFX has a very specific requirement for data that we want to display in a ListView: we need to put that data into a special type of list called an observable list. Observable lists add an important new capability to ordinary lists: when the contents of an observable list change, the list will notify other objects of those changes. By putting our Site objects into an observable list and then handing that list to the ListView, the ListView can set itself up as an observer of the list. The list will then automatically notify the ListView any time its contents change. Once the ListView receives a change notification it will automatically redraw itself to show the new list contents.
Another important aspect of ListViews is that the user can click on them to select items in the list. We can then ask the ListView to tell us which object the user has selected.
Another requirement we need to watch out for is that the ListView will want to display some text for each item stored in its list. To obtain that text the ListView will call the objects toString()
method.
In the code below we will see how each of these features are implemented in Java.
We are going to be using FXML for this example, which means that we will be writing a controller class for our window.
When we write applications that are designed to work with data we will typically have the application load that data when it starts up. The specific mechanism we will use to do this involves having our controller class implement a special Initializable interface and override that interface's initialize()
method. Doing this will cause FXML to automatically call that initialize()
method when the window opens.
Here is the code for our app's initialize()
method:
private ObservableList<Site> sites; @FXML ListView siteList; @Override public void initialize(URL url, ResourceBundle rb) { sites = FXCollections.observableArrayList(); Scanner input = null; try { input = new Scanner(new File("passwords.txt")); } catch(Exception ex) { ex.printStackTrace(); } while(input.hasNext()) { Site newSite = new Site(); newSite.readFrom(input); sites.add(newSite); } siteList.setItems(sites); }
To make an observable list we call a static observableArrayList()
method in JavaFX's FXCollections class. Once we have filled that list with Site objects that we read from the text file, we can pass that list to the ListView via the ListView's setItems()
method.
Users can click on individual items in a ListView to select them. In this application users can select Sites and ask to see their data. They can also select an item in the list and ask to remove it from the list.
Here is the code for a couple of action methods to do these things.
@FXML private void remove() { Site toRemove = (Site) siteList.getSelectionModel().getSelectedItem(); if(toRemove != null) sites.remove(toRemove); } @FXML private void view() { Site toView = (Site) siteList.getSelectionModel().getSelectedItem(); if(toView != null) { userLabel.setText(toView.getUser()); passwordLabel.setText(toView.getPassword()); } }
To find out what item in a list a user has selected, we start by calling the ListView's getSelectionModel()
method, which returns a SelectionModel object. We then call the getSelectedItem()
method on that object to learn what object the user has selected. getSelectedItem()
returns a generic object reference, so we have to do a type cast to convert the object reference to the right kind of object.