JavaFx SwingNode -随机显示黑色,背景或组件



我在JavaFx的SwingNode显示它在Java 1.8.0_102-b14中持有的组件时遇到了问题。我不确定这个问题是由于我的Java版本太旧,当前(如果我的是当前)Java版本中的错误,还是我做错了一些SwingNode进程。我知道这个问题在JavaFX和SwingNode -部分黑窗之前已经报告过了,所以我真的在寻找关于我的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中的内容纠正了这个简单代码的问题,但不适合我的大型项目。所以我猜这是内存泄漏,但是我不能从我的机器上判断出来。

有人可以测试这段代码,看看他们是否得到类似的结果?编辑:谢谢你的快速反馈,请不要把注意力集中在按钮上,因为它们只是一个展示我问题的工具。这个奇怪的功能以同样的方式作用于其他对象而不是按钮。

必须在AWT事件调度线程上创建和显示Swing组件(请参阅Swing的线程策略)。Application.start()方法是在FX应用线程上调用的。

所以你需要

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

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

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

On SplitPane分隔器位置更新:由于swingNode在SplitPane中,我也添加了侦听器。

这两个更新似乎是有效的,但如果我移动分隔线太快,黑色背景又会回来。为了解决这个问题,我将SwingNode上的缓存更改为true,并将cacheHint更改为CacheHint.SPEED。这消除了所有渲染问题,但它仍然有一个副作用,因为textArea被缓存并显示为位图,它将在一小段时间内不响应用户交互。这对我的情况有效,因为我的文本区域是不可编辑的。

注意swingutility的用法。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。

我通过在显示Stage之后以延迟的方式重新绘制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);
  }
}

我希望它有帮助!

有一个关于这个和类似问题的错误报告,据说已经解决了,但显然它并没有完全修复,因为我仍然看到黑色方块而不是启动按钮(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在他的回答中提到的,不要忘记您已经在EDT上创建了Swing组件。

最新更新