Java: keyPressed events not firing when game is opened through application class - java

I'm making a game in java, and I want to extract it into a runnable Jar. To make this possible I made this class:
public class AfterglowApp extends Application {
public void start(Stage primaryStage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(AfterglowApp.class.getResource("afterglow.fxml"));
Pane root = fxmlLoader.load();
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(AfterglowApp.class, args);
}
}
In my FXML controller I have a keyListener that triggers on keyPressed for the anchor pane in the FXML file. This works fine when I run it as an FXML application through Eclipse, but when I run it with the application class, no keypresses register (it still registers mouse clicks as normal though).
I have a feeling that the anchor pane of the FXML file loses focus, and that that's what's causing it to not register key presses, but I don't know how to fix that (if that even is the problem).
I realize there's probably something dumb I'm missing, but any help would be much appreciated.

I was correct, it was something stupidly easy. All I had to do was to call
.requestFocusInWindow();
on my anchor pane after the app started. Sorry for wasting the time of everybody who read this. I'll leave the question up in the unlikely event that somebody else gets themselves in a similar situation.

Related

Two ways to load FXML; why is one preferred over the other?

I am looking for feedback on why we should use one method over the other as far as loading and displaying new FXML stages.
Most of the time, I see tutorials and such that show the loading of a stage done from a separate class. However, it can also be done within the FXML file's controller itself, and I personally see that way as being a bit cleaner and more manageable.
Consider the following Main.java class:
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
// Method 1:
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout.fxml"));
loader.setController(new LayoutController());
stage.setScene(new Scene(loader.load()));
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
This seems to be the prevailing method. It creates the contoller and sets the Scene and then shows it.
However, if we change the start() method to this instead:
#Override
public void start(Stage stage) throws Exception {
LayoutController controller = new LayoutController();
controller.showStage();
}
and move the FXML loading code into the LayoutController constuctor, the result is the same:
public class LayoutController {
#FXML
private Label label;
private Stage stage = new Stage();
public LayoutController() {
// Method 2:
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout.fxml"));
loader.setController(this);
stage.setScene(new Scene(loader.load()));
} catch (IOException e) {
e.printStackTrace();
}
}
public void showStage() {
this.stage.showAndWait();
}
}
The benefit I see here is more separation between the view and logic. Everything about the LayoutController and its associated FXML file are contained in one place.
So my question is this: what is wrong with the second approach? I am assuming it is not the standard method for a reason, but I'm not able to see any drawbacks.
Would a question like this be more suited to Code Review? I'm not really asking for opinions, as there seems to be a general "rule" that the first method be used.
In this case there is not much difference.
For larger programs the second approach is undesirable tough:
It violates the single responsibility principle:
The class is responsible for:
Creating the scene
Creating the stage
Displaying the stage (and even worse it does this in a way that may interfere with other logic (showAndWait))
It's the controller of the view and may become responsible for handling several events
Furthermore the way the class is designed in a way that prevents the responsibilities from being moved to other classes without issues.
In a larger program you likely want to create a class that manages passing data to views, arrange windows or display the view as part of a scene, ect. The second approach is ill suited for this.
Additionally it makes if harder to not repeat yourself. Unless you move the logic to a common supertype, you also need to implement the logic for showing the scene in every controller class. Repetition of the same or similar code results in code that is hard to maintain.
Note: Using a single class for loading the fxml and for use as controller is not necessarily a bad thing, but you should go with the Custom Component approach presented in Introduction to FXML.

How to close the current window in JavaFX?

I'm using JavaFX to build an application. In some windows there's a button whose purpose is to close the current one. Currently I'm using this kind of approach, but I think it's not clean as I would like it to be.
public class AboutController {
#FXML
private Button aboutClose;
public void initialize() {
aboutClose.setOnAction(event ->
((Stage) (aboutClose.getScene().getWindow())).close());
}
}
Is there a better way to implement this task? How to get the stage I'm working on from the Controller without having to take it from the button itself?

Terminate JavaFX Application using Platform.exit()

I am using JavaFX 2.2 and I have a class which extends Application. Here is my code:
Class A extends Application {
public void Stage(final Stage primaryStage) { ... }
public void Start(){
launch();
}
btnLogin.setOnAction(new EventHandler<ActionEvent>() {
Platform.exit();
}
}
Class B{ }
Class C extends Application{
public void Stage(final Stage primaryStage) { ... }
public void Start(){
launch();
}
}
Actually, Class A is login screen; it will close when I successfully log in. Then the screen closed by platform.exit() function. After that I execute view button in Class B , Class C called but there are some problems.
java.lang.IllegalStateException: Application launch must not be called more than once
I just terminate the screen by using Platform.exit() function but I can't understand why it can't be closed.
Platform.exit() actually terminates whole jfx.
To keep things safe, just invoke launch() once and show/hide new windows.
Something like:
Platform.setImplicitExit(false);//make fx running in backgound.
Platform.runLater/AndWait {//make sure u create window in jfx thread
//window creation/show code here.
}
If Class B is the main screen and you need to Embed JavaFX in your application for Login Screen or any other screen, you don't need Class A and Class C to extend Application.
You can just create a new Window in Swing inside these classes (A and C) and use JFXPanel to embed JavaFX into your Swing Application. This way you can have full control on the application and you can easily open and close windows for Login or any other functionality that you want.
N.B. You should not have two class extending Application inside one app, as only one JavaFX thread is allowed per JVM.
Everytime you try to do this you will get this error
java.lang.IllegalStateException: Application launch must not be called more than once

How do I set up a JavaFX Control to be imported into Scene Builder?

I have a JavaFX control that is basically an amalgamation of several other JavaFX controls.
I want it such that the .jar file can be imported into Scene Builder so that it can be used like any other control. The closest analogy I can think of is when you make a custom control in C# and use it several times throughout several projects.
When I try to import the FXML file, it doesn't work. The control isn't treated as a single entity, and instead is basically just all of it's parts strung out in the FXML file.
What do I need to do with the FXML file, or the controller.java file so that the Scene Builder will be able to import the .jar, see the control(s), and allow me to import and use each custom control as a single entity? I've looked several places and even asked on Stack Overflow once before (though the answer I got was not the one for which I was looking, and have received no responses since), but nothing I've seen comes close to handling my issue.
The closest I've come has to do with this line in the FXML file:
<?scenebuilder-classpath-element /path/to/something?>
but I don't know what goes in /path/to/something
I know I can, in the initialization, simply add the control to the scene, but that is sub-optimal and something which I am desperately trying to avoid.
I was finally able to resolve the issue. After much trial and error and following the sample code that came from here, I discovered my problem was that I needed 2 classes for each FXML control group.
One to be the actual controller of the group, and another to be the object that would house the controller. I followed the code in the Unlock example and it was a godsend for helping me.
Basically it comes down to two files:
The object (which extends the type of the root node, in my case):
public class <InsertObjectClassNameHere> extends <RootContainerTypeHere>{
}
After that you need the controller class. This is with what I am most familiar, however I was still doing it wrong. This is what needs to implement initializable:
public class <InsertControllerClassNameHere> implements Initializable{
}
So for me the Object class looks like this:
public class DGCSDefiner extends GridPane {
private final DGCSDefinerController Controller;
public DGCSDefiner(){
this.Controller = this.Load();
}
private DGCSDefinerController Load(){
final FXMLLoader loader = new FXMLLoader();
loader.setRoot(this);
loader.setClassLoader(this.getClass().getClassLoader());
loader.setLocation(this.getClass().getResource("DGCSDefiner.fxml"));
try{
final Object root = loader.load();
assert root == this;
} catch(IOException ex){
throw new IllegalStateException(ex);
}
final DGCSDefinerController cntrlr = loader.getController();
assert cntrlr != null;
return cntrlr;
}
/**
* Get the settings defined by the controller.
* #return controller defined settings.
*/
public ColorSettings getColorSettings(){
return this.Controller.getColorSettings();
}
/**
* Set the controllers color settings.
* #param CS Defined color settings.
*/
public void setColorSettings(ColorSettings CS){
this.Controller.setColorSettings(CS);
}
}
and then there is the actual Controller class.
So for a straight-forward answer,
you need to have a class that will be loading your controller, and you need to pass down from your controller to that class that with which you will be working (Or, you can simply keep the controller public and access it directly).

Thin controllers

I am getting my feet wet with javafx. This is what I am doing.
FXML Views
DI Controllers
Weld-SE Managed Services and Models
Trying to confine UI to FXML
Trying keep the Controllers thin
Problem:
While trying to code the UI, most static UI is confined inside the fxml. But there are scenarios where I find my self adding, removing, showing, hiding elements etc.
I find myself doing this inside the controller as fx lets me configure controller method in the view which it will call on a particular action / event. All this code deals with Dynamic UI building / manipulating and belongs inside the view layer. But, it ends up in controller making the controllers fat.
javafx provides javascript integration. This is one possible way to abstract that view manupulation code away. But this would add not so perfect javascript into the mix.
How would I abstract the code away in java or fxml so that I don't break the Thin Controller Paradigm ?
EDIT
#assylias
Agreed, I have thought about this and this way that java class and fxml together become a reusable widget. But then, how do I wire this into FXML. FXML doesn't understand anything but a controller. Let say I wire this view class into fxml using fx:controller and not name it controller. So I have something like this.
This view class has nothing but view manipulation code. Then I would create another controller class. But then I would expect to somehow fill the form data into this controller. This should only happen when the user has submitted the form. So in a way, I need to tell javafx somehow that UI manipulation request / event is different from actual data manipulation request / event.
Your thoughts, sorry if it was verbose. Tried to articulate it in as few words as I could.
I think the easiest solution to this is to remember that the controller specified in FXML is a view controller. It's purpose is to contain code to modify and update the view, not to contain traditional MVC controller code or business logic.
For example, in a project I'm currently working on, I'm using JavaFX with Akka Actors. The application is written in scala. The JavaFX view controllers contain any code necessary to modify the view. One screen contains a login form. When the user clicks the login button, the view controller simply creates a message containing the username and password, and sends that message to the actor responsible for doing business logic. If that actor determines there is an error then it will send a message back to the view controller, and the view controller can decide what sort of updates need to be made on the screen.
I've found that using akka actors with JavaFX greatly simplifies coding the application for at least two reasons.
Because using an actor system mandates sending messages between actors, there is a natural boundary between presentation code and business code. The messages that are passed back and forth form this natural boundary.
Using actors completely replaces the complexity of working with threads/tasks. It completely eliminates the need to code javafx.concurrent.Task's for long running processes.
How about putting your view manipulation code in Main Class ?
Main Class :
public class SampleJavaFXApp extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource(
"SampleUI.fxml"));
Parent root = (Parent) loader.load();
Controller controller = loader.getController();
viewManipulationLogic(controller);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
// view manipulation logic
private void viewManipulationLogic(Controller controller) {
controller.getBlueButton().setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
System.out.println(" I am just about button!");
}
});
}
Controller :
public class Controller implements Initializable {
#FXML
private Button blueButton;
public Button getBlueButton() {
return blueButton;
}
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
//real data manipulation
}
}
cons : You need getters for all Nodes u want to manipulate , in controller class.

Categories

Resources