Q:

Sort Javafx table on multiple columns

Can a default Javafx table sort on multiple fields by dragging the columns on a dropzone?
My user need to select one or multiple columns to sort on different columns. The application is fully written in Java8 with JavaFX.
The source code that I now use is:
import java.util.Collections;
import java.util.Comparator;
import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class GroupByTable extends Application {

    public enum Color { GREEN, BLUE, RED }
    public enum Shape { RECTANGLE, CIRCLE, TRIANGLE }
    public enum Size { SMALL, MEDIUM, LARGE }

    private Label groupByLabel;
    private Comparator<Item> groupingComparator ;

    @Override
    public void start(Stage primaryStage) {
        TableView<Item> table = new TableView<>();

        table.getColumns().add(column("Size", Item::getSize));
        table.getColumns().add(column("Color", Item::getColor));
        table.getColumns().add(column("Shape", Item::getShape));

        groupByLabel = new Label("Grouping");

        groupByLabel.setOnDragOver(e -> {
            if (groupingComparator != null && "grouping".equals(e.getDragboard().getString())) {
                e.acceptTransferModes(TransferMode.COPY);
            }
        });

        groupByLabel.setOnDragDropped(e -> {
            if (groupingComparator != null && "grouping".equals(e.getDragboard().getString())) {
                table.getItems().sort(groupingComparator);
                e.setDropCompleted(true);
            }
        });

        for (Color color : Color.values()) {
            for (Size size : Size.values()) {
                for (Shape shape : Shape.values()) {
                    table.getItems().add(new Item(color, shape, size));
                }
            }
        }

        Collections.shuffle(table.getItems());

        BorderPane root = new BorderPane(table);
        BorderPane.setAlignment(groupByLabel, Pos.CENTER);
        BorderPane.setMargin(groupByLabel, new Insets(20));

        root.setTop(groupByLabel);

        Scene scene = new Scene(root, 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private <T extends Comparable<T>> TableColumn<Item,T> column(String title, Function<Item,T> property) {
        TableColumn<Item,T> col = new TableColumn<>();
        col.setCellValueFactory(cellData -> new SimpleObjectProperty<>(property.apply(cellData.getValue())));

        Label graphic = new Label(title);
        graphic.setOnDragDetected(e -> {
            groupingComparator = Comparator.comparing(property);
            Dragboard dragboard = graphic.startDragAndDrop(TransferMode.COPY);
            ClipboardContent cc = new ClipboardContent();
            cc.putString("grouping");
            dragboard.setContent(cc);
        });
        graphic.setOnDragDone(e -> {
            groupingComparator = null ;
        });

        col.setGraphic(graphic);

        return col ;
    }

    public static class Item {
        private final Color color ;
        private final Shape shape ;
        private final Size size ;
        public Item(Color color, Shape shape, Size size) {
            super();
            this.color = color;
            this.shape = shape;
            this.size = size;
        }
        public Color getColor() {
            return color;
        }
        public Shape getShape() {
            return shape;
        }
        public Size getSize() {
            return size;
        }

        @Override
        public String toString() {
            return String.format("%s %s %s", size, color, shape);
        }
    }

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

A:

I view the TableView API and found that the JavaFX table does have a default implementation of this. Just click on the columns using the shift key.

