JavaFX 在 Swing 中使用弹出窗口时存在问题,例如 Combobox

标签 java swing combobox scroll javafx

我尝试在使用 JFXPanel 作为 Scene 包装器的 Swing 应用程序中使用 JavaFX,但我遇到了以下困难:所有能够滚动长列表的弹出窗口(例如组合框)都无法正确控制。

我的示例代码:

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.HBox;
import javax.swing.*;
import java.awt.*;

public class SwingDialog extends JDialog {
    public static void main(String[] args){
        final SwingDialog dialog = new SwingDialog();
        dialog.setVisible(true);
    }

    public SwingDialog(){
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setSize(new Dimension(250, 250));
        final JComboBox combo = new JComboBox();
        for (int i = 0; i< 101; i++){
            combo.addItem("text" + i);
        }
        final JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout());
        panel.setPreferredSize(new Dimension(100, 300));
        panel.add(combo);
        panel.add(createJFXPanel());
        final JScrollPane scroll = new JScrollPane(panel);
        getContentPane().add(scroll);
    }

    private JFXPanel createJFXPanel(){
        final JFXPanel panel = new JFXPanel();
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                final HBox hbox = new HBox();
                final ComboBox combo = new ComboBox();
                for (int i = 0; i< 101; i++){
                    combo.getItems().add("text" + i);
                }
                hbox.getChildren().add(combo);
                final Scene scene = new Scene(hbox);
                panel.setScene(scene);
            };
        });
        return panel;
    }
}

JScrollPane 内有一个带有 JPanelJDialog,它有两个组合框:第一个是 JComboBox,第二个是 JFXPanel 内的 JavaFX ComboBox。 JComboBox 工作正常,但 JavaFX ComboBox 无法使用鼠标滚轮滚动。看起来鼠标滚轮不是自行滚动,而是通过所有者 JPanelJScrollPane 滚动(请参见下面的屏幕截图)。

Behavior on Scroll

预先非常感谢您的建议。

最佳答案

作为向客户展示我的原型(prototype)的临时解决方案,我这样做了:

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class SwingDialog extends JFrame { //or JDialog
    public static void main(String[] args){
        final SwingDialog dialog = new SwingDialog();
        dialog.setVisible(true);
    }

    public SwingDialog(){
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setSize(new Dimension(250, 250));
        final JComboBox combo = new JComboBox();
        for (int i = 0; i< 101; i++){
            combo.addItem("text" + i);
        }
        final JPanel panel = new JPanel();
        panel.setBorder(BorderFactory.createEmptyBorder(20, 0,0,0));
        panel.setLayout(new FlowLayout());
        panel.setPreferredSize(new Dimension(100, 300));
        panel.add(combo);
        panel.add(createJFXPanel());
        final JScrollPane scroll = new JScrollPane(panel);
        getContentPane().add(scroll);
    }

    private JFXPanel createJFXPanel(){
        final JFXPanelEx panel = new JFXPanelEx();
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                final VBox vbox = new VBox();
                final ComboBox combo = new ComboBox();
                for (int i = 0; i< 101; i++){
                    combo.getItems().add("text" + i);
                }
                vbox.getChildren().addAll(combo);
                final Scene scene = new Scene(vbox);
                panel.setScene(scene);
            };
        });
        return panel;
    }

    private class JFXPanelEx extends JFXPanel {
        public JFXPanelEx(){
            addFocusListener(new FocusAdapter() {
                @Override
                public void focusLost(FocusEvent e) {
                    hidePopUps();
                }
            });
        }

        private ComponentListener componentListener;

        @Override
        public void removeNotify() {
            super.removeNotify();
            final Component parent = SwingUtilities.getRoot(this);
            if (parent != null){
                parent.removeComponentListener(componentListener);
            }
        }

        @Override
        public void addNotify() {
            super.addNotify();
            final Component parent = SwingUtilities.getRoot(this);
            componentListener = new ComponentAdapter() {
                @Override
                public void componentMoved(ComponentEvent e) {
                    hidePopUps();
                    processComponentEvent(new ComponentEvent(JFXPanelEx.this, ComponentEvent.COMPONENT_MOVED));
                }

                @Override
                public void componentResized(ComponentEvent e) {
                    hidePopUps();
                    processComponentEvent(new ComponentEvent(JFXPanelEx.this, ComponentEvent.COMPONENT_RESIZED));
                    processComponentEvent(new ComponentEvent(JFXPanelEx.this, ComponentEvent.COMPONENT_MOVED)); //is important!!!
                }
            };
            parent.addComponentListener(componentListener);
        }

        private void hidePopUps(){
            final Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    hidePopUpsImpl();
                }
            };
            if (Platform.isFxApplicationThread()){
                runnable.run();
            } else {
                Platform.runLater(runnable);
            }
        }

        private void hidePopUpsImpl(){
            if (getScene() != null && getScene().getRoot() != null){
                for (final Node node : getScene().getRoot().getChildrenUnmodifiable()){ //JavaFX need interfaces!!!
                    final ComboBox combo = (ComboBox)node;
                    if (combo.isShowing()){ //JavaFX need interfaces!!!
                        combo.hide();
                    }
                }
            }
        }
    }
}

在 Eclispe 多窗口 RCP 环境中,我将这些事件(REZISED 和 MODEV)从 Shell 容器(复合)发布到我的框架或对话框。 因此我的 JFXPanelEx 也可以捕获它们。 通过 COMPONENT_REZISED 触发 COMPONENT_MOVED 非常重要!!!否则,在调整 Shell-Container 内的许多窗口的大小后,弹出窗口不会获得实际位置。 看来,该弹出窗口将在旧位置打开,因此没有窗口。

这是一个补丁,但我希望JavaFX不要很快出现这样的问题,否则看起来没什么用。

关于JavaFX 在 Swing 中使用弹出窗口时存在问题,例如 Combobox,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21838909/

相关文章:

java - 删除最近在 JTextPane 上添加的 JLabel

c# - 如何从组合框中的值成员中删除值?

java - 无法通过Java代码执行java -jar命令

java - 在 Android 中从第 0 行 col -1 获取字段槽失败

java - 无法创建 Jena TDB 的第一个实例

WPF DataGridTemplateColumn 与 ComboBox 绑定(bind)(MVVM 模式)

apache-flex - 组合框未在 IE 8.0.7601 的 flex 中填充

java - Sonar 在“组件” View 中包含测试文件。这是正确的吗?

java - Swing 和 Activity

java - 用户无法关闭的消息对话框