填充 JavaFX ControlsFX 自动完成文本字段导致重复内容

问题描述 投票:0回答:1

我一直在使用 JavaFX ControlsFX TextFields.bindAutoCompletion() 和异步 javafx 任务,以便在用户输入两个字符后从我的 neo4j 数据库填充自动完成结果。问题是,如果用户清除文本字段并输入新值进行搜索,现在有两个绑定,因此会显示两个自动完成弹出窗口。

Shows a text field with two autocompletion bindings, one from the old input, and one from the current input. It should just show the top one.

我需要能够完全解除旧列表中文本字段的绑定,并将其自动完成功能绑定到新列表。看来我正在使用的抽象方法,dispose()在标准AutoCompletionBinding类中没有做任何事情?

    AutoCompletionBinding<Client> clientBinding;
    private void getClientAutoComplete(TextField clientNameTextField) {
        String input = clientNameTextField.getText().toUpperCase();
        if (input.length() < 2  && clientBinding != null) {
            clientBinding.dispose();
        } else if (input.length() == 2) {
            var queryTask = SimpleCypher.getClientAutoComplete(input);

            queryTask.setOnSucceeded(event -> {
                AutoCompletionBinding<Client> clientBinding = TextFields.bindAutoCompletion(clientNameTextField, queryTask.getValue());
                clientBinding.setOnAutoCompleted(e -> getClientData(e.getCompletion().getId()));
            });

            // Start the task asynchronously
            Thread queryThread = new Thread(queryTask);
            queryThread.setDaemon(true); // Set as daemon thread to allow application exit
            queryThread.start();
        }
    }

这是 Javafx 任务:

    public static Task<List<Client>> getClientAutoComplete(String input){
        Task<List<Client>> task = new Task<>() {
                @Override
                protected List<Client> call() throws Exception {
                    List<Client> resultClients = new ArrayList<>();
                    try (Session session = DatabaseConnection.getSession()) {
                        Result result = session.run(
                                """
                                MATCH (n:Client)
                                WHERE toUpper(n.name) CONTAINS $textFieldInput
                                RETURN n.id AS id
                                , n.name AS name
                                , n.phone AS num
                                """,
                                Values.parameters("textFieldInput", input));
                        while (result.hasNext()) {
                            Record record = result.next();
                            resultClients.add(
                                new Client(
                                    record.get("id").asInt(),
                                    record.get("name").asString(),
                                    record.get("num").isNull() ? null : record.get("num").asString()
                            ));
                        }
                    }
                    return resultClients;
                }
            };
        task.setOnFailed(event -> SimpleCypher.handleQueryError(event));
        return task;
    }

我觉得解决方案是创建我自己的自定义类来重写 AutoCompletionBinding 的一些抽象方法。但是,对我来说,根据我的需要实现此目的的最佳方法是什么,即用户能够键入针对数据库查询的值,然后填充文本字段,同时还从先前的输入中删除任何先前的绑定?

这是我到目前为止的实施情况,但我不确定我需要在实施中实际投入什么才能使其正常工作?:

import java.util.Collection;

import org.controlsfx.control.textfield.AutoCompletionBinding;

import javafx.scene.Node;
import javafx.util.Callback;
import javafx.util.StringConverter;

public class Neo4jAutoCompletionBinding<T> extends AutoCompletionBinding<T> {

    protected Neo4jAutoCompletionBinding(Node completionTarget,
            Callback<ISuggestionRequest, Collection<T>> suggestionProvider, StringConverter<T> converter) {
        super(completionTarget, suggestionProvider, converter);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void dispose() {
        // TODO Auto-generated method stub
        
    }

    @Override
    protected void completeUserInput(T completion) {
        // TODO Auto-generated method stub
        
    }

}
  1. 每次运行新查询时,我都尝试处理以前的自动完成绑定。但它不起作用,所有绑定仍然存在。
  2. 我尝试绑定到 ObservableList,其中 ObservableList 由 Javafx 任务查询结果提供,但绑定永远不会更新以显示新添加的值。尽管 ObservableList 会从数据库添加新值,但它会绑定到空白列表并保持这种状态。

我希望能够输入几个字符,异步访问数据库,这样就不会冻结用户界面。然后显示有效结果,同时还消除任何先前的绑定,以便绑定不会彼此堆叠并在用户自动完成时导致混乱,并且它会自动完成到错误的值,因为应用程序焦点位于另一个绑定弹出窗口上,就像这样在此图片中可见:

Shows a text field with two autocompletion bindings, one from the old input, and one from the current input. It should just show the top one.

更新:添加 MCVE 供其他人排除故障并尝试解决方案:

项目结构: MCVE Project

代码:

package com.autocomplete.example;

import org.controlsfx.control.textfield.AutoCompletionBinding;
import org.controlsfx.control.textfield.TextFields;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

//Run project using mvn javafx:run
//You can see the bindings coninutally stack on top of eachother by using the ESC key on the keyboard to move the front one out of focus
public class AutocompleteExample extends Application {

private static final ObservableList<String> names1 = FXCollections.observableArrayList(
        "Alice", "Adam", "Alfred", "Amon", "Alfredo", "Al", "Albert"
);

private static final ObservableList<String> names2 = FXCollections.observableArrayList(
        "Bob", "Conner", "Robin", "Fred", "Freddy", "Edward", "Fredward", "Mariam"
);

@Override
public void start(Stage primaryStage) {
    TextField textField = new TextField();
    
    textField.setOnKeyTyped(event -> {
        AutoCompletionBinding<String> nameBinding = null;
        String input = textField.getText().toUpperCase();
        if (input.length() == 2){
            if (input.startsWith("A")) {
                if (nameBinding != null) nameBinding.dispose();
                nameBinding = TextFields.bindAutoCompletion(textField, names1);
                nameBinding.setOnAutoCompleted(val -> System.out.println("You selected "+ val.getCompletion() +" from list 1."));
            } else {
                if (nameBinding != null) nameBinding.dispose();
                nameBinding = TextFields.bindAutoCompletion(textField, names2);
                nameBinding.setOnAutoCompleted(val -> System.out.println("You selected "+ val.getCompletion() +" from list 2."));
            }
        } else if (nameBinding != null && input.length() < 2) nameBinding.dispose();
    });

    VBox root = new VBox(10, textField);
    Scene scene = new Scene(root, 300, 200);
    primaryStage.setScene(scene);
    primaryStage.setTitle("Autocomplete Example");
    primaryStage.show();
}

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

}

POM:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.autocomplete.example</groupId>
    <artifactId>AutocompleteExample</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.release>21</maven.compiler.release>
        <javafx.version>21.0.4</javafx.version>
        <exec.mainClass>com.autocomplete.example.AutocompleteExample</exec.mainClass>
    </properties>
        <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-base</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.neo4j.driver/neo4j-java-driver -->
        <dependency>
            <groupId>org.neo4j.driver</groupId>
            <artifactId>neo4j-java-driver</artifactId>
            <version>5.18.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.controlsfx/controlsfx -->
        <dependency>
            <groupId>org.controlsfx</groupId>
            <artifactId>controlsfx</artifactId>
            <version>11.2.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <release>${maven.compiler.release}</release>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.8</version>
                <executions>
                    <execution>
                        <!-- Default configuration for running -->
                        <!-- Usage: mvn clean javafx:run -->
                        <id>default-cli</id>
                        <configuration>
                            <mainClass>${exec.mainClass}</mainClass>
                            <options>
                                <option>--add-exports</option>
                                <option>javafx.base/com.sun.javafx.event=org.controlsfx.controls</option>
                                <option>--add-modules=javafx.base</option>
                            </options>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

模块信息文件:

module com.autocomplete.example {
requires javafx.base;
requires javafx.fxml;
requires transitive javafx.controls;
requires transitive javafx.graphics;
requires org.controlsfx.controls;

opens com.autocomplete.example to javafx.fxml;
exports com.autocomplete.example;
}

MCVE Being Ran with Double AutoCompletion Bindings

java javafx autocomplete controlsfx
1个回答
0
投票

使用来自https://stackoverflow.com/a/76615992/2423906

的@James_D答案
import java.util.Collections;
import java.util.stream.Collectors;
import org.controlsfx.control.textfield.TextFields;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

//Run project using mvn javafx:run
//You can see the bindings coninutally stack on top of eachother by using the ESC key on the keyboard to move the front one out of focus
public class App extends Application {

    private static final ObservableList<String> names1 = FXCollections.observableArrayList(
            "Alice", "Adam", "Alfred", "Amon", "Alfredo", "Al", "Albert"
    );

    private static final ObservableList<String> names2 = FXCollections.observableArrayList(
            "Bob", "Conner", "Robin", "Fred", "Freddy", "Edward", "Fredward", "Mariam"
    );

    @Override
    public void start(Stage primaryStage) {
        TextField textField = new TextField();
        TextFields.bindAutoCompletion(textField, input -> {
            if (input.getUserText().length() < 2) {
                return Collections.emptyList();
            }
            if (input.getUserText().toLowerCase().startsWith("a")) {
                return names1.stream().filter(s -> s.toLowerCase().contains(input.getUserText().toLowerCase())).collect(Collectors.toList());
            }
            return names2.stream().filter(s -> s.toLowerCase().contains(input.getUserText().toLowerCase())).collect(Collectors.toList());
        });

        VBox root = new VBox(10, textField);
        Scene scene = new Scene(root, 300, 200);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Autocomplete Example");
        primaryStage.show();
    }

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

© www.soinside.com 2019 - 2024. All rights reserved.