Пользовательские узлы JavaFX в ListCell <Object>загружаются неправильно - PullRequest
0 голосов
/ 25 июня 2019

Хорошо, это становится немного сложнее со всеми задействованными узлами.Не стесняйтесь давать советы по упрощению, если это возможно.

Я пытаюсь заполнить 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;
}

Вот пример того, что я хотел бы, чтобы каждый контакт в ListView отображался как.

Этот пользовательский узел (ContactCell) состоит из корня VBox с меткой вверху для имени контакта, а затем VBox для стекирования нескольких телефонов и VBox для стекирования пары.писемТелефоны и электронные письма помещаются в VBox через узлы ContactCellField (я объясню это чуть ниже.)

Вот fxml для ContactCell:

<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. Вот как должен выглядеть отдельный ContactCellField.

Вот текстовый файл для ContactCellField:

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

Я опущу java ContactCellField, если не будет запрошено, потому что я тоже не думаю, что в этом проблема.

Итак, в другом месте контроллера для сцены, в которой живет 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 я ПОЧТИ добился желаемого эффекта.В этот момент код не содержит ошибок и заполняет LV правильным количеством объектов Contact, переданных ему, но он визуализируется очень неправильно.

Вот поведение при запуске программы.Он ничего не показывает, пока я не нажму на ячейку, а затем он будет отображаться совершенно неправильно. Метка для имени контакта показывает только подчеркнутые эллипсы, а VBox для телефонов и VBox для электронной почты, кажется, сложены сверхудруг друга, в результате чего куча ContactCellFields складываются друг на друга.

О, я почти забыл упомянуть, когда я впервые запустил все, что показывало, что это были подчеркнутые эллипсы, затем я случайно перешел к определению fxml ListView и изменил fixedCellSize на 100.0, это сделало ячейку выше и открылосложенные vboxes.

Мне не хватает какой-то простой вещи, которая мешает ListView правильно отображать эти узлы?Я сделал что-то действительно глупое и нужно переформатировать, как все это работает?Любая помощь приветствуется.

РЕДАКТИРОВАНИЕ / ОБНОВЛЕНИЕ: я изменил строку в ContactListCell с получения / установки дочерних элементов для ContactCell на setGraphic (contactCell), и теперь загружается следующим образом. Это НАМНОГОближе к конечной цели, но не совсем.Я буду продолжать пытаться что-то делать, но все же буду открыта для советов, которые помогут мне преодолеть финишную черту или сделать вещи лучше / легче развиваться в конечном итоге.

1 Ответ

0 голосов
/ 25 июня 2019

Обновление, которое я вставил в 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 пользовательских узлов. Немного раздражает, что я потратил время на написание всего этого описания проблемы, чтобы самому разобраться в этом. , но эй, вот так иногда. Надеюсь, кто-то еще может извлечь из этого уроки, когда они пытаются создать более сложные вложенные узлы в ListViews, как я делал здесь.

...