我正在為 Uni 的一個專案撰寫一個小程式,它基本上是一個庫程式來管理書籍和讀取/寫入 JSON 檔案而不是使用資料庫,因為它會更簡單,因為它是我的第一個適當的 Java 應用程式。
我正在使用 TextField 過濾包含所有書籍標題的 ListView,它可以作業,它在串列中顯示正確的書籍并在選擇該書籍時更新螢屏上的相應資訊,問題是即使程式按預期作業,每次我更新搜索欄位時都會引發錯誤,我得到一個ArrayIndexOutOfBoundsException. 完整的堆疊如下:
Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 7
at javafx.base@19-ea/javafx.collections.transformation.FilteredList.get(FilteredList.java:172)
at com.libraryproject.javalibrary/com.libraryproject.javalibrary.MainViewController.populateDetails(MainViewController.java:200)
at com.libraryproject.javalibrary/com.libraryproject.javalibrary.MainViewController.lambda$initialize$3(MainViewController.java:127)
at javafx.graphics@19-ea/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at javafx.graphics@19-ea/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456)
at javafx.graphics@19-ea/com.sun.glass.ui.InvokeLaterDispatcher$Future.run$$$capture(InvokeLaterDispatcher.java:96)
at javafx.graphics@19-ea/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java)
at javafx.graphics@19-ea/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics@19-ea/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
經過一番谷歌搜索后,有些人建議從用戶輸入更新 GUI 時,應該在應用程式執行緒中進行,老實說,我不確定這意味著什么,但無論如何我遵循了建議并包裝了函式然后會更新 a 中的 UI 變數Platform.runLater(() -> {},但問題仍然存在,它是上面的堆疊,此時我完全不知道問題可能是什么,所以,按照堆疊發布,讓我們看看部分的代碼顯示:
我正在使用 FilteredList 來過濾 listrView 使用搜索,這是管理它的代碼和大部分初始化方法:
private FilteredList<Book> filteredBooks;
...
...
// inside the initialize method
@Override
public void initialize(URL arg0, ResourceBundle arg1) {
// Populate the variable we use throughout the program with the data from the JSON file
filteredBooks = new FilteredList<Book>(handleJSON.getBooks());
// Then update the list view for the first time
populateView(filteredBooks);
...
...
// section of code responsible to check for search changes, when found, fires populateView once more, this time with the variable updated.
searchField.textProperty().addListener((obs, oldText, newText) -> {
filteredBooks.setPredicate(book -> {
if(newText == null || newText.isEmpty() || newText.isBlank()) {
return true;
}
String lowerCaseCompare = newText.toLowerCase();
if(book.getTitle().toLowerCase().contains(lowerCaseCompare)) {
return true;
}
return false;
});
Platform.runLater(() -> populateView(filteredBooks));
}); // Listener
...
...
...
// This one handles the selection of an item in the list, when selected, the fields on the other side of the windows will get populated with the respective data from the book based on the id from the list, since they essentialy share the same FilteredList
listView.getSelectionModel().selectedItemProperty().addListener((obs, oldSel, newSel) -> {
Platform.runLater(() -> {
populateDetails(listView.getSelectionModel().selectedIndexProperty().getValue(), filteredBooks);
editButton.setDisable(false);
});
如您所見,我用 包裝了所有將更新 ListView 和視窗中的欄位的函式Platform.runLater,但它似乎沒有幫助。
現在對于第一次打開程式并且每次搜索欄位發生變化時觸發的 populateView 函式:
public void populateView(FilteredList<Book> booksList) {
// clears the listview to avoid old elements stacking in the list.
listView.getSelectionModel().clearSelection();
listView.getItems().clear();
ObservableList<String> rawTitles = FXCollections.observableArrayList();
for(Book book: booksList) {
rawTitles.add(book.getTitle());
}
listView.setItems(rawTitles);
} // populateView()
最后但并非最不重要的是 populateDetails 函式,它根據串列選擇填充有關書籍的欄位:
public void populateDetails(Integer selectedBookID, FilteredList<Book> books) {
Book currentBook = books.get(selectedBookID);
titleValue.setText(currentBook.getTitle());
authorValue.setText(currentBook.getAuthor());
languageValue.setText(currentBook.getLanguage());
genreValue.setText(currentBook.getGenre());
pagesValue.setText(currentBook.getPages().toString());
yearValue.setText(currentBook.getRelease().toString());
if (currentBook.getAvailable()) {
availableRadio.setSelected(true);
} else {
unavailableRadio.setSelected(true);
}
} // populateDetails
這基本上是我嘗試在不同的地方使用 runLater 來確定,我仍然得到相同的堆疊,知道是什么原因造成的嗎?
uj5u.com熱心網友回復:
堆疊跟蹤準確地告訴您問題所在。ArrayIndexOutOfBoundsException當您在方法的第 200 行使用 value呼叫get(..)a時,就會發生這種情況。查看您的代碼,此行必須是該行FilteredList-1MainViewController.javapopulateDetails(...)
Book currentBook = books.get(selectedBookID);
所以selectedBookID一定是罪魁禍首,具有價值-1。
selectedBookID是傳遞給該方法的引數,并且您在方法MainController.java中的 lambda 運算式中從第 127 行呼叫該initialize()方法。(同樣,此資訊在堆疊跟蹤中。)您傳遞的值是
listView.getSelectionModel().selectedIndexProperty().getValue()
檔案明確告訴您何時發生這種情況:
選定的索引是 -1,表示沒有選擇,或者是基礎資料模型大小范圍內的整數值。
因此,您的填充詳細資訊需要處理未選擇任何內容的情況(可能通過清除文本欄位)。我認為聽selectedItemProperty()而不是聽更干凈selectedIndexProperty(),因為它直接為您提供所選內容Book(或者null如果沒有選擇任何內容),并且您不必Book從串列中檢索:
public void populateDetails(Book currentBook) {
if (currentBook == null) {
titleValue.setText("");
authorValue.setText("");
languageValue.setText("");
genreValue.setText("");
pagesValue.setText("");
yearValue.setText("");
availableRadio.setSelected(false);
unavailableRadio.setSelected(false);
} else {
titleValue.setText(currentBook.getTitle());
authorValue.setText(currentBook.getAuthor());
languageValue.setText(currentBook.getLanguage());
genreValue.setText(currentBook.getGenre());
pagesValue.setText(currentBook.getPages().toString());
yearValue.setText(currentBook.getRelease().toString());
if (currentBook.getAvailable()) {
availableRadio.setSelected(true);
} else {
unavailableRadio.setSelected(true);
}
}
}
你的代碼太過分了;基本上不需要這個populateView()方法。當您更改謂詞時,過濾后的串列將更新其內容,并通知觀察者其內容已更改。所以你應該直接將串列視圖的專案串列設定為過濾串列。然后您的搜索欄位監聽器只需更新謂詞,串列視圖將自動更新。
洗掉populateView()方法并將方法更新initialize()為:
public void initialize(URL arg0, ResourceBundle arg1) {
// Populate the variable we use throughout the program with the data from the JSON file
filteredBooks = new FilteredList<Book>(handleJSON.getBooks());
listView.setItems(filteredBooks);
// ...
// ...
// section of code responsible to check for search changes, when found, fires populateView once more, this time with the variable updated.
searchField.textProperty().addListener((obs, oldText, newText) -> {
filteredBooks.setPredicate(book -> {
if(newText == null || newText.isEmpty() || newText.isBlank()) {
return true;
}
String lowerCaseCompare = newText.toLowerCase();
return book.getTitle().toLowerCase().contains(lowerCaseCompare)
});
}); // Listener
// ...
// This one handles the selection of an item in the list, when selected, the fields on the other side of the windows will get populated with the respective data from the book based on the id from the list, since they essentialy share the same FilteredList
listView.getSelectionModel().selectedItemProperty().addListener(
(obs, oldSel, newSel) -> populateDetails(newSel)
);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/528828.html
下一篇:無法處理部署“myapp-ws.war”的階段POST_MODULE原因:java.lang.RuntimeException:WFLYSRV0177:反射時出錯
