JavaFX 8 双重绑定单向验证

JavaFX 8 Double binding one way validation

在我的应用程序中,TextField 双向绑定到窗格的 layoutXProperty。文本字段中的任何更改都会立即更新窗格的 layoutX。

例如,如果 layoutX 为 500 并且删除了最后一个零,则窗格的位置会立即跳到 x = 50。同样,如果围绕 texfield 中的值拖动窗格,则会反映窗格的 layoutX 值。

'dragging' 绑定很好,但现在我想在更新窗格的 layoutX 之前等待文本字段中的 RETURN 键(我想先检查有效的边界值)。我已经尝试过 ChangeListeners 和低级绑定,但无法令人满意地工作。

有没有人对如何实现这个有任何想法。在我看来,这将是一个常见的动作,但书籍或网络上关于此的信息非常少。可以更改监听器 'consume a binding event' 吗?

我找到了 'a' 解决方案,但它似乎更像是一个拼凑。

我将 onMouseClicked 和 onActon 处理程序附加到文本字段。

在文本字段 onMouseClicked 中,我取消绑定文本字段并等待 RETURN 键被按下。在 onAction 中,我用新的文本字段值更新先前绑定的值,然后再次双向绑定它。瞧......它有效。

我可以检查 onAction 处理程序中的有效值。

但就像我说的那样,这似乎有点创可贴的解决方案。有人愿意发表评论吗?

只需向更新文本字段的 layoutX 属性 添加一个侦听器,然后向执行验证并更新 TextField 的操作事件添加一个处理程序 layoutX 属性。如果用户按下 Enter,处理程序将被触发,但之前不会。由于您不希望 layoutX 在文本更新时得到更新,因此绑定在这里并不合适。

SSCCE:

import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class PaneCoordinatesInTextField extends Application {

    @Override
    public void start(Stage primaryStage) {
        Label draggingLabel = createDraggingLabel();

        TextField textField = new TextField();
        draggingLabel.layoutXProperty().addListener((obs, oldLayoutX, newLayoutX) ->
            textField.setText(newLayoutX.toString()));

        textField.setOnAction(e -> {
            try {
                double x = Double.parseDouble(textField.getText());
                if (x < 0) {
                     x = 0 ;
                }
                if (x > 400) {
                    x = 400 ;
                }
                draggingLabel.setLayoutX(x);
            } catch (NumberFormatException exc) {
                textField.setText(Double.toString(draggingLabel.getLayoutX()));
            }
        });

        Pane pane = new Pane(draggingLabel);
        pane.setMinSize(600, 600);

        BorderPane root = new BorderPane(pane, textField, null, null, null);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    private Label createDraggingLabel() {
        Label label = new Label("Drag me", new Rectangle(200, 200, Color.CORNFLOWERBLUE));
        label.setContentDisplay(ContentDisplay.CENTER);
        label.setTextFill(Color.WHITE);
        label.setAlignment(Pos.CENTER);

        ObjectProperty<Point2D> mouseLocation = new SimpleObjectProperty<>();
        label.setOnDragDetected(e -> mouseLocation.set(new Point2D(e.getSceneX(), e.getSceneY())));
        label.setOnMouseReleased(e -> mouseLocation.set(null));
        label.setOnMouseDragged(e -> {
            if (mouseLocation.get() == null) {
                return ;
            }
            double deltaX = e.getSceneX() - mouseLocation.get().getX();
            double deltaY = e.getSceneY() - mouseLocation.get().getY();
            label.setLayoutX(label.getLayoutX() + deltaX);
            label.setLayoutY(label.getLayoutY() + deltaY);
            mouseLocation.set(new Point2D(e.getSceneX(), e.getSceneY()));
        });
        return label;
    }

    public static void main(String[] args) {
        launch(args);
    }
}