如何使用自定义对象在JavaFX中填充ListView?

我对Java,JavaFX和一般的编程有点新意,我有一个问题正在打破我的大脑。

在大多数关于填充ListView的教程中(更具体地说,使用ObservableArrayList),最简单的方法是从字符串的ObservableList中创建它,如下所示:

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

但我不想使用字符串。 我想使用一个名为Words的自定义对象:

 ObservableList 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 listViewOfWords = new ListView(wordsList); 

每个Word对象只有2个属性:wordString(单词的一个字符串)和definition(另一个字符串,即单词的定义)。 我有两个吸气剂和二传手。

您可以看到它的位置 – 代码编译和工作,但是当我在应用程序中显示它时,它不会显示ListView中每个单词的标题,而是将Word对象本身显示为字符串!

该图显示了我的应用程序及其ListView

我的问题是,具体来说,有一种简单的方法可以重写这个:

 ListView listViewOfWords = new ListView(wordsList); 

以这种方式,它不是直接从wordsList中获取Words,而是访问我的observableArrayList的每个Word中的wordString属性?

为了清楚起见,这不适用于android,并且最终将更改,保存和加载单词列表,因此我不能只创建另一个数组来保存wordStrings。 我在网上做了一些研究,似乎有一个名为’Cell Factories’的东西,但对于看似如此简单的问题似乎不必要的复杂,正如我之前所说,我有点编程方面的新手。

有人可以帮忙吗? 这是我第一次来这里,所以如果我没有包含足够的代码或者我做错了,我很抱歉。

解决方法

我建议使用细胞工厂来解决这个问题。

 listViewOfWords.setCellFactory(param -> new ListCell() { @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 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 listViewOfWords = new ListView<>(wordsList); listViewOfWords.setCellFactory(param -> new ListCell() { @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中的表示的单词的字符串表示,但我建议在ListView中提供单元工厂,以便从单词对象中提取视图数据并在其中表示它列表显示。 使用这种方法可以分离关注点,因为您没有将Word对象的图形视图与其文本的toString方法联系起来; 所以toString可以继续有不同的输出(例如Word字段的完整信息,带有单词名称和描述用于调试目的)。 此外,单元工厂更灵活,因为您可以应用各种图形节点来创建数据的可视化表示,而不仅仅是直接的文本字符串(如果您希望这样做)。

另外,另外,我建议通过删除它们的setter来使Word对象成为不可变对象 。 如果您确实需要自己修改单词对象,那么处理它的最佳方法是为对象字段提供公开的可观察属性。 如果您还希望在对象的可观察属性发生更改时更新UI,则需要通过侦听对它们的更改来使列表单元格知道相关项目的更改(这在此更复杂)案件)。 请注意,包含单词的列表已经可以观察,ListView将负责处理对该列表的更改,但是如果您在显示的单词对象中修改了单词定义,那么您的列表视图将不会获取对该列表的更改。 ListView单元工厂中没有适当的侦听器逻辑的定义。

我敢打赌你是ListView在Word上调用toString()方法的镍。 如果你没有覆盖它,它使用默认的toString()方法,它只输出那些……不太有用的信息。 覆盖它以输出一个经过精心设计的String,你应该好好去!

你的ListView目前显示的是Word的toString()。 要解决您的问题,只需在Word类中添加以下方法(这只是一个示例):

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