【问题标题】:JavaFX binding between TextField and a propertyTextField 和属性之间的 JavaFX 绑定
【发布时间】:2014-05-28 08:20:06
【问题描述】:
如果您在 JavaFX TextField 和属性之间创建绑定,则此绑定在每次击键时都会失效,从而导致文本发生更改。
如果您有一个绑定链,则默认行为可能会导致问题,因为在中间编辑值可能无效。
好的,我知道我可以创建从属性到文本字段的单向绑定,并注册一个更改侦听器,以便在光标离开字段时获得通知,并在必要时手动更新属性。
是否有一种简单、优雅的方法来更改此行为,以便仅在编辑完成时绑定无效,例如光标何时离开字段?
谢谢
【问题讨论】:
标签:
java
binding
javafx
textfield
【解决方案1】:
我认为您已经描述了完成此操作的唯一方法。这是我能看到的最简洁的实现方式(使用 Java 8,但如果需要,可以很容易地将 lambda 转换回与 JavaFX 2.2 兼容):
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CommitBoundTextField extends Application {
@Override
public void start(Stage primaryStage) {
TextField tf1 = new TextField();
createCommitBinding(tf1).addListener((obs, oldText, newText) ->
System.out.printf("Text 1 changed from \"%s\" to \"%s\"%n", oldText, newText));
TextField tf2 = new TextField();
createCommitBinding(tf2).addListener((obs, oldText, newText) ->
System.out.printf("Text 2 changed from \"%s\" to \"%s\"%n", oldText, newText));
VBox root = new VBox(5, tf1, tf2);
Scene scene = new Scene(root, 250, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
private StringBinding createCommitBinding(TextField textField) {
StringBinding binding = Bindings.createStringBinding(() -> textField.getText());
textField.addEventHandler(ActionEvent.ACTION, evt -> binding.invalidate());
textField.focusedProperty().addListener((obs, wasFocused, isFocused)-> {
if (! isFocused) binding.invalidate();
});
return binding ;
}
public static void main(String[] args) {
launch(args);
}
}
【解决方案2】:
我意识到我的回复有点晚了,但我认为这可能对某人有用。
在使用TextFields 时,我经常附上TextFormatter 来帮助验证条目。您可以将侦听器附加到格式化程序的valueProperty。该属性在提交文本时更新,而不是在每次击键时更新。
这是我正在谈论的使用专门用于整数输入的TextField 的示例。当您在文本字段中进行编辑时,当您点击 Enter、单击按钮失去焦点、切换到其他窗口等时,更改将反映在 Label 中。
import javafx.application.Application;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.converter.IntegerStringConverter;
class IntTextField extends TextField {
private final IntegerProperty value;
TextFormatter<Integer> formatter;
public double getValue() {
return value.getValue();
}
public void setValue(int newValue) {
value.setValue(newValue);
}
public IntegerProperty valueProperty() {
return value;
}
public StringBinding getStringBinding () {
return value.asString();
}
IntTextField(int initValue) {
value = new SimpleIntegerProperty(initValue);
setText(initValue + "");
formatter = new TextFormatter(new IntegerStringConverter(), initValue);
formatter.valueProperty().addListener((ObservableValue<? extends Integer> obs,
Integer oldValue, Integer newValue) -> value.setValue(newValue));
setTextFormatter(formatter);
}
IntTextField() {
this(0);
}
}
public class TFBindingDemo extends Application {
@Override
public void start(Stage stage) {
stage.setTitle("TFBindingDemo");
IntTextField intTextField = new IntTextField(12345);
intTextField.setMaxWidth(150);
Label label = new Label("Type in the TextField");
label.textProperty().bind(intTextField.getStringBinding());
Button removeFocusButton = new Button("Click Here to Remove Focus");
VBox root = new VBox(20, intTextField, label, removeFocusButton);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(20));
Scene scene = new Scene(root, 325, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}