JavaFX在创建新类时不会注入到@FXML注解的成员。

huangapple go评论72阅读模式
英文:

Javafx not injecting to @FXML annotated members when creating new class

问题

以下是您提供的代码的中文翻译部分:

我目前正在尝试创建要显示在面板上的列表每个列表都有按钮标题和一个额外的vbox来存储其中的内容)。当我创建一个新的列表以在我的面板上显示它时所有带有@FXML注释的字段都保持为空但对象的子元素存在)。问题出在以下代码行
`ListCtrl listObject = new ListCtrl();`
我怀疑注入器存在问题因为我不太确定如何使用它以下是我的代码

List.fxml
```xml
<!-- XML部分省略 -->

列表的控制器

package client.scenes;

import jakarta.inject.Inject;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.fxml.Initializable;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

public class ListCtrl extends AnchorPane implements Initializable{
    // @FXML注释部分省略

    // 构造函数和其他方法省略
}

创建新列表的代码部分

public void refresh() {
        mainBoard.getChildren().clear();
        var lists = fakeServer.getBoardLists();
        data = FXCollections.observableList(lists);

        for (BoardList currentList : data) {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("List.fxml"));
            ListCtrl listObject = loader.getController(); // 实例化要显示的新列表
            listObject.setListTitleText(currentList.title); // 设置列表的标题
            ObservableList<Card> cardsInList = FXCollections.observableList(fakeServer.getCards(currentList));
            for (Card currentCard : cardsInList) {
                CardCtrl cardObject = new CardCtrl(); // 实例化要显示的新卡片
                cardObject.setCardTitleText(currentCard.title); // 设置卡片的标题
                listObject.addCardToList(cardObject); // 将卡片添加到列表
            }
            listObject.getListAddCardButton().setOnAction(event -> mainCtrl.showAddCard(currentList));
            mainBoard.getChildren().add(listObject);
        }
    }

希望这些信息对您有所帮助。如果您有其他问题或需要进一步的帮助,请随时提问。

英文:

I am currently trying to create lists to show on a board (each list has buttons and a title and an additional vbox to store things inside of it). When I create a new list to show it on my board, all the @FXML annotated fields are left with null (but the children of the object exist). The specific line of code is:
ListCtrl listObject = new ListCtrl();
I am suspecting the injector is at fault as I am not really sure how to use it. Here is my code.

List.fxml

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;

&lt;?import javafx.geometry.Insets?&gt;
&lt;?import javafx.scene.control.Button?&gt;
&lt;?import javafx.scene.control.Label?&gt;
&lt;?import javafx.scene.layout.HBox?&gt;
&lt;?import javafx.scene.layout.VBox?&gt;

&lt;VBox maxHeight=&quot;-Infinity&quot; maxWidth=&quot;-Infinity&quot; minHeight=&quot;-Infinity&quot; minWidth=&quot;-Infinity&quot; prefHeight=&quot;240.0&quot; prefWidth=&quot;180.0&quot; style=&quot;-fx-border-color: black; -fx-border-width: 10;&quot; xmlns=&quot;http://javafx.com/javafx/19&quot; xmlns:fx=&quot;http://javafx.com/fxml/1&quot; fx:controller=&quot;client.scenes.ListCtrl&quot;&gt;
   &lt;children&gt;
      &lt;HBox alignment=&quot;TOP_RIGHT&quot; prefHeight=&quot;19.0&quot; prefWidth=&quot;140.0&quot;&gt;
         &lt;children&gt;
            &lt;Button fx:id=&quot;listEditButton&quot; mnemonicParsing=&quot;false&quot; text=&quot;Edit&quot; /&gt;
            &lt;Button fx:id=&quot;listCloseButton&quot; mnemonicParsing=&quot;false&quot; text=&quot;X&quot; /&gt;
         &lt;/children&gt;
      &lt;/HBox&gt;
      &lt;Label id=&quot;listTitle&quot; fx:id=&quot;listTitle&quot; alignment=&quot;CENTER&quot; prefHeight=&quot;17.0&quot; prefWidth=&quot;206.0&quot; text=&quot;Default List Name&quot; /&gt;
      &lt;VBox fx:id=&quot;cardBox&quot; prefHeight=&quot;142.0&quot; prefWidth=&quot;140.0&quot; /&gt;
      &lt;Button id=&quot;listAddCard&quot; fx:id=&quot;listAddCard&quot; alignment=&quot;CENTER&quot; mnemonicParsing=&quot;false&quot; prefHeight=&quot;25.0&quot; prefWidth=&quot;202.0&quot; text=&quot;Add card&quot; /&gt;
   &lt;/children&gt;
   &lt;padding&gt;
      &lt;Insets bottom=&quot;10.0&quot; left=&quot;10.0&quot; right=&quot;10.0&quot; top=&quot;10.0&quot; /&gt;
   &lt;/padding&gt;
&lt;/VBox&gt;

The controller of the list

package client.scenes;

import jakarta.inject.Inject;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.fxml.Initializable;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

public class ListCtrl extends AnchorPane implements Initializable{
    @FXML
    private Label listTitle;
    @FXML
    private VBox cardBox;
    @FXML
    private Button listAddCard;
    @FXML
    private Button listCloseButton;
    @FXML
    private Button listEditButton;

    @Override
    public void initialize(URL location, ResourceBundle resources) {

    }
    @Inject
    public ListCtrl(){
        super();
        try
        {
            FXMLLoader loader = new FXMLLoader(getClass().getResource(&quot;List.fxml&quot;));
            Parent root = loader.load();
            //Node n = loader.load();
            //this.getChildren().add(n);

        } catch (IOException ix){

        }
    }


    /** Sets the text of the title of the list
     * @param text
     */
    public void setListTitleText(String text) {
        listTitle.setText(text);
    }

    public void addCardToList(Node card){
        cardBox.getChildren().add(card);
    }

    /**
     * @return the title of the list
     */
    public Label getListTitle() {
        return listTitle;
    }

    /**
     * @return the Edit button of the list
     */
    public Button getListEditButton() {
        return listEditButton;
    }

    /**
     * @return the X button for the list
     */
    public Button getListCloseButton() {
        return listCloseButton;
    }

    /**
     * @return the list button of the list
     */
    public Button getListAddCardButton() {
        return listAddCard;
    }
}

The part of the code where I am creating a new list

public void refresh() {
        mainBoard.getChildren().clear();
        var lists = fakeServer.getBoardLists();
        data = FXCollections.observableList(lists);

        for (BoardList currentList : data) {
            ListCtrl listObject = new ListCtrl(); ///Instantiating a new list to be shown
            listObject.setListTitleText(currentList.title); //Setting the title of the list
            ObservableList&lt;Card&gt; cardsInList =
                FXCollections.observableList(fakeServer.getCards(currentList));
            for (Card currentCard : cardsInList) {
                CardCtrl cardObject = new CardCtrl(); ///Instantiating a new card to be shown
                cardObject.setCardTitleText(currentCard.title); //Setting the title of the card
                listObject.addCardToList(cardObject); //Adding the card to the list
            }
            listObject.getListAddCardButton().setOnAction(event -&gt; mainCtrl.showAddCard(currentList));
            mainBoard.getChildren().add(listObject);
        }
    }

I tried looking up information on how to use the injector for objects created through code (so objects which are not already on the main board) and didn't succeed. Thank you in advance!

EDIT:

I changed my refresh method to this:

public void refresh() {
        mainBoard.getChildren().clear();
        var lists = fakeServer.getBoardLists();
        data = FXCollections.observableList(lists);

        for (BoardList currentList : data) {
            FXMLLoader loader = new FXMLLoader(getClass().getResource(&quot;List.fxml&quot;));
            ListCtrl listObject = loader.getController(); ///Instantiating a new list to be shown
            listObject.setListTitleText(currentList.title); //Setting the title of the list
            ObservableList&lt;Card&gt; cardsInList =
                FXCollections.observableList(fakeServer.getCards(currentList));
            for (Card currentCard : cardsInList) {
                CardCtrl cardObject = new CardCtrl(); ///Instantiating a new card to be shown
                cardObject.setCardTitleText(currentCard.title); //Setting the title of the card
                listObject.addCardToList(cardObject); //Adding the card to the list
            }
            listObject.getListAddCardButton().setOnAction(event -&gt; mainCtrl.showAddCard(currentList));
            //mainBoard.getChildren().add(listObject);
        }
    }

And now listObject is null. Have I used the loader incorrectly?

答案1

得分: 4

总结下面原帖中的评论作为答案:

虽然还有其他解决方案(例如使用动态根来创建自定义组件),但我建议在这里使用标准的FXML方法。也就是说,使用FXML文件来定义UI,并保持UI逻辑(控制器,不应该是UI组件)与UI视图(FXML)之间的一定程度的分离。

@FXML 注解的字段仅在由 FXMLLoader 创建的控制器对象中初始化,当调用 load() 时;它们不会在通过调用构造函数创建的控制器类的其他实例中以某种方式神奇地初始化。

因此,你不应该将控制器类作为 AnchorPane(或任何其他UI类)的子类,也不应该从构造函数加载FXML(因为控制器是通过加载FXML创建的,而不是反过来)。

public class ListCtrl {
    @FXML
    private Label listTitle;
    @FXML
    private VBox cardBox;
    @FXML
    private Button listAddCard;
    @FXML
    private Button listCloseButton;
    @FXML
    private Button listEditButton;

    /** 设置列表标题的文本
     * @param text
     */
    public void setListTitleText(String text) {
        listTitle.setText(text);
    }

    public void addCardToList(Node card){
        cardBox.getChildren().add(card);
    }

    /**
     * @return 列表的标题
     */
    public Label getListTitle() {
        return listTitle;
    }

    /**
     * @return 列表的编辑按钮
     */
    public Button getListEditButton() {
        return listEditButton;
    }

    /**
     * @return 列表的关闭按钮
     */
    public Button getListCloseButton() {
        return listCloseButton;
    }

    /**
     * @return 列表的添加卡片按钮
     */
    public Button getListAddCardButton() {
        return listAddCard;
    }
}

将加载FXML的责任移交到需要在那里定义UI的地方,并通过调用getController()来检索控制器:

public void refresh() {
    mainBoard.getChildren().clear();
    var lists = fakeServer.getBoardLists();
    data = FXCollections.observableList(lists);

    for (BoardList currentList : data) {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("List.fxml"));
        Parent card = loader.load();
        ListCtrl listObject = loader.getController(); 
        listObject.setListTitleText(currentList.title); // 设置列表的标题
        ObservableList<Card> cardsInList = FXCollections.observableList(fakeServer.getCards(currentList));
        for (Card currentCard : cardsInList) {
            CardCtrl cardObject = new CardCtrl(); // 实例化要显示的新卡片
            cardObject.setCardTitleText(currentCard.title); // 设置卡片的标题
            listObject.addCardToList(cardObject); // 将卡片添加到列表
        }
        listObject.getListAddCardButton().setOnAction(event -> mainCtrl.showAddCard(currentList));
        mainBoard.getChildren().add(card);
    }
}
英文:

Summarizing the comments below the OP as an answer:

While other solutions are possible (e.g. using a dynamic root to create a custom component), I would recommend using the standard FXML approach here. By this, I mean use the FXML file to define the UI and keep some degree of separation between the UI logic (the controller, which should not be a UI component) and the UI view (the FXML).

@FXML-annotated fields are only initialized in the controller object that is created by the FXMLLoader when load() is called; they are not somehow magically initialized in other instances of the controller class that are created by calling the constructor.

You should move thus not make your controller class a subclass of AnchorPane (or any other UI class) and should not load the FXML from the constructor (because the controller is created from loading the FXML, not the other way round).

public class ListCtrl {
@FXML
private Label listTitle;
@FXML
private VBox cardBox;
@FXML
private Button listAddCard;
@FXML
private Button listCloseButton;
@FXML
private Button listEditButton;
/** Sets the text of the title of the list
* @param text
*/
public void setListTitleText(String text) {
listTitle.setText(text);
}
public void addCardToList(Node card){
cardBox.getChildren().add(card);
}
/**
* @return the title of the list
*/
public Label getListTitle() {
return listTitle;
}
/**
* @return the Edit button of the list
*/
public Button getListEditButton() {
return listEditButton;
}
/**
* @return the X button for the list
*/
public Button getListCloseButton() {
return listCloseButton;
}
/**
* @return the list button of the list
*/
public Button getListAddCardButton() {
return listAddCard;
}
}

Move the responsibility for loading the FXML to the point where you need the UI defined there, and retrieve the controller by calling getController() on the FXML:

public void refresh() {
mainBoard.getChildren().clear();
var lists = fakeServer.getBoardLists();
data = FXCollections.observableList(lists);
for (BoardList currentList : data) {
FXMLLoader loader = new FXMLLoader(getClass().getResource(&quot;List.fxml&quot;));
Parent card = loader.load();
ListCtrl listObject = loader.getController(); 
listObject.setListTitleText(currentList.title); //Setting the title of the list
ObservableList&lt;Card&gt; cardsInList =
FXCollections.observableList(fakeServer.getCards(currentList));
for (Card currentCard : cardsInList) {
CardCtrl cardObject = new CardCtrl(); ///Instantiating a new card to be shown
cardObject.setCardTitleText(currentCard.title); //Setting the title of the card
listObject.addCardToList(cardObject); //Adding the card to the list
}
listObject.getListAddCardButton().setOnAction(event -&gt; mainCtrl.showAddCard(currentList));
mainBoard.getChildren().add(card);
}
}

huangapple
  • 本文由 发表于 2023年3月9日 19:22:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/75683930.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定