Using Event Handlers in GWT-1.6

I’ve already blogged about Event Handlers in GWT-1.6. Everyone coding Java knows how an event Listener works. How to attach one to an event source, how to handle the events, and how to create one yourself. Handlers are a bit different though, as an example I thought I’d show you how to write a very simple (un-styled) VerticalTabBarclass using GWT-1.6. The VerticalTabBar class has the following design points:

  • It’s root Element is a simple <div> with a primary style of “VerticalTabBar”
  • Each tab is a simple <div> element (wrapped in it’s own Widget class)
  • Each tab has the CSS class-name “Tab”
  • The selected tab has the CSS class-name “Tab Tab-Selected”
  • The VerticalTabBar fires “ValueChangeEvents” with the index of the tab that has been selected

So here goes:

import java.util.List;
import java.util.ArrayList;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.DivElement;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;

import com.google.gwt.event.shared.HandlerRegistration;

import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;

import com.google.gwt.user.client.Element;

import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.ComplexPanel;

// Unlike previous GWT implementation, implementing the Has***Handlers interface of the Event type
// your class dispatches is now mandatory, and you'll see why a little further down.
public class VerticalTabBar extends ComplexPanel
        implements HasValueChangeHandlers<Integer> {

    private final DivElement root;

    private final List<Tab> tabs = new ArrayList<Tab>();

    private int selected = -1;

    public VerticalTabBar() {
        root = Document.get().createDivElement();
        setElement(root);
        setStylePrimaryName("VerticalTabBar");
    }

    private void setTabSelected(final int tabIndex, final boolean selected) {
        if(tabIndex != -1) {
            tabs.get(tabIndex).setSelected(selected);
        }
    }

    private void setSelectedTabImpl(final int tabIndex) {
        setTabSelected(selected, false);
        setTabSelected(selected = tabIndex, true);
    }

    public void add(final String tabText) {
        add(tabText, false);
    }

    public void add(final String tabText, final boolean asHTML) {
        final Tab tab = new Tab(tabText, asHTML);
        tabs.add(tab);
        add(tab, root.<Element>cast());
    }

    public int getSelectedTab() {
        return selected;
    }

    public void setSelectedTab(final int tabIndex) {
        if(tabIndex != selected) {
            if(tabIndex < 0 || tabIndex >= tabs.size()) {
                setSelectedTabImpl(-1);
            } else {
                setSelectedTabImpl(tabIndex);
            }

            // This is how you fire an Event with a Handler.
            // The signature of the "fire" method in ValueChangeEvent is:
            // public static <I> void fire(HasValueChangeHandlers<I> source, I value)
            // If we don't implement the HasValueChangeHandlers, we can't fire ValueChangeEvents
            ValueChangeEvent.fire(this, selected);
        }
    }

    public int getTabCount() {
        return tabs.size();
    }

    public HandlerRegistration addValueChangeHandler(
            final ValueChangeHandler<Integer> handler) {

        // This is really all we need to do to add a new Handler
        // addHandler is defined in Widget, and registers the Handler
        // with our HandlerManager
        return addHandler(handler, ValueChangeEvent.getType());
    }

    private class Tab extends Widget {
        private HandlerRegistration registration;

        private boolean selected = false;

        private Tab(final String tabText, final boolean asHTML) {
            final DivElement element = Document.get().createDivElement();

            if(asHTML) {
                element.setInnerHTML(tabText);
            } else {
                element.setInnerText(tabText);
            }

            setElement(element);
            setStylePrimaryName("Tab");
        }

        @Override
        protected void onLoad() {
            super.onLoad();

            // As you can see, you don't have to use onBrowserEvent anymore,
            // instead just register a Handler without exposing the Has***Handlers.
            // This technique only works for low-level browser (DOM) events.
            // Also not the fact that we invoke "addDomHandler" here, and not "addHandler"
            registration = addDomHandler(new ClickHandler() {
                public void onClick(final ClickEvent event) {
                    setSelectedTabImpl(tabs.indexOf(Tab.this));
                }
            }, ClickEvent.getType());
        }

        @Override
        protected void onUnload() {
            super.onUnload();

            registration.removeHandler();
            registration = null;
        }

        public void setSelected(final boolean selected) {
            if(this.selected = selected) {
                addStyleDependentName("Selected");
            } else {
                removeStyleDependentName("Selected");
            }
        }

    }

}

As you can see from this example, event dispatching and handling has become much easier with Handlers. You can invent your own Event types, and use them in the same way as all the other Handlers.


Advertisements