如何防止在 JavaFX TableView 中拖放列时触发事件

How to prevent event firing on drag and drop of columns in JavaFX TableView

在我的 Spring Boot JavaFX 应用程序中,我有多个 TableView。允许用户使用默认的拖放功能对列重新排序。我还有一个侦听器来检测其中一个 TableView 中的另一行被选中并相应地采取一些行动:

/*
 * Processing when a selection in a table changes.
 */ 
getTableView().getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
    this.detailsController.get().showDetails(newValue);
});

问题是,当拖动一列然后放下(在操作的放置部分)时,此侦听器会被激活。这会产生不良副作用,因为在这种情况下变量 newValue 是 'null'(它本身是处理的有效值,我只是不想在拖动后删除列时传递该值)。有没有办法在删除列时绕过此侦听器?

我尝试了各种方法来捕获拖放事件,但无济于事...我想我可以在拖放开始时停用侦听器,并在拖放完成后重新激活。

下面是一些示例代码:

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

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TestDragDrop 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));

        table.getItems().addAll(createData());
        
        table.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue == null) {
                System.out.println("===>>> Oops");
            } else {
                System.out.println("===>>> Hi there " + newValue.getFirstName());
            }
        });

        VBox checkBoxes = new VBox(5);
        checkBoxes.getStyleClass().add("controls");

        BorderPane root = new BorderPane(table);
        root.setTop(checkBoxes);

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

    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);
    }
}

Select中的一行table: ===>>> Hi there .... 输出到控制台。现在将第一列拖到 table 中的不同位置:===>>> 糟糕输出到控制台。

因此,防止这种情况的一种方法是添加一个缓冲区,以在列被释放后的一段时间内防止更改。

在我的例子中,我使用了 50 毫秒作为缓冲区,因为在我的测试中,一个人很难完成拖动并点击一个名字,因为它在我的测试中显示为 0.05 秒,这工作正常(无空已通过)但 increase/decrease 如您所愿

这里我初始化了 PauseTransition,它将在给定时间后触发

private final PauseTransition bufferReset = new PauseTransition(Duration.millis(50));
private boolean isBuffering = false;

初始化后将变量设置为不再缓冲

bufferReset.setOnFinished(event -> isBuffering = false);

下一个代码块是我们在释放列后翻转缓冲区变量并启动计时器将变量翻转回来的地方

Platform.runLater(() -> {
    for (Node header : table.lookupAll("TableHeaderRow")) {
        if(header instanceof TableHeaderRow) {
            header.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> {
                isBuffering = true;
                bufferReset.play();
            });
        }
    }
});

从那里将您的代码包装在 isBuffering if 语句中

if(!isBuffering) {
    if (newValue == null) {
        System.out.println("===>>> Oops");
    } else {
        System.out.println("===>>> Hi there " + newValue.getFirstName());
    }
}

完整代码(不包括 class 人):

public class TestDragDrop extends Application {

    private final PauseTransition bufferReset = new PauseTransition(Duration.millis(50));
    private boolean isBuffering = false;

    @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));

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

        table.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if(!isBuffering) {
                if (newValue == null) {
                    System.out.println("===>>> Oops");
                } else {
                    System.out.println("===>>> Hi there " + newValue.getFirstName());
                }
            }
        });

        bufferReset.setOnFinished(event -> isBuffering = false);

        Platform.runLater(() -> {
            for (Node header : table.lookupAll("TableHeaderRow")) {
                if(header instanceof TableHeaderRow) {
                    header.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> {
                        isBuffering = true;
                        bufferReset.play();
                    });
                }
            }
        });

        VBox checkBoxes = new VBox(5);
        checkBoxes.getStyleClass().add("controls");

        BorderPane root = new BorderPane(table);
        root.setTop(checkBoxes);

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

    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 void main(String[] args) { launch(args); }

}