2013-03-06 91 views
20

是否有可能在JavaFX中更改焦点遍历策略,就像在AWT中一样?JavaFX:如何更改焦点遍历策略?

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

+0

可以通过调用node.requestFocus()方法 – invariant 2013-03-06 04:52:57

+0

是的,我知道在JavaFX的重点要求任何节点,但这并不是一个非常巧妙的解决办法。 – Sonja 2013-03-06 06:12:49

+0

请清楚暴露您的问题,并提供和[sscce](http://sscce.org/)。 – gontard 2013-03-06 09:49:37

回答

10

在常见情况下,导航在容器中以便根据箭头键按下进行,在儿童的顺序,或。您可以更改节点的顺序 - 这将是您在这种情况下的最佳解决方案。

有在JFX后门约穿越引擎战略替代:

你也可以继承内部类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(); 
} 

这将需要对常见的情况强analitical技能;)

+5

fx8中的内部api似乎发生了相当大的变化,不再可能(可能需要通过实现自定义算法进行调整,使用它配置ParentFocusTraversalEngine) – kleopatra 2015-05-07 09:54:08

16

最简单的方法是编辑FXML文件,并适当地重新排列容器。作为一个例子,我目前的应用程序有一个注册对话框,其中可以输入序列号。有5个文本字段用于此目的。对于焦点从一个文本字段正确地传递给对方,我必须列出他们以这样的方式

<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

是的,这是最简单的方法。我遇到过同样的问题。这意味着我们正在设计UI场景生成器以附加顺序重新生成相关的FXML标签。一旦我们重新排列正确的顺序它工作正常。 – Channa 2014-03-23 15:10:24

+0

我在这里也做了同样的工作。 – ceklock 2015-03-20 04:03:09

+0

如果SB可以自动为我们做这件事,这将是很好的。 – User 2016-09-26 21:03:10

10

Bluehair的答案是正确的,但你甚至可以在JavaFX的场景生成器做到这一点。

您在左列层次面板。有来自现场的所有组件。它们的顺序表示焦点遍历顺序,并响应它们在FXML文件中的顺序。

我发现该网页上的这个提示:www.wobblycogs.co.uk

1

在场景生成器去查看菜单,然后选择显示文件。在左边将是当前fxml文档中的所有对象。在列表中向上或向下拖动控件以重新排列标签索引。由于文档窗格占据了空间,选择隐藏文档以使用其他工具。

5

我们使用JavaFX event filters为此,例如:

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

event.consume()抑制默认焦点遍历,调用requestFocus()时,否则会造成麻烦。

+0

这是最简单和最好的。谢谢。 – chris 2016-02-26 23:16:56

+0

在我的选择中,这是唯一可持续的答案。我们不调用内部API并且重新安排FXML中的节点并不总是可能的,例如,如果我们想要使用Java动态添加节点。 – aboger 2016-10-05 09:02:30

6

这是接受的答案适应内部api的变化(发生在fx-8的某个点,我现在的版本是8u60b5)。很显然,原始声明仍然适用:内部 API,随时可以随时更改,恕不另行通知!

的变化(相对于公认的答案)

  • 父需要类型ParentTraversalEngine的TraversalEngine
  • NAV不再TraversalEngine(也不ParentTE)的方法,但仅TopLevelTraversalEngine
  • 导航的实施被授予策略称为算法
  • 实际焦点转移(似乎是?)由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); 
     vb.setImpl_traversalEngine(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); 
    } 
} 
+0

有关完整算法的示例(需要在儿童中进行递归),请参阅ContainerTabOrder。但是对我来说太复杂了,我使用了一种解决方法来忽略选项卡策略中的按钮,在没有Java代码的FXML中:set'focusTraversable =“false”'。它将继续到下一个可以聚焦的因素。 – pdem 2016-05-12 09:00:28

0

我用Eventfilter作为解决方案结合引用字段的ID的,因此,所有你需要做的就是类似名称(字段1,字段2,字段3字段,字段4),所以你可以把其中u想要的字段:

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