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

Posted

技术标签:

【中文标题】如何使用自定义对象在 JavaFX 中填充 ListView?【英文标题】:How can I Populate a ListView in JavaFX using Custom Objects? 【发布时间】:2016-08-08 00:37:13 【问题描述】:

我对 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);

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

需要明确的是,这不适用于 android,单词列表最终会被更改、保存和加载,所以我不能只创建另一个数组来保存 wordStrings。我在网上做了一些研究,似乎有一个叫做“细胞工厂”的东西,但对于看起来如此简单的问题来说,它似乎不必要地复杂,正如我之前所说,我有点编程方面的新手。

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

【问题讨论】:

【参考方案1】:

解决方法

我建议使用cell factory 来解决这个问题。

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 中提供一个单元工厂以从单词对象中提取视图数据并表示它在你的 ListView 中。使用这种方法,您可以分离关注点,因为您不会将 Word 对象的图形视图与其文本 toString 方法联系起来;因此 toString 可以继续具有不同的输出(例如,带有单词名称和用于调试目的的描述的 Word 字段的完整信息)。此外,单元工厂更加灵活,因为您可以应用各种图形节点来创建数据的可视化表示,而不仅仅是纯文本字符串(如果您愿意的话)。

另外,顺便说一句,我建议通过删除它们的设置器来制作你的 Word 对象immutable objects。如果您确实需要修改单词对象本身,那么最好的处理方法是为对象字段公开可观察的属性。如果您还希望您的 UI 在对象的可观察属性发生变化时更新,那么您需要通过侦听对关联项目的更改来让列表单元了解对关联项目的更改(这在此过程中要复杂得多)案子)。请注意,包含单词的列表已经是可观察的,并且 ListView 将负责处理对该列表的更改,但是如果您修改了单词定义,例如在显示的单词对象中,那么您的列表视图将不会获取对ListView 单元工厂中没有适当的侦听器逻辑的定义。

【讨论】:

非常感谢您非常详细的回答!现在我将尝试覆盖 toString() 方法,但我将进一步阅读 Cell Factories 以更好地理解它们,然后根据您的建议修改应用程序以使用它们。我将只使用该编辑按钮修改每个单词的“定义”,并且列表视图仅跟踪“单词”属性,因此不需要跟踪“定义”的更改。 @DavidCollins 您最终是否使用单元工厂实现了解决方案?如果您发布它,我将不胜感激 @droptop 这个答案已经包含使用单元工厂的完整解决方案的代码。 object!=null 为我工作,谢谢。【参考方案2】:

我敢打赌,ListView 正在调用 Word 上的 toString() 方法。如果您没有覆盖它,它会使用默认的 toString() 方法,该方法只是输出......不太有用的信息。覆盖它以输出一个格式漂亮的字符串,你应该很高兴!

【讨论】:

嗯,有道理!我会试一试;我现在有一节课要上课,但我稍后会试一试,看看是否有效。 但这不是一个很好的解决方案:您可能希望 toString() 方法返回与您希望在 UI 中显示的内容不同的内容。

以上是关于如何使用自定义对象在 JavaFX 中填充 ListView?的主要内容,如果未能解决你的问题,请参考以下文章

JavaFX自定义窗口标题栏

JavaFX SceneBuilder:如何让textarea填充窗格?

从文本字段中提取空数据。表已填充,但无法检索对象。 Java/JavaFX

在JavaFX中仅显示填充了图像的百分比

在 JavaFX 中管理 ComboBox 项

在JavaFX中添加自定义组件