2015-08-20 3 views
0

Dans mon application, un TextField est lié bidirectionnellement à layoutXProperty d'un volet. Toute modification dans le champ de texte mettra à jour la présentationX du volet immédiatement. Par exemple, si layoutX est 500 et que le dernier zéro est supprimé, la position du volet saute immédiatement à x = 50. De même, si le volet est déplacé autour de la valeur dans le champ texfield reflète la valeur layoutX du volet. La liaison "glisser" est très bien, mais maintenant je veux attendre la touche RETOUR dans le champ de texte avant que le layoutX de la fenêtre ne soit mis à jour (je veux d'abord vérifier les valeurs limites valides). J'ai essayé ChangeListeners et la liaison de bas niveau, mais je ne peux pas le faire fonctionner de manière satisfaisante.JavaFX 8 Validation unidirectionnelle à liaison double

Est-ce que quelqu'un a des idées sur la façon de mettre en œuvre cela. Il me semble que ce serait une action commune, mais il y a TRES peu d'informations dans les livres ou sur le web à ce sujet. Un changelistener peut-il consommer un événement contraignant?

+0

Il suffit d'utiliser deux écouteurs au lieu de fixations. Utilisez un écouteur pour un 'ActionEvent' sur le' TextField' –

Répondre

1

Il suffit d'ajouter un écouteur à la propriété layoutX qui met à jour le champ de texte, et ajouter un gestionnaire pour un événement d'action au TextField qui effectue la validation et met à jour la propriété layoutX. Le gestionnaire sera viré si l'utilisateur appuie sur Entrée, mais pas avant. Puisque vous ne voulez pas que layoutX soit mis à jour chaque fois que le texte est mis à jour, une liaison n'est pas vraiment appropriée ici.

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); 
    } 
} 
+0

Ceci est le genre d'exemple que d'autres personnes peuvent trouver très utile lorsque vous cherchez une solution similaire - si bien fait pour poster un exemple complet. – Frank

0

J'ai trouvé une solution, mais cela ressemble plus à un kludge.

J'ai attaché un gestionnaire onMouseClicked et un gestionnaire onActon au champ de texte.

Dans le champ de texte onMouseClicked, je désolidarise le champ de texte et attend que la touche RETURN soit enfoncée. Dans l'onAction, je mets à jour la valeur précédemment liée avec la nouvelle valeur textfield, puis la lie de nouveau bidirectionnellement. Et voila ... ça marche.

Je peux vérifier les valeurs valides dans le gestionnaire onAction. Mais comme je l'ai dit, il semble un peu de solution bandaid. Quelqu'un a-t-il envie de commenter?

+0

Ouais je suis arrivé en quelque sorte moi-même. J'aime toujours le concept de liaison unique (pour la cohérence avec d'autres champs), donc je garderai la liaison de 'mise à jour par glisser' mais abandonnerai l'autre en faveur d'un écouteur pour un gestionnaire d'action. – Frank

+0

Si vous ne voulez pas qu'il change à chaque fois que le texte change, une reliure n'est pas ce que vous voulez. Utilisez simplement les écouteurs pour mettre à jour les valeurs directement. –