【问题标题】:adding elements defined in FXML to list with loop将 FXML 中定义的元素添加到循环列表中
【发布时间】:2016-03-22 11:16:12
【问题描述】:

我有很多名称为 a1、a2、a3、...的对象 我需要将它们放入 List 中,以便使用它们更简单,我会以某种方式使用循环吗?

我的尝试是:

List<SomeObject> list = new LinkedList<SomeObject>();
for (int i=0; i<1000; i++){
    String varName = "a"+i;
    list.add((SomeObject) varName);
}

在这种情况下,有人有什么建议吗? 在循环内创建变量不是解决方案,因为它们是 .fxml 文档的一部分。 或者给我一个建议,如何使用循环创建它,因为它在 .fxml 中创建与添加循环新对象平行的行。

为了更容易理解 .fxml 文件看起来像

  <SomeObject fx:id="a1" *other props* />
  <SomeObject fx:id="a2" *other props* />
  <SomeObject fx:id="a3" *other props* />
  <SomeObject fx:id="a4" *other props* />

非常感谢您的建议!

【问题讨论】:

  • 你为什么要创建 很多 个对象 a1、a2、a3 而不是首先将它们放在列表中?
  • 您真的在这样的 FXML 文件中定义了 1,000 个项目吗?当然,最好首先在 Java 中定义它们(即在您的控制器中),然后将它们添加到那里的 UI 中。
  • 只需要 10 次复制粘贴,2^10=1024 所以是的,我在 Scene builder=) James_D 中就是这样做的,你能举个例子来理解你的意思吗?跨度>

标签: java list javafx javafx-2


【解决方案1】:

如果您有这么多项目,最好使用 Java 来初始化它们,而不是使用 FXML。例如,而不是:

<FlowPane fx:id="container" minWidth="..." minHeight="...">
    <Label fx:id="label1" text="Label 1"/>
    <Label fx:id="label2" text="Label 2"/>
    <Label fx:id="label3" text="Label 3"/>

    <!-- ... -->

    <Label fx:id="label1000" text="Label 1000"/>
</FlowPane>

还有一个控制器

public class Controller {

    @FXML
    private FlowPane container ;
    @FXML
    private Label label1 ;
    @FXML
    private Label label2 ;
    // ...

    @FXML
    private Label label1000 ;

    // ...
}

我愿意

<FlowPane fx:id="container" minWidth="..." minHeight="...">
</FlowPane>

public class Controller {

    @FXML
    private FlowPane container ;

    private List<Label> labels ;

    public void initialize() {
        labels = new ArrayList<>();
        for (int i = 1; i <= 1000; i++) {
            Label label = new Label("Label "+i);
            labels.add(label);
            container.getChildren().add(label);
        }
    }
}

作为这个想法的一个变体,考虑定义一个自定义组件:

public class LabelFlow extends FlowPane {

    private List<Label> labels ;

    public LabelFlow(@NamedArg("numLabels") int numLabels) {
        labels = new ArrayList<>();
        for(int i = 1 ; i <= numLabels ; i++) {
            Label label = new Label("Label "+i);
            labels.add(label);
        }
        getChildren().addAll(labels);
    }

    public List<Label> getLabels() {
        return Collections.unmodifiableList(labels);
    }
}

现在在你的 FXML 中你做

<LabelFlow fx:id="labelFlow" numLabels="1000"/>

在你的控制器中

public class Controller {
    @FXML
    private LabelFlow labelFlow ;

    public void initialize() {
        for (Label label : labelFlow.getLabels()) {
            // do whatever you need with label....
        }
    }
}

如果您想在 Scene Builder 中使用类似的自定义类,则需要跳过几个环节。见Adding a custom component to SceneBuilder 2.0

如果您真的想要在 FXML 中定义所有这些控件,这将是 imo 维护的噩梦,您可以使用反射来访问变量。我不推荐这样做,不仅因为它难以维护,还因为反射本质上容易出错(没有编译时检查)且复杂。

但你可以这样做

public class Controller {

    @FXML
    private FlowPane container ;
    @FXML
    private Label label1 ;
    @FXML
    private Label label2 ;
    // ...

    @FXML
    private Label label1000 ;

    private List<Label> labels ;

    public void initialize() throws Exception {
        labels = new ArrayList<>();
        for (int i = 1; i <= 1000; i++) {
            Field field = getClass().getDeclaredField("label"+i);
            boolean wasAccessible = field.isAccessible();
            field.setAccessible(true);
            Label label = (Label) field.get(this);
            field.setAccessible(wasAccessible);
            labels.add(label);
        }
    }
}

【讨论】:

  • 非常感谢!很有用!
【解决方案2】:

您可能更喜欢将对象直接放入 fxml 中的列表中:

<fx:define>
     <FXCollections fx:id="theList" fx:factory="observableArrayList">
         <SomeObject someProperty="SomeValue 1" />
         <SomeObject someProperty="SomeValue 2" />
         <SomeObject someProperty="SomeValue 3" />
         <SomeObject someProperty="SomeValue 4" />
      </FXCollections>
</fx:define>

并在控制器中访问它:

@FXML
private ObservableList<SomeObject> theList;

@Override
public void initialize( URL url, ResourceBundle rb )
{
    // do whatever with the list
    System.out.println( "the list of some objects = " + theList);
}

如果您不单独访问每个 SomeObject,您可以为它们删除 fx:ids。有关 fxml 功能的更多信息,请参阅Introduction to FXML

【讨论】:

  • 谢谢,我今晚试试。想一想,正是我想要的!
【解决方案3】:

我个人更喜欢另一种可能性,但只是为了完整性:

你也可以在加载后通过fx:id属性访问FXMLLoader创建的Objects,使用FXMLLoader.getNamespace()

示例

命名空间.fxml

<AnchorPane xmlns:fx="http://javafx.com/fxml/1">
    <children>
        <Text fx:id="node1" text="42"/>
    </children>
</AnchorPane>

加载中

FXMLLoader loader = new FXMLLoader(getClass().getResource("namespace.fxml"));
loader.load();
System.out.println(((Text)loader.getNamespace().get("node1")).getText());

打印来自 fxml 中 Text 元素的文本(即 42)。

当然,默认情况下,您无法在控制器中访问 FXMLLoader 实例,这意味着使用这种方法,您需要自己将信息传递给控制器​​,如果那里需要的话。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-25
    • 1970-01-01
    • 2016-08-25
    • 1970-01-01
    • 2019-04-08
    • 2014-08-03
    • 2020-08-12
    • 1970-01-01
    相关资源
    最近更新 更多