在 JavaFX Spinner 中手动输入文本不会更新值(除非用户按 ENTER)
Posted
技术标签:
【中文标题】在 JavaFX Spinner 中手动输入文本不会更新值(除非用户按 ENTER)【英文标题】:Manually typing in text in JavaFX Spinner is not updating the value (unless user presses ENTER) 【发布时间】:2015-11-27 05:07:15 【问题描述】:在用户明确按下回车键之前,Spinner 控件似乎不会更新手动输入的值。因此,他们可以输入一个值(而不是按 Enter)退出控件,然后提交表单,并且微调器中显示的值不是微调器的值,而是旧值。
我的想法是为失去焦点事件添加一个侦听器,但我看不到获得对输入值的访问权限的方法?
spinner.focusedProperty().addListener((observable, oldValue, newValue) ->
//if focus lost
if(!newValue)
//somehow get the text the user typed in?
);
这是一种奇怪的行为,它似乎违反了 GUI 微调控件的约定。
【问题讨论】:
【参考方案1】:不幸的是,Spinner 的行为不如预期:在大多数操作系统中,它应该在焦点丢失时提交编辑的值。更不幸的是,它没有提供任何配置选项来轻松使其按预期运行。
所以我们必须手动将侦听器中的值提交给focusedProperty。从好的方面来说,Spinner 已经有这样做的代码 - 它是私有的,但我们必须 c&p 它
/**
* c&p from Spinner
*/
private <T> void commitEditorText(Spinner<T> spinner)
if (!spinner.isEditable()) return;
String text = spinner.getEditor().getText();
SpinnerValueFactory<T> valueFactory = spinner.getValueFactory();
if (valueFactory != null)
StringConverter<T> converter = valueFactory.getConverter();
if (converter != null)
T value = converter.fromString(text);
valueFactory.setValue(value);
// useage in client code
spinner.focusedProperty().addListener((s, ov, nv) ->
if (nv) return;
//intuitive method on textField, has no effect, though
//spinner.getEditor().commitValue();
commitEditorText(spinner);
);
注意有一个方法
textField.commitValue()
我本来希望......好吧......提交价值,这没有效果。它(最终!)实施以更新 textFormatter 的值(如果可用)。即使您使用textFormatter for validation,在微调器中也不起作用。可能是缺少一些内部侦听器,或者微调器尚未更新到相对较新的 api - 不过没有挖掘。
更新
在使用 TextFormatter 进行更多操作时,我注意到在 focusLost 上的格式化程序 guarantees to commit:
当控件失去焦点或提交时更新值(仅限TextField)
这确实像记录的那样工作,这样我们就可以向格式化程序的 valueProperty 添加一个侦听器,以便在提交值时得到通知:
TextField field = new TextField();
TextFormatter fieldFormatter = new TextFormatter(
TextFormatter.IDENTITY_STRING_CONVERTER, "initial");
field.setTextFormatter(fieldFormatter);
fieldFormatter.valueProperty().addListener((s, ov, nv) ->
// do stuff that needs to be done on commit
);
提交的触发器:
用户点击 ENTER 控制失去焦点 field.setText 以编程方式调用(这是未记录的行为!)回到微调器:我们可以使用格式化程序值的这种 commit-on-focusLost 行为来强制提交微调器工厂的值。类似的东西
// normal setup of spinner
SpinnerValueFactory factory = new IntegerSpinnerValueFactory(0, 10000, 0);
spinner.setValueFactory(factory);
spinner.setEditable(true);
// hook in a formatter with the same properties as the factory
TextFormatter formatter = new TextFormatter(factory.getConverter(), factory.getValue());
spinner.getEditor().setTextFormatter(formatter);
// bidi-bind the values
factory.valueProperty().bindBidirectional(formatter.valueProperty());
请注意,编辑(键入或以编程方式替换/附加/粘贴文本)不会触发提交 - 因此,如果需要提交文本更改,则不能使用。
【讨论】:
对于那些来寻找这个答案的人来说,为什么他们的微调器在按下回车后没有更新:你可能正在使用 ChangeListener - 它只有在值发生真正变化时才会激活, JavaFX 有时似乎会出错。尝试使用 InvalidationListener。 @kleopatra 我希望该网站通知您有关此评论,我对the answer below from Sergio 有疑问。你对此有什么想法吗?这似乎是一个简短而甜蜜的解决方案,感觉好得令人难以置信。它只比你的新一年,如果它那么好,我希望它现在会超过你的答案分数。塞尔吉奥的回答是否有需要注意的问题?还是人们一找到您的解决方案就停止滚动?当我试图在该答案下标记您时,我认为它不起作用。 @pateksan 很长一段时间没有关注这个问题(忘记了底层错误是否同时得到修复)——另一个答案是基于未指定的实现细节的破解。这种行为有点出乎意料,IMO - 我们也可以在编辑时拒绝 inc/dec。但是,如果没有提供某些东西,我们必须去做任何工作:) @kleopatra,谢谢。我还有另一个问题(对不起,如果我把你拖入一个老问题):当你说我们在编辑时也可以拒绝 inc/dec 时,我有点困惑?拒绝 inc/dec 似乎与 OPs 问题无关。这是塞尔吉奥代码的副作用吗?还是实现 OP 最初目标的另一种方法?另外,我还是新手,但如果你把它放在塞尔吉奥的回答而不是你的回答下,你的评论(上面的评论,就在一个小时前)可能对每个人都有用 - 如果建议这个是不礼貌的,我很抱歉代表多 2^8 的人。 @pateksan 简短(也是最后一个)回答:是的 - 对不起,这太离题了 - 要了解所有关于引用的知识,请阅读与弱/强引用相关的 Java 基础教程并将你学到的知识应用到这个上下文中(通过思考这里的参考路径,编写测试,在卡住时提出问题.. :) 玩得开心!【参考方案2】:@kleopatra 朝着正确的方向前进,但是复制粘贴解决方案感觉很尴尬,并且基于 TextFormatter 的解决方案根本不适合我。所以这是一个较短的,它迫使 Spinner 根据需要将其称为私有的 commitEditorText() :
spinner.focusedProperty().addListener((observable, oldValue, newValue) ->
if (!newValue)
spinner.increment(0); // won't change value, but will commit editor
);
【讨论】:
谢谢!像魅力一样工作。 不错的答案。工作过 @kleopatra 和其他任何人:如果这是一个如此简短而甜蜜的解决方案,为什么它仍然没有超过 kleopatra 答案的分数?这两个答案现在几乎都在 5 岁左右。这个答案是否存在用户需要注意的问题?人们只是向下滚动得不够远吗?【参考方案3】:根据文档,这是控件的标准行为:
editable 属性用于指定用户输入是否能够 在 Spinner 编辑器中输入。如果 editable 为真,用户输入将 一旦用户键入并按下 Enter 键,就会收到。在这 点输入传递给 SpinnerValueFactory 转换器 StringConverter.fromString(String) 方法。返回值来自 这个调用(类型 T)然后被发送到 SpinnerValueFactory.setValue(Object) 方法。如果该值有效,则 将保留为值。如果无效,值工厂将 需要做出相应的反应并取消此更改。
也许您可以使用键盘事件来监听并随时调用控件上的编辑提交。
【讨论】:
你确定了文档 :-) 但是:与往常一样,使用低级事件 (fi keyEvent) 作为触发器来验证/提交文本是一个坏主意,因为还有其他输入方式(如 fi粘贴) 你可以试试这个.... spinner.getEditor().textProperty().addListener((observable, oldValue, newValue) -> commitEditorText(); ); 是的,如果您想在输入/更改任何文本时提交值【参考方案4】:这是 Sergio 解决方案的改进变体。
initialize 方法会将 Sergio 的代码附加到控制器中的所有 Spinner。
public void initialize(URL location, ResourceBundle resources)
for (Field field : getClass().getDeclaredFields())
try
Object obj = field.get(this);
if (obj != null && obj instanceof Spinner)
((Spinner) obj).focusedProperty().addListener((observable, oldValue, newValue) ->
if (!newValue)
((Spinner) obj).increment(0);
);
catch (IllegalAccessException e)
e.printStackTrace();
【讨论】:
【参考方案5】:使用监听器应该可以。您可以通过微调器的编辑器访问输入的值:
spinner.getEditor().getText();
【讨论】:
也许你忘记了解释如何做到这一点的部分? 原始问题显示了如何添加侦听器,OP 不确定的部分是“一种访问输入值的方法”,如我的回答所示。【参考方案6】:我使用另一种方法 - 在输入时实时更新。这是我目前的实现:
getEditor().textProperty().addListener _, _, nv ->
// let the user clear the field without complaining
if(nv.isNotEmpty())
Double newValue = getValue()
try
newValue = getValueFactory().getConverter().fromString(nv)
catch (Exception e) /* user typed an illegal character */
getValueFactory().setValue(newValue)
【讨论】:
【参考方案7】:我使用了这种方法
public class SpinnerFocusListener<T> implements ChangeListener<Boolean>
private Spinner<T> spinner;
public SpinnerFocusListener(Spinner<T> spinner)
super();
this.spinner = spinner;
this.spinner.getEditor().focusedProperty().addListener(this);
@Override
public void changed(ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue)
StringConverter<T>converter=spinner.getValueFactory().getConverter();
TextField editor=spinner.getEditor();
String text=editor.getText();
try
T value=converter.fromString(text);
spinner.getValueFactory().setValue(value);
catch(Throwable ex)
editor.setText(converter.toString(spinner.getValue()));
【讨论】:
以上是关于在 JavaFX Spinner 中手动输入文本不会更新值(除非用户按 ENTER)的主要内容,如果未能解决你的问题,请参考以下文章
单击 JavaFX 突出显示 TextField 中的所有文本