-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathjava-swing.Rmd
84 lines (49 loc) · 9.4 KB
/
java-swing.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# Java Swing Framework {#java-swing}
Android applications are user-driven graphical applications. In order to become familiar with some of the _coding patterns_ involved in this kind of software, it can be useful to consider how to build simple graphical applications in Java using a different GUI framwork: the [Swing library](https://docs.oracle.com/javase/tutorial/uiswing/start/).
<p class="alert alert-info">This appendix references code found at <https://github.com/info448/appendix-java-swing>. Note that this tutorial involves Java Programming: you can either do this in Android Studio, or just using a light-weight text editor such as [Visual Studio Code](https://code.visualstudio.com/) or [Sublime Text](https://www.sublimetext.com/).</p>
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`](https://docs.oracle.com/javase/8/docs/api/javax/swing/package-summary.html) package. They also rely on the [`java.awt`](https://docs.oracle.com/javase/8/docs/api/java/awt/package-summary.html) 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`](https://docs.oracle.com/javase/8/docs/api/javax/swing/JFrame.html). `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`](https://docs.oracle.com/javase/8/docs/api/javax/swing/JButton.html), 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!
## 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`](https://docs.oracle.com/javase/8/docs/api/java/util/EventObject.html) type) that are created by the emitter. A `JButton` in particular emits [`ActionEvents`](https://docs.oracle.com/javase/8/docs/api/java/awt/event/ActionEvent.html) 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`](https://docs.oracle.com/javase/8/docs/api/java/awt/event/ActionListener.html). This type has an `actionPerformed()` method that can be called in response to an event.
We use the [Observer Pattern](https://sourcemaking.com/design_patterns/observer) 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**](https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html)? 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:
```java
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.
## Layouts and Composites `r #[end by 11:10]`
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**](https://docs.oracle.com/javase/8/docs/api/java/awt/LayoutManager.html), 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.
```java
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](http://www.oodesign.com/composite-pattern.html). 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.