Issue with 2 main Methods Java And JavaFX - java

I have currently 2 classes, both with main methods, inside one project. The first class looks like:
#SpringBootApplication
public class Application implements CommandLineRunner {
public void handle(String s) {...}
}
and the other one is a simple JavaFX starting class:
public class MainMenue extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("/Sample.fxml"));
Scene scene = new Scene(root);
primaryStage.setTitle("Hello World");
primaryStage.setScene(scene);
primaryStage.show();
}
}
My plan is to start the javafx App to have some buttons and all the fxml feature, and I would like to start the other application with a button event. That already works fine. BUT I have to use some methods from the normal Java application from the JavaFX app. To be more specific, there is a handle method which takes String as parameter, and I would like to write the String in a Textfield in JavaFX and by clicking a Button, the method "handle" from the normal Java application should get done.
I couldn't find any solution to solve this. Making the class Application extends Application AND implementing this interface isn't working fine.
Do you have any solutions or other ways to do so?
Thanks in advance!

Related

Serializing only works when called in static main method of JavaFX App class

I'm working on a project that uses JavaFX for the GUI (I know, non-serializable). I want to serialize objects such as my Users.
I'm not able to access the instance that JavaFX Application uses, but I have it associated in other classes.
For example - it is associated with my Controller class:
public class MyApp extends Application {
public void start(Stage stage){
// ... assume controller loaded
controller.setApp(this);
}
}
public class Controller {
MyApp app;
public setApp(MyApp app){
this.app = app
}
}
Now when I go to serialize an instance of MyApp, I'm having difficulty. I found a slight trick (option 1), but it feels a little messy. I'd much rather do option 2.
Option 1 [WORKS] - create an additional instance in the main method.
public class MyApp extends Application {
public void start(Stage stage){
// ... assume controller loaded
controller.setApp(this);
}
public static void main(String[] args){
MyApp app = new MyApp(); // this is a different instance than javafx instance.
launch(args)
app.users = Controller.getUsers();
writeApp(app);
}
public static void writeApp(PhotoApp photoApp) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream(storeDir + File.separator + storeFile));
oos.writeObject(photoApp);
oos.close();
}
}
Thus, option 1 essentially creates a new instance and copies stuff back and forth from the actual instance in controller.
OPTION 2 [DOES NOT WORK] - serialize the instance associated with the controller (since that is the actual instance JavaFX is using)
public class Controller {
MyApp app;
public void setApp(MyApp app){
this.app = app
}
public void someAction(){
MyApp.writeApp(this.app);
}
}
When I do Option 2 I get errors saying Controller is not serializable. I understand that it is not (which is okay), but I don't get that error in Option 1. In both options I'm calling the same method with some instance of MyApp. I'm not sure why it works for Option 1 but not Option 2.
Any reason why one option works over the other? How do most people do serialization of some of their objects when they use JavaFX?

Can't edit non-static text area in Javafx Application class from other class

I'm trying to implement a GUI in JavaFX for a text-based game I've been making.
This part of the main class sets everything up:
public class Main extends Application{
#FXML
protected TextField input;
#FXML
protected TextArea output, inventory, commands;
protected static List<String> history;
protected static int historyPointer;
protected static String textToRead = null;
private Service<Void> backgroundThread;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("Console.fxml"));
BorderPane root = (BorderPane) loader.load();
history = new ArrayList<>();
historyPointer = 0;
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("MyConsoleFXGUI"); //Could later be changed so that the actual game title is displayed here.
stage.show();
I use a FXML-file generated from SceneBuilder and Main is the controller. It works well and when I tried to set some text to input through the initialize function, the text printed fine (but I have now removed that method).
The problem comes when I then launch my Game-class and try to print text from it to the text area "Input" in main.
I use this method in Main to set the text:
/**
* Called when the game wants to print something to the game
* #param message The text to be printed to the console.
*/
public void printGameInfo(String message) {
System.out.println("This method was attempted!");
output.setText(message + System.lineSeparator());
}
This method should work, the problem I have is that I don't know how to call it from the Game-class. Since the Main class isn't instantiated I can't call on a Main-object and I can't make the text area static as that doesn't work with JavaFx applications.
So how do I go about to call the "printGameInfo" from a separate class to set some strings to a text area?
Thanks a lot!
The Main class definitely is instantiated or how should anything be able to call its start method which is not static? When you instantiate your Game class you could pass it a reference to your Main class instance. Nevertheless this is an awfull software design.

Can i create various scene in diffent class file and switch between them in javafx

Please am new to javafx. the tutorial i watched switched between two scenes that was on the sam class file. I am thinking if i have 10 scenes that will make the code very lenghty. Can i create each scene in a diffrent class file and switch between them in. How?
You sure can, Scenes are regular classes after all.
I've created a simple example.
One thing to note: I deliberately did not extend Scene, because that is usually unnecessary. You can compose the scenes anywhere you like (in dedicated classes, methods, ...). In this example I used two small factory classes.
public class App extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
primaryStage.setScene(SceneAFactory.create(primaryStage));
primaryStage.show();
}
public static void main(String[] args)
{
Application.launch(App.class, args);
}
}
public class SceneAFactory
{
public static Scene create(Stage stage)
{
Button button = new Button("Switch to B");
button.setOnAction(event -> stage.setScene(SceneBFactory.create(stage)));
return new Scene(new HBox(new Label("Scene A"), button));
}
}
public class SceneBFactory
{
public static Scene create(Stage stage)
{
Button button = new Button("Switch to A");
button.setOnAction(event -> stage.setScene(SceneAFactory.create(stage)));
return new Scene(new HBox(new Label("Scene B"), button));
}
}
Keep in mind though, that depending on the task at hand, replacing just a part of the scene graph might make more sense than replacing the entire scene.

Java Form problems

I've recently delved into JavaFX as a C# developer. One thing I noticed in Java is that you're not spoon fed the way Visual Studio/Microsoft spoonfeed us.
So. When I was creating a form using the scene builder for IntelliJ Idea on JavaFX. I inherited "Stage" for my controller class and created a void called load that will load the instance of the scene from the FXML file. Therefore when I call load() from the Main entry point or anywhere it will load the fxml file and show.
LoginController frmLogin = new LoginController();
frmLogin.load();
The problem is that it works and it does't work.
Here's my code.
Main.Java
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
LoginController frmLogin = new LoginController();
frmLogin.load();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
LoginController.Java
public class LoginController extends Stage
{
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private void btnLogin_Clicked(ActionEvent e) throws Exception
{
if (txtUsername.getText().equals("admin") && txtPassword.getText().equals("pass"))
{
Messagebox.Show("Correct Login!");
this.show(); //The problem occurs here!
}
else
{
Messagebox.Show("Incorrect Login");
}
}
public void load() throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("frmLogin.fxml"));
this.setScene(new Scene(root));
this.setTitle("JavaFX GUI");
this.setResizable(false);
this.initModality(Modality.APPLICATION_MODAL);
this.show();
}
}
Here's a GIF of the problem.
http://i.imgur.com/0hOG76M.gif
I want to know why when I call .show() it shows a blank for?
Any help would be appreicated.
Solution
Don't inherit Stage in your Controller.
JavaFX will implicitly create a Stage for your application and pass it to your application (the primaryStage parameter in your application start method).
Sample
Here is a quick update which should work. Another alternative for this is to factor out the stage management as in James's answer.
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("frmLogin.fxml"));
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("JavaFX GUI");
primaryStage.setResizable(false);
primaryStage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
. . .
public class LoginController
{
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private void btnLogin_Clicked(ActionEvent e) throws Exception
{
if (txtUsername.getText().equals("admin") && txtPassword.getText().equals("pass"))
{
Messagebox.Show("Correct Login!");
}
else
{
Messagebox.Show("Incorrect Login");
}
}
}
Aside: I am not sure what your MessageBox class is, but JavaFX 8u40 has a built-in Alert dialog box for standard message box style functionality, so that would be the preferred method to do that.
It looks like you have confused the different pieces that make up the application.
The FXML typically represents the "view"; i.e. the portion of the UI that is visible. It defines what controls are displayed and how they are laid out.
The controller implements the logic that is connected to (controls) the view. So it typically processes user input and updates the view in various ways.
A Stage is a window.
So, I don't think it really makes sense that your controller is a Stage. There are some scenarios where you might make a controller a subclass of a UI element, but those are somewhat advanced uses of JavaFX, and even then you would typically subclass a layout pane, not a Stage.
Here's roughly what happens when you call load on an FXMLLoader:
The FXMLLoader creates a hierarchy of Nodes (UI elements) corresponding to the elements defined in the FXML file
If the FXML file defines a fx:controller attribute in its root element, the FXMLLoader constructs a new instance of that class. It then injects any elements with fx:id attributes into fields in that controller instance with names matching the fx:id values. It also registers any event handlers mapping to methods in the controller instance.
The FXMLLoader's load() method returns the object corresponding to the root element of the FXML file.
So, in your code, you actually end up with two LoginController instances. You create one yourself in the start() method. You then call load() on that instance. That method calls load(...) on an FXMLLoader (via the really ugly static load method). Calling FXMLLoader.load(...) then causes the FXMLLoader to create an instance of the class declared in fx:controller. I'm guessing (you didn't show the FXML code) that class is also LoginController. So that is the second instance.
Now what happens, is that you get a reference to the UI element from FXMLLoader.load(). You put that in a Scene, and set the Scene in the LoginController, which - unusually - is a Stage. Then you make the Stage appear on the screen with show(). Note this happens in the instance you created in the start method.
When the user presses the button that has btnLogin_Clicked registered as its handler, the handler method is invoked on the controller instance: the one created by the FXMLLoader. That instance never had a Scene set, so when you then call this.show() it shows that instance of the LoginController (which, again, is a Stage). Since it never had its scene set, you see a blank window.
It's not actually clear to me what you intend with the call to this.show() in btnLogin_Clicked anyway. Assuming you thought this was the same Stage you had created from the start(...) method, that Stage is already showing.
The typical pattern is that you use the primaryStage that is passed to the start(...) method, and set a scene in that and show it. So you'd do something like:
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("frmLogin.fxml"));
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("JavaFX GUI");
primaryStage.setResizable(false);
primaryStage.initModality(Modality.APPLICATION_MODAL);
primaryStage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
and then the controller is just a controller: it just handles the logic:
public class LoginController
{
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private void btnLogin_Clicked(ActionEvent e) throws Exception
{
if (txtUsername.getText().equals("admin") && txtPassword.getText().equals("pass"))
{
Messagebox.Show("Correct Login!");
// I don't really know what you were trying to do here
// but if you need a reference to the window containing the
// associated fxml elements, you can get it from one of those
// elements:
Stage stage = (Stage) txtUsername.getScene().getWindow();
//this.show(); //The problem occurs here!
}
else
{
Messagebox.Show("Incorrect Login");
}
}
}
Typically what you want to do when the user has successfully logged in, is to display something new in the current window. The simplest way to do this is just to set the root of the current scene to the content of a different FXML file. For example:
public class LoginController
{
#FXML
private TextField txtUsername;
#FXML
private TextField txtPassword;
#FXML
private void btnLogin_Clicked(ActionEvent e) throws Exception
{
if (txtUsername.getText().equals("admin") && txtPassword.getText().equals("pass"))
{
Messagebox.Show("Correct Login!");
Scene currentScene = txtUsername.getScene();
Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));
currentScene.setRoot(root);
// resize window:
currentScene.getWindow().sizeToScene();
}
else
{
Messagebox.Show("Incorrect Login");
}
}
}
Here Main.fxml defines the main application the user sees, having successfully logged in, and defines its own controller class, etc.

MVC Pattern in JavaFX With Scene Builder

I'm new to JavaFX and am struggling to create a proper MVC architecture given my current setup. I clicked together a UI using Scene Builder and designated a Controller class.
Startup:
public class Portal extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("PortalUI.fxml"));
stage.setTitle("Portal");
stage.setScene(new Scene(root));
stage.show();
}
}
And the Controller class contains the rest of the code.
public class AccommodationPortalView implements Initializable {
#Override
public void initialize(URL url, ResourceBundle resources) {
// Work here.
}
}
My professor asked that I further separate the concerns and responsibilities of this application. The Controller is not only managing state and talking with the backend, but also updating the View.
My first response was to let the Controller class become the View and create two other classes for the Controller and Model.
However, I'm at a loss at how to connect these pieces. I never need to instantiate the View, so there is no View instance that I can pass to my Controller, for example. Next, I tried making them all singletons and simply letting Controller fetch them at runtime, but that gives me an error.
public class Portal extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("PortalUI.fxml"));
stage.setTitle("Portal");
stage.setScene(new Scene(root));
stage.show();
// Controller gets a View and Model instance in initialize();
// Error: Instantiation and Runtime Exception...
PortalController.INSTANCE.initialize();
}
}
How do I properly set-up an MVC pattern using my current configuration? Is a different architecture required?
Your,
-- View is a primary Stage provided by the JavaFX platform at start up. This stage has the only Scene (you have created and set) which in turn has a parent node content root (your variable). This root node is set by FXMLLoader and represents the layout/node structure defined in the "PortalUI.fxml" file.
In other words Stage -> Scene -> PortalUI.fxml(root) will define the view part.
-- Controller is the class that implements Initializable and that you specified in your PortalUI.fxml file with fx:controller=" " attribute. The class you have specified there (PortalController I suppose) will be created and invoked its initialize() method by the FXMLLoader. Namely the Controller will be created when the PortalUI.fxml file is loaded, so you don't need to create and initialize it yourself. To get the created/initialized instance of the controller from the FXMLLoader look the Accessing FXML controller class.
-- Model is the underlying data structure stored and managed by the controller. It can be anything representing the "data". For example, Person, PortalInfo etc. classes.

Categories