Closing a Stage from within its controller - user-interface

I need a way to close a Stage from within itself by clicking a Button.
I have a main class from which I create the main stage with a scene. I use FXML for that.
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Builder.fxml"));
stage.setTitle("Ring of Power - Builder");
stage.setScene(new Scene(root));
stage.setMinHeight(600.0);
stage.setMinWidth(800.0);
stage.setHeight(600);
stage.setWidth(800);
stage.centerOnScreen();
stage.show();
}
Now in the main window that appears I have all the control items and menus and stuff, made through FXML and appropriate control class. That's the part where I decided to include the About info in the Help menu. So I have an event going on when the menu Help - About is activated, like this:
#FXML
private void menuHelpAbout(ActionEvent event) throws IOException{
Parent root2 = FXMLLoader.load(getClass().getResource("AboutBox.fxml"));
Stage aboutBox=new Stage();
aboutBox.setScene(new Scene(root2));
aboutBox.centerOnScreen();
aboutBox.setTitle("About Box");
aboutBox.setResizable(false);
aboutBox.initModality(Modality.APPLICATION_MODAL);
aboutBox.show();
}
As seen the About Box window is created via FXML with a controller. I want to add a Button to close the new stage from within the controller.
The only way I found myself to be able to do this, was to define a
public static Stage aboutBox;
inside the Builder.java class and reference to that one from within the AboutBox.java in method that handles the action event on the closing button. But somehow it doesn't feel exactly clean and right. Is there any better way?

You can derive the stage to be closed from the event passed to the event handler.
new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
// take some action
...
// close the dialog.
Node source = (Node) actionEvent.getSource();
Stage stage = (Stage) source.getScene().getWindow();
stage.close();
}
}

In JavaFX 2.1, you have few choices. The way like in jewelsea's answer or the way what you have done already or modified version of it like
public class AboutBox extends Stage {
public AboutBox() throws Exception {
initModality(Modality.APPLICATION_MODAL);
Button btn = new Button("Close");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
close();
}
});
// Load content via
// EITHER
Parent root = FXMLLoader.load(getClass().getResource("AboutPage.fxml"));
setScene(new Scene(VBoxBuilder.create().children(root, btn).build()));
// OR
Scene aboutScene = new Scene(VBoxBuilder.create().children(new Text("About me"), btn).alignment(Pos.CENTER).padding(new Insets(10)).build());
setScene(aboutScene);
// If your about page is not so complex. no need FXML so its Controller class too.
}
}
with usage like
new AboutBox().show();
in menu item action event handler.

Related

JavaFX: How to remove Pane?

I wasn't really sure how to title this. I have a JavaFX application in which I have two pages (fxml's) which are different sizes. The first is 400x600; the second is maximized. I have a return button which sends the user back to the first fxml. I successfully set it so it goes back to the original size. However, when I go 1->2->1->2 the screen does not maximize. What I imagine is the issue is that it does not rerun the initialize() method the second time it creates this page. Another possibility is that it is caused by the new Runnable() I made, which was necessary to get the stage object.
The second controller:
#Override
public void initialize(URL location, ResourceBundle resources) {
Platform.runLater(new Runnable() {
public void run() {
// Display
stage = (Stage) menuPane.getScene().getWindow();
stage.setResizable(true);
stage.setMaximized(true);
stage.setResizable(false);
}
});
}
Thanks.

How do I display two javafx GUI screens in the same program

My question is how to display more than one user interface screen per program. I'm sure this question has been asked before, but I haven't found a solution that works for me (or should I say that I understand). There isn't anything exotic about the scenarios I'm talking about. The first is simply validating inputs from a screen and re-displaying the same screen in the case of errors.
I'll pose the question in terms of the second more complicated scenario: displaying an input data screen, processing the inputs; and then displaying the outputs. This complicated somewhat by the fact that the first, a simple screen with 5 text boxes and a command button, uses an FXML file, whereas the second, a multi-select list box does not. The flow is:
1. Main program calls
2. A loader program which loads the FXML and somehow or another calls
3. A controller which receives the inputs and processes them to produce output.
The final step is to display the output in the form of a multi-select list box. Note that the first GUI employs a controller, which is a separate file, to process the inputs, whereas the second uses an event handler, which is in the same file as the screen definition, to make the selection(s) when the user clicks a command button.
Various SO posts have said that the way to go is to not shut down the application once the first GUI has completed via but Keep the JavaFX run time going in the background with
Platform.setImplicitExit(false);
and to define each GUI and simply switch scenes to the one you want to display. But where, given the scenario I described do you put the code? The second GUI has three pieces: screen definition, event handler(s), and scene switching code. Where do you put each? #2 or #3. If you put some in #2 and some in #3, how does #3 know what you did in #2?
The code for #2 the FMXL loader:
public class inputData extends Application {
public static void load() {
launch();
}
#Override
public void start(Stage stage) throws Exception {
GridPane inpRoot = FXMLLoader.load(getClass().getResource("inputData.fxml"));
Scene inpScene = new Scene(inpRoot, 300, 275);
stage.setTitle("Amsnag 2.1 - Query Input");
stage.setScene(inpScene);
stage.show();
}
}
Code for #3, the list box definition and handlers, which worked fine running separately. It's only when I tried to incorporate it with the rest of the program that it failed.
public class multiList extends Application {
public static void load() {
launch();
}
public static final ObservableList options = FXCollections.observableArrayList();
#Override
public void start(final Stage stage) {
final ListView<String> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// load list from DB
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"select distinct connDesc,accom from option order by connDEsc,accom");
while (rs.next()) {
String opt = rs.getString("connDesc") + ": " + rs.getString("accom");
listView.getItems().add(opt);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
// button to display fares
final Button displayButton = new Button("Display Fares");
// handle button click
displayButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
Platform.exit(); // close list box
ObservableList selectedIndices = listView.getSelectionModel().getSelectedItems();
// lcreate temp table with selected options
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"create temporary table selected (connDesc varchar(200),accom varchar(50))");
for(Object o : selectedIndices){
String option = o.toString();
// extract connDesc+accom from displayed option
msg.g(option);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
}
} ); // end of display handler
// quit button
final Button resetButton = new Button("Quit");
resetButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.exit();
}
});
final HBox controls = new HBox(10);
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(displayButton, resetButton);
final VBox layout = new VBox(10);
layout.setAlignment(Pos.CENTER);
layout.setStyle("-fx-padding: 10; -fx-background-color: cornsilk;");
layout.getChildren().setAll(listView, controls);
layout.setPrefWidth(320);``enter code here
Scene scene = new Scene(layout);
// stage.setScene(new Scene(layout));
stage.setScene(scene);
stage.setTitle("Select one or more options");
stage.show();
}
public static void main(String[] args) { launch(args); }
}
You can't reuse an Application subclass in a different application.
The Application class represents an entire application, or perhaps more specifically its lifecycle. So it has methods such as init(), start(), and stop() which are invoked by the FX Application Toolkit at the appropriate moments in the lifecycle of the application.
The layout for your multiList (aside: please use proper naming conventions) class is performed in the start() method, so it can only happen at the start of the application. By putting the layout code here, you make it impossible to reuse so that it is performed at a later point in a different application.
So move the layout for MultiList to a separate class:
public class MultiList {
public static final ObservableList options = FXCollections.observableArrayList();
private final VBox view ;
public MultiList() {
final ListView<String> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// load list from DB
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"select distinct connDesc,accom from option order by connDEsc,accom");
while (rs.next()) {
String opt = rs.getString("connDesc") + ": " + rs.getString("accom");
listView.getItems().add(opt);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
// button to display fares
final Button displayButton = new Button("Display Fares");
// handle button click
displayButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
Platform.exit(); // close list box
ObservableList selectedIndices = listView.getSelectionModel().getSelectedItems();
// create temp table with selected options
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"create temporary table selected (connDesc varchar(200),accom varchar(50))");
for(Object o : selectedIndices){
String option = o.toString();
// extract connDesc+accom from displayed option
msg.g(option);
}
conn.close();
} catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
}
}); // end of display handler
// quit button
final Button resetButton = new Button("Quit");
resetButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.exit();
}
});
final HBox controls = new HBox(10);
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(displayButton, resetButton);
view = new VBox(10);
view.setAlignment(Pos.CENTER);
view.setStyle("-fx-padding: 10; -fx-background-color: cornsilk;");
view.getChildren().setAll(listView, controls);
view.setPrefWidth(320);
}
public Parent getView() {
return view ;
}
}
Now if you want to test this out on its own, you can write an application for it:
public class MultiListApp extends Application {
#Override
public void start(Stage primaryStage) {
MultiList multiList = new MultiList() ;
Scene scene = new Scene(multiList.getView());
primaryStage.setScene(scene);
primarStage.setTitle("Select one or more options");
primaryStage.show();
}
}
Or in the controller class for InputData.fxml, you can do the same thing:
public class InputDataController {
#FXML
private void someEventHandler() {
MultiList multiList = new MultiList() ;
Scene scene = new Scene(multiList.getView());
Stage stage = new Stage();
stage.setScene(scene);
stage.setTitle("Select one or more options");
stage.show();
}
}

How to enable and disable a progress indicator when using JMS in javafx8

on action i send a message to a jms topic to process data and i have a call back method which gets called when the data is ready and loads a TableView.
public void onEnter(ActionEvent actionEvent) throws IOException, InterruptedException {
new Thread() {
public void run() {
Platform.runLater(() -> {
progressIndicator.setVisible(true);
scrollPane.setDisable(true);
});
// Construct the message and publish it to a topic
};
}.start();
}
}
public void callBackMethod(List<Object> list ) {
progressIndicator.setVisible(false);
scrollPane.setDisable(false);
//load data in the table
}
This does what i want, but what if something goes wrong at the messaging system end , the call back never gets called and the UI component will be disabled forever .
Any suggestions to improve this will be helpful.
Presumably, the messaging system is going to throw some kind of exception if it fails to send the message, so you'll need a way to catch that and recover properly. If you use the JavaFX "Task" class, then you'll get events when that happens. You'll still have to deal with a failure at the receiving end, or implement some kind of a time-out, if that's appropriate.
Also, you're starting up a thread and then immediately tossing a job onto the FXAT with RunLater. The onEnter event handler, by definition, is already running on the FXAT, so you can just do your GUI stuff before you start up the thread (or Task, as I suggest). Here's a sample that shows how to launch the Task, and clean up if it fails with an exception:
public class SampleTask extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
BorderPane root = new BorderPane();
ProgressIndicator progressIndicator = new ProgressIndicator(0);
ScrollPane scrollPane = new ScrollPane();
Button button = new Button("Start");
root.setTop(progressIndicator);
root.setCenter(scrollPane);
progressIndicator.setVisible(false);
root.setBottom(button);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
button.setOnAction(actionEvent -> {
progressIndicator.setVisible(true);
scrollPane.setDisable(true);
Task<Void> testTask = new Task<Void>() {
#Override
protected Void call() throws Exception {
// Send the message
return null;
}
};
testTask.setOnFailed(event -> {
progressIndicator.setVisible(false);
scrollPane.setDisable(false);
});
new Thread(testTask).start();
});
}
}

Slow loading of layout

I have a super class which is in a library. This library take care of initializing some basic layout components and other stuff. My problem is that it takes 1.x seconds to load the layout, and shows the default layout for a while, before setting the child-specified layout.
This is the method of my super class:
public void InitializeWindow(Activity act, int layoutResourceId, String windowTitle,
Object menuAdapter, int slideMenuMode) {
super.setContentView(layoutResourceId);
super.setBehindContentView(R.layout.menu_frame);
this.menuAdapter = menuAdapter;
this.slideMenuMode = slideMenuMode;
setWindowTitle(windowTitle);
initializeSlidingMenu();
}
This is called this way:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.InitializeWindow(this, R.layout.activity_home, "\t\tHome",
new MenuAdapter(this, R.menu.slide_menu), SlidingMenu.TOUCHMODE_FULLSCREEN);
}
The application works like a charm, but it takes, as I said around 1.x seconds to load the layout passed from the child-class. Why does this happen?
By request, this is my initializeSlideMenu() method:
public void initializeSlidingMenu() {
this.setSlidingActionBarEnabled(true);
getSlidingMenu().setBehindOffsetRes(R.dimen.actionbar_home_width);
getSlidingMenu().setShadowWidthRes(R.dimen.shadow_width);
getSlidingMenu().setShadowDrawable(R.drawable.shadow);
getSlidingMenu().setTouchModeAbove(slideMenuMode);
getSlidingMenu().setBehindScrollScale(0.25f);
ListView v = new ListView(this);
v.setBackgroundColor(Color.parseColor("#000000"));
v.setAdapter((ListAdapter) menuAdapter);
getSlidingMenu().setMenu(v);
}
To avoid such problems there are three ways in general.
Let your onCreate() finish after setContentView() call as early as possible. You can use postDelayed runnable to delay few initialization which may not be needed at early stages.
Do some task when the view is ready, it causes the Runnable to be added to the message queue of that view.
Snippet
view.post(new Runnable() {
#Override
public void run() {
}
});
If none of the above helps consider "Optimize with stubs" link : http://android-developers.blogspot.in/2009/03/android-layout-tricks-3-optimize-with.html
Hope it helps.
I suspect that the trouble spot for you is with:
v.setAdapter((ListAdapter) menuAdapter);
You should do this as part of an AsyncTask. It will often be very slow to execute the loading by the adapter.
Here is a snippet from a sample AsyncTask implementation:
//before starting the load, I pop up some indicators that I'm doing some loading
progressBar.setVisibility(View.VISIBLE);
loadingText.setVisibility(View.INVISIBLE);
AsyncTask<Void, Void, Void> loadingTask = new AsyncTask<Void, Void, Void>() {
private ArrayList<Thing> thingArray;
#Override
protected Void doInBackground(Void... params) {
//this is a slow sql fetch and calculate for me
thingArray = MyUtility.fetchThings(inputValue);
return null;
}
#Override
public void onPostExecute(Void arg0) {
EfficientAdapter myAdapter = new EfficientAdapter(MyActivity.this, thingArray);
listView.setAdapter(myAdapter);
//after setting up my adapter, I turn off my loading indicators
progressBar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
RelativeLayout layout = (RelativeLayout)MyActivity.this.findViewById(R.id.spacey);
if (layout != null) {
LayoutInflater inflater = LayoutInflater.from(MyActivity.this);
View view = inflater.inflate(R.layout.name_tabled_sub, layout);
NamedTableView tableView = new NamedTableView(MyActivity.this, view);
}
progressBar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
}
};
loadingTask.execute();
You can also do "PreExecute" items with the Async task, as well as update.

JavaFX 2.0 subwindow

How can I display a new window in JavaFX 2.0? For example after button click action.
I want both windows (the main window and the new window) to communicate each other.
Thx for help.
new Stage(new Scene(new Group(new Text(10,10, "my second window")))).show();
Communicating between two windows is similar as for any two objects in Java.
You create new windows by calling new Stage() and show them by stage.show().
Here is an example of creating a new Stage with a checkbox control which modifies text of a label displayed in a different Stage.
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import javafx.stage.*;
public class SecondStage extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage primaryStage) {
// setup some dymamic data to display.
final String STANDARD_TEXT = "Every Good Boy Deserves Fruit";
final String ALTERNATE_TEXT = "Good Boys Deserve Fruit Always";
final Label label = new Label(STANDARD_TEXT);
// configure the primary stage.
StackPane primaryLayout = new StackPane();
primaryLayout.getChildren().add(label);
primaryLayout.setStyle("-fx-background-color: lightgreen; -fx-padding: 10;");
primaryStage.setScene(new Scene(primaryLayout, 200, 100));
primaryStage.setTitle("Primary Stage");
// configure the secondary stage.
final Stage secondaryStage = new Stage(StageStyle.UTILITY);
CheckBox alternateTextCheck = new CheckBox("Show alternate text");
alternateTextCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override public void changed(ObservableValue<? extends Boolean> selected, Boolean oldValue, Boolean newValue) {
if (newValue) label.setText(ALTERNATE_TEXT); else label.setText(STANDARD_TEXT);
}
});
StackPane secondaryLayout = new StackPane();
secondaryLayout.getChildren().add(alternateTextCheck);
secondaryLayout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
secondaryStage.setScene(new Scene(secondaryLayout, 200, 100));
secondaryStage.setTitle("Secondary Stage");
// specify stage locations.
secondaryStage.setX(400); secondaryStage.setY(200);
primaryStage.setX(400); primaryStage.setY(350);
// add a trigger to hide the secondary stage when the primary stage is hidden.
// this will cause all stages to be hidden (which will cause the app to terminate).
primaryStage.setOnHidden(new EventHandler<WindowEvent>() {
#Override public void handle(WindowEvent onClosing) {
secondaryStage.hide();
}
});
// show both stages.
primaryStage.show();
secondaryStage.show();
}
}
Inside the button click action you can create a new satge and then a object of the other class you want to display. after that call the start method using the created object.
Stage stage= new Stage();
NewClass nc= new NewClass();
nc.start(stage);
hope this will work!!!

Resources