6

我需要限制用户可以输入到TextFieldJavaFX 控件中的字符数。我已经延长TextField

public class LengthLimitedTextField extends TextField {
    /**
     * @param maxCharacters The max allowed characters that can be entered into this {@link TextField}.
     */
    public LengthLimitedTextField(final int maxCharacters) {
        final TextField thisField = this;
        this.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observable,
                                String oldValue, String newValue) {
                // Force correct length by deleting the last entered character if text is longer than maxCharacters
                if (newValue.length() > maxCharacters) {
                    thisField.deleteNextChar();
                }
            }
        });
    }
}

这确实按预期工作。但是,假设对于特定LengthLimitedTextFieldmaxCharacters设置为 3。如果用户输入 4 个或更多字符,并尝试撤消(通过 CTRL+Z 或鼠标上下文菜单),我会收到以下内容Exception并且文本保持不变。

java.lang.IndexOutOfBoundsException
    at javafx.scene.control.TextInputControl.replaceText(TextInputControl.java:368)
    at com.sun.javafx.scene.control.skin.TextFieldSkin.replaceText(TextFieldSkin.java:572)
    at com.sun.javafx.scene.control.behavior.TextFieldBehavior.replaceText(TextFieldBehavior.java:159)
    at com.sun.javafx.scene.control.behavior.TextInputControlBehavior$UndoManager.undo(TextInputControlBehavior.java:442)
    at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.callAction(TextInputControlBehavior.java:137)
    at com.sun.javafx.scene.control.skin.TextInputControlSkin$ContextMenuItem$1.handle(TextInputControlSkin.java:595)
    at com.sun.javafx.scene.control.skin.TextInputControlSkin$ContextMenuItem$1.handle(TextInputControlSkin.java:593)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:69)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:28)
    at javafx.event.Event.fireEvent(Event.java:171)
    at javafx.scene.control.MenuItem.fire(MenuItem.java:456)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1197)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$6.handle(ContextMenuContent.java:1148)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$6.handle(ContextMenuContent.java:1146)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:69)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33)
    at javafx.event.Event.fireEvent(Event.java:171)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3328)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3168)
    at javafx.scene.Scene$MouseHandler.access$1900(Scene.java:3123)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1563)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2265)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:250)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:173)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:292)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:528)
    at com.sun.glass.ui.View.notifyMouse(View.java:922)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
    at com.sun.glass.ui.win.WinApplication$3$1.run(WinApplication.java:73)
    at java.lang.Thread.run(Thread.java:724)

我不确定如何解决这个问题。一个可能的(但不是理想的)解决方案是完全禁用撤消/重做,但是如果不完全覆盖上下文菜单(根据这个SO 答案,这并不容易)和默认的键盘快捷键,这似乎是不可能的。

所以最终,我的问题是双重的:

是否有另一种方法可以限制 a 的字符数TextField而不会在 Undo 上引发异常?还是有一种干净的方法可以在应用程序中完全禁用撤消?

编辑:我做了更多的研究,根据https://javafx-jira.kenai.com/browse/RT-30881这似乎是一个错误。看到这个评论。那么也许这不可能实现呢?我将保留这个问题,希望有人确实有可能的解决方法。

4

4 回答 4

5

这里我会怎么做:我会使用一个普通的文本字段,并会添加一个事件过滤器。

设置:

TextField tx = new TextField();
        tx.addEventFilter(KeyEvent.KEY_TYPED, maxLength(3));

事件处理程序:

public EventHandler<KeyEvent> maxLength(final Integer i) {
        return new EventHandler<KeyEvent>() {

            @Override
            public void handle(KeyEvent arg0) {

                TextField tx = (TextField) arg0.getSource();
                if (tx.getText().length() >= i) {
                    arg0.consume();
                }

            }

        };

    }
于 2013-10-04T15:41:09.533 回答
4

这是在 JavaFX 8 上使用 Lambda 表达式的另一个解决方案

textField.textProperty().addListener(
        (observable,oldValue,newValue)-> {
            if(newValue.length() > 5) cp.setText(oldValue);
        }
);

如果 textField 长度大于 5,则不会插入更多文本。

于 2015-11-30T18:49:17.747 回答
3

adding a little spices to Magcus code

@FXML
private TextField txt_Numeric;
@FXML
private TextField txt_Letters;

@Override
public void initialize(URL url, ResourceBundle rb) {
    /* add Event Filter to your TextFields **************************************************/
    txt_Numeric.addEventFilter(KeyEvent.KEY_TYPED , numeric_Validation(10));
    txt_Letters.addEventFilter(KeyEvent.KEY_TYPED , letter_Validation(10));
}

/* Numeric Validation Limit the  characters to maxLengh AND to ONLY DigitS *************************************/
public EventHandler<KeyEvent> numeric_Validation(final Integer max_Lengh) {
    return new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent e) {
            TextField txt_TextField = (TextField) e.getSource();                
            if (txt_TextField.getText().length() >= max_Lengh) {                    
                e.consume();
            }
            if(e.getCharacter().matches("[0-9.]")){ 
                if(txt_TextField.getText().contains(".") && e.getCharacter().matches("[.]")){
                    e.consume();
                }else if(txt_TextField.getText().length() == 0 && e.getCharacter().matches("[.]")){
                    e.consume(); 
                }
            }else{
                e.consume();
            }
        }
    };
}    
/*****************************************************************************************/

 /* Letters Validation Limit the  characters to maxLengh AND to ONLY Letters *************************************/
public EventHandler<KeyEvent> letter_Validation(final Integer max_Lengh) {
    return new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent e) {
            TextField txt_TextField = (TextField) e.getSource();                
            if (txt_TextField.getText().length() >= max_Lengh) {                    
                e.consume();
            }
            if(e.getCharacter().matches("[A-Za-z]")){ 
            }else{
                e.consume();
            }
        }
    };
}    
/*****************************************************************************************/

best of luck. ^^

于 2014-06-18T04:35:07.640 回答
0

此方法让 TextField 完成所有处理(复制/粘贴/撤消安全)。不需要进行扩展类。并允许您决定每次更改后如何处理新文本(将其推送到逻辑,或返回到先前的值,甚至修改它)。

  // fired by every text property change
textField.textProperty().addListener(
  (observable, oldValue, newValue) -> {
    // Your validation rules, anything you like
      // (! note 1 !) make sure that empty string (newValue.equals("")) 
      //   or initial text is always valid
      //   to prevent inifinity cycle
    // do whatever you want with newValue

    // If newValue is not valid for your rules
    ((StringProperty)observable).setValue(oldValue);
      // (! note 2 !) do not bind textProperty (textProperty().bind(someProperty))
      //   to anything in your code.  TextProperty implementation
      //   of StringProperty in TextFieldControl
      //   will throw RuntimeException in this case on setValue(string) call.
      //   Or catch and handle this exception.

    // If you want to change something in text
      // When it is valid for you with some changes that can be automated.
      // For example change it to upper case
    ((StringProperty)observable).setValue(newValue.toUpperCase());
  }
);

对于您的情况,只需在其中添加此逻辑即可。完美运行。

    // For example 10 characters     
  if (newValue.length() >= 10) ((StringProperty)observable).setValue(oldValue);
于 2014-10-31T11:09:49.710 回答