JavaFX - 获取在 TextArea 中输入的当前单词

JavaFX - get current word being typed in TextArea

我正在尝试获取当前在 JavaFX TextArea 中键入的当前单词或部分单词,以传递给非常基本的自动完成 getPredictions(String prefix);方法。

现在我正在 KeyTyped 上使用事件处理程序,我将跟踪每个键入的字符并将其与之前键入的单词连接起来,重置为 space。这将有很多陷阱,我可以看到在键入单词的过程中自动完成被打开,如果 backspace 被击中并且插入符号再次位于单词的末尾,则获取前一个单词。

我很好奇是否有任何人能想到的替代方法,我可以使用 TextInputControl 方法从插入符号位置简单地获取单词的字符串回到最后一个 space。我还没有按照我的方式工作,但我会 post 我现在所拥有的。

我基本上需要获取当前键入的单词,将当前插入符号位置保留为使用类型。

public AutoSpellingTextArea() {

    // register and handle KEY_TYPED event
    this.addEventHandler(KeyEvent.KEY_TYPED,new EventHandler<KeyEvent>(){

        public void handle(KeyEvent t) {

            // BUG -- turning on autocomplete in middle of word.
            if(autoCompleteOn) {
                //TODO
                String pre = getCurrentPrefix(t.getCharacter());

                if(pre != null) {
                    List<String> choices = ac.predictCompletions(pre, NUM_COMPLETIONS);
                }
            }
        }

    });
}


private String getCurrentPrefix(String ch) {
    String retVal = null;

    // space entered
    if(ch.equals(" ")) {
        lastWord = currentPrefix;
        currentPrefix = "";
        return retVal;
    }

    // add next character to prefix string
    currentPrefix = currentPrefix + ch;

    return retVal;

}

也许您只需观察 caratPositionProperty 并回溯到最后一个空白字符就可以得到您需要的东西:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class AutoCompleteTextAreaTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        TextArea textArea = new TextArea();
        Label currentWord = new Label();

        textArea.caretPositionProperty().addListener((obs, oldPosition, newPosition) -> {
            String text = textArea.getText().substring(0, newPosition.intValue());
            int index ;
            for (index = text.length() - 1; index >= 0 && ! Character.isWhitespace(text.charAt(index)); index--);
            String prefix = text.substring(index+1, text.length());
            currentWord.setText(prefix);
        });


        BorderPane root = new BorderPane(textArea, currentWord, null, null, null);
        primaryStage.setScene(new Scene(root, 600, 600));
        primaryStage.show();
    }

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

您也可以将这个想法扩展到从克拉位置向前搜索,这样如果光标位于单词的中间,您可以识别在该点插入可能产生的所有可能的单词。

这是一个 SSCCE 演示这一点,使用我在网上找到的随机单词列表:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class AutoCompleteTextAreaTest extends Application {

    private static final String WORD_LIST_URL = "https://raw.githubusercontent.com/dwyl/english-words/master/words.txt?raw=true";

    @Override
    public void start(Stage primaryStage) {

        StackPane loadingRoot = new StackPane(new ProgressBar());
        Scene scene = new Scene(loadingRoot, 600, 600);


        List<String> words = new ArrayList<>();

        Task<List<String>> loadWordsTask = new Task<List<String>>() {
            @Override
            public List<String> call() throws Exception {
                try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL(WORD_LIST_URL)
                        .openConnection()
                        .getInputStream()))) {

                    return in.lines()
                            .collect(Collectors.toList());
                }
            }

        };

        ListView<String> suggestions = new ListView<>();

        TextArea textArea = new TextArea();


        textArea.caretPositionProperty().addListener((obs, oldPosition, newPosition) -> {
            String text = textArea.getText().substring(0, newPosition.intValue());
            int index ;

            for (index = text.length() - 1; 
                    index >= 0 && ! Character.isWhitespace(text.charAt(index)); 
                    index--);
            String prefix = text.substring(index+1, text.length());

            for (index = newPosition.intValue(); 
                    index < textArea.getLength() && ! Character.isWhitespace(textArea.getText().charAt(index)); 
                    index++);
            String suffix = textArea.getText().substring(newPosition.intValue(), index);

            // replace regex wildcards (literal ".") with "\.". Looks weird but correct...
            prefix = prefix.replaceAll("\.", "\.");
            suffix = suffix.replaceAll("\.", "\.");

            Pattern pattern = Pattern.compile(prefix+".*"+suffix, 
                    Pattern.CASE_INSENSITIVE);

            suggestions.getItems().setAll(
                words.stream().filter(word -> pattern.matcher(word).matches())
                .sorted(Comparator.comparing(String::length))
                .limit(100)
                .collect(Collectors.toList())
            );
        });


        BorderPane root = new BorderPane(textArea, null, suggestions, null, null);

        loadWordsTask.setOnSucceeded(e -> {
            words.addAll(loadWordsTask.getValue());
            scene.setRoot(root);
        });

        loadWordsTask.setOnFailed(e -> {
            suggestions.setPlaceholder(new Label("Could not load word list"));
            loadWordsTask.getException().printStackTrace();
            scene.setRoot(root);
        });

        Thread t = new Thread(loadWordsTask);
        t.setDaemon(true);
        t.start();

        primaryStage.setScene(scene);
        primaryStage.show();
    }

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