Greg has posted 18 posts at DZone. You can read more from them at their website. View Full User Profile

Pivot: A Practical Example, Part 2 - Event Handling

07.07.2008
| 13550 views |
  • submit to reddit

Adding Event Listeners

At this point, the entire UI has been created, but it is not yet visible. Even if it was, it wouldn't do much, since no event handlers have been added. The next thing the startup() method does is add a number of event handlers:

stocksTableView = (TableView)componentLoader.getComponent("stocksTableView");
stocksTableView.getTableViewSelectionListeners().add(new TableViewSelectionListener() {
public void selectionChanged(TableView tableView) {
refreshDetail();
}
});
...

addSymbolButton = (Button)componentLoader.getComponent("addSymbolButton");
addSymbolButton.getButtonPressListeners().add(new ButtonPressListener() {
public void buttonPressed(Button button) {
addSymbol();
}
});
...

This code demonstrates how to obtain a reference to a component defined in a WTKX file and attach an event handler to it. Component references are retrieved from a loaded WTKX file via the getComponent() method of the ComponentLoader class. In this example, the components are defined in the top-level WTKX file, so no namespace prefix is necessary. Nested components can be retrieved using the fully qualified path (including namespaces) to the component. The sample code obtains references to two components: the table view that will contain the stock quotes and the "add symbol" button. Note that the return value of getComponent() must be cast to an appropriate type.

A caller signals its interest in a particular event by implementing an interface that defines an event handler method and adding itself as an event lister on the event source. In the example above, two event handlers are created: a selection change listener on the stock quote table view and a button press listener on the "add symbol" button. Some listener interfaces, such as those shown here, define only a single event handler method, but others define more than one and serve as a grouping of related events.

Note that, though these handlers are implemented as anonymous inner classes, this is not required - any class that implements the appropriate listener interface can register as a listener.

Displaying the Content

Finally, the application is ready to be shown. The component hierarchy loaded from the WTKX files is added to a Window and the window is opened, making the application visible and allowing the user to begin interacting with it:

window = new Window();
window.setContent(content);
window.getAttributes().put(Display.MAXIMIZED_ATTRIBUTE, Boolean.TRUE);
window.open();

refreshTable();

ApplicationContext.setInterval(new Runnable() {
public void run() {
refreshTable();
}
}, REFRESH_INTERVAL);

Component.setFocusedComponent(symbolTextInput);

The application creates an instance of the undecorated Window class, sets the content component, and opens the window. The MAXIMIZED_ATTRIBUTE of the Display class is set to true so that, when opened, the window will be sized to fit the entire display area.

The code then calls refreshTable() to load the stock quote data and sets a timer interval to reload the data every 15 seconds. Finally, it sets the focus to the symbol text input, so a user can easily add a new stock to track.

Published at DZone with permission of its author, Greg Brown.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Jacek Furmankiewicz replied on Mon, 2008/07/07 - 7:22am

The event listener logic is still pretty similar to Swing's. Maybe you could consider defining a straight Java method name in the XML file and hooking up the event listener automatically when the UI is loaded?

GTK+ libGlade (which is very similar conceptually to your solution) allows you to define method names in the XML file. If I remember correctly, it even handles finding the proper method name, regardless of the language (e.g. libGlade works with C/C++/Ruby/Python). They call it simply "signals" (I believe Qt has a similar approach). We can always learn something new and different from non-Java tools. :-)

See here for some info:
http://delirial.com/archives/tutorial-building-guis-for-ruby-programs-with-glade/

E.g. in the Glade XML file they define a signal for an event, e.g.:

Now, we need to add a signal (event) for quiting our program.

  • Go to the Signals tab in the Properties window.
  • Expand the GtkWidget node.
  • Select delete-event and type on_quit in the Handler column.

 and then on the Ruby side they just need to define a method called on_quit:

 

def on_quit(widget, arg0)
    puts "This is me leaving."
    Gtk.main_quit
end

You bypass the whole event listener paradigm this way and just hook up a 'signal' directly to a method (possibly with a pre-defined signature).

This would go some way towards differentiating Pivot from Swing and giving it some real edge as compared to its older brother.

I adopted the same approach in my SwingBuilder project, see the Wiki and maybe you can pinch some ideas for your solution:

http://code.google.com/p/javabuilders/wiki/JavaBuilderEventHandling

http://code.google.com/p/javabuilders/wiki/SwingBuilderEventHandlers

 

Greg Brown replied on Mon, 2008/07/07 - 8:03am in response to: Jacek Furmankiewicz

> Maybe you could consider defining a straight Java method name in the XML file and hooking up the event listener automatically when the UI is loaded?

There is definitely value in allowing a caller to do something like this. We don't necessarily want to embed code in WTKX, but including a method name would be OK. The question is - where would the method live? Event handlers are defined by interfaces which must be implemented by a class. We have considered allowing a caller to provide a handler class name in WTKX for a given event class (e.g. buttonPressListener="com.foo.MyButtonPressHandler") - the loader would instantiate the class and wire up the listener.

> This would go some way towards differentiating Pivot from Swing and giving it some real edge as compared to its older brother.

We actually like the way event handling works in Swing, for the most part. We eliminated the event objects and came up with what we think is a cleaner way to register for and fire events, but the basic listener model is pretty sound. What do you think the limitations are?

 

Jacek Furmankiewicz replied on Mon, 2008/07/07 - 8:14am in response to: Greg Brown

Just the amount of code that is required for clicking a button to call some Java-side method. 3-5 lines may not seem much, but once you add all the buttons you have in an app, it's a fair amount.

The 'signals' approach reduces it to a simple property value. And it's not really embedding code, think of it more of as defining a signal that can be used to map to a method when the file gets loaded.

The method would have to live in the object that is loading the layout file...I presume the loading engine has a way to get a reference to the object that called it?

Just a suggestion...if it doesn't fit with your philosophy of how the toolkit should work, that's fine. After all, it's your project :-)

Greg Brown replied on Mon, 2008/07/07 - 8:27am in response to: Jacek Furmankiewicz

> The method would have to live in the object that is loading the layout file...I presume the loading engine has a way to get a reference to the object that called it? 

At the moment, it doesn't - but that is an interesting idea. It could be a bit limiting, though, since handlers are often implemented as inner classes (anonymous or otherwise). The loading class would potentially have to implement a lot of interfaces, and the event handlers would be exposed as public methods (something we generally try to avoid, if possible).

> Just a suggestion...if it doesn't fit with your philosophy of how the toolkit should work, that's fine. After all, it's your project :-)

Understood. Thanks for the suggestion.  :-)

 

Jacek Furmankiewicz replied on Mon, 2008/07/07 - 8:38am in response to: Greg Brown

You're welcome.

I don't want to troll pushing my own project (which relies on YAML instead of XML), but just to give you a quick sample of how simple and productive signals can be:

 JButton(text=Save, onAction=save

would execute directly on the calling class without any other code needed:

private void save() {

   //save logic goes here

You could easily do something like this in your XML markup. Does it handle all scenarios? No. Does it handle 80% of the most common ones...probably yes. If you have a good solution that follows the 80/20 rule then it's a valuable addition IMHO.

Greg Brown replied on Mon, 2008/07/07 - 8:54am in response to: Jacek Furmankiewicz

> You could easily do something like this in your XML markup...If you have a good solution that follows the 80/20 rule then it's a valuable addition IMHO.

Agreed. The ability to hook up event handlers in WTKX will probably be a common request - something like your solution may well work for us.

 

Jacek Furmankiewicz replied on Mon, 2008/07/07 - 9:09am in response to: Greg Brown

Well, it's really a GTK+/Qt solution...I just pinched it from them and made it Java-specific. Gotta give credit where it's due.

P.S. The tricky part is passing event-specific arguments to the method (e.g. FocusListener vs ActionListener), since it may be needed sometimes. You can't always assume everyone will want a zero-arguments method. But if you manage to figure it out in the context of your project, it would be well worth it.

P.S.S. If I have the time (which unfortunately I don't) I may be able to write a PivotBuilder for you using the same reusable engine I have for the SwingBuilder, since I already handle most of these issues in a domain-generic way...ah, if only the day had 30 hours instead of 24.

P.S.S.S. Beg Mikael Grev to write a version of MigLayout for Pivot. :-)
MigLayout is worth its weight in gold.

Gregg Bolinger replied on Mon, 2008/07/07 - 8:22pm

I think the Pivot guys, Jacek, and the Groovy SwingBuilder guys all need to get together and pull something together that encompouses what you are all trying to do.  There a lot about Jacek's builder API I like and there is a lot about Pivot I like.  In the end, as long as you screw JavaFX into a has been, I'm cool with that. Oh wait, Sun's already doing a good of that. ;)

Jacek Furmankiewicz replied on Tue, 2008/07/08 - 2:51am in response to: Gregg Bolinger

Ah, if Java only supported embedded free-form text in .java files (no need for external XML or YAML files then). I hear something like this was coming in Java 7, but who knows what will make it in there since all the senior Sun engineers seem to be wasting their valuable time on JavaFX instead.

 It kills me that Java is missing such basic things as integrated data binding and properties (Beans Binding blows, I know...I've implemented support for it...you need more code to support bindable properties than the code they supposedly replace)...and yes JavaFX was able to get it added with Chris Oliver working on it in his spare time. To make it worse, the main guy in charge of new Java 7 features is moving away from properties:

http://blogs.sun.com/abuckley/en_US/entry/properties_in_java

Wake up, Alex...properties NEED to be added to Java, but instead of simple get/set replacements they need to have automatically integrated support for firePropertyChange(), so no one should ever need to call it manually. And binding should be integrated right into the language instead of being a clumsy separate library.

 Look how simple data binding is in Flex with their annotated variables (even less than properties):

[Bindable]
private var labelText:String = "This is a label";

<mx:Label text="{labelText}"/>

That's literally 1 line of code for an annotation ([Bindable]). In Java to support Beans Binding JSR 295 it's nearly 20 lines, since you need to add PropertyChangeSupport to every bindable class and property.

And Sun is wasting valuable limited resources on JavaFX and moving away from enhancing the core Java languag to keep up the competition? This is one of the most illogical things I've ever seen done by an IT corporation.

 

 

 

Greg Brown replied on Tue, 2008/07/08 - 8:49am in response to: Jacek Furmankiewicz

In Pivot, property binding like this is handled by the (pivot.collections) Map interface, which fires change events via the MapListener interface. This allows you to treat a statically typed Java object like a dynamically typed JavaScript object, as long as it implements Map. It requires some glue code to map property keys to getters and setters, but it works well. It's a little easier for Flex to support this since ActionScript objects are already dynamically typed.

In WTKX, you can achieve something similar to your Flex example using markup like this:

<Label textKey="labelText"/> 

The data binding section of the Quick Start tutorial talks about this in more detail. I'll be publishing it as an article on Javalobby soon:

https://pivot.dev.java.net/nonav/tutorials/stocktracker.data_binding.html

 

Jacek Furmankiewicz replied on Tue, 2008/07/08 - 9:09am in response to: Greg Brown

[quote=gbrown@vmware.com]It requires some glue code to map property keys to getters and setters[/quote]

That's exactly the issue I have in general...glue code needs to be added to each getter/setter and potentially even the class. I would love it just to work out-of-the-box without the need for extra glue code...I'm dreaming in technicolor, I know...

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.