JavaFX 8与swing兼容性问题

14

我目前正在评估是否可行将我的当前Swing应用程序转换为JavaFX 8,使用SwingNode,然后逐步更改所有内容为JavaFX组件。我遇到了两个问题,这可能是JavaFX 8中存在的潜在错误,但我不能确定。

  1. After adding content to the SwingNode the Javafx menu isn't painted properly (most of the time), resulting in a black bar
  2. Making JScrollBars non-opaque doesn't seem to work

    import java.awt.Color;
    import java.awt.GridLayout;
    
    import javafx.application.Application;
    import javafx.embed.swing.SwingNode;
    import javafx.scene.Scene;
    import javafx.scene.control.Menu;
    import javafx.scene.control.MenuBar;
    import javafx.scene.layout.StackPane;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    
    
    public class Test extends Application {    
    
        @Override
        public void start(Stage stage) throws Exception {
            stage.setMaximized(true);
    
            VBox vbox = new VBox();
            MenuBar menuBar = new MenuBar();
            menuBar.getMenus().add(new Menu("bla"));
            vbox.getChildren().add(menuBar);
    
            StackPane mainPane = new StackPane();        
            mainPane.setPrefSize(9999, 9999); //Better way to ensure all space used?
            vbox.getChildren().add(mainPane);
            vbox.setVisible(true);
    
            Scene scene = new Scene(vbox, 9999, 9999); //Better way to ensure all space used?
            stage.setScene(scene);
    
            SwingNode swingNode = new SwingNode();
            mainPane.getChildren().add(swingNode);
    
            JPanel jPanel = new JPanel();
            swingNode.setContent(jPanel); //Seems to interfere with painting of menu
    
            jPanel.setBackground(Color.WHITE);
            jPanel.setLayout(new GridLayout(2, 1));
            jPanel.add(new JPanel());
    
            JPanel nonOpaquePanel = new JPanel();
            nonOpaquePanel.setOpaque(false);
            JScrollPane scrollPane = new JScrollPane(nonOpaquePanel);
            scrollPane.setOpaque(false);
            scrollPane.getViewport().setOpaque(false); //Black background -> Bug?
            jPanel.add(scrollPane);
    
            stage.show();
        }
    
        /**
         * Main method launching the application.
         */
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    Executable jar

编辑: 我修改了我的示例,使用EDT创建Swing组件。但是这并没有改变任何东西。或者我做错了什么吗?

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;


public class Test extends Application {
    private JPanel jPanel = null;

    @Override
    public void start(Stage stage) throws Exception {
        if (Platform.isFxApplicationThread()) { // all of this happens on the fx application thread
            stage.setMaximized(true);

            VBox vbox = new VBox();
            MenuBar menuBar = new MenuBar();
            menuBar.getMenus().add(new Menu("bla"));
            vbox.getChildren().add(menuBar);

            StackPane mainPane = new StackPane();        
            mainPane.setPrefSize(9999, 9999); //Better way to ensure all space used?
            vbox.getChildren().add(mainPane);
            vbox.setVisible(true);

            Scene scene = new Scene(vbox, 9999, 9999); //Better way to ensure all space used?
            stage.setScene(scene);

            SwingNode swingNode = new SwingNode();
            mainPane.getChildren().add(swingNode);                    

            SwingUtilities.invokeLater(new Runnable() { //Use EDT to build swing components              
                @Override
                public void run() {
                    jPanel = new JPanel();

                    jPanel.setBackground(Color.WHITE);
                    jPanel.setLayout(new GridLayout(2, 1));
                    jPanel.add(new JPanel());

                    JLabel nonOpaquePanel = new JLabel("Bla");
                    nonOpaquePanel.setPreferredSize(new Dimension(5000, 5000));
                    nonOpaquePanel.setOpaque(false);
                    JScrollPane scrollPane = new JScrollPane(nonOpaquePanel);
                    scrollPane.setOpaque(false);
                    scrollPane.getViewport().setOpaque(false); //Black background -> Bug?
                    jPanel.add(scrollPane);
                }
            });

            Thread.sleep(1000); //terrible way to wait for the EDT, I know
            swingNode.setContent(jPanel); //Seems to interfere with painting of menu            
            stage.show();

            SwingUtilities.invokeLater(new Runnable() { //I am pretty sure this isn't necessary, but whatever                       
                @Override
                public void run() {
                    jPanel.repaint();    
                }
            });
        }
    }

    /**
     * Main method launching the application.
     */
    public static void main(String[] args) {
        launch(args);
    }
}

3
我在JavaFX漏洞报告RT-36285中询问了这个问题,并听到了以下回答:“...请检查您代码中的线程。您在stackoverflow上提供的示例调用了JavaFX用户线程上的Swing API。这对Swing和FX都不好。Swing API应该只在EDT上调用,而FX API应该相应地在JavaFX用户线程上调用。请先修复线程问题,然后查看它如何改变事物。如果仍然存在任何问题,请提交新错误报告。” - Rob I
你不需要将 JScrollBar 设置为非不透明,而是整个 JScrollPane。但是,两者都不起作用。不太清楚你希望具有非不透明 JScrollBarJScrollPane 的行为是什么,但很可能不会按照你的期望工作(原则上)。 - Holger
当我将你的代码与Oracle教程进行比较时,似乎你需要将swingNode.setContent(...)调用放入SwingUtilities.invokeLater(...)而不是创建Swing内容本身。结合@Dashy的单线程系统属性,你应该已经尽力了。但目前你的代码看起来有问题。 - dzim
3个回答

4

在进行更新调用时,请使用以下代码包装您的更新代码。这段代码以前在其他项目中对我有所帮助。

Platform.runLater(new Runnable() {
            @Override
            public void run() {
                // Update the text node with calculated results
            }
       });

Source


1
由于这是Java8,可以使用Platform.runLater(()-> ...) - ggovan

1
JavaFX 中可能存在一个 bug,但是这个示例现在可以正常工作了。

1
你可以使用 -Djavafx.embed.singleThread=true,这样你就不必自己进行事件分发了。

JavaFx API 设计


我理解的是,这个博客上提到的只适用于MacOS系统? - kleopatra
在Windows上完美运行,在我的当前项目中进行了测试。 - Dashy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接