JavaFX Combobox отображает запись, отличную от выбранной - PullRequest
0 голосов
/ 06 января 2019

Я заметил, что значение, отображаемое в ComboBox после выбора одного из списка, может отличаться от выбранного, если два значения равны в соответствии с их методом equals (но имеют другое представление в соответствии с методом toString, при этом отображается по-разному).

Это видно из следующего примера программы

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            Group group = new Group();
            Scene scene = new Scene(group,100,40);

            ObservableList<EqualContent> content = FXCollections.observableArrayList();
            content.add(new EqualContent("A"));
            content.add(new EqualContent("B"));

            Label selection = new Label();

            ComboBox<EqualContent> demoBox = new ComboBox<EqualContent>(content);
            demoBox.setOnAction(event -> selection.setText(" selected: "+demoBox.getValue()));

            group.getChildren().add(new HBox(demoBox, selection));

            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

    class EqualContent {
        private String name;
        EqualContent(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return name;
        }
        @Override
        public boolean equals(Object other) {
            return other != null;
        }
        @Override
        public int hashCode() {
            return 0;
        }
    }
}

, где выбор пункта B приводит к следующему:

enter image description here

Более того, кажется, что потом невозможно выбрать A.

Первым вариантом решения этой проблемы, безусловно, было бы изменение метода равных, но я не хочу делать это в моем случае, потому что мой реальный класс также имеет сравнение, и я хочу, чтобы они были согласованными.

Второй вариант - построить класс-оболочку вокруг EqualContent, который также рассматривает строковое представление для равных. Я, конечно, мог бы это сделать, но я не совсем доволен этим.

Мне не хватает более простого или изящного решения?

1 Ответ

0 голосов
/ 06 января 2019

Из комментариев кажется, что я не могу обойтись с помощью обертки. Моим примером решения является следующий обобщенный класс (который предоставляет больше функциональности по мере необходимости, но у меня было что-то подобное уже в другом месте):

static class RenameWrapper<T> {
        public final T wrappedObject;
        public final Callback<T, String> renamer;
        RenameWrapper(T wrappedObject, Callback<T, String> renamer) {
            this.wrappedObject = wrappedObject;
            this.renamer = renamer;
        }
        @Override
        public String toString() {
            return renamer.call(wrappedObject);
        }
        public static <S> ArrayList<RenameWrapper<S>> wrapList(List<S> objectsToWrap, Callback<S, String> renamer) {
            ArrayList<RenameWrapper<S>> result = new ArrayList<RenameWrapper<S>>();
            objectsToWrap.forEach(o -> result.add(new RenameWrapper<S>(o, renamer)));
            return result;
        }
        /**
         * This and other are considered equal if other is a RenameWrapper that contains the same
         * wrappedObject according to their equals and the renamer produces the same String representation.
         */
        @Override
        public boolean equals(Object other) {
            if(this == other) return true;
            if(!(other instanceof RenameWrapper)) return false;
            RenameWrapper<?> otherWrapper = (RenameWrapper<?>) other;
            return wrappedObject.equals(otherWrapper.wrappedObject) &&
                    this.toString().equals(other.toString());
        }
        @Override
        public int hashCode() {
            return Objects.hash(wrappedObject, this.toString());
        }
    }

Тогда вы можете сделать

ObservableList<RenameWrapper<EqualContent>> wrappedContent = FXCollections.observableArrayList(
                    RenameWrapper.wrapList(content, eq -> eq.toString()));

и заполните ComboBox из wrappedContent вместо содержимого, как это было раньше.

Обратите внимание, что использование toString () здесь не совсем хорошая практика ...

...