I was assigned in school to build a small JAVAFX program with buttons that travels through different Scenes (layouts).
My program is a log in window, and details window. But I have to create the details window in a different class than Main class. How do I make the button call the 'details window'from Main when it's a class?
(See createAccountBtn.setOnAction)
Main Class
Detail window Class
So Here's the deal: I created an interface where I put this method:
void displayLayout(VBox layout);
And then It triggers this method in Main:
public void displayLayout(VBox layout){
Scene scene = new Scene(layout, 200, 200);
window.setScene(scene);
I also have in Main this:
Button createAccountBtn = new Button("Create new account");
createAccountBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
displayLayout(new DetailScreen(listener));
}
});
layout = new VBox();
layout.getChildren().addAll(accountNumberTF, loginBtn, createAccountBtn);
layout.setAlignment(Pos.CENTER);
displayLayout(layout);
window.show();
Which is the layout options and the button. the setOnAction button points to a new Class that inherits VBox and acts as a layout:
private ChangeScreenListener listener;
public DetailScreen(ChangeScreenListener listener) {
this.listener = listener;
Button exitBtn = new Button("EXIT");
TextField input = new TextField();
VBox layout = new VBox();
layout.getChildren().addAll(input, exitBtn);
However after I click the button, the window becomes empty and does not change layout.
Ideas?
public static variables can pass variables between classes. This is the easiest way i could think off to pull off the results you desire.
In the main class define the following variables:
public static Stage stage;
public static Scene sceneMain;
Assuming you have already set a scene and using a Stage in your main constructor write the following code after setting your Stage and Scene.
stage = primaryStage;
sceneMain = scene;
When you have done this you can easily call on to the stage in another class by and going back to your main scene like this: (this is what you can put under the event of your buttonclick())
Main.stage.setScene(sceneMain);
Or you can set another scene by simply putting the scene you want in the .setscene();
Related
I can't figure out how to create a modal window in JavaFX. Basically I have file chooser and I want to ask the user a question when they select a file. I need this information in order to parse the file, so the execution needs to wait for the answer.
I've seen this question but I've not been able to find out how to implement this behavior.
In my opinion this is not good solution, because parent window is all time active.
For example if You want open window as modal after click button...
private void clickShow(ActionEvent event) {
Stage stage = new Stage();
Parent root = FXMLLoader.load(
YourClassController.class.getResource("YourClass.fxml"));
stage.setScene(new Scene(root));
stage.setTitle("My modal window");
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(
((Node)event.getSource()).getScene().getWindow() );
stage.show();
}
Now Your new window is REALY modal - parent is block.
also You can use
Modality.APPLICATION_MODAL
Here is link to a solution I created earlier for modal dialogs in JavaFX 2.1
The solution creates a modal stage on top of the current stage and takes action on the dialog results via event handlers for the dialog controls.
JavaFX 8+
The prior linked solution uses a dated event handler approach to take action after a dialog was dismissed. That approach was valid for pre-JavaFX 2.2 implementations. For JavaFX 8+ there is no need for event handers, instead, use the new Stage showAndWait() method. For example:
Stage dialog = new Stage();
// populate dialog with controls.
...
dialog.initOwner(parentStage);
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.showAndWait();
// process result of dialog operation.
...
Note that, in order for things to work as expected, it is important to initialize the owner of the Stage and to initialize the modality of the Stage to either WINDOW_MODAL or APPLICATION_MODAL.
There are some high quality standard UI dialogs in JavaFX 8 and ControlsFX, if they fit your requirements, I advise using those rather than developing your own. Those in-built JavaFX Dialog and Alert classes also have initOwner and initModality and showAndWait methods, so that you can set the modality for them as you wish (note that, by default, the in-built dialogs are application modal).
You can create application like my sample. This is only single file JavaFX application.
public class JavaFXApplication1 extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Stage stage;
stage = new Stage();
final SwingNode swingNode = new SwingNode();
createSwingContent(swingNode);
StackPane pane = new StackPane();
pane.getChildren().add(swingNode);
stage.initModality(Modality.APPLICATION_MODAL);
stage.setTitle("Swing in JavaFX");
stage.setScene(new Scene(pane, 250, 150));
stage.show();
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
private void createSwingContent(final SwingNode swingNode) {
SwingUtilities.invokeLater(() -> {
try {
Path currentRelativePath = Paths.get("");
String s = currentRelativePath.toAbsolutePath().toString();
JasperDesign jasperDesign = JRXmlLoader.load(s + "/src/reports/report1.jrxml");
String query = "SELECT * FROM `accounttype`";
JRDesignQuery jrquery = new JRDesignQuery();
jrquery.setText(query);
jasperDesign.setQuery(jrquery);
JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
JasperPrint JasperPrint = JasperFillManager.fillReport(jasperReport, null, c);
//JRViewer viewer = new JRViewer(JasperPrint);
swingNode.setContent(new JRViewer(JasperPrint));
} catch (JRException ex) {
Logger.getLogger(AccountTypeController.class.getName()).log(Level.SEVERE, null, ex);
}
});
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
I tried a lot but just couldn't find any solution. At the moment the opened window(popup window) is always on top but the user can still access the main window. That's how it should be, but it shouldn't be possible to open the same popup window again.
Stage stage = new Stage();
stage.setTitle(panelTitle);
stage.setScene(new Scene(root));
stage.initModality(Modality.WINDOW_MODAL);
stage.setAlwaysOnTop(true);
stage.showAndWait();
Thank you in advance!
As LazerBanana said, I would disable the button that opens the window, and I would enable it when you close it.
Stage stage = new Stage();
button.setDisable(true);
stage.setTitle(panelTitle);
stage.setScene(new Scene(root));
stage.initModality(Modality.WINDOW_MODAL);
stage.setAlwaysOnTop(true);
stage.showAndWait();
// your logic here
button.setDisable(false);
An alternative solution to creating a new one each time is to create one and just setup and show.
public class Stack extends Application {
private final Stage popup = new Stage();
#Override
public void start(Stage stage) throws Exception {
BorderPane root = new BorderPane();
root.setPrefWidth(400);
root.setPrefHeight(200);
Button button = new Button("ClickMePopup");
root.setCenter(button);
button.setOnAction(
event -> {
if (!popup.isShowing()) {
// you dont set modality because after the stage is set to visible second time it will throw an exception.
// Again depends on what you need.
// popup.initModality(Modality.WINDOW_MODAL);
// this focuses the popup and main window is not clickable
// popup.initOwner(stage);
VBox dialogVbox = new VBox(20);
dialogVbox.getChildren().add(new Text("Some Dialog"));
Scene dialogScene = new Scene(dialogVbox, 300, 200);
popup.setScene(dialogScene);
// you can actually put all above into the method called initPopup() or whatever, do it once, and just show it here or just bind the property to the button.
popup.show();
}
});
Scene scene = new Scene(root);
stage.setTitle("Stack");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Or disable the button when clicked, but if your popup is not driven by the button or can be opened from other places the first idea would be a bit better in my opinion. Depends on what you need.
Or just create your own class and Springify it.
My title is badly worded because my problem is very hard to describe, so I drew an image for it:
I have an ImageView object which represents a pile of cards (not poker cards but just used them as an example). When this image view is clicked, I need a window to popup that features a ScrollPane and shows them all the card objects that are in the linked list. When the user clicks anywhere outside of the window (and later on, any right mouse button click) the scrollpane window needs to close.
Ways that I have already tried:
Scene with APPLICATION_MODAL. Then did Scene.showAndWait(). I didn't like this method because it made another application on the user's taskbar. It also felt clunky and just bad.
Changed my root pane to a StackPane, then added this Scrollpane to the stackpane when the user clicked on the deck. This for some reason was really slow and seemed really obtrusive. It was also annoying because my alternate class needed to have access to the root pane (since when it closes, it needs to go to the root StackPane and call .remove() on itself).
Are there any other better ways to accomplish this? My application is going to have many of these piles and so this framework needs to work very well.
I would still propose to open a new Stage with some restrictions to solve your issues with this approach.
You can use the initOwner property of the Stage to have another Stage as owner, therefore no other icon will appear on the taskbar.
You can use the initStyle property with TRANSPARENT or UNDECORATED StageStlye, this will ensure that only the content is visible.
And in the end you can use the focusedProperty to check whether the Stage lost focus to close it.
Example
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
Button b = new Button("Open deck");
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Stage popupStage = new Stage();
popupStage.initOwner(primaryStage);
popupStage.initStyle(StageStyle.UNDECORATED);
Scene sc = new Scene(new ScrollPane(), 300, 300);
popupStage.setScene(sc);
popupStage.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue,
Boolean newValue) {
if(!newValue)
popupStage.close();
}
});
popupStage.show();
}
});
root.setCenter(b);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
It is also possible of course to not open it in a new Stage, but draw a ScrollPane inside the current Stage which overlaps the content of the Stage using for example an AnchorPane or Group as root, but the first solution has the advantage that you are not bound to the dimensions of main Stage (the popup can have any size that you want).
You can achieve this with a low level system hook that catches the mouse events.
http://kra.lc/blog/2016/02/java-global-system-hook/ or https://github.com/kwhat/jnativehook/releases
I hope that is what you needed, otherwise i got your question wrong.
I've been struggling for weeks to resolve the memory leaks within our JavaFX application, and thought today I was at a complete loss with it so decided to write the simplest application application I could think of to prove JavaFX could in fact release memory and therefore proving I was doing something wrong. To my surprise this seems to leak memory too.
Can anyone advise why the following application still has a javafx.scene.control.Label in the heap after the button is clicked? The way I checked it was there was with jprofiler.
public class MemoryTestApplication extends Application {
#Override
public void start(Stage primaryStage) {
//root pane
final VBox root = new VBox();
//label to remove when button is clicked
final Label label = new Label("Remove Me");
root.getChildren().add(label);
//button to remove label
Button btn = new Button("Remove label");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
root.getChildren().remove(label);
}
});
root.getChildren().add(btn);
//create scene and stage
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Your anonymous inner class for the event handler is holding a reference to the label, and your button is holding a reference to the event handler.
The anonymous inner class is going to have two synthetically generated fields, created by the compiler, like:
final Label val$label;
final MemoryTestApplication this$0;
Since these are final, you can't clear label after using it by label = null;, so I think your best bet is to change the event handler to a normal, named class, pass the label reference in to its constructor, and then clear it after using it to remove the label from the layout. (You would want to test that it was not null before removing, so that it would only be removed the first time the button is pressed).
I am new to JavaFX so please don't hesitate to explain basic concepts. I am able to create a simple GUI and I have a TabPane with two tabs in my program. My question is how do I create a class that can be used as a node?
For example, tab.setContent(stuff) would add stuff to a tab, tab assuming of course that stuff is a Node. So let's say I create a class called Clock and want to add an instance of it to tab, how do I do such a thing?
The clock should be a graphical object. I'm new to graphical programming so references to Swing etc. won't be particularly helpful.
In your case, create a class "Clock" and extend it with a layout you wish it to contain. For example:
public class Clock extends BorderPane{}
Then, you can set properties or other Nodes to it by using a constructor.
public class Clock extends BorderPane{
TextArea ta = new TextArea("This is TOP");
this.setTop(ta);
Button b1 = new Button("This is button 1");
Button b2 = new Button("This is button 2");
HBox box = new HBox();
box.getChildren().addAll(b1,b2);
this.setCenter(box);
}
Then, you would call it from your main program as such:
#Override
public void start(Stage primaryStage){
primaryStage.setScene(new Scene(new Clock()));
primaryStage.show();
}
In your case, you would set the Scene when the tab is pressed. That can be done using the Tab class and adding an actionListener to it.
tab.setContent(new Clock());
Hope this helped.
Please add a method in clock, then also make a constructor that calls this method. put all
TextArea ta = new TextArea("This is TOP");
this.setTop(ta);
Button b1 = new Button("This is button 1");
Button b2 = new Button("This is button 2");
HBox box = new HBox();
box.getChildren().addAll(b1,b2);
this.setCenter(box);
inside that methode.