virtualList

virtualList эффективно отображает списки с настраиваемым содержимым элементов, отображая только видимые элементы.

XML-элемент

virtualList

Java-класс

JmixVirtualList

Основы

virtualList подключен к контейнер данных. По умолчанию отображается имя экземпляра сущности в контейнере. Используя обработчик renderer, вы можете отображать пользовательский контент.

Альтернативно можно рендерить элементы с помощью вложенного элемента fragmentRenderer. Подробнее смотрите раздел Рендерер фрагментов.

Ниже приведен пример использования virtualList в списке вместо dataGrid:

XML
<data readOnly="true">
    <collection id="customersDc"
                class="io.jmix.uisamples.entity.Customer">
        <fetchPlan extends="_base"/>
        <loader id="customersDl">
            <query>
                <![CDATA[select e from Customer e]]>
            </query>
        </loader>
    </collection>
</data>
<facets>
    <dataLoadCoordinator auto="true"/>
</facets>
    <virtualList id="virtualList" width="30em"
                 itemsContainer="customersDc"
                 alignSelf="CENTER"/>
Java
@Autowired
protected UiComponents uiComponents;
@Autowired
protected MetadataTools metadataTools;

@Supply(to = "virtualList", subject = "renderer")
protected Renderer<Customer> virtualListRenderer() {
    return new ComponentRenderer<>(customer -> {
        VerticalLayout infoLayout = createVerticalLayout();
        infoLayout.addClassNames("info-layout");

        H4 customerName = new H4(customer.getInstanceName());
        Span gradeSpan = createGradeSpan(customer.getGrade());
        infoLayout.add(customerName, gradeSpan);

        HorizontalLayout infoLine = createHorizontalLayout();
        infoLine.setAlignItems(FlexComponent.Alignment.CENTER);

        H5 emailLabel = new H5("Email:");
        Span email = new Span(customer.getEmail());
        infoLine.add(emailLabel, email);

        HorizontalLayout infoLine2 = createHorizontalLayout();

        H5 ageLabel = new H5("Age:");
        Span age = new Span(String.valueOf(customer.getAge()));

        infoLine2.add(ageLabel, age);

        VerticalLayout additionalInfoLayout = createVerticalLayout();
        additionalInfoLayout.add(infoLine, infoLine2);

        JmixDetails infoDetails = uiComponents.create(JmixDetails.class);
        infoDetails.setSummaryText("Additional information");
        infoDetails.add(additionalInfoLayout);

        infoLayout.add(infoDetails, new Hr());
        return infoLayout;
    });
}

protected VerticalLayout createVerticalLayout() {
    VerticalLayout layout = uiComponents.create(VerticalLayout.class);
    layout.setSpacing(false);
    layout.setPadding(false);
    return layout;
}

protected HorizontalLayout createHorizontalLayout() {
    HorizontalLayout layout = uiComponents.create(HorizontalLayout.class);
    layout.setPadding(false);
    return layout;
}

protected Span createGradeSpan(@Nullable CustomerGrade grade) {
    Span gradeSpan = new Span(metadataTools.format(grade));

    if (grade != null) {
        ThemeList gradeThemeList = gradeSpan.getElement().getThemeList();

        switch (grade) {
            case STANDARD -> gradeThemeList.add("badge contrast");
            case PREMIUM -> gradeThemeList.add("badge primary");
            default -> gradeThemeList.add("badge");
        }
    }

    return gradeSpan;
}

Обратите внимание, что элементы в virtualList нельзя выделять или перемещаться по ним с помощью клавиатуры. Стандартные действия списка не работают с virtualList. При необходимости определите собственные действия для CRUD-операций.

Пользовательский рендерер предметов

Компонентный рендерер может создавать пользовательское содержимое для каждого элемента списка. В примере каждое значение перечисления отображается с подписью и кнопкой-значком.

XML
<virtualList id="virtualList" width="15em"
             itemsEnum="io.jmix.uisamples.entity.Day"
             alignSelf="CENTER"/>
Java
@Autowired
protected UiComponents uiComponents;
@Autowired
protected MetadataTools metadataTools;
@Autowired
protected Notifications notifications;

@Supply(to = "virtualList", subject = "renderer")
protected Renderer<Day> virtualListRenderer() {
    return new ComponentRenderer<>(this::createDayRenderer);
}

protected HorizontalLayout createDayRenderer(Day day) {
    HorizontalLayout layout = uiComponents.create(HorizontalLayout.class);
    layout.setPadding(false);

    String dayValue = metadataTools.format(day);
    H3 label = new H3(dayValue);

    JmixButton button = uiComponents.create(JmixButton.class);
    button.addThemeVariants(ButtonVariant.LUMO_ICON);
    button.addClassName(StyleUtility.Button.LINK_BUTTON);
    button.addClickListener(__ -> showNotification(dayValue));

    Icon icon = switch (day) {
        case MONDAY -> VaadinIcon.BRIEFCASE.create();
        case TUESDAY -> VaadinIcon.LINE_CHART.create();
        case WEDNESDAY -> VaadinIcon.TROPHY.create();
        case THURSDAY -> VaadinIcon.GROUP.create();
        case FRIDAY -> VaadinIcon.CASH.create();
        case SATURDAY -> VaadinIcon.GLASS.create();
        case SUNDAY -> VaadinIcon.BED.create();
    };
    button.setIcon(icon);

    layout.add(button, label);
    return layout;
}

protected void showNotification(String dayValue) {
    String message = String.format("You've clicked on %s!", StringUtils.capitalize(dayValue));
    notifications.show(message);
}

Встроенный редактор

Компонентный рендерер, контейнер данных и диалоговые окна деталей позволяют создавать и редактировать элементы списка без отдельной таблицы.

XML
<data>
    <collection id="foodDc" class="io.jmix.uisamples.entity.Food">
        <loader id="foodDl"/>
    </collection>
</data>
    <hbox width="100%" padding="false" alignItems="CENTER">
        <h2 id="menusItemsTabTitle" text="msg://foodListTitle"/>
        <button id="addBtn"
                classNames="virtual-list-inline-editor-add-button"
                themeNames="primary"
                icon="PLUS"
                text="msg:///actions.Add"/>
    </hbox>
    <html id="foodDescription" content="msg://foodListDescription"/>
    <virtualList id="foodList" itemsContainer="foodDc" width="100%"/>
Java
@Autowired
private Messages messages;
@Autowired
private DialogWindows dialogWindows;
@ViewComponent
private DataContext dataContext;
@ViewComponent
private CollectionContainer<Food> foodDc;
@Supply(to = "foodList", subject = "renderer")
public Renderer<Food> foodListRenderer() {
    return new ComponentRenderer<>(item -> {
        HorizontalLayout rootCardLayout = new HorizontalLayout();
        rootCardLayout.setMargin(true);

        VerticalLayout infoLayout = new VerticalLayout();
        infoLayout.setSpacing(false);
        infoLayout.setPadding(false);
        infoLayout.setWidth("30%");

        Avatar avatar = new Avatar();
        avatar.addThemeVariants(AvatarVariant.XLARGE);

        if (item.getIcon() != null && item.getIcon().length > 0) {
            String iconFileName = "%s.png".formatted(item.getTitle());
            InputStreamDownloadHandler handler = DownloadHandler.fromInputStream(event -> {
                byte[] icon = item.getIcon();
                ByteArrayInputStream inputStream = new ByteArrayInputStream(item.getIcon());

                return new DownloadResponse(inputStream, iconFileName, "image/png", icon.length);
            });

            avatar.setImageHandler(handler);
        }

        VerticalLayout verticalLayout = new VerticalLayout();
        verticalLayout.setWidthFull();

        HorizontalLayout itemDetailLayout = new HorizontalLayout();
        itemDetailLayout.add(new Text(item.getDescription()));
        itemDetailLayout.add(new Html(
                messages.formatMessage(getClass(), "foodListItemDescription", item.getPrice()))
        );
        itemDetailLayout.setPadding(false);
        itemDetailLayout.setAlignItems(FlexComponent.Alignment.CENTER);

        infoLayout.add(new Html(messages.formatMessage(getClass(), "foodItemTitle", item.getTitle())));
        infoLayout.add(itemDetailLayout);

        VerticalLayout buttonsPanel = new VerticalLayout();
        buttonsPanel.setWidth("AUTO");
        buttonsPanel.setPadding(false);
        buttonsPanel.setSpacing(false);

        Button detailButton = new Button(new Icon(VaadinIcon.PENCIL));
        detailButton.setText(messages.getMessage("actions.Edit"));
        detailButton.addClickListener(e -> dialogWindows.detail(this, Food.class)
                .withViewClass(FoodDetailView.class)
                .editEntity(item)
                .withAfterCloseListener(closeEvent -> {
                    if (closeEvent.closedWith(StandardOutcome.SAVE)) {
                        foodDc.replaceItem(closeEvent.getSource().getView().getEditedEntity());
                    }
                })
                .open());
        detailButton.addClassName(StyleUtility.Button.LINK_BUTTON);

        Button removeButton = new Button(new Icon(VaadinIcon.TRASH));
        removeButton.setText(messages.getMessage("actions.Remove"));
        removeButton.addThemeVariants(ButtonVariant.ERROR);
        removeButton.addClassName(StyleUtility.Button.LINK_BUTTON);
        removeButton.addClickListener(e -> {
            foodDc.getMutableItems().remove(item);
            dataContext.remove(dataContext.merge(item));
        });

        buttonsPanel.add(detailButton, removeButton);
        rootCardLayout.add(avatar, infoLayout, buttonsPanel);
        return rootCardLayout;
    });
}
@Subscribe(id = "addBtn", subject = "clickListener")
public void onAddBtnClick(final ClickEvent<JmixButton> event) {
    dialogWindows.detail(this, Food.class)
            .withViewClass(FoodDetailView.class)
            .newEntity()
            .withAfterCloseListener(closeEvent -> {
                if (closeEvent.closedWith(StandardOutcome.SAVE)) {
                    foodDc.replaceItem(closeEvent.getSource().getView().getEditedEntity());
                }
            })
            .open();
}

Атрибуты

virtualList имеет следующие уникальные атрибуты:

Имя Описание По умолчанию

itemsContainer

Задаёт имя контейнера данных со списком элементов.

itemsEnum

Определяет класс enumeration для создания списка элементов.

virtualList имеет следующие общие атрибуты:

Обработчики

virtualList имеет следующие уникальные обработчики:

Имя Описание

Renderer

Пользовательский рендерер элементов списка можно установить только в контроллере экрана с помощью Java-кода.

ValueProvider

Если вы хотите отображать простой текст, можно указать средство визуализации с помощью ValueProvider.

virtualList имеет следующие общие обработчики:

Смотрите также