强制UI更新

时间:2013-01-09 23:34:41

标签: java javafx-2 javafx

我有一段代码用于截取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;
    }
}

它有一个扭曲,那就是它应该在拍摄截图之前隐藏给定的节点(如果它们与节点重叠,在某些情况下是明确的。)

然而,在拍摄截图之前,我很难找到一种强制重绘以包含不透明度变化的方法 - 我发现的唯一参考是requestLayout(),但没有喜悦。

我应该调用哪种方法强制并等待重绘完成?

1 个答案:

答案 0 :(得分:7)

我觉得你的代码很奇怪:

  1. 为什么使用node.setOpacity(0)使其不可见,而不是node.setVisible(false)
  2. 为什么要返回AWT BufferedImage而不是JavaFX Image
  3. 为什么要使用机器人来捕捉屏幕而不是拍摄场景的快照?
  4. 为什么要混合使用Swing和JavaFX并最终担心渲染顺序?
  5. 也许这些事情有理由我不明白,但我只是这样做:

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

    我创建了一个使用上述例程的小sample app

    主窗口包括一个带圆和矩形的组。发出快照命令时,矩形隐藏在主窗口中,拍摄主要快照,然后再次在主要窗体中显示矩形。

    primary snapshot


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

    如果出于某种原因,scene.snapshot对您不起作用,并且您希望保留类似于原始策略的内容,那么您要做的是:

    1. 在JavaFX应用程序线程上发出一些更新命令(例如,将节点不透明度设置为0)。
    2. 发出Platform.runLater来电并在runLater正文中拍摄机器人快照。
    3. 一旦真正拍摄了快照(在某些awt回调中发出通知),请发出另一个Platform.runLater命令以重新获得JavaFX应用程序线程。
    4. 返回JavaFX应用程序线程,发出更多更新命令(例如,将节点不透明度设置回1)。
    5. 这应该可以工作,因为它允许JavaFX系统执行另一个脉冲,在机器人实际拍摄快照之前执行不透明度变化的屏幕渲染布局。另一种机制是使用JavaFX AnimationTimer,它会在JavaFX脉冲发生时为您提供回调。保持AWT和JavaFX线程之间所有这一切的正确同步会很烦人。