【发布时间】:2013-06-13 05:54:38
【问题描述】:
我正在使用 Vaadin 文本字段,我想将其限制为仅支持其中的数字。我试图覆盖setValue() 并返回而不调用super。 setValue() 如果文本不是数字。但这似乎不起作用。我该如何纠正?
我正在使用 Vaadin 7。我认为它也不支持 NumberField。
【问题讨论】:
我正在使用 Vaadin 文本字段,我想将其限制为仅支持其中的数字。我试图覆盖setValue() 并返回而不调用super。 setValue() 如果文本不是数字。但这似乎不起作用。我该如何纠正?
我正在使用 Vaadin 7。我认为它也不支持 NumberField。
【问题讨论】:
TextField 是一个始终具有 String 类型值的组件。将其他类型的属性绑定到文本字段时,如果支持两种类型之间的转换,则会自动转换值。
public class MyBean {
private int value;
public int getValue() {
return value;
}
public void setValue(int integer) {
value = integer;
}
}
从MyBean 构造的BeanItem 中名为“value”的属性将是Integer 类型。将属性绑定到TextField 将自动使无法转换为整数的文本的验证失败。
final MyBean myBean = new MyBean();
BeanItem<MyBean> beanItem = new BeanItem<MyBean>(myBean);
final Property<Integer> integerProperty = (Property<Integer>) beanItem
.getItemProperty("value");
final TextField textField = new TextField("Text field", integerProperty);
Button submitButton = new Button("Submit value", new ClickListener() {
public void buttonClick(ClickEvent event) {
String uiValue = textField.getValue();
Integer propertyValue = integerProperty.getValue();
int dataModelValue = myBean.getValue();
Notification.show("UI value (String): " + uiValue
+ "\nProperty value (Integer): " + propertyValue
+ "\nData model value (int): " + dataModelValue);
}
});
addComponent(new Label("Text field type: " + textField.getType()));
addComponent(new Label("Text field type: " + integerProperty.getType()));
addComponent(textField);
addComponent(submitButton);
在本例中,输入一个数字并按下按钮会使TextField 的值成为String,属性值将是一个Integer,表示相同的值,bean 中的值将是相同的整数。如果例如在字段中输入一个字母并按下按钮,验证将失败。这会导致为该字段显示一个通知。字段值仍然更新,但属性值和 bean 值保持在它们之前的值。
【讨论】:
如果我理解您的问题是正确的,您希望有一个字段忽略所有不是数字的输入,而不仅仅是将该字段标记为无效。 Vaadins 架构设计为浏览器中的每个字段在服务器上都有其表示。在我看来,实现这一点的最简洁的方法是拥有一个允许输入字母和其他错误字符的浏览器字段。我在 Vaadin 7 中找不到这样的字段。vaadin 6 似乎有一个名为 Number Field 的附加组件,但我没有对其进行测试。
您有多种选择:
将此插件移植到 vaadin 7 或请作者完成
编写您自己的字段。也许扩展 VTextField 和 TextFieldConnector
在服务器端做所有事情并接受延迟和流量(恕我直言,丑陋)
由于我认为选项 3不是要走的路,我可能不应该展示这段代码,但它是实现它的最快方法。
public class IntegerField extends TextField implements TextChangeListener {
String lastValue;
public IntegerField() {
setImmediate(true);
setTextChangeEventMode(TextChangeEventMode.EAGER);
addTextChangeListener(this);
}
@Override
public void textChange(TextChangeEvent event) {
String text = event.getText();
try {
new Integer(text);
lastValue = text;
} catch (NumberFormatException e) {
setValue(lastValue);
}
}
}
【讨论】:
Vaadin 7 允许扩展他们的内置小部件(如果你想对此有更多的了解,我真的推荐这个post)这是一个使用该机制的解决方案。
它由两个类组成: 连接器和扩展
扩展
package com.infosystem.widgets.vaadin;
import com.vaadin.server.AbstractClientConnector;
import com.vaadin.server.AbstractExtension;
import com.vaadin.ui.TextField;
public class NumberField extends AbstractExtension {
public static void extend(TextField field) {
new NumberField().extend((AbstractClientConnector) field);
}
}
连接器:
package com.infosystem.widgets.vaadin.client.numberField;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.infosystem.widgets.vaadin.NumberField;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.extensions.AbstractExtensionConnector;
import com.vaadin.client.ui.VTextField;
import com.vaadin.shared.ui.Connect;
@Connect(NumberField.class)
public class NumberFieldConnector extends AbstractExtensionConnector {
private static final long serialVersionUID = -737765038361894693L;
private VTextField textField;
private KeyPressHandler keyPressHandler = new KeyPressHandler() {
@Override
public void onKeyPress(KeyPressEvent event) {
if (textField.isReadOnly() || !textField.isEnabled()) {
return;
}
int keyCode = event.getNativeEvent().getKeyCode();
switch (keyCode) {
case KeyCodes.KEY_LEFT:
case KeyCodes.KEY_RIGHT:
case KeyCodes.KEY_BACKSPACE:
case KeyCodes.KEY_DELETE:
case KeyCodes.KEY_TAB:
case KeyCodes.KEY_UP:
case KeyCodes.KEY_DOWN:
case KeyCodes.KEY_SHIFT:
return;
}
if (!isValueValid(event)) {
textField.cancelKey();
}
}
};
@Override
protected void extend(ServerConnector target) {
textField = (VTextField) ((ComponentConnector) target).getWidget();
textField.addKeyPressHandler(keyPressHandler);
}
private boolean isValueValid(KeyPressEvent event) {
String newText = getFieldValueAsItWouldBeAfterKeyPress(event.getCharCode());
try {
parseValue(newText);
return true;
} catch (Exception e) {
return false;
}
}
protected long parseValue(String value) {
return Long.valueOf(value);
}
private String getFieldValueAsItWouldBeAfterKeyPress(char charCode) {
int index = textField.getCursorPos();
String previousText = textField.getText();
StringBuffer buffer = new StringBuffer();
buffer.append(previousText.substring(0, index));
buffer.append(charCode);
if (textField.getSelectionLength() > 0) {
buffer.append(previousText.substring(index + textField.getSelectionLength(),
previousText.length()));
} else {
buffer.append(previousText.substring(index, previousText.length()));
}
return buffer.toString();
}
}
要使用上面的代码,您需要将其添加到您当前的小部件集中。 之后this的使用如下:
TextField field = new TextField();
NumberField.extend(field);
【讨论】:
在 Vaadin 7 中,您可以使用 TextField 并将验证器设置为仅允许数字:
TextField textField;
textField.addValidator(new RegexpValidator("[-]?[0-9]*\\.?,?[0-9]+"), "This is not a number!");
更改正则表达式以满足您的需求。 请记住,它仍在处理字符串,因此您仍然需要转换 TextField 的返回值:
Long.parseLong(textField.getValue())
【讨论】:
这是@raffael 答案的更新(2017 年 vaadin 8):
public class DoubleField extends TextField implements ValueChangeListener<String> {
public String lastValue;
public DoubleField() {
setValueChangeMode(ValueChangeMode.EAGER);
addValueChangeListener(this);
lastValue="";
}
@Override
public void valueChange(ValueChangeEvent<String> event) {
String text = (String) event.getValue();
try {
new Double(text);
lastValue = text;
} catch (NumberFormatException e) {
setValue(lastValue);
}
}
【讨论】:
NumberField 现在可用于 Vaadin 7 和 8。
【讨论】:
使用 Vaadin 8,您可以使用 Binder:
Binder<YouBean> binder = new Binder<>();
binder.forField(textField)
.withConverter(new StringToIntegerConverter("Must be Integer"))
.bind(YouBean::getter, YouBean::setter);
binder.setBean(bean); //optional
【讨论】: