JavaFX editable ComboBox with history function

一笑奈何 提交于 2019-12-13 20:12:34

问题


I am attempting to make a JavaFX ComboBox that remembers the history of the entries entered by the user. Adding new items works, but selecting from the drop-down does not.

In a nutshell, I am trying to get the control to

  1. Add the most recently typed entry to the top, as the first item of the ComboBox.
  2. Clear the TextField portion for the next entry.
  3. Upon selecting an item from the ComboBox, will copy selection to the TextField, without modifying the ComboBox's items.

Adding new items works fine, it's the copying a previous entry to the field is proving frustrating.

The only similar problem I could find was javafx combobox items list issue, whose solution unfortunately did not fix my problem.

Code

import java.util.LinkedList;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.ComboBox;


public class HistoryField<String> extends ComboBox<String> {
    public final static int DEFAULT_MAX_ENTRIES = 256;

    //Data members
    private int maxSize;
    private final ObservableList<String> history;

   //Default constructor
    public HistoryField() {
        this(DEFAULT_MAX_ENTRIES, (String[]) null);
    }

    public HistoryField(int maxSize, String ... entries) {
        super(FXCollections.observableList(new LinkedList<>()));
        this.setEditable(true);

        this.maxSize = maxSize;
        this.history = this.getItems();


        //Populate list with entries (if any)
        if (entries != null) {
            for (int i = 0; ((i < entries.length) && (i < this.maxSize)); i++) {
                this.history.add(entries[i]);
            }
         }

        this.valueProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
            if ((oldValue == null) && (newValue != null)) {                
                if (this.getSelectionModel().getSelectedIndex() < 0) {
                    this.getItems().add(0, newValue);
                    this.getSelectionModel().clearSelection();
                }
            } else {

                //This throws IndexOutOfBoundsException
                this.getSelectionModel().clearSelection();
            }
        });
    }
}

Test class

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class HistoryFieldTest extends Application {
    private HistoryField<String> historyField;

    @Override
    public void start(Stage primaryStage) {        
        this.historyField = new HistoryField<>();

        BorderPane root = new BorderPane();
        root.setBottom(historyField);

        Scene scene = new Scene(root, 300, 250);
        primaryStage.setTitle("History Field Test");
        primaryStage.setScene(scene);

        primaryStage.show();
    }

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

Thank you!


回答1:


Try the following updated HistoryField class. There are a couple of changes I made.

First of all, instead of passing a varargs of Strings, just pass the array you want. You can then convert the array to a List ready for the ComboBox using Arrays.asList() method.

Also, I streamlined the process of adding a new item to the list. The listener will now only add an item to the list if it doesn't already exist.

You also never handled the max_size situation, so I added a simple check to remove the older entry once the max_size has been reached.

The field is only cleared if an item is added to the list; it is unclear from your question if that is the desired behavior.

public class HistoryField extends ComboBox<String> {

    private final static int DEFAULT_MAX_ENTRIES = 5;

    //Data members
    private int maxSize;
    private final ObservableList<String> history;

    //Default constructor
    public HistoryField() {
        this(DEFAULT_MAX_ENTRIES, null);
    }

    /* Changed parameter to an array instead of list of Strings */
    public HistoryField(int maxSize, String[] entries) {
        this.setEditable(true);

        this.maxSize = maxSize;

        /* Convert the passed array to a list and populate the dropdown */
        if (entries != null) {
            history = FXCollections.observableArrayList(Arrays.asList(entries));
        } else {
            history = FXCollections.observableArrayList();
        }

        setItems(history);

        this.valueProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue != null) {

                // Check if value already exists in list
                if (!this.history.contains(newValue)) {
                    this.history.add(0, newValue);

                    // If the max_size has been reached, remove the oldest item from the list
                    if (this.history.size() > maxSize) {
                        this.history.remove(history.size() - 1);
                    }

                    System.out.println(history);

                    // Clear the selection when new item is added
                    this.getSelectionModel().clearSelection();
                }
            }
        });
    }
}


来源:https://stackoverflow.com/questions/51993138/javafx-editable-combobox-with-history-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!