-1

So I have a school project, I am creating a small personal tracker. I am dynamically adding tasks and hobbies to a FlowPane inside of a GridPane in an FXML file, which is being loaded as the center element of a BorderPane in an another FXML file.

   private void hobbies() throws IOException {
        Node[] taskview= new Node[GodHands.hobby.size()];
        for(int i=0;i<GodHands.hobby.size();i++){

            FXMLLoader loader=new FXMLLoader(getClass().getResource("/hobbies.fxml"));
            taskview[i]=loader.load();
            System.out.println(loader.getController()==null);
            GodHands.hobbycontroller.add(loader.getController());
            GodHands.hobbycontroller.get(i).update(GodHands.hobby.get(i));
            hobbysection.getChildren().add(taskview[i]);
        }
    }

This is the code for adding the hobbies to the Flowpane. I am then trying to apply the data to the nodes inside of the FXML file through the loop. The controller even though is not null, the radio buttons are always null and I think I am missing something to the controller's working.

This is the code for the Controller Class of Hobbies:

`public class HobbyController implements Initializable {

    @FXML private Label name; @FXML private Label often; @FXML private ProgressIndicator progress;
    @FXML private ToggleButton mon; @FXML private ToggleButton tue;  
    @FXML private ToggleButton wed; @FXML private ToggleButton thu;
    @FXML private ToggleButton fri; @FXML private ToggleButton sat; @FXML private ToggleButton sun;

    @FXML ToggleButton[] toggles={mon,tue,wed,thu,fri,sat,sun};


    public void update(Hobby hobby) {
        System.out.println(toggles[0]=null);
        hobby.hobby_display(name, often, toggles, progress);

    }


    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {}

}

This is the hobby class which the controller refers to:

public class Hobby{

    public String name; public String often; public int[] days; public int[] progress = new int[2];

    public Hobby(){}

    public Hobby(String name, String often, int[] days){
        this.name=name;
        this.often = often;
        this.days = days;
    }

    public void add_new_hobby(String name, String oftenbox, int[] days) {
        this.name= name; this.often =oftenbox; this.days=days;
        GodHands.hobby.add(this);
    }

    public void hobby_display(Label name, Label often, ToggleButton[] toggles, ProgressIndicator progress){
        name.setText(this.name);
        often.setText(this.often);
        often.setAlignment(Pos.CENTER);
        this.progress[1]=7;
        int i=0;
        for(ToggleButton TB: toggles){
            button_state(TB, this.days[i]);
            i+=1;
        }
        progress.setProgress((double) this.progress[0]/this.progress[1]);
        action_setter(toggles, progress);
    }


    private void button_state(ToggleButton a, int i){
        if(i==0){
            a.setDisable(true);
            a.setOpacity(0.5);
            this.progress[1] -=1;}
        if(i==2){
            a.setSelected(true);
            a.setDisable(true);
            a.setOpacity(1);
        }
    }

    public void action_setter(ToggleButton[] toggles, ProgressIndicator progress){
        int i=0;
        for(ToggleButton TB: toggles){
            int index = i;
            TB.setOnAction(actionEvent -> { button_pressed(TB, progress, index);});
            i+=1;
        }
    }

    private void button_pressed(ToggleButton a, ProgressIndicator progress, int index){
        a.setDisable(true);
        a.setOpacity(1);
        this.progress[0]+=1;
        this.days[index]=2;
        progress.setProgress((double) this.progress[0]/this.progress[1]);
    }
}

FXML File:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.control.ToggleButton?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.paint.Color?>
<?import javafx.scene.paint.LinearGradient?>
<?import javafx.scene.paint.Stop?>
<?import javafx.scene.shape.Line?>
<?import javafx.scene.shape.Rectangle?>
<?import javafx.scene.text.Font?>

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="66.0" prefWidth="515.0" spacing="20.0" style="-fx-background-color: #19B89B; -fx-background-radius: 20; -fx-border-color: white; -fx-border-radius: 20;" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Project.HobbyController">
   <children>
      <Pane prefHeight="65.0" prefWidth="531.0">
         <children>
            <Rectangle arcHeight="5.0" arcWidth="5.0" height="65.0" layoutX="-1.0" layoutY="-1.0" stroke="WHITE" strokeType="INSIDE" style="-fx-arc-height: 40; -fx-arc-width: 40;" width="515.0">
               <fill>
                  <LinearGradient endX="1.0" endY="1.0" startX="0.03546099290780142" startY="0.08983451536643026">
                     <stops>
                        <Stop>
                           <color>
                              <Color red="0.4470588266849518" green="0.2705882489681244" blue="0.929411768913269" />
                           </color>
                        </Stop>
                        <Stop offset="1.0">
                           <color>
                              <Color red="0.7904239892959595" green="0.28344297409057617" blue="0.8684210777282715" />
                           </color>
                        </Stop>
                     </stops>
                  </LinearGradient>
               </fill>
            </Rectangle>
            <ImageView fitHeight="24.0" fitWidth="26.0" layoutX="14.0" layoutY="3.0" pickOnBounds="true" preserveRatio="true">
               <image>
                  <Image url="@icons/about.png" />
               </image>
            </ImageView>
            <Label fx:id="name" layoutX="49.0" layoutY="7.0" prefHeight="16.0" prefWidth="157.0" text="Studying" textFill="WHITE">
               <font>
                  <Font name="Arial" size="14.0" />
               </font>
            </Label>
            <Line endX="100.0" endY="13.0" layoutX="-57.0" layoutY="25.0" startX="100.0" startY="-20.0" stroke="WHITE" />
            <Label fx:id="often" layoutX="49.0" layoutY="28.0" prefHeight="14.0" prefWidth="58.0" style="-fx-border-color: white; -fx-border-radius: 15; -fx-background-color: #3A545A;" text=" Everyday" textFill="WHITE">
               <font>
                  <Font name="Arial" size="7.0" />
               </font>
            </Label>
            <ProgressIndicator fx:id="progress" layoutX="477.0" layoutY="15.0" mouseTransparent="true" prefHeight="57.0" prefWidth="27.0" progress="0.0" style="-fx-background-color: transparent;" stylesheets="@pi.css" />
         </children>
      </Pane>
      <HBox prefHeight="100.0" prefWidth="386.0" spacing="5.0">
         <children>
            <ToggleButton fx:id="mon" mnemonicParsing="false" prefHeight="25.0" prefWidth="33.0" stylesheets="@button%20-%20Copy%20-%20Copy.css" text="M">
               <font>
                  <Font name="Arial Bold" size="10.0" />
               </font>
               <HBox.margin>
                  <Insets left="130.0" top="10.0" />
               </HBox.margin>
            </ToggleButton>
            <ToggleButton fx:id="tue" mnemonicParsing="false" prefHeight="25.0" prefWidth="33.0" stylesheets="@button%20-%20Copy%20-%20Copy.css" text="Tu">
               <font>
                  <Font name="Arial Bold" size="10.0" />
               </font>
               <HBox.margin>
                  <Insets top="10.0" />
               </HBox.margin>
            </ToggleButton>
            <ToggleButton fx:id="wed" mnemonicParsing="false" prefHeight="25.0" prefWidth="36.0" stylesheets="@button%20-%20Copy%20-%20Copy.css" text="We">
               <font>
                  <Font name="Arial Bold" size="10.0" />
               </font>
               <HBox.margin>
                  <Insets top="10.0" />
               </HBox.margin>
            </ToggleButton>
            <ToggleButton fx:id="thu" mnemonicParsing="false" prefHeight="25.0" prefWidth="33.0" stylesheets="@button%20-%20Copy%20-%20Copy.css" text="Th">
               <font>
                  <Font name="Arial Bold" size="10.0" />
               </font>
               <HBox.margin>
                  <Insets top="10.0" />
               </HBox.margin>
            </ToggleButton>
            <ToggleButton fx:id="fri" mnemonicParsing="false" prefHeight="25.0" prefWidth="33.0" stylesheets="@button%20-%20Copy%20-%20Copy.css" text="Fr">
               <font>
                  <Font name="Arial Bold" size="10.0" />
               </font>
               <HBox.margin>
                  <Insets top="10.0" />
               </HBox.margin>
            </ToggleButton>
            <ToggleButton fx:id="sat" mnemonicParsing="false" prefHeight="25.0" prefWidth="33.0" stylesheets="@button%20-%20Copy%20-%20Copy.css" text="Sa">
               <font>
                  <Font name="Arial Bold" size="10.0" />
               </font>
               <HBox.margin>
                  <Insets top="10.0" />
               </HBox.margin>
            </ToggleButton>
            <ToggleButton fx:id="sun" mnemonicParsing="false" prefHeight="25.0" prefWidth="33.0" stylesheets="@button%20-%20Copy%20-%20Copy.css" text="Su">
               <font>
                  <Font name="Arial Bold" size="10.0" />
               </font>
               <HBox.margin>
                  <Insets top="10.0" />
               </HBox.margin>
            </ToggleButton>
         </children>
         <VBox.margin>
            <Insets left="65.0" />
         </VBox.margin>
      </HBox>
   </children>
</VBox>

This is the error:

false
null
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: com.example.personalmanagementsystem/Project.Internal.switchers.dashboard(switchers.java:52)
    at com.example.personalmanagementsystem/Project.DashboardController.dashboard(DashboardController.java:29)
    ... 57 more
Caused by: java.lang.NullPointerException: Cannot invoke "javafx.scene.control.ToggleButton.setDisable(boolean)" because "a" is null
    at com.example.personalmanagementsystem/Project.Internal.Hobby.button_state(Hobby.java:44)
    at com.example.personalmanagementsystem/Project.Internal.Hobby.hobby_display(Hobby.java:34)
    at com.example.personalmanagementsystem/Project.HobbyController.update(HobbyController.java:23)
    at com.example.personalmanagementsystem/Project.DashboardContentController.hobbies(DashboardContentController.java:86)
    at com.example.personalmanagementsystem/Project.DashboardContentController.initialize(DashboardContentController.java:31)
    at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2655)
    ... 61 more

I haven't yet found any solutions. Please provide some insight as to why this is happening. I am still new to FX and programming as a whole. Thank you.

  • @kloeopatra i am unsure of what you mean, do u mean in code? – Naeem Ahmad Jan 05 '23 at 03:50
  • For example: Method names use `camelCase` (e.g., `hobby_display` should be `hobbyDisplay`). Using camel case for methods is part of the _standard_ Java naming conventions. They don't affect the behavior of your code (at least not in this case) but using widely accepted standards when posting on a public forum aids readability. – Slaw Jan 05 '23 at 05:35
  • @Slaw Oh, i am sorry. I didn't know of that. I will be mindful to follow a standard convention from next time. – Naeem Ahmad Jan 05 '23 at 06:40
  • Check out https://www.javatpoint.com/java-naming-conventions – Slaw Jan 05 '23 at 06:49

1 Answers1

2

The problem is here:

@FXML ToggleButton[] toggles={mon,tue,wed,thu,fri,sat,sun}

This is executed when the controller is being instantiated, which occurs before the FXML fields are injected. In other words, at the time this line of code is executed, mon, tue, wed, thu, fri, sat, and sun are all null.

One solution is to create the array in the initialize method:


// ...

private ToggleButton[] toggles; // no @FXML necessary, as it's not being injected

// ...

@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
    // the FXML-injected fields are injected by the time this method is called
    toggles = new ToggleButton[]{mon, tue, wed, thu, fri, sat, sun};
}

// ...

If you want to try defining the array in FXML and injecting it, look into fx:define and fx:reference (see Introduction to FXML). Though you may have to change to a list implementation rather than an array; I'm not sure if FXML is capable of creating arrays.


Note on design: Your Hobby class looks to be a model class. The model should not know about the view. Having Hobby responsible for setting the content and properties of various UI nodes is not ideal. That is the controller's job. Also, given you're dealing with days of the week, you might want to look into using the java.time API (e.g., DayOfWeek).

Slaw
  • 37,820
  • 8
  • 53
  • 80
  • thank you, that fixed my issue. I was just having a little problem understanding how the general flow works. And yes, i will work on making the model independent. If its not a problem/a forum policy violation, i just had one more question, as was in the code above, if i had to access the controller of the FXML file from the dynamically added FXML files, what would be the best way to do that? If its a problem, its fine if you can link me to someplace. Thank you! – Naeem Ahmad Jan 05 '23 at 03:54
  • I'm not sure I understand the question. You get the controllers from the `FXMLLoader` instances, which is good. That's how you get the managed controller instances. From there, ideally you'd communicate between controllers via a model. Research software architecture patterns such as MVC, MVP, and MVVM. For instance: [Applying MVC With JavaFx](https://stackoverflow.com/questions/32342864/applying-mvc-with-javafx). – Slaw Jan 05 '23 at 05:45
  • Yes, that's exactly what I meant to ask, of a model. Thank you so much for your time. – Naeem Ahmad Jan 05 '23 at 06:44