自动调整Canvas大小以填充封闭的Parent

我最近想在JavaFX中创建一个动画背景,类似于这里看到的Swing示例。 我使用Canvas绘制,如使用Canvas API ,以及绘图循环的AnimationTimer ,如动画基础所示。 不幸的是,我不确定如何在调整封闭Stage大小时自动调整Canvas大小。 什么是好方法?

图片

在下面的示例中,静态嵌套类CanvasPanePane包装Canvas实例并覆盖layoutChildren()以使canvas尺寸与封闭Pane匹配。 请注意, CanvasisResizable()返回false ,因此“在布局期间父级无法resize”,并且“除了将可resize的子级调整为其首选大小之外, Pane ”不执行布局。 用于构造canvas的widthheight成为其初始大小。 在Ensemble粒子模拟Fireworks.java使用类似的方法来缩放背景图像,同时保持其纵横比。

另外,请注意与原始颜色相比使用完全饱和的颜色的区别。 这些相关示例说明了在动画背景上放置控件。

图片

 import java.util.LinkedList; import java.util.Queue; import java.util.Random; import javafx.animation.AnimationTimer; import javafx.application.Application; import javafx.beans.Observable; import javafx.scene.Scene; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.control.CheckBox; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.stage.Stage; /** * @see https://stackoverflow.com/a/31761362/230513 * @see https://stackoverflow.com/a/8616169/230513 */ public class Baubles extends Application { private static final int MAX = 64; private static final double WIDTH = 640; private static final double HEIGHT = 480; private static final Random RND = new Random(); private final Queue queue = new LinkedList<>(); private Canvas canvas; @Override public void start(Stage stage) { CanvasPane canvasPane = new CanvasPane(WIDTH, HEIGHT); canvas = canvasPane.getCanvas(); BorderPane root = new BorderPane(canvasPane); CheckBox cb = new CheckBox("Animate"); cb.setSelected(true); root.setBottom(cb); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); for (int i = 0; i < MAX; i++) { queue.add(randomBauble()); } AnimationTimer loop = new AnimationTimer() { @Override public void handle(long now) { GraphicsContext g = canvas.getGraphicsContext2D(); g.setFill(Color.BLACK); g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight()); for (Bauble b : queue) { g.setFill(bc); g.fillOval(bx, by, bd, bd); } queue.add(randomBauble()); queue.remove(); } }; loop.start(); cb.selectedProperty().addListener((Observable o) -> { if (cb.isSelected()) { loop.start(); } else { loop.stop(); } }); } private static class Bauble { private final double x, y, d; private final Color c; public Bauble(double x, double y, double r, Color c) { this.x = x - r; this.y = y - r; this.d = 2 * r; this.c = c; } } private Bauble randomBauble() { double x = RND.nextDouble() * canvas.getWidth(); double y = RND.nextDouble() * canvas.getHeight(); double r = RND.nextDouble() * MAX + MAX / 2; Color c = Color.hsb(RND.nextDouble() * 360, 1, 1, 0.75); return new Bauble(x, y, r, c); } private static class CanvasPane extends Pane { private final Canvas canvas; public CanvasPane(double width, double height) { canvas = new Canvas(width, height); getChildren().add(canvas); } public Canvas getCanvas() { return canvas; } @Override protected void layoutChildren() { super.layoutChildren(); final double x = snappedLeftInset(); final double y = snappedTopInset(); // Java 9 - snapSize is depricated used snapSizeX() and snapSizeY() accordingly final double w = snapSize(getWidth()) - x - snappedRightInset(); final double h = snapSize(getHeight()) - y - snappedBottomInset(); canvas.setLayoutX(x); canvas.setLayoutY(y); canvas.setWidth(w); canvas.setHeight(h); } } public static void main(String[] args) { launch(args); } } 

你不能用Binding做到这一点吗? 以下似乎产生相同的结果,而无需添加派生类。

 import java.util.LinkedList; import java.util.Queue; import java.util.Random; import javafx.animation.AnimationTimer; import javafx.application.Application; import javafx.beans.Observable; import javafx.beans.binding.DoubleBinding; import javafx.scene.Scene; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.control.CheckBox; import javafx.scene.layout.BorderPane; import javafx.scene.paint.Color; import javafx.stage.Stage; /** * @see http://stackoverflow.com/a/31761362/230513 * @see http://stackoverflow.com/a/8616169/230513 */ public class Baubles extends Application { private static final int MAX = 64; private static final double WIDTH = 640; private static final double HEIGHT = 480; private static final Random RND = new Random(); private final Queue queue = new LinkedList<>(); private Canvas canvas; @Override public void start(Stage stage) { canvas = new Canvas(WIDTH, HEIGHT); BorderPane root = new BorderPane(canvas); CheckBox cb = new CheckBox("Animate"); cb.setSelected(true); root.setBottom(cb); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); // Create bindings for resizing. DoubleBinding heightBinding = root.heightProperty() .subtract(root.bottomProperty().getValue().getBoundsInParent().getHeight()); canvas.widthProperty().bind(root.widthProperty()); canvas.heightProperty().bind(heightBinding); for (int i = 0; i < MAX; i++) { queue.add(randomBauble()); } AnimationTimer loop = new AnimationTimer() { @Override public void handle(long now) { GraphicsContext g = canvas.getGraphicsContext2D(); g.setFill(Color.BLACK); g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight()); for (Bauble b : queue) { g.setFill(bc); g.fillOval(bx, by, bd, bd); } queue.add(randomBauble()); queue.remove(); } }; loop.start(); cb.selectedProperty().addListener((Observable o) -> { if (cb.isSelected()) { loop.start(); } else { loop.stop(); } }); } private static class Bauble { private final double x, y, d; private final Color c; public Bauble(double x, double y, double r, Color c) { this.x = x - r; this.y = y - r; this.d = 2 * r; this.c = c; } } private Bauble randomBauble() { double x = RND.nextDouble() * canvas.getWidth(); double y = RND.nextDouble() * canvas.getHeight(); double r = RND.nextDouble() * MAX + MAX / 2; Color c = Color.hsb(RND.nextDouble() * 360, 1, 1, 0.75); return new Bauble(x, y, r, c); } public static void main(String[] args) { launch(args); } }