我要求限制用户可以输入TextField
JavaFX控件的字符数。我像{<}>那样延长了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();
}
}
});
}
}
这确实按预期工作。但是,假设对于特定的LengthLimitedTextField
,maxCharacters
设置为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)
我不确定如何解决这个问题。一个可能的(但不是理想的)解决方案是完全禁用Undo / Redo,但如果没有完全覆盖上下文菜单(根据此SO answer,这并不容易)并且默认的键盘快捷方式,这似乎是不可能的。
所以最终,我的问题是双重的:
是否有其他方法可以限制TextField
的字符数而不会在撤消时抛出异常?或者是否有一种干净的方法可以在应用程序中完全禁用撤消?
编辑:我做了更多研究,根据https://javafx-jira.kenai.com/browse/RT-30881,这似乎是一个错误。见this comment。那么也许这是不可能实现的呢?我将打开这个问题,希望有人确实有可能的解决方法。
答案 0 :(得分:4)
我将如何做到这一点: 我会使用普通的文本字段,并添加一个事件过滤器。
设置:
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();
}
}
};
}
答案 1 :(得分:3)
在 Magcus 代码中添加一些香料
@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();
}
}
};
}
/*****************************************************************************************/
祝你好运。 ^^
答案 2 :(得分:2)
这是在JavaFX 8上使用Lambda Expressions的另一种解决方案
textField.textProperty().addListener(
(observable,oldValue,newValue)-> {
if(newValue.length() > 5) cp.setText(oldValue);
}
);
如果textField长度超过5,则不会插入更多文本。
答案 3 :(得分: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);