0% found this document useful (0 votes)
3 views

8. JavaFX Event Handling

This document introduces event-driven programming in GUI applications, explaining how user actions like button clicks trigger events that require specific handlers to process them. It details the creation of event handlers, the registration process with event sources, and the use of inner classes for organizing handler logic. Additionally, it discusses the concept of anonymous inner classes as a way to simplify event handler creation.

Uploaded by

mustafawalid304
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

8. JavaFX Event Handling

This document introduces event-driven programming in GUI applications, explaining how user actions like button clicks trigger events that require specific handlers to process them. It details the creation of event handlers, the registration process with event sources, and the use of inner classes for organizing handler logic. Additionally, it discusses the concept of anonymous inner classes as a way to simplify event handler creation.

Uploaded by

mustafawalid304
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 87

Section 15.

1
§15.1 Introduction
 Suppose you want
to develop a GUI
application to
calculate loan
payments:
 The user should
be able type the
pertinent values into the corresponding boxes, and
then click “calculate”
 How can our program tell when the “Calculate”
button has been pressed (clicked on), so we can
run the code to do the calculation?
§15.1 Introduction
 Let’s run through a simple example to get a taste
of what’s involved. We’ll create a stage with two
buttons – “OK” and “Cancel”
 When the “OK” button is clicked, we’ll output “OK
Button Clicked” on the console
 When the “Cancel” button is clicked, we’ll output
“Cancel button clicked” on the console
§15.1 Introduction
 So far, all we have written is procedural code
 The program’s flow execution is determined by the
program’s structure (and perhaps its input)
 In event-driven code, the user is responsible for
determining what happens next
 GUIs use event-driven programming to respond
to events:
 Clicking on a button
 Selecting a check box or a radio button
 Clicking on a scroll bar
§15.1 Introduction
 To respond to a button click, you need to write
the code to process the button-clicking action.
 The button is an event source object – where the
action originates.
 You need to create an object capable of handling
the action (click) event on a button.
 This object is called an event handler, as shown
in Figure 15.3 (below)
§15.1 Introduction
 Not just any ol’ object can be a handler for an
action event. To be an action event handler:
1. The object must implement the interface
EventHandler<T extends Event>, which defines
the common behavior for all action handlers
2. The EventHandler object handler must be
registered with the event source object using the
source.setOnAction(handler) method
 The EventHandler<ActionEvent> interface
contains the method handle(ActionEvent) for
processing the event -- your handler class must
override this method to respond to the event
§15.1 Introduction
 Listing 15.1 (p. 587) is an event-driven application
that we can use to explore the components that
have to be in place for event-driven code to work

 Normally, I would just refer you to this code, and


count on you to walk through it, and the book is
usually pretty good about explaining the code
examples.

 However, because this is our first piece of event-


handling code, let’s go through this one in small
steps…
§15.1 Introduction
There’s a LOT going on in this
code, and it’s worth carefully
exploring some of the details
§15.1 Introduction
Remember, @Override exists to
help keep us from making a
mistake that could be hard to find
without it

Suppose a typo left us with


public void Handle
rather than
public void handle

We would get a public void


method called Handle, but it
would not override handle in the
EventHandler interface!
§15.1 Introduction
Second, these two handler
classes (OKHandlerClass and
CancelHandlerClass) are
defined in the same .java file as
the main (Application) class

Early in the semester, I said “one


class, one .java source file”.
Well, that’s not entirely accurate.

Only one class in a .java file can


be public, but you can have
“helper classes” in the same file
as long as they’re not public.

Since these classes don’t have


any visibility modifier, they have
the default visibility - package
§15.1 Introduction
Third, these two handler classes’
handle methods implement (i.e.,
are where we put) the
functionality we want executed
when the (click) event occurs

The first one announces (on the


console) that the “OK” button was
clicked
The second one announces (also
on the console) that the “Cancel”
button was clicked
§15.1 Introduction
Fourth, the creation of the GUI is
just like what we did in Chapter 14:
Our class extends Application
We create a Pane to contain our UI
components (an HBox in this case)
Create the components (Buttons),
and add them to the Pane
Finally, we add the Pane to a
Scene, and add the Scene to the
Stage, and then show the Stage
In procedural code, the program
ends when main() ends; however,
in event-driven code, the program
ends when the GUI closes!
§15.1 Introduction
Finally, the connection between
the UI components and their
event handlers is accomplished
by registering the handlers

First, we instantiate the handlers


(the handlers have to exist before
they can “answer the phone”)

Second, we tell the btOK and


btCancel buttons who to “call”
when an event occurs (handler1
and handler2, respectively)
Section 15.2
§15.2 Events and Event Sources
 In event-driven programming, events drive
(determine) the program’s execution
 An event is a signal (message) that something
has happened
 Some events (like button clicks, key presses,
mouse movements) are triggered by user action
 Some events (like timer ticks) can be generated
by internal program activities
§15.2 Events and Event Sources
 In one sense, events are like exceptions:
 They’re objects (in java) that correspond to
something happening
 An exception object is created (and thrown) when a
problem occurs in our code (an exception). Who
“gets” the exception object depends on whether we’re
in a try/catch block, or if we’re going to throw it on
elsewhere
 An event object is created (and fired) when something
happens in the UI code. Who “gets” the event is
whoever is registered as a handler for the object that
generated the event (the source)
§15.2 Events and Event Sources
 EventObject hierarchy:

 EventObject has a getSource() method (so


do its descendants), so an event handler can tell
who generated an event it receives (caller ID)
§15.2 Events and Event Sources
 Table 15.1 (p. 589) has a partial listing of some User
Actions, UI Components, events, and handlers
What’s most important for you to take away from this table is
that different KINDS of source objects (GUI elements) generate
different KINDS of events (e.g., buttons get clicked; not moved,
like the mouse), and different kinds of events require different
kinds of handlers.

Although they both answer the phone and then provide some
service, if the event is “Somebody is robbing the bank”, I want a
“police” KIND of handler, rather than a “customer service” KIND
handler.

If the event is “I want to order a dozen golf balls”, I don’t need


the SWAT team; I need a customer service handler
Section 15.3
§15.3 Registering Handlers & Handling Events
 In order for an object to be a handler:
 It must implement (be an instance of) the appropriate
event handler interface for the kind of event it handles
○ EventHandler<ActionEvent> must be implemented by
Button event handlers, because Buttons will fire
ActionEvents. The EventHandler<ActionEvent>
interface forces us to provide handle(ActionEvent e)
 It (the handler) must be registered to the source object
(the method to do the registration depends on the kind
of event it’s registering; for an ActionEvent, it’s
setOnAction; for a keypress, it’s setOnKeyPressed)
 When an event occurs, the source notifies (sends a
message or “places a phone call” to) every listener
registered by invoking its handler method
§15.3:Registering Handlers & Handling Events
 Let’s revisit how this worked in the previous
program (Listing 15.1, p. 587):

// Create the button line 16):


Button btOK = new Button("OK");

// Create handler to receive button’s events (18):


OKHandlerClass handler1 = new OKHandlerClass();

// Register the handler with the button (line 19)


// This tells the button where to send ActionEvents
btOK.setOnAction(handler1);
§15.3:Registering Handlers & Handling Events
 The text walks through the incremental build of a
program to display a circle, and a GUI with two
buttons – one to enlarge the circle, and one to
shrink the circle (Fig. 15.6):

 First, we write the code to create our GUI by


extending Application, adding a Pane, and
adding the (inert for now) Buttons to the UI:
§15.3 Registering Handlers & Handling Events
Our program starts by extending
Application, and overriding the start
method, where we build the UI

The Circle goes into a StackPane

The two Buttons get created and put into


an HBox pane

The StackPane with the Circle goes into


the middle section of a BorderPane, and
the HBox with the two Buttons goes into
the bottom section.

Finally, add the BorderPane to the Scene,


and the Scene to the Stage, and make
the Stage visible

That gets the UI created, but nothing is


configured to handle events (yet)
§15.3:Registering Handlers & Handling Events
 This code is being written incrementally (which is
often a good way to write code; rarely can we
see everything we need to do from the starting
line).
 So, what’s next?
 Since the circle need to be able to respond to
requests to resize, it should have enlarge and
shrink methods
 Since the resizable circle will have its own
methods, it should be its own class, rather than
just being a shape inside some other class
§15.3 Registering Handlers & Handling Events
The new CirclePane class will be an extension
of a StackPane that contains the circle

Again, because we are not defining a second


public class, this package-level-visibility class
can reside in the same .java source file.

Because it’s an extension of the StackPane, the


CirclePane can access its own list of children,
and add a circle in its constructor

The CirclePane’s enlarge and shrink


methods simply add or subtract 2 to the circle’s
current radius. In the shrink method, it checks
before subtracting 2 to make sure it’s over 2 (the
radius can’t go negative, and it’s probably a good
idea to keep it from going to zero, so > 2, as
opposed to >= 2, is a wise idea.
§15.3 Registering Handlers & Handling Events
Armed with our new, fully-functional CirclePane
class, we can just make it a field of the
ControlCircle class. We instantiate it as we
declare it

When we build the BorderPane, we add the


CirclePane to its center section, just a before
§15.3 Registering Handlers & Handling Events
Now things get a bit more interesting.

We know that the Buttons will need a class they


can fire their events to.

We create the EnlargeHandler class to handle


the ActionEvents that will come from the
“Enlarge” Button.

The EnlargeHandler class has a handle


method, which will only have to call the
CirclePane’s enlarge method.

Look closely, and you’ll realize that the


ControlCircle class runs from line 14 to 49

The EnlargeHandler class runs from line 43


through line 48 – it’s INSIDE the ControlCircle
class!

WHAT !?!
§15.3 Registering Handlers & Handling Events
So far, classes have contained fields and
methods, period.

In this example, we have a class that also


contains another class. This nesting of classes
is called creating an inner class. We’ll talk more
about this in the next section.

One of the reasons to have the inner class is


related to variable scope. Because circlePane
is a field, its scope is class-wide, so inside the
EnlargeHandler’s handle method, circlePane
is in-scope, so we can see it to call its enlarge
method
§15.3 Registering Handlers & Handling Events
So, now that we have a handler class for our
“enlarge” Button, we need to instantiate it and
tell the button to send its ActionEvents to it.

Line 29 does both in one shot

This code, as-written, doesn’t have any provision


for the shrink button.

The book leaves that as an exercise, but by now,


it should be obvious what we need to do:

Create a second inner class, ShrinkHandler,


that works just like EnlargeHandler, except that
its handle method should call the circlePane’s
shrink method, rather than its enlarge method.

The other thing we need to do is to instantiate


the ShrinkHandler class, and register it with the
“shrink” Button.

This is just a copy of line 29, except that it’s


btnShrink and new ShrinkHandler()
Section 15.4
§15.4 Inner Classes
 An inner class (which we used in the preceding
section without really talking about) is a class
defined within the scope of another class
 They are sometimes called nested classes.

 Inner classes are useful for defining listener


(event-handling) classes
 The class within which the inner class is defined
is called, appropriately enough, an outer class
 Normally, you define an inner class when it will
be used by only the outer class
§15.4 Inner Classes
 Inner classes can help make your source files
easier to manage
public class Test public class Test
{ {
… …
} // inner class
Test.java → Test.class class A
{

}
public class A }
{
Test.java → Test.class

and → Test$A.class
}
A.java → A.class
§15.4 Inner Classes
 The inner class can reference fields and
methods of the outer class directly, so you don’t
need to pass references from the outer class to
the inner class
 Avoiding this potentially cumbersome reference issue
can make your programs more simple and concise.
 “A listener class is designed specifically to create
a handler object for a GUI component (e.g., a
button). The handler class will not be shared by
other applications and therefore is appropriate to
be defined inside the main class as an inner
class.”
§15.4 Inner Classes
 See the other rules and nuances regarding inner
classes on p. 594.
 For our purposes, most of these are immaterial
 We will be using inner classes to create handlers
for UI elements in the outer class.
Section 15.5
§15.5 Anonymous Inner Classes
 “An anonymous inner class is an inner class
without a name. It combines defining an inner
class and creating an instance of the class into
one step”
 Uhhhh?
 Back when we introduced arrays, we had and
example of creating an anonymous array to pass
to a method (next slide):
§7.6 Passing Arrays to Methods
public static void printArray(int[] array)
{
for (int i = 0; i < array.length; i++)
System.out.print(array[i] + " ");
}

Invoke the method:

int[] list = {3, 1, 2, 6, 4, 2};


printArray(list);

Invoke the method:


printArray(new int[] {3, 1, 2, 6, 4, 2});

“Anonymous array”
§15.5 Anonymous Inner Classes
 An anonymous inner class is handled pretty
much the same way
 Rather than having the definition of the class (the
code that makes up the class) one place,…
 …a declaration of an instance variable and an
instantiation (with new) someplace else, just so
we can pass a reference to an instance of the
class to some other method, …
 We can use an anonymous inner class.
§15.5 Anonymous Inner Classes
 We have seen anonymous objects (such as
.setForeground(new Color(Color.RED)),
which perform an instantiation and pass the
reference to some method, rather than storing it
in a reference variable and passing that.
 An anonymous inner class combines a class’s
definition and instantiation in one step:
§15.5 Anonymous Inner Classes
public void start(Stage primaryStage)
{
// Several Lines Omitted
btnEnlarge.setOnAction(new EnlargeHandlner());
}

// EnlargeHandler as an inner class


class EnlargeHandler implements EventHandler<ActionEvent>
{
@Override
public void handle(ActionEvent e)
{
circlePane.enlarge();
}
}
§15.5 Anonymous Inner Classes
public void start(Stage primaryStage)
{
// Several Lines Omitted
btnEnlarge.setOnAction(new EnlargeHandlner());
}

// EnlargeHandler as an inner class


class EnlargeHandler implements EventHandler<ActionEvent>
{
@Override
public void handle(ActionEvent e)
{
circlePane.enlarge();
}
}
§15.5 Anonymous Inner Classes
public void start(Stage primaryStage)
{
// Several Lines Omitted
btnEnlarge.setOnAction(new EnlargeHandlner());
}

// EnlargeHandler as an inner class


class EnlargeHandler implements EventHandler<ActionEvent>
{
@Override
public void handle(ActionEvent e)
{
circlePane.enlarge();
}
}
§15.5 Anonymous Inner Classes
public void start(Stage primaryStage)
{
// Several Lines Omitted
btnEnlarge.setOnAction(new EnlargeHandlner());
}

// EnlargeHandler as an inner class


class EnlargeHandler implements EventHandler<ActionEvent>
{
@Override
public void handle(ActionEvent e)
{
circlePane.enlarge();
}
}
§15.5 Anonymous Inner Classes
public void start(Stage primaryStage)
{
// Several Lines Omitted
btnEnlarge.setOnAction(new EventHandler<ActionEvent>
{
@Override
public void handle(ActionEvent e)
{
circlePane.enlarge();
}
});
}
§15.5 Anonymous Inner Classes
 Since an anonymous inner class is a special kind
of inner class, it is treated like an inner class with
the following four features:
1. An anonymous inner class must always extend a
superclass or implement an interface, but it cannot
have an explicit extends or implements clause.
2. An anonymous inner class must implement all the
abstract methods in the superclass or in the interface
(it must “go concrete”).
§15.5 Anonymous Inner Classes
 Since an anonymous inner class is a special kind
of inner class, it is treated like an inner class with
the following four features:
3. An anonymous inner class always uses the no-arg
constructor from its superclass to create an instance.
If an anonymous inner class implements an
interface, the constructor is Object().
4. An anonymous inner class is compiled into a class
named OuterClassName$n.class. For example, if
the outer class Test has two anonymous inner
classes, they are compiled into Test$1.class and
Test$2.class.
§15.5 Anonymous Inner Classes
 In Listing 15.4 (pp. 595-596, and next slide),
Liang shows the code for an application with four
buttons.
 When each button is clicked, it produces output
on the console (although we can certainly have
its click event handler do anything we want):
§15.5 Anonymous Inner Classes
I’ve omitted the import statements from the top

Lines 14 – 21 create an HBox and four Buttons,


which it adds to the HBox.

The “New” button (btnNew) needs to be


registered with the object that will receive (and
handle) its ActionEvents.

That object will have to be an instance of a class


that implements EventHanlder<ActionEvent>

That’s precisely what the blue-shaded code in


lines 24 – 29 IS

The same format is repeated to register an


anonymous inner EventHandler<ActionEvent>
class for btnOpen, btnSave, and btnPrint

By “embedding” the handler classes inside the


class with the UI, we eliminate the need for
several explicitly-defined classes that will be
used only to handle events for one object!
Section 15.6
§15.6 Lambda Expressions and Events
 Lambda Expressions (new to Java 8) can be
considered an anonymous inner class with an
abbreviated syntax.
 The compiler treats lambda expressions like an
object created from an anonymous inner class
§15.6 Lambda Expressions and Events

 The compiler knows that the parameter of the


setOnAction method must be something of type
EventHandler<ActionEvent>, and that
particular interface has only one abstract
method, so the code between the braces must
be the statements for that method
 Let’s revisit the anonymous inner class handler:
§15.6 Lambda Expressions and Events
In the original code, each button had its own
complete anonymous inner class to handle the
ActionEvent from its corresponding button

In the updated version, we show four


different ways (styles) of expressing the
registration of the event handler using
lambda expressions

The first explicitly gives the type of e

The second omits the type, because the


compiler can infer that e is of type
ActionEvent

The third omits the parentheses, because


there is only one parameter

The fourth omits the braces, because there


is only one line of code (much like a one-line
then or else clause of an if / then / else,
or a one-statement loop body)

Lambda expressions make for cleaner code!


Section 15.7
§15.7: Case Study: Loan Calculator
 This chapter began with:
 Suppose you want to
develop a GUI
application to calculate
loan payments:
 This section builds this program, start-to-finish
 We introduce the TextField node – a box into
which we can type text
 The Monthly Payment and Total Payment fields
are display-only (the user can’t type In these)
 Let’s take a closer look at this code…
§15.7: Case Study: Loan Calculator
Seventy-five lines of code simply isn’t readable at this
size, so we’ll go through this program in pieces
§15.7: Case Study: Loan Calculator

Lines 1 – 9 simply import the JavaFX components we


will need for this program
§15.7: Case Study: Loan Calculator

Lines 11 – 17 begin our LoanCalculator class (an


extension of Application), and create the five
TextFields (and the “Calculate” Button) as fields of the
class

These are set up as fields so that the method to do the


calculation (see below) can see them, and we don’t have
to pass them all as parameters (by making them fields,
they have class-wide scope)
§15.7: Case Study: Loan Calculator

Line 20 begins our code, with an override of start

Lines 22 – 35 set up a 5-x-2 GridPane, with Labels (left


side) and TextFields (right side) for the first 4 rows

The 5th row holds only the “Calculate” Button, right-


justified in the right column (nothing in the left column)
§15.7: Case Study: Loan Calculator
Line 38 centers the GridPane in its Scene

Lines 39 – 43 cause the text in the TextFields to be


right-justified within the fields

Lines 44 – 45 keep the user from being able to edit (type


inside of) those two TextFields (we’re going to
calculate their values and fill them in via code; the user
shouldn’t be able to enter anything in them)
Line 46 right-aligns the “Calculate” Button in its grid cell
§15.7: Case Study: Loan Calculator
Line 49 uses a Lambda Expression to register the
“Calculate” Button to its handle method (located in the
implicit anonymous inner class), which contains a single
line of code – a call to the calculateLoanPayment
method
§15.7: Case Study: Loan Calculator
Lines 51 – 56 form the typical end of the start method:

The container for our UI elements (the GridPane) is


added to a new Scene (l. 52), which is added to the
Stage (l. 54).

The Stage is given a title (l. 53), and made visible (l. 55)
§15.7: Case Study: Loan Calculator
Line 49 registers a call to calculateLoanPayment as
the handler for the “Calculate” Button’s ActionEvent.

This is that code.

In order to do the calculations, we need the values to be


in numeric variables, but the TextField boxes all hold
Strings, so we have to parse them into numeric
variables first (ll. 60-64)
§15.7: Case Study: Loan Calculator
Once we have the numeric versions of the Strings in
the three TextFields, we can use those to create a
Loan object (see Listing 10.2, pp. 368 – 369) – l. 67

Once instantiated, the Loan class provides the methods


getMonthlyPayment() and getTotalPayment() to
give us the values to display in the last two TextFields
(ll. 70 – 73).
Section 15.8
§15.8: Mouse Events
 Mouse events are fired whenever the mouse…
 …button goes down (MousePressed)
 …button comes back up (MouseReleased)
 …button is clicked (a down/up cycle – MouseClicked)
 …first enters an element (MouseEntered)
 …leaves an element (MouseExited)
 …moves while over an element (MouseMoved)
 …is dragged (moved with the mouse button held
down – MouseDragged)
 These events are from Table 15.1 (p. 589)
§15.8: Mouse Events
 All mouse events have some common methods
we can use to tell more about the state / location
of the mouse and the keyboard’s Alt / Control /
Shift / Meta (Mac) keys:
§15.8: Mouse Events
 JavaFX (currently) supports 3 buttons, left,
middle, and right (some mice have more)
 We can tell which mouse button was pressed by
using the MouseEvent’s getButton() method
and comparing the result to:
MouseButton.PRIMARY,
MouseButton.SECONDARY,
MouseButton.MIDDLE, or
MouseButton.NONE
 NONE is on the list simply because some mouse
events (like MouseMoved) don’t involve a button
§15.8: Mouse Events
 The book presents a sample program that lets
the user drag (click, holding the primary (left?)
mouse button down, and moving the mouse
before releasing the button) a Text node around
on the pane:

 The (surprisingly short) code is on the next slide


§15.8: Mouse Events

When the mouse is dragging the


text, set the text’s X/Y location to
the X/Y location of the mouse
(which we get from the event, e)
Section 15.9
§15.9: Key Events
 The user can also interact with the GUI through
the keyboard.
 We can capture (handle) events that are
generated by the keyboard on a key-by-key
basis, without having to wait for the user to press
Enter, as we do with the Scanner
 We can tell when keys go down (KEY_PRESSED),
come back up (KEY_RELEASED), or make a
down-and-up round-trip (KEY_TYPED)
 We can also tell if the SHIFT, ALT, CONTROL, or
META (Mac) keys are down at the same time
(isShiftDown(), isAltDown(), …)
§15.9: Key Events
 Not all keys generate a character we can display
(some don’t generate a character at all!)
 For the KEY_PRESSED and KEY_RELEASED
events, e.getCode() tells us which key was
pressed or released (whether or not it generates
a character)
 Typically used for non-character-generating keys like
the cursor keys (KeyCode.UP, KeyCode.PAGE_DOWN,
etc.). See here for the full list
§15.9: Key Events - Demo
 The KeyEventDemo program (Listing 15.8,
pp. 604 – 605, and next slide) starts with a
window (Stage) containing “A”
 If the user types a letter or digit, it replaces the
character displayed.
 If the user presses the UP, DOWN, LEFT, or RIGHT
cursor-movement keys, the letter moves in the
corresponding direction by 10 pixels.
§15.9: Key Events - Demo
When our Text node
receives a KeyPressed
event, if it’s UP, DOWN, LEFT,
or RIGHT, move the text 10
pixels in the proper
direction.

If it’s none of those four, but


it IS a letter or digit, then
change the content of the
Text node to whatever the
key was.

How do we know our Text


node (as opposed to some
other node, if we had more
on the pane) will receive the
key press?

We give it the focus


(see p. 605)
§15.9: Key Events
 Listing 15.9 (pp. 605 – 606, and next slide) adds
key and mouse controls to the enlarge / shrink
Circle project
 The original version allowed ONLY the buttons to
enlarge or shrink the circle’s radius
 Now we add the ability to have the “U” and “D”
keys to take the radius “U”p and “D”own by
having the CirclePane handle KeyPress events
 We also let the left / right mouse buttons do the
same thing
 Again, we make sure the CirclePane object
receives key events by giving it the focus
§15.9: Key Events
Listing 15.9 (pp. 605 – 606) adds key
and mouse controls to the enlarge /
shrink Circle project

The original version allowed ONLY


the buttons to enlarge or shrink the
circle’s radius

Now we add the ability to have the “U”


and “D” keys to take the radius “U”p
and “D”own by having the
CirclePane handle KeyPress events:
§15.9: Key Events
The left and right mouse buttons can
now do the same thing:

Again, we make sure the CirclePane


object receives key events by giving it
the focus (l. 58)
§15.9: Key Events
 A minor point to note:
 When you click on a button, it (the button) receives
the focus
 So, in our last example, if we click on the “Enlarge”
button, the button will get (and keep) the focus
 That means that a “D” or a “U” won’t GO to the
CirclePane – it will go to the last button that was
pressed!
 To fix this, add circlePane.requestFocus() to the
end of both button-click handlers (between lines 31/32
and lines 34/35)
Section 15.10
§15.10: Listeners for Observable Objects
 Liang does a terrible job of explaining what this
topic is all about.
 In Chapter 14, we showed how to bind one
property to another, so that when one changed in
value, that change would get forwarded to the
one it was bound to (unidirectional binding)
 We also saw how to do bidirectional binding, and
a change to either would change the other.
§15.10: Listeners for Observable Objects
 This property binding is sort of a limited form of
what we’re talking about in this section, but
rather than one property changing another, when
a property changes in value, it fires an event,
which we can handle
 The implications here are huge
 Rather than one property just automatically
forcing a change in another (which is already
plenty useful), we can have the even handler’s
code do whatever we want, potentially a LOT
more than just changing another property.
§15.10: Listeners for Observable Objects
 So, when we have an observable object (i.e., one
that implements the Observable interface), we
can observe (“keep an eye on”, or more
accurately, “listen to”) a property, and when the
property’s value changes, an event will fire
(which, obviously, we will handle).
 In order to “listen to” the property we want to
monitor, we have to use the .addListener
method to tell it who to notify when the property
changes.
§15.10: Listeners for Observable Objects
 The object that does the listening must
implement InvalidationListener, which
means it has an invalidated method that takes
an Observable property o as its parameter.
 Binding properties are Observable, so the same
kinds of things we were doing with bound
properties can be used to fire invalidated
methods to invoke code when the property
changes value.
§15.10: Listeners for Observable Objects
 Listing 15.10 (p. 607) is a rather contrived
example of how this works.
 balance is instantiated as a property (not a
property of anything in particular; just some
property) – Line 8
 Lines 9 – 14 register a listener for this property.
This is done with an anonymous inner class that
implements the invalidated method, which
displays the property’s new value on the console
 Lambda expressions make doing handlers
easier, so in the middle of the page, we see the
inner class replaced with a Lambda expression
§15.10: Listeners for Observable Objects
 So, with all of this set up, when line 16 runs (the
property we’re observing receives a new value),
the event fires, and “The new value is 4.5”
gets printed.
§15.10: Listeners for Observable Objects
 The book next provides a somewhat more
practical example
 The analog clock face we built in Chapter 14 did
not resize when the window was resized
 If we “listen to” the window’s size, and then fire
an event when it changes, the code we connect
to that change can resize the clock face
 The program in listing 15.11 (pp. 607 – 608) does
exactly that.
§15.10: Listeners for Observable Objects
 It’s the same as Listing 14.20 (which just displays
the face), except for lines 29 – 35, which were
added to listen to the pane’s width and height:

 When either the pane’s height or width changes,


rather than binding one property to another
(which could also have solved this problem), we
execute code to set the clock’s height / width.

You might also like