MultipleSelectionModel: какой список изменений событий ожидать от selectedIndices?

... в частности, при изменении базовых элементов?

Ниже приведен быстрый пример, который выбирает диапазон и добавляет элемент над выбором для TableView и ListView. Выбранные идентификаторы до / после добавления:

indices before modification: [2, 3]
indices after modification: [3, 4]

Ожидаемые варианты:

  • один был заменен на весь диапазон
  • wasRemoved для "2" и wasAdded для "4"
  • ??

Актуально:

  • выбор таблицы запускает два события, каждое с одним wasAdded
  • выбор списка запускает wasPermutated (это чокнутый, или что я пропускаю?)

Выход (jdk8u40b12):

Change #0 on TableView indices 
list = [3]
    Change event data:

 class javafx.scene.control.MultipleSelectionModelBase$3
 javafx.scene.control.MultipleSelectionModelBase$3@4ececa
    cursor = 0
        Kind of change: added
        Affected range: [0, 1]
        Added size: 1
        Added sublist: [3]

Change #1 on TableView indices 
list = [3, 4]
    Change event data:

 class javafx.scene.control.MultipleSelectionModelBase$3
 javafx.scene.control.MultipleSelectionModelBase$3@b0161d
    cursor = 0
        Kind of change: added
        Affected range: [1, 2]
        Added size: 1
        Added sublist: [4]

Change #0 on ListView indices 
list = [3, 4]
    Change event data:

 class com.sun.javafx.collections.NonIterableChange$SimplePermutationChange
 { permutated by [4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0] }
    cursor = 0
        Kind of change: permutated
        Affected range: [0, 2]
        Permutation: [0->4, 1->3]

Производящий код:

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.ListChangeListener.Change;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableView;
import javafx.stage.Stage;

public class SelectedIndicesOnItemsModified extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        ObservableList<Integer> items = FXCollections.observableArrayList(1, 2, 3, 4);
        TableView<Integer> table = new TableView<>(items);
        table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        table.getSelectionModel().selectRange(2, 4);
        System.out.println("indices before modification: " + 
            table.getSelectionModel().getSelectedIndices());
        ListView<Integer> list = new ListView<>(items);
        list.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        list.getSelectionModel().selectRange(2, 4);

        new PrintingListChangeListener("TableView indices ", 
             table.getSelectionModel().getSelectedIndices());        
        new PrintingListChangeListener("ListView indices ", 
             list.getSelectionModel().getSelectedIndices());  
        items.add(0, 111);
    }

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

    public static <T> void prettyPrint(Change<? extends T> change) {
        StringBuilder sb = new StringBuilder("\tChange event data:\n");
        sb.append("\n " + change.getClass() + "\n " + change);
        int i = 0;
        change.reset();
        while (change.next()) {
            sb.append("\n\tcursor = ").append(i++).append("\n");

            final String kind = change.wasPermutated() ? "permutated" : change
                    .wasReplaced() ? "replaced"
                    : change.wasRemoved() ? "removed"
                            : change.wasAdded() ? "added"
                                    : change.wasUpdated() ? "updated" : "none";
            sb.append("\t\tKind of change: ").append(kind).append("\n");

            sb.append("\t\tAffected range: [").append(change.getFrom())
                    .append(", ").append(change.getTo()).append("]\n");

            if (kind.equals("added") || kind.equals("replaced")) {
                sb.append("\t\tAdded size: ").append(change.getAddedSize())
                        .append("\n");
                sb.append("\t\tAdded sublist: ")
                        .append(change.getAddedSubList()).append("\n");
            }

            if (kind.equals("removed") || kind.equals("replaced")) {
                sb.append("\t\tRemoved size: ").append(change.getRemovedSize())
                        .append("\n");
                sb.append("\t\tRemoved: ").append(change.getRemoved())
                        .append("\n");
            }

            if (kind.equals("permutated")) {
                StringBuilder permutationStringBuilder = new StringBuilder("[");
                for (int k = change.getFrom(); k < change.getTo(); k++) {
                    permutationStringBuilder.append(k).append("->")
                            .append(change.getPermutation(k));
                    if (k < change.getTo() - 1) {
                        permutationStringBuilder.append(", ");
                    }
                }
                permutationStringBuilder.append("]");
                String permutation = permutationStringBuilder.toString();
                sb.append("\t\tPermutation: ").append(permutation).append("\n");
            }
        }
        System.out.println(sb.toString());
    };

    public static class PrintingListChangeListener implements ListChangeListener {
        String source;
        int counter;
        public PrintingListChangeListener() {
        }

        public PrintingListChangeListener(String message, ObservableList<?> list) {
            list.addListener(this);
            source = message;
        }
        @Override
        public void onChanged(Change change) {
            System.out.println("Change #" + counter++ + " on " +source + 
                 "\nlist = " + change.getList());
            prettyPrint(change);
        }
    }

}

Поданы две проблемы, RT-39393 для ListView, RT-39394 для TableView

1 ответ

Предварительный частично ответ на мой собственный вопрос

Количество уведомлений

Номер уведомления (он же: вызовы к изменению (Изменить c)) должен совпадать с номером в базовых данных в псевдокоде.

itemsChanges = 0; 
itemsListener = c -> itemsChanges++;
getItems().addListener(itemsListener);
selectedChanges = 0; 
selectedListener = c -> selectedChanges++; 
getSelectedIndices().addListener(selectedListener);
getItems().modifySomehow(...); 
assertEquals(itemsChanges, selectedChanges);

Изменить типы

Думая об этом, большинство изменений в базовых элементах, кажется, отображаются на замененные в selectedIndices ("value" ниже обозначает элементы в selectedIndices):

"реальный" wasAdded: все значения, превышающие положение вставки, должны быть увеличены при добавлении размера

// selectedIndices before
[2, 4]
items.add(0, something);
// selectedIndices after
[3, 5]

-> Чистый эффект: два значения устанавливаются (== заменяется на) новым значением

"реальный" wasRemoved: все значения, указывающие на удаленные элементы, также должны быть удалены, значения, которые больше, чем местоположение удаления, должны быть уменьшены при удалении

// selectedIndices before
[2, 4, 5, 8]
items.removeAll(items.get(3), items.get(5))
// selectedIndices after
[2, 3, 6]

-> Чистый эффект: заменить [4, 5, 8] в поз. 1 на [3, 6]

wasUpdated: индексы не изменились, хотя базовые элементы как-то изменились. В зависимости от контекста, может быть хорошей идеей передать эти обновления своему слушателю или нет.

Все еще открыто: заменено / перестановка в элементах

Чтобы получить ожидаемые (по крайней мере, по моей бумажной кодировке:-) уведомления правильные, не совсем тривиальные - и пошли не так в MultipleSelectionModelBase и подклассах. В настоящее время играет с перемещением всех неприятных деталей в отдельный IndicesList (который является TransformList с элементами в качестве sourceList).

Другие вопросы по тегам