JavaFX - 在ComboBox上使用ChangeListener使用ArrayList填充另一个ComboBox导致ArrayList为空

时间:2017-09-21 11:21:26

标签: arraylist javafx combobox observablelist changelistener

两天前我发布了this question关于将一个ChangeListener附加到ComboBox以指示在第二个ComboBox上显示什么输出。简而言之,我们的想法是第一个ComboBox显示一些单位类型,并且取决于所选的单位类型将取决于根据所选单位类型在第二个ComboBox中显示的单位列表。例如,如果您选择" Elites"第一个ComboBox中的单元类型第二个ComboBox应填充所有" Elite"单元。

现在,回复my original question的人对ChangeListener中的代码提供了很多帮助,我确实设法使其正常运行。但是,只有在一次将一个单元添加到单元类型时,它才有效。理想情况下,我想要将UnitList或其他一些合适的单位数据结构添加到单元类型,而不是单个单元。这会减少代码,并且更有效率。

因此,您可以在此代码段中看到,目前elites单元类型addUnit方法一次只接受单个字符串,

elites = new Unit_Type("Elites");
        elites.addUnit("Dreadnought");
        elites.addUnit("Ironclad Dreadnought");

我希望如此,所以我可以向精英单元类型addUnit方法添加一个ArrayList或simular数据结构,就像这段代码片段一样:

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个列表,每个列表代表不同的单元类型。 现在我尝试更改Unit Type类中的代码,使单元ArrayList的类型为ArrayList<String>,并尝试更改addUnit方法以接受ArrayList<String>的参数但是每当我运行程序,在第一个ComboBox中选择一个Unit Type,导致第二个ComboBox中出现一个空数组。

以下是AddUnitPane类(视图)和Unit_Type类(模型)的其余代码

AddUnitPane类

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类

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;        
    }
}

1 个答案:

答案 0 :(得分:1)

我不会将支持列表存储在Unit_Type类中。最好将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 s一般不可序列化,因为如果它们引用UI元素,则很难/不可能持久监听器。在这种情况下,我们只需将列表中的字符串读/写为数组:

stream.writeObject(units.toArray());
units = (ObservableList) FXCollections.<Object>observableArrayList((Object[])stream.readObject());

有关详细信息,请参阅此处的自定义默认协议部分:http://www.oracle.com/technetwork/articles/java/javaserial-1536170.html