virtualList
virtualList эффективно отображает списки с настраиваемым содержимым элементов, отображая только видимые элементы.
XML-элемент |
|
|---|---|
Java-класс |
|
Основы
virtualList подключен к контейнер данных. По умолчанию отображается имя экземпляра сущности в контейнере. Используя обработчик renderer, вы можете отображать пользовательский контент.
Альтернативно можно рендерить элементы с помощью вложенного элемента fragmentRenderer. Подробнее смотрите раздел Рендерер фрагментов.
Ниже приведен пример использования virtualList в списке вместо dataGrid:
<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"/>
@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-операций.
Пользовательский рендерер предметов
Компонентный рендерер может создавать пользовательское содержимое для каждого элемента списка. В примере каждое значение перечисления отображается с подписью и кнопкой-значком.
<virtualList id="virtualList" width="15em"
itemsEnum="io.jmix.uisamples.entity.Day"
alignSelf="CENTER"/>
@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);
}
Встроенный редактор
Компонентный рендерер, контейнер данных и диалоговые окна деталей позволяют создавать и редактировать элементы списка без отдельной таблицы.
<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%"/>
@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 имеет следующие уникальные атрибуты:
| Имя | Описание | По умолчанию |
|---|---|---|
Задаёт имя контейнера данных со списком элементов. |
— |
|
Определяет класс enumeration для создания списка элементов. |
— |
virtualList имеет следующие общие атрибуты:
Обработчики
virtualList имеет следующие уникальные обработчики:
| Имя | Описание |
|---|---|
Пользовательский рендерер элементов списка можно установить только в контроллере экрана с помощью Java-кода. |
|
Если вы хотите отображать простой текст, можно указать средство визуализации с помощью |
virtualList имеет следующие общие обработчики: