JavaFX: How to change the focus traversal policy?

后端 未结 10 1877
广开言路
广开言路 2020-11-30 04:34

Is it possible in JavaFX to change the focus traversal policy, like in AWT?

Because the traversal order for two of my HBoxes is wrong.

相关标签:
10条回答
  • 2020-11-30 05:30

    You can do this easily with SceneBuilder. Open fxml file with scenebuilder and go to hierarchy under Document tab. Place input controls into a order you want by dragging input controls. Remember to check Focus Traversal in properties. Then focus traversal policy will work nicely when you press tab bar.

    0 讨论(0)
  • 2020-11-30 05:33

    The simplest solution is to edit the FXML file and reorder the containers appropriately. As an example, my current application has a registration dialog in which a serial number can be entered. There are 5 text fields for this purpose. For the focus to pass from one text field to the other correctly, I had to list them in this way:

    <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" />
    
    0 讨论(0)
  • 2020-11-30 05:33

    We're using JavaFX event filters for this, e.g.:

    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();
            }
        }
    });
    

    The event.consume() suppresses the default focus traversal, which otherwise causes trouble when calling requestFocus().

    0 讨论(0)
  • 2020-11-30 05:34

    In common case the navigation is done in a container order, in order of children, or according to arrow keys pressing. You can change order of nodes - it will be the optimal solution for you in this situation.

    There is a back door in JFX about traversal engine strategy substitution :

    you can subclass the internal class com.sun.javafx.scene.traversal.TraversalEngine

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

    And use

    setImpl_traversalEngine(engine); 
    

    call to apply that engine.

    You can observe the code of OpenJFX, to understand, how it works, and what you can do.

    Be very careful : it is an internal API, and it is likely to change, possibly, in the nearest future. So don't rely on this (you cannot rely on this officialy, anyway).

    Sample implementation :

    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();
    }
    

    It will require strong analitical skills for common case ;)

    0 讨论(0)
提交回复
热议问题