Thursday, October 8, 2009

Easy Way to Catch Double Clicks in GWT

Today I decided that I wanted to add some code to one of my widgets that would execute when a HorizontalPanel was double-clicked.

First thing I did was to type the variable name for the HorizontalPanel, then ".add" and scroll down the list for something like addDblClickHandler() or something like that.



No beans. There wasn't even a addClickHandler()! I researched that a little bit and realized that I had to wrap the HorizontalPanel in a FocusPanel in order to get the addClickHandler().

O.K. fine. But what about my addDblClickHandler()? I poked around a bit in the Official GWT JavaDocs, and I found the HasDoubleClickHandlers interface... but it listed not a single class that implements it. Wierd.

A lot of painful googling finally got me to a helpful post on osdir.com titled Double Click in GWT 1.6?

The suggestion put forth there was to create a new class extending the desired existing GWT Widget, something like this:
public class DoubleClickListBox extends ListBox implements
HasDoubleClickHandlers{

public DoubleClickListBox(boolean isMultipleSelect) {
super(isMultipleSelect);
}

public DoubleClickListBox() {
super();
}

public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) {
return addDomHandler(handler, DoubleClickEvent.getType());
}
}
(Note: they choose to extend the ListBox)

Well, I tried that and it did work... but creating a whole new class, just for that nonsense? Seemed like way too much hassle. Has to be a better way.

My first thought was to just add the handler manually, using an Anonymous Class. Something like this:



HorizontalPanel vHorizontalPanel = new HorizontalPanel();

/* this doesn't work */
vHorizontalPanel.addDomHandler(new DoubleClickHandler() {
@Override
public void onDoubleClick(
DoubleClickEvent pDoubleClickEvent) {

Window.alert("chicken");
}
}, DoubleClickEvent.getType());



...but it turns out that the addDomHandler() function on the HorizontalPanel class is protected, meaning I can't call it publicly.

So I did some research, and found that there is a way to do it all in one swing, using an initializer. This time, it was JAVA In a Nutshell to the rescue with Anonymous Classes. The tip that helped was this:
Since an anonymous class has no name, it is not possible to define a constructor for an anonymous class. If your class requires a constructor, you must use a local class instead. However, you can often use an instance initializer as a substitute for a constructor. In fact, instance initializers were introduced into the language for this very purpose.


I gave that a try, and came up with this simple solution:

HorizontalPanel vHorizontalPanel = new HorizontalPanel() {
{
addDomHandler(new DoubleClickHandler() {

@Override
public void onDoubleClick(DoubleClickEvent pEvent) {
Window.alert("chicken");
}

}, DoubleClickEvent.getType());
}
};

.....and that works!

The rest of the context on that nutshell page makes me think maybe this isn't the best use of an anonymous class... I'm still new to Java, but to me, it sure seems to beat making a separate java file and class, just to catch a double-click.

Am I totally wrong? Is there a better way to do this?

I'd love to hear what you think.

2 comments:

  1. Thanks a ton......this was very useful.
    --Asif

    ReplyDelete
  2. Did you see the solution at http://stackoverflow.com/questions/4303320/adding-clickhandler-to-div-which-contains-many-other-widget? The guy says to simply use the methods sinkEvents and addHandler on the Widget class. But I did try your method and it worked!

    ReplyDelete