Posted by
evanx on July 21, 2008 at 1:02 PM PDT
In the Gooey MvnC prequel, we consider an MVC-type separation for
programming a Swing UI panel, and convention-over-configuration
to automate events, threading and beans binding.
Now as an illustration of the above intent,
we implement a minimalistic helper class to create dynamic proxies
for event listeners wired to event handler methods in our controller.
Click here to read "Gooey Event Proxy"
A part of "Gooey Beans, part of a trilogy in 42 parts"
Salient Samples
In our controller, we implement event handler methods as follows.
public class LoginController implements GEventController {
LoginView view = new LoginView();
LoginModel model = new LoginModel();
GEventHelper eventHelper = new GEventHelper(this, view, model);
...
void usernameFocusLost() {
... // check that username is valid
}
void cancelActionPerformed() {
... // handle cancel button pressed
}
SwingWorker okActionPerformed() {
... // worker for OK button pressed to login
}
}
Our helper creates dynamic proxies as event listeners
registered on our components, as follows.
public class GEventHelper {
protected GEventController controller;
protected Map<Field, Component> components = new HashMap();
protected Map<Component, GBeanProperty> bindings = new HashMap();
public GEventHelper(GEventController controller, Component view, GBean model) {
this.controller = controller;
initComponents(view); // populate components map
bind(model); // create bindings/listeners
connectController();
}
...
public void connectController() {
for (Component component : components.values()) {
if (GInputComponentAdapterFactory.isInputComponent(component)) {
GInputComponentAdapterFactory.create(component).addActionListener(
(ActionListener) createListenerProxy(component,
ActionListener.class));
GInputComponentAdapterFactory.create(component).addFocusListener(
(FocusListener) createListenerProxy(component,
FocusListener.class));
... // other events
}
}
}
protected Object createListenerProxy(Component source, Class listenerType) {
return Proxy.newProxyInstance(
source.getClass().getClassLoader(),
new Class[]{listenerType},
new GEventProxy(controller, source));
}
...
}
where our GEventProxy below will invoke methods in our controller
eg. usernameFocusLost() when focusLost() is invoked on a proxy listener
registered on a component named "username".
public class GEventProxy implements InvocationHandler {
...
protected void invoke(Method method, Object[] args) {
for (Method eventMethod : eventMethods.values()) {
if (eventMethod.getName().equalsIgnoreCase(
component.getName() + method.getName())) {
invokeHandler(eventMethod, args);
break;
}
}
}
}