Удаление JavaFX ListChangeListener вызывает изменение дополнения - PullRequest
0 голосов
/ 29 сентября 2019

Мне нужно объяснить, почему он так себя ведет, и найти решение, как лучше всего это решить.

У меня есть пользовательский элемент управления JavaFX.Модель имеет список данных, который используется для построения элемента управления в представлении.У меня также есть Поведение, которое удалит переполнение списка, основываясь на ограничении, установленном на то, сколько значений должно быть в списке и показано в элементе управления.Когда элементы в списке удалены, он должен удалить их из представления.В MVC элемент управления - это модель, скин - это вид, а поведение - контроллер.

public class CustomControl
public class CustomControlSkin
public class CustomControlBehavior

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

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

Для простоты в примере просто создается метка для каждого значения в модели, и сначала создается 30 значений.Поскольку ограничение установлено на 10, элемент управления должен отображать только 10, но на самом деле отображает 10 + 30.

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TestListRemovals extends Application {

    private VBox root = new VBox();

    private ObservableList<String> values = FXCollections.observableList(new LinkedList<String>());

    public static void main(String[] args) {
        TestListRemovals.launch(args);
    }

    /* (non-Javadoc)
     * @see javafx.application.Application#start(javafx.stage.Stage)
     */
    @Override
    public void start(Stage primaryStage) throws Exception {
        Scene scene = new Scene(root, 400, 600);
        primaryStage.setScene(scene);
        primaryStage.show();

        values.addListener(new ListChangeListener<String>() {

            @Override
            public void onChanged(Change<? extends String> change) {
                System.out.println("onChanged");
                while (change.next()) {
                    System.out.println("change wasAdded " + change.wasAdded());
                    System.out.println("change wasPermutated " + change.wasPermutated());
                    System.out.println("change wasRemoved " + change.wasRemoved());
                    System.out.println("change wasReplaced " + change.wasReplaced());
                    System.out.println("change wasUpdated " + change.wasUpdated());
                    if (change.wasAdded()) {
                        System.out.println("Added " + change.getAddedSize());
                        addValues(change.getAddedSubList());
                    }

                    if (change.wasRemoved()) {
                        System.out.println("Removed " + change.getRemovedSize());
                        removeValues(change.getRemoved());
                    }
                    removeOverflow();
                }
            }

        });

        Timer delay = new Timer();
        delay.schedule(new TimerTask() {

            @Override
            public void run() {
                List<String> newValues = new LinkedList<>();
                for (int i = 1; i < 31; i++) {
                    final String value = "Value " + i;
                    newValues.add(value);
                }
                Platform.runLater(() -> {
                    values.addAll(newValues);
                });
            }

        }, 2000);

    }

    public void removeOverflow() {
        System.out.println("removeOverflow");
        final int limit = 10;
        final int size = values.size();
        final int overflow = size - limit;
        if (overflow > 0) {
            System.out.println("Remove from 0 to " + overflow);
            values.remove(0, overflow);
        }
    }

    public void addValues(List<? extends String> values) {
        values.forEach(value -> {
            final Label label = new Label(value);
            root.getChildren().add(0, label);
        });
    }

    public void removeValues(List<? extends String> values) {
        values.forEach(value -> {
            for (Iterator<Node> itr = root.getChildren().iterator(); itr.hasNext();) {
                final Node node = itr.next();
                if (node instanceof Label) {
                    if (((Label) node).getText().equals(value)) {
                        itr.remove();
                    }
                }
            }
        });
    }

}

Удаление фактически вызывает добавление оставшихся в списке,не удаленные элементы.

Я могу исправить это, поместив удаление в задачу JavaFX для выполнения в другом потоке.Однако это не похоже на правильное решение проблемы.Кроме того, почему удаление фактически вызывает второе добавление, не удаленных элементов, а оставшихся элементов?

Редактировать: кажется, что не рекомендуется изменять список из ListChangeListener.Любое предложение, как я могу изменить список за пределами ListChangeListener, который вызывается из дополнения?Я пытался с помощью Задачи JavaFX вызвать удаление, и это работает, но кажется излишним иметь поток в пользовательском элементе управления для каждого обновления своих данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...