Bind window visibility with running state of a service in JavaFX - java

I'm using a loading screen in my application while a service is running a task in another thread.
After the service is done, I would like to close the loading screen.
But instead of calling something like window.hide() every time, I would like to have a binding between the service state and the visibility of the window.
Service runs --> loading screen visible
Service runs not --> loading screen invisible
The service has properties like onRunningProperty() or runningProperty() and the window has onShownProperty() or showingProperty() but I didn't manage to bind them.
How can I bind the visibility of the loading screen with the running state of a service, so that the loading screen is automatically shown, when the service runs and hidden, when the service is done?
Example:
HelloApplication.java
public class HelloApplication extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
HelloController.java
public class HelloController {
#FXML
private Label welcomeText;
private final Service<Void> service = new Service<>() {
#Override
protected Task<Void> createTask() {
return new Task<>() {
#Override
protected Void call() throws Exception {
Thread.sleep(3000);
return null;
}
};
}
};
public HelloController() {
service.setOnSucceeded(e -> {
// now I want to hide the loading screen
WaitController.waitController.waitLabel.getScene().getWindow().hide();
});
}
#FXML
protected void onHelloButtonClick() {
welcomeText.setText("Welcome to JavaFX Application!");
service.restart();
try {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("wait.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 630, 400);
Stage stage = new Stage();
stage.setTitle("New Window");
stage.setScene(scene);
stage.show();
WaitController.waitController = fxmlLoader.getController();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
WaitController.java
public class WaitController {
#FXML
Label waitLabel;
public static WaitController waitController;
}
wait.fxml
<AnchorPane prefHeight="291.0" prefWidth="428.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.test.WaitController">
<children>
<Label fx:id="waitLabel" layoutX="161.0" layoutY="100.0" prefHeight="92.0" prefWidth="105.0" text="Wait..." textAlignment="CENTER">
<font>
<Font size="18.0" />
</font>
</Label>
</children>
</AnchorPane>
hello-view.fxml
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.test.HelloController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
</padding>
<Label fx:id="welcomeText"/>
<Button text="Hello!" onAction="#onHelloButtonClick"/>
</VBox>

You won't be able to use bindings for this. There are no writable properties of Window that control whether or not it's showing. There is, of course, the showing property, but it is read-only. In other words, there's no appropriate property of Window that you can bind to the service's running property.
What you can do, however, is listen to the service's running property and call show() / hide() on the window instance as appropriate. For example:
import javafx.application.Application;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
var service = new ServiceImpl();
setupWindowForService(primaryStage, service);
var button = new Button("Start service");
button.disableProperty().bind(service.runningProperty());
button.setOnAction(e -> {
e.consume();
service.restart();
});
primaryStage.setScene(new Scene(new StackPane(button), 600, 400));
primaryStage.show();
}
private void setupWindowForService(Window owner, Service<?> service) {
var window = new Stage();
window.initOwner(owner);
window.initModality(Modality.WINDOW_MODAL);
window.setTitle("Service Window");
window.setScene(new Scene(new StackPane(new Label("Service running...")), 300, 150));
window.setOnCloseRequest(e -> {
if (service.isRunning()) {
// prevents user from closing the window while service is
// running. Perhaps it would make more sense to cancel the
// service?
e.consume();
}
});
// the code that shows and hides the window based on the service's state
service.runningProperty().addListener((obs, wasRunning, isRunning) -> {
if (isRunning) {
window.show();
} else {
window.hide();
}
});
}
private static class ServiceImpl extends Service<Void> {
#Override protected Task<Void> createTask() {
return new Task<>() {
#Override protected Void call() throws Exception {
int max = 3_000;
for (int i = 0; i < max; i++) {
Thread.sleep(1L);
}
return null;
}
};
}
}
}

Related

How to write conditions on button click in JavaFX

I want to make it so that when a button is pressed, different windows are displayed, and for this I need conditions. I don't want to create many methods for each button
This code doesn't work:
#Override
public void buttonOnAction(ActionEvent event){
if(btnReaders.isPressed()){
btnReaders.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
Parent parent = null;
try {
parent = FXMLLoader.load(getClass().getResource("readersMenu.fxml"));
} catch (IOException ex) {
ex.printStackTrace();
}
Scene scene = new Scene(parent);
Stage window = (Stage) ((Node)event.getSource()).getScene().getWindow();
window.setScene(scene);
window.show();
}
});
}
else if(btnDashboard.isPressed()){
btnDashboard.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
Parent parent = null;
try {
parent = FXMLLoader.load(getClass().getResource("librarianMenu.fxml"));
} catch (IOException ex) {
ex.printStackTrace();
}
Scene scene = new Scene(parent);
Stage window = (Stage) ((Node)event.getSource()).getScene().getWindow();
window.setScene(scene);
window.show();
}
});
}
}
Here is an example of a parameterized event handler that will open the selected FXML in a new scene that will be set for the same stage containing the source node of the event.
When the event handler is created, the application stores, in the event handler, the name of the FXML resource to be loaded.
The event handler is assigned to a button action.
When the button is actioned, the event handler loads a new FXML into a new scene and attaches that scene to the window that the button is defined in.
Example App
For this example, FXML files should be in the same location as the package containing the SceneSelector application.
SceneSelector.java
import javafx.application.Application;
import javafx.event.*;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.Objects;
public class SceneSelector extends Application {
#Override
public void start(Stage stage) {
Button sceneAButton = new Button("Scene A");
sceneAButton.setOnAction(
new SceneChangeEventHandler(
"sceneA.fxml"
)
);
Button sceneBButton = new Button("Scene B");
sceneBButton.setOnAction(
new SceneChangeEventHandler(
"sceneB.fxml"
)
);
Pane layout = new HBox(10,
sceneAButton,
sceneBButton
);
layout.setPadding(new Insets(10));
layout.setPrefSize(200, 150);
stage.setScene(
new Scene(layout)
);
stage.show();
}
class SceneChangeEventHandler implements EventHandler<ActionEvent> {
private final String fxmlResourceName;
public SceneChangeEventHandler(String fxmlResourceName) {
this.fxmlResourceName = fxmlResourceName;
}
#Override
public void handle(ActionEvent event) {
try {
Stage stage = (Stage) ((Node) event.getSource())
.getScene()
.getWindow();
changeScene(stage, fxmlResourceName);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void changeScene(
Stage stage,
String fxmlResourceName
) throws IOException {
Parent parent = FXMLLoader.load(
Objects.requireNonNull(
getClass().getResource(
fxmlResourceName
)
)
);
Scene scene = new Scene(parent);
stage.setScene(scene);
stage.setTitle(fxmlResourceName);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
sceneA.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<StackPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
prefHeight="150.0" prefWidth="200.0" style="-fx-background-color: lemonchiffon;"/>
sceneB.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<StackPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
prefHeight="150.0" prefWidth="200.0" style="-fx-background-color: azure;"/>

Getting a LoadException when I put controller in nested FXML file

The application works fine up until I put a fx:controller attribute inside of a nested fxml file.
Main class:
#SpringBootApplication
public class MyApplication extends Application{
private Stage primaryStage;
private AnchorPane rootLayout;
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
launch(args);
}
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Survey Creator");
initRootLayout();
}
private void initRootLayout() {
try {
String pathToCss = "survey-generator/out/production/classes/css/Default.css";
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MyApplication.class.getResource("/view/MainLayout.fxml"));
rootLayout = loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
scene.getStylesheets().add(pathToCss);
primaryStage.setResizable(false);
primaryStage.show();
}
catch(Exception e){
throw new RuntimeException(e);
}
}
}
ClientController class:
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
public class ClientController {
#FXML
private ComboBox<String> clientList;
#FXML
public void initialize(){
clientList = null;
}
public ClientController(ComboBox<String> clientList) {
this.clientList = clientList;
}
}
FXML:
<AnchorPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" prefHeight="475.0" prefWidth="923.0"
fx:controller="com.surveycreator.controllers.MainController">
<children>
<fx:include source="MyText.fxml"/>
<VBox alignment="CENTER" layoutX="134.0" layoutY="48.0" prefHeight="100.0" prefWidth="668.0">
<children>
<fx:include source="ClientComboBox.fxml" fx:id="clientList"/>
//more stuff below
ClientCombo.fxml - works fine until I add "fx:controller="com.app.controllers.ClientController"
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ComboBox?>
<ComboBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" prefHeight="25.0" prefWidth="519.0"
promptText="Select Client"/>
After I add the above controller to ClientCombo.fxml I get the following error:
Caused by: java.lang.RuntimeException: javafx.fxml.LoadException:
/C:/Users/myusername/IdeaProjects/survey-generator/out/production/classes/view/ClientComboBox.fxml:7
/C:/Users/myusername/IdeaProjects/survey-generator/out/production/classes/view/MainLayout.fxml:18
at com.app.MyApplication.initRootLayout(MyApplication.java:45)
at com.app.MyApplication.start(MyApplication.java:28)
your not managing fxml context in spring, bootifying should delegate this to spring so try using this
#SpringBootApplication
public class AirQualityFxApplication extends Application {
private ConfigurableApplicationContext context;
private Parent rootNode;
#Override
public void init() throws Exception {
SpringApplicationBuilder builder = new SpringApplicationBuilder(AirQualityFxApplication.class);
context = builder.run(getParameters().getRaw().toArray(new String[0]));
FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
loader.setControllerFactory(context::getBean);
rootNode = loader.load();
}
#Override
public void start(Stage primaryStage) throws Exception {
Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
double width = visualBounds.getWidth();
double height = visualBounds.getHeight();
primaryStage.setScene(new Scene(rootNode, width, height));
primaryStage.centerOnScreen();
primaryStage.show();
}
#Override
public void stop() throws Exception {
context.close();
}
}

FXMLLoader load time in JDK 8u92

In my JavaFX application I have to load many fxml files (200+) in the same time. I have decided to load them in background Task just like in https://stackoverflow.com/a/34878843 answear. Everything works fine (load time was acceptable) until JDK update. Newest version of JDK lengthened load time 3-4 times.
I have checked previous JDK releases and that problem appears from the JDK 8u92.
To test that issue I created new simple JavaFX FXML Application in Netbeans 8.1 and use only generated classes and fxml. Creating view from code works fine.
Application class:
public class FXMLLoaderTest extends Application {
private static Executor ex = Executors.newCachedThreadPool();
//private static Executor ex = Executors.newFixedThreadPool(400);
//private static Executor ex = Executors.newSingleThreadExecutor();
#Override
public void start(Stage stage) throws Exception {
VBox box = new VBox();
ScrollPane root = new ScrollPane(box);
Button b = new Button("GENERATE");
b.setOnAction(e -> {
IntStream.range(0, 1000).forEach(i -> {
Task<Parent> task = new Task<Parent>() {
#Override
protected Parent call() throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
Parent root = null;
try {
root = loader.load();
} catch (IOException ex) {
Logger.getLogger(Loader.class.getName()).log(Level.SEVERE, null, ex);
}
// StackPane root= new StackPane();
// Button click = new Button("Click");
// root.setPrefSize(300, 300);
// root.getChildren().add(click);
return root;
}
};
task.setOnSucceeded(ev -> {
final Parent parent = task.getValue();
box.getChildren().add(parent);
});
task.setOnFailed(ev -> task.getException().printStackTrace());
ex.execute(task);
});
});
box.getChildren().add(b);
Scene scene = new Scene(root, 400, 500);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
FXMLDocument.fxml
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fxmlloader.FXMLDocumentController">
<children>
<Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
<Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
</children>
</AnchorPane>
FXMLDocumentController.java
public class FXMLDocumentController implements Initializable {
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
label.setText("Hello World!");
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
I have tested this on several computers and result was always the same. On JDK 8u91 fxml files load fast. I have checked release note of 8u92 and I haven't found any changes in FXMLLoader class.
Has anybody encounter this issue? Mayby I am doing something wrong then please correct me.

JavaFX resize canvas in fxml

I'm trying to resize a canvas in Javafx. I am using scene builder and fxml. So far, when the user clicks on the canvas the canvas turns black, and when I resize the screen and click on the canvas only the original size of the canvas turns black (canvas is not being resized). I'm not sure how to solve this. Any ideas or solutions would help alot.
Code:
Controller:
public class MainFXMLController implements Initializable
{
#FXML
private Canvas mainCanvas;
#FXML
public GraphicsContext gc;
public void initGraphics()
{
gc = mainCanvas.getGraphicsContext2D();
}
public void drawClicked(MouseEvent me)
{
gc.clearRect(0, 0, mainCanvas.getWidth(), mainCanvas.getHeight());
gc.setFill(Color.BLACK);
gc.fillRect(0, 0, mainCanvas.getWidth(), mainCanvas.getHeight());
System.out.println("Current mosue position: " + me.getX() + ":" + me.getY());
}
#Override
public void initialize(URL url, ResourceBundle rb)
{
initGraphics();
}
}
Fxml:
<AnchorPane id="AnchorPane" prefHeight="600.0" prefWidth="750.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.MainFXMLController">
<children>
<Canvas fx:id="mainCanvas" height="565.0" onMouseClicked="#drawClicked" width="750.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="35.0" />
Main Java file:
public class DrawFx extends Application
{
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("MainFXML.fxml"));
Scene scene = new Scene(root);
stage.setTitle("DrawFx");
stage.getIcons().add(new Image("/icon/icon.png"));
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
First some Javadocs :)
A Canvas node is constructed with a width and height that specifies the size of the image into which the canvas drawing commands are rendered. All drawing operations are clipped to the bounds of that image.
So every time the user resize the window we need to change the width of the canvas and then we need to re-draw the canvas.
Lets start by adding a fx:id to the root layout.
<AnchorPane fx:id="anchorPane" prefHeight="600.0" prefWidth="750.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.MainFXMLController">
<children>
<Canvas fx:id="mainCanvas" height="565.0" onMouseClicked="#drawClicked" width="750.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="35.0"/>
</children>
</AnchorPane>
Next step is to add a change listener to the root layout which will set the new height and width to the canvas and then redraw it. We can do it inside the initialize() of the controller.
public class Controller implements Initializable {
#FXML
AnchorPane anchorPane;
#FXML
private Canvas mainCanvas;
#FXML
public GraphicsContext gc;
public void initGraphics() {
gc = mainCanvas.getGraphicsContext2D();
}
public void drawClicked() {
gc.clearRect(0, 0, mainCanvas.getWidth(), mainCanvas.getHeight());
gc.setFill(Color.BLACK);
gc.fillRect(0, 0, mainCanvas.getWidth(), mainCanvas.getHeight());
}
#Override
public void initialize(URL url, ResourceBundle rb) {
initGraphics();
anchorPane.prefWidthProperty().addListener((ov, oldValue, newValue) -> {
mainCanvas.setWidth(newValue.doubleValue());
drawClicked();
});
anchorPane.prefHeightProperty().addListener((ov, oldValue, newValue) -> {
mainCanvas.setHeight(newValue.doubleValue());
drawClicked();
});
}
}
I haven't created a new method for reDraw() since your drawClicked() wasn't doing anything. But, you can separate both the methods once it makes more sense.
The last thing is to bind to root layout's prefWidthProperty() and prefHeightProperty() to the scene's width and height respectively.
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws IOException {
AnchorPane root = FXMLLoader.load(getClass().getResource("MainFXML.fxml"));
Scene scene = new Scene(root);
stage.setTitle("DrawFx");
stage.setScene(scene);
stage.show();
root.prefWidthProperty().bind(scene.widthProperty());
root.prefHeightProperty().bind(scene.heightProperty());
}
}
If you want to resize canvas in fxml and presumably redraw its contents afterwards, the absolute minimum set is something like this:
test.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="test.TestController">
<children>
<Pane fx:id="pane" VBox.vgrow="ALWAYS">
<children>
<Canvas fx:id="canvas" height="${pane.height}" width="${pane.width}"
onWidthChange="#redraw" onHeightChange="#redraw" />
</children>
</Pane>
</children>
</VBox>
TestController.java
package test;
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
public class TestController {
#FXML
private Canvas canvas;
#FXML
private void redraw() {
double w=canvas.getWidth();
double h=canvas.getHeight();
GraphicsContext gc=canvas.getGraphicsContext2D();
gc.clearRect(0, 0, w, h);
gc.beginPath();
gc.rect(10, 10, w-20, h-20);
gc.stroke();
}
}
Wrapping (it is not part of the functionality, just provided for completeness)
Test.java
package test;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Test extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader=new FXMLLoader(getClass().getResource("test.fxml"));
Parent root=loader.load();
primaryStage.setTitle("Test");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
the test package is there for allowing modular magic,
module-info.java
module cnvtest {
requires transitive javafx.graphics;
requires javafx.fxml;
opens test to javafx.fxml;
exports test;
}
and there are really no more files.

KeyListener JavaFX

I was looking on the internet but I didn't find good information for it. I'm trying to detect key presses every time the app is running. I'm using JavaFX and running it with FXML. I tryed a lot of thing but none work. Please help me.
You should check out the Ensemble sample. Here's the key listener code.
/**
* Copyright (c) 2008, 2012 Oracle and/or its affiliates.
* All rights reserved. Use is subject to license terms.
*/
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
/**
* A sample that demonstrates various key events and their usage. Type in the
* text box to view the triggered events: key pressed, key typed and key
* released. Pressing the Shift, Ctrl, and Alt keys also trigger events.
*
* #see javafx.scene.input.KeyCode
* #see javafx.scene.input.KeyEvent
* #see javafx.event.EventHandler
*/
public class KeyEventsSample extends Application {
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setScene(new Scene(root));
//create a console for logging key events
final ListView<String> console = new ListView<String>(FXCollections.<String>observableArrayList());
// listen on the console items and remove old ones when we get over 20 items in the list
console.getItems().addListener(new ListChangeListener<String>() {
#Override public void onChanged(Change<? extends String> change) {
while (change.next()) {
if (change.getList().size() > 20) change.getList().remove(0);
}
}
});
// create text box for typing in
final TextField textBox = new TextField();
textBox.setPromptText("Write here");
textBox.setStyle("-fx-font-size: 34;");
//add a key listeners
textBox.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(KeyEvent ke) {
console.getItems().add("Key Pressed: " + ke.getText());
}
});
textBox.setOnKeyReleased(new EventHandler<KeyEvent>() {
public void handle(KeyEvent ke) {
console.getItems().add("Key Released: " + ke.getText());
}
});
textBox.setOnKeyTyped(new EventHandler<KeyEvent>() {
public void handle(KeyEvent ke) {
String text = "Key Typed: " + ke.getCharacter();
if (ke.isAltDown()) {
text += " , alt down";
}
if (ke.isControlDown()) {
text += " , ctrl down";
}
if (ke.isMetaDown()) {
text += " , meta down";
}
if (ke.isShiftDown()) {
text += " , shift down";
}
console.getItems().add(text);
}
});
VBox vb = new VBox(10);
vb.getChildren().addAll(textBox, console);
root.getChildren().add(vb);
}
#Override public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}
This worked for me:
At the FXML add the onKeyPressed attribute in the element. Here is an example, note that the onKeyPressed attribute is in the AnchorPane element.
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" onKeyPressed="#handleKeyPressed" xmlns:fx="http://javafx.com/fxml" fx:controller="com.jtetris.jtetris.FXMLController">
<children>
<Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
<Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
</children>
</AnchorPane>
Next, add the method (handleKeyPressed) inside the controller
public class FXMLController implements Initializable {
#FXML
private Label label;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
label.setText("Hello World!");
}
#FXML
private void handleKeyPressed(KeyEvent ke){
System.out.println("Key Pressed: " + ke.getCode());
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
Finally, load the fxml in the start method.
public class MainApp extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/fxml/Scene.fxml"));
Scene scene = new Scene(root);
stage.setTitle("JavaFX and Maven");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Categories