I have an app in JavaFX with .FXML file and i add a button to the scene. Then i tried to add accelerator to it but when it launches it throws NullPointerException. Why it doesn't work and how to solve this.
#FXML
Button addQuickNote;
#FXML
public void handlequickNote(ActionEvent e) {
String text = SampleController.getSelectedText();
if (text != null) {
SampleController.profileManager.insertNote(DataParser.getNote(text));
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
addQuickNote.getScene().getAccelerators().put(new KeyCodeCombination(KeyCode.Q, KeyCombination.SHORTCUT_DOWN), new Runnable() {
#Override
public void run() {
addQuickNote.fire();
}
});
}
My .fxml is pretty complicated because it contains whole module for my app so i paste only a line with the button. The button is placed in the ToolBar.
<Button fx:id="addQuickNote" mnemonicParsing="false" onAction="#handlequickNote" prefWidth="77.0" text="Z tekstu" />
I'm loading .fxml as a part of main scene. I'm doing this by this code.
try {
panel = FXMLLoader.load(getClass().getResource("Notes.fxml"));
} catch (IOException ex) {
showErrorDialog ....;
}
rightPanel.getChildren().add(panel);
mainPanel.setRight(rightPanel);
As user714965 mentionend your Scene was not constructed fully yet and therefor addQuickNote.getScene() is null.
Another solution might be something like this:
#Override
public void initialize(URL url, ResourceBundle rb) {
Platform.runLater(() -> {
addQuickNote.getScene().getAccelerators().put(new KeyCodeCombination(KeyCode.Q, KeyCombination.SHORTCUT_DOWN), () -> {
addQuickNote.fire();
});
});
}
I guess that addQuickNote.getScene() is null because your controls are not fully initialized at this point and the Button just has no Scene set.
Solve this by not calling addQuickNote.getScene().getAccelerators()... in the initialize method. After your Controller-initialization in your main-method do another call to your controller to a method in which you are initialize your accelerators.
EDIT:
Your start method seems to be incomplete. It have to look something like this:
#Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader();
AnchorPane page = (AnchorPane) loader.load(getClass().getResourceAsStream("MainScene.fxml"));
Scene scene = new Scene(page);
MainSceneController controller = loader.getController();
controller.initializeAccelerators();
primaryStage.setScene(scene);
primaryStage.show();
}
Related
I have a bunch of fxml windows that I built in SceneBuilder for javafx. When I switch between my settings window and my main menu the sliders (for various sound levels) reset to their original value, however i would want them to keep the values they were at last time the window was open.
I've tried settings values in initialise and settings a variable through the class that is changed and used to set the sliders when it is launched, but that didn't work either.
The FXML
<!-- The slider code (scenebuilder forces me to choose a value) ->
<Slider fx:id="soundfxSlider" onMouseDragged="#updateSFX" showTickLabels="true" showTickMarks="true" snapToTicks="true" value="50.0" />
<Slider fx:id="musicSlider" minorTickCount="1" onMouseDragged="#updateMusic" showTickLabels="true" showTickMarks="true" snapToTicks="true" />
The method for launching accessed by the main menu and other windows when switching
static void launchScreen(String fileName) {
fileName = "/screens/" + fileName + ".fxml";
try {
Parent root = FXMLLoader.load(MainMenuController.class.getResource(fileName));
Scene scene = new Scene(root);
stage.setTitle("Fortress Combat");
stage.setScene(scene);
stage.show();
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(420);
});
} catch (IOException e) {
e.printStackTrace();
}
}
The settings controller
public class SettingsController {
#FXML // fx:id="soundfxSlider"
private Slider soundfxSlider; // Value injected by FXMLLoader
#FXML // fx:id="musicSlider"
private Slider musicSlider; // Value injected by FXMLLoader
#FXML
void updateMusic(MouseEvent event) {
double musicVolume = musicSlider.getValue();
//setMusicSlider(musicVolume);
Launcher.adjustVolume(musicVolume);
}
#FXML
void updateSFX(MouseEvent event) {
double vol = soundfxSlider.getValue();
//setSoundfxSlider(vol);
Launcher.adjustSfx(vol);
}
private void setMusicSlider(double sliderVal) {
musicSlider.setValue(sliderVal);
}
private void setSoundfxSlider(double sfxVal) {
soundfxSlider.setValue(sfxVal);
}
#FXML
void playTestSfx(ActionEvent event) {
Launcher.playTestSFX();
}
#FXML
void goBack(ActionEvent event) {
Launcher.launchScreen("main_menu");
}
#FXML // This method is called by the FXMLLoader when initialization is complete
void initialize() {
assert soundfxSlider != null : "fx:id=\"soundfxSlider\" was not injected: check your FXML file 'settings.fxml'.";
assert musicSlider != null : "fx:id=\"musicSlider\" was not injected: check your FXML file 'settings.fxml'.";
}
If no value is given to the sliders in the fxml they take the default of 0 - it seems that a default value is forced and it doesn't "remember" when windows are switched.
I believe I understand why you get the default values on your slider all the time that you open up a new window or rather loading a new window.
If you take a look at the code below we see that you create a new parent by loading your FXML with the name that is given as an argument, after which you set the new Scene to your stage. The key thing to note from this is that you create a new parent and scene which has no idea about any values set you your sliders in other scenes.
static void launchScreen(String fileName) {
fileName = "/screens/" + fileName + ".fxml";
try {
Parent root =
FXMLLoader.load(MainMenuController.class.getResource(fileName));
Scene scene = new Scene(root);
stage.setTitle("Fortress Combat");
stage.setScene(scene);
stage.show();
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(420);
});
} catch (IOException e) {
e.printStackTrace();
}
}
A few suggestions:
Save the actual scene and then if the filename argument corresponds to an already created scene then you can really "switch" scene instead of creating a new one.
Below is a quickly made example with an hard coded if statement which is just to show you what I mean, but I'm sure you could solve it better than that and with less duplicated code.
private Scene sceneA;
static void launchScreen(String fileName) {
if(fileName.equals("sceneA") && sceneA != null){
/*if we want to open up sceneA and it has been created before (meaning it's not null) then open the already existing scene.*/
stage.setTitle("Fortress Combat");
stage.setScene(sceneA);
stage.show();
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(420);
});
}else{
fileName = "/screens/" + fileName + ".fxml";
try {
Parent root =
FXMLLoader.load(MainMenuController.class.getResource(fileName));
Scene scene = new Scene(root);
stage.setTitle("Fortress Combat");
stage.setScene(scene);
stage.show();
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(420);
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
Suggestion 2 is to store them somewhere, maybe in some handler class or in a file if you wish for them to be persistent.
or suggestion 3 which could be to maybe declare some static variables in your controller which you set to the values of your sliders when the slider values have changed. Then you may set the slider values in your initialize method by accessing your static variables.
Anyway, those were my thoughts and I hope that I could be of some help.
Let us know how it works out :)
I'm making an application with JavaFX and Scene Builder.
I have two controllers:Controller and FontController
I have Main class that launch my program and open Stage with first fontroller (Controller)
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
try {
Parent root = FXMLLoader.load(getClass().getResource("/card/card.fxml"));
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("card/resources/logo-icon.png"));
primaryStage.show();
//adding resize and drag primary stage
ResizeHelper.addResizeListener(primaryStage);
//assign ALT+ENTER to maximize window
final KeyCombination kb = new KeyCodeCombination(KeyCode.ENTER, KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (kb.match(event)) {
primaryStage.setMaximized(!primaryStage.isMaximized());
primaryStage.setResizable(true);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
There is a label and a button in Controller. When I click on the button a method is called and new window with second controller appears(FontController):
#FXML private Button btnFont;
#FXML private Label category1
#FXML
void changeFont(ActionEvent event) {
try {
FXMLLoader fxmlLoader = new
FXMLLoader(getClass().getResource("font.fxml"));
Parent rootFont = (Parent) fxmlLoader.load();
Stage stage = new Stage();
stage.setTitle("Select Font");
stage.setScene(new Scene(rootFont));
stage.show();
} catch (Exception e) {
System.out.println("can't load new window");
}
}
There is the button "OK" and label in FontCOntroller:
#FXML private Label fontLabel;
#FXML private Button btnFontOk;
Please tell me, what should I do to send and apply text from label in FontController when I click on the burtton "OK" to label in Controller?
SOLUTION FOUND:
I created class "Context" in my project directory to make all controllers communicate each other. You can add as many controllers as you want there.
Here it looks like:
package card;
public class Context {
private final static Context instance = new Context();
public static Context getInstance() {
return instance;
}
private Controller controller;
public void setController(Controller controller) {
this.controller=controller;
}
public Controller getController() {
return controller;
}
private FontController fontController;
public void setFontController(FontController fontController) {
this.fontController=fontController;
}
public FontController getFontController() {
return fontController;
}
}
Controller:
I created getters and setters (ALT + Insert in IDEA) for Label that I wanna change
public Label getCategory1() {
return category1;
}
public void setCategory1(Label category1) {
this.category1 = category1;
}
To get FontController variables and methods through Context class I placed line of code
//getting FontController through Context Class
FontController fontCont = Context.getInstance().getFontController();
I registered Controller in Context class through my initialize method (my class implements Initializable)
#FXML
public void initialize(URL location, ResourceBundle resources) {
//register Controller in Context Class
Context.getInstance().setController(this);
}
FontController:
to get Controller variables and methods I placed this code:
//getting Controller variables and methods through Context class
Controller cont = Context.getInstance().getController();
I also registered FontController in Context class through initialize method:
#Override
public void initialize(URL location, ResourceBundle resources) {
//register FontController in Context Class
Context.getInstance().setFontController(this);
}
Method that send text and text color from label in this FontController to label in Controller when I click on button:
#FXML
void applyFont(ActionEvent event) {
cont.getCategory1().setText(fontLabel.getText());
cont.getCategory1().setTextFill(fontLabel.getTextFill());
}
*By creating Context class you can make controllers communicate each other and create as many controllers as you want there. Controllers see variables and methods of each other
I'm creating an JavaFX application with Scene Builder. I added a video at the beginning. So I wanna play video before my application start in fullscreen mode.
The Problem is when it is stopped I see only black screeen and nothing happened, I guess it is because video is fullscreen and it is not automatically closed.
I also have a bug before the video starts, some blink of my main window .I guess it is because video is placed in the controller that begins after my application starts.
How to close video or remove it after finish?
How to place video in main class?
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("resources/fxml/card.fxml"));
Parent root = 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("src/card/resources/logo-icon.png"));
primaryStage.show();
//adding resize and drag primary stage
ResizeHelper.addResizeListener(primaryStage);
//assign ALT+ENTER to maximize window
final KeyCombination kb = new KeyCodeCombination(KeyCode.ENTER,
KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event) {
if (kb.match(event)) {
primaryStage.setMaximized(!primaryStage.isMaximized());
primaryStage.setResizable(true);
Controller cont = Context.getInstance().getController();
if (!primaryStage.isMaximized()) {
cont.getBtnFont().setPrefWidth(20);
cont.getBtnPalette().setPrefWidth(20);
cont.getBtnQuestCards().setPrefWidth(20);
cont.getBtnNonQuestCards().setPrefWidth(20);
cont.getRandomCard().setTopAnchor(cont.getRandomCard(), 80.0);
cont.getRandomCard().setBottomAnchor(cont.getRandomCard(), 70.0);
cont.getRandomCard().setLeftAnchor(cont.getRandomCard(), 300.0);
cont.getRandomCard().setRightAnchor(cont.getRandomCard(), 200.0);
cont.getRandomCardBack().setTopAnchor(cont.getRandomCardBack(), 80.0);
cont.getRandomCardBack().setBottomAnchor(cont.getRandomCardBack(), 70.0);
cont.getRandomCardBack().setLeftAnchor(cont.getRandomCardBack(), 300.0);
cont.getRandomCardBack().setRightAnchor(cont.getRandomCardBack(), 200.0);
cont.getRectRandom().setWidth(1060);
cont.getRectRandom().setHeight(410);
cont.getRectRandomBack().setWidth(1060);
cont.getRectRandomBack().setHeight(410);
cont.getRandomCard().setPrefWidth(800);
cont.getRandomCard().setPrefHeight(200);
cont.getRandomCardBack().setPrefWidth(800);
cont.getRandomCardBack().setPrefHeight(200);
} else if (primaryStage.isMaximized()){
cont.getBtnFont().setPrefWidth(50);
cont.getBtnPalette().setPrefWidth(50);
cont.getBtnQuestCards().setPrefWidth(50);
cont.getBtnNonQuestCards().setPrefWidth(50);
cont.getRandomCard().setTopAnchor(cont.getRandomCard(), 150.0);
cont.getRandomCard().setBottomAnchor(cont.getRandomCard(), 130.0);
cont.getRandomCard().setLeftAnchor(cont.getRandomCard(), 450.0);
cont.getRandomCard().setRightAnchor(cont.getRandomCard(), 270.0);
cont.getRandomCardBack().setTopAnchor(cont.getRandomCardBack(), 150.0);
cont.getRandomCardBack().setBottomAnchor(cont.getRandomCardBack(), 130.0);
cont.getRandomCardBack().setLeftAnchor(cont.getRandomCardBack(), 450.0);
cont.getRandomCardBack().setRightAnchor(cont.getRandomCardBack(), 270.0);
cont.getRectRandom().setWidth(1160);
cont.getRectRandom().setHeight(760);
cont.getRectRandomBack().setWidth(1160);
cont.getRectRandomBack().setHeight(760);
cont.getRandomCard().setPrefWidth(800);
cont.getRandomCard().setPrefHeight(400);
cont.getRandomCardBack().setPrefWidth(800);
cont.getRandomCardBack().setPrefHeight(400);
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
Controller CLass:
#FXML
public void initialize(URL location, ResourceBundle resources) {
String path = new File("src/card/resources/intro.mp4").getAbsolutePath();
me = new Media(new File(path).toURI().toString());
mp = new MediaPlayer(me);
media.setMediaPlayer(mp);
mp.setAutoPlay(true);
media.setSmooth(true);
}
I'm not sure about the blink you see, but it should be easy to react when the video stops.
The JavaFX MediaPlayer class provides a method statusProperty() which returns an object of type ReadOnlyObjectProperty<MediaPlayer.Status>.
The class ReadOnlyObjectProperty<T> implements the interface ObservableValue<T> which provides the method addListener(ChangeListener<? super T>).
The interface ChangeListener<T> is a "functional interface" which requires an implementation for the method void changed(ObservableValue<T> observable, T oldValue, T newValue).
So, putting it together, you should be able to create a listener which will react to the changing status of your MediaPlayer:
private void addStatusListener(MediaPlayer mp) {
ReadOnlyObjectProperty<MediaPlayer.Status> statusProperty = mp.
statusProperty();
statusProperty.addListener((v, o, n) -> playerStatusChanged(v, o, n));
}
private void playerStatusChanged(
ObservableValue<? extends MediaPlayer.Status> observable,
MediaPlayer.Status oldValue, MediaPlayer.Status newValue) {
if (oldValue == MediaPlayer.Status.PLAYING &&
newValue == MediaPlayer.Status.STOPPED) {
// TODO: CALL METHOD TO REACT TO VIDEO BEING STOPPED.
}
}
SOLUTION FOUND!
I made animation to opacity of MediaView:
#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();
}
I have an fxml that describes my gui. I want to change text of the gui and start a task on any key press anywhere.
FXML
<Text fx:id="barcodeText"/>
Controller
#FXML
Text barcodeText;
public void start(Stage primaryStage) throws IOException {
this.primaryStage=primaryStage;
Scene mainScene =new Scene(root);
primaryStage.setScene(mainScene);
primaryStage.setResizable(false);
primaryStage.show();
Parent root = FXMLLoader.load(getClass().getResource("/view/foo.fxml"));
mainScene.addEventHandler(KeyEvent.KEY_PRESSED,new KeyboardEventHandler(){
#Override
public void handle(KeyEvent event) {
barcodeText.setText("foo");
}
});
This gives me a NullPointerException(inside JavaFX Application Thread) for the barcodeText pointer when I fire the event.
Am I doing something wrong?
The examples I looked at were using this approach without fxml, do I have to use an annotation to define the handler? where would I put "onAction" for the scene in the fxml?
(Aside: it looks like you are trying to use the same class for the controller, and for the application. Don't do that.)
Define a method in the controller class for setting the barcode text:
public void setBarcodeText(String barcode) {
barcodeText.setText(barcode);
}
Then call that method from your handler:
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/foo.fxml"));
Parent root = loader.load();
MyControllerClass controller = loader.getController();
Scene mainScene = new Scene(root);
mainScene.addEventHandler(KeyEvent.KEY_PRESSED, new KeyboardEventHandler(){
#Override
public void handle(KeyEvent event) {
controller.setBarcodeText("foo");
}
});
Obviously, replace MyControllerClass with the actual name of the controller class.
I am just beginning to learn JAVAFX and I have run into a problem now. I have a login screen and after I clicked login, a dialog box appeared and the problem is I don't know how to eliminate the login screen after the dialog has showed up. Please help me. This is my code
Main.java (contains login screen)
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("../view/LoginScreen.fxml"));
primaryStage.setTitle("Weltes Mart O2 Tank Module");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
LoginController.java (showing a dialog box)
public class LoginController {
#FXML private Text loginStatusMessage;
#FXML private Button btnLogin;
#FXML public void handleLoginButton(ActionEvent event){
System.out.println("BUTTON PRESSED");
try {
Parent root = FXMLLoader.load(getClass().getResource("../view/LoginSuccessDialog.fxml"));
Stage primaryStage = new Stage();
primaryStage.setScene(new Scene(root));
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
return;
}
}
}
You can use any Node in a Scene to get a reference to that scene. You can use a Scene to get the Window that contains it. You can close that window.
Assuming the Node fields are actually injected by the loader, you can close the Stage using this code:
btnLogin.getScene().getWindow().hide();