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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36657299/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 18:32:47  来源:igfitidea点击:

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

javalistviewjavafx

提问by David Collins

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). I have getters and setters for both.

每个 Word 对象只有 2 个属性:wordString(单词的字符串)和定义(单词的定义的另一个字符串)。我对两者都有 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 对象本身显示为一个字符串!

Image showing my application and its ListView

显示我的应用程序及其 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. 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.

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

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.

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

采纳答案by jewelsea

Solution Approach

解决方法

I advise using a cell factoryto 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

示例应用程序

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

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

尽管您可以在 Word 类中覆盖 toString 以提供旨在在 ListView 中表示的单词的字符串表示,但我建议在 ListView 中提供一个单元工厂,用于从单词对象中提取视图数据并在您的列表显示。使用这种方法,您可以分离关注点,因为您不会将 Word 对象的图形视图与其文本 toString 方法联系起来;所以 toString 可以继续有不同的输出(例如,带有单词名称和用于调试目的的描述的 Word 字段的完整信息)。此外,细胞工厂更加灵活,因为您可以应用各种图形节点来创建数据的可视化表示,而不仅仅是直接的文本字符串(如果您希望这样做)。

Also, as an aside, I recommend making your Word objects immutable objects, by removing their setters. 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). 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.

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

回答by GaiusOctavian

I would bet you a nickel that the ListView is calling the toString() method on Word. If you haven't overridden it, it uses the default toString() method, which just outputs that... not-so-useful information. Override it to output a prettily formatted String, and you should be good to go!

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

回答by Andrei-Marius Longhin

What your ListView is displaying at the moment is the Word's toString(). To fix your problem just add the following method in your Word class (this is just an example):

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

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