JavaFX - Detect & highlight TableColumn being drag

2019-02-20 03:26发布

What's the best way to discover which TableColumn (in a TableView) that a DragEvent occurred?

I'd like to be able to highlight (ex. change the background color to yellow, or show a yellow border around the column) a TableColumn as a dragged item is "hovering" over the it, to make it clear to the user which column they're dropping onto. After the drag is finished of course I'll have to make sure the TableView's style is restored.

To be clear, the exact cell being dropped onto doesn't matter - just the column.

1条回答
Evening l夕情丶
2楼-- · 2019-02-20 04:10

To do this, you need to register drag listeners with each cell that cause the column to update its style when the cells are dragged over. I would do this using an ObjectProperty to represent the current "drop target column" (i.e. column over which dragging is occurring), and both observe it and update it from the cells. To access the cells, you need a cell factory.

The following method will add such support to a column. It also respects any functionality defined in a previously-attached cell factory:

private <S,T> void configureDropTargetColumn(TableColumn<S,T> column, ObjectProperty<TableColumn<?,?>> dropTarget) {

    Callback<TableColumn<S,T>, TableCell<S,T>> currentCellFactory = column.getCellFactory() ;

    column.setCellFactory(tc -> {
        TableCell<S,T> cell = currentCellFactory.call(tc);
        dropTarget.addListener((obs, oldColumn, newColumn) -> 
                cell.pseudoClassStateChanged(PseudoClass.getPseudoClass("drop-target"), newColumn==column));
        cell.setOnDragEntered(e -> dropTarget.set(column));
        cell.setOnDragExited(e -> dropTarget.set(null));
        return cell ;
    });
}

Here's a SSCCE using the usual "contact table" example:

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.css.PseudoClass;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
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;
import javafx.util.Callback;

public class TableColumnDropTargetExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        TableView<Person> table = new TableView<>();
        table.getColumns().add(column("First Name", Person::firstNameProperty));
        table.getColumns().add(column("Last Name", Person::lastNameProperty));
        table.getColumns().add(column("Email", Person::emailProperty));

        ObjectProperty<TableColumn<?,?>> dropTargetColumn = new SimpleObjectProperty<>();

        table.getColumns().forEach(col -> configureDropTargetColumn(col, dropTargetColumn));

        table.getItems().addAll(createData());

        Label label = new Label("Drag me");
        label.getStyleClass().add("drag-source");

        label.setOnDragDetected(e -> {
            Dragboard db = label.startDragAndDrop(TransferMode.COPY_OR_MOVE);
            ClipboardContent cc = new ClipboardContent();
            cc.putString(label.getText());
            db.setContent(cc);
        });
        label.setOnDragDone(e -> dropTargetColumn.set(null));

        BorderPane root = new BorderPane(table);
        root.setTop(label);
        BorderPane.setAlignment(label, Pos.CENTER);

        Scene scene = new Scene(root, 800, 600);
        scene.getStylesheets().add("style.css");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private <S,T> void configureDropTargetColumn(TableColumn<S,T> column, ObjectProperty<TableColumn<?,?>> dropTarget) {

        Callback<TableColumn<S,T>, TableCell<S,T>> currentCellFactory = column.getCellFactory() ;

        column.setCellFactory(tc -> {
            TableCell<S,T> cell = currentCellFactory.call(tc);
            dropTarget.addListener((obs, oldColumn, newColumn) -> 
                    cell.pseudoClassStateChanged(PseudoClass.getPseudoClass("drop-target"), newColumn==column));
            cell.setOnDragEntered(e -> dropTarget.set(column));
            cell.setOnDragExited(e -> dropTarget.set(null));
            return cell ;
        });
    }

    private static <S,T> TableColumn<S,T> column(String text, Function<S, ObservableValue<T>> property) {
        TableColumn<S,T> col = new TableColumn<>(text);
        col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
        return col ;
    }

    private List<Person> createData() {
        return Arrays.asList(
                new Person("Jacob", "Smith", "jacob.smith@example.com"),
                new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
                new Person("Ethan", "Williams", "ethan.williams@example.com"),
                new Person("Emma", "Jones", "emma.jones@example.com"),
                new Person("Michael", "Brown", "michael.brown@example.com")     
        );
    }

    public static class Person {
        private final StringProperty firstName = new SimpleStringProperty();
        private final StringProperty lastName = new SimpleStringProperty();
        private final StringProperty email = new SimpleStringProperty();


        public Person(String firstName, String lastName, String email) {
            setFirstName(firstName);
            setLastName(lastName);
            setEmail(email);
        }


        public final StringProperty firstNameProperty() {
            return this.firstName;
        }



        public final String getFirstName() {
            return this.firstNameProperty().get();
        }



        public final void setFirstName(final String firstName) {
            this.firstNameProperty().set(firstName);
        }



        public final StringProperty lastNameProperty() {
            return this.lastName;
        }



        public final String getLastName() {
            return this.lastNameProperty().get();
        }



        public final void setLastName(final String lastName) {
            this.lastNameProperty().set(lastName);
        }



        public final StringProperty emailProperty() {
            return this.email;
        }



        public final String getEmail() {
            return this.emailProperty().get();
        }



        public final void setEmail(final String email) {
            this.emailProperty().set(email);
        }



    }

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

and the stylesheet:

.table-cell:drop-target {
    -fx-background-color: -fx-background ;
    -fx-background: yellow ;
    -fx-border-color: -fx-table-cell-border-color -fx-table-cell-border-color transparent transparent ;
}
.drag-source {
    -fx-font-size: 18 ;
    -fx-padding: 10 ;
}
查看更多
登录 后发表回答