【问题标题】:JavaFx SwingNode - Displays Black, Background, or Component RandomlyJavaFx SwingNode - 随机显示黑色、背景或组件
【发布时间】:2016-08-17 18:10:54
【问题描述】:

JavaFx 的 SwingNode 显示它在 Java 1.8.0_102-b14 中包含的组件时遇到问题。我不确定这个问题是否是由于我的 Java 版本太旧、当前(如果我是当前)Java 版本中的错误,或者我做错了一些 SwingNode 进程。我知道这个问题在JavaFX and SwingNode - partial black Window 之前已经报告过,所以我真的在寻找有关我的 Java 版本的反馈。

此代码通常会错误地显示“button1”,如黑色或背景色,直到鼠标悬停在它上面或单击它。这对我来说不是一个理想的解决方法,因为我的项目在 Fx 项目中使用了更复杂的摆动面板,不幸的是,纯 Fx 不是一个选项。

import javafx.application.Application;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

import javax.swing.JButton;

public class Test extends Application {

  @Override
  public void start(Stage stage) {
      final SwingNode swingNode1 = new SwingNode();
      final SwingNode swingNode2 = new SwingNode();

      swingNode2.setContent(new JButton("Click me!"+2));
      swingNode1.setContent(new JButton("Click me!"+1));

      BorderPane pane = new BorderPane();
      pane.setLeft(swingNode1);
      pane.setRight(swingNode2);

      stage.setScene(new Scene(pane, 200, 50));
      stage.show();
  }

  public static void main(String[] args) {
      launch(args);
  }
}

在创建这个 sssce 时,我发现在设置右侧 SwingNode 中的内容之前设置左侧 SwingNode 中的内容更正了这个简单代码的问题,但不适用于我的大型项目。所以我猜这是内存泄漏,但我无法从我的机器上分辨出来。

有人可以测试这段代码,看看他们是否得到类似的结果吗?

编辑:感谢您的快速反馈,请不要专注于按钮,因为它们只是演示我的问题的工具。奇怪的功能与其他对象而不是按钮的行为方式相同。

【问题讨论】:

  • 即使按照@James_D 复制 Javadocs 示例,问题仍然存在。还有其他想法吗?这个bug显然在U40中已经修复了,会不会又被破解了?
  • 我仍然在 Windows 7 上的最新 8u121 中看到这个问题,使用 invokeLater() 或 invokeAndWait()

标签: java swing debugging javafx


【解决方案1】:

您必须在 AWT Event Dispatch 线程上创建和显示 Swing 组件(请参阅Swing's threading policy)。 Application.start() 方法是invoked on the FX Application Thread。

所以你需要

public class Test extends Application {

  @Override
  public void start(Stage stage) {
      final SwingNode swingNode1 = new SwingNode();
      final SwingNode swingNode2 = new SwingNode();

      SwingUtilities.invokeLater(() -> {
          swingNode2.setContent(new JButton("Click me!"+2));
          swingNode1.setContent(new JButton("Click me!"+1));
      });

      BorderPane pane = new BorderPane();
      pane.setLeft(swingNode1);
      pane.setRight(swingNode2);

      stage.setScene(new Scene(pane, 200, 50));
      stage.show();
  }

  public static void main(String[] args) {
      launch(args);
  }
}

【讨论】:

  • 使用此方法,按钮再次显示为黑色。但是我不应该在点击窗口时,按钮变得可见。单击另一个按钮也会使其可见。虽然不完美,但我认为这可以工作。
【解决方案2】:

在 SwingNode 中使用 JTextArea(使用 JDK 1.8.0_202)时,我再次看到了这个渲染问题。对我来说,这个问题发生在两种情况下:

在每次尝试调整窗口大小时:为了解决这个问题,我添加了一个侦听器来在每次调整窗口大小时设置摆动节点的内容。

SplitPane 分隔线位置更新: 由于 swingNode 位于 SplitPane 中,因此我也将侦听器添加到其中。

这两个更新似乎起到了作用,除非我将分隔线移动得太快,黑色背景会再次出现。为了解决这个问题,我将 SwingNode 上的缓存更改为 true,将 cacheHint 更改为 CacheHint.SPEED。这消除了所有的渲染问题,但它仍然有一个副作用,因为 textArea 被缓存并显示为位图,它不会在一小段时间内响应用户交互。这适用于我的情况,因为我的 textarea 不可编辑。

请注意,使用 SwingUtilities.invokelater 似乎对渲染没有任何影响,因此我将其省略了此代码。

swingNode.setContent(textArea);

ChangeListener<Number> sizeListener = (o, oldSize, newSize) -> {
  swingNode.setContent(textArea);
};

splitPane.getDividers().forEach( divider -> divider.positionProperty().addListener(sizeListener));
primaryStage.heightProperty().addListener(sizeListener);
primaryStage.widthProperty().addListener(sizeListener);

swingNode.setCache(true);
swingNode.setCacheHint(CacheHint.SPEED);

对于 JButton,此问题似乎已修复。

【讨论】:

    【解决方案3】:

    我已通过在显示舞台后延迟重绘 Swing 组件来解决此问题:

    new Timer().schedule(new TimerTask() {
            public void run() {
                swingNode1.getContent().repaint();
                swingNode2.getContent().repaint();
            }
        }, 100L);
    

    所以,您的代码将如下所示:

    import javafx.application.Application;
    import javafx.embed.swing.SwingNode;
    import javafx.scene.Scene;
    import javafx.scene.layout.BorderPane;
    import javafx.stage.Stage;
    
    import javax.swing.JButton;
    
    public class Test extends Application {
    
      @Override
      public void start(Stage stage) {
          final SwingNode swingNode1 = new SwingNode();
          final SwingNode swingNode2 = new SwingNode();
    
          swingNode2.setContent(new JButton("Click me!"+2));
          swingNode1.setContent(new JButton("Click me!"+1));
    
          BorderPane pane = new BorderPane();
          pane.setLeft(swingNode1);
          pane.setRight(swingNode2);
    
          stage.setScene(new Scene(pane, 200, 50));
          stage.show();
    
          new Timer().schedule(new TimerTask() {
               public void run() {
                    swingNode1.getContent().repaint();
                    swingNode2.getContent().repaint();
               }
           }, 100L);
      }
    
      public static void main(String[] args) {
          launch(args);
      }
    }
    

    希望对你有帮助!

    【讨论】:

      【解决方案4】:

      有一个 bug report 涉及这个问题和类似的问题,据说已经解决了,但显然它并没有完全解决,因为我在启动时仍然看到黑色方块而不是按钮 (1.8.0_91-b15)。

      这是因为您的 JavaFX 组件是在您的 Swing 组件之前创建的。您可以通过在每个组件上调用 requestFocus() 来修复此行为,以确保它们正确呈现:

      SwingUtilities.invokeLater(() -> {
          swingNode1.setContent(new JButton("Click me!"+1));
          swingNode1.requestFocus();
          swingNode2.setContent(new JButton("Click me!"+2));
          swingNode2.requestFocus();
      });
      

      或者通过使用invokeAndWait(),让您的应用程序等到您的 Swing 组件创建完成。

      SwingUtilities.invokeAndWait(() -> {
          swingNode1.setContent(new JButton("Click me!"+1));
          swingNode2.setContent(new JButton("Click me!"+2));
      });
      

      另外,正如 James_D 在他的 answer 中提到的,不要忘记您已经在 EDT 上创建了 Swing 组件。

      【讨论】:

        猜你喜欢
        • 2015-03-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-24
        • 2021-10-31
        • 2017-08-30
        相关资源
        最近更新 更多