如何创建完全自定义的JavaFX控件?或者:如何使用动态绘制的背景创建窗格? [英] How to create totally custom JavaFX control? Or: how to create Pane with dynamically drawn background?

查看:278
本文介绍了如何创建完全自定义的JavaFX控件?或者:如何使用动态绘制的背景创建窗格?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



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



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



同样,窗格应该像普通窗格一样,即可以添加子窗口。



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



更新



我想设计一个集装箱自定义背景。



例如,像这样:





(它应该是无穷无尽的,即一旦控件调整大小就显示更多行)



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



像这样:



使用



附录:在此相关的

  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
* /
公共类LineTest扩展应用程序{

@Override
public void start(Stage Stage){
Stage.setTitle(LineTest);
LinePane linePane = new LinePane();
按钮按钮=新按钮(按钮);
LinePane.setAlignment(button,Pos.TOP_LEFT);
LinePane.setMargin(button,new Insets(50));
linePane.getChildren()。add(button);
场景场景=新场景(linePane,320,240);
Stage.setScene(场景);
Stage.show();
}

私有静态类LinePane扩展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);
}
}


I would like to create totally custom JavaFX control. I don't want "to prefer composition", because I don't have things to compose of.

For example, suppose I need a pane, which has coordinate grid inside. Pane should work as normal pane, i.e. it should be possible to add controls or geometric fugures there, but at backgroud of Pane a grid should be drawn. Additionaly, numeric values should be drawn at the edge of the pane.

All this should dynamically reflect the transform and viewport of the pane.

Another example: suppose I want to have tiled image as beckground. There are millions of tiles, like in google maps, so I can't load them as child nodes, because they will exhaust memory. I need they dynamilcally loaded and unloaded while user scrolls the pane.

Again, the pane should behave as normal pane, i.e. childs can be added to it.

How to accomplish this? I am finding, that low-level methods similar to paintConponent are either absent or deprecated. So, what to do?

UPDATE

I want to design A CONTAINER with custom background.

For example, like this:

(it should be endless, i.e. show more lines once control resized)

Container should have no children by default, but still has background. Background should not be child of a container. I, programmer, should be able to add children to this container and only after that, children should appear in the container. They should appear above background.

Like this:

Note, that we have only 2 children here.

UPDATE

In code below ScrollBarstandard control is displayed. As you can see, it contains knob, which can be moved, and arrow buttons, which can be pressed.

Simultaneously, the number of children in this control is reported as zero.

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

Okay, I agree if everybody say it is impossible to draw like in Swing, let's do composition. But how to hide this composition from the user, as it done it ScrollBarcontrol?

解决方案

Use Canvas to render arbitrary content using commands provided by a GraphicsContext; add the Canvas to a Pane having a suitable layout. In this example, "CanvasPane wraps an instance of Canvas in a Pane and overrides layoutChildren() to make the canvas dimensions match the enclosing Pane." Switching the root from BorderPane to StackPane allows placing controls atop the animated background. The example adds a single CheckBox, but you can add a Parent containing any desired controls. Resize the stage to see the effect.

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

Addendum: In this related example, cited by @jewelsea, the background is rendered directly in the implementation of layoutChildren(), also filling the Pane as the enclosing Parent is resized.

You still have Canvas as a child.

Yes, it's a convenient way to render endless background, albeit with some overhead: "Each call pushes the necessary parameters onto the buffer where they will be later rendered onto the image of the Canvas node by the rendering thread at the end of a pulse."

I want to design controls like Oracle does, from scratch, not combining existing ones.

As discussed here, "JavaFX UI controls…are built by using nodes in the scene graph," including images, text and basic geometric shapes. This significantly mitigates the overhead of the context switch required by Swing paintComponent() and even JavaFX getGraphicsContext2D(). Of course, as discussed here, "writing new UI Controls is not trivial." You'll have to decide if the effort is justified in your use case.

Can I hide part of children from control user, so that he didn't see that control contains Canvas?

Yes, Canvas is convenient, but not essential. In the example below, LinePane extends StackPane and tiles a Rectangle with a portion of an image in your question. Note that LinePane is still a StackPane in LineTest, which adds a Button in Pos.TOP_LEFT. The Rectangle occupies the Pos.CENTER by default. Overriding layoutChildren() affords an opportunity for the Rectangle to be resized dynamically.

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

这篇关于如何创建完全自定义的JavaFX控件?或者:如何使用动态绘制的背景创建窗格?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆