JavaFX: ¿Cómo cambiar la política de cruce de enfoque?

¿Es posible en JavaFX cambiar la política de cruce de foco , como en AWT?

Porque el orden transversal para dos de mis HBox es incorrecto.

En el caso común, la navegación se realiza en un orden de contenedor, en orden de niños, o de acuerdo con las teclas de flecha presionando. Puede cambiar el orden de los nodos: será la solución óptima para usted en esta situación.

Hay una puerta trasera en JFX sobre la sustitución de la estrategia del motor transversal:

puede subclasificar la clase interna com.sun.javafx.scene.traversal.TraversalEngine

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

Y use

 setImpl_traversalEngine(engine); 

llamar para aplicar ese motor.

Puede observar el código de OpenJFX, comprender, cómo funciona y qué puede hacer.

Tenga mucho cuidado: es una API interna, y es probable que cambie, posiblemente, en un futuro cercano. Así que no confíe en esto (no puede confiar en esto oficialmente, de todos modos).

Implementación de ejemplo:

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

Exigirá fuertes habilidades analíticas para el caso común;)

La solución más simple es editar el archivo FXML y reordenar los contenedores de manera apropiada. Como ejemplo, mi aplicación actual tiene un cuadro de diálogo de registro en el que se puede ingresar un número de serie. Hay 5 campos de texto para este propósito. Para que el enfoque pase de un campo de texto al otro correctamente, tuve que enumerarlos de esta manera:

      

La respuesta de Bluehair es correcta, pero puedes hacerlo incluso en JavaFX Scene Builder.

Tienes el panel Jerarquía en la columna izquierda. Están todos tus componentes de la escena. Su orden representa el orden transversal de enfoque y responde a su orden en un archivo FXML.

Encontré este consejo en esta página web: http://www.wobblycogs.co.uk

Esta es la respuesta aceptada adaptada al cambio de api interna (sucedió en algún punto de fx-8, mi versión actual es 8u60b5). Obviamente, el descargo de responsabilidad original aún se aplica: es una API interna , ¡está abierta a cambios sin previo aviso en cualquier momento!

Los cambios (en comparación con la respuesta aceptada)

  • El padre necesita un motor Traversal de tipo ParentTraversalEngine
  • nav ya no es un método de TraversalEngine (ni de ParentTE) sino solo de TopLevelTraversalEngine
  • la implementación de navegación se delega a la estrategia llamada Algoritmo
  • la transferencia de enfoque real es (¿parece ser?) manejada por TopLevelTE, Algorithm solo encuentra y devuelve el nuevo objective

La traducción simple del código de ejemplo:

 /** * 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); } } 

Estamos utilizando filtros de eventos JavaFX para esto, por ejemplo:

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

El event.consume() suprime el recorrido de enfoque predeterminado, que de lo contrario causa problemas al llamar a requestFocus() .

En el generador de escenas vaya al menú de visualización y seleccione mostrar documento. a la izquierda estarán todos los objetos en su documento actual de fxml. arrastre los controles hacia arriba o hacia abajo en la lista para reordenar para índice de tabs. Seleccione ocultar documento para usar las otras herramientas, ya que el panel del documento ocupa el espacio.

Usé Eventfilter como solución en combinación con referencias a los ID de campo, por lo que todo lo que tienes que hacer es nombrar los campos como (campo1, campo2, campo3, campo4) para que puedas colocar los campos donde quieras:

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

Puede usar NodeName.requestFocus() como se dijo anteriormente; Además, asegúrese de solicitar este enfoque después de crear instancias y agregar todos sus nodos para el diseño de la raíz, ya que al hacerlo, el enfoque cambiará.