JavaFX/FXML: TableView правильно обновляется в случае редактирования элементов, но не в случае удаления элементов (ArrayList->TableView)

Я работаю над более крупным проектом (вместе с большим количеством сокурсников) и пытаюсь отобразить список массивов (тип, который мы используем снова и снова в нашем проекте, поэтому мне бы не хотелось измените все это на ObservableLists только для моей довольно небольшой проблемы) в TableView.

Я создал небольшой проект только для того, чтобы добраться до сути проблемы и показать вам:

В классе editMyGrades (вместе с одноименным файлом fxml) я хочу отобразить свои SUBJECTs (-> String subjectName), каждый со своим GRADE (-> intgrade) в TableView.

При инициализации родительского корня и tableView объекты ArrayList преобразуются в ObservableList, который загружается в TableView.

Я действительно хотел бы теоретически понять, почему изменение GRADE работает с tableView, а удаление — нет, и знать, что с этим делать на практике ;-)

Заранее большое спасибо!

Основной класс:

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.util.ArrayList;

public class Main extends Application {
    private static ArrayList<Subject> subjects = new ArrayList<>();
    public static ArrayList<Subject> getSubjects () {return subjects;}

//    private static ObservableList<Subject> subjects = FXCollections.observableArrayList();
//    public static ObservableList<Subject> getSubjects () {return subjects;}


    public static void main (String[] args) {

        subjects.add(new Subject("computer science", 2));
        subjects.add(new Subject("literature", 4));

        launch (args);
    }

    public void start (Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("/editMyGrades.fxml"));
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

Тема класса:

public class Subject {
    private String subjectName;
    private int grade;

    public Subject (String subjectName, int grade) {
        this.subjectName = subjectName;
        this.grade = grade;
    }

    public String getSubjectName () {return subjectName;}

    public int getGrade () {return grade;}

    public void setGrade (int grade) {this.grade = grade;}


}

управляющий класс editMyGrades:

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;

import java.util.ArrayList;

public class editMyGrades {

    Subject subjectToEdit;

    @FXML
    private TableView<Subject> tableView;

    @FXML
    private TableColumn<Subject, String> subjectCol;

    @FXML
    private TableColumn<Subject, Number> gradeCol;

    @FXML
    private TextField textField;

    @FXML
    private Button changeButton;

    @FXML
    private Button deleteButton;

    @FXML
    public void initialize() {
        subjectCol.setCellValueFactory(new PropertyValueFactory<>("subjectName"));
        gradeCol.setCellValueFactory(new PropertyValueFactory<>("grade"));

        //tableView.setItems (FXCollections.observableArrayList(Main.getSubjects()));
        ObservableList<Subject> subjects = FXCollections.observableArrayList(Main.getSubjects());
        tableView.setItems(subjects);
    }

    @FXML
    private void tableViewClicked(MouseEvent event) {
        if (event.getClickCount() == 2) {
            subjectToEdit = tableView.getSelectionModel().getSelectedItem();
            textField.setText ("" + tableView.getSelectionModel().getSelectedItem().getGrade());
        }
    }

    @FXML
    private void change(ActionEvent event) {
        subjectToEdit.setGrade(Integer.parseInt(textField.getText()));
        textField.clear();

        tableView.refresh();
    }

    @FXML
    private void delete (ActionEvent event) {
        Subject selectedSubject = tableView.getSelectionModel().getSelectedItem();
        ArrayList<Subject> subjects = Main.getSubjects();
        subjects.remove(selectedSubject);

        tableView.refresh();
    }

}

редактированиеMyGrades.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.HBox?>
<?import javafx.scene.layout.VBox?>

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="editMyGrades">
   <children>
      <Label text="change your grades!" />
      <HBox>
         <children>
            <TableView fx:id="tableView" onMouseClicked="#tableViewClicked" prefHeight="200.0" prefWidth="518.0">
              <columns>
                <TableColumn fx:id="subjectCol" prefWidth="262.0" text="subject" />
                <TableColumn fx:id="gradeCol" minWidth="0.0" prefWidth="146.0" text="grade" />
              </columns>
            </TableView>
            <VBox alignment="CENTER">
               <children>
                  <Button mnemonicParsing="false" onAction="#delete" text="deleteButton" />
               </children>
            </VBox>
         </children>
      </HBox>
      <TextField fx:id="textField" maxWidth="100.0" />
      <Button fx:id="changeButton" mnemonicParsing="false" onAction="#change" text="Change!" />
   </children>
</VBox>

person MagisterInformaticus    schedule 08.01.2021    source источник
comment
только беглый просмотр кода (он не Minimal :) - а) неправильно вызывать table.refresh в общем случае, если он нужен - это признак того, что что-то не так в проводке/настройке! б) не используйте статическую область видимости! c) соглашения об именах java, пожалуйста   -  person kleopatra    schedule 08.01.2021
comment
ошибка в том, что вы удаляете тему из списка в Main (см. последний комментарий - статическая область неверна :) - вы должны удалить ее из элементов таблицы   -  person kleopatra    schedule 08.01.2021
comment
Спасибо! Я всегда стараюсь следовать условностям, которым научились у моих учителей. Где я должен был использовать другие имена?   -  person MagisterInformaticus    schedule 08.01.2021
comment
имена классов должны начинаться с заглавной буквы   -  person kleopatra    schedule 08.01.2021
comment
Еще два вопроса: если я вообще не буду использовать static, то, следовательно, я никогда не ставлю туда никаких атрибутов и аксессоров (чтобы избежать статической области видимости), верно? +++ Если я правильно вас понял, мне нужно удалить элементы из самого tableView? Через google я не нашел команды для этого.   -  person MagisterInformaticus    schedule 08.01.2021
comment
(Я поместил материал в Main, чтобы иметь доступ к нему на протяжении всей программы. Как бы вы управляли чем-то подобным, не связывая его каким-либо образом с классом Main?)   -  person MagisterInformaticus    schedule 08.01.2021
comment
Создайте модель данных и передайте ее контроллеру. См., например, stackoverflow.com/questions/32342864/applying-mvc -с-javafx   -  person James_D    schedule 08.01.2021


Ответы (2)


При работе с JavaFX рекомендуется всегда использовать FXCollections.

Когда вы вносите изменения в ArrayList, изменения не генерируют уведомления об изменении списка. Вы должны получить ObservableList из табличного представления и выполнить над ним операции, поскольку это оболочка (умный список), поддерживаемая вашим ArrayList.

person Community    schedule 08.01.2021

Искренне спасибо за ваши пояснения! На самом деле в моей реальной задаче я использую MVC, но объект Контроллер находится в главном - статическом - и, как оказалось, по-другому нельзя. Для иллюстрации моей проблемы я подумал, что id не будет иметь значения, если бы у меня был статический объект контроллера со списком или просто сам список (возможно, я ошибался...). Теперь, попробовав применить все, что вы мне сказали, все как раз наоборот: tableView отображает удаления, но никаких изменений.

только класс управления fxml EditMyGrades:

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;


public class EditMyGrades {

    private ObservableList<Subject> subjects = FXCollections.observableArrayList();

    private Subject subjectToEdit;

    @FXML
    private TableView<Subject> tableView;

    @FXML
    private TableColumn<Subject, String> subjectCol;

    @FXML
    private TableColumn<Subject, Number> gradeCol;

    @FXML
    private TextField textField;

    @FXML
    private Button changeButton;

    @FXML
    private Button deleteButton;

    @FXML
    public void initialize() {
        subjectCol.setCellValueFactory(new PropertyValueFactory<>("subjectName"));
        gradeCol.setCellValueFactory(new PropertyValueFactory<>("grade"));

        subjects.add(new Subject("computer science", 2));
        subjects.add(new Subject("literature", 4));

        tableView.setItems(subjects);
    }

    @FXML
    private void tableViewClicked(MouseEvent event) {
        if (event.getClickCount() == 2) {
            subjectToEdit = tableView.getSelectionModel().getSelectedItem();
            textField.setText ("" + tableView.getSelectionModel().getSelectedItem().getGrade());
        }
    }

    @FXML
    private void change(ActionEvent event) {
        subjectToEdit.setGrade(Integer.parseInt(textField.getText()));
        textField.clear();
    }

    @FXML
    private void delete (ActionEvent event) {
        ObservableList<Subject> subjects = tableView.getItems();
        Subject selectedSubject = tableView.getSelectionModel().getSelectedItem();
        subjects.remove(selectedSubject);
    }

}
person MagisterInformaticus    schedule 08.01.2021
comment
хм.. это ответ или модификация вопроса? Если первое, уточните, что/как именно оно решает, если второе, вместо этого отредактируйте вопрос - person kleopatra; 09.01.2021