Welcome to GUIs

One of the most important things about Java is that it allows the user to interact with your applications. Since Java applets are by nature graphical, GUIs are very important in writing applets. They are also important for stand-alone applications. GUI stands for Graphical User Interface. It is the part of the program that the user sees and interacts with. Java provides a vast array of predefined GUI objects, such as buttons, text fields, menus, windows, and more. In this lesson, we're going to look at how to use these GUI objects, we'll take a look at layouts, and we'll learn about handling events.

First let's see what GUI components are available.

NameWhat it is
MenuBarThe menu bar at the top of the window
MenuA menu on the menu bar, such as File or Edit
ButtonA clickable button
ListA list of selectable choices
ChoiceA popup menu of choices
CheckboxGroupA group of checkboxes acting together
CheckboxA checkbox like in HTML
TextFieldA single-line text blank; can be set to be uneditable
TextAreaA multi-line TextField; can be set to be uneditable

There are a couple of other GUI components, but they more advanced, and less useful. The ones listed in the table are the ones you'll be using the vast majority of the time. For an example of what the components look like, click on the button below. To make the example go away again, click on the button a second time.

Now let's look at the code behind that example.

import java.awt.*; import java.applet.*; public class AllComponentsFrame extends Frame { protected Panel panel1, panel2, panel3, panel4, panel5; protected CheckboxGroup group; protected List list; protected Choice choice1, choice2; protected TextField text; protected TextArea area; protected MenuBar menu; protected Menu file, help; protected Button button; public AllComponentsFrame (String title) { super (title); } public void init () { menu = new MenuBar(); this.setMenuBar(menu); file = new Menu("File"); file.add(new MenuItem("Open")); file.add(new MenuItem("Quit!")); menu.add(file); help = new Menu("Help"); help.add(new MenuItem("About")); menu.add(help); menu.setHelpMenu(help); panel1 = new Panel (); panel2 = new Panel (); panel3 = new Panel (); panel4 = new Panel (); panel5 = new Panel (); this.setLayout(new FlowLayout(FlowLayout.CENTER,50,75)); panel1.setLayout(new BorderLayout(5,5)); panel2.setLayout(new BorderLayout(5,5)); panel3.setLayout(new BorderLayout(5,5)); panel4.setLayout(new BorderLayout(5,5)); panel5.setLayout(new BorderLayout(5,5)); group = new CheckboxGroup (); panel2.add("North", new Checkbox ("George", group, true)); panel2.add("Center", new Checkbox ("Fred", group, false)); panel2.add("South", new Checkbox ("Frank", group, false)); panel3.add("North", new Checkbox ("Gisela")); panel3.add("Center", new Checkbox ("Zelda")); panel1.add("West", panel2); panel1.add("East", panel3); list = new List (3, true); list.addItem("First Choice"); list.addItem("Second Choice"); list.addItem("Third Choice"); choice1 = new Choice (); choice1.addItem("Choice 1"); choice1.addItem("Choice 2"); choice1.addItem("Choice 3"); choice2 = new Choice (); choice2.addItem("Choice A"); choice2.addItem("Choice B"); choice2.addItem("Choice C"); panel4.add("West", choice1); panel4.add("Center", choice2); panel4.add("East", list); text = new TextField ("This is a TextField."); panel5.add ("West", text); button = new Button ("Press Me!"); panel5.add ("East", button); area = new TextArea ("This is a TextArea."); area.setEditable(false); panel5.add ("South", area); this.add (panel1); this.add (panel5); this.add (panel4); } }

I'm not going to discuss this code in depth at this point. I have presented it here mainly as a reference to see how each Component is dealt with. After finishing this lesson, though, you should be able to follow this code fairly easily.

Before we discuss how to really use these objects, we need to talk about how GUIs are structured.
There are two general classes of GUI objects, Containers and Components. Containers are things you can put Components in. It is possible for a Container to be a Component. (In fact, they always are.) So, you can put a Container into another Container. All of the objects in the above example are Components. Containers are things like Frames, Windows, Canvases, and Applets. Because an Applet is a Container, you can add Buttons and other Components directly to it.
There are also things called LayoutManagers. LayoutManagers control the way the Components are arranged inside of a Container. There are five basic LayoutMangers, and they're listed below. LayoutManagers make it easy to layout a GUI by doing all of the hard parts for you.
And finally, there are things called Events. An event happens when the user does something with the GUI, such as clicking on a button or entering text in a field. When an event happens, an Event is created and must be handled by the GUI. This is the way GUIs function in Java. Every Component (and Containter) can have an event handler to respond to Events. We'll see more about these at the end of this lesson.

All of the Components in the above example need some form of layout (except for the Menu and MenuBar). The layout determines how the components will be arranged on the screen. A Container's layout is controlled by a LayoutManager. Java has a pretty good variety of LayoutManagers to choose from:

NameWhat it is
BorderLayoutEach component is placed against an edge or in the center as specified by "North," "South," "East," "West" or "Center"
CardLayoutOnly displays a single component at a time. Methods are available for cycling through the components
FlowLayoutArranges the components in rows from left to right, filling each row as much as possible before starting a new row.
GridBagLayoutThe most complex of the layouts. It works rather similarly to tables in HTML
GridLayoutLays out the components into cells in a grid, one Component per cell. You have to specify the height and/or width of the layout in cells.

Various Containers, such as Applet, Panel, Window and Frame, have different default LayoutManagers. Applet, for instance, defaults to FlowLayout.

Let's look now at examples of the five LayoutManagers. You can click once on any of the buttons below to bring up an example of that LayoutManager. Click on the button a second time to remove it.

Click on the button labeled "BorderLayout." First we're going to look at the BorderLayout LayoutManager.

import java.awt.*; import java.applet.*; public class BorderTest extends Frame { private Button button1, button2, button3, button4, button5; public BorderTest (String title) { super (title); } public void init () { this.setLayout(new BorderLayout(3,3)); button1 = new Button("North"); button2 = new Button("West"); button3 = new Button("Center"); button4 = new Button("East"); button5 = new Button("South"); this.add("North", button1 ); this.add("West", button2 ); this.add("Center", button3 ); this.add("East", button4 ); this.add("South", button5 ); } }

Short, sweet, to the point. This class extends Frame, which is why it can stand alone. It has a constructor which calls its parent's constructor. It also has an init() method which lays out the GUI components. First of all, init() declares the LayoutManager to use, BorderLayout in this case, by using a method of Container, setLayout(). When we create the new BorderLayout we pass it two numbers. Those numbers are the size of the gap to leave between the Components. In this example, we've specified a 3 pixel horizontal and 3 pixel vertical gap.
Next, we instantiate five buttons and give them all names. Then we add them to the Container with the add() method. When using a BorderLayout, we must tell the Container where we want to add the Components. So, in the add() method we specify one of the four directions.
NOTE: The directions have to be "Center", "North", "South", "East" and "West" exactly. The first letter must be capitalized and they must be in quotes. Also note that only one Component can be in each location. So, a BorderLayout can have at most 5 Components. We will see a way around this a little later.

Now, go back up, click again on BorderLayout to dispose of the example. Click on FlowLayout.

import java.awt.*; import java.applet.*; public class FlowTest extends Frame { private Button button1, button2, button3, button4; private Button button5, button6, button7, button8; public FlowTest (String title) { super (title); } public void init () { this.setLayout(new FlowLayout(FlowLayout.CENTER, 3,3)); button1 = new Button("These"); button2 = new Button("buttons"); button3 = new Button("are"); button4 = new Button("laid"); button5 = new Button("out"); button6 = new Button("using"); button7 = new Button("FlowLayout"); button8 = new Button("."); this.add(button1); this.add(button2); this.add(button3); this.add(button4); this.add(button5); this.add(button6); this.add(button7); this.add(button8); } }

As you can see, FlowLayout is even easier than BorderLayout to use! This program works in exactly the same way as the last except that the add() method doesn't specify a direction. That is because FlowLayout just sticks the Components into the Container in the order they're added. When it has filled up one row, it starts a new one.
Here the big difference is that when we create the FlowLayout object, we pass it two numbers and a constant. The numbers are still the horizontal and vertical gaps. The constant specifies when in it's space each Component should be placed. FlowLayout.CENTER says to center each Component in its area. FlowLayout.LEFT and FlowLayout.RIGHT are the other two possibilities.
Now, resize the example. Make it smaller. What happened? The Buttons moved to fit into the new shape of the screen. Now go back and bring up the BorderLayout example. Resize it. What happened there? The Buttons' sizes cahnged, but they stayed in the same places. That's the way BorderLayout works.

O.K. Get rid of both examples and click on GridLayout.

import java.awt.*; import java.applet.*; public class GridTest extends Frame { private Button button1, button2, button3, button4, button5, button6; public GridTest (String title) { super (title); } public void init () { this.setLayout(new GridLayout(2,3,3,3)); button1 = new Button("Top Left"); button2 = new Button("Top Middle"); button3 = new Button("Top Right"); button4 = new Button("Bottom Left"); button5 = new Button("Bottom Middle"); button6 = new Button("Bottom Right"); this.add(button1 ); this.add(button2 ); this.add(button3 ); this.add(button4 ); this.add(button5 ); this.add(button6 ); } }

Again, the code looks very similar. The difference to note here is that when we declare the LayoutManager in init() (with the setLayout() method), we now give it four numbers. They are the number of columns, the number or rows, the horizontal gap and the vertical gap respectively. With GridLayout you must specify what size the grid will be. If you want, you can pass a 0 to the number of columns or rows, and the GridLayout will compute the other dimension from the number of Components added.
Resize this one. Just like BorderLayout, the Buttons change size but not position. However, unlike BorderLayout, all of the Buttons remain the same size.

Kill the GridLayout and bring up the CardLayout

import java.awt.*; import java.applet.*; public class CardTest extends Frame { private Button button1, button2, button3, button4; private CardLayout layout = new CardLayout (5,5); public CardTest (String title) { super (title); } public void init () { this.setLayout(layout); button1 = new Button("Button1"); button2 = new Button("Button2"); button3 = new Button("Button3"); button4 = new Button("Button4"); this.add(button1); this.add(button2); this.add(button3); this.add(button4); } public boolean handleEvent (Event event) { if ((event.id == Event.ACTION_EVENT) && ((event.target == button1) || (event.target == button2) || (event.target == button3) || (event.target == button4))) { layout.next(this); return true; } else return false; } }

This one is a little different. We have this new method called handleEvent(). This method is an event handler. (Duh.) And there's a reason it's here. The CardLayout only displays one Component at a time. To switch among Components, we use the next(), previous(), first() and last() methods of CardLayout; I've chosen to use the next() method. In order to run the next method, I added the event handler. I won't discuss how it works here. That comes a little later. Click on the Button. The visible Component should switch to a new Button.
Notice another difference. In the previous examples we've created the LayoutManager on the fly in the setLayout() method. This time we've created a variable to store the CardLayout. Why? Well, the next() method has to be invoked on the appropriate CardLayout object. So, we keep a copy of our CardLayout to use with next(). We could've also just used ((CardLayout)this.getLayout()).next(this) to call next() and not have had to worry about keeping a copy of the CardLayout, but that's a little more complicated.

Alright, get rid of the CardLayout and click on GridBagLayout.

import java.awt.*; import java.applet.*; public class GridBagTest extends Frame { private GridBagLayout layout = new GridBagLayout (); private Button button1, button2, button3, button4; public GridBagTest (String title) { super (title); } public void init () { this.setLayout(layout); button1 = new Button("Top"); button2 = new Button("Middle Left"); button3 = new Button("Middle Right"); button4 = new Button("Bottom"); constrain (button1, 0, 0, 2, 1, 1.0, 2.0, GridBagConstraints.NORTHWEST); this.add(button1); constrain (button2, 0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHEAST); this.add(button2); constrain (button3, 1, 1, 1, 1, 3.0, 1.0, GridBagConstraints.SOUTHWEST); this.add(button3); constrain (button4, 0, 2, 2, 1, 1.0, 4.0, GridBagConstraints.SOUTHEAST); this.add(button4); } public void constrain (Component c, int x, int y, int w, int h, double wx, double wy, int a) { GridBagConstraints constraints = new GridBagConstraints (); constraints.gridx = x; constraints.gridy = y; constraints.gridwidth = w; constraints.gridheight = h; constraints.weightx = wx; constraints.weighty = wy; constraints.anchor = a; constraints.fill = GridBagConstraints.BOTH; constraints.insets = new Insets (2,2,2,2); layout.setConstraints (c, constraints); } }

Now this one is quite a bit different. We have a new method, constrain() which we use in init() to lay out the Components. Also, we're keeping a copy of the GridBagLayout, as we did with the CardLayout, and for much the same reason.
What does the constrain() method do, and why do we need it? Well, to lay out the Components with a GridBagLayout we need another object, GridBagConstraints. GridBagConstraints is an object which contains all of the formatting information needed to do the layout. With a GridBagConstraints, we can set specific constraints for each Component. In the previous examples, all Components were treated the same. In GridBagLayout, every Component can have a different set of constraints.
We use the GridBagConstraints object by setting each constraint variable and then binding those settings to the Component with the setConstraints() method of GridBagLayout. Below is a table of the variables in GridBagConstraints.
We created a new constrain() method because every object must be configured using a GridBagConstraints object, and it was easier to create a method than to replicate the code once for every Component.

Now, resize the example window. Notice that different buttons grow and shrink at different rates. That is because we told them to. GridBagLayout is by far the most versatile and useful of the LayoutManagers.

gridx, gridy Specify the grid position of the Component. The constant GridBagConstraints.RELATIVE tells the GridBagLayout to position them like a FlowLayout
gridwidth, gridheight Specify the size in cells of the Component. Similar to ROLSPAN and COLSPAN in HTML
fill Specifies in which directions if any the Component will change size when the window is resized. Can equal GridBagConstraints.HORIZONTAL, GridBagConstraints.VERTICAL, GridBagConstraints.BOTH or GridBagConstraints.NONE
ipadx, ipady Specify internal padding for the Component. Effectively increases the minimum size needed by the Component
insets An Inset object used to set the margins between objects
anchor Specifies which side of the cell to have the Component stay against if the window grows and the Component doesn't. Can be any of the eight cardinal directions or center, in the following format: GridBagConstraints.NORTHEAST
weightx, weighty Specify how the Component should grow in proportion to other components. Similar to the * notation for Frames in HTML. If ComponentA has weightx = 1.0 and ComponentB has weightx = 2.0, ComponentB will grow twice as fast in the x direction.

Now, in some LayoutManagers like BorderLayout and CardLayout, you're limited in how many Components you can have visible at a time. There is a very simple way around this. In the java.awt package there is a class called Panel. A Panel in a Container, so you can put Components in it. But, as all Containers are, it is also a Component. All you have to do is put Components into a Panel, and then add the Panel to your LayoutManager. It will be treated as a single Component for placement, even though it contains several.

Go up and click on the button labeled "Panel."

import java.awt.*; import java.applet.*; public class PanelTest extends Frame { private Button button1, button2, button3, button4; public PanelTest (String title) { super (title); } public void init () { Panel panel = new Panel (); this.setLayout (new GridLayout (3,1,3,3)); panel.setLayout (new GridLayout (1,2,3,3)); button1 = new Button("Top"); button2 = new Button("Middle Left"); button3 = new Button("Middle Right"); button4 = new Button("Bottom"); panel.add(button2); panel.add(button3); this.add (button1); this.add (panel); this.add (button4); } }

This is a slightly modified version of our GridLayout example. Let's step through it.
In init(), the first thing we do is instantiate a Panel. We'll use this Panel to fit two Buttons into a single cell of the GridLayout. Next we set the LayoutManager of the Frame to a GridLayout with 3 rows and 1 column and a cell spacing of 3. Then we set the LayoutManager of the Panel to a GridLayout with 1 row and 2 columns, also with a cell spacing of three. All Containers need a LayoutManager. We instantiate four Buttons. Then we add button2 and button3 to the Panel. Finally, we add button1, button2 and our Panel to the Frame.
Looking at the example, what you see is that we have successfully thwarted GridLayout's attempts at making everything the same. The middle row has more Buttons than the other two. In actuality, there is only one Component in the middle row, but that Component contains two Buttons.
Now, bring the GridBagLayout example back up. Notice that the two look very similar. Now resize them. A-ha! The difference is that the GridLayout, as always, resizes everything the same. The GridBagLayout allows for each Component to have a different set of constraints.

Close the GridBagLayout and Panel examples and bring up Panel 2.

import java.awt.*; import java.applet.*; public class Panel2Test extends Frame { private Button button1, button2, button3, button4; private CardLayout layout = new CardLayout (5,5); public Panel2Test (String title) { super (title); } public void init () { Panel panel = new Panel (); this.setLayout(layout); panel.setLayout(new GridLayout(1,2,5,5)); button1 = new Button("Button1"); button2 = new Button("Button2"); button3 = new Button("Button3"); button4 = new Button("Button4"); this.add(button1); panel.add(button2); panel.add(button3); this.add(panel); this.add(button4); } public boolean handleEvent (Event event) { if ((event.id == Event.ACTION_EVENT) && ((event.target == button1) || (event.target == button2) || (event.target == button3) || (event.target == button4))) { layout.next(this); return true; } else return false; } }

Here we have a CardLayout. CardLayout only lets one Component be visible at a time. But what if that Component is a Container? Click on the button a few times a see what happens. button2 and button3 are both visible at the same time. That's because the visible Component is a Panel, and button2 and button3 are contained in the Panel.
Notice that the Frame uses CardLayout and the Panel uses GridLayout. Every Container in your GUI can have a different LayoutManager if you like. Also, you can nest Containers as deeply as you like. The only limit is room on the screen.
There are several Containers, and they're all a little different. For windows that stand on their own like these examples, Frame is good. For grouping Components in layouts, Panel is the best.

At this point, you should be able to go back to the very first example, the one I said I wasn't going to explain, and it should make sense.

Now that we have seen how to lay out Components and what kinds of Components we can lay out, let's look at the stuff that really makes GUIs work, the eventHandlers.

In both of the examples using CardLayout, I added in an event handler to react when the user clicks on a button. Otherwise, we wouldn't have been able to cycle through the Components in the layout. Event handlers are the way you're given to react to user input. Almost everything the user does results in an Event being generated.

First let's look at the kinds of events that can be generated.

NameWhen it occurs
ACTION_EVENTSignifies that the user did something. The exact meaning depends on which Component the event occured for.
GOT_FOCUSSignifies that the user brought the Component into focus, i.e. if the user types something, the Component will receive it.
KEY_ACTIONUser pressed a function key
KEY_ACTION_RELEASEUser released a function key
KEY_PRESSUser pressed a regular key
KEY_RELEASEUser released a regular key
LIST_SELECTUser selected an item from a List
LIST_DESELECTUser deselected an item from a List
LOAD_ -->