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

ひねりがあり、スクリーンショットを撮る前に特定のノードを非表示にする必要があります(ノードと重複する場合、場合によっては明確です)。

ただし、スクリーンショットを撮る前に、不透明度の変更を含めるように再描画を強制する方法を見つけるのに行き詰まっています。私が見つけた唯一の参照は、でしたがrequestLayout()、そこには喜びがありませんでした。

再描画が完了するのを強制して待機するには、どのメソッドを呼び出す必要がありますか?

4

1 に答える 1

9

あなたのコードはかなり奇妙だと思います:

  1. node.setOpacity(0)ではなく、非表示にするために使用するのはなぜnode.setVisible(false)ですか?
  2. BufferedImageJavaFX ではなくAWT を返すのはなぜ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 レンダリング スレッドは、2 つの別個のものとして扱われます。必要なことは、JavaFX アプリケーション スレッドで処理を実行し、シード コントロールを JavaFX システムに戻し、レンダリングが行われるのを待ってから、結果を調べることです。メソッドがそのscene.snapshot同期を処理するので、心配する必要はありません。

何らかの理由でscene.snapshotうまくいかず、元の戦略に似たものを維持したい場合は、次のようにします。

  1. JavaFX アプリケーション スレッドでいくつかの更新コマンドを発行します (ノードの不透明度を 0 に設定するなど)。
  2. 呼び出しを発行Platform.runLaterし、runLater 本体でロボットのスナップショットを取得します。
  3. スナップショットが実際に取得されたら (awt コールバックでの通知)、別のPlatform.runLaterコマンドを発行して JavaFX アプリケーション スレッドに戻ります。
  4. JavaFX アプリケーション スレッドに戻り、さらにいくつかの更新コマンドを発行します (たとえば、ノードの不透明度を 1 に戻します)。

これは、ロボットが実際にスナップショットを取得する前に、JavaFX システムが別のパルスを実行して、不透明度の変更を伴う画面のレンダリング レイアウトを実行できるようにするため、機能するはずです。別のメカニズムはAnimationTimer、JavaFX パルスが発生するたびにコールバックを提供する JavaFX を使用することです。AWT スレッドと JavaFX スレッド間でこれらすべての適切な同期を維持するのは面倒です。

于 2013-01-10T01:11:23.330 に答える