简体   繁体   English

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

[英]How can I Populate a ListView in JavaFX using Custom Objects?

I'm a bit new to Java, JavaFX, and programming in general, and I have an issue that is breaking my brain.我对 Java、JavaFX 和一般编程有点陌生,但我遇到了一个让我头疼的问题。

In most of the tutorials I have looked up regarding populating a ListView (Using an ObservableArrayList, more specifically) the simplest way to do it is to make it from an ObservableList of Strings, like so:在我查阅过的关于填充 ListView 的大多数教程中(使用 ObservableArrayList,更具体地说),最简单的方法是从字符串的 ObservableList 中创建它,如下所示:

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

But I don't want to use Strings.但我不想使用字符串。 I would like to use a custom object I made called Words:我想使用我制作的名为 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);

Each Word object only has 2 properties: wordString (A string of the word), and definition (Another string that is the word's definition).每个 Word 对象只有 2 个属性:wordString(单词的字符串)和 definition(单词定义的另一个字符串)。 I have getters and setters for both.我有两者的 getter 和 setter。

You can see where this is going- the code compiles and works, but when I display it in my application, rather than displaying the titles of every word in the ListView, it displays the Word object itself as a String!您可以看到这是怎么回事——代码可以编译和工作,但是当我在我的应用程序中显示它时,它不是在 ListView 中显示每个单词的标题,而是将 Word 对象本身显示为一个字符串!

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

My question here is, specifically, is there a simple way to rewrite this:我的问题是,具体来说,是否有一种简单的方法来重写它:

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

In such a way that, rather than taking Words directly from wordsList, it accesses the wordString property in each Word of my observableArrayList?以这种方式,而不是直接从 wordsList 中获取 Words,它访问我的 observableArrayList 的每个 Word 中的 wordString 属性?

Just to be clear, this isn't for android, and the list of words will be changed, saved, and loaded eventually, so I can't just make another array to hold the wordStrings.需要说明的是,这不适用于 android,单词列表最终会被更改、保存和加载,所以我不能只创建另一个数组来保存 wordStrings。 I have done a bit of research on the web and there seems to be a thing called 'Cell Factories', but it seems unnecessarily complicated for what seems to be such a simple problem, and as I stated before, I'm a bit of a newbie when it comes to programming.我在网上做了一些研究,似乎有一个叫做“细胞工厂”的东西,但对于一个看起来如此简单的问题来说,它似乎不必要地复杂,而且正如我之前所说,我有点编程方面的新手。

Can anyone help?谁能帮忙? This is my first time here, so I'm sorry if I haven't included enough of my code or I've done something wrong.这是我第一次来这里,所以如果我没有包含足够的代码或者我做错了什么,我很抱歉。

Solution Approach解决方法

I advise using a cell factory to solve this problem.我建议使用细胞工厂来解决这个问题。

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

Sample Application示例应用程序

添加图片

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

Implementation Notes实施说明

Although you could override toString in your Word class to provide a string representation of the word aimed at representation in your ListView, I would recommend providing a cell factory in the ListView for extraction of the view data from the word object and representation of it in your ListView.尽管您可以在 Word 类中重写 toString 以提供针对 ListView 中表示的单词的字符串表示,但我建议在 ListView 中提供一个单元工厂,以便从单词对象中提取视图数据并在您的列表显示。 Using this approach you get separation of concerns as you don't tie a the graphical view of your Word object with it's textual toString method;使用这种方法,您可以分离关注点,因为您不会将 Word 对象的图形视图与其文本 toString 方法联系起来; so toString could continue to have different output (for example full information on Word fields with a word name and a description for debugging purposes).因此 toString 可以继续具有不同的输出(例如,带有单词名称和用于调试目的的描述的 Word 字段的完整信息)。 Also, a cell factory is more flexible as you can apply various graphical nodes to create a visual representation of your data beyond just a straight text string (if you wish to do that).此外,单元工厂更灵活,因为您可以应用各种图形节点来创建数据的可视化表示,而不仅仅是纯文本字符串(如果您希望这样做)。

Also, as an aside, I recommend making your Word objects immutable objects , by removing their setters.另外,顺便说一句,我建议通过删除它们的设置器来使您的 Word 对象成为不可变对象 If you really need to modify the word objects themselves, then the best way to handle that is to have exposed observable properties for the object fields.如果您确实需要修改单词对象本身,那么最好的处理方法是为对象字段公开可观察的属性。 If you also want your UI to update as the observable properties of your objects change, then you need to make your list cells aware of the changes to the associated items, by listening for changes to them (which is quite a bit more complex in this case, an extractor can help).如果您还希望您的 UI 在对象的可观察属性发生变化时更新,那么您需要通过侦听对关联项目的更改来让列表单元了解对关联项目的更改(这在此过程中要复杂得多)情况下, 提取器可以提供帮助)。 Note that, the list containing the words is already observable and ListView will take care of handling changes to that list, but if you modified the word definition for instance within a displayed word object, then your list view wouldn't pick up changes to the definition without appropriate listener logic in the ListView cell factory (or, preferably, an extractor).请注意,包含单词的列表已经是可观察的,并且 ListView 将负责处理对该列表的更改,但是如果您修改了单词定义,例如在显示的单词对象中,那么您的列表视图将不会获取对ListView 单元工厂(或者,最好是提取器)中没有适当的侦听器逻辑的定义。

What your ListView is displaying at the moment is the Word's toString(). 目前,您的ListView显示的是Word的toString()。 To fix your problem just add the following method in your Word class (this is just an example): 要解决您的问题,只需在Word类中添加以下方法(这只是一个示例):

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

I would bet you a nickel that the ListView is calling the toString() method on Word.我敢打赌,ListView 正在 Word 上调用 toString() 方法。 If you haven't overridden it, it uses the default toString() method, which just outputs that... not-so-useful information.如果您没有覆盖它,它会使用默认的 toString() 方法,该方法只是输出......不太有用的信息。 Override it to output a prettily formatted String, and you should be good to go!覆盖它以输出格式精美的字符串,您应该一切顺利!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM