Reach a variable from another class (which is controller class of JavaFX) - java

I'm trying to implement a game on JavaFX. Moreover, I'm dealing with an FXML file so I have a main class and controller class. My question is how can I reach the objects of the main class from the controller class. To be more clear I will share a simple code.
This is main class:
public class JavaFXApplication1 extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("Risk3.fxml"));
// Main Pane
BorderPane borderPane = new BorderPane();
borderPane.setCenter(root);
// Main scene
Scene scene = new Scene(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
So for example I want to reach root or borderPane from controller class which is:
public class SampleController implements Initializable {
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
Should I make root and borderPane global and static or is there any another way to reach them ?.

The root panel can simply reached from the FXML controller using
#FXML tag like any component.
<BorderPane xmlns:fx="http://javafx.com/fxml" fx:id="root">
...
</BorderPane>

Related

How to remove flickering of main window at the beginning of a video in Javafx?

I'm making the application with JavaFX and Scene Builder. I have a fullscreen video before my appliaction start. SO it is a kind of intro for my appliaction.
A video has the same background as my main stage.
The problem is I have flickering effect at the very beginning when I launch the appliaction. Buttons and text from main window appear during like half of a second and then my video start.
How can I remove or hide that effect?
Please, check this link for more clear understanding of my problem
Video
Main Class:
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("fxml/card.fxml"));
Parent root = (Parent) loader.load();
Scene scene = new Scene(root, 1600, 600);
primaryStage.setScene(scene);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setMaximized(true);
primaryStage.setResizable(true);
primaryStage.getIcons().add(new Image("logo-icon.png"));
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Controller where I place All buttons and text. I also have a video here:
public class Controller implements Initializable {
#FXML private AnchorPane mainAnchor, anchorRow, randomCard, randomCardBack, previewCard, bolt1;
#FXML private StackPane hBoxCat0;
#FXML private Button btnPalette, btnFont, btnQuestCards, btnNonQuestCards;
#FXML private ToggleButton btnPref1, btnPref2, btnPref3, btnPref4, btnPref5, btnPref6, btnPref7, btnPref8;
#FXML private Label lb_randomCard, lb_randomCardBack, answerID, lb_previewCard, category1, category2, category3, category4, category5, category6, category7, category8;
#FXML private ToggleGroup group;
#FXML private JFXColorPicker colorPickerCategory;
#FXML private Rectangle rectRandom, rectRandomBack;
#FXML private MediaView media;
private MediaPlayer mp;
private static final String MEDIA_URL = "intro.mp4";
#FXML
public void initialize(URL location, ResourceBundle resources) {
mp = new MediaPlayer(new Media(this.getClass().getResource(MEDIA_URL).toExternalForm()));
media.setMediaPlayer(mp);
media.setSmooth(true);
mp.setAutoPlay(true);
Timeline tm = new Timeline(new KeyFrame(Duration.millis(3000), new KeyValue(media.opacityProperty(), 0.0)));
tm.setDelay(Duration.millis(5500));
tm.play();
addQuestionsToArrayCat1();
addAnswersToArrayCat1();
addSentencesToArray();
matchSentences();
}
}

JavaFX Setting SplitPane divider after init regarding components

I want to set my SplitPane divider to a "specific" starting position so, that takes into consideration the components of the window.
There is a fix starter sized TableView, but the window size can be different. So I would like to set the divider position at start so, that the table is fully visible, and right next to it stands the divider.
I have the following code so far in the public void initialize():
Controller:
#FXML
SplitPane splitPane;
#FXML
TreeTableView treeTable;
public void initialize() {
getStage().addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
double tableWidth = treeTable.getWidth();
double stageWidth = getStage().getWidth();
splitPane.setDividerPositions(tableWidth / stageWidth);
}
});
}
FXML:
<SplitPane fx:id="splitPane">
<items>
<TreeTableView fx:id="treeTable" prefWidth="280">
<!-- table -->
</TreeTableView>
<AnchorPane fx:id="anchorPane">
<!-- anchor pane -->
</AnchorPane>
</items>
</SplitPane>
But it's not working, beacuse the Stage is null at this moment.
So the problem is everything isn't loaded yet so to fix this you can call it at the end of your Main start function as below
Main:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("/Sample.fxml"));
Scene scene = new Scene(loader.load());
primaryStage.setScene(scene);
Controller controller = loader.getController();
controller.setStartingPosition(primaryStage);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}
Controller:
public class Controller {
public SplitPane splitPane;
public TreeTableView treeTable;
public AnchorPane anchorPane;
public void setStartingPosition(Stage stage){
stage.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
double tableWidth = treeTable.getWidth();
double stageWidth = stage.getWidth();
splitPane.setDividerPositions(tableWidth / stageWidth);
}
});
}
}
I cannot tell if this works as I do not have the "other components" of which you speak so let me know if this works it looks the same both ways for me

How to separate the javafx GUI structures and its implementation?

For example this code:
public class VendorManagementSystem extends Application {
#Override
public void start(Stage primaryStage) {
VBox main_pane = new VBox(15);
main_pane.setPadding(new Insets(10,10,10,10));
Button addItem_mainPane = new Button("Add Item");
addItem_mainPane.setPrefWidth(120);
Button cancelation_button = new Button("Close");
cancelation_button.setPrefWidth(120);
main_pane.getChildren().addAll(addItem_mainPane,sellItem_mainPane,cancelation_button);
main_pane.setAlignment(Pos.CENTER);
Scene scene = new Scene(main_pane);
primaryStage.setHeight(450);
primaryStage.setWidth(650);
primaryStage.setTitle("Vendor Management System");
primaryStage.setScene(scene);
primaryStage.show();
Then I wanna to separate this in another class:
cancelation_button.setOnAction(e->{
primaryStage.close();
});
I tried to create another class and inherit the javafx class to create the implementation in another class:
public class ControlsImplementation extends VendorManagementSystem{
cancelation_button.setOnAction(e->{
primaryStage.close();
});
}
but it doesnt work i got those errors:
error: < identifier > expected
cancelation_button.setOnAction(e->{
error: < identifier > expected
cancelation_button.setOnAction(e->{
error: illegal start of type
});
Any suggestion how to separate the javafx gui structures and the controls implementation.
You can create separate classes for the view and for the controller. You should make both of these independent of the Application class, which has responsibility for starting the application and managing its lifecycle:
View class:
public class View extends VBox {
private final Controller controller ;
public View(Controller controller) {
this.controller = controller ;
buildUI();
}
private void buildUI() {
setPadding(new Insets(10,10,10,10));
Button addItem = new Button("Add Item");
addItem.setPrefWidth(120);
Button cancelation = new Button("Close");
cancelation.setPrefWidth(120);
cancelation.setOnAction(e -> controller.exit(this));
getChildren().addAll(addItem, /* sellItem, */ cancelation);
setAlignment(Pos.CENTER);
}
}
Controller class:
public class Controller {
public void exit(Node view) {
view.getScene().getWindow().hide();
}
}
Application class:
public class VendorManagementSystem extends Application {
#Override
public void start(Stage primaryStage) {
Controller controller = new Controller();
View view = new View(controller);
Scene scene = new Scene(view);
primaryStage.setHeight(450);
primaryStage.setWidth(650);
primaryStage.setTitle("Vendor Management System");
primaryStage.setScene(scene);
primaryStage.show();
}
}
Obviously in a real application you would have a data model in addition to the view and controller classes.
You can vary the relationship between the view and the controller according to how you want things set up, e.g. instead of giving the view a reference to the controller, you could give the controller a reference to the view, define a getCancelationButton() method in the view, and call getCancelationButton().setOnAction(...) in the controller. It just depends which variant of MVC you are trying to implement.

JavaFX Updating UI from another thread

I have a main class
public class Main {
public static void main(String[] args) {
Application.launch(View.class);
View view = new View();
Platform.runLater(() -> view.changeTitle());
}
}
and a view JavaFX class
public class View extends Application {
Stage primaryStage;
public View() {
}
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
this.primaryStage = primaryStage;
primaryStage.show();
}
public void changeTitle() {
primaryStage.setTitle("YEA!");
}
}
I want the main class to do something in JavaFX thread but my code doesn't work. In documentation said that I can call Platform.runLater() from any thread I want. If I call Platform.runLater() from JavaFX thread (in start(), for example), everything is OK.
First, main(String) is blocked on Application.launch(Class).
From Oracle Javadocs:
The launch method does not return until the application has exited, either via a call to Platform.exit or all of the application windows have been closed.
Second, you're creating a new View instance on the second line. That will NOT be same instance Application created, so your Platform.runLater wouldn't affect the launched application even if that code were reachable before it exited.

How to swtich to new scene in the same stage JavaFX

I try to switch between scenes in same stage. I'm beginner in JavaFX so I don't know how to do it easily without spaghetti code. When I start code below I get null pointer at rootLayout.setCenter(content) in showCarChoosePage method (second scene). I know rootLayout is null and I was trying create new scene with and load it to primaryStage but then I got null pointer too. showCarChoosePage method is calling from LoginController. Thanks for your help
public class MC extends Application {
public Scene scene;
private GridPane grid;
public AnchorPane content;
public BorderPane rootLayout;
public Stage primaryStage;
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("VMHT v0.1");
try {
FXMLLoader loader = new FXMLLoader(MC.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
showLoginPage();
//showCarChoosePage();
}
public void showLoginPage(){
try {
FXMLLoader loader = new FXMLLoader(MC.class.getResource("view/LoginView.fxml"));
content = (AnchorPane) loader.load();
rootLayout.setCenter(content);
LoginController controller = loader.getController();
controller.setMC(this);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void showCarChoosePage(){
try {
FXMLLoader loader = new FXMLLoader(MC.class.getResource("view/CarChooseView.fxml"));
AnchorPane content = (AnchorPane) loader.load();
rootLayout.setCenter(content);
CarChooseController controller = loader.getController();
controller.setMC(this);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Here is another way to handle multiple scenes. It uses Controllers for each scene. You must pass the initial stage that was created in Main to each new Controller. You must also pass whatever data you want to share from Controller to Controller. This is all done with methods that are added to your controllers.
In this simple case a Person object was created with name, sex, and age class variables as well as setters and getters for these objects.
Then I created 3 fxml files (using SceneBuilder) that displayed the name, sex, and age values. Of course, I also created 3 controllers, one for each fxml file.
The user was allowed to edit these values. One scene allowed name entry, another allowed sex entry, and the last allowed age entry. This simulated a complex app where data entry and processing was divided among 3 different scenes.
The three fxml files and the 3 controllers look very similar to one another. Here is the NameController. It first has setters for the Stage and Person objects. It also has button event handlers to allow the user to navigate to the other stages.
package multiscene;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import java.net.URL;
import java.util.ResourceBundle;
public class NameController implements Initializable {
#FXML private TextField lblSex;
#FXML private TextField lblAge;
#FXML private TextField txtName;
private Stage myStage;
private Person person;
public void setStage(Stage myStage) {
this.myStage = myStage;
}
public void setPerson(Person person) {
this.person = person;
lblAge.setText(person.getAge());
txtName.setText(person.getName());
lblSex.setText(person.getSex());
}
#FXML
private void ageClicked(ActionEvent event) throws Exception{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(NameController.class.getResource("Age.fxml"));
AnchorPane page = (AnchorPane) loader.load();
Stage dialogStage = new Stage();
dialogStage.setTitle("Person Editor");
// dialogStage.initModality(Modality.WINDOW_MODAL);
//dialogStage.initOwner(primaryStage);
Scene scene = new Scene(page);
dialogStage.setScene(scene);
// Set the person into the controller
person.setName(txtName.getText());
AgeController newController = loader.getController();
newController.setStage(dialogStage);
newController.setPerson(person);
dialogStage.show();
myStage.close();
}
#FXML
private void sexClicked(ActionEvent event) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(AgeController.class.getResource("Sex.fxml"));
AnchorPane page = (AnchorPane) loader.load();
Stage dialogStage = new Stage();
dialogStage.setTitle("Person Editor");
// dialogStage.initModality(Modality.WINDOW_MODAL);
//dialogStage.initOwner(primaryStage);
Scene scene = new Scene(page);
dialogStage.setScene(scene);
// Set the person into the controller
person.setName(txtName.getText());
SexController newController = loader.getController();
newController.setStage(dialogStage);
newController.setPerson(person);
dialogStage.show();
myStage.close();
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
Main.java must initialize the Stage and the shared data as follows. Both the Stage and the shared data are passed to the first Scene when it is loaded.
package multiscene;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Person person = new Person();
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("Name.fxml"));
AnchorPane page = (AnchorPane) loader.load();
Stage dialogStage = new Stage();
dialogStage.setTitle("Person Editor");
Scene scene = new Scene(page);
dialogStage.setScene(scene);
NameController nameController = loader.getController();
nameController.setStage(dialogStage);
nameController.setPerson(person);
dialogStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here is one simple method you might try
import javafx.stage.Stage;
public class ManyScenes extends Application {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(final Stage primaryStage) {
primaryStage.setTitle("Hello World");
Group root1 = new Group();
Group root2 = new Group();
Group root3 = new Group();
final Scene scene1 = new Scene(root1, 300, 250);
final Scene scene2 = new Scene(root2, 300, 250);
final Scene scene3 = new Scene(root3, 300, 250);
Button go1 = new Button();
go1.setLayoutX(100);
go1.setLayoutY(80);
go1.setText("Go to scene2");
go1.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
primaryStage.setScene(scene2);
}
});
root1.getChildren().addAll(new Label("Scene 1"), go1);
Button go2 = new Button();
go2.setLayoutX(100);
go2.setLayoutY(80);
go2.setText("Go to scene3");
go2.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
primaryStage.setScene(scene3);
}
});
root2.getChildren().addAll(new TextField(), go2);
Button go3 = new Button();
go3.setLayoutX(100);
go3.setLayoutY(80);
go3.setText("Back to scene1");
go3.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
primaryStage.setScene(scene1);
}
});
root3.getChildren().addAll(new TextArea(), go3);
primaryStage.setScene(scene1);
primaryStage.show();
}
}

Categories