JavaFX:如何更改焦点遍历策略?

在JavaFX中是否可以更改焦点遍历策略 ,例如在AWT中?

因为我的两个HBox的遍历顺序是错误的。

在通常情况下,导航以容器顺序,按子女的顺序或按箭头键按下完成。 您可以更改节点的顺序 – 在这种情况下,它将是您的最佳解决方案。

JFX中有一个关于遍历引擎策略替换的后门:

你可以inheritance内部类com.sun.javafx.scene.traversal.TraversalEngine

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

并使用

 setImpl_traversalEngine(engine); 

打电话申请该引擎。

您可以观察OpenJFX的代码,了解它,它是如何工作的,以及您可以做什么。

要非常小心:它是一个内部API,可能会在最近的将来发生变化。 所以不要依赖于此(无论如何,你不能依靠这个官方)。

示例实施:

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

普通案件需要强大的分析能力;)

最简单的解决方案是编辑FXML文件并适当地重新排序容器。 例如,我当前的应用程序有一个注册对话框,可以在其中输入序列号。 为此目的有5个文本字段。 为了使焦点从一个文本字段正确传递到另一个文本字段,我必须以这种方式列出它们:

      

Bluehair的答案是对的,但即使在JavaFX Scene Builder中也可以这样做。

左列中有“层次结构”面板。 场景中有你的所有组件。 它们的顺序代表焦点遍历顺序,它响应它们在FXML文件中的顺序。

我在这个网页上找到了这个小贴士: http://www.wobblycogs.co.uk

这是适用于内部api更改的公认答案(发生在fx-8的某个点,我目前的版本是8u60b5)。 显然原始的免责声明仍然适用:它是内部 API,随时可以随时更改,恕不另行通知!

变化(与接受的答案相比)

  • 父需要一个ParentTraversalEngine类型的TraversalEngine
  • nav不再是TraversalEngine(也不是ParentTE)的方法,而只是TopLevelTraversalEngine的方法
  • 导航实现被委托给名为Algorithm的策略
  • 实际的焦点转移是(似乎是?)由TopLevelTE处理,算法只找到并返回新的目标

示例代码的简单翻译:

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

我们正在使用JavaFX事件filter ,例如:

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

event.consume()禁止默认的焦点遍历,否则在调用requestFocus()时会导致麻烦。

在场景构建器中,转到视图菜单并选择显示文档。 在左侧将是您当前fxml文档中的所有对象。 在列表中向上或向下拖动控件以重新排序选项卡索引。 选择隐藏文档以使用其他工具,因为文档窗格占用了空间。

我使用Eventfilter作为解决方案并结合引用字段ID,因此您所要做的就是将字段命名为(field1,field2,field3,field4),以便将字段放在您想要的位置:

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

您可以使用上面所述的NodeName.requestFocus() ; 此外,确保在实例化并添加根布局的所有节点后请求此焦点,因为当您这样做时,焦点将会改变。