好吧,涉及到的所有各个节点都会有点复杂。如有可能,请随时提出简化建议。
我正在尝试用自定义对象填充JavaFX ListView,并以自定义节点格式化。
我有一个名为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;
}
Here's an example of what I'd like each contact in the ListView to show as.
此自定义节点(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个ContactCellField节点。 Here's what an individual ContactCellField should look like.
这是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>
如您所见,ContactCellField包含一个DetailField,这是我创建的另一个自定义节点,我认为该节点根本与该问题无关,因此我不再赘述,但基本上它是一个TextField与复制按钮。
这就是全部的FXML,现在用于某些Java。
这是ContactListCell的代码,它是ListCell的扩展,还包含ContactCell节点定义:
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,因为我也不认为这是问题所在。
因此,在Controller中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的格式设置为ContactListCells,我想包含ContactCell的格式。然后,联系单元应从联系对象中接收电话和电子邮件,将其信息放入ContactCellFields,然后将这些ContactCellFields(堆叠)放入适当的VBox(vbox_phones / vbox_emails)中。
经过大量的反复试验和模糊不清的StackOverflow帖子,我ALMOST取得了预期的效果。目前,该代码没有错误,并且确实使用传递给它的正确数量的Contact对象填充了LV,但是它看起来非常错误。
Here's the behavior when I run the program. It doesn't show anything until I click on the cell, and then it visualizes completely wrong.联系人姓名的标签仅显示带下划线的椭圆,电话的VBox和电子邮件的VBox似乎彼此堆叠,从而导致一堆ContactCellFields彼此堆叠。
哦,我几乎忘了提及,当我第一次运行它时,它显示的是带下划线的椭圆,然后我随机进入ListView的fxml定义,并将fixedCellSize更改为100.0,这使单元格更高并显示出堆叠的vbox 。
我错过了一些简单的事情来阻止ListView正确显示这些节点吗?我做的事情真的很愚蠢,需要重新格式化所有这些工作方式吗?任何帮助表示赞赏。
编辑/更新:我将ContactListCell中的行从获取/设置为ContactCell的子项更改为setGraphic(contactCell),现在it loads like this.距离最终目标还很近,但还不太接近。我将继续尝试,但仍然愿意接受建议,以使我走到终点,或者使事情更好/更容易长远发展。
答案 0 :(得分:-1)
我在OP中添加的更新:
编辑/更新:我将ContactListCell中的行从 获取/设置ContactCell的孩子 setGraphic(contactCell),现在像这样加载。这很 接近最终目标,但不太准确。我会继续尝试 但仍然愿意接受建议,以使我超越终点线或使 从长远来看,情况会更好/更容易发展。
解决方法是
@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 of custom nodes.有点烦人,我花时间写了整个问题描述,只是为了弄清楚自己,但是嘿,就是这样有时去。希望其他人像我在这里一样尝试在ListViews中创建更复杂的嵌套节点时,可以从中学到这一点。