I have a tilepane inside the center of a borderpane. For every entry in a linked hashmap I want to add a button to the tilepane. This tilepane already exists in the fxml file and has the fx:id fieldContainer.
If I add a key-value pair one at a time, the buttons will appear one after the other. But when I try to add them all at once by importing the linked hashmap from a text file, the buttons won't appear. It will load one entry and then throw a nullpointerexception.
The methods for importing the text file and adding the entries to the map all seem to be working fine. I think the problem lies with the fieldContainer. I don't understand why it will only add buttons one by one and not all at once. Could someone please explain what's wrong with my code?
UPDATE: I fixed the nullpointerexception by assigning fieldContainer to a new TilePane at the top of the Controller class:
@FXML
private TilePane fieldContainer = new TilePane();
The error is gone, it prints the entire map, but it still won't show me the buttons. Anyone?
The FXML:
<BorderPane fx:id="container" stylesheets="/css/main.css" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<top>
// Some code
</top>
<center>
<TilePane fx:id="fieldContainer" prefColumns="2" prefTileHeight="100.0" prefTileWidth="135.0">
</TilePane>
</center>
<bottom>
// Some code
</bottom>
Class Register (with import method):
public class Register {
private Controller ctrl;
public Register(Controller ctrl) {
this.ctrl = ctrl;
}
public void exportTo(File file) {
LinkedHashMap<String, BigDecimal> pMap = ctrl.getProductMap();
try (
FileOutputStream fos = new FileOutputStream(file);
PrintWriter writer = new PrintWriter(fos);
) {
for(Map.Entry<String, BigDecimal> entry : pMap.entrySet()) {
writer.printf("%s|%s%n",
entry.getKey(),
entry.getValue());
}
} catch(IOException ioe) {
System.out.printf("Problem saving %s %n", file);
ioe.printStackTrace();
}
}
public void importFrom(File file) {
try (
FileInputStream fis = new FileInputStream(file);
BufferedReader reader = new BufferedReader(new InputStreamReader(fis));
) {
String line;
while((line = reader.readLine()) != null) {
String[] args = line.split("\\|");
ctrl.addProduct(args[0], new BigDecimal(args[1]));
}
} catch(IOException ioe) {
System.out.printf("Problems loading %s %n", file);
ioe.printStackTrace();
}
}
}
Class Controller (button creation):
public class Controller {
private static LinkedHashMap<String, BigDecimal> mProductMap = new LinkedHashMap<>();
@FXML
private TilePane fieldContainer;
public LinkedHashMap<String, BigDecimal> getProductMap() {
return mProductMap;
}
// Puts key and value to the Map and creates a button for each entry
public void addProduct(String product, BigDecimal price) {
mProductMap.put(product, price);
System.out.println(mProductMap);
fieldContainer.getChildren().clear();
for (Map.Entry<String, BigDecimal> entry : mProductMap.entrySet()) {
StackPane newField = new StackPane();
Button main = new Button();
Button multiply = new Button("X");
Button subtract = new Button("-");
main.setText(entry.getKey() + "\n" + entry.getValue());
multiply.getStyleClass().add("inner-button");
subtract.getStyleClass().add("inner-button");
StackPane.setAlignment(multiply, Pos.BOTTOM_LEFT);
StackPane.setAlignment(subtract, Pos.BOTTOM_CENTER);
StackPane.setMargin(multiply, new Insets(0, 0, 8, 8));
StackPane.setMargin(subtract, new Insets(0, 0, 8, 16));
// Three buttons on top of each other
newField.getChildren().add(main);
newField.getChildren().add(multiply);
newField.getChildren().add(subtract);
fieldContainer.setAlignment(Pos.TOP_LEFT);
// this should add a button for each entry,
// but it only works when you add them one by one
fieldContainer.getChildren().add(newField);
}
}
}
Main:
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setScene(new Scene(root, 300, 500));
primaryStage.show();
Controller controller = new Controller();
Register register = new Register(controller);
register.importFrom(new File("prices.txt"));
}
@Override
public void stop(){
Controller controller = new Controller();
Register register = new Register(controller);
register.exportTo(new File("prices.txt"));
}
public static void main(String[] args) {
launch(args);
}
}
Error:
Exception in Application start method
{Coffee=1.50} // the first entry from the Map prints
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:473)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:372)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:945)
Caused by: java.lang.RuntimeException: Exception in Application start method
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:973)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:198)
at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: java.lang.NullPointerException
at sample.Controller.addProduct(Controller.java:54) // fieldContainer.getChildren().clear();
at sample.Register.importFrom(Register.java:40) // ctrl.addProduct(args[0], new BigDecimal(args[1]));
at sample.Main.start(Main.java:23) // register.importFrom(new File("prices.txt"));
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:919)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$11(PlatformImpl.java:449)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$9(PlatformImpl.java:418)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:417)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:175)
... 1 more
Exception running application sample.Main