我想问的是,当我尝试从索引为0的supplement的表视图中删除第一行时,为什么会出现IndexOutOfBoundsException。我正在使用一个按钮删除该行
更新:更新代码,使其具有最少的可重现示例
SupplementTest.java
public class SupplementTest extends Application {
WindowController windowGUI = new WindowController();
Stage stageGUI;
Scene sceneGUI;
@Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader assignment2 = new FXMLLoader(getClass().getResource("SupplementFXML.fxml"));
Parent fxmlFile = assignment2.load();
try {
stageGUI = primaryStage;
windowGUI.initialize();
sceneGUI = new Scene(fxmlFile, 250, 350);
stageGUI.setScene(sceneGUI);
stageGUI.setTitle("Supplement");
stageGUI.show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
WindowController.java
public class WindowController {
Stage newWindow = new Stage();
boolean deleteSupplement;
@FXML
private GridPane primaryGrid = new GridPane();
@FXML
private Label supplementLabel = new Label();
@FXML
private Button deleteBtn = new Button(), addBtn = new Button();
public TableView<Supplement> supplementView = new TableView<>();
int suppIndex;
ArrayList<Supplement> supplementList = new ArrayList<>();
// initialize Method
public void initialize() {
newWindow.initModality(Modality.APPLICATION_MODAL);
newWindow.setOnCloseRequest(e -> e.consume());
initializeWindow();
updateSupplementList();
}
public void initializeWindow() {
deleteSupplement = false;
TableColumn<Supplement, String> suppNameColumn = new TableColumn<>("Name");
suppNameColumn.setCellValueFactory(new PropertyValueFactory<>("supplementName"));
TableColumn<Supplement, Double> suppCostColumn = new TableColumn<>("Weekly Cost");
suppCostColumn.setCellValueFactory(new PropertyValueFactory<>("weeklyCost"));
supplementView.getColumns().addAll(suppNameColumn, suppCostColumn);
supplementView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
suppIndex = supplementView.getSelectionModel().getSelectedIndex();
addBtn.setOnAction(e -> {
supplementList.add(new Supplement("Test1", 10));
supplementList.add(new Supplement("Test2", 20));
supplementList.add(new Supplement("Test3", 15));
updateSupplementList();
});
// remove button
deleteBtn.setOnAction(e -> {
deleteSupplement = true;
deleteSupplement();
});
}
public void updateSupplementList() {
supplementView.getItems().clear();
if (supplementList.size() > 0) {
for(int i = 0; i < supplementList.size(); i++) {
Supplement supplement = new Supplement(supplementList.get(i).getSupplementName(),
supplementList.get(i).getWeeklyCost());
supplementView.getItems().add(supplement);
}
}
}
public void deleteSupplement() {
try {
ObservableList<Supplement> supplementSelected, allSupplement;
allSupplement = supplementView.getItems();
supplementSelected = supplementView.getSelectionModel().getSelectedItems();
supplementSelected.forEach(allSupplement::remove);
supplementList.remove(suppIndex);
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
Supplement.java
public class Supplement implements Serializable {
private String supplementName;
private double weeklyCost;
public Supplement() {
this.supplementName = "";
this.weeklyCost = 0.00;
}
public Supplement(String suppName, double weeklyCost) {
this.supplementName = suppName;
this.weeklyCost = weeklyCost;
}
public String getSupplementName() {
return supplementName;
}
public double getWeeklyCost() {
return weeklyCost;
}
public void setSupplementName(String supplementName) {
this.supplementName = supplementName;
}
public void setWeeklyCost(double weeklyCost) {
this.weeklyCost = weeklyCost;
}
}
如何修复此问题,以便在删除表视图中的任何索引时,不会出现IndexOutOfBoundsException?
1条答案
按热度按时间eagi6jfj1#
很难确定是什么导致了异常,因为您的代码既不完整(因此这里没有人可以复制、粘贴并运行它来重现错误),又非常混乱(其中充满了看起来不必要的代码)。但是:
您似乎在执行两种不同的操作来从表中删除选定的项目:
这是一个尝试删除 * 所有 * 选定的项目(虽然我不相信它会工作,如果一个以上的项目被选中)
和
这将删除选定的项目,如选择模型中的selected index属性所定义。(在单选模型中,它是当前选定的项目;在多选模型中,它是最后选定的项目;如果未选择任何内容,则为
-1
。)后者不起作用,因为您只在初始化代码中设置了
suppIndex
:当然,在执行这段代码时,用户还没有机会选择任何内容(此时甚至没有显示表),因此没有选择任何内容,因此
suppIndex
被赋值为-1
。你会得到一个明显的例外。
如果您只支持单选,并且想删除当前选中的项目(或多选中最后选中的项目),只需在当时获取选择即可,您可能还想检查是否选中了某些项目:
对此稍有不同,我认为更可取的是使用实际对象而不是其索引:
现在,当然(在一个对您的许多代码都通用的主题中),您可以完全删除
suppIndex
;它是完全多余的。如果您希望支持多重选择,并删除 * 所有 * 选定项,那么如果选择了多个项,您当前拥有的代码将导致问题。问题是,如果从表的项列表中删除选定项,则也将从选择模型的选定项列表中删除它。因此,当您使用
forEach(...)
对代码中的选定项列表(supplementSelected
)进行迭代时,它会发生变化,这将引发ConcurrentModificationException
。若要避免这种情况,您应该将选取项目的清单复制到另一个清单中,然后移除这些项目:
当然,这段代码也适用于单个选择(当列表的长度总是0或1时)。
为了解决其他几个问题:保留一个单独的
Supplement
项列表确实没有意义。表已经保留了该列表,您可以随时使用supplementView.getItems()
引用它。(如果您希望在其他地方引用该列表,例如在MVC设计的模型中,您应该确保只有对现有列表的第二个引用;不创建新列表。)特别是,您不应该在每次向列表中添加新项时完全从头开始重建表。从代码中完全删除冗余的
supplementList
。完全删除updateSupplementList()
;首先,它做了太多的工作,其次(也是更重要的),仅仅因为你添加了一个新项目,它就会替换所有现有的项目。2这会丢失重要的信息(例如,它会重置选择)。要添加新项目,只需
您的代码中还有许多其他部分没有任何意义,例如:
deleteSupplement
变量。这似乎没有任何意义。deleteSupplement
方法中的try
-catch
。这里唯一可以抛出的异常是由编程逻辑错误(例如您看到的那个)引起的未检查异常。捕获这些异常没有意义;您需要修复这些错误以便不会抛出异常。@FXML
注解。您永远不应该初始化注解为@FXML
的字段。这个注解表示FXMLLoader
会初始化这些字段。在这个案例中(就我所知),这些字段什至与FXML档案完全没有相关,因此应该移除注解。