进行多次变更触发导致更少的动作 [英] Make multiple change firings lead to far fewer actions

查看:71
本文介绍了进行多次变更触发导致更少的动作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 TextArea ,我的应用程序用户可以在其中编写内容。 ChangeListener 也在监听此 TextArea StringProperty 文本 $ c>。每当文本内容更改时, ChangeListener.changed()都会将脏 BooleanProperty 设置为 true 在中央应用程序对象上。

I have a TextArea in which the user of my app can write things. A ChangeListener is also listening to the StringProperty "text" of this TextArea. Whenever the text content changes, ChangeListener.changed(), among other things, sets a "dirty" BooleanProperty to true on a central app object. Where "dirty" has the sense of "document needs saving".

但是我刚刚在我的应用程序中实现了一个东西,因此只要 dirty 属性设置为 true 会自动触发保存文件到磁盘操作,因此用户不必担心手动保存东西。注意,保存行为还会将设置为 false

But I've just implemented a thing in my app whereby any time that the "dirty" Property gets set to true triggers a save-file-to-disk action, automatically, so the user doesn't have to worry about manually saving things. NB the act of saving also sets dirty back to false of course.

与此有关的一个问题是,它会减慢 TextArea 的键入速度(特别是这种保存发生在FX线程上)。因此,每个添加或删除的新字符都会触发保存操作。

One problem with this is, though, that it slows typing in the TextArea (particulary as this saving takes place on the FX thread). Each new character added or deleted therefore fires a save action.

我想找到一种解决方案,其中每个文本更改操作始终最多保存一次例如,在1秒钟之内,但是节省的时间永远不会超过1秒钟一次……显然在非FX线程中。

I want to find a solution where every single change-of-text action is always followed by a save at most within, say, 1 second, but where saving never happens more than once per this 1 second ... obviously in a non-FX thread.

第一次遇到这种情况,过去我经常修改各种计时器队列,依此类推。但是很难找到一个同时满足这两个标准的解决方案,我只是想知道是否存在一种众所周知的技术来解决这个问题……甚至可能是某些图书馆提供的东西?

This isn't the first time I've encountered this situation and in the past I've tinkered with various queues of timers, and so on. But it's difficult to find a solution where both criteria are met and I'm just wondering if there's a well-known technique to deal with this... even maybe something from some library?

这是MRE / MCVE:

Here's an MRE/MCVE:

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;

public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    BooleanProperty dirty;
    void setDirty(Boolean value) {
        dirtyProperty().set(value);
        if( value ){
            System.out.println( "serialise to file again...");
            // successful saving also means we become clean again:
            dirtyProperty().set( false );
        }
    }
    Boolean getDirty() { return dirtyProperty().get(); }
    BooleanProperty dirtyProperty() {
        if ( dirty == null) dirty = new SimpleBooleanProperty(this, "dirty");
        return dirty;
    }

    @Override
    public void start(Stage stage) throws Exception {
        Scene scene = new Scene(new Group());
        Group sceneRoot = (Group)scene.getRoot();
        TextArea textArea = new TextArea();

        textArea.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observableValue, String s, String t1) {
                setDirty( true );
            }
        });

        sceneRoot.getChildren().add( textArea );
        stage.setMinWidth( 600 );
        stage.setMinHeight( 400 );
        stage.setScene(scene);
        stage.show();
    }
}

每次击键都会导致保存...

Each keystroke causes a save...

推荐答案

而不是使用后台线程,而是使用暂停过渡:

Instead of using background threads, use a pause transition:

PauseTransition pause = new PauseTransition(Duration.seconds(1));
pause.setOnFinished(e -> {
    // save action here
});

dirtyProperty().addListener((obs, wasDirty, isNowDirty) -> {
    if (isNowDirty && pause.getStatus() != Animation.Status.RUNNING) {
        pause.playFromStart();
    }
});

这将在 dirtyProperty()时开始一秒钟的暂停code>更改为true,在暂停结束时将保存数据。但是,通过检查暂停的状态,它最多不会安排每秒保存一次。

This will start the one-second pause when the dirtyProperty() changes to true, and at the end of the pause, will save the data. However, by checking the status of the pause, it will not schedule more than one save per second.

如果您确实想使用后台线程,则可以执行一些操作以下行:

If you do want to use background threads, you can do something along the following lines:

BlockingQueue<String> textToSave = new ArrayBlockingQueue<>(1);
ScheduledExecutorService exec = Executors.newScheduledThreadPool(1);
exec.scheduleWithFixedDelay(() -> {
    try {
        String text = textToSave.take();
        // save text
    } catch (InterruptedException interrupt) {
        Thread.currentThread().interrupt();
    }
}, 0, 1, TimeUnit.SECONDS);

然后

dirtyProperty().addListener((obs, wasDirty, isNowDirty) -> {
    textToSave.clear();
    textToSave.offer(myTextArea.getText());
});

这假设除FX Application Thread之外没有其他线程将数据推送到 textToSave 队列(不确定大小是否小于&= 1的东西是否正确称为队列),这似乎很容易确保。

This assumes no threads other than the FX Application Thread is pushing data to the textToSave "queue" (not sure if something of size <= 1 is properly called a "queue"), which seems easy enough to ensure.

此方法的优点是IO发生在后台线程上,这意味着没有任何机会通过写入文件来阻止FX Application线程。

The advantage of this approach is that the IO happens on a background thread, which means there's no opportunity whatsoever to block the FX Application Thread by writing the file.

这篇关于进行多次变更触发导致更少的动作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆