web-dev-qa-db-fra.com

JavaFX: Comment changer la politique de traversée de focus?

Est-il possible dans JavaFX de changer la règle de traversée de focus , comme dans AWT?

Parce que l'ordre de traversée pour deux de mes HBoxes est erroné. 

21
Sonja

Dans les cas courants, la navigation s'effectue dans un ordre de conteneur, dans l'ordre des enfants ou en fonction des touches de direction. Vous pouvez changer l'ordre des nœuds - ce sera la solution optimale pour vous dans cette situation.

Il y a une porte dérobée dans JFX concernant le remplacement de la stratégie de moteur transversal: 

vous pouvez sous-classer la classe interne com.Sun.javafx.scene.traversal.TraversalEngine

engine = new TraversalEngine(this, false) {
            @Override public void trav(Node owner, Direction dir) {
                // do whatever you want
            }
        };

Et utilise 

setImpl_traversalEngine(engine); 

appelez pour appliquer ce moteur.

Vous pouvez observer le code de OpenJFX, pour comprendre, comment cela fonctionne et ce que vous pouvez faire.

Soyez très prudent: il s’agit d’une API interne qui risque d’être modifiée dans un proche avenir. Donc, ne comptez pas sur cela (vous ne pouvez pas vous fier officiellement à cela, de toute façon).

Exemple de mise en œuvre: 

public void start(Stage stage) throws Exception {
    final VBox vb = new VBox();

    final Button button1 = new Button("Button 1");
    final Button button2 = new Button("Button 2");
    final Button button3 = new Button("Button 3");

    TraversalEngine engine = new TraversalEngine(vb, false) {
        @Override
        public void trav(Node node, Direction drctn) {
            int index = vb.getChildren().indexOf(node);

            switch (drctn) {
                case DOWN:
                case RIGHT:
                case NEXT:
                    index++;
                    break;
                case LEFT:
                case PREVIOUS:
                case UP:
                    index--;
            }

            if (index < 0) {
                index = vb.getChildren().size() - 1;
            }
            index %= vb.getChildren().size();

            System.out.println("Select <" + index + ">");

            vb.getChildren().get(index).requestFocus();
        }
    };

    vb.setImpl_traversalEngine(engine);

    vb.getChildren().addAll(button1, button2, button3);
    Scene scene = new Scene(vb);
    stage.setScene(scene);
    stage.show();
}

Il faudra de fortes compétences analytiques pour les cas courants;)

11
Alexander Kirov

La solution la plus simple consiste à modifier le fichier FXML et à réorganiser les conteneurs de manière appropriée. Par exemple, mon application actuelle comporte une boîte de dialogue d’enregistrement dans laquelle un numéro de série peut être saisi. Il y a 5 champs de texte à cette fin. Pour que le focus passe correctement d'un champ de texte à l'autre, je devais les lister de la manière suivante:

<TextField fx:id="tfSerial1" layoutX="180.0" layoutY="166.0" prefWidth="55.0" />
<TextField fx:id="tfSerial2" layoutX="257.0" layoutY="166.0" prefWidth="55.0" />
<TextField fx:id="tfSerial3" layoutX="335.0" layoutY="166.0" prefWidth="55.0" />
<TextField fx:id="tfSerial4" layoutX="412.0" layoutY="166.0" prefWidth="55.0" />
<TextField fx:id="tfSerial5" layoutX="488.0" layoutY="166.0" prefWidth="55.0" />
17
Bluehair

La réponse de Bluehair est correcte, mais vous pouvez le faire même dans JavaFX Scene Builder.

Vous avez le panneau Hiérarchie dans la colonne de gauche. Il y a tous vos composants de la scène. Leur ordre représente l'ordre de traversée du focus et il répond à leur ordre dans le fichier FXML.

J'ai trouvé cette astuce sur cette page: www.wobblycogs.co.uk

11
none_

C'est la réponse acceptée adaptée au changement de l'API interne (arrivé à un moment de fx-8, ma version actuelle est 8u60b5). Évidemment, le disclaimer original reste valable: il s'agit de internal api, ouvert à modification sans préavis à tout moment! 

Les changements (par rapport à la réponse acceptée)

  • Le parent a besoin d'un TraversalEngine de type ParentTraversalEngine
  • nav n'est plus une méthode de TraversalEngine (ni de ParentTE) mais uniquement de TopLevelTraversalEngine
  • l'implémentation de navigation est déléguée à une stratégie appelée algorithme
  • le transfert de focus réel est (semble être?) géré par TopLevelTE, l’algorithme recherche et renvoie uniquement la nouvelle cible

La traduction simple de l'exemple de code:

/**
 * Requirement: configure focus traversal
 * old question with old hack (using internal api):
 * http://stackoverflow.com/q/15238928/203657
 * 
 * New question (closed as duplicate by ... me ..)
 * http://stackoverflow.com/q/30094080/203657
 * Old hack doesn't work, change of internal api
 * rewritten to new internal (sic!) api
 * 
 */
public class FocusTraversal extends Application {

    private Parent getContent() {
        final VBox vb = new VBox();

        final Button button1 = new Button("Button 1");
        final Button button2 = new Button("Button 2");
        final Button button3 = new Button("Button 3");

        Algorithm algo = new Algorithm() {

            @Override
            public Node select(Node node, Direction dir,
                    TraversalContext context) {
                Node next = trav(node, dir);
                return next;
            }

            /**
             * Just for fun: implemented to invers reaction
             */
            private Node trav(Node node, Direction drctn) {
                int index = vb.getChildren().indexOf(node);

                switch (drctn) {
                    case DOWN:
                    case RIGHT:
                    case NEXT:
                    case NEXT_IN_LINE:    
                        index--;
                        break;
                    case LEFT:
                    case PREVIOUS:
                    case UP:
                        index++;
                }

                if (index < 0) {
                    index = vb.getChildren().size() - 1;
                }
                index %= vb.getChildren().size();

                System.out.println("Select <" + index + ">");

                return vb.getChildren().get(index);
            }

            @Override
            public Node selectFirst(TraversalContext context) {
                return vb.getChildren().get(0);
            }

            @Override
            public Node selectLast(TraversalContext context) {
                return vb.getChildren().get(vb.getChildren().size() - 1);
            }

        };
        ParentTraversalEngine engine = new ParentTraversalEngine(vb, algo);
        // internal api in fx8
        // vb.setImpl_traversalEngine(engine);
        // internal api since fx9
        ParentHelper.setTraversalEngine(vb, engine);
        vb.getChildren().addAll(button1, button2, button3);
        return vb;
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setScene(new Scene(getContent()));
        primaryStage.show();
    }

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

Pour cela, nous utilisons les filtres d'événements JavaFX , par exemple:

cancelButton.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
    @Override
    public void handle(KeyEvent event) {
        if (event.getCode() == KeyCode.TAB && event.isShiftDown()) {
            event.consume();
            getDetailsPane().requestFocus();
        }
    }
});

La event.consume() supprime la traversée du focus par défaut, ce qui pose problème lors de l'appel de requestFocus().

5
dwagelaar

Dans le générateur de scènes, accédez au menu Affichage et sélectionnez Afficher le document. à gauche, tous les objets de votre document XML actuel. faites glisser les contrôles vers le haut ou le bas dans la liste pour réorganiser l'index de tabulation. Sélectionnez Masquer le document pour utiliser les autres outils, car le volet de document cache l’espace.

1
Chris

Vous pouvez utiliser NodeName.requestFocus() comme indiqué ci-dessus; De plus, assurez-vous de demander ce focus après l'instanciation et l'ajout de tous vos nœuds pour la mise en page racine, car le focus sera modifié.

0
Aaron

J'ai utilisé Eventfilter comme solution en combinaison avec le référencement des ID de champs. Il vous suffit donc de nommer les champs tels que (champ1, champ2, champ3, champ4) pour pouvoir les placer à l'endroit souhaité

mainScene.addEventFilter(KeyEvent.KEY_PRESSED, (event) -> {
        if(event.getCode().equals(KeyCode.TAB)){
            event.consume();
            final Node node =  mainScene.lookup("#field"+focusNumber);
            if(node!=null){
                node.requestFocus();
            }
            focusNumber ++;
            if(focusNumber>11){
              focusNumber=1;
            }
        }
    }); 
0
Patrick Eckert