Как я могу заполнить ListView в JavaFX с помощью пользовательских объектов?

Я немного новичок в Java, JavaFX и программировании в целом, и у меня есть проблема, которая ломает мне голову.

В большинстве уроков, которые я посмотрел, касалось заполнения ListView (в частности, с помощью ObservableArrayList), самый простой способ сделать это - создать его из ObservableList of Strings, например, так:

ObservableList<String> wordsList = FXCollections.observableArrayList("First word","Second word", "Third word", "Etc."); 
ListView<String> listViewOfStrings = new ListView<>(wordsList);

Но я не хочу использовать строки. Я хотел бы использовать созданный мной объект под названием Words:

ObservableList<Word> wordsList = FXCollections.observableArrayList();
wordsList.add(new Word("First Word", "Definition of First Word");
wordsList.add(new Word("Second Word", "Definition of Second Word");
wordsList.add(new Word("Third Word", "Definition of Third Word");
ListView<Word> listViewOfWords = new ListView<>(wordsList);

Каждый объект Word имеет только 2 свойства: wordString (строка слова) и определение (еще одна строка, которая является определением слова). У меня есть геттеры и сеттеры для обоих.

Вы можете видеть, куда это идет - код компилируется и работает, но когда я отображаю его в своем приложении, а не отображает заголовки каждого слова в ListView, он отображает сам объект Word в виде строки!

Изображение, показывающее мое приложение и его ListView

Мой вопрос здесь, в частности, есть ли простой способ переписать это:

ListView<Word> listViewOfWords = new ListView<>(wordsList);

Таким образом, что вместо того, чтобы брать слова непосредственно из wordsList, он обращается к свойству wordString в каждом Word моего observableArrayList?

Просто чтобы быть понятным, это не для Android, и список слов будет изменен, сохранен и загружен в конце концов, поэтому я не могу просто создать другой массив для хранения wordStrings. Я провел небольшое исследование в сети, и, кажется, есть такая вещь, как "Сотовые фабрики", но она кажется излишне сложной для того, что кажется такой простой проблемой, и, как я уже говорил, я немного новичок, когда дело доходит до программирования.

Кто-нибудь может помочь? Это мой первый раз здесь, так что извините, если я не включил достаточно моего кода, или я сделал что-то не так.

3 ответа

Решение

Решение Подход

Я советую использовать фабрику клеток для решения этой проблемы.

listViewOfWords.setCellFactory(param -> new ListCell<Word>() {
    @Override
    protected void updateItem(Word item, boolean empty) {
        super.updateItem(item, empty);

        if (empty || item == null || item.getWord() == null) {
            setText(null);
        } else {
            setText(item.getWord());
        }
    }
});

Образец заявки

добавить изображение

import javafx.application.Application;
import javafx.collections.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;

public class CellFactories extends Application {    
    @Override
    public void start(Stage stage) {
        ObservableList<Word> wordsList = FXCollections.observableArrayList();
        wordsList.add(new Word("First Word", "Definition of First Word"));
        wordsList.add(new Word("Second Word", "Definition of Second Word"));
        wordsList.add(new Word("Third Word", "Definition of Third Word"));
        ListView<Word> listViewOfWords = new ListView<>(wordsList);
        listViewOfWords.setCellFactory(param -> new ListCell<Word>() {
            @Override
            protected void updateItem(Word item, boolean empty) {
                super.updateItem(item, empty);

                if (empty || item == null || item.getWord() == null) {
                    setText(null);
                } else {
                    setText(item.getWord());
                }
            }
        });
        stage.setScene(new Scene(listViewOfWords));
        stage.show();
    }

    public static class Word {
        private final String word;
        private final String definition;

        public Word(String word, String definition) {
            this.word = word;
            this.definition = definition;
        }

        public String getWord() {
            return word;
        }

        public String getDefinition() {
            return definition;
        }
    }

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

Замечания по реализации

Хотя вы можете переопределить toString в своем классе Word, чтобы обеспечить строковое представление слова, предназначенного для представления в вашем ListView, я бы рекомендовал предоставить фабрику ячеек в ListView для извлечения данных представления из объекта word и представления его в вашем Посмотреть список. Используя этот подход, вы получаете разделение задач, поскольку вы не связываете графическое представление вашего объекта Word с его текстовым методом toString; поэтому toString может по-прежнему иметь различный вывод (например, полную информацию о полях Word с именем слова и описанием для целей отладки). Кроме того, фабрика ячеек более гибкая, поскольку вы можете применять различные графические узлы для создания визуального представления ваших данных, кроме простой текстовой строки (если вы хотите это сделать).

Кроме того, в дополнение, я рекомендую сделать ваши объекты Word неизменяемыми, удалив их установщики. Если вам действительно нужно изменить текстовые объекты сами по себе, то лучший способ справиться с этим - предоставить видимые свойства для полей объекта. Если вы также хотите, чтобы ваш пользовательский интерфейс обновлялся по мере изменения наблюдаемых свойств ваших объектов, то вам необходимо информировать ячейки списка об изменениях в связанных элементах, прислушиваясь к их изменениям (что в этом случае немного сложнее). дело). Обратите внимание, что список, содержащий слова, уже доступен для наблюдения, и ListView позаботится об обработке изменений в этом списке, но если вы изменили определение слова, например, в отображаемом объекте word, то ваше представление списка не будет воспринимать изменения в определение без соответствующей логики слушателя в фабрике ячеек ListView.

В настоящий момент ваш ListView отображает toString() в Word. Чтобы решить вашу проблему, просто добавьте следующий метод в ваш класс Word (это только пример):

@Override
public String toString(){
    return (this.word + " --- Definition: " + this.definition);
}

Я бы поспорил, что никель вызывает то, что ListView вызывает метод toString() в Word. Если вы не изменили его, он использует метод toString() по умолчанию, который просто выводит эту... не очень полезную информацию. Переопределите его, чтобы вывести красиво отформатированную строку, и вы должны быть готовы!

Другие вопросы по тегам