Handle mouse event anywhere with JavaFX - java

I have a JavaFX application, and I would like to add an event handler for a mouse click anywhere within the scene. The following approach works ok, but not exactly in the way I want to. Here is a sample to illustrate the problem:
public void start(Stage primaryStage) {
root = new AnchorPane();
scene = new Scene(root,500,200);
scene.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("mouse click detected! "+event.getSource());
}
});
Button button = new Button("click here");
root.getChildren().add(button);
primaryStage.setScene(scene);
primaryStage.show();
}
If I click anywhere in empty space, the EventHandler invokes the handle() method, but if i click the button, the handle() method is not invoked. There are many buttons and other interactive elements in my application, so I need an approach to catch clicks on those elements as well without having to manually add a new handler for every single element.

You can add an event filter to the scene with addEventFilter(). This will be called before the event is consumed by any child controls. Here's what the code for the event filter looks like.
scene.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
System.out.println("mouse click detected! " + mouseEvent.getSource());
}
});

Related

JavaFX: Button event still executes without having to press the button for a second time

my problem is the following:
I have a JavaFX application with a Button called "bindB";
Button bindB = new Button("None");
bindB.setOnAction(event -> {
bindB.setText("...");
BindKey.bindKey(scene, bindB);
});
with the text "None". Is this button pressed, his text first changes to "..."
and by then calling the method "BindKey.bindKey();", the text will change to the
name of the key, the user is pressing on his keyboard.
This is the code of the method "BindKey.bindKey();":
public static void bindKey(Scene scene, Button bindB){
scene.setOnKeyPressed(event -> {
bindB.setText(String.valueOf(event.getCode()));
});
}
As you can see, in the args of the method we give the button "bindB", so that the method knows
what button to change the name of, aswell as the current scene.
This code does work, but the problem is, that even after the button was
pressed, and its text has already be changed, the text still changes to the name of different
keys if you press them afterwards WITHOUT having to press the button a second time.
I thought that you had to end the "setOnAction" event by calling
event.consume();
but that didnt work...
So how do I make the buttons text only change, if the button has actually been pressed a second or third time?
Otherwise, the task which the button performs is toggled by EVERY key because technically
every key is the toggle key as the task reads the name of the button to know what key is for toggling.
Full code example:
Main class:
public class Main {
// Initialize GUI
public static void main(String[] args) {
GUI gui = new GUI();
gui.run();
}
}
GUI class:
public class GUI extends Application {
public void run() {
launch();
}
#Override
public void start(Stage window) throws Exception {
// When closing Window
window.setOnCloseRequest(event -> {
exitApplication(window);
});
// GridPane
GridPane grid = new GridPane();
grid.setPadding(new Insets(10, 10, 10,10));
grid.setVgap(15);
grid.setHgap(30);
// Scene
Scene scene = new Scene(grid, 200, 200);
window.setScene(scene);
// Bind Button
Button bindB = new Button("None");
GridPane.setConstraints(bindB, 1, 1);
bindB.setOnAction(event -> {
bindB.setText("...");
BindKey.bindKey(scene, bindB);
});
// Add to Grid
grid.getChildren().addAll(bindB);
// Show Window
window.show();
}
// Provide a clean exit
private void exitApplication(Stage window){
window.close();
Platform.exit();
System.exit(0);
}
}
BindKey class:
public class BindKey {
// Changes Buttons text
public static void bindKey(Scene scene, Button bindB){
scene.setOnKeyPressed(event -> {
bindB.setText(String.valueOf(event.getCode()));
});
}
}
I am not to 100% sure if this is the problem, but I think the only thing you have to do is the following:
scene.setOnKeyPressed(event -> {
bindB.setText(String.valueOf(event.getCode()));
scene.setOnKeyPressed(null);
});
}
You just have to remove the Key-Listener from the scene after you set the text. Because otherwise it will listen for keys the entire time.

How to check is mouse cursor is on the button in Java FX?

I'm new to Java and I'm wondering if it is possible to check if mouse cursor is for example on the button? I mean not getting clicking events but just moving cursor on the button.
I had working code getting click and then printing something, but I want to change it a little and I can't find out why it doesn't work.
public class Main extends Application implements EventHandler<MouseEvent> {
Button button;
Stage window;
Scene scene;
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Where's the button?");
button.setText("Click me!");
button.setOnMouseMoved(this);
StackPane layout = new StackPane();
layout.getChildren().add(button);
Scene scene = new Scene(layout, 300,350);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
#Override
public void handle(MouseEvent actionEvent) {
System.out.println("You clicked the button!");
}
}
I have made small code for you. Take a look. It prints in the console "Ho-Ho-Ho-Hovereed!" once you hover over your button.
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Hover over me.'");
btn.hoverProperty().addListener((event)->System.out.println("Ho-Ho-Ho-Hovereeed!"));
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Mouse manpulation example in JavaFX!");
primaryStage.setScene(scene);
primaryStage.show();
}
i guess you can do that with event handler or css, like...
button.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
System.out.println("Cursor Over Button");
}
});
or/with styles (css)
.button:hover{
-fx-background-color: red;
}
Each Node provides a hover property to track whether the mouse cursor is hovering over it or not. By using a simple listener, you can detect when the mouse starts and stops hovering:
button.hoverProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
System.out.println("Hovering...");
} else {
System.out.println("Retreating...");
}
});
With this listener, newValue will always be true if the mouse is currently hovering over the button and change to false when the mouse leaves the area.
There is also a useful check on a Node - isHover() You can use it on MouseEvents to check if the mouse is hovering over the needed node. Such a tip ;)
button.setOnMouseEntered( (event ) -> {
button.setTranslateX(button.getTranslateX() + 20);
button.setTranslateY(button.getTranslateY() + 20);
});`
Replace button.setOnMouseMoved(this); with my code. This will do!

How to pause javafx class

I am building an alarm and it consists of two parts
an animated button created in javafx class and the engine which is created normally
what I need is whenever user press the animated button that closes the button and fire up the engine then after the engine is closed there will be some time then animated button appears again and so on
so I used ::
notify_me.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
new engine();
Platform.exit();
}
});
and in order to repeat this process I used
Timer t = new Timer(0,new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
while(true){
javafx.launch(javafx.class);
//some extra code goes here including sleep for
//some time and check for engine window state
}
}
});
t.start();
but I am facing two problems:
some extra code isn`t implemented until platform is exited,
launch() cannot be called more than once
so how can I achieve that without using threads ?? thanks
You probably won't get around using Threads. I'd recommend not shutting down the fx application thread however. Just close all windows and show (some of) them again after the delay:
#Override
public void start(Stage primaryStage) {
Button btn = new Button("Hide me 5 sec");
// prevent automatic exit of application when last window is closed
Platform.setImplicitExit(false);
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
// timer should be a daemon (-> not prevent jvm shutdown)
Timer timer = new Timer(true);
btn.setOnAction((ActionEvent event) -> {
timer.schedule(new TimerTask() {
#Override
public void run() {
// make window reappear (needs to happen on the application thread)
Platform.runLater(primaryStage::show);
}
}, 5000l);
// hide window
primaryStage.close();
});
// allow exiting the application by clicking the X
primaryStage.setOnCloseRequest(evt -> Platform.exit());
primaryStage.show();
}

JavaFX OnDragDropped Not Registering

I am trying to drag and drop labels on top of each other.
I have a list of labels called starsAndBars, and every label has this called on it as it is created:
private void giveDragAndDropProperties(Label label) {
//Enable drag actions to pick up the label
label.setOnDragDetected(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
System.out.println("Drag and drop started!");
Dragboard db = label.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
int index = starsAndBars.indexOf(label);
content.putString("test");
db.setContent(content);
event.consume();
}
});
label.setOnDragEntered(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
System.out.println("drag entered!");
}
});
label.setOnDragExited(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
System.out.println("drag left!");
}
});
//Enable a label to be dropped on this label
label.setOnDragDropped(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
System.out.println("Drop occurred!");
}
});
}
When I attempt to perform a drag and drop operation, this is what I see:
Drag and drop started!
drag entered!
drag left!
drag entered!
<I release the mouse here>
drag left!
The "onDragDropped" event never registers, as far as I can tell. What am I doing wrong? I can tell that each Label has the drag and drop event handlers since they register the "entered" and "exited" events. This is the only piece of code that deals with Drag and Drop event handlers - so it should be being consumed by something else first.
All of these labels are in an HBox that is within a ScrollPane, if that makes any difference.
Edit: I have now tried putting a drop event on the HBox the labels are contained in with the same result - it never seems to get called. My code for that setOnDragDropped was essentially identical to the one in my other example.
I have tried rewriting both my setOnDragDetected and setOnDragDropped, and I am trying to match them as closely to this example (http://docs.oracle.com/javafx/2/drag_drop/jfxpub-drag_drop.htm) as possible, which has worked for me before.
Edit 2: I have another project that currently has drag and drop events working with events very nearly identical to this, though with different objects - I have tried copying source from that project (and changing the variable names) with no luck. I can include that source as well, if it might help. But it is essentially the same.
What more can I include to help reach an answer?
You need to accept the transfer mode in setOnDragOver, like this:
label.setOnDragOver(new EventHandler <DragEvent>() {
public void handle(DragEvent event) {
event.acceptTransferModes(TransferMode.ANY);
event.consume();
}
});

ListView edit is not switching cell to input state

I have a ListView with table names in it and an add button.
tablesListView.setEditable(true);
tablesListView.setCellFactory(TextFieldListCell.forListView());
tablesListView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
addTableButton.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent evt) {
tablesListView.getItems().add("");
tablesListView.getSelectionModel().selectLast();
tablesListView.edit(tablesListView.getSelectionModel().getSelectedIndex());
}
});
So when i click on some cell in the list it switches its state to the TextField, but that's not working with the button, the cell becomes selected, but not switched. Any help?
Try
tablesListView.setEditable(true);
tablesListView.setCellFactory(TextFieldListCell.forListView());
tablesListView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
final Timeline animation = new Timeline(
new KeyFrame(Duration.seconds(.1),
new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
tablesListView.edit(tablesListView.getSelectionModel().getSelectedIndex());
}
}));
animation.setCycleCount(1);
addTableButton.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent evt) {
tablesListView.getItems().add("");
tablesListView.getSelectionModel().selectLast();
animation.play();
}
});
Actually Platform.runLater() works, but only occasionally, cause it runs the runnable in "some unspecified time in the future". So you need to specify the time when the edit command should run by for instance the timeline in my code example. The edit command postponed for 0.1 sec.

Categories

Resources