JavaFx: bind columns reordering

739 views Asked by At

I have a view, in that view a few tableViews, but lets say I have two tables. When I reorder the columns in the first table(by using drag and drop) I want to do the same thing in the second table. I was trying in a way but I get IllegalStateException: Duplicate TreeTableColumns detected in TreeTableView columns list with titles ''

This is that I have tried:

    private void handleDragAndDrop() {
        firstTable.getColumns().addListener((ListChangeListener<? super TableColumn<?, ?>>) c -> {
            while (c.next()) {
                List<String> addedIDs = c.getAddedSubList()
                        .stream()
                        .map(TableColumn::getId)
                        .collect(Collectors.toList());
                Map<String, String> changedIds = getChangedIds(addedIDs);
                changedIds.entrySet()
                        .stream()
                        .reduce((firstEntry, lastEntry) -> lastEntry)
                        .ifPresent(entry-> reorderTheSecondTable(secondTable.getColumns(),entry.getValue(),
                                entry.getKey()));
            }
        });
    }

    private Map<String, String> getChangedIds(List<String> iDs) {
        return new LinkedHashMap<>(iDs.stream()
                .skip(1) // I don't need the first column, that will be never reordered.
                .filter(id -> !id.equals(String.valueOf(iDs.indexOf(id)))) // I need only the changed id-index pairs.
                .collect(Collectors.toMap(i -> String.valueOf(iDs.indexOf(i)), Function.identity())));
    }

    private void reorderTheSecondTable(ObservableList<? extends TableColumnBase<?, ?>> list, String start, String end) {
        Collections.rotate(list.subList(Integer.valueOf(start), Integer.valueOf(end) + 1), 1);
    }

The columns have ids from 1 to table.getColumns().size()-1 (0 is the immutable column)

The exception is thrown at line Collections.rotate(...

I don't know if is this the correct way to do, but I didn't find any better solution and even this is not a solution since is not working.

1

There are 1 answers

7
James_D On BEST ANSWER

You can do something like this:

TableView<FirstTablesData> firstTable = new TableView<>();

// create a list that holds the original order of the columns:
List<TableColumn<FirstTablesData, ?>> firstTableOriginalColumns = new ArrayList<>(...);

firstTable.getColumns().setAll(firstTableOriginalColumns);

// same for second table:

TableView<SecondTablesData> secondTable = new TableView<>();
List<TableColumn<SecondTablesData, ?>> secondTableOriginalColumns = new ArrayList<>(...);
secondTable.getColumns().setAll(secondTableOriginalColumns);

firstTable.getColumns().addListener((Change<? extends TableColumn<FirstTablesData,?>> change) -> 
    secondTable.getColumns().setAll(firstTable.getColumns().stream()
        .mapToInt(firstTableOriginalColumns::indexOf)
        .mapToObj(secondTableOriginalColumns::get)
        .collect(Collectors.toList())));

This creates lists that "remember" the original ordering of the columns in each table. The listener just looks up the index in the original list of columns for the first table, and creates a list corresponding to that ordering for the second table's columns. This code assumes both tables have the same number of columns.

Here is a SSCCE:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import javafx.application.Application;
import javafx.collections.ListChangeListener.Change;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class CoordinatedTableColumns extends Application {

    @Override
    public void start(Stage primaryStage) {
        TableView<Object> firstTable = new TableView<>();
        List<TableColumn<Object, ?>> firstTableOriginalColumns = Arrays.asList(
                new TableColumn<>("Column 1"),
                new TableColumn<>("Column 2")
        );
        firstTable.getColumns().setAll(firstTableOriginalColumns);

        TableView<String> secondTable = new TableView<>();
        List<TableColumn<String, ?>> secondTableOriginalColumns = Arrays.asList(
                new TableColumn<>("Column A"),
                new TableColumn<>("Column B")
        );
        secondTable.getColumns().setAll(secondTableOriginalColumns);

        firstTable.getColumns().addListener((Change<? extends TableColumn<Object,?>> change) -> 
        secondTable.getColumns().setAll(firstTable.getColumns().stream()
            .mapToInt(firstTableOriginalColumns::indexOf)
            .mapToObj(secondTableOriginalColumns::get)
            .collect(Collectors.toList())));

        HBox root = new HBox(5, firstTable, secondTable);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

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