gridLayout
gridLayout располагает компоненты или элементы данных в адаптивной двумерной CSS-сетке.
XML-элемент |
|
|---|---|
Java-класс |
|
Основы
Добавьте дочерние компоненты декларативно и используйте gap и columnMinWidth, чтобы настроить интервалы и адаптивную ширину столбцов. Оба атрибута принимают CSS-величины длины и переменные.
<gridLayout id="gridLayout" gap="var(--lumo-space-m)" columnMinWidth="18em" width="100%" height="100%">
<image width="100%" height="100%" resource="icons/jmix-icon.png"/>
<image width="100%" height="100%" resource="icons/jmix-icon.png"/>
<image width="100%" height="100%" resource="icons/jmix-icon.png"/>
<image width="100%" height="100%" resource="icons/jmix-icon.png"/>
<image width="100%" height="100%" resource="icons/jmix-icon.png"/>
<image width="100%" height="100%" resource="icons/jmix-icon.png"/>
<image width="100%" height="100%" resource="icons/jmix-icon.png"/>
<image width="100%" height="100%" resource="icons/jmix-icon.png"/>
</gridLayout>
Привязка к данным
Задайте itemsContainer, чтобы связать gridLayout с контейнером коллекции. Контейнер создаёт по одному отображаемому компоненту для каждого элемента контейнера данных.
<data readOnly="true">
<collection id="customerDc" class="io.jmix.uisamples.entity.Customer"
fetchPlan="_base">
<loader id="customerDl" readOnly="true">
<query>
<![CDATA[select e from Customer e]]>
</query>
</loader>
</collection>
</data>
<facets>
<dataLoadCoordinator auto="true"/>
</facets>
<layout>
<split width="100%" height="100%">
<gridLayout id="gridLayout" itemsContainer="customerDc"
width="100%" height="100%"/>
<div/>
</split>
</layout>
По умолчанию компонент отображает имя экземпляра сущности.
| Вы можете переопределить отображение по умолчанию, настроив пользовательский рендерер. |
Элементы перечисления
Компонент также поддерживает отображение значений перечисления с помощью атрибута itemsEnum.
<gridLayout itemsEnum="com.company.onboarding.entity.DayOfWeek"
width="100%"/>
Пользовательский рендерер элементов
Используйте рендерер, чтобы заменить стандартную текстовую надпись элемента компонентом. В примере gridLayout связывается с контейнером коллекции в XML, а контроллер передаёт ComponentRenderer с помощью @Supply.
<data readOnly="true">
<collection id="customerDc" class="io.jmix.uisamples.entity.Customer"
fetchPlan="_base">
<loader id="customerDl" readOnly="true">
<query>
<![CDATA[select e from Customer e]]>
</query>
</loader>
</collection>
</data>
<facets>
<dataLoadCoordinator auto="true"/>
</facets>
<layout>
<split width="100%" height="100%">
<vbox>
<gridLayout id="gridLayout" itemsContainer="customerDc" gap="var(--lumo-space-m)"
width="100%" height="100%"/>
</vbox>
<div/>
</split>
</layout>
@Autowired
private UiComponents uiComponents;
@Autowired
private MetadataTools metadataTools;
@Autowired
private MessageTools messageTools;
@Autowired
private Metadata metadata;
private int colorIndex;
@Supply(to = "gridLayout", subject = "renderer")
public ComponentRenderer<JmixCard, Customer> customerRenderer() {
return new ComponentRenderer<>(this::createCard, this::initCard);
}
private JmixCard createCard() {
JmixCard card = uiComponents.create(JmixCard.class);
card.setWidthFull();
card.addThemeVariants(CardVariant.OUTLINED, CardVariant.ELEVATED);
return card;
}
private void initCard(JmixCard card, Customer customer) {
card.setHeaderPrefix(createAvatar(customer));
card.setTitle(customer.getInstanceName());
card.setSubtitle(createSubtitle(customer));
card.add(createAdditionalInfo(customer));
}
private Avatar createAvatar(Customer customer) {
Avatar avatar = new Avatar(customer.getInstanceName());
avatar.setColorIndex(colorIndex++ % 7);
return avatar;
}
private Component createSubtitle(Customer customer) {
Span span = uiComponents.create(Span.class);
String ageCaption = getPropertyCaption(customer, "age");
span.setText("%s: %s".formatted(ageCaption, customer.getAge()));
return span;
}
private Component createAdditionalInfo(Customer customer) {
HorizontalLayout emailLayout = uiComponents.create(HorizontalLayout.class);
emailLayout.setPadding(false);
emailLayout.add(createPropertyCaption(customer, "email"), new Span(customer.getEmail()));
HorizontalLayout gradeLayout = uiComponents.create(HorizontalLayout.class);
gradeLayout.setPadding(false);
gradeLayout.add(createPropertyCaption(customer, "grade"), createGradeBadge(customer));
VerticalLayout infoLayout = uiComponents.create(VerticalLayout.class);
infoLayout.add(emailLayout, gradeLayout);
return infoLayout;
}
private Component createPropertyCaption(Customer customer, String property) {
Span span = uiComponents.create(Span.class);
String propertyCaption = getPropertyCaption(customer, property);
span.setText("%s: ".formatted(propertyCaption));
return span;
}
private String getPropertyCaption(Customer customer, String property) {
MetaClass metaClass = metadata.getClass(customer);
return messageTools.getPropertyCaption(metaClass, property);
}
private Component createGradeBadge(Customer customer) {
Span span = uiComponents.create(Span.class);
CustomerGrade gradeValue = customer.getGrade();
String gradeCaption = metadataTools.format(gradeValue);
span.setText(gradeCaption);
span.getElement().getThemeList().add("badge " + getGradeColor(Objects.requireNonNull(gradeValue)));
return span;
}
private String getGradeColor(CustomerGrade customerGrade) {
return switch (customerGrade) {
case STANDARD -> "contrast";
case HIGH -> "success";
case PREMIUM -> "primary";
};
}
Рендерер фрагмента
Элементы также можно отображать с помощью вложенного элемента fragmentRenderer.
-
Создайте дескриптор фрагмента:
<fragment xmlns="http://jmix.io/schema/flowui/fragment"> <data> <instance id="userDc" class="com.company.onboarding.entity.User"> <loader id="userDl"/> <fetchPlan extends="_base"/> </instance> </data> <content> <vbox id="root" padding="false"/> </content> </fragment> -
Создайте контроллер фрагмента.
Расширьте
FragmentRenderer, указав корневой компонент фрагмента и отображаемую сущность как параметры типа:Показать код
@FragmentDescriptor("card-fragment.xml") @RendererItemContainer("userDc") public class CardFragment extends FragmentRenderer<VerticalLayout, User> { @Autowired private FileStorageLocator fileStorageLocator; @Autowired private Metadata metadata; @Autowired private MessageTools messageTools; @Override protected void onAttach(AttachEvent attachEvent) { super.onAttach(attachEvent); initLayout(); } private void initLayout() { Card card = uiComponents.create(Card.class); card.setWidthFull(); card.addThemeVariants(CardVariant.LUMO_OUTLINED, CardVariant.LUMO_ELEVATED); card.setHeaderPrefix(createAvatar(getItem())); card.setTitle(getItem().getFirstName() + " " + getItem().getLastName()); card.setSubtitle(createSubtitle(getItem())); card.setHeaderSuffix(createHeaderSuffix(getItem())); getContent().add(card); } private Image createAvatar(User user) { Image image = uiComponents.create(Image.class); FileRef fileRef = user.getPicture(); if (fileRef != null) { image.setWidth("50px"); image.setHeight("50px"); InputStreamDownloadHandler handler = DownloadHandler.fromInputStream(event -> { InputStream inputStream = fileStorageLocator.getByName(fileRef.getStorageName()).openStream(fileRef); return new DownloadResponse(inputStream, fileRef.getFileName(), fileRef.getContentType(), -1); }); image.setSrc(handler); } return image; } private Span createSubtitle(User user) { Span span = uiComponents.create(Span.class); span.setText("%s: %s".formatted( getPropertyCaption(user, "department"), (user.getDepartment() != null ? user.getDepartment().getName() : "Not assigned"))); return span; } private String getPropertyCaption(User user, String property) { MetaClass metaClass = metadata.getClass(user); return messageTools.getPropertyCaption(metaClass, property); } private Span createHeaderSuffix(User user) { Span span = uiComponents.create(Span.class); if (user.getActive()) { span.setText("Active"); span.getElement().getThemeList().add("badge success"); } else { span.setText("Inactive"); span.getElement().getThemeList().add("badge error"); } return span; } } -
Используйте
fragmentRendererдля компонентаgridLayout:<gridLayout id="gridUsers" width="100%" itemsContainer="usersDc" gap="var(--lumo-space-m)"> <fragmentRenderer class="com.company.onboarding.view.layout.gridlayout.CardFragment"/> </gridLayout>
Атрибуты
Общие атрибуты имеют одинаковое назначение для всех компонентов. Следующие атрибуты настраивают gridLayout или его непосредственные дочерние элементы:
| Имя | Описание | По умолчанию |
|---|---|---|
Задаётся у дочернего элемента и управляет его выравниванием внутри ячейки сетки по блочной оси (оси столбца). Соответствует CSS-свойству align-self. |
|
|
Задает минимальную ширину для столбцов сетки в |
|
|
Задаёт интервалы между ячейками сетки по строкам и столбцам. Значение должно быть допустимой CSS-величиной длины, например |
|
|
Задаётся у дочернего элемента и управляет его выравниванием внутри ячейки сетки по строчной оси (оси строки). Соответствует CSS-свойству justify-self. |
|
Обработчики
Общие обработчики настраиваются одинаково для всех компонентов.
Следующие обработчики специфичны для gridLayout.
| Имя | Описание |
|---|---|
Настраивает текст, который создаёт стандартный рендерер элементов. |
|
Задаёт рендерер, который создаёт компонент для каждого элемента. См. Пользовательский рендерер элементов. |
Элементы
gridLayout без привязки к данным может содержать компоненты напрямую. Контейнер с привязкой к данным может содержать fragmentRenderer, который создаёт компонент для каждого элемента.