如何创建完全自定义的JavaFX控件? 或者:如何使用动态绘制的背景创建窗格?

我想创建完全自定义的JavaFX控件。 我不想“喜欢作曲”,因为我没有东西可以写作。

例如,假设我需要一个窗格,里面有坐标网格。 窗格应该像普通窗格一样工作,即应该可以在那里添加控件或几何图形,但是在窗格的背景中应该绘制网格。 另外,应在窗格的边缘绘制数值。

所有这些都应该动态地反映窗格的转换和视口。

另一个例子:假设我想将平铺图像作为beckground。 有数百万的瓷砖,比如谷歌地图,所以我无法将它们作为子节点加载,因为它们会耗尽内存。 当用户滚动窗格时,我需要动态加载和卸载它们。

同样,窗格应该像普通窗格一样运行,即可以向其添加子项。

怎么做到这一点? 我发现,类似于paintConponent的低级方法要么不存在,要么弃用。 那么该怎么办?

UPDATE

我想设计一个具有自定义背景的容器。

例如,像这样:

在此处输入图像描述

(它应该是无穷无尽的,即一旦控件resize就会显示更多行)

容器默认情况下应该没有子节点,但仍然具有背景。 背景不应该是容器的子代。 我,程序员,应该能够将子容器添加到此容器中,只有在此之后,子容器才会出现在容器中。 它们应该出现在背景之上。

像这样:

在此处输入图像描述

请注意,我们这里只有2个孩子。

UPDATE

在下面的代码中,显示ScrollBar标准控件。 如您所见,它包含可以移动的旋钮和可以按下的箭头按钮。

同时,此控件中的子项数报告为零。

 import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.ScrollBar; import javafx.scene.layout.AnchorPane; import javafx.stage.Stage; public class ChildrenOfDefaultControls extends Application { @Override public void start(Stage primaryStage) throws Exception { ScrollBar scrollBar = new ScrollBar(); AnchorPane root = new AnchorPane(scrollBar); AnchorPane.setLeftAnchor(scrollBar, 0.); AnchorPane.setRightAnchor(scrollBar, 0.); AnchorPane.setTopAnchor(scrollBar, 100.); Scene scene = new Scene(root, 800, 600); primaryStage.setTitle(String.valueOf(scrollBar.getChildrenUnmodifiable().size())); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { Application.launch(ChildrenOfDefaultControls.class, args); } } 

好吧,我同意如果每个人都说不可能像在Swing中那样画画,那就让我们来做作曲。 但是如何从用户那里隐藏这个组合,因为它完成了ScrollBar控件?

使用Canvas使用GraphicsContext提供的命令呈现任意内容; 将Canvas添加到具有合适布局的Pane中。 在此示例中 ,“ CanvasPanePane包装Canvas实例并覆盖layoutChildren()以使canvas尺寸与封闭Pane匹配。” 将rootBorderPane切换到StackPane允许将控件放在动画背景的顶部。 该示例添加了一个CheckBox ,但您可以添加包含任何所需控件的Parent 。 调整舞台大小以查看效果。

  StackPane root = new StackPane(); root.getChildren().addAll(canvasPane, cb); 

图片

附录:在@jewelsea 引用的这个相关示例中 ,背景直接在layoutChildren()的实现中呈现,同时在调整封闭Parent的大小时填充Pane

你小时候还有Canvas

是的,这是渲染无尽背景的一种方便方法,虽然有一些开销:“每次调用都会将必要的参数推送到缓冲区,然后在脉冲结束时渲染线程将它们渲染到Canvas节点的图像上。 “

我想从头开始设计像Oracle一样的控件,而不是现有的控件。

如此处所讨论的,“JavaFX UI控件……是通过使用场景图中的节点构建的”,包括图像,文本和基本几何形状 。 这显着减轻了Swing paintComponent()甚至JavaFX getGraphicsContext2D()所需的上下文切换的开销。 当然,正如这里所讨论的,“编写新的UI控件并非易事。” 您必须确定您的用例中的工作是否合理。

我可以隐藏部分孩子来自控制用户,这样他就不会看到该控件包含Canvas吗?

是的, Canvas很方便,但不是必需的。 在下面的示例中, LinePane extends StackPane并在您的问题中使用图像的一部分来LinePane extends StackPane Rectangle 。 请注意, LinePane仍然是LinePane中的StackPane ,它在LineTest中添加了一个Button 。 默认情况下, Rectangle占用Pos.CENTER 。 覆盖layoutChildren()可以动态调整Rectangle大小。

图片

 import javafx.application.Application; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.image.Image; import javafx.scene.layout.StackPane; import javafx.scene.paint.ImagePattern; import javafx.scene.shape.Rectangle; import javafx.stage.Stage; /** * @see https://stackoverflow.com/a/43814198/230513 */ public class LineTest extends Application { @Override public void start(Stage Stage) { Stage.setTitle("LineTest"); LinePane linePane = new LinePane(); Button button = new Button("Button"); LinePane.setAlignment(button, Pos.TOP_LEFT); LinePane.setMargin(button, new Insets(50)); linePane.getChildren().add(button); Scene scene = new Scene(linePane, 320, 240); Stage.setScene(scene); Stage.show(); } private static class LinePane extends StackPane { private static final String URL = "https://i.stack.imgur.com/bqXKK.png"; private final Image lines = new Image(URL); private final Rectangle rectangle = new Rectangle(); public LinePane() { rectangle.setFill(new ImagePattern(lines, 8, 22, 34, 34, false)); getChildren().add(rectangle); } @Override protected void layoutChildren() { super.layoutChildren(); final double x = snappedLeftInset(); final double y = snappedTopInset(); final double w = snapSize(getWidth()) - x - snappedRightInset(); final double h = snapSize(getHeight()) - y - snappedBottomInset(); rectangle.setLayoutX(x); rectangle.setLayoutY(y); rectangle.setWidth(w); rectangle.setHeight(h); } } public static void main(String[] args) { launch(args); } }