You are on page 1of 24

Introduction to Java GUI programming

Bob Dickerson January 2005

1 Applets and JFrame applications
In Java (J2SE) there are two main types of program that can have graphical user interfaces: applets and JFrame applications: • an applet (produced by JApplet) is a mini-program that can be executed inside special viewer like appletviewer or browsers like mozilla or internet explorer. An applet is not a free standing program it is started from within another program (eg a browser), • a JFrame application is just like a non-gui Java application (in other words, it must have a main function) except its class inherits from, or uses the JFrame class and it contains code to build and use the graphical user interface. An applet is usually used for drawing or graphics to enhance a web page whereas a JFrame usually provides a GUI for an application. Applets don’t seem to be used very much any more this might be because of the increasing use of server-side languages like ASP, JSP or PHP which are much more useful for internet commerce, data-base servers, search engines etc. Client side (in the browser) functionality is not as important. Whereas there are now loads of widely used Java applications with graphical user interfaces (using JFrame).

1.1 An applet ImageApplet0.java
This example just displays an image, a JPEG file. What it shows is a little bit about the structure if an applet. First the program: import java.net.*; import java.awt.*; import javax.swing.*; public class ImageApplet0 extends JApplet Image img; URL codebase; {

public void init() { // called when page is loaded into browser codebase = getCodeBase(); img = getImage( codebase, "tux.jpg"); } public void paint(Graphics g) { // called every time applet needs redrawing g.drawImage(img, 0, 0, this); } public void start() { // called every time page containing applet is displayed } public void stop() { // called every time page containing applet is overwritten } public void destroy() { // called when applet removed from browser. } } 1

1 APPLETS AND JFRAME APPLICATIONS

2

The program is in the file ImageApplet0.java and the image in tux.jpg. This applet displays an image that looks like this when executed by the browser firefox:

The file that must be viewed by the browser so that the applet is loaded and started is called ImageApplet0.html: <html> <body> <h1> Here is Tux </h1> <p> <applet code="ImageApplet0.class" width="300" height="350"> </applet> </p> </body> </html> The file is ImageApplet0.html. The comments on ImageApplet0.java can be divided into those about how the image is displayed and some more general comments about the structure and behaviour of applets. First the image display. The drawing routines in Graphics can render an Image on the screen but first it must be loaded from a file. Where is the file? An applet can be fetched from a server and run in a browser on any computer in the world (if connected), so there must be a way for it to find its data file, its image. The getImage function has two arguments that are joined together to name the image file, the first is a URL, it names the host machine and directory where the file is, and the last part is the file name. The URL could be built in and absolute to a named machine but this is inflexible, the preferred solution is to find out where the applet class came from and get the image from the same place. The function getCodeBase() returns a URL to the directory the applet came from. This works for remote systems needing networking and for local systems when the browser got an applet from the local computer. Next the structure of the applet. An applet is not an ordinary stand-alone program (there is no main), it is run inside a browser (or similar program), this means it can be stopped and started as other pages are visited, also it shares a browser window with other images and HTML stuff. What it does and what happens to it are known as the applet life-cycle: • when the HTML file containing the <applet> tag is first viewed the applet is loaded, at this point the class derived from the JApplet is used to create an applet object. At this time the: public void init() {...} function is called, it is used to do any initialisation, for example finding and fetching the image. Nothing needs to be drawn on the screen yet. • the function: public void start() {...} is called whenever a page containing the applet is visited. The applet might be loaded in the browser but the user has been to other pages and has then gone ”back” to the applet page. If the applet is doing some animation start can contain code to re-start the animation,

2 JAVA GUI APPLICATIONS

3

• the other non-empty function is: public void paint(Graphics g) { this function is called by the surrounding system (usually a browser) whenever it thinks the applet needs to re-draw itself. This can be, for example, when the window is re-sized or when it is uncovered. The argument of type Graphics is associated with the screen that needs re-drawing and its is a way of accessing all the graphical functions, • the function public void stop() is called when the applet can stop execution although it is not being killed. This happens when a browser visits a new page that replaces the one containing the applet, • the function public void destroy() is called only when the applet is removed from the browser. The applet in ImageApplet0.java contains init which fetches the image once when the applet is loaded, and it defines paint to redraw the image whenever the browser thinks it necessary. The other functions start, stop and destroy are not needed by this applet (many applets don’t need them) they have been included as empty “do nothing” functions, however this isn’t really necessary as the parent class JApplet has empty default versions of these functions.

2 Java GUI applications
A Java GUI application is just like an ordinary Java application, it has a main function where it starts, it is stand alone, it is not run inside some other program. The only difference between a GUI application and an non-GUI one is that it creates and makes visible an object based on a JFrame. A visible JFrame object is a window on a graphical interface screen. There are two ways to use a JFrame, either: • create a JFrame object and add graphical interface components to it. This is quite simple but becomes messier and more complicated to manage as more functionality is required of it, or, • to write a new class derived from JFrame and to include the required additional functionality there. Initially this is a bit more complicated for very simple windows but it is more structured and provides access to more some functionality that might be harder to exploit with a simple object. In these notes the earlier, simpler programs use simple objects but from then most of the programs will define new classes that extend JFrame (that means inherit from JFrame).

2.1 An application: TuxApp1.java
This first application just displays an image and a label: import java.awt.*; import javax.swing.*; public class TuxApp1 { public static void main(String args[]) { JFrame window = new JFrame(); Container content = window.getContentPane(); JLabel pic = new JLabel(new ImageIcon("tux-smaller.jpg")); content.add(pic); window.pack(); window.setVisible(true); } } The program is in the file TuxApp1.java and the image in tux-smaller.jpg. When run it displays:

2 JAVA GUI APPLICATIONS

4

Things to note about this program: • The program generates a JFrame object and assigns it to the variable window, it then manipulates it by, in this case, adding an image to it. • All graphical user interface objects in Java applications must be added to some container that can be displayed. The function getContentPane() returns the graphical object container of the JFrame. Unless objects, images, text, or buttons are added to this container (directly of indirectly) they will not appear in the window. See the next program for a different way to add to the content pane. • There are different ways to display an image in a graphical user interface, the previous applet examples used the Graphics drawing routines. This program uses a GUI component called a JLabel, a label can be “labelled” with text or a picture, in this case an image. The difference between drawing and using a GUI component is that all the drawing and re-drawing of components (including images) is handled automatically by the JFrame, it does not need to use the paint function. It adds the component to the content pane. • The operation window.pack() causes the JFrame to calculate the size of all the components added to the content pane, without this the components won’t appear on the window. • Finally it is necessary to call window.setVisible(true), without this the whole window belonging to the JFrame won’t be shown in the screen. The window can be hidden again by calling window.setVisible(false). In older versions of Java making the window visible was done by window.show() but this is now depreceated.

2.2 TuxApp2.java using JPanel
The previous program added a component, a JLabel, directly onto the content pane. This works OK but the built-in content pane is a Container and if more than one item is added to a Container then they “stack” in front of each other, in other words later ones added, hide ones added earlier. So if we wanted a text label next to the image label, to describe it, it wouldn’t work. Instead we can use a more complicated container called a JPanel. A JPanel is just a container, it isn’t visible, it just holds other components, but unlike the primitive Container, when components are added to a JPanel they are laid out side by side. To use our own content pane we declare the variable content and assign it a newly created empty JPanel. The program then generates two labels, one labelled with text and the other “labelled” with an image, these are both added to the JPanel. Now the program sets the frame’s content pane to be the new JPanel using: window.setContentPane(content); so the rule is still true, that you must put components on the coontent pane in order have them in the window but we’ve done it by providing our own content pane, and a better one than the original. Here is the program: import java.awt.*; import javax.swing.*; public class TuxApp2 { public static void main(String args[]) { JFrame window = new JFrame(); JPanel content = new JPanel(); JLabel pic = new JLabel(new ImageIcon("tux-smaller.jpg")); JLabel caption = new JLabel("this is Tux");

2 JAVA GUI APPLICATIONS

5

content.add(caption); content.add(pic); window.setContentPane(content); window.pack(); window.setVisible(true); } } The program is in the file TuxApp2.java and the image in tux-smaller.jpg. notice there are two components side by side: This is what is displayed,

2.3 TuxApp3.java using inheritance
This program is in some ways completely different from the previous program because it uses inheritance, on the other hand, it is almost identical in its functionality, it displays a text label and an image side by side, in fact, the output is not shown because it is the same as the previous one. The new class TuxFrame is defined, it extends JFrame which means it has all the contents, functions and data attributes of a JFrame plus the ones it adds itself. So a TuxFrame will have functions like getContentPane, pack, setVisible etc. This is what inheritance means. The new class will be used in a different way from a plain JFrame, instead of making an object and then poking around at it from outside using operations on it, this program will arrange that when an object of type TuxFrame is created it will build itself. This is achieved by writing all the instructions to create and add the required components inside the constructor of the new class. In Java the constructor of a class is executed automatically when an object is created, the constructor has the same name as the class (in this case TuxFrame), it is public and unlike an ordinary function it has no result type. Whenever an object of this type is created the constructor is executed and it builds the object in the required way. Because we want our new type of object to have various new components, in this case two labels, we also declare data attributes which are like variables inside the object. These declarations are at the “top level” of the class meaning that they are part of the object and will exist as long as the object exists, if the attributes (variables) were inside the constructor they would disappear when the constructor finished. Here is the program: import java.awt.*; import javax.swing.*; class TuxFrame extends JFrame { JPanel content; JLabel pic; JLabel caption; public TuxFrame() { content = new JPanel(); pic = new JLabel(new ImageIcon("tux-smaller.jpg")); caption = new JLabel("this is Tux"); content.add(caption); content.add(pic); setContentPane(content); pack(); } }

3 GRAPHICAL INTERFACE COMPONENTS

6

public class TuxApp3 { public static void main(String args[]) { TuxFrame window = new TuxFrame(); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setVisible(true); } } The program is in the file TuxApp3.java and the image in tux-smaller.jpg. • the class has three data attributes to hold its components: the JPanel, for the new content pane, and two JLabels. • in this program all the work is done in the constructor to build the window components, for this simple program no other functions are required, • notice one important result of building “inside” the object, instead of from outside as before, the same operations are still needed: setting the content pane and packing but they are now called differently. In the previous program a JFrame object was created and assigned to the variable window, then the pane was set and packed with: window.setContentPane(content); window.pack(); now, in the constructor for TuxFrame the same job is accomplished with: setContentPane(content); pack(); Why the difference? This code is being executed “inside” a newly created object, the functions getContentPane and pack are also functions “inside” TuxFrame (inherited from JFrame) so we can call them directly. These functions are inside the same object as the constructor containing the calls not inside some separate object in a variable that requires the variable name and “dot” to find them. • the only task for main is to create a TuxFrame object: TuxFrame window = new TuxFrame(); this will cause execution of the constructor inside the object and the creation of our window with two labels. There is only one of the operations that has been left “outside” the object, ie. it is not in the constructor, and that is the setVisible(true) call. It could have been put inside, in which case the “window.” prefix would be dropped. It has been left out because I thought that while it is appropriate for the new object type TuxFrame to build itself, the decision about whether and when to display it (make it visible) should be left to the code that decides when and if to create it. • There is one other change in this program that is not related to the decision to switch to using inheritance rather than a JFrame object. It is a separate gratuitous decision. The line: setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); means that if the window is closed the program stops. Without this Java will allow the window to be closed but the program is still running, not doing anything useful because it’s lost its display, but still not dead.

3 Graphical interface components
This section just introduces some useful graphical interface components that can be used to build graphical applications. In each case a very simple program is used to display a component. The programs don’t try to do anything useful. In most cases, in order to keep things simple, and because no special functionality is required, they use JFrame objects in main rather than defining new classes by extending JFrame.

3.1 Component JTextArea
A JTextArea is a component that is an area for displaying text, words. It is not really intended for the user to enter or edit text, it is really for display. There are more complicated components that allow text-editing.

3 GRAPHICAL INTERFACE COMPONENTS

7

3.1.1 Program TextAreaApp0.java This version uses a JFrame object: import java.awt.*; import javax.swing.*; class TextAreaApp0 { public static void main(String args[]) { JFrame win = new JFrame(); Container myWindow = win.getContentPane(); JTextArea messages = new JTextArea(12,20); messages.append("This is a message\n"); messages.append("This is another message\n"); messages.append("Yet another message\n"); myWindow.add(messages); win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); win.pack(); win.setVisible(true); } } The program is in the file TextAreaApp0.java. Running TextAreaApp0.java:

Notes: • This uses the simple JFrame structure, just an object, no inheritance, • a JTextArea is a GUI component that can just hold text. You can append strings to it, • when it can created it is given an initial numbers of rows and columns. • this very simple program is only adding one component to the window’s content pane so instead of creating a JPanel and setting that as the content pane, it uses the existing content pane. To do this it calls getContentPane() and just adds its single component textarea to that. 3.1.2 Program TextAreaApp1.java This version defines a new class using inheritance but the functionality should be similar to the previous program. import java.awt.*; import javax.swing.*; class TextFrame extends JFrame { Container myWindow; JTextArea messages; public TextFrame() {

3 GRAPHICAL INTERFACE COMPONENTS

8

myWindow = getContentPane(); messages = new JTextArea(12,20); messages.append("This is a message\n"); messages.append("This is another message\n"); messages.append("Yet another message\n"); myWindow.add(messages); pack(); } } class TextAreaApp1 { public static void main(String args[]) { TextFrame win = new TextFrame(); win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); win.setVisible(true); } } The program is in the file TextAreaApp1.java.

3.2 Component JScrollPane
A JScrollPane is not really a separate component, it is a wrapper to put around other components to allow a small window to scoll over a larger object. So for example if a JTextArea requires a small window but has lots of lines added to it, it can be surrounded by a JScrollPane. 3.2.1 Program ScrollApp0.java Using a simple JFrame object in main: import java.awt.*; import javax.swing.*; class ScrollApp0 { public static void main(String args[]) { JFrame win = new JFrame(); Container myPane = win.getContentPane(); JTextArea messages = new JTextArea(12,20); JScrollPane scroller = new JScrollPane(messages); myPane.add(scroller); for(int i=0; i<100; i++) { messages.append("message no: " + i + "\n"); } win.pack(); win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); win.setVisible(true); } } The program is in the file ScrollApp0.java. Running ScrollApp0.java:

4 LAYOUT MANAGERS

9

Notes • there is still a JTextArea to hold all the lines, it is still necessary to append strings to it, • but now there is a JScrollPane and the JTextArea is put inside it (it is given as a parameter to the constructor of the JScrollPane when that is created), • now the JScrollPane, not the JTextArea, is added to the frame’s content pane, • without a scroller around a JTextArea it would grow as more lines were added to it, this could make a mess of the window. 3.2.2 Program ScrollApp1.java Using a new class: class ScrollFrame extends JFrame { Container myPane; JTextArea messages; JScrollPane scroller; public ScrollFrame() { myPane = getContentPane(); messages = new JTextArea(17,20); scroller = new JScrollPane(messages); myPane.add(scroller); for(int i=0; i<100; i++) { messages.append("message no: " + i + "\n"); } pack(); } } class ScrollApp1 { public static void main(String args[]) { ScrollFrame win = new ScrollFrame(); win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); win.setVisible(true); } } The program is in the file ScrollApp1.java.

4 Layout managers
A layout manager is a hidden, invisible component that can be added to a container to control the way that multiple components are positioned in the container. So depending on the layout manager when lots of components are added to a container they might be positioned differently. There are quite a few layout managers in Java:

4 LAYOUT MANAGERS

10

• BorderLayout, allows the position of items relative to the edges of the container, • BoxLayout, allows fixed vertical or horizontal positioning, • CardLayout, a layout that overlays components but allows the currently visible component to be changed, • FlowLayout, the simplest, it just puts one component after another, the components are added left to right and warp round to another row depending on the dimensions of the container, • GridLayout, has a fixed number of rows and columns and adds components to the cells of the grid in order, the cells of the grid are all the same fixed size, • GridBagLayout, a much more complicated version of a GridLayout that allows cells to be different sizes, • and others. This section will only consider the FlowLayout and BorderLayout managers.

4.1 FlowLayout manager
This is the simplest manager, it is the one present in JPanel by default. 4.1.1 LayOutApp0.java This program demonstrates the effect of just adding some components to flowlayout manager without the size being given. import java.awt.*; import javax.swing.*; public class LayoutApp0 {

public static void main(String args[]) { JFrame win = new JFrame(); Container myPane = win.getContentPane(); myPane.setLayout(new FlowLayout()); myPane.add(new JButton(" <One> ")); myPane.add(new JButton(" <Two> ")); myPane.add(new JButton("<Three>")); myPane.add(new JButton(" <Four>")); myPane.add(new JButton(" <Five> ")); myPane.add(new JButton(" <Six> ")); win.pack(); win.setVisible(true); } } The program is in the file LayoutApp0.java. Running LayoutApp0.java:

Notes: • Layout managers are added to containers (JPanels or content panes) to allow many GUI components to be added. Adding a FlowLayout manager to the standard content pane returned by getContentPane() makes it function like a JPanel. • FlowLayout adds one component after another, left to right then top to bottom, in this case because the size isn’t given the window is long and thin.

4 LAYOUT MANAGERS

11

4.1.2 LayOutApp1.java This time the program specified a preferred size for the JFrame and consequently for the container inside so that when components are added the FlowLayout manager positions them differently. import java.awt.*; import javax.swing.*; public class LayoutApp1 {

public static void main(String args[]) { JFrame win = new JFrame(); Container myPane = win.getContentPane(); myPane.setLayout(new FlowLayout()); myPane.add(new JButton(" <One> ")); myPane.add(new JButton(" <Two> ")); myPane.add(new JButton("<Three>")); myPane.add(new JButton(" <Four>")); myPane.add(new JButton(" <Five> ")); myPane.add(new JButton(" <Six> ")); win.setPreferredSize(new Dimension(200,140)); win.pack(); win.setVisible(true); } } The program is in the file LayoutApp1.java. Running LayoutApp1.java:

4.2 The BorderLayout manager
The BorderLayout manager allows components to added to fixed positions relative to the edges of the container: • BorderLayout.NORTH, at the top, • BorderLayout.SOUTH, at the bottom, • BorderLayout.EAST, at the right edge, • BorderLayout.WEST, at the left edge, • BorderLayout.CENTER, in the middle. 4.2.1 LayOutApp2.java This program demonstrates items being added to all the five possible positions: import java.awt.*; import javax.swing.*; public class LayoutApp2 { public static void main(String args[]) { JFrame win = new JFrame();

4 LAYOUT MANAGERS

12

Container myPane = win.getContentPane(); myPane.setLayout(new BorderLayout(5,5)); // 5,5 are x and y space between myPane.add(new myPane.add(new myPane.add(new myPane.add(new myPane.add(new JButton(" <One> "),BorderLayout.NORTH); JButton(" <Two> "),BorderLayout.SOUTH); JButton("<Three>"),BorderLayout.EAST); JButton(" <Four>"),BorderLayout.WEST); JButton(" <Five>"),BorderLayout.CENTER);

win.pack(); win.setVisible(true); } } The program is in the file LayoutApp2.java. Running LayoutApp2.java:

Notes: • the BorderLayout allows you to place GUI in specific positions, it has positions NORTH, SOUTH, EAST, WEST, and CENTER, • when the BorderLayout is created and assigned to a pane you can select the size of the borders between the components, • borderlayouts behave in special ways when resized, try dragging a corner, it is usually the CENTER component that varies in size. 4.2.2 LayoutApp3.java This program shows that it is not necessary to use all the positions in a BorderLayout before packing it and displaying it: import java.awt.*; import javax.swing.*; public class LayoutApp3 {

public static void main(String args[]) { JFrame win = new JFrame(); Container myPane = win.getContentPane(); myPane.setLayout(new BorderLayout(5,5)); JButton top = new JButton(" <One> "); JButton mid = new JButton(" <Two> "); JButton bot = new JButton("<Three>"); bot.setPreferredSize(new Dimension(150,50)); myPane.add(top, BorderLayout.NORTH); myPane.add(mid, BorderLayout.CENTER); myPane.add(bot, BorderLayout.SOUTH); win.pack(); win.setVisible(true); } }

4 LAYOUT MANAGERS

13

The program is in the file LayoutApp3.java. Running LayoutApp3.java:

Notes: • You don’t need to use all the five positions in a border layout, this program just uses three, • also the size of all the components is affect if you set the preferred size of one of them, but notice that because they are stacked vertically, it has affected the width but not the height of the other components, 4.2.3 LayoutApp4 This program shows different types of component being added to a BorderLayout manager. It also shows the effect on the others of specifying the size of one. import java.awt.*; import javax.swing.*; public class LayoutApp4 { public static void main(String args[]) { JFrame win = new JFrame(); Container myPane = win.getContentPane(); myPane.setLayout(new BorderLayout(5,5)); JLabel title = new JLabel("An example"); JTextArea ta = new JTextArea(10,20); JScrollPane sp = new JScrollPane(ta); JButton but = new JButton("Quit"); for(int i=0;i<12;i++) ta.append("hahaha\n"); myPane.add(title, BorderLayout.NORTH); myPane.add(sp, BorderLayout.CENTER); myPane.add(but, BorderLayout.SOUTH); win.pack(); win.setVisible(true); } } The program is in the file LayoutApp4.java. Running LayoutApp4.java:

4 LAYOUT MANAGERS

14

Notes: • not all the components in a container have to be of the same type, here there are a JLabel, a JScrollPane, and a JButton, • the same rules of sizing and resizing apply but when mixed with larger objects some of the “smaller” are enlarged too—be careful.

4.3 Combining layout managers: LayoutApp5
Sometimes it is useful to combine layout managers and containers to achieve the desired effect. If for example it is necessary to have a structured layout using a BorderLayout manager but to put two JButtons at the bottom (SOUTH) then create a single JPanel (with flow layout) and put that at the bottom. Then into that panel put the two buttons. It is a container in a container. This solution can be used in many other ways. import java.awt.*; import javax.swing.*; public class LayoutApp5 { public static void main(String args[]) { JFrame win = new JFrame(); Container myPane = win.getContentPane(); myPane.setLayout(new BorderLayout(5,5)); JLabel title = new JLabel("An example"); JTextArea ta = new JTextArea(10,20); JScrollPane sp = new JScrollPane(ta); JPanel buttonPanel = new JPanel(); // by default a JPanel has a FlowLayout manager so: // buttonpanel.add(new FlowLayout()) // is not needed JButton saveBut = new JButton("Save"); JButton quitBut = new JButton("Quit"); buttonPanel.add(saveBut); buttonPanel.add(quitBut); for(int i=0;i<12;i++) ta.append("hahaha\n"); myPane.add(title, BorderLayout.NORTH); myPane.add(sp, BorderLayout.CENTER); myPane.add(buttonPanel, BorderLayout.SOUTH); win.pack(); win.setVisible(true); } }

5 EVENT DRIVEN PROGRAMMING

15

The program is in the file LayoutApp5.java. Running LayoutApp5.java:

There is also another advantage with putting JPanels into a BorderLayout (or a GridLayout) and that is that with a JPanel components keep their preferred size and “float” inside the panel, but with the BorderLayout components are stretched to fill the required size of the border layout cell.

5 Event driven programming
• applications with graphical interfaces have different structure and behaviour from other applications, • the normal operation is: – build the interface, – wait for user input from mouse or keyboard, – deal with whatever input occurred, – wait again . . . • unlike ordinary applications these GUI programs cannot control the I/O, they must be prepared to deal with whatever type of input occurs: mouse click, button press, menu selection, or text input to a box. The following pathetic picture tries to show the difference between event-driven and non-eventdriven programs:
main main

functions

functions

The picture on the left shows the flow of control (sequence of execution) of a non-event-driven program, on the right an event-driven program. Note: • the non-event-driven program starts in main, then the program decides which functions to call and it retains control of what is happening all the way to the end, • in contrast the event-driven profram usually creates the GUI objects by calling constructors and then the main program path of execution terminates and the program waits. From then on all execution of functions is caused by user keyboard or mice events,

5 EVENT DRIVEN PROGRAMMING

16

• in the first diagram the small broken line indicates input or output, in a non-event-driven program this only occurs when the path of execution of the program chooses to execute I/O statements. If the user types anything nothing will happen (it will probably be buffered by the operating system) until the program decides to get the input, • in contrast, in the event-driven program mouse or keyboard activity immediately trigger execution of functions in the program.

5.1 Handling “events” in Java
• Java handles all GUI events in a similar way, • there must be a “handler” function to deal with each event, for example text being entered, of a quit button being pressed, • this function is then linked to the source of the event, the text field, or the button, • then the program waits, • when the event occurs the “handler” is executed, • linking GUI component events to handlers is a bit complicated, handlers can only exist in objects and you tell the GUI component which object contains the handler you do not tell it the name of the function (see example).

5.2 ButtonEventApp1.java
import java.awt.*; import java.awt.event.*; import javax.swing.*; class ButtonFrame extends JFrame implements ActionListener Container myPane; JButton but; JTextArea text; int bcount; public ButtonFrame() { bcount = 0; myPane = getContentPane(); myPane.setLayout(new FlowLayout()); text = new JTextArea(10,15); myPane.add(text); but = new JButton("Press Me"); myPane.add(but); but.addActionListener(this); setPreferredSize(new Dimension(200,240)); pack(); } public void actionPerformed(ActionEvent e) { bcount++; text.append(e.getActionCommand()+" button pressed "+bcount+"\n"); } } public class ButtonEventApp1 { public static void main(String args[]) { ButtonFrame win = new ButtonFrame(); win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); win.setVisible(true); } } The program is in the file ButtonEventApp1.java. Running ButtonEventApp1.java: {

5 EVENT DRIVEN PROGRAMMING

17

Notes: • The handler for button events must always be called actionPerformed and it takes a parameter of type ActionEvent giving details of the event. The programmer must provide this function. • the next task is to inform the Java runtime system about the event handler and associate it with the button. This is not done directly, instead it is necessary to tell the button which object contains the event handler function: but.addActionListener(this); this line means: tell the button but that the ActionListener (that’s the object containing the function) for it to use is “this”, which is the main frame object that the constructor is in, the current object, • Java requires that any class containing an actionPerformed function must implement ActionListener: class ButtonFrame extends JFrame implements ActionListener { • the button generates events when it is clicked, these events are detected by the Java virtual machine that looks for the correct event handler. • The actionPerformed function takes an object that contains information about the event, if the event parameter is e then e.getActionCommand() returns the name (label) of the button, • in this program when the handler is executed it increments a counter and and adds a message to a textarea in the window.

5.3 ButtonEventApp2.java
This program just illustrates how a program handles events from more than one source. This program has three buttons. Once again there must be a function called actionPerformed, each button must be told that the current object contains the function. But when any button causes actionPerformed to be executed how does the code determine which button it was? The function e.getSource(), applied to the ActionEvent parameter returns the object causing the event, this can be compared with the variables holding the button objects. class ButtonFrame extends JFrame implements ActionListener Container myPane; JButton but1, but2, but3; JTextArea text; public ButtonFrame() { myPane = getContentPane(); myPane.setLayout(new FlowLayout()); text = new JTextArea(10,22); but1 = new JButton("Press Me"); myPane.add(text); myPane.add(but1); {

5 EVENT DRIVEN PROGRAMMING

18

but2 = new JButton("PANIC"); myPane.add(but2); but3 = new JButton("Quit"); myPane.add(but3); but1.addActionListener(this); but2.addActionListener(this); but3.addActionListener(this); setPreferredSize(new Dimension(280,230)); pack(); } public void actionPerformed(ActionEvent e) { if(e.getSource() == but1) text.append("button 1 pressed\n"); else if(e.getSource() == but2) text.append("button 2 pressed\n"); else if(e.getSource() == but3) System.exit(0); else text.append("don’t know what was pressed\n"); } } public class ButtonEventApp2 { public static void main(String args[]) { ButtonFrame win = new ButtonFrame(); win.setVisible(true); } } The program is in the file ButtonEventApp2.java. Running ButtonEventApp2.java:

5.4 ButtonEventApp5.java
This program shows that it is possible to create a separate special object to handle events, it doesn’t have to be the main frame object. import java.awt.*; import java.awt.event.*; import javax.swing.*; class ButtonFrame extends JFrame {

private class MyActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { bcount++; text.append(e.getActionCommand()+" button pressed "+bcount+"\n"); } } Container myPane; JButton but;

5 EVENT DRIVEN PROGRAMMING

19

JTextArea text; int bcount; public ButtonFrame() { bcount = 0; myPane = getContentPane(); myPane.setLayout(new FlowLayout()); MyActionListener listen = new MyActionListener(); text = new JTextArea(10,15); myPane.add(text); but = new JButton("Press Me"); myPane.add(but); but.addActionListener(listen); setPreferredSize(new Dimension(190,240)); pack(); } } public class ButtonEventApp5 { public static void main(String args[]) { ButtonFrame win = new ButtonFrame(); win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); win.setVisible(true); } } The program is in the file ButtonEventApp5.java. Notes on ButtonEventApp5.java: • this shows an alternative way to handle events, it creates a small, special purpose, private, local class MyActionListener to make an object to handle the event rather than use the main JFrame object, • notice the special class only has the handler inside, • notice that a single object is created: MyActionListener listen = new MyActionListener(); • it would be possible to write three separate local classes and create an object from each and each could have a different handler for three different buttons, however it is easier to use e.getSource()

5.5 TextFieldEventApp0.java
This program illustrates the handling of an event caused by a different component, a JTextField. A JTextField is intended to used for text input from the user. import java.awt.*; import java.awt.event.*; import javax.swing.*; class MyFrame extends JFrame implements ActionListener Container myPane; JButton but; JLabel inLab, res; JTextField inp; public MyFrame() { myPane = getContentPane(); myPane.setLayout(new FlowLayout()); inLab = new JLabel("inches: "); myPane.add(inLab); inp = new JTextField(5); myPane.add(inp); res = new JLabel("cms = 0.0"); myPane.add(res); but = new JButton("Quit"); myPane.add(but); inp.addActionListener(this); but.addActionListener(this); setPreferredSize(new Dimension(130,130)); pack(); }

{

5 EVENT DRIVEN PROGRAMMING

20

public void actionPerformed(ActionEvent e) { if(e.getSource() == inp) { int n = Integer.parseInt(e.getActionCommand()); res.setText( "cms = " + n * 2.54); } else if(e.getSource() == but) System.exit(0); } } public class TextFieldEventApp0 { public static void main(String args[]) { MyFrame win = new MyFrame(); win.setVisible(true); } } The program is in the file TextFieldEventApp0java. Running TextFieldEventApp0.java:

Notes: • a JTextField can hold only one line of text that can be entered by a user, when “enter” is typed it causes an ActionEvent, • the button also causes an ActionEvent so the handler must use e.getSource to find the source of the event, • if it finds it’s the text field then it uses getActionCommand() on its parameter e to get the string entered, (it could instead have called inp.getText()), which is a function to return the text from a JTextField.

5.6 Practical exercise

• the task is to produce a program that will accept a file name in a JTextField, open the file, read the file and display its contents in a scrolled JTextArea.

5 EVENT DRIVEN PROGRAMMING

21

• the program from last week’s practical did nearly all the work, I have provided a listing of program ScrollFileViewer1.java that you can download and modify, • it is just necessary to add JTextField input, use some of the code from TextFieldEventApp0.java as a model, • the major difference will be that you must open and read the file in the actionPerformed event handler, • the first version doesn’t need a “quit” button, that is the second task, • the third task is to try and catch the FileNotFoundException so that if the user mistypes the file name the program will not crash. Download and look at FileCopyExcept.java (also in the notes) for an example of how to catch this exception. 5.6.1 ScrollFileViewer1.java This is a simple solution to the previous exercise (not included here) to display a file in a scrolling text area. It is important because the exercise above can build on this program. import java.io.*; import java.awt.*; import javax.swing.*; class ScrollFrame extends JFrame { Container myPane; JTextArea messages; JScrollPane scroller; public ScrollFrame(String fname) throws Exception { String line; myPane = getContentPane(); messages = new JTextArea(17,20); scroller = new JScrollPane(messages); myPane.add(scroller); BufferedReader inpf=new BufferedReader(new FileReader(fname) ); line = inpf.readLine(); while(line != null) { messages.append(line + "\n"); line = inpf.readLine(); } pack(); } } class ScrollFileViewer1 { public static void main(String args[]) throws Exception { if(args.length != 1) { System.err.println("Need a filename"); System.exit(1); } ScrollFrame win = new ScrollFrame(args[0]); win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); win.setVisible(true); } } The program is in the file ScrollFileViewer1.java.

5.7 ListApp0.java
This program demonstrates use of a JScrollPane containing a JList which in turn contains an array of strings. The idea of using all these components together is that they allow single or multiple selections to be made from a list and for a ValueChanged event to be called, and the selection to be used.

5 EVENT DRIVEN PROGRAMMING

22

class MyFrame extends JFrame implements ListSelectionListener { Container myPane; JTextArea messages; JList langList; JPanel bp; JScrollPane scroller; String listModel[]={ "Ada","Algol60","Algol68","APL","bash","Basic","BCPL","C", "C++","Cobol","CLU","CSP","Dylan","Eiffel","Forth", "Fortran","Haskell","Hope","Icon","Java","Javascript", "Lisp","Logo","Lua","Miranda","Modula","Modula2","Modula3", "Oberon","Occam","Pascal","Perl","PHP","PL/1","Pop-2", "Postscript","Prolog","Sather","SASL","Scheme","Simula-67"} public MyFrame() { myPane = getContentPane(); myPane.setLayout(new BorderLayout(5,5)); langList = new JList(listModel); langList.addListSelectionListener(this); scroller = new JScrollPane(langList); scroller.setPreferredSize(new Dimension(100,300)); myPane.add(scroller,BorderLayout.CENTER); bp = new JPanel(); messages = new JTextArea(5,18); bp.add(messages); myPane.add(bp,BorderLayout.SOUTH); setPreferredSize(new Dimension(240,420)); pack(); } public void valueChanged(ListSelectionEvent e) { if (! e.getValueIsAdjusting()) { messages.setText("you selected:"); int sels[]=langList.getSelectedIndices(); for(int i=0;i<sels.length;i++) { if(i % 4 == 0) messages.append("\n"); messages.append(listModel[sels[i]]+", "); } } } } public class ListApp0 { public static void main(String args[]) { MyFrame win = new MyFrame(); win.setVisible(true); } } The program is in the file ListApp0.java. Running ListApp0.java:

5 EVENT DRIVEN PROGRAMMING

23

Notes: • this program uses a JList. A list can contain an array of strings (called its data model) and allows one or more of the entries in the list to be selected, • there is another type of event, a ListSelectionEvent, when a selection is made the event is triggered and the handler valueChanged (in a class object that implements ListSelectionListener) is called, • the function in JList called getSelectedIndices() returns an array of the indices in the list that are selected, • sometimes random events are called during selection, these can be avoid by checking if (! e.getValueIsAdjusting()). WARNING: JLists and their models are much more complicated than this note suggests, changing or modifying a list requires, at least, a DefaultListModel, so read more if you are going to use one.

5.8 Mouse events MouseEventsApp3.java
Like button events, textfield events and list events it is possible to detect mouse events. Unfortunately these are quite complicated, different types of event are generated, when the mouse moves, which is continually occurring, when mouse buttons are depressed, when they are released, when the mouse leaves or re-enters the window etc. • The events are handled by two different sorts of “listener” which means that to have the main frame object handle all of the events it must “implement” two things MouseListener and MouseMotionListener. • then there are differently named functions to handle different types of mouse event, for example mousePressed or mouseMoved. If the class is to be a listener it must provide functions for all the events even though it might not be interested in them all, if so it must still have empty functions. • another point is that there is no “mouse object” generating events unlike a button object. So what object to we call addMouseMotionListener(this) or addMouseListener(this) on? We call them inside ourseleves with no object “.” prefix.

5 EVENT DRIVEN PROGRAMMING

24

class MouseFrame extends JFrame implements MouseListener, MouseMotionListener { static final String message = "Move, drag or click mouse"; JLabel report, messageLabel; Container myWin; public MouseFrame() { myWin = getContentPane(); myWin.setLayout(new BorderLayout()); messageLabel = new JLabel(message); myWin.add(messageLabel, BorderLayout.NORTH); report = new JLabel(); myWin.add(report, BorderLayout.SOUTH); addMouseMotionListener(this); addMouseListener(this); setPreferredSize(new Dimension(250,180)); pack(); } public void mousePressed(MouseEvent e) { reportEvent("Mouse down " + e.getX()+ " " + e.getY()); } public void mouseReleased(MouseEvent e) { reportEvent("Mouse up " + e.getX()+ " " + e.getY()); } public void mouseClicked(MouseEvent e) { reportEvent("Mouse clicked " + e.getX()+ " " + e.getY()); } public void mouseEntered(MouseEvent e) { reportEvent("Mouse entered"); } public void mouseExited(MouseEvent e) { reportEvent("Mouse exited"); } public void mouseDragged(MouseEvent e) { reportEvent("Mouse dragged at: " + e.getX()+ " " + e.getY()); } public void mouseMoved(MouseEvent e) { reportEvent("Mouse moved at: " + e.getX()+ " " + e.getY()); } private void reportEvent(String m) { report.setText(m); } } public class MouseEventsApp3 { public static void main(String args[]) { MouseFrame win = new MouseFrame(); win.setVisible(true); } } The program is in the file MouseEventsApp3.java.