[英]How can I Populate a ListView in JavaFX using Custom Objects?
我对 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 对象只有 2 个属性:wordString(单词的字符串)和 definition(单词定义的另一个字符串)。 我有两者的 getter 和 setter。
您可以看到这是怎么回事——代码可以编译和工作,但是当我在我的应用程序中显示它时,它不是在 ListView 中显示每个单词的标题,而是将 Word 对象本身显示为一个字符串!
我的问题是,具体来说,是否有一种简单的方法来重写它:
ListView<Word> listViewOfWords = new ListView<>(wordsList);
以这种方式,而不是直接从 wordsList 中获取 Words,它访问我的 observableArrayList 的每个 Word 中的 wordString 属性?
需要说明的是,这不适用于 android,单词列表最终会被更改、保存和加载,所以我不能只创建另一个数组来保存 wordStrings。 我在网上做了一些研究,似乎有一个叫做“细胞工厂”的东西,但对于一个看起来如此简单的问题来说,它似乎不必要地复杂,而且正如我之前所说,我有点编程方面的新手。
谁能帮忙? 这是我第一次来这里,所以如果我没有包含足够的代码或者我做错了什么,我很抱歉。
解决方法
我建议使用细胞工厂来解决这个问题。
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);
}
}
实施说明
尽管您可以在 Word 类中重写 toString 以提供针对 ListView 中表示的单词的字符串表示,但我建议在 ListView 中提供一个单元工厂,以便从单词对象中提取视图数据并在您的列表显示。 使用这种方法,您可以分离关注点,因为您不会将 Word 对象的图形视图与其文本 toString 方法联系起来; 因此 toString 可以继续具有不同的输出(例如,带有单词名称和用于调试目的的描述的 Word 字段的完整信息)。 此外,单元工厂更灵活,因为您可以应用各种图形节点来创建数据的可视化表示,而不仅仅是纯文本字符串(如果您希望这样做)。
另外,顺便说一句,我建议通过删除它们的设置器来使您的 Word 对象成为不可变对象。 如果您确实需要修改单词对象本身,那么最好的处理方法是为对象字段公开可观察的属性。 如果您还希望您的 UI 在对象的可观察属性发生变化时更新,那么您需要通过侦听对关联项目的更改来让列表单元了解对关联项目的更改(这在此过程中要复杂得多)情况下, 提取器可以提供帮助)。 请注意,包含单词的列表已经是可观察的,并且 ListView 将负责处理对该列表的更改,但是如果您修改了单词定义,例如在显示的单词对象中,那么您的列表视图将不会获取对ListView 单元工厂(或者,最好是提取器)中没有适当的侦听器逻辑的定义。
目前,您的ListView显示的是Word的toString()。 要解决您的问题,只需在Word类中添加以下方法(这只是一个示例):
@Override
public String toString(){
return (this.word + " --- Definition: " + this.definition);
}
我敢打赌,ListView 正在 Word 上调用 toString() 方法。 如果您没有覆盖它,它会使用默认的 toString() 方法,该方法只是输出......不太有用的信息。 覆盖它以输出格式精美的字符串,您应该一切顺利!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.