5

我有一段代码用于截取 JavaFX 中的一个节点:

public BufferedImage getSnapshot(final Node... hideNodes) {
    Window window = getScene().getWindow();
    Bounds b = localToScene(getBoundsInLocal());
    int x = (int) Math.round(window.getX() + getScene().getX() + b.getMinX());
    int y = (int) Math.round(window.getY() + getScene().getY() + b.getMinY());
    int w = (int) Math.round(b.getWidth());
    int h = (int) Math.round(b.getHeight());
    try {
        Robot robot = new Robot();
        for(Node node : hideNodes) {
            node.setOpacity(0);
            node.getParent().requestLayout();
        }
        BufferedImage image = robot.createScreenCapture(new java.awt.Rectangle(x, y, w, h));
        for(Node node : hideNodes) {
            node.setOpacity(1);
            node.getParent().requestLayout();
        }
        return image;
    }
    catch(AWTException ex) {
        return null;
    }
}

它有一个转折,那就是它应该在截屏之前隐藏给定的节点(以防它们与节点重叠,这在某些情况下是确定的。)

但是,我一直在寻找一种方法来强制重绘以在截取屏幕截图之前包含不透明度更改 - 我发现的唯一参考是 to requestLayout(),但那里没有乐趣。

我应该调用什么方法来强制并等待重绘完成?

4

1 回答 1

9

我觉得你的代码很奇怪:

  1. 为什么使用node.setOpacity(0)使其不可见,而不是node.setVisible(false)
  2. 为什么返回 AWTBufferedImage而不是 JavaFX Image
  3. 为什么使用机器人捕捉屏幕而不是拍摄场景快照?
  4. 为什么混合 Swing 和 JavaFX 并最终不得不担心渲染顺序?

也许这些事情有我不明白的原因,但我会这样做:

public Image takeSnapshot(Scene scene, final Node... hideNodes) {
  for (Node node: hideNodes) node.setVisible(false);
  Image image = scene.snapshot(null);
  for (Node node: hideNodes) node.setVisible(true);

  return image;
}  

我创建了一个使用上述例程的小型示例应用程序。

主窗口包括一个带有圆形和矩形的组。当发出快照命令时,矩形隐藏在主节点中,拍摄主节点的快照,然后矩形再次在主节点中可见。

基本的 快照


要回答有关强制 UI 更新的问题的标题 - 你真的不能。JavaFX 应用程序线程和JavaFX 渲染线程将被视为两个独立的事物。您需要做的是在 JavaFX 应用程序线程上运行您的处理,将控制返回给 JavaFX 系统,等待它进行渲染,然后检查结果。该scene.snapshot方法将为您处理该同步,因此您无需担心。

如果由于某种原因scene.snapshot对您不起作用并且您想保持与原始策略类似的东西,那么您将做的是:

  1. 在 JavaFX 应用程序线程上发出一些更新命令(例如,将节点不透明度设置为 0)。
  2. 发出Platform.runLater调用并在 runLater 主体中拍摄您的机器人快照。
  3. 一旦真正拍摄了快照(某些 awt 回调中的通知),发出另一个Platform.runLater命令以返回 JavaFX 应用程序线程。
  4. 回到 JavaFX 应用程序线程,发出更多更新命令(例如,将节点不透明度设置回 1)。

这应该可以工作,因为它将允许 JavaFX 系统执行另一个脉冲,该脉冲在您的机器人实际拍摄快照之前执行屏幕的渲染布局,其中不透明度发生变化。另一种机制是使用 JavaFX AnimationTimer,它会在 JavaFX 脉冲发生时为您提供回调。在 AWT 和 JavaFX 线程之间保持所有这些的正确同步会很烦人。

于 2013-01-10T01:11:23.330 回答