在TableView更新期间,鼠标事件有时会丢失

时间:2017-05-17 22:42:14

标签: java javafx javafx-8

背景。我正在使用来自JavaFX 2.1 TableView refresh items的刷新解决方案,因为我正在进行的项目停留在Java 8u40上。我知道在8u60中添加了refresh()

我也使用自定义单元格内容,而且内容包含鼠标听众,客户要求离奇,不要问。

问题是在表更新时发生的鼠标事件有时会丢失。 TableCell可以看到该事件,但不是自定义内容。

最后的示例演示了如果您反复点击"自定义内容"标签。我希望总能看到一对事件。

MouseClicked on custom stuff
MouseClicked on TableCell

但是,在某些情况下,我们只会看到MouseClicked on TableCell

我只能假设此时表格位于重绘的中间位置,自定义标签目前不是场景图形的一部分。我不明白这是如何发生的,因为鼠标事件和表更新都发生在同一个线程上....

我在更好地解释正在发生的事情和可能的解决方法之后。

public class Test extends Application {

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

    public class Item {
        private int x;
        public Item(int x) {
            this.x = x;
        }
        public int getX() {
            return x;
        }
        public void setX(int x) {
            this.x = x;
        }
        @Override
        public int hashCode() {
            return x;
        }
        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Item) {
                return x == ((Item) obj).x;
            }
            return false;
        }
    }

    private static class CustomCell extends TableCell<Item, Integer> {

        private Label custom;

        public CustomCell() {
            super();
            setOnMouseClicked(event -> System.out.println("MouseClicked on TableCell"));
            custom = new Label("Custom stuff");
            custom.setOnMouseClicked(event -> System.out.println("MouseClicked on custom stuff"));
        }

        @Override
        protected void updateItem(Integer item, boolean empty) {
            super.updateItem(item, empty);
            if (item == null || empty) {
                setGraphic(null);
            } else {
                setGraphic(custom);
            }
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        List<Item> original = Arrays.asList(new Item(1), new Item(2));
        ObservableList<Item> list = FXCollections.observableArrayList(original);
        TableView<Item> tableView = new TableView<Item>(list);
        TableColumn<Item, Integer> column = new TableColumn<>();
        column.setPrefWidth(100);
        column.setCellValueFactory(new PropertyValueFactory<>("x"));
        column.setCellFactory(col -> new CustomCell());
        tableView.getColumns().add(column);
        primaryStage.setScene(new Scene(new HBox(tableView)));
        primaryStage.show();

        Timeline animation = new Timeline();
        animation.setCycleCount(Animation.INDEFINITE);
        animation.getKeyFrames().add(new KeyFrame(Duration.millis(400), e -> {
            // poor-mans refresh
            list.removeAll(list);
            list.addAll(original);
        }));
        animation.play();
    }
}

进一步的调查显示,setGraphic(null)的调用导致了问题,而removeAll()又引发了问题。在项目到位之前的这段时间内,事件将丢失。

我开发了一种替代刷新机制,它滥用了单元格值工厂并避免了removeAll()调用。

1)将JavaFX属性添加到您的项目模型中,该属性不会用于其他任何内容

private IntegerProperty update = new SimpleIntegerProperty();

public final IntegerProperty updateProperty() {
    return this.update;
}

public void update() {
    updateProperty().set(update.get() + 1);
}

2)更改所有需要更新的列以使用它,这并不重要,因为所有列都是从多个字段派生它们的值

 column.setCellValueFactory(new PropertyValueFactory<>("update"));

3)改变穷人刷新

// poor-mans refresh
for (Item each : list) {
    each.update();
}

0 个答案:

没有答案