2

ここでリアクティブプログラミングを初めて使用します。

ReactFX を使用して JavaFX に「怠惰な」リアルタイム検索テキスト領域を実装しようとしています。ここで遅延とは、ユーザーが入力を 1 秒間停止すると検索を実行することを意味します。そのためのコードは非常に単純でした:

EventStream<Change<String>> textEvents = EventStreams.changesOf(textArea.textProperty())
.successionEnds(Duration.ofSeconds(1));

次に、そのイベント ストリームをサブスクライブすると、出来上がりです。

しかし、ユーザーが Enter キーを押すとすぐに検索を実行することも必要です。「反応的」な方法でそれを行う方法がわかりません。Enter キー イベントで検索を実行するだけで、検索が 2 回 (キー イベントに対して 1 つとテキスト変更に対して 1 つ) 発生するため、これが私の現在の解決策です。

BooleanProperty hasSearched = new SimpleBooleanProperty(false);
EventStream<KeyEvent> enterKeyPressedEvents = EventStreams.eventsOf(textArea, KeyEvent.KEY_PRESSED)
        .filter(k -> k.getCode() == KeyCode.ENTER);
AwaitingEventStream<Change<String>> textEvents = EventStreams.changesOf(textArea.textProperty())
        .successionEnds(Duration.ofSeconds(1));

subs = Subscription.multi(
        //Text changed
        textEvents.subscribe(e -> {
            if (hasSearched.get()) {
                hasSearched.set(false);
                System.out.println("ignored text event");
            } else {
                performSearch(textArea.getText());
            }
        }),

        //Enter key pressed
        enterKeyPressedEvents.subscribe(e -> {
            e.consume();
            if (e.isShiftDown()) {
                textArea.insertText(textArea.getCaretPosition(), "\n");
            } else {
                hasSearched.set(true);
                System.out.println("enter pressed");
                performSearch(textArea.getText());
                if (!textEvents.isPending()) {
                    hasSearched.set(false);
                }
            }
        })
);

保留中のすべてのイベントを「ドロップ」すると考えて使用してみましSuspendableEventStream.suspend()たが、期待どおりに機能せず、保留中のイベントがまだ発行されています。

EventStream<KeyEvent> enterKeyPressedEvents = EventStreams.eventsOf(textArea, KeyEvent.KEY_PRESSED)
        .filter(k -> k.getCode() == KeyCode.ENTER);
SuspendableEventStream<Change<String>> textEvents = EventStreams.changesOf(textArea.textProperty())
        .successionEnds(Duration.ofSeconds(1)).suppressible();

subs = Subscription.multi(
        //Text changed
        textEvents.subscribe(e -> {
                performSearch(textArea.getText());
        }),

        //Enter key pressed
        enterKeyPressedEvents.subscribe(e -> {
            e.consume();
            if (e.isShiftDown()) {
                textArea.insertText(textArea.getCaretPosition(), "\n");
            } else {
                Guard guard = textEvents.suspend();
                System.out.println("enter pressed");
                performSearch(textArea.getText());
                guard.close();
            }
        })
);

より良い (より反応性の高い) ソリューションをどのように考えることができますか?

4

3 に答える 3

2

ここに別の解決策があります。これは、Enter キーを押した回数をカウントし、その間に Enter 回数が変更されていない場合にのみ、編集イベントが検索をトリガーできるようにします。

import java.time.Duration;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;

import org.reactfx.EventStream;
import org.reactfx.EventStreams;

public class AutoSearch2 extends Application {

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

    @Override
    public void start(Stage stage) throws Exception {
        TextArea area = new TextArea();

        EventStream<KeyEvent> enterPressed = EventStreams.eventsOf(area, KeyEvent.KEY_PRESSED)
                .filter(k -> k.getCode() == KeyCode.ENTER)
                .hook(KeyEvent::consume);

        EventStream<Long> enterCount = enterPressed.accumulate(0L, (n, k) -> n + 1)
                                                   .withDefaultEvent(0L);

        EventStream<Long> delayedEdits = enterCount.emitOnEach(EventStreams.changesOf(area.textProperty()))
                                                   .successionEnds(Duration.ofSeconds(1));

        // check that the delayed edit event still has the current value of the Enter counter
        EventStream<?> validEdits = enterCount.emitBothOnEach(delayedEdits)
                                              .filter(cd -> cd.test((current, delayed) -> delayed == current));

        EventStream<?> searchImpulse = EventStreams.merge(enterPressed, validEdits);

        searchImpulse.subscribe(x -> System.out.println("Search now!"));

        stage.setScene(new Scene(area));
        stage.show();
    }

}
于 2016-09-09T03:44:38.557 に答える