GUIs
Most modern comsumer applications are written with Graphical User Interfaces. Command-line programs are still useful, but they are mostly used by software engineers, network managers, and other computer-science professionals. You need to learn about GUI programming if you plan to write code for anybody outside this group.
This is not a course in GUI programming. However, it is a course in Object Oriented Programming. GUI libraries happen to use a lot of object-oriented code. Even a simple GUI program can give you a lot of practice with OOP.
It is possible to sink a lot of time into the GUI elements of a program. Knowing how to write code is not enough - the purpose of a GUI is to simplify communications between a user and the program. If you do a poor job of designing your interface, the program will be difficult to use whether it has a GUI or not. GUI design is one of the more painful but rewarding skills in CS. Many books have been written on the subject, with the summary being: try something, get feedback from the users, modify, repeat until the user (and you) are happy.
|
NOTE - This sort of feedback from users is an important part of HCI (Human-Computer Interaction). HCI involves the study and design of the interaction between people and computers. It is often described as the intersection of Computer Science, Behavioral Sciences, and Design. We all know a bad interface when we see it, but many people are surprised to find how difficult it can be to design a good one. |
It will not surprise you to hear that Java provides a great number of extremely useful classes to help you with this, which can be found in the java.awt package and the javax.swing packages.
AWT vs. Swing
There are two main GUI packages for Java. They are provide similar functionality. You can mix and match both in the same program. Here is a high-level overview:
AWT (Abstract Window Toolkit) is a connection between Java and your operating system's native GUI libraries. This toolkit uses the window-drawing capabilities that already exist in your OS, whether you are on Windows, Linux, iOS, etc. This toolkit tends to have a look and feel that is more native to your OS. So the same AWT program running on Windows or iOS will have different buttons, fonts, etc.
Swing is a GUI toolkit that is implemented entirely within Java. This makes it highly portable. The programs will look the same on every OS. They have a distinct "Java" look and feel rather than looking like other programs on your OS.
First Java GUI Program
The basic workflow of GUI development is:
- Make a background window
- Put Components into the window
- Add Listeners to the Components
Most Swing Objects start with a capital “J”. The basic background window in Swing is a JFrame. The basic background window in AWT is a Frame. Both provide the basic window components seen in the image to the right.
- Note that javax.swing.JFrame inherits from java.awt.Frame.
- Both inherit from java.awt.Component.
Component: A component is an object having a graphical representation that can be displayed on the screen and that can interact with the user.
Container: A generic Abstract Window Toolkit(AWT) container object is a component that can contain other AWT components.
Note that java.swt.Container inherits from java.awt.Component.
So, decide what you want in your GUI, find the Components that correspond, add them to Containers, and add Containers to a basic background window.
So, here's our first Swing code!
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* @author Billy
*
*/
public class BasicSwingFrame {
/**
* @param args
*/
public static void main(String[] args) {
JButton button=new JButton("I am a button!");
JPanel myPanel=new JPanel(new BorderLayout());
myPanel.add(button, BorderLayout.NORTH);
JFrame window = new JFrame("My First Swing Window!");
window.add(myPanel);
window.setSize(400, 200);
window.setVisible(true);
}
}
Now, some explanation. The constructor containing the string:
JFrame window = new JFrame("My First Swing Window!");
sets the header bar of the window to whatever you want. The setSize() function sets the dimensions of the window. Finally, to display, we set the visibility of the window to true.
To add something to the window, we need to put in a new JPanel in the JFrame. A JFrame is a container, so it can hold components (as well as be one).
Two things to mention here:
- To place a component into a container, you use the add function window.add(myPanel);
- The argument in the JPanel constructor (new BorderLayout()) in the JPanel constructor tells Swing how to arrange components in the container. This is specific to a JPanel and all its children, and can be set at construct time. There are many different layout managers that can be put in here- including those you create- BorderLayout is just among the simplest to use.
As a general rule of practice, this is how GUI’s are developed. You start with the Frame, put in a Container like a panel, then start adding other components (like Buttons, JPanels, etc) to that.
To give us something to see- we added a JButton. As you can imagine, this gives us a clickable component that we can see on the panel. To add components to any container, you simply use the add function.
A BorderLayout divides the Container into five regions: NORTH, SOUTH, EAST, WEST, and CENTER. Each region can hold one Component- either a JButton or other simple component, or another Container- holding multiple components. Let’s add another component: a JTextField.
JTextField textField=new JTextField("I contain text!");
myPanel.add(textField,BorderLayout.WEST);
And, for fun, lets add a JComboBox
String[] selections=new String[]{"One","Two","Three","Four","Lots"};
JComboBox comboBox=new JComboBox(selections);
myPanel.add(comboBox, BorderLayout.CENTER);
As expected, a ComboBox is more difficult to construct, needing a list of Objects to display. But the result fits the pattern: create the component, add it to the container...
There are many more components you can add- and you can create your own. I invite you to explore resources to find these.
Action Listeners
Putting components on the window is only the beginning, however. We want our GUI’s to do useful work- not just look pretty. Useful work in this case is obeying the user’s commands: when they click a button, we want something to happen. When we select something from a ComboBox, we want something to happen. When they press a key, we want something to happen. Essentially, we want the GUI to LISTEN for user input, and react according to important inputs- like button clicks, etc.
Every Component has a series of Listeners that can be active on it. One of the more useful is the MouseListener. As an example, let's consider our JButton button. We want to change the text on the button when it is clicked. button has a method called addMouseListener(MouseListener m).
Listeners are interfaces, and Java has provided for us the abstract class MouseAdapter that does much of the work of implementing this Listener. Now, MouseAdapter has a number of different methods; take a look. One of them is mouseClicked. If a mouse is clicked onto our button, we want the button to be changed. So we create a class that extends MouseAdapter, and overwrite mouseClicked:
public class MyMouseAdapter extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
((JButton)e.getComponent()).setText("You Clicked Me!");
}
}
We can then add to our code
button.addMouseListener(new MyMouseAdapter());
and clicking the button would result in that mouseClicked code being run, changing the text on the button.
But this seems very silly! We're making a whole class (MyMouseAdapter) to override exactly one method and be used exactly once. Surely there's a way to do this without doing all of this! The answer is with anonymous classes:
button.addMouseListener(
new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
((JButton)e.getComponent()).setText("You Clicked Me!");
}
}
);
Whoa! This makes a class, with no name, that overwrites mouseClicked, to be used once, of type MouseAdapter. And down the Object-Oriented rabbit hole we go! To write this Anonymous Inner Class, we first declare a new MouseAdapter(). Right after the new, we start the class bracket: anything here will be part of this anonymous class- and make no mistake, this is a class. We can put other methods and fields in there if we want... for example:
button.addMouseListener(new MouseAdapter() {
String[] answers={"You Clicked Me!",
"You Clicked me again!!",
"That Makes 3 Times",
"Don't you have anything better to do?"};
int clickCount=0;
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
((JButton)e.getComponent()).setText(answers[clickCount]);
if (clickCount<answers.length-1)
clickCount++;
else
clickCount=0;
}
});
And you can see snarkiness in action.
Often you use button actions to grab data from other components in the form. This functionality would still go in the mouseClicked function of the listener, but you would have to have some way of getting the data from the components.
Components all have functions to get the contents back from them. Each Component has its own function to do this. For example, our JComboBox has the function getSelectedItem() which returns the Object that was selected, and the JTextField has the function getText. The issue: scoping. In order to see the components from inside one of these Anonymous listeners, you have to declare the components as final. Like so:
final JTextField textField=new JTextField("I contain text!");
myPanel.add(textField,BorderLayout.WEST);
String[] selections=new String[]{"One","Two","Three","Four","Lots"};
final JComboBox comboBox=new JComboBox(selections);
myPanel.add(comboBox, BorderLayout.CENTER);
button.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
String text=textField.getText();
String combo=(String)comboBox.getSelectedItem();
System.out.println(text+" "+combo);
}
});
Here I just print out the values. If I wanted to use them somewhere else, I would create a second class to handle this input, declare it final, then just call whatever function I wanted to use here.
This is your quick introduction to GUIs. This is a very BASIC introduction: there is much more than can be covered. The hope here is you can take this knowledge and expand on it. I invite you to do so: play around with the different layouts, use different components, make listeners not just anonymously, but extend the listeners and create your own listener classes. Java Swing GUI libraries are very rich in functionality: you should explore them as much as you can.