如何使用自定义对象在 JavaFX 中填充 ListView? [英] How can I Populate a ListView in JavaFX using Custom Objects?

查看:63
本文介绍了如何使用自定义对象在 JavaFX 中填充 ListView?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对 Java、JavaFX 和一般编程有点陌生,我遇到了一个让我脑子都坏掉的问题.

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

ObservableListwordsList = FXCollections.observableArrayList("第一字","第二字","第三字","等");列表视图<字符串>listViewOfStrings = new ListView<>(wordsList);

但我不想使用字符串.我想使用我创建的名为 Words 的自定义对象:

ObservableListwordsList = FXCollections.observableArrayList();wordsList.add(new Word("第一个单词", "第一个单词的定义");wordsList.add(new Word("第二个词", "第二个词的定义");wordsList.add(new Word("第三个词", "第三个词的定义");列表视图<字>listViewOfWords = 新的 ListView<>(wordsList);

每个 Word 对象只有 2 个属性:wordString(词的字符串)和定义(词的定义的另一个字符串).我有两者的 getter 和 setter.

你可以看到这是怎么回事 - 代码编译和工作,但是当我在我的应用程序中显示它时,它不是在 ListView 中显示每个单词的标题,而是将 Word 对象本身显示为一个字符串!

>

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

ListViewlistViewOfWords = 新的 ListView<>(wordsList);

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

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

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

解决方案

解决方案

我建议使用

import javafx.application.Application;导入 javafx.collections.*;导入 javafx.scene.Scene;导入 javafx.scene.control.*;导入 javafx.stage.Stage;公共类 CellFactories 扩展应用程序 {@覆盖公共无效开始(阶段阶段){ObservableListwordsList = FXCollections.observableArrayList();wordsList.add(new Word("第一个单词", "第一个单词的定义"));wordsList.add(new Word("第二个词", "第二个词的定义"));wordsList.add(new Word("第三个词", "第三个词的定义"));列表视图<字>listViewOfWords = 新的 ListView<>(wordsList);listViewOfWords.setCellFactory(param -> new ListCell() {@覆盖protected void updateItem(Word item, boolean empty) {super.updateItem(item, 空);if (empty || item == null || item.getWord() == null) {设置文本(空);} 别的 {setText(item.getWord());}}});stage.setScene(新场景(listViewOfWords));舞台表演();}公共静态类字 {私人最终字符串字;私有最终字符串定义;公共字(字符串字,字符串定义){this.word = 字;this.definition = 定义;}公共字符串 getWord() {回话;}公共字符串 getDefinition() {返回定义;}}公共静态无效主(字符串 [] args){发射(参数);}}

实施注意事项

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

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

I'm a bit new to Java, JavaFX, and programming in general, and I have an issue that is breaking my brain.

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:

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:

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.

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!

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?

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.

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

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.

这篇关于如何使用自定义对象在 JavaFX 中填充 ListView?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆