B Swing Framework

Android applications are user-driven graphical applications. In order to become familiar with some of the coding patterns involved in this kind of software (without the overhead of the Android framework), let’s consider how to build simple graphical applications in Java using the Swing library

This appendix references code found at https://github.com/info448-s17/lecture02-activities, in the java/ folder. Note that this tutorial involves Java Programming: while it is possible to do this in Android Studio, it’s often easier to just utilize a light-weight text editor such as Visual Studio Code or Sublime Text.

The Swing library is a set of Java classes used to specify graphical user interfaces (GUIs). These classes can be found in the javax.swing package. They also rely on the java.awt package (the “Advanced Windowing Toolkit”), which is an older GUI library that Swing builds on top of.

  • Fun fact: Swing library is named after the dance style: the developers wanted to name it after something hip and cool and popular. In the mid-90s.

Let’s look at an incredibly basic GUI class: MyGUI found in the src/main/java/ folder. The class subclasses (extends) JFrame. JFrame represents a “window” in your operating system, and does all the work of making that window show up and interact with the operating system in a normal way. By subclassing JFrame, we get that functionality for free! This is how we build all GUI applications using this framework.

Most of the work defining a Swing GUI happens in the JFrame constructor (called when the GUI is “created”).

  1. We first call the parent constructor (passing in the title for the window), and then call a method to specify what happens when we hit the “close” button.

  2. We then instantiate a JButton, which is a class representing a Java Button. Note that JButton is the Swing version of a button, building off of the older java.awt.Button class.

  3. We then .add() this button to the JFrame. This puts the button inside the window. This process is similar to using jQuery to add an HTML element to web page.

  4. Finally, we call .pack() to tell the Frame to resize itself to fit the contents, and then .setVisible() to make it actually appear.

  5. We run this program from main by just instantiating our specialized JFrame, which will contain the button.

You can compile and run this program with ./gradlew -q run. And voila, we have a basic button app!

B.1 Events

If we click the button… nothing happens. Let’s make it print out a message when clicked. We can do this through event-based programming (if you remember handling click events from JavaScript, this is the same idea).

Most computer systems see interactions with its GUI as a series of events: the event of clicking a button, the event of moving the mouse, the event of closing a window, etc. Each thing you interact with generates and emits these events. So when you click on a button, it creates and emits an “I was clicked!” event. (You can think of this like the button shouting “Hey hey! I was pressed!”) We can write code to respond to this shouting to have our application do something when the button is clicked.

Events, like everything else in Java, are Objects (of the EventObject type) that are created by the emitter. A JButton in particular emits ActionEvents when pressed (the “action” being that it was pressed). In other words, when buttons are pressed, they shout out ActionEvents.

In order to respond to this shouting, we need to “listen” for these events. Then whenever we hear that there is an event happening, we can react to it. This is like a person manning a submarine radar, or hooking up a baby monitor, or following someone on Twitter.

But this is Java, and everything in Java is based on Objects, we need an object to listen for these events: a “listener” if you will. Luckily, Java provides a type that can listen for ActionEvents: ActionListener. This type has an actionPerformed() method that can be called in response to an event.

We use the Observer Pattern to connect this listener object to the button (button.addActionListener(listener)). This registers the listener, so that the Button knows who to shout at when something happens. (Again, like following someone on Twitter). When the button is pressed, it will go to any listeners registered with it and call their actionPerformed() methods, passing in the ActionEvent it generated.

But look carefully: ActionListener is not a concrete class, but an abstract interface. This means if we want to make an ActionListener object, we need to create a class that implements this interface (and provides the actionPerformed() method that can be called when the event occurs). There are a few ways we can do this:

  1. We already have a class we’re developing: MyGUI! So we can just make that class implement ActionListener. We’ll fill in the provided method, and then specify that this object is the listener, and voila.

    • This is my favorite way to create listeners in Java (since it keeps everything self-contained: the JFrame handles the events its buttons produce).

    • We’ll utilize a variant of this pattern in Android: we’ll make classes implement listeners, and then “register” that listener somewhere else in the code (often in a nested class).

  2. But what if we want to reuse our listener across different classes, but don’t want to have to create a new MyGUI object to listen for a button to be clicked? We can instead use an inner or nested class. For example, create a nested class MyActionListener that implements the interface, and then just instantiate one of those to register with the button.

    • This could be a static nested class, but then it wouldn’t be able to access instance variables (because it belongs to the class, not the object). So you might want to make it an inner class instead. Of course then you can’t re-use it elsewhere without making the MyGUI (whose instance variables it referenes anyway)… but at least we’ve organized the functionality.
  3. It seems sort of silly to create a whole new MyActionListener class that has one method and is just going to be instantiated once. So what if instead of giving it a name, we just made it an anonymous class? This is similar to how you’ve made anonymous variables by instantiating objects without assigning them to named variables, you’re just doing the same thing with a class that just implements an interface. The syntax looks like:

    button.addActionListener(new ActionListener() {
        //class definition (including methods to override) goes in here!
          public void actionPerformed(ActionEvent event) {
                //...
          }
    });

    This is how buttons are often used in Android: we’ll create an anonymous listener object to respond to the event that occurs when they are pressed.

B.2 Layouts and Composites

What if we want to add a second button? If we try to just .add() another button… it replaces the one we previously had! This is because Java doesn’t know where to put the second button. Below? Above? Left? Right?

In order to have the JFrame contain multiple components, we need to specify a layout, which knows how to organize items that are added to the Frame. We do this with the .setLayout() method. For example, we can give the frame a BoxLayout() with a PAGE_AXIS orientation to have it lay out the buttons in a vertical row.

container.setLayout(new BoxLayout(container, BoxLayout.PAGE_AXIS));
container.add(theButton);
container.add(otherButton);
  • Java has different LayoutManagers that each have their own way of organizing components. We’ll see this same idea in Android.

What if we want to do more complex layouts? We could look for a more complex LayoutManager, but we can actually achieve a lot of flexibility simply by using multiple containers.

For example, we can make a JPanel object, which is basically an “empty” component. We can then add multiple buttons to this this panel, and add that panel to the JFrame. Because JPanel is a Component (just like JButton is), we can use the JPanel exactly as we used the JButton—this panel just happens to have multiple buttons.

And since we can put any Component in a JPanel, and JPanel is itself a Component… we can create nest these components together into a tree in an example of the Composite Pattern. This allows us to create very complex user interfaces with just a simple BoxLayout!

  • This is similar to how we can create complex web layouts just by nesting lots of <div> elements.