I am trying to employ dragging my stage around via dragging a certain component on my application.
It works perfectly like this:
miniTop.setOnMousePressed(e-> {
xOffset = e.getSceneX();
yOffset = e.getSceneY();
});
miniTop.setOnMouseDragged(e -> {
stage = (Stage) ((Node)e.getSource()).getScene().getWindow();
stage.setX(e.getScreenX() - xOffset);
stage.setY(e.getScreenY() - yOffset);
});
But when I do this... nononon
#FXML
public void getOffset(MouseEvent e) {
xOffset = e.getSceneX();
yOffset = e.getSceneY();
}
#FXML
public void dragStage(MouseEvent e) {
stage = (Stage) ((Node)e.getSource()).getScene().getWindow();
stage.setX(e.getScreenX() - xOffset);
stage.setY(e.getScreenY() - yOffset);
}
where these event handlers are linked to:
I've tried encasing the dragStage method with a Platform.runLater() since it's changing the UI by moving the stage but that did not help...
What's the difference between .setOnMouse and Scene Builder event handlers?
EDIT: it started working after running it again after 30 mins? what? (So hard to learn when you think you've made a mistake when you haven't haha)
There should be no difference, the documentation available for the FXML event handlers simply links to: javafx.beans.property.ObjectProperty<javafx.event.EventHandler<javafx.event.ActionEvent>> onAction
which is here
this would suggest they are implemented in the same way. can you check in your FXML file that the event handler was properly assigned to the element you were expecting?
Related
My controller class has a moveButton method that on button click moves the button to a new location. This works fine and is called by a number of buttons which do the same thing. I want to add a key listener so when a button has been clicked once, until a different button is clicked, the user can use the up arrow to move the button (ie call the same moveButton function). The below is how I have tried to implement it, I also tried putting the key listener in the initialize method but neither seem to be working. Any advice would be greatly appreciated!
public void moveButton(ActionEvent actionEvent) {
Button buttonPressed = (Button) actionEvent.getSource();
double newAnchor = getNewAnchor(AnchorPane.getBottomAnchor(buttonPressed)) // separate method that returns new anchor location
AnchorPane.setBottomAnchor(buttonPressed, newAnchor);
buttonPressed.getScene().setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if(event.getCode() == KeyCode.UP){
moveButton(actionEvent);
}
}
});
}
Don't treat the events like data that you need to pass around. Use them as triggers to do work. Generally, don't write generic event handlers that are called from multiple events and multiple nodes. Write short event handlers that just call methods to do something, and pass them the minimum from the event that they need to do the job.
If you do this, then it changes your thinking about how all of this stuff works and then it's just plain old Java, with no magic. And it's simple:
public class MoveButton extends Application {
private Node activeButton;
private Pane pane;
#Override
public void start(Stage primaryStage) throws Exception {
pane = new Pane();
Scene scene = new Scene(pane, 1200, 800);
primaryStage.setScene(scene);
primaryStage.show();
Button button1 = new Button("Button 1");
Button button2 = new Button("Button 2");
button2.setTranslateX(80);
button1.setOnAction(evt -> buttonClick(button1));
button2.setOnAction(evt -> buttonClick(button2));
pane.getChildren().addAll(button1, button2);
pane.setOnKeyPressed(evt -> moveButton(evt.getCode()));
}
private void moveButton(KeyCode keyCode) {
switch (keyCode) {
case UP -> activeButton.setTranslateY(activeButton.getTranslateY() - 30);
case RIGHT -> activeButton.setTranslateX(activeButton.getTranslateX() + 30);
case DOWN -> activeButton.setTranslateY(activeButton.getTranslateY() + 30);
case LEFT -> activeButton.setTranslateX(activeButton.getTranslateX() - 30);
}
}
private void buttonClick(Node button) {
activeButton = button;
pane.requestFocus();
}
}
When I try to drag a popupwindow an exception appears:
java.lang.IllegalArgumentException: argument type mismatch
I designed the popwindow in SceneBuilder and added two methods for the anchorpane:
setOnMousePressed -> returns the position of the popupwindow
setOnMouseDragged ->returns the exception.
The first method works fine and the second not.
As I can't find a solution although the topic has been covered, I would be very thankful for some help.
Code of the poupwindow:
private static double xOffset = 0;
private static double yOffset = 0;
#FXML
void setOnMouseDragged(MouseDragEvent event) {
Stage window=(Stage)((Node) event.getSource()).getScene().getWindow();
window.setX(event.getScreenX() + xOffset);
window.setY(event.getScreenY() + yOffset);
}
#FXML
void setOnMousePressed(MouseEvent event) {
Stage window=(Stage)((Node) event.getSource()).getScene().getWindow();
xOffset = window.getX() - event.getScreenX();
yOffset = window.getY() - event.getScreenY();
System.out.println("setOnMouseDraggedx:"+xOffset+" yOffset:"+yOffset);
//this method works and prints out x:-419.0 yOffset:-31.0
}
The EventHandler of the Node#onMouseDragged property handles MouseEvents, not MouseDragEvents. The former cannot be cast to the latter.
onMouseDragged
public final ObjectProperty<EventHandler<? super MouseEvent>> onMouseDraggedProperty
Defines a function to be called when a mouse button is pressed on this Node and then dragged.
Using:
#FXML
void setOnMouseDragged(MouseEvent event) { /* code */ }
Should solve the IllegalArgumentException.
Note that setOnMouseDragged and setOnMousePressed are odd names for methods that don't set the event handlers, but instead actually handle their respective events.
I have a controller that opens a new stage as a popup:
#FXML
private void onClickPayments(ActionEvent event) throws IOException{
FXMLLoader loader = new FXMLLoader(getClass().getResource("ClientPayments.fxml"));
Parent root = (Parent) loader.load();
ClientPaymentsController controller = (ClientPaymentsController) loader.getController();
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setScene(scene);
stage.initModality(Modality.APPLICATION_MODAL);
stage.setResizable(false);
stage.setOnCloseRequest((WindowEvent we) -> {
clientBLL.retrieve(clientID);
updateWarning();
});
stage.show();
controller.setClientID(clientID);
}
When the other stage is closed by the 'x' button the 'stage.setOnCloseRequest' is executed. But in that stage I have a button to close:
#FXML
private void onClickExit(ActionEvent event){
((Stage) ((Node) event.getSource()).getScene().getWindow()).close();
}
It closes, but the method 'stage.setOnCloseRequest' isn't being executed.
Is this method wrong or is there a way to close a popup in a way that acts exactly like the 'x' button in the window?
The setOnCloseRequest handler is executed when there is an external request to close the window (i.e. not one from your code). See the documentation.
If it's enough to execute that logic immediately after the window is closed, just use the regular onHidden handler instead:
stage.setOnHidden((WindowEvent we) -> {
clientBLL.retrieve(clientID);
updateWarning();
});
If you really need to use the onCloseRequest handler (e.g. because you might want to veto the close), just move your close logic to a method and invoke it from both types of request to close the window.
public void doClose() {
clientBLL.retrieve(clientID);
updateWarning();
}
#FXML
private void onClickExit(ActionEvent event){
doClose();
((Node) event.getSource()).getScene().getWindow().hide();
}
and
stage.setOnCloseRequest((WindowEvent we) -> {
controller.doClose();
});
I have a little issue close a secondary stage after clicking the close at the top right corner.
I'm using fxml with controller class, i need a way to handle this situation.
Here is what i do but i get a nullpointer exception :
#Override
public void initialize(URL location, ResourceBundle resources) {
Stage stage = (Stage) tbTabPaneHome.getScene().getWindow();
stage.setOnCloseRequest(e -> {
Platform.exit();
System.exit(0);
});
}
Because the stage not yet intialized completly, so any other ideas ?
Since the Scene nor the Stage are created yet, you can't call them or you get a NPE, as you already mentioned.
One way to install the event handler on the stage will be listening to changes in the sceneProperty() of tbTabPaneHome.
Once the node is added to the scene, that property will give you the Scene instance.
But the scene is not added to the Stage yet, so you need to wait till this is done, with Platform.runLater():
public void initialize() {
tbTabPaneHome.sceneProperty().addListener((obs, oldScene, newScene) -> {
Platform.runLater(() -> {
Stage stage = (Stage) newScene.getWindow();
stage.setOnCloseRequest(e -> {
Platform.exit();
System.exit(0);
});
});
});
}
Did you try to deal with your secondary stage entirely in the main stage controller?
I want to hide or show a help windows from a button or help menu in my main application controller. Something like the following:
public Button helpBtn;
Stage anotherStage = new Stage();
boolean secondaryInitialyzed = false;
boolean secondaryShowing = false;
public void showOrHideHelp(ActionEvent actionEvent) throws IOException {
if (!secondaryInitialyzed){
Parent anotherRoot = FXMLLoader.load(getClass().getResource("mySecondaryStage.fxml"));
anotherStage.setTitle("Secondary stage");
Scene anotherScene = new Scene(anotherRoot, 500, 350);
anotherStage.setScene(anotherScene);
secondaryInitialyzed = true;
}
if (secondaryShowing){
anotherStage.hide();
secondaryShowing = false;
helpBtn.setText("Show Help");
}
else {
anotherStage.show();
secondaryShowing = true;
helpBtn.setText("Hide Help");
}
It does work, and there might be a way for you to handle your setOnCloseRequest within the main controller.
I have the opposing issue, i.e preventing closing the secondary stage window by clicking the close at the top right corner. I'll look into setOnCloseRequest and see if there is a way there.
I also have an other unrelated problem: can I position the secondary in reference to the primary one?
A few days ago I started studying JavaFX, and came across the desire to perform 2 experiments. Firstly, I would like to know if it is possible to put an animated background behind an user interface. I've succeeded in creating an animated background, and now I'm having great difficulties to position some controls in the middle of my interface.
I'd like to introduce you 2 pictures of my program. The first demonstrates the undesirable result that I'm getting:
I believe this is my nodes tree:
This is the code of my application:
public class AnimatedBackground extends Application
{
// #########################################################################################################
// MAIN
// #########################################################################################################
public static void main(String[] args)
{
Application.launch(args);
}
// #########################################################################################################
// INSTÂNCIAS
// #########################################################################################################
private Group root;
private Group grp_hexagons;
private Rectangle rect_background;
private Scene cenario;
// UI
private VBox lay_box_controls;
private Label lab_test;
private TextArea texA_test;
private Button bot_test;
// #########################################################################################################
// INÍCIO FX
// #########################################################################################################
#Override public void start(Stage stage) throws Exception
{
this.confFX();
cenario = new Scene(this.root , 640 , 480);
this.rect_background.widthProperty().bind(this.cenario.widthProperty());
this.rect_background.heightProperty().bind(this.cenario.heightProperty());
stage.setScene(cenario);
stage.setTitle("Meu programa JavaFX - R.D.S.");
stage.show();
}
protected void confFX()
{
this.root = new Group();
this.grp_hexagons = new Group();
// Initiate the circles and all animation stuff.
for(int cont = 0 ; cont < 15 ; cont++)
{
Circle circle = new Circle();
circle.setFill(Color.WHITE);
circle.setEffect(new GaussianBlur(Math.random() * 8 + 2));
circle.setOpacity(Math.random());
circle.setRadius(20);
this.grp_hexagons.getChildren().add(circle);
double randScale = (Math.random() * 4) + 1;
KeyValue kValueX = new KeyValue(circle.scaleXProperty() , randScale);
KeyValue kValueY = new KeyValue(circle.scaleYProperty() , randScale);
KeyFrame kFrame = new KeyFrame(Duration.millis(5000 + (Math.random() * 5000)) , kValueX , kValueY);
Timeline linhaT = new Timeline();
linhaT.getKeyFrames().add(kFrame);
linhaT.setAutoReverse(true);
linhaT.setCycleCount(Animation.INDEFINITE);
linhaT.play();
}
this.rect_background = new Rectangle();
this.root.getChildren().add(this.rect_background);
this.root.getChildren().add(this.grp_hexagons);
// UI
this.lay_box_controls = new VBox();
this.lay_box_controls.setSpacing(20);
this.lay_box_controls.setAlignment(Pos.CENTER);
this.bot_test = new Button("CHANGE POSITIONS");
this.bot_test.setAlignment(Pos.CENTER);
this.bot_test.setOnAction(new EventHandler<ActionEvent>()
{
#Override public void handle(ActionEvent e)
{
for(Node hexagono : grp_hexagons.getChildren())
{
hexagono.setTranslateX(Math.random() * cenario.getWidth());
hexagono.setTranslateY(Math.random() * cenario.getHeight());
}
}
});
this.texA_test = new TextArea();
this.texA_test.setText("This is just a test.");
this.lab_test = new Label("This is just a label.");
this.lab_test.setTextFill(Color.WHITE);
this.lab_test.setFont(new Font(32));
this.lay_box_controls.getChildren().add(this.lab_test);
this.lay_box_controls.getChildren().add(this.texA_test);
this.lay_box_controls.getChildren().add(this.bot_test);
this.root.getChildren().add(this.lay_box_controls);
}
}
I've tried to make the use of a StackPane as the root of my scene graph, but also found an undesired result. Despite the controls have stayed in the center of the window, the circles begin to move in as they grow and shrink, making it appear that everything is weird.
The second thing I would like to know is if it is possible to customize the controls so they perform some animation when some event happens. Although we can change the appearance of controls using CSS, it's harder to create something complex. For example, when a control changes its appearance due to a change of state, the transition state change is not made in an animated way, but in an abrupt and static way. Is there a way to animate, for example, a button between its states? This would be done using the JavaFX API? Or would that be using CSS? Or would not be possible in any way?
Thank you for your attention.
after much struggle, I and some users of the Oracle community could resolve this issue. I see no need to repeat here all the resolution made by us, so I'll post the link so you can access the solution of the problem. I hope this benefits us all. Thanks for your attention anyway.
https://community.oracle.com/thread/2620500