web-dev-qa-db-ja.com

カスタムオブジェクトを使用してJavaFXでListViewを作成するにはどうすればよいですか?

私は、Java、JavaFX、およびプログラミング全般に少し慣れていないため、頭を痛めている問題があります。

ListViewの作成(具体的にはObservableArrayListの使用)に関するほとんどのチュートリアルで、最も簡単な方法は、次のように文字列のObservableListから作成することです。

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オブジェクトには、wordString(Wordの文字列)とdefinition(Wordの定義である別の文字列)の2つのプロパティのみがあります。両方のゲッターとセッターがあります。

コードのコンパイルと動作を確認できますが、ListViewですべてのWordのタイトルを表示するのではなく、アプリケーションで表示すると、Wordオブジェクト自体が文字列として表示されます。

私のアプリケーションとそのListViewを示す画像

ここでの私の質問は、具体的には、これを書き換える簡単な方法があるかどうかです。

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

このようにして、wordsListからWordsを直接取得するのではなく、observableArrayListの各WordのwordStringプロパティにアクセスします。

明確にするために、これはAndroid向けではなく、単語のリストは最終的に変更、保存、およびロードされるため、wordStringsを保持する別の配列を作成することはできません。私はウェブ上で少し研究しましたが、「セルファクトリー」と呼ばれるものがあるようですが、そのような単純な問題と思われるもののために不必要に複雑に思われます。プログラミングに関しては初心者です。

誰でも助けることができますか?ここが初めてなので、コードが十分に含まれていないか、何か間違ったことをしてしまった場合は申し訳ありません。

23
David Collins

ソリューションアプローチ

セルファクトリ を使用してこの問題を解決することをお勧めします。

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());
        }
    }
});

サンプルアプリケーション

add image

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);
    }
}

実装上の注意

WordクラスのtoStringをオーバーライドして、ListViewでの表現を目的としたWordの文字列表現を提供することもできますが、Wordオブジェクトからのビューデータの抽出とその表現でのセルファクトリをListViewで提供することをお勧めしますリストビュー。このアプローチを使用すると、WordオブジェクトのグラフィカルビューをテキストのtoStringメソッドと結び付けないため、懸念事項が分離されます。そのため、toStringは引き続き異なる出力を持つ可能性があります(たとえば、Wordの名前とデバッグ用の説明を含むWordフィールドの完全な情報)。また、セルファクトリは、さまざまなグラフィカルノードを適用して、単なるテキスト文字列を超えてデータの視覚的表現を作成できるため、より柔軟です(必要な場合)。

また、余談ですが、セッターを削除して、Wordオブジェクト 不変オブジェクト を作成することをお勧めします。 Wordオブジェクト自体を本当に変更する必要がある場合、それを処理する最良の方法は、オブジェクトフィールドの監視可能なプロパティを公開することです。オブジェクトの監視可能なプロパティの変更に合わせてUIも更新する場合は、変更をリッスンすることにより、関連するアイテムの変更をリストセルに認識させる必要があります(これはかなり複雑です)場合)。単語を含むリストは既に観察可能であり、ListViewはそのリストへの変更を処理しますが、表示されたWordオブジェクト内でインスタンスのWord定義を変更した場合、リストビューはListViewセルファクトリ内の適切なリスナーロジックのない定義。

31
jewelsea

現在ListViewが表示しているのは、WordのtoString()です。問題を解決するには、Wordクラスに次のメソッドを追加します(これは単なる例です)。

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

ListViewがWordのtoString()メソッドを呼び出していることは間違いありません。オーバーライドしていない場合は、デフォルトのtoString()メソッドを使用しますが、このメソッドはあまり有用ではない情報を出力します。それをオーバーライドして、きれいにフォーマットされた文字列を出力します。

2
GaiusOctavian