当前位置: 首页 > 知识库问答 >
问题:

ListCell中的JavaFX自定义节点未正确加载

长孙哲
2023-03-14

好吧,这变得有点复杂,涉及到所有不同的节点。如有可能,请随时提供简化建议。

我试图用自定义对象填充JavaFXListView,并用自定义节点格式化。

我有一个名为Contact的自定义对象,它的定义如下:

public class Contact {
    private int id;
    private int clientID;
    private String firstName;
    private String middleInitial;
    private String lastName;
    private Date birthdate;
    private String ssn;
    private Address address;
    private List<Phone> phones;
    private List<Email> emails;
    private int listPosition;
    private ContactTitle title;
}

下面是我希望ListView中的每个联系人显示为的示例。

此自定义节点(ContactCell)由一个VBox根节点(顶部有一个用于联系人姓名的标签)和一个用于堆叠两部电话的VBox以及一个用于堆叠两封电子邮件的VBox组成。手机和电子邮件通过ContactCellField节点放入VBox(我将在下面进一步解释)

以下是ContactCell的fxml:

<fx:root type="VBox" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <Separator prefWidth="200.0" />
      <Label fx:id="lbl_printName" underline="true">
         <VBox.margin>
            <Insets left="10.0" />
         </VBox.margin>
      </Label>
      <VBox fx:id="vbox_phones" />
      <VBox fx:id="vbox_emails" />
   </children>
</fx:root>

因此,vbox_phones应该以编程方式填充0-2个ConrectCellField节点。以下是单独的ConrectCellField应该是什么样子。

以下是ContactCellField的fxml:

<fx:root type="Pane" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <VBox>
         <children>
            <Label fx:id="lbl_fieldLabel" text="Mobile" textFill="#616161" underline="true">
               <font>
                  <Font size="8.0" />
               </font>
               <padding>
                  <Insets left="3.0" />
               </padding>
            </Label>
            <DetailField fx:id="df_fieldContent" text="(817)-555-5555" />
         </children>
      </VBox>
   </children>
</fx:root>

正如你所看到的ConrectCellField包含一个DetailField,这是我制作的另一个自定义节点,我不认为这个节点与这个问题有关,所以我不会深入讨论,但基本上它是一个带有复制按钮的文本字段。

这就是所有的FXML,现在是一些java。

以下是ConrectListCell的代码,它是ListCell的扩展,也包含ConrectCell节点定义:

public class ContactListCell extends ListCell<Contact> {
    private ContactCell contactCell;

    public ContactListCell() {
        contactCell = new ContactCell();
    }

    @Override
    protected void updateItem(Contact contact, boolean empty) {
        super.updateItem(contact, empty);
        if (contact != null) {
            contactCell.updateContact(contact);
            getChildren().setAll(contactCell);
        }
    }


    /**
     * the ContactListCell basically just takes the contact it gets
     * and visualizes it using the ContactCell node
     */
    private class ContactCell extends VBox {
        @FXML Label lbl_printName;
        @FXML VBox vbox_phones;
        @FXML VBox vbox_emails;

        public ContactCell() {
            super();
            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("contactcell.fxml"));
            fxmlLoader.setRoot(this);
            fxmlLoader.setController(this);
            fxmlLoader.setClassLoader(getClass().getClassLoader());

            try {
                fxmlLoader.load();
            } catch (IOException exception) {
                throw new RuntimeException(exception);
            }
        }

        public void updateContact(Contact contact) {
            String printName = String.join(" ", contact.getFirstName(), contact.getLastName());
            lbl_printName.setText(printName);

            // Takes a list of phone objects and turns them into a list of ContactCellField objects
            // Then fills the phones list box with those objects
            List<ContactCellField> phones = contact.getPhones().stream().map(p -> new ContactCellField(p.getName(), p.getNumber())).collect(Collectors.toList());
            vbox_phones.getChildren().setAll(phones);
            // Same thing that phones do but for emails
            List<ContactCellField> emails = contact.getEmails().stream().map(e -> new ContactCellField(e.getName(), e.getAddress())).collect(Collectors.toList());
            vbox_emails.getChildren().setAll(emails);
        }
    }
}

除非有人要求,否则我将省略ContactCellField的java,因为我认为这也不是问题所在。

因此,在ListView所在场景的控制器的其他地方,我就是这样填充LV并设置单元工厂的:

public void initContactsLV(List<Contact> contacts) {
        // sorts the contacts by their list position and then passes that ordered list through to the list view
        contacts.sort(Comparator.comparing(Contact::getListPosition));
        ObservableList<Contact> contactList = FXCollections.observableArrayList(contacts);
        lv_contacts.setPlaceholder(new Label("No contacts found."));
        lv_contacts.setCellFactory(lv -> new ContactListCell());
        lv_contacts.setItems(contactList);
    }

因此,细胞工厂的目标是将LV的格式设置为接触列表细胞,我想包含接触细胞的格式。然后,联系人单元应该接收来自联系人对象的电话和电子邮件,将其信息放入联系人字段,然后将这些联系人字段(堆叠)放入适当的VBox(vbox_phones/vbox_emails)。

经过大量的反复试验和模糊的StackOverflow帖子,我几乎达到了预期的效果。此时此刻,代码是无错误的,并且用传递给它的正确数量的联系人对象填充LV,但是它的可视化非常错误。

这是我运行程序时的行为。直到我点击单元格,它才显示任何内容,然后它的可视化完全错误。联系人名称的标签只显示一个带下划线的省略号,手机的VBox和电子邮件的VBox似乎堆叠在一起,导致一堆联系人字段堆叠在一起。

哦,我差点忘了提到,当我第一次运行它时,它显示的是带下划线的省略号,然后我随机转到ListView的fxml定义,将fixedCellSize更改为100.0,这使单元格更高,并显示了堆叠的VBox。

我是否缺少一些简单的东西,阻止ListView正确显示这些节点?我是不是做了一些非常愚蠢的事情,需要重新设计这一切的运作方式?谢谢你的帮助。

编辑/更新:我更改了ConrectListCell中的行,从获取/设置子元素到ConrectCell到setGrapic(接触细胞),现在它像这样加载。这离最终目标更近了,但还不完全是。我会继续尝试,但仍然愿意接受建议,让我越过终点线,或者从长远来看让事情变得更好/更容易发展。

共有1个答案

史昊焱
2023-03-14

更新我放在OP:

编辑/更新:我更改了ConrectListCell中的行,从获取/设置子元素到ConrectCell到setGrapic(接触细胞),现在它像这样加载。这离最终目标更近了,但还不完全是。我会继续尝试,但仍然愿意接受建议,让我越过终点线,或者从长远来看让事情变得更好/更容易发展。

以下是修复方法:

@Override
protected void updateItem(Contact contact, boolean empty) {
    super.updateItem(contact, empty);
    if (contact != null) {
        contactCell.updateContact(contact);
        setGraphic(contactCell);
    } else {
        setGraphic(null);
    }
}

完全从ListView中删除固定单元格大小首选项会导致自定义节点的ListView工作。有点恼人,我花时间写了整个问题描述,只是为了自己弄明白,但嘿,有时候就是这样。希望其他人在尝试像我在这里做的那样在ListView中创建更复杂的嵌套节点时可以从中学习。

 类似资料:
  • 我试图找出如何使用一个与一个在,但没有运气,我已经查了,我能找到的都是不完整的教程和问题。下面是我的FXML 还有我的模特 和我不完整的 还有我的控制器 请不要告诉我找不到。我现在迷路了,我不知道在我的FXML中更新图像视图和标签的正确方法。任何人来帮助或与一个教程的链接,我可以遵循,我将不胜感激。

  • 我正在尝试为JavaFX创建一个非常简单的自定义。运行应用程序时,

  • 问题内容: 我尝试使用自定义ListCell在ListView中查看自定义对象。为了说明这个问题,我选择了一个例子。同样出于演示目的,我在渲染时直接禁用了ListCell。这些项目是由线程模拟的外部过程添加的。一切看起来都很不错,直到我为禁用的ListCell应用CSS着色为止。现在看来,有些虚幻项与创建它们的ListCell一起被禁用。 我该如何解决? App.java app.css 问题答案

  • 问题的核心是,我不能刷新或更改一个场景的节点的内容(这里是TablesMain)从另一个类(这里是NamePriceCell)。 我正在使用主StackPane(TableMainController扩展StackPane)构建和应用程序,其中包含其他节点,其中一些节点是ListView。在一个特定的ListView(比如“readitemslistview”)中,我创建了一个自定义ListCel

  • 我正在开发一个应用程序,有必要将节点布置在彼此之外(或彼此顶部等)。但是,此布局只是初始布局,用户可以任意移动这些节点。在JavaFX中,如何以正确的方式实现这一点?我将用一个简单的例子来解释我的问题: 假设我有两个矩形,想把矩形2放在矩形1的右边。 在这种情况下,JavaFX还没有确定rect1的宽度,它将为零。直观地说,我会执行一个调用,让JavaFX绘制rect1,从而确定其宽度,然后添加r

  • 问题内容: 单击按钮后,它会更改其位置。 但是当我移动鼠标时,按钮又回到场景的中心,为什么呢? 我有以下代码: 问题答案: 建议的方法 对于您的特定代码段,也许使用Pane而不是StackPane是最好的方法。 为什么您的代码无法满足您的要求 如果要手动设置布局值,请不要使用会自动为您设置布局值的布局窗格(例如StackPane)。如果使用布局窗格,则下次布局窗格执行布局传递时(例如,调整其大小或