2

我们在 JavaFX 中创建了一个小型绘画应用程序。出现了一个新要求,我们必须警告用户他进行了更改,这些更改尚未持久化并询问他,用户是否愿意在关闭之前先保存。

示例快照:

画布图像

不幸的是,有很多不同的节点,并且可以通过多种方式更改节点,例如多边形点可以移动。节点本身可以​​拖动。它们可以旋转等等。因此,在为 Node 对象对画布的每一次可能更改触发无数事件之前,我想问一下,是否有人对如何简化这种方法有想法。我很好奇,如果有任何听众,我可以在 JavaFX 的场景图中收听画布对象的任何变化。

特别是因为我只想知道是否有任何变化,而不需要知道具体的变化。

此外,我也不希望获得每个事件,例如简单的选择,这会导致在所选节点周围显示边框(如图所示),这并不意味着用户必须保存他的离开前申请。

有人有想法吗?或者我真的需要为节点中的每一次更改触发事件吗?

4

2 回答 2

6

我认为你以错误的方式处理这个问题。屏幕上显示的节点应该只是底层模型的可视化表示。你真正需要知道的是底层模型已经改变。

例如,如果您正在编写文本编辑器,则屏幕上显示的文本将由某种模型支持。假设模型是String. 您无需检查屏幕上显示的任何文本节点是否已更改,只需将原始字符串数据与当前字符串数据进行比较,以确定是否需要提示用户保存。

于 2014-11-07T10:16:45.380 回答
2

本杰明的答案可能是最好的答案:您应该使用底层模型,该模型可以轻松检查相关状态是否已更改。在应用程序开发的某个阶段,您会意识到这是做事的正确方法。看来你已经到了那个地步。

但是,如果您想进一步推迟不可避免地重新设计应用程序(并在您确实达到这一点时使其更加痛苦;)),您可以考虑使用另一种方法。

显然,您有某种东西Pane正在握住正在绘制的对象。用户必须正在创建这些对象,并且您在某个时候将它们添加到窗格中。只需创建一个处理该添加的方法,并在您这样做时使用感兴趣的属性注册一个失效侦听器。该结构将如下所示:

private final ReadOnlyBooleanWrapper unsavedChanges = 
    new ReadOnlyBooleanWrapper(this, "unsavedChanged", false);

private final ChangeListener<Object> unsavedChangeListener = 
    (obs, oldValue, newValue) -> unsavedChanges.set(true);

private Pane drawingPane ;

// ...

Button saveButton = new Button("Save");
saveButton.disableProperty().bind(unsavedChanges.not());

// ...
@SafeVarArgs
private final <T extends Node> void addNodeToDrawingPane(
        T node, Function<T, ObservableValue<?>>... properties) {

    Stream.of(properties).forEach(
        property -> property.apply(node).addListener(unsavedChangeListener));
    drawingPane.getChildren().add(node);
}

现在你可以做类似的事情

    Rectangle rect = new Rectangle();

    addNodeToDrawingPane(rect, 
            Rectangle::xProperty, Rectangle::yProperty, 
            Rectangle::widthProperty, Rectangle::heightProperty);

    Text text = new Text();
    addNodeToDrawingPane(text, 
            Text::xProperty, Text::yProperty, Text::textProperty);

即,您只需指定添加新节点时要观察的属性。您也可以创建一个删除侦听器的删除方法。除了您已经拥有的代码之外,额外代码的数量非常少,因为(可能,我还没有看到您的代码)是重构。

同样,你真的应该有一个单独的视图模型等。我想发布这个以表明@kleopatra 对这个问题的第一条评论(“监听相关状态的无效”)如果你接近,不一定涉及很多工作它以正确的方式。起初,我认为这种方法与@Tomas Mikula 提到的撤消/重做功能不兼容,但您甚至可以使用这种方法作为基础。

于 2014-11-07T13:44:43.410 回答