[email protected]1 CIS 5930-04 – Spring 2001 Part 4: GUIs and Events http://aspen.csit.fsu.edu/it1spring01 Instructors: Geoffrey Fox , Bryan Carpenter Computational Science and Information Technology Florida State University Acknowledgements: Nancy McCracken Syracuse University
CIS 5930-04 – Spring 2001 Part 4: GUIs and Events. http://aspen.csit.fsu.edu/it1spring01 Instructors: Geoffrey Fox , Bryan Carpenter Computational Science and Information Technology Florida State University Acknowledgements: Nancy McCracken Syracuse University. - PowerPoint PPT Presentation
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
In the standard Java windowing toolkits, the GUI (Graphical User Interface) is built hierarchically in terms of components—one component nested inside another starting with the smallest buttons, including menus, text fields etc. and ending with full window divided into frames, menu bars, etc.
The user can interact with the GUI on many of its components, by clicking a button, typing in text, etc. These actions cause an event to be generated, which will be reported by the system to a class that is an event listener. The event listener will have an event handler method for the event. This method will provide the appropriate response to the user's action.
We notice that the main() method ends as soon as the window appears. But the command java FrameTest does not terminate. There is no new command prompt.
Closing the window using its system menu doesn’t change this. The command still does not terminate.
To explain this behavior, we should look to threads. As will be discussed in detail later, a Java program
can start extra threads during its execution. These run concurrently with the original thread (the thread in which the system started the main() method).
The JVM, and thus the java command, terminates when the main() method has terminated and all threads the main() method created, directly or indirectly, have also terminated.
We can guess that FrameTest started one or more additional threads running, and those threads don’t terminate.
. . . they don’t appear to be documented in detail, but one of them is surely the event-dispatching thread associated with the AWT system event queue. (Likely scenario: event queue classes are dynamically loaded when the first event-related operation occurs. When this happens an event dispatching thread is started. In the current case, opening a window creates the first event, and thus starts the thread.)
Event dispatching and the event queue will be discussed in more detail later.
But to make progress now, we must explicitly use the event mechanism to solve the problem of terminating FrameTest.
We only care about window-closing events (which happen when the user closes a window). Our implementation is:
class TerminationListener implements WindowListener {
public void windowActivated(WindowEvent e) {} public void windowClosed(WindowEvent e) {} public void windowClosing(WindowEvent e) { System.exit(0) ; // Shut down the
JVM! } public void windowDeactivated(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowOpened(WindowEvent e) {} }
A Swing frame has a fairly complicated structure, with several panes. Some of these are used to implement pluggable look and feel. We will only directly use the Content Pane.
Atomic Components and Containers For each atomic component, one can create one
or more instances of the component type and then use the add() methods to place them into a container component, such as a panel.
For now, we assume that components are added to the panel in order from left to right and top to bottom, as they fit.– This is the default layout strategy for JPanels—flow
layout.
For JFrames, the content pane is a container obtained by using the getContentPane() method. Typically we add() a JPanel containing further components to this pane.– The content pane itself has border layout by default,
which means it can only directly display a single component in its center field.
A Panel with Buttons We will derive a class from JPanel. The
constructor adds three buttons with labels “Red”, “Blue” and “Green”. class ButtonPanel extends JPanel . . . { public ButtonPanel { redButton = new JButton(“Red”) ; blueButton = new JButton(“Blue”) ; greenButton = new JButton(“Green”)
Action Listeners When a button is pushed, it creates an ActionEvent. To respond to this kind of event, the application
must provide a listener implementing the ActionListener interface.
Simpler than the WindowListener—it only has one method:
interface ActionListener extends EventListener { public void actionPerformed(ActionEvent evt)
; }
The listener may be implemented by a purpose-built class, like earlier TerminationListener. But it is quite common and convenient that the container holding a component also acts as its listener.
Completing ButtonTest An instance of our ButtonPanel is created, and
added to the content pane of the JFrame: public static void main(String [] args) { JFrame frame = new JFrame() ; frame.setTitle(“ButtonTest”) ; frame.setSize(450, 300) ; frame.addWindowListener(new
TerminationListener()) ;
frame.getContentPane().add(new ButtonPanel()) ;
frame.show() ; }
Calling add(new ButtonPanel()) nests a ButtonPanel container inside the content pane container associated with the frame. Latter has border layout: can only directly hold a single component per “area”—here the central area.
Check Boxes and Radio Buttons Check boxes and radio buttons are variations on
the stateless, clickable button we have just seen. The new versions have a definite (and clearly displayed) internal state. This state is toggled by clicking them.
At any time, a check box is either selected, or not selected (checked or unchecked). Radio buttons come in groups, and only one member of the group can be selected at a time. “Switching on” a new member of the group causes its predecessor to automatically be “switched off”.
Like JButton, the associated JCheckBox and JRadioButton components generate an ActionEvent when they are clicked. If the buttons demand immediate action (not always the case) these events may be handled by action listeners.
Adding a Check Box to a Panelclass CheckBoxPanel extends JPanel implements ActionListener { public CheckBoxPanel() { redButton = new JCheckBox(“Red”, false) ; // Initially
unselected add(redButton) ;
redButton.addActionListener(this) ; } public void actionPerformed(ActionEvent evt) { if (redButton.isSelected()) // Test if
Handling Radio Button Events Note grouping radio buttons with ButtonGroup in no way
affects their display. That is a layout issue. The button group only ensures other buttons are switched off when a new one is switched on (separation of control and view).
class RadioButtonPanel extends JPanel implements ActionListener {
. . . Declaration of constructor (previous slide) . . . public void actionPerformed(ActionEvent evt) { if (redButton.isSelected()) // Test if
selected setBackground(Color.red) ; if (blueButton.isSelected()) setBackground(Color.blue) ; if (greenButton.isSelected()) setBackground(Color.green) ; repaint() ; } private JRadioButton redButton, blueButton,
Swing components are considered to have three aspects:– Contents, e.g., the state of a button, text string in a text
field.– Visual appearance, e.g., colour, size and shape of a button.– Behavior, e.g, response to events such as mouse clicks.
Swing attempts to keep these aspects separate—it tries to follow a model-view-controller design pattern, as far as practical.
In practice the interaction between behavior and visual appearance usually needs to be quite strongly coupled, e.g. to meet the requirements of a custom look and feel.
However Swing components do normally provide well-defined, well-separated, model classes.
A text field is a component used for inputting a small amount of text—it only accepts a single line. It automatically takes care of normal line-editing, as determined by whatever look-and-feel is in effect.
The associated class is JTextField. For larger amounts of text you should use a text
area. A text field might also be used for displaying a short
amount of text—not allowing editing. The model associated with a text field (or a text
area) is a, document model, implementing DocumentModel.
For these components, listeners are usually added to the model, rather than the component itself.
Insert and remove updates have straightforward meaning. Changed updates not generated by the plain documents we discuss (but by style documents with embedded formatting).
In simple cases may not care which method was invoked—may simply reread whole string from field every time it is updated.
Note this interface is in javax.swing.event (not java.awt.event)
class TextFieldPanel extends JPanel implements DocumentListener { public TextFieldPanel() { input = new JTextField(20) ; // Preferred width in
columns. input.getDocument().addDocumentListener(this) ; add(input) ; display = new JTextField(20) ; // Displays current input text display.setEditable(false) ; // Editing disabled for this
field. add(display) ; } public void insertUpdate(DocumentEvent evt) { display.setText(input.getText()) ; // Copy from input field to
display } public void removeUpdate(DocumentEvent evt) { display.setText(input.getText()) ; // Copy from input field to
Buttons and text fields allow input from the user. But they cannot force the user to do anything. The program simply reacts to whatever component the user is focused on.
Sometimes you need to insist the user makes a choice before anything else can proceed.
Scenario:– The user clicks a button to change something in the
display (e.g. an entry in a list). At that point you need to enter into a dialog with the user to establish exactly what is required, before user’s attention goes elsewhere.
Following example illustrates a new way of getting text input from user. User clicks change button, then is prompted with a dialog box.
The text field allows the user to enter or edit a single line of text.
A text area has almost identical behavior, but it holds multiple lines of text.
Creation and handling of a raw text area is almost the same as a text field, except you have to specify the number of rows, and you can optionally set wrapping policies for long lines. The class is JTextArea:
JTextArea input = new JTextArea(8, 10) ; // rows and columns
input.setLineWrap(true) ; // wrap long lines.
By itself this component responds inconveniently when text gets too long to fit in the specified area. The area just grows in an antisocial way.
In practice a text area will usually be enclosed in a specialized container called a scroll pane, class JScrollPane.
In general a scroll pane can include any large component. It adds vertical and horizontal scroll bars, that allow the whole of the component to be traversed.
Components that are especially likely to appear in scroll panes may implement the Scrollable interface. This allows them to provide extra information to control scrolling.
These “scroll-savvy” components include JTextField, JTextArea, JList, JTable, and JTree.
The policy for the vertical scroll bar on a scroll pane is one of: VERTICAL_SCROLLBAR_ALWAYS VERTICAL_SCROLLBAR_NEVER VERTICAL_SCROLLBAR_AS_NEEDED
Similarly the policy for the horizontal scroll bar is one of: HORIZONTAL_SCROLLBAR_ALWAYS HORIZONTAL_SCROLLBAR_NEVER HORIZONTAL_SCROLLBAR_AS_NEEDED
The default for both is “as-needed”: the scrollbars only appear if the component dimensions grow larger than the view area.
Don’t particularly recommend these defaults. Sudden appearance of scrollbars can affect global layout (and currently the response of text areas seems somewhat buggy).
Groups of buttons can be used to select from a small, usually static, set of behaviors, e.g. toggling between three primary colors.
If we have a larger set of options to choose between, especially if those options are likely to change dynamically in the course of the program, a list component is likely to be a more suitable device.
The class of the Swing list component is JList. In the display, the elements of a list are stacked
vertically in a column. As with text areas, you will nearly always want
Typically valueChanged() is dispatched several times during the selection process (to allow for more sophisticated handling strategies). When the final selection has stabilized, the value returned by getValueIsAdjusting() will be false.
The class DefaultListModel object generally behaves like (a slightly out of date) java.util.Vector object.
You can, for example, create a DefaultListModel, add initial list elements to this object as if it was a Vector, then create a JList component containing this model.
The most important special property of a DefaultListModel is that it will automatically generate events to update the JList display, whenever the model’s contents are changed.
class DefaultListModelPanel extends JPanel implements ActionListener {
public DefaultListModelPanel () { . . . } public void actionPerformed(ActionEvent evt) { int index = list.getSelectedIndex() ; if(index >= 0) keys.remove(index) ; else Toolkit.getDefaultToolkit().beep() ; // No current
selection } private JList list ; private DefaultListModel keys ;}
Don’t assume that you must listen to every component that can generate events. You only need to set up a listener for a particular kind of event if an immediate programmatic response is needed. – In the immediately preceding example we did not have
to listen for list selection events; nothing happened until a button was pressed.
– You may not need to listen for updates to a document; again, perhaps the text is only saved when some button is pressed (but be careful not to lose user’s inputs!)
– A checkbox or radio button may only need to be interrogated for its selection status when a form is submitted, say. In this case there is no need for immediate response at the time of selection.
Menus are conceptually easy, although the programming tends to get verbose (in general, GUI programming tends to get verbose. . .)
They have a hierarchical organization:– Add a menu bar (JMenuBar) to a JFrame– Add one or more menus (JMenu) to a menu bar– Add one or more menu items (JMenuItem), or sub-
menus (JMenu again) to each menu.
Each menu item is handled simply like a button, by adding an ActionListener to it.
We assume the chooser is displayed by a handler for an action event, perhaps an open or save item in a menu.
A file chooser object is created, then some attributes are set for it: here we only set the current directory. In general one can also set default files, and filters for file extensions, etc.
The chooser object with these attributes may be saved and reused in several places.
A dialog window is created by the showOpenDialog() method. There is also a showSaveDialog() method.
The selected file is returned by the getSelectedFile() method.
A label, class JLabel, is what it says: a text label usually describing another component or group of components. No listener is required.
A combo-box, class JComboBox, is a drop-down “menu”, usually placed in a panel. Reminiscent of a list, but programmatically different—generates action events on selection—can use getSelectedItem() to discover choice.
A slider, class JSlider, is similar to a scroll bar. It is a convenient way of inputting analogue data. Uses a new kind of listener—a change listener.
Can only cover a limited selection of Swing components in these lectures. For further components, and more details on using the components already covered, see for example:
– Core Java 2, Volume I, Chapter 9: “User Interface Components with Swing”,
– Core Java 2, Volume II, Chapter 6: “Advanced Swing”,
– packages javax.swing, java.awt, and their sub-packages, at:
If a container contains nested panels, the subcomponents in each panel are laid out independently.
One can lay components out “by hand”, with positioning in pixel space.
However it is very difficult to make this machine independent. Instead one tends to use general strategies embodied in several layout managers. These all implement the LayoutManager interface.
To specify a layout, such as flow layout, in your panel use the setLayout() method: setLayout(new FlowLayout()) ;
Some Available Layout Managers FlowLayout is a one-dimensional layout where
components are “flowed” into panel in order they were added. When a row is full up, it is wrapped onto next row.
BorderLayout arranges the components into five areas called North, South, East, West and Center.
GridLayout is a two-dimensional layout where you define a N by M set of cells and again the components are assigned sequentially to cells starting at top left hand corner—one component is in each cell. The cells are the same size.
BoxLayout is a one-dimensional layout where you have more control over the spacing and sizing of the components.
GridBagLayout uses a class GridBagConstraints to customize positioning of individual components in one or more cells.
Using BoxLayout As usual, may setLayout() on a panel to box layout. Alternatively can use a special container called Box
whose default layout is a vertical or horizontal box layout, e.g.: Box b = Box.createHorizontalBox( ) ;
or Box b = Box.createVerticalBox( ) ;
You can add components to a box in the usual way They are flowed into the one dimension. The
strategy is to make them fit into one row or column, using the preferred size and alignment if possible, but growing them to maximum size if necessary.
There are invisible fillers available to space the components.
– A strut adds some space between components, e.g.:
b.add(button1) ; b.add( Box.createHorizontalStrut(8) ) ; b.add(button2) ; adds 8 pixels between these buttons in a horizontal box.
– A rigid area also adds a fixed amount of space between components, but may also specify a second dimension, affecting the height of a horizontal box and the width of a vertical box e.g.:
b.add ( Box.createRigidArea( new Dimension (10, 20) ) ;
– Adding glue separates the components as much as possible, e.g.:
Hierarchical Use of Layouts Each component in a Layout may itself be a Panel
with another Layout Manager, thus subdividing areas of the user interface. Using this hierarchy one can lay out complex GUIs.
A simple example divides the main applet space into two components in a BorderLayout. The Center field might contain graphics. The North component is itself a Panel that has three buttons in a GridLayout. This example is a very simple example of a standard paradigm for drawing or displaying data.
In a hierarchical layout, you might put a border around a panel to visibly group together the set of components it contains. The border can optionally include a title for the panel.
You might also put an empty border around a component, simply to create extra space around it.
You create a border using static methods of the BorderFactory class, then add it to a panel or other component using the setBorder() method of JComponent.
The constructor of JLabel has an optional align argument, which ought to make the setAlignmentX(0.5F) invocation unnecessary—it didn’t work for me.
If you setAlignmentX(0.0F) (the default), Box treats this as right alignment. If you setAlignmentX(1.0F), Box treats it as left alignment. This is the opposite of what you might have guessed (and what the documentation suggests).
Finally, note the API documentation says setAlignmentX() sets vertical alignment. Actually (more logically) it sets horizontal alignment.
Icons can be used in various places. Buttons, labels and related components generally allow an icon to be displayed in addition to or in place of a text label:
JButton(String label, Icon icon) // Text and icon
JMenuItem(String label, Icon icon) // Text and icon
JLabel(Icon icon) // Just icon
Icons are usually loaded from image files, e.g. Icon icon = new ImageIcon(“icon.gif”) ;
As briefly mentioned earlier in this lecture set, any AWT program typically involves at least two concurrent threads. Both of these will access components of the GUI at some point:
Allowing multiple threads to access the same data structures is always a potentially dangerous situation. To avoid unexpected effects some mutual exclusion strategy is required.
In fact (we will see later) Java provides synchronized methods to support this kind of mutual exclusion.
Unfortunately synchronized methods come with some overhead, and Swing components generally do not use synchronized methods.
Instead there is an assumption that essentially all operations on GUI components should occur in the event dispatching thread.
This implies that all dynamic modifications and operations on the GUI should occur in event-handling methods.
This creates a paradox, because there is no way to get started!
Therefore an exception is made: before any part of the GUI is displayed (in practice this means before the event-dispatching thread is even started), the main thread is allowed to work on the GUI—to create it!
Sometimes another thread really must do something to the GUI. It can achieve this by using invokeLater(): Runnable task = new MyTask() ; SwingUtilities.invokeLater(task) ;
The Runnable interface defines one method—run(). MyTask should overload this method with the code that manipulates the GUI.
MyTask is put in the event queue just as if it was an external event. When it reaches the front, it is executed in the event-dispatching thread.
Note this is essentially like a batch processing system with a job queue.
Low Level Events Most of the events we saw earlier (ActionEvent,
DocumentEvent, ListSelectionEvent, etc.) are called semantic events. They reflect what the user is doing at a logical, component level.
There is another family of events including KeyEvent, MouseEvent, FocusEvent and WindowEvent, which directly reflect the interaction of the user with the input devices. These are called low-level events.
Any component, including containers, can generate mouse and keyboard events, as the user moves or clicks the mouse in the window, or types a single key on the keyboard.
These might be useful in an application that allows some “analogue” mode of input, such as drawing on a panel.
– Called when the mouse button is pressed with the cursor in this component; when the mouse button is pressed and released without moving; when the mouse button is let up; when the mouse cursor enters the component; or when the mouse cursor leaves it.
Graphics is Event-Driven The windowing system creates an image with a
Graphics object to keep track of the state of the window.
In order to draw or directly write text to the window, you override the paintComponent() method: public void paintComponent(Graphics g)
This is essentially an event-handling method.
The window can be interrupted for various reasons—the user resized it or some other window was put on top of it and then removed. The system does not save a copy of the pixels. Instead it calls the paintComponent() method. So even if you only draw a window once, paint can be called many times.
The call g.setFont(f) will set the current font in Graphics object g to f—an instance of class Font.
The class Font has an important constructor: Font myFont = new Font(“Serif”, Font.PLAIN, 36) ;
– one can use specific font names like TimesRoman, Courier, Helvetica, ZapfDingbats, but Sun recommend you use generic names like Serif, SansSerif, and MonoSpaced.
– Font.PLAIN, Font.BOLD, Font.ITALIC and Font.BOLD + Font.ITALIC are possible text styles
To find out which fonts are available, one may call String fonts[] GraphicsEnvironment .getLocalGraphicsEnvirontment() .getAvailableFontFamilyNames() ;
An Image represents a complex image, such as a photograph.
Getting an image from a file: Image img =
Toolkit.getDefaultToolkit()getImage(“image.gif”)
Getting an image from the Net: URL u = new URL(http://www.someplace.com/image.gif) ; Image img = Toolkit.getDefaultToolkit()getImage(u) ;
The Image class in java.awt has many methods for images, such as getWidth(imageobserver) and getHeight(imageobserver), which return the width and height in pixels.
The Graphics class provides a method drawImage() to actually display the image: void paintComponent ( ) { g.drawImage (img, 10, 10, this) ; }– the top left corner of the image will be drawn at (x,y)
position (10,10).– this is the ImageObserver argument—it is the
component in which the image is displayed.
You can also scale the image to a particular width and height: g.drawImage (img, 10, 10, w, h, this) ;
When drawImage() is called, it draws only the pixels of the image that are already available.
It creates a thread for the ImageObserver. Whenever more of the image becomes available, it activates the method imageUpdate(), which in turn call paint() and drawImage(), so that more of the image will show on the screen.
More control over showing the image as it downloads can be obtained by working with the MediaTracker class, using methods that tell you when the image has fully arrived.
The Graphics2D Class Additional graphics 2D drawing capabilities are
found in the Graphics2D class. Graphics2D is a subclass of the Graphics class,
and the argument passed to paintComponent() is actually an instance of this subclass. Converting the graphics context to this class allows additional methods to be called.
public void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g ; . . . }
Note that there are also packages for 3D graphics and other media frameworks not covered in the current lectures.
With Graphics2D, the current graphics context includes the current “paint”: g2d.setPaint (p) ;
where p is anything that implements the interface Paint.
The Color class implements Paint. New classes include GradientPaint, SystemColor, and TexturePaint:new GradientPaint (x1, y1, color1, x2, y2, color2, boolval)– colors a gradient which starts at the first coordinate and
shades in the direction of the second coordinate, shading from the first color to the second color. The boolean value determines whether the shading is cyclic (true) or acyclic (false).
new TexturePaint (image, rectangle)– where the image is cropped to the size of the rectangle
With Graphics2D, the current graphics context includes the current “stroke”: g2d.setStroke(s) ;
where s is anything that implements the interface Stroke.
Class BasicStroke provides a variety of constructors to specify the width of the line, how the line ends (the end caps), how lines join together (join lines), and the dash attributes of the line:new BasicStroke (width, endcaps, joins, mitrelimit, dasharray, dashphase)– where dasharray gives the lengths of a sequence of
dashes and dashphase gives which dash to start with.