it-swarm.com.de

JavaFX: Wie kann ich die Fokus-Traversal-Richtlinie ändern?

Ist es in JavaFX möglich, die focus-Durchquerungsrichtlinie zu ändern wie in AWT?

Weil die Durchsetzungsreihenfolge für zwei meiner HBoxes falsch ist. 

21
Sonja

In der Regel erfolgt die Navigation in einer Containerreihenfolge, in der Reihenfolge der Kinder oder nach Drücken der Pfeiltasten. Sie können die Reihenfolge der Knoten ändern - in dieser Situation ist dies die optimale Lösung für Sie.

In JFX gibt es eine Hintertür über die Substitutionsstrategie der Traversal-Engine: 

sie können die interne Klasse com.Sun.javafx.scene.traversal.TraversalEngine unterteilen

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

Und verwenden 

setImpl_traversalEngine(engine); 

rufen Sie an, um diesen Motor anzuwenden.

Sie können den Code von OpenJFX beobachten, um zu verstehen, wie es funktioniert und was Sie tun können.

Seien Sie sehr vorsichtig: Es handelt sich um eine interne API, die sich wahrscheinlich in nächster Zukunft ändern wird. Verlassen Sie sich also nicht darauf (Sie können sich sowieso nicht auf dieses Amt verlassen).

Beispielimplementierung: 

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

Es wird starke analytische Fähigkeiten für häufige Fälle erfordern;)

11
Alexander Kirov

Die einfachste Lösung besteht darin, die FXML-Datei zu bearbeiten und die Container entsprechend neu anzuordnen. Zum Beispiel verfügt meine aktuelle Anwendung über einen Registrierungsdialog, in dem eine Seriennummer eingegeben werden kann. Dafür gibt es 5 Textfelder. Damit der Fokus korrekt von einem Textfeld zum anderen übertragen wird, musste ich sie folgendermaßen auflisten:

<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

Bluehairs Antwort ist richtig, aber Sie können dies sogar im JavaFX Scene Builder tun.

In der linken Spalte befindet sich das Hierarchiefenster. Es sind alle Ihre Komponenten aus der Szene. Ihre Reihenfolge stellt die Fokusdurchquerungsreihenfolge dar und entspricht ihrer Reihenfolge in der FXML-Datei.

Ich habe diesen Tipp auf dieser Webseite gefunden: www.wobblycogs.co.uk

11
none_

Dies ist die akzeptierte Antwort, die an die Änderung der internen API angepasst ist (irgendwann in fx-8, meine aktuelle Version ist 8u60b5). Natürlich gilt weiterhin der ursprüngliche Haftungsausschluss: Es ist internal api und kann jederzeit ohne vorherige Ankündigung geändert werden! 

Die Änderungen (im Vergleich zur akzeptierten Antwort)

  • Eltern benötigt eine TraversalEngine vom Typ ParentTraversalEngine
  • nav ist nicht mehr eine Methode von TraversalEngine (oder ParentTE), sondern nur noch von TopLevelTraversalEngine
  • die Navigationsimplementierung wird an eine Strategie namens Algorithmus delegiert
  • die eigentliche Fokusübertragung wird von TopLevelTE durchgeführt (scheint es?), der Algorithmus findet nur das neue Ziel und gibt es zurück

Die einfache Übersetzung des Beispielcodes:

/**
 * 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

Wir verwenden JavaFX-Ereignisfilter , zum Beispiel:

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

Die event.consume() unterdrückt die voreingestellte Fokusdurchquerung, die sonst beim Aufruf von requestFocus() zu Problemen führt.

5
dwagelaar

Sie können dies einfach mit SceneBuilder tun. Öffnen Sie die fxml-Datei mit scenebuilder und gehen Sie zur Hierarchie unter der Registerkarte Dokument . Platzieren Sie die Eingabesteuerelemente in der gewünschten Reihenfolge, indem Sie die Eingabesteuerelemente ziehen. Denken Sie daran, in den Eigenschaften das Kontrollkästchen Fokus-Traversal zu aktivieren. Wenn Sie die Tab-Leiste drücken, funktioniert die Fokus-Traversal-Richtlinie einwandfrei.

chnage focus traversal policy with SB

1
Jamith Nimantha

Gehen Sie im Scene Builder zum Menü Ansicht und wählen Sie Dokument anzeigen. Auf der linken Seite werden alle Objekte in Ihrem aktuellen fxml-Dokument angezeigt. Ziehen Sie die Steuerelemente in der Liste nach oben oder unten, um den Tab-Index neu anzuordnen. Wählen Sie Dokument ausblenden aus, um die anderen Werkzeuge zu verwenden, da der Dokumentbereich den Platz belegt.

1
Chris

Sie können NodeName.requestFocus() wie oben beschrieben verwenden. Stellen Sie außerdem sicher, dass Sie diesen Fokus anfordern, nachdem Sie alle Knoten für das Root-Layout instanziiert und hinzugefügt haben, da sich der Fokus dadurch ändert.

0
Aaron

Ich habe Eventfilter als Lösung in Verbindung mit der Referenzierung der Feld-IDs verwendet. Sie müssen also nur die Felder wie (Feld1, Feld2, Feld3, Feld4) benennen.

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