【问题标题】:Tab key navigation in JavaFX TextAreaJavaFX TextArea 中的 Tab 键导航
【发布时间】:2012-10-03 08:27:26
【问题描述】:

如何让 TextArea 中的 Tab 键导航到下一个控件?

我可以为按键按下事件添加一个侦听器,但是如何使 TextArea 控件失去焦点(不知道要聚焦的链中的下一个字段)?

@FXML protected void handleTabKeyTextArea(KeyEvent event) {
    if (event.getCode() == KeyCode.TAB) {
        ...
    }
}

【问题讨论】:

    标签: java user-interface javafx-2 javafx


    【解决方案1】:

    如果按 TAB,则此代码遍历焦点,如果按 CONTROL+TAB,则插入标签

    textArea.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                if (event.getCode() == KeyCode.TAB) {
                    SkinBase skin = (SkinBase) textArea.getSkin();
                    if (skin.getBehavior() instanceof TextAreaBehavior) {
                        TextAreaBehavior behavior = (TextAreaBehavior) skin.getBehavior();
                        if (event.isControlDown()) {
                            behavior.callAction("InsertTab");
                        } else {
                            behavior.callAction("TraverseNext");
                        }
                        event.consume();
                    }
    
                }
            }
        });
    

    【讨论】:

    • 一个小问题:它应该检查 event.isShiftDown() 应该调用“TraversePrevious”,而不是“TraverseNext”。
    • 至少对于 JavaFX 8,SkinBase 应该改为 TextAreaSkin。
    【解决方案2】:

    我使用遍历方法

    @Override
    public void handle(KeyEvent event) {
        if (event.getCode().equals(KeyCode.TAB)) {
            Node node = (Node) event.getSource();
            if (node instanceof TextField) {
                TextFieldSkin skin = (TextFieldSkin) ((TextField)node).getSkin();
                if (event.isShiftDown()) {
                    skin.getBehavior().traversePrevious();
                }
                else {
                    skin.getBehavior().traverseNext();
                }               
            }
            else if (node instanceof TextArea) {
                TextAreaSkin skin = (TextAreaSkin) ((TextArea)node).getSkin();
                if (event.isShiftDown()) {
                    skin.getBehavior().traversePrevious();
                }
                else {
                    skin.getBehavior().traverseNext();
                }
            }
    
            event.consume();
        }
    }
    

    【讨论】:

    • 这个解决方案是我发现的最干净的。虽然没有必要为 TextFields 定义它,因为这已经是默认设置(至少在 Java 8 中)。
    • 但问题是,它会强制您扩展 TextArea - 如果您使用 Scene Builder,则不是很方便。
    • ...实际上@Override 让我失望了,TextArea 中没有要覆盖的句柄方法,所以我想这只是一个常规的处理程序方法。不过,我希望有一个更简单的方法 - 哦,好吧。
    • ...实际上,有一种方法可以让它更容易重复使用!将一个专门用于此的静态类事件处理程序放在公共的地方,并在 TextArea 上调用它: .addEventFilter(KeyEvent.KEY_PRESSED, new UsabilityUtil.TabTraversalEventHandler());
    • TextAreaSkin 在 Java 1.8.0_311-b11 中无法解析,但 MarcG 的解决方案运行良好。
    【解决方案3】:

    我遇到了同样的问题,我喜欢 Tom 使用的遍历方法。 但我也想在按下 ctrl+tab 时插入一个选项卡。

    电话

    behavior.callAction("InsertTab");
    

    不适用于 JavaFX8。查看 TextAreaBehaviour 类,我发现现在有一个“TraverseOrInsertTab”动作。

    但是,我认为这种动作调用在多个 java 版本中非常不稳定,因为它依赖于传递的字符串。

    所以我没有使用 callAction() 方法,而是使用了

    textArea.replaceSelection("\t");
    

    【讨论】:

      【解决方案4】:

      如果选项卡 - 焦点问题的不同解决方案。 CTRL+TAB 键的 TextArea 的默认行为是将焦点移动到下一个控件。所以我用 CTRL+TAB 键事件替换了 TAB 键事件,当用户按下 CTRL+TAB 时,TextArea 中会插入一个制表符。

      我的问题:可以在事件过滤器中触发事件吗?是否可以将 KeyEvent 的文本替换为 FOCUS_EVENT_TEXT,以便指示它是用户生成的事件,还是来自事件过滤器中创建的事件。

      事件过滤器:

      javafx.scene.control.TextArea textArea1 = new javafx.scene.control.TextArea();
      textArea1.addEventFilter(KeyEvent.KEY_PRESSED, new TextAreaTabToFocusEventHandler());
      

      事件处理程序:

      public class TextAreaTabToFocusEventHandler implements EventHandler<KeyEvent>
      {
          private static final String FOCUS_EVENT_TEXT = "TAB_TO_FOCUS_EVENT";
      
          @Override
          public void handle(final KeyEvent event)
          {
              if (!KeyCode.TAB.equals(event.getCode()))
              {
                  return;
              }
      
              // handle events where the TAB key or TAB + CTRL key is pressed
              // so don't handle the event if the ALT, SHIFT or any other modifier key is pressed
              if (event.isAltDown() || event.isMetaDown() || event.isShiftDown())
              {
                  return;
              }
      
              if (!(event.getSource() instanceof TextArea))
              {
                  return;
              }
      
              final TextArea textArea = (TextArea) event.getSource();
              if (event.isControlDown())
              {
                  // if the event text contains the special focus event text
                  // => do not consume the event, and let the default behaviour (= move focus to the next control) happen.
                  //
                  // if the focus event text is not present, then the user has pressed CTRL + TAB key,
                  // then consume the event and insert or replace selection with tab character
                  if (!FOCUS_EVENT_TEXT.equalsIgnoreCase(event.getText()))
                  {
                      event.consume();
                      textArea.replaceSelection("\t");
                  }
              }
              else
              {
                  // The default behaviour of the TextArea for the CTRL+TAB key is a move of focus to the next control.
                  // So we consume the TAB key event, and fire a new event with the CTRL + TAB key.
      
                  event.consume();
      
                  final KeyEvent tabControlEvent = new KeyEvent(event.getSource(), event.getTarget(), event.getEventType(), event.getCharacter(),
                                                                FOCUS_EVENT_TEXT, event.getCode(), event.isShiftDown(), true, event.isAltDown(), event.isMetaDown());
                  textArea.fireEvent(tabControlEvent);
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        受先前答案的启发,对于一个非常相似的案例,我构建了以下类:

        /**
         * Handles tab/shift-tab keystrokes to navigate to other fields,
         * ctrl-tab to insert a tab character in the text area.
         */
        public class TabTraversalEventHandler implements EventHandler<KeyEvent> {
            @Override
            public void handle(KeyEvent event) {
                if (event.getCode().equals(KeyCode.TAB)) {
                    Node node = (Node) event.getSource();
                    if (node instanceof TextArea) {
                        TextAreaSkin skin = (TextAreaSkin) ((TextArea)node).getSkin();
                        if (!event.isControlDown()) {
                            // Tab or shift-tab => navigational action
                            if (event.isShiftDown()) {
                                skin.getBehavior().traversePrevious();
                            } else {
                                skin.getBehavior().traverseNext();
                            }
                        } else {
                            // Ctrl-Tab => insert a tab character in the text area
                            TextArea textArea = (TextArea) node;
                            textArea.replaceSelection("\t");
                        }
                        event.consume();
                    }
                }
            }
        }
        

        我只是没有看到在 TextField 的上下文中处理选项卡的必要性,所以我删除了这部分。

        那么这个类就可以如User所描述的那样非常容易地使用:

        TextArea myTextArea = new TextArea();
        mytTextArea.addEventFilter(KeyEvent.KEY_PRESSED, new TabTraversalEventHandler());
        

        整个事情就像一个魅力:)

        【讨论】:

          【解决方案6】:

          从 Java 9 (2017) 开始,此页面中的大多数答案都不起作用,因为您不能再使用 skin.getBehavior()

          这行得通:

          @Override
          public void handle(KeyEvent event) {
              KeyCode code = event.getCode();
          
              if (code == KeyCode.TAB && !event.isShiftDown() && !event.isControlDown()) {
                  event.consume();
                  Node node = (Node) event.getSource();
                  try {
                      Robot robot = new Robot();
                      robot.keyPress(KeyCode.CONTROL.getCode());
                      robot.keyPress(KeyCode.TAB.getCode());
                      robot.delay(10);
                      robot.keyRelease(KeyCode.TAB.getCode());
                      robot.keyRelease(KeyCode.CONTROL.getCode());
                      }
                  catch (AWTException e) { }
                  }
              }
          

          这也有效:

          @Override
          public void handle(KeyEvent event) {
              KeyCode code = event.getCode();
          
              if (code == KeyCode.TAB && !event.isShiftDown() && !event.isControlDown()) {
                  event.consume();
                  Node node = (Node) event.getSource();            
                  KeyEvent newEvent 
                    = new KeyEvent(event.getSource(),
                               event.getTarget(), event.getEventType(),
                               event.getCharacter(), event.getText(),
                               event.getCode(), event.isShiftDown(),
                               true, event.isAltDown(),
                               event.isMetaDown());
          
                  node.fireEvent(newEvent);            
                  }
              }
          

          当用户按下TAB 时,两者都模拟按下CTRL+TABCTRL+TAB 的 TextArea 的默认行为是将焦点移动到下一个控件。请注意,第二个代码基于 Johan De Schutter 的回答。

          【讨论】:

          • 此解决方案也适用于 Java 1.8.0_311-b11。谢谢大佬。
          【解决方案7】:

          JavaFX 16(以及 17)中的 TextAreaSkin 类没有 getBehavior() 方法。为了防止在 TextArea 中插入标签,TextAreaTabToFocusEventHandler 的解决方案适用于我

          【讨论】:

          • 访问该行为总是脏的——如果我们被允许,我们仍然可以在其 inputMap 中以类似的脏度和tweak the keyMappings 访问它
          猜你喜欢
          • 2011-10-12
          • 2017-06-11
          • 1970-01-01
          • 2015-08-21
          • 1970-01-01
          • 1970-01-01
          • 2019-08-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多