如何在tilepane中添加多个按钮?

时间:2017-12-05 19:16:54

标签: java javafx linkedhashmap

我在边框的中心有一块瓷砖。对于链接的hashmap中的每个条目,我想在tilepane中添加一个按钮。此tilepane已存在于fxml文件中,并具有fx:id fieldContainer。

如果我一次添加一个键值对,按钮将一个接一个地出现。但是当我尝试通过从文本文件导入链接的散列映射来一次性添加它们时,按钮将不会出现。它将加载一个条目然后抛出nullpointerexception。

导入文本文件并将条目添加到地图的方法似乎都正常工作。我认为问题在于fieldContainer。我不明白为什么它只会逐个添加按钮而不是一次添加按钮。有人可以解释我的代码有什么问题吗?

更新:我通过将fieldContainer分配给Controller类顶部的新TilePane来修复nullpointerexception:

@FXML
private TilePane fieldContainer = new TilePane();

错误消失了,它会打印整个地图,但它仍然不会显示按钮。任何人吗?

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>

类注册(使用导入方法):

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();
        }
    }
}

类控制器(创建按钮):

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);
        }
    }
}

主:

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);
    }
}

错误:

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

1 个答案:

答案 0 :(得分:0)

我自己修好了。所有按钮现在在您运行Main时立即加载。所以这就是我所做的:

我没有在main中调用Register的importFrom方法,而是像在初始化方法中那样从Controller类中调用它,如下所示:

ImageView