15

如何在 TextArea 中按下 Tab 键导航到下一个控件?

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

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

7 回答 7

13

我使用遍历方法

@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();
    }
}
于 2015-02-06T09:21:18.120 回答
10

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

            }
        }
    });
于 2012-10-12T15:42:32.573 回答
10

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

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

于 2017-12-05T04:11:23.277 回答
3

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

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

事件过滤器:

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);
        }
    }
}
于 2017-10-11T10:24:57.863 回答
1

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

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

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

于 2017-11-27T14:02:45.773 回答
0

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

通话

behavior.callAction("InsertTab");

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

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

因此,我使用了 callAction() 方法而不是

textArea.replaceSelection("\t");
于 2017-09-30T12:48:41.850 回答
0

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

于 2021-10-16T07:51:07.070 回答