Workshop Lab2 - Writing a GUI from scratch

From Juneday education
Jump to: navigation, search

Description

In this workshop, we'll build a Swing GUI together from scratch (starting with an empty Java file and two eager hands).

We'll build a simplistic but functional GUI for the old assignment about the address book from our "Programming in Java" book.

Preparations

If you want to come prepared to this workshop, start by locating your old address book assignment from the Programming in Java book. You will copy classes and stuff from your solution to that workshop to this workshop. This will take valuable time from the workshop schedule, so it is great if you have located your solution to the workshop before the workshop (see course schedule for when this workshop is held).

If you haven't done the address book assignment, you must do that before taking this workshop. Link: Assignment_-_Address_book

You should also have read the chapter Introduction_to_Swing_-_A_GUI_for_the_Address_book_assignment (and seen the videos) before coming to the workshop. We will assume that you have done the above.

Phase one - locate your code from the address book assignment

We need to find the code for the address book assignment, so that we have a working address book programming library (an API with classes for storing contacts and listing contacts etc).

Find your code from your working address book assignment. You will copy stuff from that assignment to your directory structure for this workshop.

Just make a note of where your classes are. If you can't find them, ask a friend to send you his or her files later.

Phase two - create a directory structure for this workshop

This is the layout we're looking for, to work with in this workshop:

.
└── org
    └── contactcompany
        ├── api
        │   ├── container
        │   ├── entry
        │   ├── ui
        │   │   └── textmenu
        │   └── util
        └── application
            ├── list
            ├── main
            └── ui

You can create that directory structure the following way:

$ mkdir swing-workshop
$ cd swing-workshop
$ mkdir -p org/contactcompany/{api/{container,entry,ui/textmenu,util},application/{list,main,ui}}

Phase three - copy some files from the address book assignment to your new project directory

Find the following files from your assignment with the address book:

Hint: You can use the command find to find files, e.g. find ~ -name 'SimpleMutableList.java'.

Copy the file SimpleMutableList.java to org/contactcompany/api/container/ and verify that the file was copied:

$ ls org/contactcompany/api/container/SimpleMutableList.java 
org/contactcompany/api/container/SimpleMutableList.java

Copy Contact.java to org/contactcompany/api/entry/ and verify that it was copied:

$ ls org/contactcompany/api/entry/Contact.java 
org/contactcompany/api/entry/Contact.java

Copy MenuAction.java and Menu.java to org/contactcompany/api/ui/textmenu/ and verify the copying.

Copy TextUtils.java to org/contactcompany/api/util/ and verify.

Your directory should now look like this:

.
└── org
    └── contactcompany
        ├── api
        │   ├── container
        │   │   └── SimpleMutableList.java
        │   ├── entry
        │   │   └── Contact.java
        │   ├── ui
        │   │   └── textmenu
        │   │       ├── MenuAction.java
        │   │       └── Menu.java
        │   └── util
        │       └── TextUtils.java
        └── application
            ├── list
            ├── main
            └── ui

The package org.contactcompany.api will contain all classes for the address book API which we have gotten from the old assignment.

Change the package declarations in the classes you copied so that they match the directory structure. Why? Because, you had a different package structure when you did the assignment!

If you get it right, you should be able to verify it by comparing to this:

$ grep -r 'package ' .
org/contactcompany/api/container/SimpleMutableList.java:package org.contactcompany.api.container;
org/contactcompany/api/util/TextUtils.java:package org.contactcompany.api.util;
org/contactcompany/api/entry/Contact.java:package org.contactcompany.api.entry;
org/contactcompany/api/ui/textmenu/Menu.java:package org.contactcompany.api.ui.textmenu;
org/contactcompany/api/ui/textmenu/MenuAction.java:package org.contactcompany.api.ui.textmenu;

Compile to verify that your classes at least compile:

$ javac org/contactcompany/api/*/*.java org/contactcompany/api/ui/textmenu/*.java

There is one more file to copy from the old address book assignment, and that is ContactList.java. It is an implementation of the SimpleMutableList<Contact> from the api, we'll actually pretend that we wrote that now, and we'll put it in the org/contactcompany/application/list/ package. It is a class using the api stuff about contacts but it will be used by code in our GUI application, so we decided to put it closer to home, so to speak (closer to where it is used). ContactList will be used directly in our GUI code, and we'll pretend that we wrote that code now using the stuff in the api package.

If you cant find it, use this: ContactList.java.

Change the class' package declaration to package org.contactcompany.application.list; and make sure you change the import statements for the api stuff to:

import org.contactcompany.api.container.SimpleMutableList;
import org.contactcompany.api.entry.Contact;

Compile it:

# remove all class files from previous compilation:
$ find . -name '*.class' | xargs rm
$ javac org/contactcompany/application/list/ContactList.java

If it compiles (and compiles a few classes in the api packages too), you know that you got the package declarations and the import statement right. If not, raise your hand and get a supervisor to help you fix this quickly, so that we can move on in the workshop!

We decided to create the org.contactcompany.api package to show you how you could separate code which are related to modelling stuff in your application from the actual application GUI code. By putting everything from the old address book assignment in its own package, this becomes clear, we hope. The only exception is the ContactList which is so called "client code" to the SimpleMutableList and Contact from the api package. (Client code means code that uses other code).

The last little thing to do, is to add a method to ContactList, public List<Contact>entries(), which returns the entire list of Contact objects. This is needed by the GUI, when the GUI needs to list the contacts and search for contacts etc.

public List<Contact> entries() {
  return entries;
}

Phase four - Starting on the GUI

We suggest that we start with the main class, which should start the application, since this is the class we will run as a program. Starting with the main class and main method, makes it easy to see what we need and to test the things we write from now on.

Create the class org.contactcompany.application.main.AddressBook (of course in org/contactcompany/application/main/AddressBook.java) with the following contents:

package org.contactcompany.application.main;
import  org.contactcompany.application.ui.MenuWithContactList;
import  org.contactcompany.application.ui.MainWindow;

public class AddressBook{
  public static void main(String[] args){
    String ui = System.getProperty("ui");
    // Default: Run CLI (command line interface)
    if ( ui == null ||
         ui.equals("cli")){
      new MenuWithContactList().start();
    } else if (ui.equals("gui")) {
      try{
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
              new MainWindow().show();
            }
          });
        //new MenuWithContactList().start();
      }catch(Exception e){
        e.printStackTrace();
        System.err.println("Fatal error. Please see logfile...bla");
      }
    }
  }
}

Compile it to see what classes are missing!

$ javac org/contactcompany/application/main/AddressBook.java 
org/contactcompany/application/main/AddressBook.java:2: error: package org.contactcompany.application.ui does not exist
import  org.contactcompany.application.ui.MenuWithContactList;
                                         ^
org/contactcompany/application/main/AddressBook.java:3: error: package org.contactcompany.application.ui does not exist
import  org.contactcompany.application.ui.MainWindow;
                                         ^
org/contactcompany/application/main/AddressBook.java:11: error: cannot find symbol
      new MenuWithContactList().start();
          ^
  symbol:   class MenuWithContactList
  location: class AddressBook
org/contactcompany/application/main/AddressBook.java:16: error: cannot find symbol
              new MainWindow().show();
                  ^
  symbol: class MainWindow
4 errors

The errors are because the compiler can't find the two classes org.contactcompany.application.ui.MenuWithContactList and org.contactcompany.application.ui.MainWindow. So let's create them!

The full names of the classes tell you where to create the classes. Remember, a package is the same as a relative path to a class.

Let's start with MenuWithContactList, which is actually not a GUI class, but a class which uses your old assignment's command line menu system. So create the class org.contactcompany.application.ui.MenuWithContactList in org/contactcompany/application/ui/MenuWithContactList.java with the following contents:

package org.contactcompany.application.ui;

import org.contactcompany.api.container.SimpleMutableList;
import org.contactcompany.api.entry.Contact;
import org.contactcompany.api.ui.textmenu.Menu;
import org.contactcompany.api.ui.textmenu.MenuAction;
import org.contactcompany.application.list.ContactList;
import static org.contactcompany.api.util.TextUtils.askFor;
import java.util.Iterator;

public class MenuWithContactList {
  private SimpleMutableList<Contact> list;
  private Menu menu = new Menu("Address book");

  /**
   * Constructs a new MenuWithContactList
   * with a fresh address book (ContactList) loaded from file
   * if the file exists.
   */
  public MenuWithContactList() {
    list = new ContactList();
    list.load();
    System.out.println(list.numberOfEntries() +
                       " items loaded from file.");
  }

  private void createMenu() {
    /* Add a menu item for listing all contacts */
    menu.addMenuItem("List", new MenuAction() {
        public void onItemSelected() {
          list.listEntries();
        }
      });

    /* Add a menu item for adding a contact */
    menu.addMenuItem("Add", new MenuAction() {
        public void onItemSelected() {
          String name = askFor("Name: ");
          String email = askFor("Email: ");
          String phone = askFor("Phone: ");
          
          // Only accept real names!
          while ("".equals(name)) {
            System.out.println("Name cannot be empty");
            name = askFor("Name: ");
          }

          list.addEntry(new Contact(name, email, phone));
          list.save();
        }
      });
  }

  /**
   * Starts the application by setting up and starting the menu system.
   */
  public void start() {
    createMenu();
    menu.start();
  }
}

Compile the main class again and verify that you now have fewer errors:

$ javac org/contactcompany/application/main/AddressBook.java org/contactcompany/application/main/AddressBook.java:3: error: cannot find symbol
import  org.contactcompany.application.ui.MainWindow;
                                         ^
  symbol:   class MainWindow
  location: package org.contactcompany.application.ui
org/contactcompany/application/main/AddressBook.java:16: error: cannot find symbol
              new MainWindow().show();
                  ^
  symbol: class MainWindow
2 errors

That's good! Now we only have to write the MainWindow class, right? With some gui stuff!

But why did we include a command line version of the address book? We did so to show you that the application is using the api (classes) from the old address book assignment. It can now use the command line user interface from the api, or add its own, graphical user interface. The api from the address book can be used both with the included command line user interface or with some custom interface which you can write yourself. Your custom GUI will make use of the API via its version of the ContactList.java which implements the interface SimpleMutableList from the address book API.

Phase five - Writing the first version of the MainWindow

Now it is time to write the MainWindow class (which is imported, instantiated and has its show() method invoked by the main method of our application.

The main method investigate how it should run the application by looking at the system property ui. If the property is missing or set to cli, the main method will run the command line user interface. If it is set to gui, it will run the graphical user interface (MainWindow which we will start on now). This should make it clear that the appication can use the classes in the address book API both for a command line user interface and a graphical user interface. You have re-used the API for two different implementations!

But, back to the project. Create the class org.contactcompany.application.ui.MainWindow in org/contactcompany/application/ui/MainWindow.java. Start with the package declaration and some imports:

package org.contactcompany.application.ui;
import org.contactcompany.api.entry.Contact;
import org.contactcompany.application.list.ContactList;

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;

public class MainWindow {

}

Now, add a no-arguments constructor which does nothing in its block (for now), and a show method:

  public MainWindow() {
    // nothing yet
  }

  public void show() {
    // nothing yet
  }

Now, you should be able to compile the main class (which will compile also MainWindow):

$ find . -name '*.class' | xargs rm -f && javac org/contactcompany/application/main/AddressBook.java

Deleting all class files before compiling is a way to convince yourself that what is needed will be compiled, and no old compiled files will lay around and be used.

Now you can run the main class without any arguments and verify that the command line version of the program works:

$ java org.contactcompany.application.main.AddressBook 
4 items loaded from file.

====Address book====

0 List
1 Add
2 quit
Please enter a number from the menu: 2

Bye!

(I had an old address book file with four contacts when I did this!)

Phase six - Deciding what components to use and the layout

We're going to stick to the layout shown in our video lecture in this chapter: Introduction to Swing - A GUI for the Address book assignment.

We'll use the presentation introduction to Swing in this phase for planning the GUI layout.

The outcome from the presentation will be a list of components to add to our MainWindow class.

Here are some ASCII art of the layout plan:

Main window with two menus:
+------------------------+
|File | Contacts         |
+------------------------+
|\Quit  \Add contact     |
|       \List contact    |
|       \Search          |
+------------------------+

Add contact view
+------------------------+
|Name:  ________________ |
|Email: ________________ |
|Phone: ________________ |
|+------+ +------+       |
||cancel| | add  |       |
|+------+ +------+       |
+------------------------+

List contacts view (a list of contacts)
+---------------------------+
|Bengt bengan@email 031123  |
|Johan johan@email 081234   |
|Zack zack@email 1234567    |
|                           |
|                           |
|                           |
|                           |
+---------------------------+

Search view                  Search results (text area)
+------------------------+  +--------------------------+
|Name: Bengt___________  |  |Bengt bengan@email 031123 |
|+------+ +------+       |  +--------------------------+
||cancel| |search|       |
|+------+ +------+       |
+------------------------+

After the presentation, we'll add the following instance variables to our class:

 private ContactList contacts; // (accessing contacts from address book file)

 private JFrame frame; // main window
 private JMenuBar menuBar;
 private JMenu contact;
 private JMenu file;
 private JMenuItem add;
 private JMenuItem search;
 private JMenuItem list;
 private JMenuItem quit;
 private JList<Contact> contactList; // List of all contacts
 private DefaultListModel<Contact> resultsListModel; // the actual contacts in the list
 private JScrollPane contactsSP; // Allow the list to scroll
 private JPanel addPanel; // Area for the add new contact components
 private JPanel listPanel; // contains the scrollpane with the list
 private JPanel searchPanel; // Area for the search components
 private JPanel resultsPanel;// Area for the search results’ textarea
 private JTextArea resultsTA;// a textcomponent with the search results
 private JLabel statusLabel; // shows a text message ("3 contacts loaded from file")
 private JLabel nameLabel; // The label "Name: " for add contact
 private JLabel emailLabel; // for add contact
 private JLabel phoneLabel; // for add contact
 private JTextField nameTF; // for add contact
 private JTextField emailTF; // for add contact
 private JTextField phoneTF; // for add contact
 private JButton addButton; // for add contact
 private JButton cancelButton; // for add contact
 private JLabel searchNameLabel; // for search
 private JTextField searchNameTF; // for search
 private JButton cancelSearchButton;// for search
 private JButton searchButton; // for search

Phase seven - Finish the constructor and create stubs for the private methods

Now, when we have components figured out, let's finish the constructor and from it, call some new private methods. Let's write stubs (skeletons) for those new methods too, while we're at it, so that we can compile our code. Compiling often is a virtue and very rewarding. We want to catch mistakes and errors early, so the more often we compile, the quicker we'll find our mistakes and can fix them.

In the constructor, do the following:

  • call initComponents(); // doesn't yet exist!
  • call layoutComponents(); // doesn't yet exist!
  • call frame.pack(); // doesn't yet exist!

Then, in order for us to be able to compile, create initComponents() as a private void method which doesn't do anything and the same for layoutComponents().

Since they are called from the constructor, they must exist for us to be able to compile, but they don't have to do anything yet.

Remove all classes and compile main again:

$ find . -name '*.class' | xargs rm -f && javac org/contactcompany/application/main/AddressBook.java

Phase eight - Write initComponents()

Here we'll initialize all components (instance variables and components). We'll start with the frame and finish the show() method, so that we can test the main class to see if it opens the gui version. Then we'll initialize the rest.

In the initComponents() body, initialize the frame instance variable to a new JFrame with a String argument for the title of the window, like "Address book" or something, set the layout to a new BorderLayout and finally set the default close operation to JFrame.EXIT_ON_CLOSE.

Remember the method, public void show() from above? Let's finish the code for it! In the show() method, first call setSize(800,800) and then call setVisible(true) on the frame.

Delete old class files, compile and run the main class with the Java flag -Dui=gui to see if the main creates and shows an empty window for you.

$ find . -name '*.class' | xargs rm
$ javac org/contactcompany/application/main/AddressBook.java && \
java -Dui=gui org.contactcompany.application.main.AddressBook

Expand using link to the right to see a suggestion for the Java code

The below was hidden until the user clicked "Expand":

  private void initComponents() {
    frame = new JFrame("Contact book");
    frame.setLayout(new BorderLayout());
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }

  public void show() {
    frame.setSize(800,800);
    frame.setVisible(true);
  }

Ok, now we know that it works. Let's initialize all components. This will take a while to go through, so we'll just put the code here and explain it during the workshop.

    contacts = new ContactList(); // This is the programming interface to the address book
    contacts.load(); // Load old contacts, if any
    // Initialize the status label and set the text to the result of loading contacts
    statusLabel = new JLabel(contacts.numberOfEntries() + " contacts loaded from file");
    frame = new JFrame("Contact book"); // the main window
    frame.setLayout(new BorderLayout()); // the main window's layout manager
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // close it when user wants to
    listPanel = new JPanel(); // A panel for listing contacts
    initMenus(); // call initMenus (we'll discuss this method soon, it doesn't exist yet!)
    resultsListModel = new DefaultListModel<Contact>(); // A model for our list of contacts
    contactList = new JList<Contact>(resultsListModel); // the swing-list of contacts
    contactList.setVisibleRowCount(20); // 20 contacts visible at the time
    contactsSP = new JScrollPane(contactList); // We should be able to scroll the list
    initAddPanel(); // call this method, it doesn't exist yet, so we'll write it soon!
    initSearchPanel(); // call this method, it doesn't exist yet, so we'll write it soon!

Now, as you see, in the full listing for initComponents, three new methods are called. What are they?

Compile:

$ javac org/contactcompany/application/main/AddressBook.java
./org/contactcompany/application/ui/MainWindow.java:69: error: cannot find symbol
    initMenus();
    ^
  symbol:   method initMenus()
  location: class MainWindow
./org/contactcompany/application/ui/MainWindow.java:74: error: cannot find symbol
    initAddPanel();
    ^
  symbol:   method initAddPanel()
  location: class MainWindow
./org/contactcompany/application/ui/MainWindow.java:75: error: cannot find symbol
    initSearchPanel();
    ^
  symbol:   method initSearchPanel()
  location: class MainWindow
3 errors

That's to be expected! We haven't written those methods yet! Here they are:

  private void initMenus() {
    menuBar = new JMenuBar();
    contact = new JMenu("Contacts");
    file = new JMenu("File");
    add = new JMenuItem("Add contact");
    list = new JMenuItem("List contacts");
    quit = new JMenuItem("Quit");
    quit.addActionListener(new QuitListener());
    search = new JMenuItem("Search");
  }

  private void initAddPanel() {
    addPanel = new JPanel();
    GroupLayout layout = new GroupLayout(addPanel);
    layout.setAutoCreateGaps(true);
    layout.setAutoCreateContainerGaps(true);
    addPanel.setLayout(layout);
    nameLabel = new JLabel("Name:");
    emailLabel = new JLabel("Email:");
    phoneLabel = new JLabel("Phone:");
    nameTF = new JTextField(20);
    emailTF = new JTextField(20);
    phoneTF = new JTextField(20);
    cancelButton = new JButton("Cancel");
    addButton = new JButton("Add");
  }

  private void initSearchPanel() {
    searchButton = new JButton("Search");
    cancelSearchButton = new JButton("Cancel");
    searchPanel = new JPanel();
    searchPanel.setLayout(new GridLayout(2,2));
    resultsPanel = new JPanel();
    resultsTA = new JTextArea();
    searchNameLabel = new JLabel("Name:");
    searchNameTF = new JTextField(20);
    resultsPanel.add(resultsTA);
  }

Compile again:

$ javac org/contactcompany/application/main/AddressBook.java
./org/contactcompany/application/ui/MainWindow.java:85: error: cannot find symbol
    quit.addActionListener(new QuitListener());
                               ^
  symbol:   class QuitListener
  location: class MainWindow
1 error

Now, the compiler complains about adding a "QuitListener" to the quit menu item. Don't worry. We'll have to write that class!

Write the following as an inner class (an inner class is a member of a class just like an instance variable or instance method):

  private class QuitListener extends WindowAdapter implements ActionListener{
    public void actionPerformed(ActionEvent ae) {
      frame.dispose();
    }
    
    public void WindowClosing(WindowEvent we) {
      frame.dispose();
    }
  }

Compile again and confirm that it works to compile now.

So, now we have all the components ready to be laid out in the frame. That's the next phase!

Phase nine - Write layoutComponents() and addListeners()

It's time to do the layout - what component goes into what container?

The code for layoutComponents() is quite long. We'll explain it during the workshop.

  private void layoutComponents() {
    listPanel.add(contactsSP);
    file.add(quit);
    menuBar.add(file);
    menuBar.add(contact);
    frame.add(menuBar, BorderLayout.NORTH);
    frame.add(statusLabel, BorderLayout.SOUTH);
    addListeners();
    GroupLayout layout = (GroupLayout)addPanel.getLayout();
    layout
      .setHorizontalGroup(layout.createSequentialGroup()
                          .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                                    .addComponent(nameLabel)
                                    .addComponent(emailLabel)
                                    .addComponent(phoneLabel)
                                    .addComponent(cancelButton))
                          .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                                    .addComponent(nameTF)
                                    .addComponent(emailTF)
                                    .addComponent(phoneTF)
                                    .addComponent(addButton))
                          );
    layout
      .setVerticalGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                                  .addComponent(nameLabel)
                                  .addComponent(nameTF))
                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                                  .addComponent(emailLabel)
                                  .addComponent(emailTF))
                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                                  .addComponent(phoneLabel)
                                  .addComponent(phoneTF))
                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                                  .addComponent(cancelButton)
                                  .addComponent(addButton))
                        );
    layout.linkSize(SwingConstants.HORIZONTAL, cancelButton, addButton);
    searchPanel.add(searchNameLabel);searchPanel.add(searchNameTF);
    searchPanel.add(cancelSearchButton);searchPanel.add(searchButton);
    contact.add(add);
    contact.add(list);
    contact.add(search);
  }

Here's the planned layout for the add view again:

Add contact view
+------------------------+
|Name:  ________________ |
|Email: ________________ |
|Phone: ________________ |
|+------+ +------+       |
||cancel| | add  |       |
|+------+ +------+       |
+------------------------+
The Add Contact view

The GroupLayout aligns the labels and textfields horizontally (labels start at the same column, textviews start at the same column, and vertically first name and name text field, then email and email text field, then phone and phone text field, then cancel button and add button. After this, it naked cancel button and add button the same size.

The layoutComponents() method calls addListeners(), so we'll have to write that method too!

We need listeners for the following components:

  • the list menu item
    • we'll create a method for this, showList()
  • the add menu item
    • we'll call a method for this, setCenter(addPanel)
  • the addButton of the add new contact view
    • save the contact and showList()
  • the cancelButton of the add new contact view
    • call showList()
  • the cancelSearchButton of the search view
    • call showList()
  • the search menu item
    • we'll call a method for this, showSearch()
  • the searchButton of the search view
    • clear the contents of resultsTA
    • loop through the contacts and if there's a match, add it to resultsTA
    • setCenter(resultsTA);
  private void addListeners() {
    list.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
          showList();
        }
      });
    add.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
          setCenter(addPanel);
        }
        
      });
    addButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
          Contact c = new Contact(nameTF.getText(), emailTF.getText(), phoneTF.getText());
          contacts.addEntry(c);
          contacts.save();
          statusLabel.setText(contacts.numberOfEntries()+" contacts loaded from file");
          showList();
        }
      });

    cancelButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
          showList();
        }
      });
    cancelSearchButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
          showList();
        }
      });
    search.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
          showSearch();
        }
      });
    searchButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
          resultsTA.setText("");
          for(Contact c : contacts.entries()) {
            if(c.name().toLowerCase().startsWith(searchNameTF.getText().toLowerCase())){
              resultsTA.append(c.toString());resultsTA.append("\n");
            }
          }
          setCenter(resultsTA);          
        }
      });
  }

The helper methods called from the listeners are listed here:

  private void showSearch() {
    setCenter(searchPanel);
  }
  
  private void setCenter(Component c) {
    frame.remove(addPanel);
    frame.remove(searchPanel);
    frame.remove(contactsSP);
    frame.remove(resultsTA);
    frame.getContentPane().add(c, BorderLayout.CENTER);
    frame.validate();
    frame.pack();
  }
  
  private void showList() {
       ((DefaultListModel<Contact>)contactList.getModel()).removeAllElements();
       Collections.sort(contacts.entries(), new Comparator<Contact>(){
           public int compare(Contact a, Contact b){
             return a.name().toLowerCase().compareTo(b.name().toLowerCase());
           }
         });
          for(Contact c : contacts.entries()){
            ((DefaultListModel<Contact>)contactList.getModel()).addElement(c);
          }
          setCenter(contactsSP);
  }

Phase ten - test run!

Now it is time to run the program and test that it works! Run it in both command line mode and gui mode. Add contacts, search for names. Have fun!

Phase eleven - Conclusions and questions and answers

  • What have we learned?
  • Your questions?

Some points to get the discussion started:

  • The teachers seem to like methods, and methods calling methods
    • Your opinions on this?
  • The GroupLayout syntax looks complicated but the result looks kind of good
    • Your opinions on this?
  • The application re-uses the CLI (command line interface) from the address book "api" you wrote/used in the old assignment
    • Did you see that code could be re-used?
    • Did you see that the same address book classes (Contact, etc) could be used both from a command line interface and a GUI?
  • Did you understand how the main method decided whether to fire up the CLI or the GUI?
    • What other means could we use to make this decision?
    • We can show you emacs versus emacs -nw
  • Did you think about the fact that the CLI version uses the terminal for interaction, while the GUI "locks" the terminal and shows a window - the terminal remains blocked until you close the window!
    • What about running a servlet container such as Winstone - same happens - the terminal is occupied with running Winstone
    • How would you write a small Java program which blocks the terminal until you finish the application forcefully?
  • Switching from e.g. the list of contacts view, to the add contact view - how was that done?
    • There are probably other ways to do this - use google or a book on Swing :)
  • The total amount of lines of code was
    • 850 all in all (on the teachers computer)
    • 380 for the org.contactcompany.api
    • 470 for the org.contactcompany.application
    • 255 for the MainWindow class
  • Is this a big application (in terms of lines of code)?
    • How many lines of code is there in Firefox?

Expand using link to the right to see the answer to the question on lines of code for firefox.

Answer: 35 812 867 lines of code (source: https://www.openhub.net/p/firefox/analyses/latest/languages_summary)

Source code for the application written in this workshop can be found at https://github.com/progund/java-extra-lectures/tree/master/swing/addressbook if you don't know how to clone the whole git repo java-extra-lectures, then remember to download either the whole repo as a big zipfile, or download from the source code's "raw" links (so that you don't download the HTML pages from github).