JavaFX ControlsFX自动补全TextField出现重复绑定弹窗问题
JavaFX + ControlsFX 自动补全绑定叠加问题
我在JavaFX中使用ControlsFX的TextFields.bindAutoCompletion()结合异步JavaFX Task实现自动补全功能:当用户输入2个字符后,从Neo4j数据库异步获取补全结果。但遇到以下问题:
用户清空文本框后再次输入新内容时,会出现多个自动补全弹窗叠加的情况,旧的绑定并未被清除。
我尝试过两种解决方案,但都无效:
- 每次发起新查询前调用
AutoCompletionBinding.dispose()移除旧绑定,但旧绑定依然保留 - 将自动补全绑定到由Task更新的ObservableList,但列表内容更新后,绑定并未同步更新
期望实现:
- 异步查询过程不阻塞UI
- 每次发起新查询时彻底清除旧绑定,避免弹窗叠加导致用户选择错误
核心代码
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())); }); // 异步启动任务 Thread queryThread = new Thread(queryTask); queryThread.setDaemon(true); // 设置为守护线程,不影响应用退出 queryThread.start(); } }
JavaFX Task代码
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; }
自定义Binding类(未完成)
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 自动生成的构造函数存根 } @Override public void dispose() { // TODO 自动生成的方法存根 } @Override protected void completeUserInput(T completion) { // TODO 自动生成的方法存根 } }
MCVE代码
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; // 使用mvn javafx:run运行项目 // 按ESC键可以看到绑定的弹窗不断叠加,把前面的弹窗移开就能发现 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("你选择了列表1中的"+ val.getCompletion() +"。")); } else { if (nameBinding != null) nameBinding.dispose(); nameBinding = TextFields.bindAutoCompletion(textField, names2); nameBinding.setOnAutoCompleted(val -> System.out.println("你选择了列表2中的"+ val.getCompletion() +"。")); } } 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("自动补全示例"); 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> <!-- Neo4j Java驱动 --> <dependency> <groupId>org.neo4j.driver</groupId> <artifactId>neo4j-java-driver</artifactId> <version>5.18.0</version> </dependency> <!-- 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> <!-- 默认运行配置 --> <!-- 使用: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-info文件
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; }
内容的提问来源于stack exchange,提问作者GQ16
相关产品推荐
相关产品推荐

