Не удается обновить ячейки TableView при добавлении новых данных - PullRequest
0 голосов
/ 14 апреля 2019

Два дня я пытался заставить ячейки TableView обновлять свое содержимое при добавлении новых данных в базовую модель данных.

Это простое приложение, которое отображает информацию о контактах в TableView. Список контактов представляет собой ObservableList , который хранится в файле XML. Модель контактных данных использует JavaFX properties для своих полей.

Новые контакты добавляются путем нажатия пункта меню «Добавить» на панели инструментов ( showAddContactDialog () ). Откроется диалоговое окно, где мы можем ввести контактную информацию. Затем контакт сохраняется в списке контактов, а затем сохраняется в XML-файле, который будет загружен при следующем запуске приложения. При запуске приложения контакты корректно отображаются в табличном представлении, но когда я добавляю новое, оно не добавляется в табличное представление, а сохраняется только в списке контактов и в XML-файле.

Не знаю, чего мне не хватает, нужна помощь, пожалуйста?

Я перепробовал много решений для stackoverflow и других, но ни одно из них не было успешным.

Ниже приведен код для класса контактов:

public class Contact {
private SimpleStringProperty mFirstName;
private SimpleStringProperty mLastName;
private SimpleStringProperty mPhoneNumber;
private SimpleStringProperty mNotes;

public Contact(){...}
public Contact(String firstName, String lastName, String phoneNumber, String notes){
    this.mFirstName = new SimpleStringProperty(firstName);
    this.mLastName = new SimpleStringProperty(lastName);
    this.mPhoneNumber = new SimpleStringProperty(phoneNumber);
    this.mNotes = new SimpleStringProperty(notes);
}

String getFirstName() { return mFirstName.get(); }
public SimpleStringProperty mFirstNameProperty() { return mFirstName; }
void setFirstName(String value) { mFirstName.set(value); }

String getLastName() { return mLastName.get(); }
public SimpleStringProperty mLastNameProperty() { return mLastName; }
void setLastName(String value) { mLastName.set(value); }

String getPhoneNumber() { return mPhoneNumber.get(); }
public SimpleStringProperty mPhoneNumberProperty() { return mPhoneNumber; }
void setPhoneNumber(String value) { mPhoneNumber.set(value); }

String getNotes() { return mNotes.get(); }
public SimpleStringProperty mNotesProperty() { return mNotes;}
void setNotes(String value) { mNotes.set(value); }

Ниже приведен код для класса ContactData , который отвечает за загрузку и сохранение контактов из файла XML и за операции добавления и удаления контактов из списка:

public class ContactData {
private static final String CONTACTS_FILE = "contacts.xml";
private static final String CONTACT = "contact";
private static final String FIRST_NAME = "first_name";
private static final String LAST_NAME = "last_name";
private static final String PHONE_NUMBER = "phone_number";
private static final String NOTES = "notes";

private ObservableList<Contact> contacts;

public ContactData() {
   contacts = FXCollections.observableArrayList();
   loadContacts();
}

public void addContact(Contact newContact){
    contacts.add(newContact);
    saveContacts();
}

public void deleteContact(Contact contact){
    contacts.remove(contact);
    saveContacts();
}

public ObservableList<Contact> getContacts() {
    return FXCollections.observableList(contacts);
}

public void loadContacts() {
    try {
        // First, create a new XMLInputFactory
        XMLInputFactory inputFactory = XMLInputFactory.newInstance();
        // Setup a new eventReader
        InputStream in = new FileInputStream(CONTACTS_FILE);
        XMLEventReader eventReader = inputFactory.createXMLEventReader(in);
        // read the XML document
        Contact contact = null;

        while (eventReader.hasNext()) {
            XMLEvent event = eventReader.nextEvent();

            if (event.isStartElement()) {
                StartElement startElement = event.asStartElement();
                // If we have a contact item, we create a new contact
                if (startElement.getName().getLocalPart().equals(CONTACT)) {
                    contact = new Contact();
                    continue;
                }

                if (event.isStartElement()) {
                    if (event.asStartElement().getName().getLocalPart()
                            .equals(FIRST_NAME)) {
                        event = eventReader.nextEvent();
                        contact.setFirstName(event.asCharacters().getData());
                        continue;
                    }
                }
                if (event.asStartElement().getName().getLocalPart()
                        .equals(LAST_NAME)) {
                    event = eventReader.nextEvent();
                    contact.setLastName(event.asCharacters().getData());
                    continue;
                }

                if (event.asStartElement().getName().getLocalPart()
                        .equals(PHONE_NUMBER)) {
                    event = eventReader.nextEvent();
                    contact.setPhoneNumber(event.asCharacters().getData());
                    continue;
                }

                if (event.asStartElement().getName().getLocalPart()
                        .equals(NOTES)) {
                    event = eventReader.nextEvent();
                    contact.setNotes(event.asCharacters().getData());
                    continue;
                }
            }

            // If we reach the end of a contact element, we add it to the list
            if (event.isEndElement()) {
                EndElement endElement = event.asEndElement();
                if (endElement.getName().getLocalPart().equals(CONTACT)) {
                    contacts.add(contact);
                }
            }
        }
    }
    catch (FileNotFoundException e) {
        //e.printStackTrace();
    }
    catch (XMLStreamException e) {
        e.printStackTrace();
    }
}

public void saveContacts() {
    try {
        // create an XMLOutputFactory
        XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
        // create XMLEventWriter
        XMLEventWriter eventWriter = outputFactory
                .createXMLEventWriter(new FileOutputStream(CONTACTS_FILE));
        // create an EventFactory
        XMLEventFactory eventFactory = XMLEventFactory.newInstance();
        XMLEvent end = eventFactory.createDTD("\n");
        // create and write Start Tag
        StartDocument startDocument = eventFactory.createStartDocument();
        eventWriter.add(startDocument);
        eventWriter.add(end);

        StartElement contactsStartElement = eventFactory.createStartElement("",
                "", "contacts");
        eventWriter.add(contactsStartElement);
        eventWriter.add(end);

        for (Contact contact: contacts) {
            saveContact(eventWriter, eventFactory, contact);
        }

        eventWriter.add(eventFactory.createEndElement("", "", "contacts"));
        eventWriter.add(end);
        eventWriter.add(eventFactory.createEndDocument());
        eventWriter.close();
    }
    catch (FileNotFoundException e) {
        System.out.println("Problem with Contacts file: " + e.getMessage());
        e.printStackTrace();
    }
    catch (XMLStreamException e) {
        System.out.println("Problem writing contact: " + e.getMessage());
        e.printStackTrace();
    }
}

private void saveContact(XMLEventWriter eventWriter, XMLEventFactory eventFactory, Contact contact)
        throws FileNotFoundException, XMLStreamException {

    XMLEvent end = eventFactory.createDTD("\n");

    // create contact open tag
    StartElement configStartElement = eventFactory.createStartElement("",
            "", CONTACT);
    eventWriter.add(configStartElement);
    eventWriter.add(end);
    // Write the different nodes
    createNode(eventWriter, FIRST_NAME, contact.getFirstName());
    createNode(eventWriter, LAST_NAME, contact.getLastName());
    createNode(eventWriter, PHONE_NUMBER, contact.getPhoneNumber());
    createNode(eventWriter, NOTES, contact.getNotes());

    eventWriter.add(eventFactory.createEndElement("", "", CONTACT));
    eventWriter.add(end);
}

private void createNode(XMLEventWriter eventWriter, String name,
                        String value) throws XMLStreamException {

    XMLEventFactory eventFactory = XMLEventFactory.newInstance();
    XMLEvent end = eventFactory.createDTD("\n");
    XMLEvent tab = eventFactory.createDTD("\t");
    // create Start node
    StartElement sElement = eventFactory.createStartElement("", "", name);
    eventWriter.add(tab);
    eventWriter.add(sElement);
    // create Content
    Characters characters = eventFactory.createCharacters(value);
    eventWriter.add(characters);
    // create End node
    EndElement eElement = eventFactory.createEndElement("", "", name);
    eventWriter.add(eElement);
    eventWriter.add(end);
}
}

Ниже приведен код для класса Controller:

public class Controller {
@FXML
private TableView<Contact> mContactsTableView;
@FXML
private BorderPane mMainWindow;
private ContactData mContactData;
private ObservableList<Contact> mContactsList;

public void initialize(){
    mContactData = new ContactData();
    mContactsList = mContactData.getContacts();
    mContactsTableView.setItems(mContactsList);

    //Initializing the columns of the TableView
    TableColumn<Contact, String> firstNameCol = new TableColumn<>("First Name");
    firstNameCol.setCellValueFactory(param -> param.getValue().mFirstNameProperty());

    TableColumn<Contact, String> lastNameCol = new TableColumn<>("Last Name");
    lastNameCol.setCellValueFactory(new PropertyValueFactory<Contact, String>("mLastName"));

    TableColumn<Contact, String> phoneCol = new TableColumn<>("Phone Number");
    phoneCol.setCellValueFactory(new PropertyValueFactory<Contact, String>("mPhoneNumber"));

    TableColumn<Contact, String> notesCol = new TableColumn<>("Notes");
    notesCol.setCellValueFactory(new PropertyValueFactory<Contact, String>("mNotes"));

    mContactsTableView.getColumns().setAll(firstNameCol, lastNameCol, phoneCol, notesCol);
}


@FXML
private void showAddContactDialog(){
    Dialog<ButtonType> dialog = new Dialog<>();
    dialog.initOwner(mMainWindow.getScene().getWindow());
    dialog.setTitle("ADD NEW CONTACT");
    FXMLLoader fxmlLoader = new FXMLLoader();     
    fxmlLoader.setLocation(getClass()
        .getResource("AddContactDialog.fxml"));
    try {
        dialog.getDialogPane().setContent(fxmlLoader.load());
    } catch (IOException e) {
        System.out.println("Couldn't load the dialog");
        e.printStackTrace();
    }
    dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
    dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);

    Optional<ButtonType> result = dialog.showAndWait();
    if (result.isPresent() && result.get() == ButtonType.OK){
        AddContactDialogController dialogController = fxmlLoader.getController();
        Contact newContact = dialogController.addContact();
    }

Ниже приведен код для класса AddContactDialogController:

public class AddContactDialogController {
@FXML
private TextField firstNameField;
@FXML
private TextField lastNameField;
@FXML
private TextField phoneNumberField;
@FXML
private TextField notesField;

public Contact addContact(){
    String firstName = firstNameField.getText().trim();
    String lastName = lastNameField.getText().trim();
    String phoneNumber = phoneNumberField.getText().trim();
    String notes = notesField.getText().trim();
    Contact newContact = new Contact(firstName, lastName, phoneNumber, notes);
    ContactData contactData = new ContactData();
    contactData.addContact(newContact);
    return newContact;
}
}

Ниже приведен файл mainLayout.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane fx:id="mMainWindow" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
    <top>
        <MenuBar>
           <menus>
               <Menu text="Contacts">
                  <items>
                      <MenuItem onAction="#showAddContactDialog" text="Add" />
                      <MenuItem onAction="#handleEditContact" text="Edit" />
                      <MenuItem onAction="#handleDeleteContact" text="Delete" />
                  </items>
               </Menu>
           </menus>
        </MenuBar>
    </top>
    <center>
        <TableView fx:id="mContactsTableView" />
    </center>
</BorderPane>

Ниже приведен файл AddContactDialog.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.text.Text?>
<DialogPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
        fx:controller="sample.AddContactDialogController">
    <header>
        <Text text="Add a New Contact"/>
    </header>
    <content>
        <GridPane vgap="10" hgap="10">
            <Label text="First Name" GridPane.rowIndex="0" GridPane.columnIndex="0"/>
            <TextField fx:id="firstNameField" GridPane.rowIndex="0" GridPane.columnIndex="1"/>

            <Label text="Last Name" GridPane.rowIndex="1" GridPane.columnIndex="0"/>
            <TextField fx:id="lastNameField" GridPane.rowIndex="1" GridPane.columnIndex="1"/>

            <Label text="Phone Number" GridPane.rowIndex="2" GridPane.columnIndex="0"/>
            <TextField fx:id="phoneNumberField" GridPane.rowIndex="2" GridPane.columnIndex="1"/>

            <Label text="Notes" GridPane.rowIndex="3" GridPane.columnIndex="0"/>
            <TextField fx:id="notesField" GridPane.rowIndex="3" GridPane.columnIndex="1"/>
        </GridPane>
    </content>
</DialogPane>

1 Ответ

2 голосов
/ 14 апреля 2019

У вас фактически есть как минимум две проблемы с вашей реализацией.

Во-первых, ваш getContacts() метод возвращает new ObservableList, а не тот, который обновляется в addContact() method:

public ObservableList<Contact> getContacts() {
    return FXCollections.observableList(contacts);
}

Поскольку этот новый список является тем, который вы привязали к своему TableView, он никогда не обновляется при добавлении нового контакта.

Обновите метод getContact(), чтобы он просто возвращал contacts.Это уже ObservableList, поэтому нет необходимости звонить FXCollections.observableList().


Во-вторых, внутри вашего AddContactDialogController вы также создаете новый класс ContactData, и именно здесь вы 'повторное добавление нового контакта.Опять же, это не тот список ContactData или contact, который вы привязали к своему TableView.

Вместо этого вам нужно передать ссылку на ваш оригинальный объект ContactData в AddContactDialogController.addContact() метод ...

...