【问题标题】:JavaFX TabPane switch tabs only when focusedJavaFX TabPane 仅在获得焦点时切换选项卡
【发布时间】:2013-10-02 05:54:35
【问题描述】:

我需要在 Ctrl-TabCtrl-Shift-Tab 上切换选项卡。但它仅在 TabPane 处于焦点状态 时有效。我有带有 setContent(textArea) 的标签。 当 textArea 成为焦点时,我按下 Ctrl-Tab,焦点转到菜单中的某处:

    TextArea@3f40a15a[styleClass=text-input text-area]
    TextArea@7b7c82a4[styleClass=text-input text-area]
    TabPane[id=null, styleClass=tab-pane]
    TextArea[id=null, styleClass=text-input text-area]
    MenuButtonSkinBase$MenuLabeledImpl[id=null, styleClass=label]
    TextArea[id=null, styleClass=text-input text-area]
    MenuButtonSkinBase$MenuLabeledImpl[id=null, styleClass=label]
    TextArea[id=null, styleClass=text-input text-area]
    TabPane[id=null, styleClass=tab-pane]
    TextArea[id=null, styleClass=text-input text-area]
    MenuButtonSkinBase$MenuLabeledImpl[id=null, styleClass=label]
    TextArea[id=null, styleClass=text-input text-area]

我像这样跟踪焦点变化:

    scene.focusOwnerProperty().addListener(new ChangeListener<Node>() {

        @Override
        public void changed(ObservableValue<? extends Node> ov, Node t, Node t1) {
            System.out.println(t1);
        }
    });

我几乎在每个组件(包括菜单、工具栏、textArea 和窗格)上都将 setFocusTraversable 设置为 false

我创建了 Tabs 并使用了 setContent(textArea)

我还想在每次用户打开/关闭选项卡并在选项卡之间切换(使用鼠标和键盘热键)时在 textArea 上requestFocus

    tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {

        @Override
        public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
            Platform.runLater(new Runnable() {

                @Override
                public void run() {
                    if (tabPane.getTabs().size() > 0) {
                        FileTab tab = (FileTab)tabPane.getTabs().get(tabPane.getSelectionModel().getSelectedIndex());
                        tab.getTextArea().requestFocus();
                    }
                }
            });

        }
    });

这不起作用。 我试过 tab.getContent().requestFocus() - 也没有用。

关于在 TabPane 子项处于焦点时更改选项卡: 似乎 TabPane 获得了 KeyEvent,但因为它不在焦点中(焦点中的 textArea),所以它只是跳过了这个事件。 也许我可以通过在 TabPane 触发 KeyEvent 来复制默认功能,或者甚至可以自己重新实现它。或者,也许我可以从此监听器调用 nextTab/previousTab 默认操作:

tabPane.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler() {

        @Override
        public void handle(KeyEvent t) {
            if (t.getCode().equals(KeyCode.TAB) && t.isControlDown()) {
                  //Event.fireEvent(tabPane, event); //I'm not sure how to do it like this
                  //Maybe it's better to just call default actions on TabPane.
                  t.consume();
            }
        }
    }); 

http://s23.postimg.org/poeujzwbf/screenshot_185.png

但我仍然不知道如何在标签切换后将焦点设置在 TextArea 上。 也许我应该在延迟一段时间后尝试调用 Platform.runLater()

【问题讨论】:

  • 你能filter the event on the scene而不是TabPane吗?
  • 似乎是个好主意。我认为 JavaFX 有更优雅的解决方案。无论如何我让它工作了,但仍然不知道在选项卡切换后将焦点设置在 Tab 内的 TextArea 上,有时它工作,有时它不工作(requestFocus())。
  • @AS3Boyan,你能不能试着把tab.getTextArea().requestFocus();放到Platform.runLater中,也就是把if语句去掉。例如,或者使用时间轴在 100 毫秒后请求焦点。
  • @Uluk Biy,我想过延迟后运行,我想知道在 Java 8 中这些东西是否已修复。 new Timer() 似乎工作正常,但应用程序没有正确终止。我将尝试使用 Timeline,如果您有如何将其用作计时器的示例,请告诉我。

标签: java javafx


【解决方案1】:

选项卡开关解决方案:

    scene.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {

        @Override
        public void handle(KeyEvent t) {
            if (t.getCode().equals(KeyCode.TAB) && t.isControlDown()) {
                int size = tabPane.getTabs().size();

                if (size > 0) {
                    SkinBase skin = (SkinBase) tabPane.getSkin();
                    TabPaneBehavior tabPaneBehavior = (TabPaneBehavior) skin.getBehavior();

                    int selectedIndex = tabPane.getSelectionModel().getSelectedIndex();

                    if (!t.isShiftDown()) {
                        if (selectedIndex < size -1) {
                            tabPaneBehavior.selectNextTab();
                        } else {
                            tabPaneBehavior.selectTab(tabPane.getTabs().get(0));
                        }
                    } else {
                        if (selectedIndex > 0) {
                            tabPaneBehavior.selectPreviousTab();
                        } else {
                            tabPaneBehavior.selectTab(tabPane.getTabs().get(size - 1));
                        }
                    }

                    t.consume();
                }

            }
        }
    });

将焦点设置在选定选项卡上的解决方案:

        tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {

        @Override
        public void changed(ObservableValue<? extends Number> ov, Number t, final Number t1) {
            Platform.runLater(new Runnable() {

                @Override
                public void run() {
                    if (tabPane.getTabs().size() > 0) {
                        final FileTab tab = (FileTab) tabPane.getTabs().get(t1.intValue());

                        final Timer timer = new Timer();
                        timer.schedule(new TimerTask() {

                            @Override
                            public void run() {
                                Platform.runLater(new Runnable() {

                                    @Override
                                    public void run() {
                                        tab.getContent().requestFocus();
                                        timer.cancel();
                                        timer.purge();
                                    }
                                });
                            }
                        }, 25);
                    }
                }
            });

        }
    });

将焦点设置在选定选项卡上的替代解决方案(基于 @Uluk Biy 的 https://stackoverflow.com/a/16920898):

        tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {

        @Override
        public void changed(ObservableValue<? extends Number> ov, Number t, final Number t1) {
            Platform.runLater(new Runnable() {

                @Override
                public void run() {
                    if (tabPane.getTabs().size() > 0) {
                        final FileTab tab = (FileTab) tabPane.getTabs().get(t1.intValue());

                        final Timeline animation = new Timeline(
                                new KeyFrame(Duration.millis(25),
                                new EventHandler<ActionEvent>() {
                                    @Override
                                    public void handle(ActionEvent actionEvent) {
                                        Platform.runLater(new Runnable() {

                                            @Override
                                            public void run() {
                                                tab.getContent().requestFocus();
                                            }
                                        });
                                    }
                                }));
                        animation.setCycleCount(1);
                        animation.play();
                    }
                }
            });

        }
    });

这应该是比只使用计时器更好的解决方案。

【讨论】:

  • Platform.runlater(...)KeyFrame处理程序中无需包装代码
  • 我使用的是 JDK 8u162,我测试了这段代码,它在我的项目中运行:getTabs().forEach(e -&gt; e.setOnSelectionChanged(o -&gt; Platform.runLater(() -&gt; e.getContent().requestFocus())));。也许它也适用于你的。
  • 在我上面的解决方案中,如果用户再次点击Tab,其内容将失去焦点。在TabPane 上使用此代码,以确保后续点击Tab 或点击标签栏中的空间将继续关注其内容:setOnMousePressed(e -&gt; getSelectionModel().getSelectedItem().getContent().requestFocus());
【解决方案2】:

只需使用以下方法聚焦第一个选项卡(即选项卡 0):

Platform.runLater(() -> tabPane.getTabs().get(0).getContent().requestFocus());

【讨论】:

    猜你喜欢
    • 2018-05-14
    • 2017-07-04
    • 1970-01-01
    • 2017-01-28
    • 2013-01-02
    • 1970-01-01
    • 2016-09-14
    • 1970-01-01
    • 2020-03-03
    相关资源
    最近更新 更多