2013-01-09 113 views
3

我有一段代碼,設計上採用節點的屏幕截圖的JavaFX:強制UI更新

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(),但那裏沒有快樂。

我應該調用什麼方法強制等待重繪完成?

回答

7

我覺得你的代碼很奇怪:

  1. 爲什麼要用node.setOpacity(0)使其無形的,而不是node.setVisible(false)
  2. 爲什麼要返回AWT BufferedImage而不是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; 
} 

我創建了一個小sample app其使用上述程序。

主窗口包括一個帶圓圈和矩形的組。當發出快照命令時,該矩形隱藏在主要區域中,獲取主要快照,然後再次在主要區域中使該矩形可見。

primarysnapshot


要回答你的問題的標題約迫使UI更新 - 你真的不能。 JavaFX應用程序線程和JavaFX呈現線程將被視爲兩個獨立的東西。你需要做的是在JavaFX應用程序線程上運行你的處理,種子控制回到JavaFX系統,等待它進行渲染,然後檢查結果。 scene.snapshot方法將爲您處理同步,因此您不必擔心。

如果出於某種原因,scene.snapshot不會爲你工作,你想保持類似的東西你原來的策略,那麼你會做的是:

  1. 發出一些更新命令(例如設置節點不透明度爲0)在JavaFX應用程序線程上。
  2. 發出Platform.runLater調用並在runLater主體中取出您的機器人快照。
  3. 一旦快照實際上已被採用(通過某個awt回調函數),請發出另一個Platform.runLater命令以恢復JavaFX應用程序線程。
  4. 回到JavaFX應用程序線程,發出一些更多的更新命令(例如,將節點不透明度設置回1)。

這應該可以工作,因爲它可以讓JavaFX系統執行另一個脈衝,在機器人實際拍攝快照之前執行不透明度更改的屏幕渲染布局。另一種機制是使用JavaFX AnimationTimer,只要出現JavaFX脈衝,它就會爲您提供回調。在AWT和JavaFX線程之間保持所有這些的適當同步會令人討厭。

+1

非常感謝您的全面回答 - 許多古怪的事情可以通過我根本不知道快照方法的事實來解釋,並且認爲使用舊的awt機器人是唯一的選擇。我會很樂意使用這種方法並取消它!然而,BufferedImage是需要的,因爲它提供給需要BufferedImage的庫方法 - 但這是一個足夠簡單的轉換。 – berry120

+3

要將您的JavaFX'Image'轉換爲'BufferedImage',我建議使用[SwingFXUtils.fromFXImage](http://docs.oracle.com/javafx/2/api/javafx/embed/swing/SwingFXUtils.html#fromFXImage %28javafx.scene.image.Image,%20java.awt.image.BufferedImage%29) – jewelsea