I’ve been working a bit with JavaFX for a little while now, looking to achieve complex graphical applications with more power and greater programmatic ease than I ever could with the Java Foundation Classes (AWT, Swing, and Java 2D). If nothing else, the ability to tool the look and feel through CSS (Cascading Style Sheets) is a huge advance over pure Java programming, But I digress…
I’ve had one application that uses JavaFX Canvas (javafx.scene.canvas.Canvas) for some complex rendering, but it hasn’t worked the way I wanted it to. So I backed up and created a simple JavaFX lab to work with and better understand how to use a JavaFX Canvas in a BorderPane. I did finally come to understand how to use the various components together. I’m sharing this in the hope that it’ll help anyone with similar needs and looking for a solution.
package borderpaneexample;import javafx.application.Application;import javafx.event.ActionEvent;import javafx.geometry.Insets;import javafx.geometry.Pos;import javafx.scene.Scene;import javafx.scene.control.Button;import javafx.scene.layout.BorderPane;import javafx.scene.layout.HBox;import javafx.scene.layout.Pane;import javafx.scene.layout.StackPane;import javafx.scene.layout.VBox;import javafx.stage.Stage;public class BorderPaneExample extends Application {@Overridepublic void start(Stage primaryStage) {BorderPane borderPane = new BorderPane();Button rightButton = new Button("Right");rightButton.setOnAction((ActionEvent event) -> {System.out.println("Right button");});Button leftButton = new Button("Left");leftButton.setOnAction((ActionEvent event) -> {System.out.println("Left button");});Button topButton = new Button("Top");topButton.setOnAction((ActionEvent event) -> {System.out.println("Top button");});Button bottomButton = new Button("Bottom");bottomButton.setOnAction((ActionEvent event) -> {System.out.println("Bottom button");});Pane centerPane = new Pane();ResizeableCanvas resizeableCanvas = new ResizeableCanvas(centerPane);centerPane.getChildren().add(resizeableCanvas);centerPane.setStyle("-fx-padding: 0;" +"-fx-border-style: solid inside;" +"-fx-border-width: 1;" +"-fx-border-insets: 0;" +"-fx-border-radius: 0;" +"-fx-border-color: #000;");borderPane.setCenter(centerPane);borderPane.setRight(rightButton);BorderPane.setAlignment(rightButton, Pos.CENTER_RIGHT);VBox vbox = makeVBox();vbox.getChildren().add(leftButton);borderPane.setLeft(vbox);BorderPane.setAlignment(leftButton, Pos.CENTER_LEFT);HBox hbox = makeHBox();hbox.getChildren().add(topButton);borderPane.setTop(hbox);BorderPane.setAlignment(topButton, Pos.TOP_CENTER);borderPane.setBottom(bottomButton);BorderPane.setAlignment(bottomButton, Pos.BOTTOM_CENTER);borderPane.setStyle("-fx-padding: 2;" +"-fx-border-style: solid inside;" +"-fx-border-width: 2;" +"-fx-border-insets: 2;" +"-fx-border-radius: 0;" +"-fx-border-color: #cccccc;");Scene scene = new Scene(borderPane, 800, 600);primaryStage.setTitle("BorderPane Example with Canvas");primaryStage.setScene(scene);primaryStage.show();}public static void main(String[] args) {launch(args);}public HBox makeHBox() {HBox hbox = new HBox();hbox.setPadding(new Insets(5, 5, 5, 5));hbox.setSpacing(5);hbox.setStyle("-fx-background-color: #cccccc;");return hbox;}public VBox makeVBox() {VBox vbox = new VBox();vbox.setPadding(new Insets(5, 5, 5, 5));vbox.setSpacing(5);vbox.setStyle("-fx-background-color: #dddddd;");return vbox;}}
The main class is where the application is constructed and launched. The key to making this work is to create a class derived from Canvas.
package borderpaneexample;import javafx.scene.canvas.Canvas;import javafx.scene.canvas.GraphicsContext;import javafx.scene.layout.Region;import javafx.scene.paint.Color;import javafx.scene.text.Font;public class ResizeableCanvas extends Canvas {private double width, height;public ResizeableCanvas(Region region) {widthProperty().bind(region.widthProperty());heightProperty().bind(region.heightProperty());widthProperty().addListener(event -> resizeDraw() );heightProperty().addListener(event -> resizeDraw() );}private void resizeDraw() {width = getWidth();height = getHeight();GraphicsContext gc = getGraphicsContext2D();gc.clearRect(0, 0, width, height);gc.fillOval((width - 40)/2.0, (height - 40)/2.0, 40, 40);Font font = gc.getFont();gc.fillText("Width: " + Double.toString(width), width/2 + 2, font.getSize());// Some interesting graphics context manipulation. Move to the left// edge and print the height, rotated 90 degrees parallel to the// left edge.//gc.save();gc.translate(font.getSize(), height/2);gc.rotate(-90);gc.fillText("Height: " + Double.toString(height), 2, 0);gc.restore();gc.setStroke(Color.RED);gc.setLineWidth(2);gc.strokeLine(0, 0, width, height);gc.strokeLine(0, height, width, 0);gc.strokeLine(width/2.0, 0, width/2.0, height);gc.strokeLine(0, height/2.0, width, height/2.0);}@Overridepublic boolean isResizable() { return true;} @Overridepublic double prefWidth(double height) { return getWidth();} @Overridepublic double prefHeight(double width) { return getHeight();}}
The exposition for how this works is in the comments of ResizeableCanvas. But if you want to see where it was added, then check out highlighted lines 46-48 in the main class. All of this was done in NetBeans 8.2 as the editor/IDE, and Java 8 update 152. It’s going to be a while yet before I move to Java 9; all the tools I need just aren’t there yet.
You must be logged in to post a comment.