Reputation: 549
I have a lot of Objects with names a1, a2, a3, ... I need to put them into List, so that it was simplier to work with them, will I do it somehow with loop?
My attempt was:
List<SomeObject> list = new LinkedList<SomeObject>();
for (int i=0; i<1000; i++){
String varName = "a"+i;
list.add((SomeObject) varName);
}
Does anyone have suggestions in this case? Create variables inside loop is not a solution, because they are a part of .fxml document. Or give me an advice how to create that with loop, for it create lines in .fxml parallel to adding in loop new objects.
To be more understandable .fxml file looks like
<SomeObject fx:id="a1" *other props* />
<SomeObject fx:id="a2" *other props* />
<SomeObject fx:id="a3" *other props* />
<SomeObject fx:id="a4" *other props* />
Thanks a lot in advice!
Upvotes: 2
Views: 9511
Reputation: 82461
Personally I'd prefer the other possibility, but just for the sake of completness:
You can also access Object
s created by the FXMLLoader
by the fx:id
attribute after loading, using FXMLLoader.getNamespace()
:
<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());
Prints the text from the Text
element in the fxml (i.e. 42
).
Of course you do not get access to the FXMLLoader
instance by default in the controller, which means with this approach you need to pass the information to the controller yourself, if it's required there.
Upvotes: 3
Reputation: 209340
If you have that many items, it's probably best to initialize them using Java, rather than using FXML. For example, instead of:
<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>
and a controller
public class Controller {
@FXML
private FlowPane container ;
@FXML
private Label label1 ;
@FXML
private Label label2 ;
// ...
@FXML
private Label label1000 ;
// ...
}
I would do
<FlowPane fx:id="container" minWidth="..." minHeight="...">
</FlowPane>
and
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);
}
}
}
As a variation on this idea, consider defining a custom component:
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);
}
}
Now in your FXML you do
<LabelFlow fx:id="labelFlow" numLabels="1000"/>
and in your controller
public class Controller {
@FXML
private LabelFlow labelFlow ;
public void initialize() {
for (Label label : labelFlow.getLabels()) {
// do whatever you need with label....
}
}
}
You need to jump through a couple of hoops if you want to use a custom class like that in Scene Builder. See Adding a custom component to SceneBuilder 2.0
If you really want to define all those controls in FXML, which would be a maintenance nightmare imo, you can use reflection to access the variables. I don't recommend this, not just because it's hard to maintain, but also because reflection by its nature is error-prone (no compile-time checking) and complex.
But you could do
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);
}
}
}
Upvotes: 9
Reputation: 49185
You may prefer to put your objects into the list in the fxml directly:
<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>
and access it in the controller as:
@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);
}
if you are not accessing each SomeObject individually you can drop fx:id
s for them. For more info about fxml features refer to Introduction to FXML.
Upvotes: 5