自动调整Canvas大小以填充封闭的Parent
我最近想在JavaFX中创建一个动画背景,类似于这里看到的Swing示例。 我使用Canvas
绘制,如使用Canvas API ,以及绘图循环的AnimationTimer
,如动画基础所示。 不幸的是,我不确定如何在调整封闭Stage
大小时自动调整Canvas
大小。 什么是好方法?
在下面的示例中,静态嵌套类CanvasPane
在Pane
包装Canvas
实例并覆盖layoutChildren()
以使canvas尺寸与封闭Pane
匹配。 请注意, Canvas
从isResizable()
返回false
,因此“在布局期间父级无法resize”,并且“除了将可resize的子级调整为其首选大小之外, Pane
”不执行布局。 用于构造canvas的width
和height
成为其初始大小。 在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); } }