Строки JavaFX TableView не обновляются при добавлении элемента списка - PullRequest
0 голосов
/ 07 июня 2018

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

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

ЗдесьMainController

@Override
public void initialize(URL url, ResourceBundle rb) {
    if (firstLoad) {
        inventory.addPart(new Part(1,"Widget",1.13,5,1,8));
        inventory.addPart(new Part(2,"Sprocket",2.88,5,1,8));
        inventory.addPart(new Part(3,"Gear",3.46,5,1,8));

        firstLoad = false;
    }

    colPartID.setCellValueFactory(new PropertyValueFactory<>("partID"));
    colPartName.setCellValueFactory(new PropertyValueFactory<>("name"));
    colPartInvLevel.setCellValueFactory(new PropertyValueFactory<>("inStock"));
    colPartPrice.setCellValueFactory(new PropertyValueFactory<>("price"));

    tblParts.setItems(FXCollections.observableArrayList(inventory.getAllParts()));

}

класс инвентаризации имеет статический массив списков деталей

private static ArrayList<Part> allParts = new ArrayList<>();

, а addpartcontroller просто добавляет к списку массивов, который прекрасно работает

inv.addPart(new Part(1,"test",2,3.46));
stage.close();

После того, как этап закрыт, кажется, что главный экран вообще не обновляется, в представлении таблицы деталей все еще есть 3 части

Если я оставлю текстовое поле поиска пустым и нажму кнопку поиска,4-ая часть показывает

        FilteredList<Part> filteredData = new FilteredList<>(FXCollections.observableArrayList(inventory.getAllParts()), p -> true);

        String newValue = txtPartSearch.getText();

        filteredData.setPredicate(part -> {
            if (newValue == null || newValue.isEmpty()) {
                return true;
            }

            String lowerCaseFilter = newValue.toLowerCase();
            if (part.getName().toLowerCase().contains(lowerCaseFilter)) {
                return true;
            }
            return Integer.toString(part.getPartID()).equals(lowerCaseFilter);
        });

        SortedList<Part> sortedData = new SortedList<>(filteredData);
        sortedData.comparatorProperty().bind(tblParts.comparatorProperty());
        tblParts.setItems(sortedData);

Я пробовал tblParts.refresh (), я также пытался заставить addpartcontroller вызывать метод в maincontroller для установки таблицы перед закрытием, но таблица никогда не обновляется, пока я не вызовуметод поиска.

РЕДАКТИРОВАТЬ:

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

inventory.addPart(new Part(6,"Test",5.23,4,2,8));
tblParts.setItems(FXCollections.observableArrayList(inventory.getAllParts()));

1 Ответ

0 голосов
/ 07 июня 2018

В вашем коде есть пара ошибок.Наиболее актуально то, что вы используете FXCollections.observableArrayList(Collection).Этот Javadoc этого метода гласит:

Создает новый список наблюдаемых массивов и добавляет к нему содержимое коллекции col.

Другими словами, копирует Collection в новый ObservableList, который поддерживается ArrayList.Любые обновления к исходному Collection никогда не будут даже добавлены к ObservableList.Вы должны использовать FXCollections.observableList(List), если хотите, чтобы переданный List был поддержкой List.

Javadoc для FXCollections.observableList(List) (выделение мое):

Создает ObservableList, который поддерживается указанным списком.Об операциях мутации в экземпляре ObservableList будет сообщено наблюдателям, которые зарегистрировались в этом экземпляре. Обратите внимание, что об операциях мутации, выполненных непосредственно в нижележащем списке, не сообщается наблюдателям ни одного ObservableList, который его обертывает.

Этот Javadoc намекает на вторую проблему.Если вы не делаете по-другому в коде, который вы не опубликовали, то, по-видимому, вы добавляете элементы в поле ArrayList (с именем allParts). Из-за этого ObservableList никогда не знает о каких-либо изменениях, и, таким образом, никакие события изменения не запускаются.Срабатывание событий изменения кодируется в ObservableList. Если вы хотите получать уведомления об изменениях, вы должны получить доступ к списку только через ObservableList, который охватывает ArrayList.

В этом случае вашкод по-прежнему будет работать (когда вы звоните tableView.refresh()), если бы не факт, что вы также оберните ObservableList в FilteredList. A FilteredList создает «представление» поддержки ObservableList. Еслиподдержка ObservableList никогда не запускает никаких изменений, FilteredList никогда не уведомляется о каких-либо изменениях, что означает, что он никогда не узнает об обновлении этого "представления". Это означает, что когда вы добавляете элементы в ArrayList, эти новые элементы "находятся вне представления"(если бы вы заменили элемент, который находится" внутри вида ", изменение было бы видно).

Это поведение можно увидеть с помощью следующего кода:

import java.util.*;
import javafx.collections.*;
import javafx.collections.transformation.*;

public class Main {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("World");

        ObservableList<String> obsList = FXCollections.observableList(list);
        FilteredList<String> filteredList = new FilteredList<>(obsList);

        System.out.println("INITIAL STATE");
        System.out.printf("\tList: %s%n", list);
        System.out.printf("\tFilteredList: %s%n", filteredList);

        list.add("Goodbye");
        list.add("World");

        System.out.println("AFTER ADDING ELEMENTS");
        System.out.printf("\tList: %s%n", list);
        System.out.printf("\tFilteredList: %s%n", filteredList);

        list.set(0, "Foo");
        list.set(1, "Bar");

        System.out.println("AFTER REPLACING ELEMENTS");
        System.out.printf("\tList: %s%n", list);
        System.out.printf("\tFilteredList: %s%n", filteredList);
    }

}

Какие отпечатки выt:

INITIAL STATE
        List: [Hello, World]
        FilteredList: [Hello, World]
AFTER ADDING ELEMENTS
        List: [Hello, World, Goodbye, World]
        FilteredList: [Hello, World]
AFTER REPLACING ELEMENTS
        List: [Foo, Bar, Goodbye, World]
        FilteredList: [Foo, Bar]

Учитывая все это, самый простой способ исправить ваш код - сделать allParts ObservableList.В противном случае вы должны позаботиться о том, чтобы использовать только ObservableList, созданный вами вокруг ArrayList.


Редактировать

Вы также упоминаете, что «ЕслиЯ оставляю поле поиска пустым и нажимаю кнопку поиска, появляется четвертая часть ".Я хочу обратиться к этому.Вот ваш код с пояснениями, почему новый Part появляется в TableView, когда вы нажимаете кнопку поиска:

/*
 * I extracted the FXCollections.observableArrayList(Collection) out of the FilteredList
 * constructor to more easily see what is going on.
 */

/* 
 * You create a **new** ObservableList using FXCollections.observableArrayList(Collection). This basically
 * creates a *snapshot* of the List returnd by getAllParts() as it currently is. At this point the 4th
 * Part is in that returned List. This means the newly created ObservableList will also contian the new
 * Part (since observableArrayList(Collection) copies the data). However, the *old* ObservableList that
 * was already set on the TableView *does not* contain that 4th Part.
 */
ObservableList<Part> parts = FXCollections.observableArrayList(inventory.getAllParts());

// You create a FilteredList that performs no filtering around "parts". Note
// here that a Predicate that always returns true is equivalent to passing null
// as the Predicate.
FilteredList<Part> filteredData = new FilteredList<>(parts, p -> true);

// Get the search criteria
String newValue = txtPartSearch.getText();

filteredData.setPredicate(part -> {
    if (newValue == null || newValue.isEmpty()) {
        return true; // don't filter if there is no search criteria
                     // since the newValue will be null or blank in this
                     // case no Parts are filtered
    }

    // filter based on lower-case names and search critiera
    String lowerCaseFilter = newValue.toLowerCase();
    if (part.getName().toLowerCase().contains(lowerCaseFilter)) {
        return true;
    }
    // else filter by ID
    return Integer.toString(part.getPartID()).equals(lowerCaseFilter);
});

// Wrap the FilteredList in a SortedList and bind the comparatorProperty to
// the comparatorProperty of the TableView (allows sorting by column).
SortedList<Part> sortedData = new SortedList<>(filteredData);
sortedData.comparatorProperty().bind(tblParts.comparatorProperty());

// Set the sortedData to the TableView
tblParts.setItems(sortedData);

Итак, основная причина, по которой при поиске вы видите новое Part шоуЭто связано с тем, что вы создаете новый ObservableList каждый раз при поиске.Этот новый ObservableList имеет самое последнее состояние getAllParts() List.Кроме того, как я уже упоминал в комментариях, ваша правка в основном делает то же самое, что и ваш код сортировки.Поскольку вы делаете:

inventory.addPart(new Part(6,"Test",5.23,4,2,8));
tblParts.setItems(FXCollections.observableArrayList(inventory.getAllParts()));

, который добавляет Part перед созданием ObservableList.Опять же, FXCollections.observableArrayList(Collection) делает снимок из Collection, который при вызове метода содержит этот новый Part.Если вы перевернете код на:

tblParts.setItems(FXCollections.observableArrayList(inventory.getAllParts()));
inventory.addPart(new Part(6, "Test", 5.23, 4, 2, 8));

, тогда свойство TableView items не будет содержать новое Part.Однако allParts ArrayList в inventory будет .

...