Im fairly new to both stackoverflow and javafx so please be nice.
Description of what Im doing:
I am making a simple quiz game. First window is just a like a welcome/start screen, when that button is clicked we get to the second screen where all the category buttons are, when one of them is clicked it will randomly pick a question of that kind of category user has selected and the third and last window will appear with the questions category as a label, question as textfield and answear as textfield.
Problem:
Whenever a category is clicked I need that current controller to set the next controllers textfields and label. I havent come to far with this. I just get a nullpointerexception when im calling the setQuestion method in the second controller, FXMLCategoriesDocumentController, when I tried to debug it it just says that the instantiated "questControll" is null all the time, and the "questControll.question/category/answear" is referenced from a null object
Code:
second controller
public class FXMLCategoriesDocumentController implements Initializable {
/**
* Initializes the controller class.
*/
private FXMLQuestionDocumentController questControll;
private Question quest;
#FXML
private void geografButtonAction(ActionEvent event) {
try {
FXMLLoader fxmlQuestLoader = new FXMLLoader(getClass().getResource("FXMLQuestionDocument.fxml"));
this.questControll = fxmlQuestLoader.<FXMLQuestionDocumentController>getController();
quest = new Question("Geografi", "Vad heter Sveriges huvudstad?", "Stockholm");
questControll.setQuestion(quest.getCategory(), quest.getQuestion(), quest.getAnswear());
Parent root1 = (Parent) fxmlQuestLoader.load();
root1.setId("pane");
Stage app_stage = (Stage)((Node) event.getSource()).getScene().getWindow();
Scene root1_scene = new Scene(root1);
root1_scene.getStylesheets().addAll(this.getClass().getResource("style.css").toExternalForm());
app_stage.hide();
app_stage.setScene(root1_scene);
app_stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
third controller
public class FXMLQuestionDocumentController implements Initializable {
private FXMLCategoriesDocumentController catControll;
private Question quest;
#FXML
public Label category = new Label();
#FXML
public TextField question = new TextField();
#FXML
public TextField answear = new TextField();
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
}
public void setQuestion(String cat, String quest, String ans){
if(category.getText() == null || question.getText() == null || answear.getText() == null){
System.out.println("everything is null");
}else{
category.setText(cat);
question.setText(quest);
answear.setText(ans);
}
}
Question class
public class Question {
private String category;
private String question;
private String answear;
public Question(String cat, String quest, String ans){
this.category = cat;
this.question = quest;
this.answear = ans;
}
public void setCategory(String cat){
this.category = cat;
}
public void setQuestion(String quest){
this.question = quest;
}
public void setAnswear(String ans){
this.answear = ans;
}
public String getCategory(){
return category;
}
public String getQuestion(){
return question;
}
public String getAnswear(){
return answear;
}
}
FXML second controller(category)
category xml
FXML third controller(question)
question xml
You can specify the the class of controller in each XML-File by using the fx:controller-Tag (here) in the highest element of your (f)xml-Tree.
You can load it then by using this:
YourCustomController controller;
//some code...
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("FXMLQuestionDocument.fxml"));
controller = loader.<YourCustomController>getController();
//assume that we create a question here
Question q = new Question(...);
controller.setQuestion(q);
}
//further code...
Edit after comments:
As fabian said, attributes annotated with #FXML are created before the initialize()-method if the fx:id-tag is set for an element in your FXML-file. The fx:id-tag must be the same as the attribute in your controller.
In you fxml-file (for example a Label):
<Label fx:id="question" ...>
....
</Label>
In your Controller-class:
public class YourCustomController implements Initializable {
#FXML Label question;
//...
public void initialize() {
//...
}
public void setQuestion(Question q) {
question.setText(q.getQuestion();
}
}
For further information, see the link in jewelseas comment.
I tried this tomorrow and I hope I transferred it here the right way.
Edit from July, 13th
In your FXMLQuestionDocumentController, you don't need to initiliaze your controls. See:
public class FXMLQuestionDocumentController implements Initializable {
private FXMLCategoriesDocumentController catControll;
private Question quest;
#FXML
public Label category;
#FXML
public TextField question;
#FXML
public TextField answear;
}
Also, initialize your controller after you initialized your Pane.
`#FXML
private void geografButtonAction(ActionEvent event) {
try {
FXMLLoader fxmlQuestLoader = new FXMLLoader(getClass().getResource("FXMLQuestionDocument.fxml"));
quest = new Question("Geografi", "Vad heter Sveriges huvudstad?", "Stockholm");
Parent root1 = (Parent) fxmlQuestLoader.load();
root1.setId("pane");
this.questControll = fxmlQuestLoader.FXMLQuestionDocumentController>getController();
questControll.setQuestion(quest.getCategory(), quest.getQuestion(), quest.getAnswear());
Stage app_stage = (Stage)((Node) event.getSource()).getScene().getWindow();
Scene root1_scene = new Scene(root1);
root1_scene.getStylesheets().addAll(this.getClass().getResource("style.css").toExternalForm());
app_stage.hide();
app_stage.setScene(root1_scene);
app_stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}`
Related
I have this method in my application which is constructing RadioButtons using an helper class: Mode. I would like to call out of the createModesRadios method .getText() for the user's selected RadioButton. Also I would like to save the user's choices in order to remember them in a following use. Is there an easy way to call them and set the choices into the CheckBoxes, into the RadioButtons and into the PrefixSelectionComboBoxs?
I am using a Model Class Configuration.java in order to store the information, because I will need to process some calculations with the user inputs. I would like to store in it also the choices made from the controllers mentioned above.
Part of my Class
public class ConfigurationEditDialogController {
// General Tab
#FXML
private TextField configurationNameField;
#FXML
private TextField commentField;
#FXML
private TextField creationTimeField;
#FXML
private TextField startAddress;
#FXML
private ToggleSwitch triggerEnable;
#FXML
private ToggleSwitch softwareTrigger;
#FXML
private ToggleSwitch ctsEnable;
// Data Acquisition Tab
#FXML
private Pane dataAcquisitionTab;
#FXML
private Button okButton;
private Mode[] modes = new Mode[]{
new Mode("Vertical", 4),
new Mode("Hybrid", 2),
new Mode("Horizontal", 1)
};
private int count = Stream.of(modes).mapToInt(Mode::getCount).max().orElse(0);
private ToggleGroup group = new ToggleGroup();
private IntegerProperty elementCount = new SimpleIntegerProperty();
private ObservableMap<Integer, String> elements = FXCollections.observableHashMap();
CheckBox[] checkBoxes = new CheckBox[count];
private ObservableList<String> options = FXCollections.observableArrayList(
"ciao",
"hello",
"halo");
private Stage dialogStage;
private Configuration configuration;
private boolean okClicked = false;
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*/
#FXML
private void initialize() {
HBox radioBox = createModesRadios(elementCount, modes);
GridPane grid = new GridPane();
VBox root = new VBox(20, radioBox);
root.setPrefSize(680, 377);
grid.setHgap(40);
grid.setVgap(30);
grid.setAlignment(Pos.CENTER);
elementCount.addListener((o, oldValue, newValue) -> {
// uncheck checkboxes, if too many are checked
updateCheckBoxes(checkBoxes, newValue.intValue(), -1);
});
for (int i = 0; i < count; i++) {
final Integer index = i;
CheckBox checkBox = new CheckBox();
checkBoxes[i] = checkBox;
PrefixSelectionComboBox<String> comboBox = new PrefixSelectionComboBox<>();
comboBox.setItems(options);
comboBox.setPromptText("Select Test Bus " + i);
comboBox.setPrefWidth(400);
comboBox.setVisibleRowCount(options.size() -1);
comboBox.valueProperty().addListener((o, oldValue, newValue) -> {
// modify value in map on value change
elements.put(index, newValue);
});
comboBox.setDisable(true);
checkBox.selectedProperty().addListener((o, oldValue, newValue) -> {
comboBox.setDisable(!newValue);
if (newValue) {
// put the current element in the map
elements.put(index, comboBox.getValue());
// uncheck checkboxes that exceed the required count keeping the current one unmodified
updateCheckBoxes(checkBoxes, elementCount.get(), index);
} else {
elements.remove(index);
}
});
grid.addRow(i, comboBox, checkBox);
}
// enable Ok button iff the number of elements is correct
okButton.disableProperty().bind(elementCount.isNotEqualTo(Bindings.size(elements)));
root.getChildren().add(grid);
dataAcquisitionTab.getChildren().add(root);
}
/**
* Create Radio Buttons with Mode class helper
*
* #param count
* #param modes
* #return
*/
private HBox createModesRadios(IntegerProperty count, Mode... modes) {
ToggleGroup group = new ToggleGroup();
HBox result = new HBox(50);
result.setPadding(new Insets(20, 0, 0, 0));
result.setAlignment(Pos.CENTER);
for (Mode mode : modes) {
RadioButton radio = new RadioButton(mode.getText());
radio.setToggleGroup(group);
radio.setUserData(mode);
result.getChildren().add(radio);
radio =
}
if (modes.length > 0) {
group.selectToggle((Toggle) result.getChildren().get(0));
count.bind(Bindings.createIntegerBinding(() -> ((Mode) group.getSelectedToggle().getUserData()).getCount(), group.selectedToggleProperty()));
} else {
count.set(0);
}
return result;
}
/**
* Method for updating CheckBoxes depending on the selected modek
*
* #param checkBoxes
* #param requiredCount
* #param unmodifiedIndex
*/
private static void updateCheckBoxes(CheckBox[] checkBoxes, int requiredCount, int unmodifiedIndex) {
if (unmodifiedIndex >= 0 && checkBoxes[unmodifiedIndex].isSelected()) {
requiredCount--;
}
int i;
for (i = 0; i < checkBoxes.length && requiredCount > 0; i++) {
if (i != unmodifiedIndex && checkBoxes[i].isSelected()) {
requiredCount--;
}
}
for (; i < checkBoxes.length; i++) {
if (i != unmodifiedIndex) {
checkBoxes[i].setSelected(false);
}
}
}
/**
* Sets the stage of this dialog.
*
* #param dialogStage
*/
public void setDialogStage(Stage dialogStage) {
this.dialogStage = dialogStage;
}
/**
* Sets the configuration to be edited in the dialog.
*
* #param configuration
*/
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
configurationNameField.setText(configuration.getConfigurationName());
commentField.setText(configuration.getComment());
//Set the comboboxes and the checkboxes and the radiobutton
creationTimeField.setText(LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm")));
}
/**
* Returns true if the user clicked OK, false otherwise.
*
* #return
*/
public boolean isOkClicked() {
return okClicked;
}
/**
* Called when the user clicks ok.
*/
#FXML
private void handleOk() {
if (isInputValid()) {
configuration.setConfigurationName(configurationNameField.getText());
configuration.setComment(commentField.getText());
configuration.setTestBus1(elements.get(0));
configuration.setTestBus2(elements.get(1));
configuration.setTestBus3(elements.get(2));
configuration.setTestBus4(elements.get(3));
configuration.setCreationTime(DateTimeUtil.parse(creationTimeField.getText()));
// Store the information of the sample mode (vertical, hybrid or horizontal).
okClicked = true;
dialogStage.close();
}
}
Mode Helper Class
public class Mode {
private final String text;
private final int count;
public Mode(String text, int count) {
this.text = text;
this.count = count;
}
public String getText() {
return text;
}
public int getCount() {
return count;
}
}
Configuration.java
public class Configuration {
private final StringProperty configurationName;
private final StringProperty comment;
private final StringProperty testBus1;
private final StringProperty testBus2;
private final StringProperty testBus3;
private final StringProperty testBus4;
private final StringProperty triggerSource;
private final StringProperty sampleMode;
private final StringProperty icDesign;
private final StringProperty triggerMode;
private final DoubleProperty acquisitionTime;
private final ObjectProperty<LocalDateTime> creationTime;
/**
* Default constructor.
*/
public Configuration() {
this(null, null);
}
/**
* Constructor with some initial data.
*
* #param configurationName
* #param comment
*/
public Configuration(String configurationName, String comment) { //todo initialize null values
this.configurationName = new SimpleStringProperty(configurationName);
this.comment = new SimpleStringProperty(comment);
// Some initial sample data, just for testing.
this.testBus1 = new SimpleStringProperty("");
this.testBus2 = new SimpleStringProperty("");
this.testBus3 = new SimpleStringProperty("");
this.testBus4 = new SimpleStringProperty("");
this.triggerSource = new SimpleStringProperty("");
this.sampleMode = new SimpleStringProperty("");
this.icDesign = new SimpleStringProperty("Ceres");
this.triggerMode = new SimpleStringProperty("Internal");
this.acquisitionTime = new SimpleDoubleProperty(2346.45);
this.creationTime = new SimpleObjectProperty<>(LocalDateTime.of(2010, 10, 20,
15, 12));
}
/**
* Getters and Setters for the variables of the model class. '-Property' methods for the lambdas
* expressions to populate the table's columns (just a few used at the moment, but everything
* was created with scalability in mind, so more table columns can be added easily)
*/
// If more details in the table are needed just add a column in
// the ConfigurationOverview.fxml and use these methods.
// Configuration Name
public String getConfigurationName() {
return configurationName.get();
}
public void setConfigurationName(String configurationName) {
this.configurationName.set(configurationName);
}
public StringProperty configurationNameProperty() {
return configurationName;
}
// Comment
public String getComment() {
return comment.get();
}
public void setComment (String comment) {
this.comment.set(comment);
}
public StringProperty commentProperty() {
return comment;
}
// Test Bus 1
public String getTestBus1() {
return testBus1.get();
}
public void setTestBus1(String testBus1) {
this.testBus1.set(testBus1);
}
public StringProperty testBus1Property() {
return testBus1;
}
// Test Bus 2
public String getTestBus2() {
return testBus2.get();
}
public void setTestBus2(String testBus2) {
this.testBus2.set(testBus2);
}
public StringProperty testBus2Property() {
return testBus2;
}
// Test Bus 3
public String getTestBus3() {
return testBus3.get();
}
public void setTestBus3(String testBus3) {
this.testBus3.set(testBus3);
}
public StringProperty testBus3Property() {
return testBus3;
}
// Test Bus 4
public String getTestBus4() {
return testBus4.get();
}
public void setTestBus4(String testBus4) {
this.testBus4.set(testBus4);
}
public StringProperty testBus4Property() {
return testBus4;
}
// Trigger Source
public String getTriggerSource(){
return triggerSource.get();
}
public void setTriggerSource (String triggerSource){
this.triggerSource.set(triggerSource);
}
public StringProperty triggerSourceProperty() {
return triggerSource;
}
// Sample Mode
public String getSampleMode() {
return sampleMode.get();
}
public void setSampleMode (String sampleMode){
this.sampleMode.set(sampleMode);
}
public StringProperty sampleModeProperty() {return sampleMode;}
// IC Design
public String getIcDesign() {
return icDesign.get();
}
public void setIcDesign (String icDesign){
this.icDesign.set(icDesign);
}
public StringProperty icDesignProperty() {
return icDesign;
}
// Trigger Mode
public String getTriggerMode() {
return triggerMode.get();
}
public void setTriggerMode (String triggerMode){
this.triggerMode.set(triggerMode);
}
public StringProperty triggerModeProperty() {return triggerMode;}
// Acquisition Time
public double getAcquisitionTime() {
return acquisitionTime.get();
}
public void setAcquisitionTime(double acquisitionTime){
this.acquisitionTime.set(acquisitionTime);
}
public DoubleProperty acquisitionTimeProperty() {
return acquisitionTime;
}
// Last modified - Creation Time
#XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
public LocalDateTime getCreationTime() {
return creationTime.get();
}
public void setCreationTime(LocalDateTime creationTime){
this.creationTime.set(creationTime);
}
public ObjectProperty<LocalDateTime> creationTimeProperty() {
return creationTime;
}
}
You can use the java.util.Properties class to easily read/write settings like this to/from an XML file.
Save To Properties
Properties props = new Properties();
// Set the properties to be saved
props.setProperty("triggerMode", triggerMode.get());
props.setProperty("acquisitionTime", String.valueOf(acquisitionTime.get()));
// Write the file
try {
File configFile = new File("config.xml");
FileOutputStream out = new FileOutputStream(configFile);
props.storeToXML(out,"Configuration");
} catch (IOException e) {
e.printStackTrace();
}
This creates the config.xml file and populates it with all the properties you set using the props.setProperty() method.
Within the props.setProperty() method, you have two parameters. The first is the name of the property, the second is the actual value. The name is important as you will use it to read the appropriate value later.
The above code outputs the following XML file:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Configuration</comment>
<entry key="acquisitionTime">12.0</entry>
<entry key="triggerMode">My Trigger Mode</entry>
</properties>
Read from Properties
Reading from the XML file is just as simple. Use the loadFromXML() method:
FileInputStream in;
// Load the settings file
in = new FileInputStream(DataFiles.LOCAL_SETTINGS_FILE);
properties.loadFromXML(in);
From there, you can set your Configuration model object by getting each property:
configuration.setAcquisitionTime(props.getProperty("acquisitionTime", "0.0"));
configuration.setTriggerMode(props.getProperty("triggerMode", "Manual"));
The getProperty() method also takes two parameters. The first is obviously the name of the property we saved earlier. The second is the default value to use if the requested property name does not exist in the XML file.
Pretty simple!
I do recommend updating all the values in your Configuration.java model and saving properties from there (instead of trying to save the properties directly from your textfields, comboboxes, etc).
EDIT
In order to retrieve the values from your controls within the for loop, you need to add them to a list that is accessible from outside that loop.
Create appropriate lists to hold the controls as their created:
List<PrefixSelectionComboBox<String>> comboBoxes = new ArrayList<PrefixSelectionComboBox<String>>();
List<CheckBox> checkBoxes = new ArrayList<>();
At the bottom of your loop, add the new controls to these lists:
comboBoxes.add(comboBox);
checkBoxes.add(checkBox);
grid.addRow(i, comboBox, checkBox);
Now, when you need their values, you can iterate over those lists to extract them:
for (PrefixSelectionComboBox cb : comboBoxes) {
cb.getValue(); // Do with this what you must
}
for (CheckBox cb : checkBoxes) {
cb.isSelected();
}
I'm trying to display playing cards in a FlowPane. I have a main layout and a nested layout. For some reason when I debug IntelliJ reports that all fields, on both controllers, annotated with #FXML are null.
Here's a shortened version of what I've got thus far. Full Code on GitHub:
MainWindow.fxml
<BorderPane fx:controller="controller.MainWindowController">
<center>
<fx:include fx:id="tableScene" source="TableScene.fxml"/>
</center>
</BorderPane>
MainWindowController.java
public class MainWindowController implements Initializable {
#FXML
MenuBar menuBar;
#FXML
Menu fileMenu;
[...] more fields
#Override
public void initialize(URL location, ResourceBundle resources) {
// nothing here in my code
}
}
TableScene.fxml
<AnchorPane fx:controller="controller.TableSceneController">
<children>
<FlowPane fx:id="dealerHandFlowPane"></FlowPane>
<FlowPane fx:id="playerHandFlowPane"></FlowPane>
</children>
</AnchorPane>
TableSceneController
public class TableSceneController implements Initializable {
#FXML
private FlowPane dealerHandFlowPane;
#FXML
private FlowPane playerHandFlowPane;
public void displayInitialHand(Player player) {
var cards = new ArrayList<>(player.getHand().getCards());
for (BlackjackCard card : cards) {
if(player.getName().equals("Dealer")) {
dealerHandFlowPane.getChildren().add(new ImageView(getCardFace(card)));
} else {
playerHandFlowPane.getChildren().add(new ImageView(getCardFace(card)));
}
}
}
public void displayHand(Player player) {
var cards = new ArrayList<>(player.getHand().getCards());
}
public Image getCardFace(BlackjackCard card) {
return new Image("/images/cards/" + card.getRank().getLetter()
+ card.getSuit().getLetter() + ".png");
}
public Image getCardBack() {
String color[] = {"blue","red"};
String design = "123";
return new Image("/images/backs/" + color[0] + design.charAt(2));
}
#Override
public void initialize(URL location, ResourceBundle resources) {
// nothing here in my code either
}
}
BlackjackMain
public class BlackjackMain extends Application {
private final String MAIN_WINDOW_PATH = "/fxml/MainWindow.fxml";
private final String ICON_PATH = "/images/blackjack_icon.png";
private final String MAIN_STYLE_PATH = "/css/MainWindow.css";
private final String TABLE_STYLE_PATH = "/css/TableScene.css";
private final Image MAIN_ICON = new Image(getClass().getResourceAsStream(ICON_PATH));
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Blackjack");
// close the app gracefully when the 'X' is clicked
primaryStage.setOnCloseRequest(e -> Platform.exit());
primaryStage.centerOnScreen();
primaryStage.setResizable(false);
initializeMainWindow(primaryStage);
primaryStage.getIcons().add(MAIN_ICON);
primaryStage.show();
primaryStage.toFront();
initializeGame();
}
public void initializeMainWindow(Stage primaryStage) {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource(MAIN_WINDOW_PATH));
try {
Parent mainWindow = loader.load();
Scene scene = new Scene(mainWindow,600,600);
scene.getStylesheets().add(TABLE_STYLE_PATH);
primaryStage.setScene(scene);
} catch (IOException e) {
System.err.println("There was a problem loading /fxml/MainWindow.fxml");
e.printStackTrace();
}
}
public void initializeGame() {
var tableSceneController = new TableSceneController();
var mainWindowController = new MainWindowController();
Dealer dealer = new Dealer();
List<Player> allPlayers = new ArrayList<>();
var playerName = tableSceneController.getPlayerName();
allPlayers.add(new BlackjackPlayer(playerName));
BlackjackGame game = new BlackjackGame(dealer, allPlayers,
mainWindowController, tableSceneController);
game.playGame();
}
}
BlackjackGame.java
public class BlackjackGame implements BlackjackGameRules {
private List<Player> playerList;
private Deck deck;
private Shoe shoe;
private final TableSceneController tableSceneController;
private final MainWindowController mainWindowController;
public BlackjackGame(Dealer dealer, List<Player> players,
final MainWindowController mainWindowController,
final TableSceneController tableSceneController) {
Objects.requireNonNull(dealer,
"You must provide a dealer to begin the game.");
Objects.requireNonNull(players,
"You must provide a list of players to begin the game.");
playerList = new ArrayList<>();
this.tableSceneController = tableSceneController;
this.mainWindowController = mainWindowController;
// add dealer first for easier future access
playerList.add(dealer);
playerList.addAll(players);
deck = new Deck(BlackjackGameRules.NUMBER_OF_DECKS);
// place the shuffled deck in the shoe
shoe = new Shoe(deck.getDeck());
}
public void dealInitialCards() {
for (Player player : playerList) {
player.getHand().addCard(shoe.dealCard());
player.getHand().addCard(shoe.dealCard());
}
}
public boolean hasValidNumberOfPlayers() {
// this number includes the dealer
var numPlayers = playerList.size();
return numPlayers >= BlackjackGameRules.MIN_PLAYERS &&
numPlayers <= BlackjackGameRules.MAX_PLAYERS;
}
public List<Player> getPlayers() {
return new ArrayList<>(playerList);
}
public Shoe getShoe() {
return shoe;
}
public void playGame() {
dealInitialCards();
for(Player player: playerList) {
tableSceneController.displayInitialHand(player);
}
}
}
I get a NullPointerException on displayIntitialHand in TableSceneController. Here's the brief stacktrace:
Caused by: java.lang.NullPointerException
at blackjack.controller.TableSceneController.displayInitialHand(TableSceneController.java:35)
at blackjack.model.BlackjackGame.playGame(BlackjackGame.java:139)
at blackjack.controller.BlackjackMain.initializeGame(BlackjackMain.java:70)
at blackjack.controller.BlackjackMain.start(BlackjackMain.java:44)
For the life of me I cannot figure this one out. Where have I gone wrong? I have double checked that I've set the names of the controllers in the fx:controller attribues in the *.fxml files. I have also double checked that I have the fx:id attributes correct in the components and that they also match the #FXML annotations in the controller correctly.
My understanding of the process of JavaFX is:
that load() is supposed to load the *.fxml file
instantiate the controller (specified by the fx:controller attribute in the .fxml file)
Calls the no-arg constructor on the controller
Sets the #FXML values (by injection)
Registers any event handlers
Calls initialize on each controller
Is the problem with my nested fxml files? If this was the case I would think that the #FXML fields in MainWindowController.java would not also be null. I'm s truggling to figure this out. I could use another set of eyes and someone smarter than myself.
Thanks in advance.
Took a while to figure out, but when you create the controllers for your scenes, within initializeGame() you do:
var tableSceneController = new TableSceneController();
var mainWindowController = new MainWindowController();
What this means is you are creating a new instance of the controller, not the instance that is created when you load your FXML files within initializeMainWindow.
To remedy this, I'd suggest creating a class variable to hold each of your controllers, and then assign them when you load the FXML files.
So, in BlackJackMain.java, declare class variables
private TableSceneController tableSceneController;
private MainWindowController mainWindowController;
then when you load them, I can see you load the main window in initializeMainWindow, so add
mainWindowController = loader.getController();
to the try block, just after the loader.load line.
This resolves your null pointers for this scene, but I cannot figure out where or if you load the table scene FXML, and thus you don't have an instance of the controller to pass into your method. If you do load the file, apply the same logic to it to get an instance of that controller too.
public class SerialCommunicationController{
#FXML public Label tempReading;
#FXML public Label errorReading;
private final Logger logger = LoggerFactory.getLogger(getClass());
private ArduinoInterfaceControl arduinoInterfaceControl;
private Stage stage;
private String prop;
private String inte;
private String deri;
private boolean lastCommandSuccessful;
public static final String LOCAL_SIMULATOR = "Local Simulator";
private String[] params;
private ArduinoInterfaceControl arduinoInterface;
public SerialCommunicationController() {
}
public SerialCommunicationController(String cmd){
setValues(cmd);
}
public void setValues(String readings){
String[] params = readings.split("\\s+");
// something went wrong, just decode to blank.
if(params.length < 1) {
lastCommandSuccessful = false;
}
else {
tempReading.setText(params[0]);
errorReading.setText(params[1]);
// successful command received.
lastCommandSuccessful = Boolean.valueOf(params[1]);
}
}
}
Hi Guys i am getting NullPointException when trying to do tempReading.setText() and errorReading.setText(). I have defined default values of label text in my fxml file.
It seems that both tempReading and errorReading are null.
Perhaps binding to the FXML is incorrect?
Make sure the fx:id in the FXML file matches the names of your Label fields in the Java class.
I use the afterburner.fx framework for the javafx program as follows,
NameModel used for sharing the state between views or the implementation of business logic.
public class NameModel {
private final StringProperty name = new SimpleStringProperty();
public String getName() {
return name.get();
}
public void setName(String value) {
name.set(value);
}
public StringProperty nameProperty() {
return name;
}
}
SchoolPresenter I used to enter the value for NameModel.If the name in Model get value starting with character
starts with "A",I pass that name to ClassAPresenter and it is similar to names starting with character B, C ...for ClassB,ClassC...
public class SchoolPresenter implements Initializable {
#FXML
private StackPane stackPane;
#Inject NameModel nameModel;
private ClassAView classAView;
private ClassBView classBView;
private ClassCView classCView;
.............................
private ClassNView classNView;
#FXML
void txtName(ActionEvent event) {
String name=txtName.getText();
if (!name.isEmpty()) {
//My program runs fine not wrong but
//If I have N classes, and with declarations for the same classes below
//the program will be very confusing
//Please help me to collapse my code
if(name.startWith("A")){
if (classAView == null) {
classAView = new ClassAView();
ClassAPresenter classAPresenter = (ClassAPresenter) classAView.getPresenter();
classAPresenter.initNameModel(nameModel);
stackPane.getChildren().add(classAView.getView());
}
}else if(name.startWith("B")){
classBView = new ClassBView();
ClassBPresenter classAPresenter = (ClassBPresenter) classBView.getPresenter();
classBPresenter.initNameModel(nameModel);
stackPane.getChildren().add(classBView.getView());
}else if.............//
nameModel.setName(txtName.getText());
}
}
}
The ClassAPresenter, ClassBPresenter ...ClassNPresenter is structured as follows
public class ClassAPresenter implements Initializable {
private NameModel nameModel;
public void initDomainModel(NameModel nameModel) {
// ensure model is only set once:
if (this.nameModel != null) {
throw new IllegalStateException("Model can only be initialized once");
}
this.nameModel = nameModel;
}
}
To give some background: I now am able to load files onto my mp3 program and play them but all the values in my tableview are null?
My song class
package application;
//imports here
public class Song {
private String title;
private String artist;
private String album;
private SimpleStringProperty pTitle;
private SimpleStringProperty pArtist;
private SimpleStringProperty pAlbum;
private Media music;
private MediaPlayer mp;
private Image coverArt;
public Song(File file) {
music = new Media(file.toURI().toString());
music.getMetadata().addListener((Change<? extends String, ? extends Object> c) -> {
if (c.wasAdded()) {
if ("artist".equals(c.getKey())) {
System.out.println(c.getKey()+":"+c.getValueAdded());
this.pArtist = new SimpleStringProperty(c.getValueAdded().toString());
//pArtist.set(c.getValueAdded().toString());
artist = c.getValueAdded().toString();
} else if ("title".equals(c.getKey())) {
title = c.getValueAdded().toString();
System.out.println(c.getKey()+":"+c.getValueAdded());
} else if ("album".equals(c.getKey())) {
album = c.getValueAdded().toString();
System.out.println(c.getKey()+":"+c.getValueAdded());
} else if ("image".equals(c.getKey())) {
coverArt = (Image) c.getValueAdded();
}
}
});
mp = new MediaPlayer(music);
System.out.println(pArtist);
System.out.println(artist);
//artist = (String) mp.getMedia().getMetadata().get("artist");
//title = (String) music.getMetadata().get("title");
//album = (String) music.getMetadata().get("album");
//artist = "test";
//album = "test";
//title = "test";
}
public void play() {
mp.play();
}
public void pause() {
mp.pause();
}
public void stop() {
mp.stop();
}
public String getTitle(){
return title;
}
public void setTitle(String title){
this.title = title;
}
public String getArtist(){
return artist;
}
public void setArtist(String artist){
this.artist = artist;
}
public String getAlbum(){
return album;
}
public void setAlbum(String album){
this.album = album;
}
public Image getCover(){
return coverArt;
}
public MediaPlayer getMP(){
return mp;
}
}
Weirdly enough at first I thought it was because my String variables were not setting correctly and were set to null since it shows as null in the console when I put these print lines to test it when the Song object is being constructed. Here is a sample of the console when I test this.
null
null
artist:Foo Fighters
album:Saint Cecilia EP
title:Saint Cecilia
Here is my controller class
public class SceneController implements Initializable{
#FXML
private Button stopBtn;
#FXML
private Slider volume;
#FXML
private Button loadBtn;
#FXML
private Button playBtn;
#FXML
private TableView<Song> table;
#FXML
private Label label;
#FXML
private ProgressBar proBar;
private TableColumn songCol,artistCol,albumCol;
ObservableList<Song> songList = FXCollections.observableArrayList();
List<File> list;
FileChooser fileChooser = new FileChooser();
Desktop desktop;
Song mySong;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
TableColumn songCol = new TableColumn("Song");
TableColumn artistCol = new TableColumn("Artist");
TableColumn albumCol = new TableColumn("Album");
songCol.setCellValueFactory(
new PropertyValueFactory<Song,String>("title"));
//songCol.setCellFactory(new Callback);
artistCol.setCellValueFactory(
new PropertyValueFactory<Song,String>("artist"));
albumCol.setCellValueFactory(
new PropertyValueFactory<Song,String>("album"));
volume.setMin(0);
volume.setMax(100);
volume.setValue(100);
volume.valueProperty().addListener(new InvalidationListener() {
#Override
public void invalidated(Observable observable) {
mySong.getMP().setVolume(volume.getValue()/100.0);
}
});
}
// Event Listener on Button[#loadBtn].onAction
#FXML
public void loadFile(ActionEvent event) {
Node source = (Node) event.getSource();
Window theStage = source.getScene().getWindow();
//set fileChooser filter
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("MP3 files", "*.mp3");
fileChooser.getExtensionFilters().add(extFilter);
fileChooser.setTitle("Select MP3 files");
//File file = fileChooser.showOpenDialog(theStage);
//mySong = new Song(file);
list = fileChooser.showOpenMultipleDialog(theStage);
if(list!=null){
for(File x: list) {
mySong = new Song(x);
System.out.println(mySong.getTitle());
songList.add(mySong);
}
}
table.setItems(songList);
}
#FXML
public void playSong(ActionEvent event) {
mySong.play();
}
#FXML
public void stopSong(ActionEvent event) {
//mySong.pause();
System.out.println("song title: "+mySong.getArtist()+mySong.getTitle());
ImageView img = new ImageView(mySong.getCover());
//img.fitWidthProperty().bind(label.widthProperty());
//img.fitHeightProperty().bind(label.heightProperty());
img.setFitHeight(120);
img.setFitWidth(200);
label.setGraphic(img);
//label.setGraphic(new ImageView(mySong.getCover()));
}
But I made another test print line for my "Stop" button in the controller class and after everything is loaded and I press it, it prints out the artist and title fine. I have saw this other thread and checked my getter methods and they seem to be correct? I am really lost on this and if anyone could provide some insight and a solution as to whether it is because my variables are null or my PropertyValueFactory is not done correctly
Also I notice that the nulls come first even though should they not be the last thing printed since when I create a new song object in my controller class the first print lines that run are in the if statements?
There are several things wrong with the way you have your current code, that are evident from the limited example you posted in the question:
Your Song class does not properly follow the JavaFX properties pattern. In particular, you store each "property" twice, once in a "traditional" JavaBean-style field, for example private String title, and once in a JavaFX property: private StringProperty pTitle;. Each property should be stored once. If you want the table to be aware when the value changes, you should use JavaFX properties, and have the "standard" getXXX() and setXXX() retrieve and set the underlying values stored in those properties.
The listener you attach to the media's metadata is called asynchronously at some indeterminate point in the future. When you add the song to the table's list, the cell value factories attached to the columns will, at some point, be executed, and retrieve the assigned property from the Song instance. With the code the way you currently have it, those property instances are only actually created once the listener on the metadata is invoked. So it is possible (perhaps likely) that the cell value factory will inspect the Song instance for its property before the JavaFX property is instantiated, making it impossible for the table to properly observe the property and respond to changes in it. You should instantiate the JavaFX properties when the Song instance is created, and set their value in the listener on the metadata.
At no point do you add the columns you create in the controller to the table. If you are creating them in the FXML file (which you didn't post in the question), you should inject those columns into the controller and initialize those columns with the cell value factories. (Since the screenshot shows there are columns in the table, I am going to assume they are defined in the FXML file, and have appropriate fx:ids.)
So your Song class should look something like this:
public class Song {
private final StringProperty title = new SimpleStringProperty();
private final StringProperty artist = new SimpleStringProperty();
private final StringProperty album = new SimpleStringProperty();
private Media music;
private MediaPlayer mp;
private Image coverArt;
public Song(File file) {
music = new Media(file.toURI().toString());
music.getMetadata().addListener((Change<? extends String, ? extends Object> c) -> {
if (c.wasAdded()) {
if ("artist".equals(c.getKey())) {
setArtist(c.getValueAdded().toString());
} else if ("title".equals(c.getKey())) {
setTitle(c.getValueAdded().toString());
} else if ("album".equals(c.getKey())) {
setAlbum(c.getValueAdded().toString());
} else if ("image".equals(c.getKey())) {
// maybe this needs to be a JavaFX property too: it is not clear from your question:
coverArt = (Image) c.getValueAdded();
}
}
});
mp = new MediaPlayer(music);
}
public void play() {
mp.play();
}
public void pause() {
mp.pause();
}
public void stop() {
mp.stop();
}
public StringProperty titleProperty() {
return title ;
}
public final String getTitle(){
return titleProperty().get();
}
public final void setTitle(String title){
titleProperty().set(title);
}
public StringProperty artistProperty() {
return artist ;
}
public final String getArtist(){
return artistProperty().get();
}
public final void setArtist(String artist){
artistProperty.set(artist);
}
public StringProperty albumProperty() {
return album ;
}
public final String getAlbum(){
return albumProperty().get();
}
public final void setAlbum(String album){
albumProperty().set(album);
}
public Image getCover(){
return coverArt;
}
public MediaPlayer getMP(){
return mp;
}
}
For your controller, I am going to assume your FXML file has defined table columns with fx:ids of "songCol", "artistCol", and "albumCol", respectively. You need to inject these into the controller as you do with the other columns. I also strongly recommend not using the PropertyValueFactory class, which uses reflection and lacks much in the way of compile-time checking, and implementing the callback yourself. Using lambda expressions makes this pretty easy.
So your controller should look like:
public class SceneController implements Initializable{
// non-table code omitted...
#FXML
private TableView<Song> table;
#FXML
private Label label;
#FXML
private ProgressBar proBar;
#FXML
private TableColumn<Song, String> songCol ;
#FXML
private TableColumn<Song, String> artistCol ;
#FXML
private TableColumn<Song, String> albumCol;
ObservableList<Song> songList = FXCollections.observableArrayList();
List<File> list;
FileChooser fileChooser = new FileChooser();
Desktop desktop;
Song mySong;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
songCol.setCellValueFactory(cellData -> cellData.getValue().titleProperty());
artistCol.setCellValueFactory(cellData -> cellData.getValue().artistProperty());
albumCol.setCellValueFactory(cellData -> cellData.getValue().albumProperty());
// ...
}
// other non-table code omitted...
}
You didn't post an minimal, complete, verifiable example, so there may well be other errors in your code which prevent the table from displaying correctly. This should get you started, however.
Normally the TableColumns would be defined in FXML and injected via #FXML.
See the Oracle TableView FXML example.
If you don't want to do it that way, you need to do:
table.getColumns().add(songCol);
And similarly for your other columns.
Also, as HypnicJerk pointed out in comments you also need to follow appropriate naming conventions when using the PropertyValueFactory.
songCol.setCellValueFactory(
new PropertyValueFactory<Song,String>("title")
);
For more details see:
Javafx tableview not showing data in all columns