该帖已经被评为隐藏帖
|
|
---|---|
作者 | 正文 |
发表时间:2010-03-07
源文转自:An MVP-compatible EnumListBox for GWT
A frequent request on the GWT and gwt-presenter forums is for a ListBox that implements HasValue like a TextBox. I recently needed one myself, and thought it would be especially cool if I could use it with a Java enum type like this:
public static enum Frequency {DAILY, WEEKLY, MONTHLY}; private ConstantsWithLookup enumLabels = GWT.create(EnumLabels.class); private EnumListBox<Frequency> freqBox; freqBox = new EnumListBox<Frequency>(Frequency.class, enumLabels); In keeping with MVP philosophy, the presenter’s display interface only needs the HasValue type to get and set the selected value as well as add a ValueChangeHandler to respond to a new selection. Here as some relevant excerpts from a presenter that uses the EnumListBox:
public interface Display extends WidgetDisplay { HasValue<Frequency> getFrequency(); } ... protected void onFirstRequest() { ... display.getFrequency().addValueChangeHandler(new ValueChangeHandler<Frequency>() { @Override public void onValueChange(ValueChangeEvent<Frequency> event) { // Do something with the newly selected event.getValue() ... } }); }
Here’s a straightforward implementation of an EnumListBox that implements HasValue. Thanks to the gwt-ent project for the original idea for this。
package com.roa.app.client.ui.widget; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.i18n.client.ConstantsWithLookup; import com.google.gwt.user.client.ui.HasValue; import com.google.gwt.user.client.ui.ListBox; import com.roa.common.client.util.EnumTranslator; public class EnumListBox<T extends Enum<T>> extends ListBox implements HasValue<T> { private final Class<T> clazzOfEnum; private boolean valueChangeHandlerInitialized; public EnumListBox(final Class<T> clazzOfEnum, final ConstantsWithLookup constants) { if (clazzOfEnum == null) throw new IllegalArgumentException("Enum class cannot be null"); this.clazzOfEnum = clazzOfEnum; EnumTranslator enumTranslator = new EnumTranslator(constants); T[] values = clazzOfEnum.getEnumConstants(); for (T value : values) { // this.addItem(constant.toString(), constant.name()); this.addItem(enumTranslator.getText(value), value.name()); } } public T getSelectedValue() { if (getSelectedIndex() >= 0) { String name = getValue(getSelectedIndex()); T[] values = clazzOfEnum.getEnumConstants(); for (T value : values) { if (value.name().equals(name)) return value; } } return null; } public void setSelectedValue(T value) { T[] values = clazzOfEnum.getEnumConstants(); for (int i = 0; i < values.length; i++) { if (values[i] == value) { this.setSelectedIndex(i); return; } } throw new IllegalArgumentException("No index found for value " + value.toString()); } /* * Methods to implement HasValue */ @Override public T getValue() { return this.getSelectedValue(); } @Override public void setValue(T value) { this.setValue(value, false); } @Override public void setValue(T value, boolean fireEvents) { T oldValue = getValue(); this.setSelectedValue(value); if (fireEvents) { ValueChangeEvent.fireIfNotEqual(this, oldValue, value); } } @Override public HandlerRegistration addValueChangeHandler(ValueChangeHandler<T> handler) { // Initialization code if (!valueChangeHandlerInitialized) { valueChangeHandlerInitialized = true; super.addChangeHandler(new ChangeHandler() { public void onChange(ChangeEvent event) { ValueChangeEvent.fire(EnumListBox.this, getValue()); } }); } return addHandler(handler, ValueChangeEvent.getType()); } }
There’s really not much to it, just a little weirdness that always comes with generics. Notice that the constructor uses an EnumTranslator to populate the labels in the ListBox. This allows you to use a standard GWT ConstantsWithLookup inteface to supply localized text for the enum values instead of the constant names. ConstantsWithLookup is just like Constants, but with the important ability to find a value dynamically without invoking a method corresponding to the property name. Unfortunately, you still have to define a method for each value of the enum in your ConstantsWithLookup class, even though it’s never used directly. Here’s a sample interface:
public interface EnumLabels extends ConstantsWithLookup { // Enums String com_mypackage_MyClass_Frequency_DAILY(); String com_mypackage_MyClass_Frequency_WEEKLY(); String com_mypackage_MyClass_Frequency_MONTHLY(); String com_mypackage_MyClass_Frequency_QUARTERLY(); String com_mypackage_MyClass_Frequency_YEARLY();
And the corresponding default properties file EnumLabels.properties:
com_mypackage_MyClass_Frequency_DAILY=daily com_mypackage_MyClass_Frequency_WEEKLY=weekly com_mypackage_MyClass_Frequency_MONTHLY=monthly com_mypackage_MyClass_Frequency_QUARTERLY=quarterly com_mypackage_MyClass_Frequency_YEARLY=yearly
And finally, here’s my EnumTranslator:
package com.roa.common.client.util; import com.google.gwt.i18n.client.ConstantsWithLookup; /** * Does a properties file lookup to get text associated with an enum value * Property keys use the full class name with all dots and dollars * converted to underscores. Keys are case-sensitive and GWT requires a * method in the interface that extends ConstantsWithLookup, even though * the method is never called. * * Example: * String my_package_class_Frequency_DAILY(); * * In the corresponding properties file: * my_package_class_Frequency_DAILY=daily * * @author David Chandler */ public class EnumTranslator { private ConstantsWithLookup constants; public EnumTranslator(ConstantsWithLookup constants) { this.constants = constants; } public String getText(Enum e) { String lookupKey = e.getClass().getName() + "." + e.name(); lookupKey = lookupKey.replace(".", "_"); lookupKey = lookupKey.replace("$", "_"); return constants.getString(lookupKey); } }
This EnumListBox is a fairly hard-wired kind of ListBox. In the near future, I anticipate refactoring along these lines:
Add a constructor that takes any java.util.List, not just an Enum. Create an interface HasSelectedValue that extends HasValue by adding a populateAllSelections() method. This would allow the available selections to come from the presenter through the Display interface and is thus even better for MVP. Versions of the new method could also take a java.util.List or Enum and would replace the constructor. Ditto for a HasSelectedValues interface to deal with multiple-select type ListBoxes. Stay tuned. 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
浏览 1276 次