所以我对JavaFX很陌生,并且一直在努力让听众在我的TableView上正常工作。我发现了几个不同的例子,但似乎没有一个像我期望的那样工作,或者我的代码中的其他地方存在一个阻止它工作的问题。
以下是我的initialize方法中侦听器代码的片段:
bookList.addListener((Change<? extends LibraryBook> c) -> {
while (c.next()) {
if (c.wasAdded()) {
MainController.getInstance().setFieldChange(true);
}
if (c.wasRemoved()) {
MainController.getInstance().setFieldChange(true);
}
if (c.wasUpdated()) {
MainController.getInstance().setFieldChange(true);
}
}
});
tvLibraryTable.setItems(bookList);
bookName.setCellValueFactory(new PropertyValueFactory<>("book"));
quantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
我的observableList目前在我的构造函数中初始化(但我也将它移动到其他几个地方:
if(library.getBooks() != null) {
this.newList = new ArrayList<LibraryBook>(library.getBooks());
this.bookList = FXCollections.observableArrayList(library.getBooks());
因此,在我的控制器视图的其余部分,我有按钮可以从列表中添加和删除书籍,如果您双击列表中的项目,则可以更新数量。我正在为所有这些选项使用自定义模式对话框。当我从对话框返回结果时,我更新了我的bookList,我认为这就是我的问题所在。这是控制器类的完整代码。而且我很确定代码非常粗糙,但它目前正在运行...减去tableview监听器。
package app;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfWriter;
import javafx.beans.Observable;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextInputDialog;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.util.Pair;
import javafx.util.converter.NumberStringConverter;
import javafx.scene.control.Alert.AlertType;
import models.Book;
import models.Library;
import models.LibraryBook;
import models.ValidationException;
import view.ViewType;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Dialog;
public class LibraryDetailViewController implements Initializable {
private static Logger logger = LogManager.getLogger();
private Library library;
private Library origLibrary;
private List<LibraryBook> newList;
private ObservableList<LibraryBook> bookList;
@FXML private TextField tfLibrary;
@FXML private TableView<LibraryBook> tvLibraryTable;
@FXML private TableColumn<LibraryBook, Book> bookName;
@FXML private TableColumn<LibraryBook, Integer> quantity;
@FXML private ComboBox<Book> cbBook;
@FXML private Button saveButton, auditTrail, inventoryReport, addBook, deleteBook;
@FXML private Label saveLabel;
public LibraryDetailViewController(Library library) {
this.library = library;
this.origLibrary = new Library(library);
if(library.getBooks() != null) {
this.newList = new ArrayList<LibraryBook>(library.getBooks());
this.bookList = FXCollections.observableArrayList(library.getBooks());
} else {
this.newList = new ArrayList<LibraryBook>();
this.bookList = FXCollections.observableArrayList();
}
}
//event handler for buttons
@FXML private void handleButtonAction(ActionEvent event) {
Object source = event.getSource();
if (source == saveButton) {
try {
saveLibraryFields();
saveLabel.setText(library.saveLibrary(origLibrary));
origLibrary = library;
} catch (SQLException e) {
logger.error(e);
Alert alert = new Alert(AlertType.ERROR);
alert.setHeaderText("Save Error");
alert.setContentText("Error saving data. Please check fields and try again.");
alert.showAndWait();
//revert to original model data on error
library = origLibrary;
} catch (ValidationException e) {
Alert alert = new Alert(AlertType.ERROR);
alert.setHeaderText(e.getMessage());
alert.showAndWait();
}
} else if(source == auditTrail) {
if(library.getId() == 0) {
Alert alert = new Alert(AlertType.WARNING);
alert.setHeaderText("Audit Trail Error");
alert.setContentText("No audit trail exists! Please save the library first.");
alert.showAndWait();
} else {
MainController.getInstance().changeView(ViewType.LIBRARY_AUDITTRAIL, library);
}
} else if(source == inventoryReport) {
try {
createPdf("Inventory Report.pdf");
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Success");
alert.setHeaderText(null);
alert.setContentText("The report has been created!");
alert.showAndWait();
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
} else if(source == addBook) {
// create modal dialogue box
List<Book> books = new ArrayList<Book>(MainController.getInstance().getBookGateway().getBooks());
Dialog<Pair<Book, String>> dialog = new Dialog<>();
dialog.setTitle("Book List");
dialog.setHeaderText("Add a book to the library");
dialog.setGraphic(new ImageView(this.getClass().getResource("/view/book-stack.png").toString()));
ButtonType okButtonType = new ButtonType("Ok", ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(okButtonType, ButtonType.CANCEL);
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20, 150, 10, 10));
ObservableList<Book> bookListOptions = FXCollections.observableArrayList(books);
ChoiceBox<Book> cbBooks = new ChoiceBox<Book>(bookListOptions);
TextField tfQuantity = new TextField();
tfQuantity.setTextFormatter(new TextFormatter<>(new NumberStringConverter()));
tfQuantity.setPromptText("Quantity");
grid.add(new Label("Please select a book"), 0, 0);
grid.add(cbBooks, 1, 0);
grid.add(new Label("Please enter a quantity"), 0, 1);
grid.add(tfQuantity, 1, 1);
dialog.getDialogPane().setContent(grid);
// convert results that were selected
dialog.setResultConverter(dialogButton -> {
if (dialogButton == okButtonType) {
return new Pair<>(cbBooks.getSelectionModel().getSelectedItem(), tfQuantity.getText());
}
return null;
});
// return results of dialogue box to main view and reinitialize tableView list
Optional<Pair<Book, String>> result = dialog.showAndWait();
result.ifPresent(book -> {
newList.add(new LibraryBook(book.getKey(), Integer.parseInt(book.getValue()), true));
library.setBooks(newList);
bookList = FXCollections.observableArrayList(library.getBooks());
bookName.setCellValueFactory(new PropertyValueFactory<>("book"));
quantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
tvLibraryTable.setItems(bookList);
});
} else if(source == deleteBook) {
newList.remove(tvLibraryTable.getSelectionModel().getSelectedItem());
library.setBooks(newList);
bookList = FXCollections.observableArrayList(library.getBooks());
bookName.setCellValueFactory(new PropertyValueFactory<>("book"));
quantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
tvLibraryTable.setItems(bookList);
}
}
/**
* event handler for double clicking a book from the ListView
*
* @param event
*/
@FXML private void handleMouseClick(MouseEvent event) {
if(event.getButton().equals(MouseButton.PRIMARY)){
if(event.getClickCount() == 2){
if (tvLibraryTable.getSelectionModel().isEmpty()) {
Alert alert = new Alert(AlertType.WARNING);
alert.setHeaderText("Update Quantity Error");
alert.setContentText("No book was selected!");
alert.showAndWait();
} else {
LibraryBook lb = tvLibraryTable.getSelectionModel().getSelectedItem();
TextInputDialog dialog = new TextInputDialog();
dialog.setTitle("Edit Book");
dialog.setHeaderText("Please enter the new quantity.");
dialog.setContentText("Quantity:");
Optional<String> result = dialog.showAndWait();
result.ifPresent(quantity -> {
lb.setQuantity(Integer.parseInt(quantity));
tvLibraryTable.refresh();
});
}
}
}
}
public void saveLibraryDetail() {
try {
saveLibraryFields();
library.saveLibrary(origLibrary);
origLibrary = library;
} catch (SQLException e) {
logger.error(e);
library = origLibrary;
} catch (ValidationException e) {
Alert alert = new Alert(AlertType.ERROR);
alert.setHeaderText(e.getMessage());
alert.showAndWait();
}
}
public void saveLibraryFields() {
library.setLibraryName(tfLibrary.getText());
library.setBooks(tvLibraryTable.getItems());
}
private class MyFooter extends PdfPageEventHelper {
Font ffont = new Font(Font.FontFamily.UNDEFINED, 18, Font.ITALIC);
public void onEndPage(PdfWriter writer, Document document) {
PdfContentByte cb = writer.getDirectContent();
Phrase header = new Phrase(library.getLibraryName(), ffont);
ColumnText.showTextAligned(cb, Element.ALIGN_CENTER,
header,
(document.right() - document.left()) / 2 + document.leftMargin(),
document.top() + 10, 0);
}
}
public void createPdf(String filename) throws IOException, DocumentException {
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(filename));
MyFooter event = new MyFooter();
writer.setPageEvent(event);
document.open();
//create table with 4 evenly-spaced columns
PdfPTable table = new PdfPTable(4);
table.addCell("Title:");
table.addCell("Author:");
table.addCell("Publisher:");
table.addCell("Quantity:");
table.setHeaderRows(1);
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
for(int aw = 0; aw < library.getBooks().size(); aw++){
table.addCell(library.getBooks().get(aw).getBook().getTitle());
table.addCell(library.getBooks().get(aw).getBook().getAuthor().getFullName());
table.addCell(library.getBooks().get(aw).getBook().getPublisher());
table.addCell(String.valueOf(library.getBooks().get(aw).getQuantity()));
table.completeRow();
}
document.add(table);
document.close();
}
@Override
public void initialize(URL location, ResourceBundle resources) {
//init field values
tfLibrary.setText(library.getLibraryName());
/*if(library.getBooks() != null) {
ObservableList<LibraryBook> booksList = FXCollections.observableArrayList(library.getBooks());
bookName.setCellValueFactory(new PropertyValueFactory<>("book"));
quantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
tvLibraryTable.setItems(booksList);
}*/
//event listeners for field changes
if(library.getId() == 0) {
MainController.getInstance().setFieldChange(true);
}
tfLibrary.textProperty().addListener(new ChangeListener<String>() {
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
MainController.getInstance().setFieldChange(true);
}
});
/**
* Unable to get listener for tableview to work properly :(
*
ObservableList<LibraryBook> bookList = FXCollections.observableArrayList(book ->
new Observable[] {
(Observable) book.getBook(),
book.getObsQuantity()
});
*/
bookList.addListener((Change<? extends LibraryBook> c) -> {
while (c.next()) {
if (c.wasAdded()) {
MainController.getInstance().setFieldChange(true);
}
if (c.wasRemoved()) {
MainController.getInstance().setFieldChange(true);
}
if (c.wasUpdated()) {
MainController.getInstance().setFieldChange(true);
}
}
});
tvLibraryTable.setItems(bookList);
bookName.setCellValueFactory(new PropertyValueFactory<>("book"));
quantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
}
}
这是我的fxml代码:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane id="pane-bg" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label layoutX="106.0" layoutY="63.0" text="Library Name:">
<font>
<Font name="Serif Regular" size="18.0" />
</font>
</Label>
<Button fx:id="saveButton" layoutX="465.0" layoutY="126.0" mnemonicParsing="false" onAction="#handleButtonAction" text="Save">
<font>
<Font name="Serif Regular" size="14.0" />
</font>
</Button>
<TextField fx:id="tfLibrary" layoutX="224.0" layoutY="61.0" />
<Label fx:id="saveLabel" layoutX="431.0" layoutY="277.0" textFill="#f20909">
<font>
<Font name="Serif Regular" size="18.0" />
</font>
</Label>
<Button fx:id="auditTrail" layoutX="440.0" layoutY="170.0" mnemonicParsing="false" onAction="#handleButtonAction" text="View Audit Trail" />
<TableView fx:id="tvLibraryTable" editable="true" layoutX="174.0" layoutY="104.0" onMouseClicked="#handleMouseClick" prefHeight="215.0" prefWidth="248.0">
<columns>
<TableColumn fx:id="bookName" prefWidth="169.0" text="Book Name" />
<TableColumn fx:id="quantity" prefWidth="77.0" text="Quantity" />
</columns>
</TableView>
<Button fx:id="inventoryReport" layoutX="437.0" layoutY="215.0" mnemonicParsing="false" onAction="#handleButtonAction" text="Inventory Report" />
<Button fx:id="addBook" layoutX="174.0" layoutY="325.0" mnemonicParsing="false" onAction="#handleButtonAction" text="Add Book">
<font>
<Font name="Serif Regular" size="14.0" />
</font>
</Button>
<Button fx:id="deleteBook" layoutX="331.0" layoutY="325.0" mnemonicParsing="false" onAction="#handleButtonAction" text="Delete Book">
<font>
<Font name="Serif Regular" size="14.0" />
</font>
</Button>
<!-- <Button fx:id="backToListButton" layoutX="309.0" layoutY="286.0" mnemonicParsing="false" onAction="#handleButtonAction" text="Back to Author List" /> -->
</children>
</AnchorPane>