This is my first question to Stackoverflow, so be gentle with me :)
I recently discovered JavaFX as a new approach to design a GUI for Java applications. Currently I am working on a project, that requires to draw up to 500x500 (250.000) objects on a Canvas
, which can be from 1x1px to 20x20px big (all have the same size).
Sadly the performance isn't very good. Drawing a field of 500x500 rectangles takes like 10 seconds.
I made an example application to demonstrate the problem.
Why is the performance so bad? Since the drawing takes place in the UI thread, the UI is blocked until the drawing is finished. I have 2 aproaches:
- Make drawing fast.
- Draw to a buffer object in a separate thread and display the result on the canvas.
My experiments with writing to a BufferedImage before drawing that image on the canvas failed hard (canvas stayed empty).
Thanks for taking your time to help research this topic :)
sample.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.GridPane?>
<AnchorPane prefHeight="275.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="sample.Controller">
<children>
<ScrollPane fx:id="scrollPane" content="$null" prefHeight="237.0" prefWidth="300.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="38.0" />
<Button layoutX="14.0" layoutY="14.0" mnemonicParsing="false" onAction="#draw" text="Draw" />
</children>
</AnchorPane>
Controller.java:
package sample;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.ScrollPane;
import javafx.scene.paint.Color;
import java.util.Random;
public class Controller {
@FXML
private ScrollPane scrollPane;
Canvas canvas;
@FXML
protected void draw(ActionEvent event) {
canvas = new Canvas(500,500);
scrollPane.setContent(canvas);
GraphicsContext gc = canvas.getGraphicsContext2D();
Random r = new Random();
for(int i = 0; i<=500; i++) {
for(int j = 0; j<=500; j++) {
if (r.nextBoolean()) {
gc.setFill(Color.BLACK);
} else {
gc.setFill(Color.BLUE);
}
gc.fillRect(i, j, i+1, j+1);
}
}
}
}
Main.java:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
ScrollPane scrollPane = (ScrollPane) root.lookup("#scrollPane");
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}