GUI components |
Java has GUI components like those in Grace, though associating actions with components requires a few more steps.
To install and use a GUI component you must:
Adding graphic user interface (GUI) items to the screen is pretty easy. Here are the steps:
figureMenu = new JComboBox<String>(); figureMenu.addItem("FramedSquare"); figureMenu.addItem("FramedCircle"); figureMenu.addItem("FilledSquare");
add(figureMenu, BorderLayout.SOUTH); add(colorMenu, BorderLayout.NORTH); validate();
Let's look at a simplified drawing program to see a few of those steps. DrawingProgram.
First we declare the ComboBox (pop-up menu):
// The menu determining which kind of object is drawn private JComboBox<String> figureMenu;
Then initialize it in the begin method:
public void begin() { // Create menu for selecting figures figureMenu = new JComboBox<String>(); figureMenu.addItem("FramedSquare"); figureMenu.addItem("FramedCircle"); figureMenu.addItem("FilledSquare"); add(figureMenu, BorderLayout.SOUTH); validate(); ... }
The first statement creates the menu. The next three add entries to the menu. The add statement adds the figureMenu to the bottom of the screen. The last statement tells Java to arrange all the pieces nicely in the window.
Let's talk briefly about layout managers. The main window generated by WindowController uses BorderLayout. A window with BorderLayout has 5 slots that can hold items. They are the NORTH, SOUTH, EAST, WEST, and CENTER. There are public constants from the BorderLayout class referring to each of those. You can refer to them by prefixing the name with BorderLayout, e.g., BorderLayout.SOUTH.
The add method when you have border layout takes two parameters, one with the component, while the second has where to put the item. (See the code above.) When your class extends WindowController the CENTER portion of the screen will be filled with the drawing canvas.
Each of the slots in the window can hold only one item. Thus if you put the figureMenu is the south slot, then other GUI items must be put elsewhere. Luckily, Java has containers that can hold multiple items and they can be put in one of those slots. The containers in Java are generated by class JPanel. Adding a bit to the complexity, JPanels use a different layout manager than our WindowController. It uses FlowLayout rather than BorderLayout. When you add items to a container using FlowLayout it simply adds them to the container from left to right and centers them. Thus, to add an item to a JPanel, we use an add method that only needs a single parameter, the item to be added to the panel.
The first drawing program that we described when explaining arrays used a JPanel to place three combo boxes in the south part of the window.
// menus for shape, color, and command private JComboBox<String> shapeChoice; private JComboBox<String> colorChoice; private JComboBox<String> commandChoice;
The begin method then created the menus, put them in the JPanel, and then added the JPanel to the window in the south:
public void begin() { // create panel to hold choice buttons JPanel menuPanel = new JPanel(); // menu for selecting or adding commandChoice = new JComboBox<String>(); commandChoice.addItem("Add new item"); commandChoice.addItem ("Recolor item"); commandChoice.addItem("Move item"); commandChoice.addItem("Delete item"); menuPanel.add(commandChoice); // Note only a single parameter -- no direction! // Set up menu for shapes shapeChoice = new JComboBox<String>(); shapeChoice.addItem("Circle"); shapeChoice.addItem("Square"); menuPanel.add(shapeChoice); // Note only a single parameter -- no direction! // Set up menu for colors colorChoice = new JComboBox<String>(); colorChoice.addItem("Red"); colorChoice.addItem("Green"); colorChoice.addItem("Blue"); menuPanel.add(colorChoice); // Note only a single parameter -- no direction! // Add the panel to screen add(menuPanel, BorderLayout.SOUTH); // Two parameters because adding to WindowController validate(); }
Java supports more kinds of GUI components than just JComboBox. A relatively compact introduction to the most popular components and how to use them can be found at http://www.cs.pomona.edu/classes/cs051/handouts/SwingGUICheatSheet.html. These include JButton (corresponding to Button in Grace), JLabel (corresponding to TextBox in Grace), JSlider, JTextField (corresponding to TextField in Grace), and JTextArea.
We will use JButton in examples below. We construct a button via new JButton(s) where s is the label on the string. Methods getText and setText(newS) are available to retrieve and update the label.
As in Grace, if we wish to have a GUI component react to a user selection then we must associate an action with it. Here is the handler for Grace that we wrote for a clear button:
clearButton.onMousePressDo {mevt:MouseEvent -> canvas.clear }
We used a method like onMousePressDo to associate an action with the clearButton. That action took a MouseEvent parameter mevt and then cleared the canvas.
For pop-up menus (items of type Choice), we used the method onChangeDo:
// when user select new color, change color of newShape colorMenu.onChangeDo{ evt: Event -> match(colorMenu.selected) case {"red" -> newShape.color := red} case {"blue" -> newShape.color := blue} case {"green" -> newShape.color := green} case {"yellow" -> newShape.color := yellow} }
Until very recently, Java did not allow us to pass blocks like these to methods, so it developed a different mechanism for associating actions with GUI components. It associated Listener objects with GUI components. These listener objects had methods that could respond to user actions. For example, JButton objects are prepared to add ActionListeners. The interface ActionListener has a single method with the following header:
void actionPerformed (ActionEvent e)
If lisOb is an object of type ActionListener then we associate it with clearButton by writing clearButton.addActionListener(lisOb). Once this has been done, any time the user clicks on clearButton, the system will make a method request of actionPerformed on lisObj - resulting in execution of that object's code for the method.
Different GUI items have different kinds of listeners. For now we will only focus on responses to buttons, pop-up menus, and text fields, and they all are associated with the same kind of listener, ActionListener. Read the GUI cheat sheet documentation for more information on the different kinds of listeners associated with different GUI items. More information is available in the documentation for the standard Java libraries.
For simplicity here, we will always have the main program (the class extending WindowController) act as the listener for all components. Thus to set up the main program to handle events generated by the user pressing a button, we must do the following:
colorMenu.addActionListener(this);
public class DrawingProgram extends WindowController implements ActionListener { ... }
public void actionPerformed(ActionEvent event) { ... }
Let's see what this looks like in practice. We are going to write a Java program that is equivalent to our drawingProgram in Grace. The complete Java program can be found here
This program has two pop-up menus, only one of which, the color menu, responds to user selections. The other one is only consulted when the user clicks on the screen. The color menu is declared with the other instance variables at the top of the screen:
// The menu determining which color the object should be private JComboBox<String> colorMenu;
The begin method contains the code to initialize it and put it on the screen
public void begin() { ... // create menu for selecting colors colorMenu = new JComboBox<String>(); colorMenu.addItem("Red"); colorMenu.addItem("Blue"); colorMenu.addItem("Green"); colorMenu.addItem("Yellow"); colorMenu.addActionListener(this); // this object now notified of any selections on menu ... add(colorMenu, BorderLayout.NORTH); // add menu to north side of window. ...
For it to be legal to use this as the listener, we need it to implement ActionListener. This requires us to implement the method actionPerformed, but also requires us to tell Java that we want this class to be an ActionListener:
public class DrawingProgram extends WindowController implements ActionListener {...}
Remember Java will not believe a DrawingProgram is of type ActionListener unless we tell it so in the class header.
In the following we have the implementation of method actionPerformed. Because of this code, when the user makes a selection from the color menu, the method will first make sure newShape (the last object put on the screen) is different from null (i.e., has a value associated with it) and then calls the private helper method setColorFromMenu to change the color of newShape.
/** * Select a new color for the last object drawn * @param event - contains information on object generating the event */ public void actionPerformed(ActionEvent event) { if (newShape != null) { setColorFromMenu(); } } /** * Change the color of the newest shape on canvas to new value of * the color menu. */ private void setColorFromMenu() { Object colorChoiceString = colorMenu.getSelectedItem(); if (colorChoiceString.equals("Red")) { newShape.setColor(Color.RED); } else if (colorChoiceString.equals("Blue")) { newShape.setColor(Color.BLUE); } else if (colorChoiceString.equals("Green")) { newShape.setColor(Color.GREEN); } else if (colorChoiceString.equals("Yellow")) { newShape.setColor(Color.YELLOW); } }
We didn't use the event parameter to actionPerformed, but in general it can be used to determine, for example, the source of the event. This is necessary in Java because one listener can be listening to many different GUI components, so we sometimes need to figure out which one. See the description of ActionEvent in the Java documentation for a listing of all methods.
JTextBox objects also use ActionListeners.
Thus the main differences between handling user events on GUI items in Grace and Java are:
GUI components |