I'm using Spring JPA with OpenJFX. It's this project JavaFX-weaver, simply adding spring-boot-start-data-jpa inside pom.
However my starting time of Spring JPA is 15-20s and the UI will not show until spring is initalized. When users will start the application it takes a lot of time, every time!
As a workaround i tried to create a simply java fx application without Spring (using this demo here) and then starting there in the main method the main method from spring over a button (see example bellow). That will start spring, but dependencies and properties are not laoded.
Do you know a good way to practice that case ? Every help is welcome.
Thank you
AppBootstrap (Java + OpenJFX)
public class AppBootstrap extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
// start spring jpa main method
btn.setOnAction(event -> App.main(new String[]{""}));
StackPane root = new StackPane();
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}
App (Spring JPA + javafx-weaver)
#SpringBootApplication
public class App {
public static void main(String[] args) {
Application.launch(SpringbootJavaFxApplication.class, args);
}
}
Startup of an JPA powered Application increases load time for ApplicationContext. While you can make things faster by not checking or creating a database scheme, e.g. by setting hibernate.hbm2ddl.auto=none, this is not the best option.
It is by design that the primary stage is shown after the ApplicationContext is loaded, since it should be able to be dependency injected.
The best practice I recommend is using a splash screen while loading the ApplicationContext. It's a bit tricky, since you have separate Threads, but roughly it looks like this:
Create a splash window
public class Splash {
private static final int SPLASH_WIDTH = 200;
private static final int SPLASH_HEIGHT = 200;
private final Parent parent;
private final Stage stage;
public Splash() {
this.stage = new Stage();
stage.setWidth(SPLASH_WIDTH);
stage.setHeight(SPLASH_HEIGHT);
Label progressText = new Label("Application loading ...");
VBox splashLayout = new VBox();
splashLayout.setAlignment(Pos.CENTER);
splashLayout.getChildren().addAll(progressText);
progressText.setAlignment(Pos.CENTER);
splashLayout.setStyle(
"-fx-padding: 5; " +
"-fx-background-color: white; " +
"-fx-border-width:5; " +
"-fx-border-color: white;"
);
splashLayout.setEffect(new DropShadow());
this.parent = splashLayout;
}
public void show() {
Scene splashScene = new Scene(parent);
stage.initStyle(StageStyle.UNDECORATED);
final Rectangle2D bounds = Screen.getPrimary().getBounds();
stage.setScene(splashScene);
stage.setX(bounds.getMinX() + bounds.getWidth() / 2 - SPLASH_WIDTH / 2.0);
stage.setY(bounds.getMinY() + bounds.getHeight() / 2 - SPLASH_HEIGHT / 2.0);
stage.show();
}
public void hide() {
stage.toFront();
FadeTransition fadeSplash = new FadeTransition(Duration.seconds(0.3), parent);
fadeSplash.setFromValue(1.0);
fadeSplash.setToValue(0.0);
fadeSplash.setOnFinished(actionEvent -> stage.hide());
fadeSplash.play();
}
}
Initialize Application
public class SpringbootJavaFxApplication extends Application {
private ConfigurableApplicationContext context;
class ApplicationContextLoader extends Task<Void> {
private final Stage primaryStage;
ApplicationContextLoader(Stage primaryStage) {
this.primaryStage = primaryStage;
}
#Override
protected Void call() {
ApplicationContextInitializer<GenericApplicationContext> initializer =
context -> {
context.registerBean(Application.class, () -> SpringbootJavaFxApplication.this);
context.registerBean(Stage.class, () -> primaryStage);
context.registerBean(Parameters.class,
SpringbootJavaFxApplication.this::getParameters); // for demonstration, not really needed
};
SpringbootJavaFxApplication.this.context = new SpringApplicationBuilder()
.sources(JavaFxSpringbootDemo.class)
.initializers(initializer)
.run(getParameters().getRaw().toArray(new String[0]));
return null;
}
}
#Override
public void start(Stage primaryStage) {
var splash = new Splash();
splash.show();
final ApplicationContextLoader applicationContextLoader = new ApplicationContextLoader(primaryStage);
applicationContextLoader.stateProperty().addListener((observableValue, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
context.publishEvent(new StageReadyEvent(primaryStage));
splash.hide();
}
});
new Thread(applicationContextLoader).start();
}
#Override
public void stop() {
this.context.close();
Platform.exit();
}
}
Related
Hi Guys i build a GUI and on this GUI is a Button and when I press the Button a second GUI appears, on the second GUI is also a Button and when i press the Button it goes back
GU1
btn.setOnAction(new EventHandler <ActionEvent>(){
public void handle(ActionEvent arg0) {
try {
new GUI2().start(primaryStage);
} catch (Exception e) {
e.printStackTrace();
}
}
});
My Questions!
Is GUI1 still running when i press the Button?
GUI2
btn.setOnAction(new EventHandler <ActionEvent>(){
public void handle(ActionEvent arg0) {
try {
//back to the main menu
new GUI1().start(primaryStage);
} catch (Exception e) {
e.printStackTrace();
}
}
});
When i press the Button, does it go back to the same instance when beginning the program? Or make it a new Instance witch has the same look, and use it more RAM;
How should it works, when i want to open the second GUI in a external Window
When i press the Button, does it go back to the same instance when beginning the program?
No, a new instance is created based on your code new GUI2().start(primaryStage);. Always remember that thenew keyword ALWAYS creates a new object.
How should it works, when i want to open the second GUI in a external Window?
There are lots of ways to do this.
Method 1
If it happen that you created two applications, both extending the Application class, this method should work.
public class MultiWindowFX {
private static final Logger logger = Logger.getGlobal();
public static class GUI1 extends Application {
private final Button buttonShowGUI2;
private final GUI2 gui2;
public GUI1() {
buttonShowGUI2 = new Button("Show GUI 2");
gui2 = new GUI2();
}
public Button getButtonShowGUI2() {
return buttonShowGUI2;
}
#Override
public void start(Stage primaryStage) throws Exception {
//add an action event on GUI2's buttonShowGUI1 to send front GUI1
gui2.getButtonShowGUI1().setOnAction(gui2ButtonEvent -> {
if (primaryStage.isShowing()) primaryStage.toFront();
else primaryStage.show();
});
//button with action to show GUI 2
buttonShowGUI2.setOnAction(actionEvent -> {
try {
if (gui2.getPrimaryStage() == null) gui2.start(new Stage());
else gui2.getPrimaryStage().toFront();
} catch (Exception ex) {
logger.log(Level.SEVERE, null, ex);
}
});
//set scene and its root
Pane root = new StackPane(buttonShowGUI2);
Scene stageScene = new Scene(root, 400, 250);
//set stage
primaryStage.setScene(stageScene);
primaryStage.centerOnScreen();
primaryStage.setTitle("GUI 1");
primaryStage.show();
}
public static void launchApp(String... args) {
GUI1.launch(args);
}
}
public static class GUI2 extends Application {
private Stage primaryStage;
private final Button buttonShowGUI1;
public GUI2() {
buttonShowGUI1 = new Button("Show GUI 1");
}
public Button getButtonShowGUI1() {
return buttonShowGUI1;
}
public Stage getPrimaryStage() {
return primaryStage;
}
#Override
public void start(Stage primaryStage) throws Exception {
//get stage reference
this.primaryStage = primaryStage;
//set scene and its root
Pane root = new StackPane(buttonShowGUI1);
Scene stageScene = new Scene(root, 400, 250);
//set stage
primaryStage.setScene(stageScene);
primaryStage.centerOnScreen();
primaryStage.setTitle("GUI 2");
primaryStage.show();
}
public static void launchApp(String... args) {
GUI2.launch(args);
}
}
public static void main(String... args) {
GUI1.launchApp(args);
}
}
Method 2
For me, this is the best approach especially if you want window ownership and modality works.
public class GUI1 extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Show GUI2");
btn.setOnAction(actionEvent -> {
//prepare gui2
Stage gui2Stage = createGUI2();
//set window modality and ownership
gui2Stage.initModality(Modality.APPLICATION_MODAL);
gui2Stage.initOwner(primaryStage);
//show
gui2Stage.show();
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 400, 250);
primaryStage.setTitle("GUI 1");
primaryStage.setScene(scene);
primaryStage.show();
}
private Stage createGUI2() {
Button btn = new Button();
btn.setText("Show GUI1");
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 150);
Stage gui2Stage = new Stage();
gui2Stage.setTitle("GUI 2");
gui2Stage.setScene(scene);
//add an action event to GUI2's button, which hides GUI2 and refocuses to GUI1
btn.setOnAction(actionEvent -> gui2Stage.hide());
return gui2Stage;
}
public static void main(String[] args) {
launch(args);
}
}
...and among other methods. Choose the approach that fits to your requirements.
I was doing some homework today and I've accomplished all of the goals of the assignment, which I'm sure will get me full points.
In an earlier class, however, we used the same Event Handler for more than one action (in this example, you either type a color in the text field, or click a button to change the background color of the box).
I can't figure out how I would do that in this case... do I have to choose a Type in the constructor? If the first parameter could be a button or a textfield then I think that would help.
I'm just trying to figure out how to apply DRY (Don't Repeat Yourself), where ever I can.
public class ColorChooserApplication extends Application
{
#Override
public void start(Stage stage)
{
// Create all UI components
VBox backgroundBox = new VBox(10);
backgroundBox.setPadding(new Insets(10));
HBox topBox = new HBox(10);
HBox bottomBox = new HBox(10);
TextField colorPrompt = new TextField();
colorPrompt.setOnAction(new ColorHandler(colorPrompt, backgroundBox));
Button redButton = new Button("Red");
redButton.setOnAction(new ButtonHandler(redButton, backgroundBox));
Button whiteButton = new Button("White");
whiteButton.setOnAction(new ButtonHandler(whiteButton, backgroundBox));
Button blueButton = new Button("Blue");
blueButton.setOnAction(new ButtonHandler(blueButton, backgroundBox));
// Assemble
topBox.getChildren().add(colorPrompt);
bottomBox.getChildren().addAll(redButton, whiteButton, blueButton);
backgroundBox.getChildren().addAll(topBox, bottomBox);
backgroundBox.setAlignment(Pos.CENTER);
topBox.setAlignment(Pos.CENTER);
bottomBox.setAlignment(Pos.CENTER);
// Set scene and show
stage.setScene(new Scene(backgroundBox));
stage.show();
}
class ColorHandler implements EventHandler<ActionEvent>
{
TextField colorTf;
VBox bgVbox;
public ColorHandler(TextField colorTf, VBox bgVbox)
{
this.colorTf = colorTf;
this.bgVbox = bgVbox;
}
#Override
public void handle(ActionEvent event)
{
String color = colorTf.getText();
bgVbox.setStyle("-fx-background-color:" + color);
}
}
class ButtonHandler implements EventHandler<ActionEvent>
{
Button colorButton;
VBox bgVbox;
public ButtonHandler(Button colorButton, VBox bgVbox)
{
this.colorButton = colorButton;
this.bgVbox = bgVbox;
}
#Override
public void handle(ActionEvent event)
{
String color = colorButton.getText();
bgVbox.setStyle("-fx-background-color:" + color);
}
}
public static void main(String[] args)
{
launch(args);
}
}
If you're using Java 8, you can do
class ColorHandler implements EventHandler<ActionEvent> {
Supplier<String> colorSupplier ;
VBox bgVbox ;
public ColorHandler(Supplier<String> colorSupplier, VBox bgVbox) {
this.colorSupplier = colorSupplier ;
this.bgVbox = bgVbox ;
}
#Override
public void handle(ActionEvent event) {
String color = colorSupplier.get();
bgVbox.setStyle("-fx-background-color: "+color);
}
}
and then
colorPrompt.setOnAction(new ColorHandler(colorPrompt::getText, backgroundBox));
redButton.setOnAction(new ColorHandler(redButton::getText, backgroundBox));
Note that all you need to provide for the first parameter is some function that returns the correct string for use in the css. So you can do things like
whiteButton.setOnAction(new ColorHandler(() -> "#ffffff", backgroundBox));
blueButton.setOnAction(new ColorHandler(() -> "cornflowerblue", backgroundBox));
etc.
I am using javafx combined with FXML.
I want to apply the MVC pattern. For that I want my Model.java class to be the model, which launches the View.fxml and the controller of that view would be viewController.java.
I need to bring Model.java and Controller.java to communicate at some point. So let's say ViewController.java looks this way:
public class ViewController implements Initializable {
private String parameter = "hello";
#FXML
private Label label;
#FXML
private Accordion acccord;
public String getParemeter() {
return this.parameter;
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
ViewController has a private string and its own methods.
And Model.java :
public class Model extends Application {
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("View.fxml") );
Parent root = loader.load(); // Here the View is loaded and the Contoller is created along.
loader.getController(); // ?
//Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
How can I access the ViewContoller parameters / methods (such as getPamareter() ) ?
I tried to get the controller with loader.getController() but it returns a generic type, what should I do with it, provided it has something to do with it? I went to the oracle documentation but I am not quite sure to understan, does getController() return an instance of my ViewController.java ?
HOw can I access the Model from the ViewController?
For instance a buton is triggered, the vieController would update a value in Model.java.
when I use this code the result is empty page:
public class Vaadin6biuApplication extends Application {
#Override
public void init() {
xx a = new xx();
Window w = new Window("aness conf");
w.addComponent(a);
setMainWindow(w);
}
}
public class xx extends CustomComponent {
#AutoGenerated
private AbsoluteLayout mainLayout;
#AutoGenerated
private Button button_1;
public xx() {
buildMainLayout();
setCompositionRoot(mainLayout);
}
#AutoGenerated
private AbsoluteLayout buildMainLayout() {
mainLayout = new AbsoluteLayout();
mainLayout.setImmediate(false);
button_1 = new Button();
mainLayout.addComponent(button_1, "top:100.0px;left:100.0px;");
return mainLayout;
}
}
how to add custom component to application?
thank you for your answers
Have you read the wiki tutorial for vaadin6 + spring? With spring > 2.5 it's fairly simple:
#Configurable(preConstruction = true)
public class SpringHelloWorld extends com.vaadin.Application {
#Autowired
private MyBeanInterface bean;
public void init() {
final Window main = new Window("Hello window");
setMainWindow(main);
main.addComponent(new Label( bean.myMethod() ));
}
}
I am working on an application where I want to implement a menu. I have a GameState and a MainMenu class. Both extends Group. I can't figure out how to write a change listener to the Main.state, so when it changes from .MENU to .GAME the scenes will switch.
Here's is a part of the MainMenu class:
public class MainMenu extends Group {
private final Image background;
private final Rectangle bgRect;
private final int buttonNo = 3;
private MenuButton[] buttons;
private final double xStart = -200;
private final double yStart = 100;
private Group root;
private Scene scene;
public MainMenu () {
background = new Image(getClass().getResource("mainmenuBg.png").toString());
bgRect = new Rectangle(660,660);
bgRect.setFill(new ImagePattern(background));
root = new Group();
scene = new Scene(root, 650, 650);
scene.setCamera(new PerspectiveCamera());
root.getChildren().add(bgRect);
initButtons(root);
//Start game
buttons[0].setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event t) {
Main.state = STATE.GAME;
}
});
//Options
buttons[1].setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event t) {
//options menu will come here
}
});
//Exit
buttons[2].setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event t) {
Platform.exit();
}
});
}
//...
}
The main class:
public class Main extends Application {
public int difficulty = 1;
GameState gameState;
MainMenu mainMenu;
public enum STATE {
MENU,
GAME
}
public static STATE state = STATE.MENU;
#Override
public void start(Stage stage) {
stage.resizableProperty().setValue(false);
stage.setTitle("Main");
Scene scene = new Scene(new StackPane(), 650, 650);
stage.setScene(scene);
stage.show();
if(Main.state == STATE.MENU)
enterMainMenu(stage);
if(Main.state == STATE.GAME)
enterGameState(stage);
}
//...
}
Any help would be appreciated.
I have succeeded to find a good solution.
I have removed the scene field from these classes, and added the super method in the constructors, than added the elements to the class (this.getChildren().addAll(..)).
Finally, here's my main controller:
public class Main extends Application {
public int difficulty = 1;
public GameState gameState = new GameState(difficulty);
public MainMenu mainMenu = new MainMenu();;
StackPane stackPane = new StackPane();
#Override
public void start(final Stage stage) {
stage.resizableProperty().setValue(false);
stage.setTitle("Main");
Scene scene = new Scene(stackPane, 650, 650);
scene.setCamera(new PerspectiveCamera());
stage.setScene(scene);
stage.show();
stackPane.getChildren().add(mainMenu);
mainMenu.getStartButton().setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
changeScene(gameState);
try {
gameState.startGame();
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public void changeScene(Parent newPage) {
stackPane.getChildren().add(newPage);
EventHandler<ActionEvent> finished = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
stackPane.getChildren().remove(0);
}
};
final Timeline switchPage = new Timeline(
new KeyFrame(Duration.seconds(0), new KeyValue(stackPane.getChildren().get(1).opacityProperty(), 0.0), new KeyValue(stackPane.getChildren().get(0).opacityProperty(), 1.0)),
new KeyFrame(Duration.seconds(3), finished, new KeyValue(stackPane.getChildren().get(1).opacityProperty(), 1.0), new KeyValue(stackPane.getChildren().get(0).opacityProperty(), 0.0))
);
switchPage.play();
}
}