How to create totally custom JavaFX control? Or: how to create Pane with dynamically drawn background?

前端 未结 1 724
花落未央
花落未央 2020-12-22 05:21

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 ne

相关标签:
1条回答
  • 2020-12-22 06:12

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题