如何在Vaadin中创建仅支持数字的文本框。

12

我正在使用 Vaadin 的文本字段,希望将其限制为只支持数字。我试图重写 setValue() 方法,并在文本不是数字时返回而不调用 super.setValue() 但似乎并没有起作用。请问如何解决这个问题?我正在使用的是 Vaadin 7,我认为它也不支持 NumberField。

7个回答

20
如果我正确理解您的问题,您希望有一个字段忽略所有不是数字的输入,并且不仅将该字段标记为无效。 Vaadin的架构被设计为浏览器中的每个字段都在服务器上有其表示。在我看来,实现这一点最干净的方法是拥有一个浏览器字段,允许输入字母和其他错误字符。我在Vaadin 7中找不到这样的字段。似乎有一个名为Number Field的vaadin 6插件,但我没有测试过。
您有多种选择:

  1. 将此插件移植到vaadin 7或要求作者这样做

  2. 编写自己的字段。可能扩展VTextField和TextFieldConnector

  3. 在服务器端完成所有操作并接受延迟和流量(在我看来很丑陋)

由于我认为选项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);
    }
}
}

谢谢,伙计。它运行良好。我添加了一些空值检查和非空检查并进行了处理。 - Sanjaya Liyanage
工作正常,但当快速输入大量字符时,仍可能输入不想要的内容。 - Eyal
1
通常认为,使用异常来控制流程是不良实践。请参考http://c2.com/cgi/wiki?DontUseExceptionsForFlowControl,http://programmers.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why或https://dev59.com/onRB5IYBdhLWcg3wET1J。 - s.froehlich
1
感谢你的代码,Raffael。但是需要指出的是,这并不是正确的解决方案,因为由于通信延迟,该值可能会接受错误的值或拒绝有效的值。 - Diego Quirós

12

Vaadin 7允许扩展其内置小部件(如果您想对此有更多了解,我真的建议您查看这篇文章)。下面是一种使用该机制的解决方案。

它由两个类组成:Connector和Extension。

  1. Extension:

  2. 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);
            }
    }
    
  3. 连接器:

  4. 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();
    }
    }
    

要使用上面的代码,您需要将其添加到当前的小部件集中。 之后,使用方式如下:

TextField field = new TextField();
NumberField.extend(field);

1
要将其添加到小部件集中,连接器类应该在名为“client”的文件夹中,与自定义小部件集文件(例如MyWigetSet.gwt.xml)处于同一级别。要使用自定义小部件集,必须在servlet中声明此项为init-param。 - enkara

4
在Vaadin 7中,您可以使用TextField并设置验证器以仅允许数字:
TextField textField;
textField.addValidator(new RegexpValidator("[-]?[0-9]*\\.?,?[0-9]+"), "This is not a number!");

更改正则表达式以满足您的需求。 请记住,此仍然处理字符串,因此您仍需要转换TextField的返回值:

Long.parseLong(textField.getValue())

3

使用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

我从Vaadin 7的TextField迁移到了Vaadin 8,并且使用了Binder<TextField>,而不需要'bind'调用。关于从7迁移到8没有任何注释,你应该在官方文档中添加注释。 - Sebastian D'Agostino

2

这是一个关于@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);
    }

}

2

TextField 是一种组件,它的值总是字符串类型。当将另一种类型的属性绑定到文本字段时,如果两种类型之间的转换被支持,则该值将自动转换。

public class MyBean {
    private int value;

    public int getValue() {
        return value;
    }

    public void setValue(int integer) {
        value = integer;
    }
}

从构建自 MyBeanBeanItem 中获取的名为 "value" 的属性将是 Integer 类型。 将属性绑定到 TextField 上,将自动使无法转换为整数的文本验证失败。请注意保留 HTML 标签。
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值将保持在其先前的值。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接