gridLayout 表格布局

gridLayout 组件将内部的子组件布局在一个响应式、二维的表格系统中,基于 CSS 的 Grid 布局

XML 元素

gridLayout

Java 类

GridLayout

XML 属性

id - alignSelf - classNames - columnMinWidth - css - enabled - gap - height - itemsContainer - itemsEnum - justifySelf - maxHeight - maxWidth - minHeight - minWidth - visible - width

事件和处理器

AttachEvent - itemLabelGenerator - renderer - DetachEvent

XML 内部元素

fragmentRenderer

基本用法

支持声明式在 XML 中将组件添加至 gridLayout,也支持是编程式通过调用 gridLayout.add() 在控制器添加组件。

声明式添加的示例:

<gridLayout id="gridLayout" width="100%">
    <textField placeholder="City" label="Where from?"/>
    <textField placeholder="City" label="Where to?"/>
    <datePicker label="Depart"/>
    <datePicker label="Return"/>
    <button text="Search tickets" height="AUTO"/>
</gridLayout>

在视图控制器添加组件的示例:

@ViewComponent
private GridLayout<Object> gridLayout;

@Autowired
private UiComponents uiComponents;

@Subscribe
public void onInit(final InitEvent event) {
    Checkbox checkbox = uiComponents.create(Checkbox.class);
    checkbox.setLabel("I verify that all information is accurate");
    checkbox.setValue(false);
    gridLayout.add(checkbox);
}
grid layout basic

数据绑定

数据绑定是指将组件与 数据容器 进行关联。

gridLayout 组件中,将 itemsContainer 属性设置为数据容器实现数据绑定。

<data>
    <collection id="usersDc" class="com.company.onboarding.entity.User"> (1)
        <fetchPlan extends="_base">
            <property name="department" fetchPlan="_base"/>
        </fetchPlan>
        <loader id="usersDl" readOnly="true">
            <query>
                <![CDATA[select e from User e]]>
            </query>
        </loader>
    </collection>
</data>
<facets>
    <dataLoadCoordinator auto="true"/> (2)
</facets>
<layout>
    <gridLayout width="100%"
                itemsContainer="usersDc"/> (3)
</layout>
1 保存 User 实例集合的数据容器。
2 数据加载协调器,自动为组件提供要显示的实例。
3 指定包含要显示的条目列表的容器。

默认情况下,组件显示实体的 实例名称

grid layout items
可以通过配置 自定义渲染器 来修改显示内容。

该组件还支持使用 itemsEnum 属性显示枚举值。

<gridLayout itemsEnum="com.company.onboarding.entity.DayOfWeek"
            width="100%"/>

条目渲染

可以自定义条目的渲染方式。渲染器对每个条目有效,创建一个表示该条目的组件。

可以通过两种不同的方法实现自定义渲染。

编程方式

在视图控制器中,可以使用以下任一方式:

  • setRenderer() 方法;

  • @Supply 注解。

基于 前面的示例,我们可以生成一个 renderer,将每个用户显示为一个自定义的 Card 组件。

Show code
@Supply(to = "gridLtUsers", subject = "renderer")
private ComponentRenderer<Card, User> gridLtUsersRenderer() { (1)
    return new ComponentRenderer<>(this::createCard, this::initCard);
}

private Card createCard() { (2)
    Card card = uiComponents.create(Card.class);
    card.setWidthFull();
    card.addThemeVariants(CardVariant.LUMO_OUTLINED, CardVariant.LUMO_ELEVATED);
    return card;
}

private void initCard(Card card, User user) { (3)
    card.setHeaderPrefix(createAvatar(user));
    card.setTitle(user.getFirstName() + " " + user.getLastName());
    card.setSubtitle(createSubtitle(user));
    card.setHeaderSuffix(createHeaderSuffix(user));
}

private Image createAvatar(User user) { (4)
    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;
}
1 自定义渲染器,将每个 User 显示为 Card
2 创建一个带有通用样式的基本 Card 组件。
3 使用用户特定数据初始化卡片内容。为每个卡片实例调用,并传入对应的 User 实体。
4 从用户的图片创建一个头像 图片 组件。
grid layout renderer

声明式方式

或者,可以使用嵌套的 fragmentRenderer 元素来渲染项目。

  1. 创建 FragmentRenderer XML:

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <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>
  2. 创建 FragmentRenderer Java 控制器

    fragment 渲染器类应继承 FragmentRenderer 基类,并使用类型参数设置根组件的类型和渲染的实体,例如:

    Show code
    @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;
        }
    }
  3. gridLayout 组件使用 fragmentRenderer

    <gridLayout id="gridUsers"
                width="100%"
                itemsContainer="usersDc"
                gap="var(--lumo-space-m)">
        <fragmentRenderer class="com.company.onboarding.view.layout.gridlayout.CardFragment"/>
    </gridLayout>

XML 属性

通用属性 对所有组件都是一样的配置。

下面是 gridLayout 的特殊属性:

名称

描述

默认值

alignSelf

控制组件在其网格单元格内沿块轴(列轴)的垂直对齐方式。对应于 CSS 的 align-self 属性。另请参考 alignSelf

适用于版本 2.7.2+ 的项目。

AUTO

columnMinWidth

设置 gridLayout 中网格列的最小宽度。可以防止在容器调整大小时列变得过窄。该值为 CSS 长度值(例如 "50px""10rem")。

19rem

gap

控制网格单元格之间的间距(行间距和列间距)。该值为 CSS 长度值(例如 "10px""1rem")。

var(--lumo-space-s)

justifySelf

控制组件在其网格单元格内沿内轴(行轴)的水平对齐方式。对应于 CSS 的 justify-self 属性。

适用于版本 2.7.2+ 的项目。

AUTO

事件和处理器

通用事件和处理器 对所有组件都是一样的配置。

下面是 gridLayout 的特殊事件和处理器:

在 Jmix Studio 生成处理器桩代码时,可以使用 Jmix UI 组件面板的 Handlers 标签页或者视图类顶部面板的 Generate Handler 添加,也可以通过 CodeGenerate 菜单(Alt+Insert / Cmd+N)生成。

名称

描述

itemLabelGenerator

支持对每个条目的显示标签进行自定义。参阅 自定义条目标签.

renderer

设置内部条目的渲染器。渲染器对每个条目创建一个可视化组件。参考 条目渲染