JavaFX - 在 ComboBox 上使用 ChangeListener 以 ArrayList 填充另一个 ComboBox 导致 ArrayList 为空
JavaFX - Using a ChangeListener on a ComboBox to populate another ComboBox with an ArrayList results in the ArrayList being empty
我在 两天前发布了关于将 ChangeListener 附加到 ComboBox 以指示将在第二个 ComboBox 上显示什么输出的帖子。简而言之,想法是第一个 ComboBox 显示一些单位类型,并且根据所选的单位类型将取决于第二个 ComboBox 中显示的基于所选单位类型的单位列表。例如,如果您在第一个 ComboBox 中选择 "Elites" 单位类型,则第二个 ComboBox 应填充所有 "Elite" 单位。
现在回答 的人对 ChangeListener 中的代码提供了很多帮助,我确实设法让它工作了。但是,目前它仅在您一次将一个单元添加到单元类型时才有效。理想情况下,我想将 ArrayList 或其他一些合适的单元数据结构添加到单元类型,而不是单个单元。这将减少代码量,并提高效率。
因此,正如您在此代码片段中所见,目前 elites Unit Type addUnit 方法一次仅接受单个字符串,
elites = new Unit_Type("Elites");
elites.addUnit("Dreadnought");
elites.addUnit("Ironclad Dreadnought");
如我所愿,我可以将 ArrayList 或模拟数据结构添加到精英单元类型 addUnit 方法中,如以下代码片段:
elitesList = createArrayList("Dreadnought", "Ironclad Dreadnought", "Venerable Dreadnought", "Assault Terminator", "Centurion Assault", "Command", "Honour Guard","Sternguard Veteran", "Terminator", "Vanguard Veterans");
elites = new Unit_Type("Elites");
elites.addUnit(elitesList);
请注意,elitesList 是由辅助方法生成的,因为还有 8 个类似的列表,每个列表代表不同的单位类型。
现在我已经尝试更改单元类型 class 中的代码,使单元 ArrayList 的类型为 ArrayList<String>
并且还尝试更改 addUnit 方法以接受 ArrayList<String>
的参数但是每当我 运行 程序时,在第一个 ComboBox 中选择一个 Unit Type 会导致第二个 ComboBox 中的数组为空。
这是 AddUnitPane class(视图)和 Unit_Type class(模型)
的其余代码
AddUnitPane Class
public class AddUnitPane extends GridPane
{
private Label unitTypeLbl, unitLbl, squadNameLbl, squadSizeLbl;
private ComboBox<Unit_Type> unitTypeCombo;
private ComboBox<String> unitCombo;
private ComboBox<Integer> squadSizeCombo;
private TextField squadNameTf;
private Button addSquadBtn;
private ArrayList<String> elitesList, fastAtkList, heavySptList, hqList, lordsowList, specialCList, transportList, troopsList; //Declare the sublists that hold all the units for the type of unit
Unit_Type elites, fastAttk, heavySpt, hQ, lordsOW, specialC, transport, troops;
public AddUnitPane()
{
this.setVgap(15);
this.setHgap(20);
this.setAlignment(Pos.CENTER);
ColumnConstraints col1 = new ColumnConstraints();
col1.setHalignment(HPos.RIGHT);
this.getColumnConstraints().add(col1);
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
elitesList = createArrayList("Dreadnought", "Ironclad Dreadnought", "Venerable Dreadnought", "Assault Terminator", "Centurion Assault", "Command", "Honour Guard",
"Sternguard Veteran", "Terminator", "Vanguard Veterans");
fastAtkList = createArrayList("Attack Bike", "Stormhawk Interceptor", "Stormtalon Gunship", "Assault", "Bike", "Land Speeder", "Scout Bike");
heavySptList = createArrayList("Hunter", "Land Raider Crusader", "Land Raider Redeemer", "Land Raider", "Predator", "Stalker", "Stormraaven Gunship", "Vindicator",
"Whirlwind", "Centurion Devastator", "Devastator", "Thunderfire Cannon");
hqList = createArrayList("Captain", "Chaplain", "Librarian", "Techmarine");
lordsowList = createArrayList("Marneus Calger", "Roboute Guilliman");
specialCList = createArrayList("Antaro Chronus", "Cato Sicarius", "Ortan Cassius", "Torias Telion", "Varro Tigurius");
transportList = createArrayList("Drop Pod", "Land Speeder Storm", "Razorback", "Rhino");
troopsList = createArrayList("Scout", "Tactical");
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
elites = new Unit_Type("Elites");
//elites.addUnit(elitesList);
elites.addUnit("Dreadnought");
elites.addUnit("Ironclad Dreadnought");
fastAttk = new Unit_Type("Fast Attack");
fastAttk.addUnit("Attack Bike");
fastAttk.addUnit("Stormhawk Interceptor");
ObservableList<Unit_Type> unitTypeOList = FXCollections.observableArrayList(elites, fastAttk); //add each Unit_Type to an Observable List
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
unitTypeLbl = new Label("Select The Unit Class: "); //Initialise all of the labels
unitLbl = new Label("Select The Unit: ");
squadNameLbl = new Label("Squad Name: ");
squadSizeLbl = new Label("Squad Size");
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
unitTypeCombo = new ComboBox<Unit_Type>(); //Initialise the unitTypeCombo (first ComboBox)
unitTypeCombo.setItems(unitTypeOList); //Populate the unitTypeCombo with the UnitTypeOList (observable list) from line 82
//unitTypeCombo.getSelectionModel().selectFirst(); //Set the unitTypeCombo to show the first item
unitCombo = new ComboBox<>(); //Initialise the unitCombo (second ComboBox)
unitTypeCombo.valueProperty().addListener(new ChangeListener<Unit_Type>()
{
@Override
public void changed(ObservableValue<? extends Unit_Type> observable, Unit_Type oldValue, Unit_Type newValue)
{
unitCombo.setItems(newValue == null ? FXCollections.emptyObservableList() : newValue.getUnitsForType());
}
});
squadNameTf = new TextField();
squadSizeCombo = new ComboBox<>();
addSquadBtn = new Button("Add Squad");
this.add(unitTypeLbl, 0, 1);
this.add(unitTypeCombo, 1, 1);
this.add(unitLbl, 0, 2);
this.add(unitCombo, 1, 2);
this.add(squadNameLbl, 0, 3);
this.add(squadNameTf, 1, 3);
this.add(squadSizeLbl, 0, 4);
this.add(squadSizeCombo, 1, 4);
this.add(new HBox(), 0, 5);
this.add(addSquadBtn, 1, 5);
}
public void AddUnitHandler(EventHandler<ActionEvent> handler)
{
addSquadBtn.setOnAction(handler);
}
private static <T> ArrayList<T> createArrayList(T... items) //generates the unit lists
{
ArrayList<T> result = new ArrayList<>(items.length);
for (T item : items)
{
result.addAll(result);
}
return result;
}
}
Unit_Type Class
public class Unit_Type implements Serializable
{
private String typeName;
private ArrayList<String> units; //a unit type is an aggregation of units. Tried changing this type to ArrayList<String>
public Unit_Type(String typeName)
{
this.typeName = typeName;
units = new ArrayList<>();
}
public void addUnit(String u) //tried changing this parameter to ArrayList<String>
{
units.add(u);
}
public void setTypeName(String name)
{
typeName = name;
}
public ObservableList<String> getUnitsForType() //method used in the ChangeListener in the AddUnitPane class
{
ObservableList unitsOList = FXCollections.observableArrayList(units);
return unitsOList;
}
@Override
public String toString() //allows the ComboBox to display values correctly
{
return typeName;
}
}
我不会将支持列表存储在 Unit_Type
class 中。最好将 ObservableList
存储在字段中,编写自定义序列化方法并使字段成为瞬态。
这样你也可以使用
初始化单位类型
elites.getUnitsForType().addAll("Dreadnought", "Ironclad Dreadnought", "Venerable Dreadnought", "Assault Terminator", "Centurion Assault", "Command", "Honour Guard",
"Sternguard Veteran", "Terminator", "Vanguard Veterans");
但在构造函数中初始化列表可能更短:
public class Unit_Type implements Serializable {
private String typeName;
private transient ObservableList<String> units;
private void writeObject(ObjectOutputStream stream)
throws IOException {
stream.defaultWriteObject();
// serialize units list as string array
stream.writeObject(units.toArray());
}
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
// read array from stream and initialize list with it
// Note: because of the way we write objects of this type we can use the raw type here safely
units = (ObservableList) FXCollections.<Object>observableArrayList((Object[])stream.readObject());
}
public Unit_Type(String typeName, String... units) {
this.typeName = typeName;
this.units = FXCollections.observableArrayList(units);
}
public void setTypeName(String name) {
typeName = name;
}
public ObservableList<String> getUnitsForType() {
return units;
}
@Override
public String toString() {
return typeName;
}
}
elites = new Unit_Type("Elites",
"Dreadnought", "Ironclad Dreadnought", "Venerable Dreadnought",
"Assault Terminator", "Centurion Assault", "Command",
"Honour Guard", "Sternguard Veteran", "Terminator",
"Vanguard Veterans"
);
在(反)序列化期间,除 ObservableList
之外的所有内容都使用默认序列化机制处理。
stream.defaultWriteObject();
stream.defaultReadObject();
transient
关键字导致该字段被忽略(ObservableList
通常不可序列化,因为 hard/impossible 持久化监听器,尤其是当它们引用 UI 元素)。在这种情况下,我们只是 read/write 列表中的字符串作为数组:
stream.writeObject(units.toArray());
units = (ObservableList) FXCollections.<Object>observableArrayList((Object[])stream.readObject());
有关详细信息,请参阅此处的自定义默认协议部分:http://www.oracle.com/technetwork/articles/java/javaserial-1536170.html
我在
现在回答
因此,正如您在此代码片段中所见,目前 elites Unit Type addUnit 方法一次仅接受单个字符串,
elites = new Unit_Type("Elites");
elites.addUnit("Dreadnought");
elites.addUnit("Ironclad Dreadnought");
如我所愿,我可以将 ArrayList 或模拟数据结构添加到精英单元类型 addUnit 方法中,如以下代码片段:
elitesList = createArrayList("Dreadnought", "Ironclad Dreadnought", "Venerable Dreadnought", "Assault Terminator", "Centurion Assault", "Command", "Honour Guard","Sternguard Veteran", "Terminator", "Vanguard Veterans");
elites = new Unit_Type("Elites");
elites.addUnit(elitesList);
请注意,elitesList 是由辅助方法生成的,因为还有 8 个类似的列表,每个列表代表不同的单位类型。
现在我已经尝试更改单元类型 class 中的代码,使单元 ArrayList 的类型为 ArrayList<String>
并且还尝试更改 addUnit 方法以接受 ArrayList<String>
的参数但是每当我 运行 程序时,在第一个 ComboBox 中选择一个 Unit Type 会导致第二个 ComboBox 中的数组为空。
这是 AddUnitPane class(视图)和 Unit_Type class(模型)
的其余代码AddUnitPane Class
public class AddUnitPane extends GridPane
{
private Label unitTypeLbl, unitLbl, squadNameLbl, squadSizeLbl;
private ComboBox<Unit_Type> unitTypeCombo;
private ComboBox<String> unitCombo;
private ComboBox<Integer> squadSizeCombo;
private TextField squadNameTf;
private Button addSquadBtn;
private ArrayList<String> elitesList, fastAtkList, heavySptList, hqList, lordsowList, specialCList, transportList, troopsList; //Declare the sublists that hold all the units for the type of unit
Unit_Type elites, fastAttk, heavySpt, hQ, lordsOW, specialC, transport, troops;
public AddUnitPane()
{
this.setVgap(15);
this.setHgap(20);
this.setAlignment(Pos.CENTER);
ColumnConstraints col1 = new ColumnConstraints();
col1.setHalignment(HPos.RIGHT);
this.getColumnConstraints().add(col1);
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
elitesList = createArrayList("Dreadnought", "Ironclad Dreadnought", "Venerable Dreadnought", "Assault Terminator", "Centurion Assault", "Command", "Honour Guard",
"Sternguard Veteran", "Terminator", "Vanguard Veterans");
fastAtkList = createArrayList("Attack Bike", "Stormhawk Interceptor", "Stormtalon Gunship", "Assault", "Bike", "Land Speeder", "Scout Bike");
heavySptList = createArrayList("Hunter", "Land Raider Crusader", "Land Raider Redeemer", "Land Raider", "Predator", "Stalker", "Stormraaven Gunship", "Vindicator",
"Whirlwind", "Centurion Devastator", "Devastator", "Thunderfire Cannon");
hqList = createArrayList("Captain", "Chaplain", "Librarian", "Techmarine");
lordsowList = createArrayList("Marneus Calger", "Roboute Guilliman");
specialCList = createArrayList("Antaro Chronus", "Cato Sicarius", "Ortan Cassius", "Torias Telion", "Varro Tigurius");
transportList = createArrayList("Drop Pod", "Land Speeder Storm", "Razorback", "Rhino");
troopsList = createArrayList("Scout", "Tactical");
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
elites = new Unit_Type("Elites");
//elites.addUnit(elitesList);
elites.addUnit("Dreadnought");
elites.addUnit("Ironclad Dreadnought");
fastAttk = new Unit_Type("Fast Attack");
fastAttk.addUnit("Attack Bike");
fastAttk.addUnit("Stormhawk Interceptor");
ObservableList<Unit_Type> unitTypeOList = FXCollections.observableArrayList(elites, fastAttk); //add each Unit_Type to an Observable List
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
unitTypeLbl = new Label("Select The Unit Class: "); //Initialise all of the labels
unitLbl = new Label("Select The Unit: ");
squadNameLbl = new Label("Squad Name: ");
squadSizeLbl = new Label("Squad Size");
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
unitTypeCombo = new ComboBox<Unit_Type>(); //Initialise the unitTypeCombo (first ComboBox)
unitTypeCombo.setItems(unitTypeOList); //Populate the unitTypeCombo with the UnitTypeOList (observable list) from line 82
//unitTypeCombo.getSelectionModel().selectFirst(); //Set the unitTypeCombo to show the first item
unitCombo = new ComboBox<>(); //Initialise the unitCombo (second ComboBox)
unitTypeCombo.valueProperty().addListener(new ChangeListener<Unit_Type>()
{
@Override
public void changed(ObservableValue<? extends Unit_Type> observable, Unit_Type oldValue, Unit_Type newValue)
{
unitCombo.setItems(newValue == null ? FXCollections.emptyObservableList() : newValue.getUnitsForType());
}
});
squadNameTf = new TextField();
squadSizeCombo = new ComboBox<>();
addSquadBtn = new Button("Add Squad");
this.add(unitTypeLbl, 0, 1);
this.add(unitTypeCombo, 1, 1);
this.add(unitLbl, 0, 2);
this.add(unitCombo, 1, 2);
this.add(squadNameLbl, 0, 3);
this.add(squadNameTf, 1, 3);
this.add(squadSizeLbl, 0, 4);
this.add(squadSizeCombo, 1, 4);
this.add(new HBox(), 0, 5);
this.add(addSquadBtn, 1, 5);
}
public void AddUnitHandler(EventHandler<ActionEvent> handler)
{
addSquadBtn.setOnAction(handler);
}
private static <T> ArrayList<T> createArrayList(T... items) //generates the unit lists
{
ArrayList<T> result = new ArrayList<>(items.length);
for (T item : items)
{
result.addAll(result);
}
return result;
}
}
Unit_Type Class
public class Unit_Type implements Serializable
{
private String typeName;
private ArrayList<String> units; //a unit type is an aggregation of units. Tried changing this type to ArrayList<String>
public Unit_Type(String typeName)
{
this.typeName = typeName;
units = new ArrayList<>();
}
public void addUnit(String u) //tried changing this parameter to ArrayList<String>
{
units.add(u);
}
public void setTypeName(String name)
{
typeName = name;
}
public ObservableList<String> getUnitsForType() //method used in the ChangeListener in the AddUnitPane class
{
ObservableList unitsOList = FXCollections.observableArrayList(units);
return unitsOList;
}
@Override
public String toString() //allows the ComboBox to display values correctly
{
return typeName;
}
}
我不会将支持列表存储在 Unit_Type
class 中。最好将 ObservableList
存储在字段中,编写自定义序列化方法并使字段成为瞬态。
这样你也可以使用
初始化单位类型elites.getUnitsForType().addAll("Dreadnought", "Ironclad Dreadnought", "Venerable Dreadnought", "Assault Terminator", "Centurion Assault", "Command", "Honour Guard",
"Sternguard Veteran", "Terminator", "Vanguard Veterans");
但在构造函数中初始化列表可能更短:
public class Unit_Type implements Serializable {
private String typeName;
private transient ObservableList<String> units;
private void writeObject(ObjectOutputStream stream)
throws IOException {
stream.defaultWriteObject();
// serialize units list as string array
stream.writeObject(units.toArray());
}
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
// read array from stream and initialize list with it
// Note: because of the way we write objects of this type we can use the raw type here safely
units = (ObservableList) FXCollections.<Object>observableArrayList((Object[])stream.readObject());
}
public Unit_Type(String typeName, String... units) {
this.typeName = typeName;
this.units = FXCollections.observableArrayList(units);
}
public void setTypeName(String name) {
typeName = name;
}
public ObservableList<String> getUnitsForType() {
return units;
}
@Override
public String toString() {
return typeName;
}
}
elites = new Unit_Type("Elites",
"Dreadnought", "Ironclad Dreadnought", "Venerable Dreadnought",
"Assault Terminator", "Centurion Assault", "Command",
"Honour Guard", "Sternguard Veteran", "Terminator",
"Vanguard Veterans"
);
在(反)序列化期间,除 ObservableList
之外的所有内容都使用默认序列化机制处理。
stream.defaultWriteObject();
stream.defaultReadObject();
transient
关键字导致该字段被忽略(ObservableList
通常不可序列化,因为 hard/impossible 持久化监听器,尤其是当它们引用 UI 元素)。在这种情况下,我们只是 read/write 列表中的字符串作为数组:
stream.writeObject(units.toArray());
units = (ObservableList) FXCollections.<Object>observableArrayList((Object[])stream.readObject());
有关详细信息,请参阅此处的自定义默认协议部分:http://www.oracle.com/technetwork/articles/java/javaserial-1536170.html