userMenu 用户菜单
userMenu 组件显示一个按钮,当点击时,打开一个下拉信息菜单。
XML 元素 |
|
|---|---|
Java 类 |
|
XML 属性 |
id - alignSelf - classNames - colspan - css - enabled - focusShortcut - openOnHover - overlayClass - tabIndex - themeNames - title - visible |
事件和处理器 |
AttachEvent - BlurEvent - DetachEvent - FocusEvent - UserChangedEvent - buttonRenderer - headerRenderer |
XML 内部元素 |
actionItem - componentItem - separator - textItem - viewItem |
基本用法
主按钮的布局中显示了当前用户的信息。 用户点击此按钮可打开下拉菜单。
下拉菜单包含一系列操作,每个操作由一个可点击的菜单项或子菜单表示。
以下是一个 userMenu 的示例,使用了默认按钮渲染器和多个菜单项:
<userMenu>
<items>
<viewItem id="profileMenuItem" text="msg://profileMenuItem.text" icon="USER"
viewId="ProfileView"
themeNames="non-checkable"/>
<viewItem id="settingsMenuItem" text="msg://settingsMenuItem.text" icon="COG"
viewClass="com.company.onboarding.view.settings.SettingsView"
themeNames="non-checkable"/>
<actionItem id="themeMenuItem"
themeNames="non-checkable">
<action id="themeMenuItem" type="userMenu_themeSwitch"/>
</actionItem>
<separator/>
<actionItem id="logoutMenuItem" ref="logoutAction"
themeNames="non-checkable"/>
</items>
</userMenu>
XML 内部元素
在 XML 中,userMenu 可以包含内部元素:
actionItem
actionItem 元素用于关联用户菜单项与点击该项时的特定 操作。
可以声明式地定义一个操作,也可以使用 ref 属性指向已定义的 action 的 id。
<actions>
<action id="logoutAction" text="msg:///actions.logout.text" type="logout"/>
</actions>
<layout>
<userMenu id="userMenuActions">
<items>
<actionItem id="aboutMenuItem">
<action id="aboutAction" text="msg://aboutAction.text" icon="INFO_CIRCLE_O"/> (1)
</actionItem>
<actionItem id="logoutMenuItem" ref="logoutAction"/> (2)
</items>
</userMenu>
</layout>
| 1 | 声明式定义的操作。 |
| 2 | 引用已有操作。 |
当用户点击下拉菜单中的 actionItem 时,Jmix 会自动触发该 action。
可以为某个操作生成 ActionPerformedEvent 处理方法来实现其逻辑:
@Autowired
private Notifications notifications;
@Subscribe("userMenuActions.aboutMenuItem.aboutAction")
public void onUserMenuActionsAboutMenuItemAboutAction(final ActionPerformedEvent event) {
notifications.show("About");
}
框架提供了一些内置的 用户菜单操作。
componentItem
componentItem 元素可以为 userMenu 添加自定义的内部组件。
<userMenu id="userMenuComponent">
<items>
<componentItem id="emailItMenuItem">
<hbox padding="false">
<icon icon="MAILBOX"/>
<span text="E Mail"/>
</hbox>
</componentItem>
</items>
</userMenu>
在 Jmix Studio 中,可以为 componentItem 添加一个 UserMenuItem.HasClickListener.ClickEvent 事件处理的桩代码:
@Autowired
private Notifications notifications;
@Subscribe("userMenuComponent.emailItMenuItem")
public void onUserMenuComponentEmailItMenuItemClick(final UserMenuItem.HasClickListener.ClickEvent<ComponentUserMenuItem> event) {
notifications.show("Email: test@river.net");
}
textItem
textItem 元素可以添加文本和图标。
<userMenu id="userMenuText">
<items>
<textItem id="contactUsMenuItem" text="msg://contactUsItem.text" icon="PHONE"/>
</items>
</userMenu>
在 Jmix Studio 中,可以为 textItem 添加一个 UserMenuItem.HasClickListener.ClickEvent 事件处理的桩代码:
@Autowired
private Notifications notifications;
@Subscribe("userMenuText.contactUsMenuItem")
public void onUserMenuTextContactUsMenuItemClick(final UserMenuItem.HasClickListener.ClickEvent<TextUserMenuItem> event) {
notifications.show("Phone number: +6(876)5463");
}
viewItem
viewItem 元素支持打开特定的视图。
<userMenu id="userMenuView">
<items>
<viewItem id="profileMenuItem" text="msg://profileMenuItem.text" icon="USER"
viewId="ProfileView"/> (1)
<viewItem id="settingsMenuItem" text="msg://settingsMenuItem.text" icon="COG"
viewClass="com.company.onboarding.view.settings.SettingsView"
openMode="DIALOG"/> (2)
</items>
</userMenu>
| 1 | 用指定的 id 打开特定视图。 |
| 2 | 以 DIALOG 模式打开指定类的视图。 |
separator
separator 元素用于在下拉菜单中分隔菜单项。
分隔符在满足以下条件时会自动隐藏:
-
多个分隔符连续出现。此时仅保留一个分隔符可见。
-
分隔符是第一个子元素且未定义 header 渲染器。
-
分隔符是最后一个子元素。
子菜单
textItem 和 componentItem 元素支持定义内部菜单项,从而可以定义具有层级结构的菜单。
<userMenu>
<items>
<textItem id="helpMenuItem"
text="msg://helpMenuItem.text" icon="QUESTION_CIRCLE"
themeNames="non-checkable">
<items>
<textItem id="documentationMenuItem" text="msg://documentationMenuItem.text"/>
<textItem id="aboutMenuItem" text="msg://aboutMenuItem.text"/>
</items>
</textItem>
</items>
</userMenu>
样式版本
themeNames 支持为组件设置一个预定义的样式版本。
-
tertiary- 移除按钮背景
<userMenu> <items> <actionItem id="logoutMenuItem" ref="logoutAction"/> </items> </userMenu> <userMenu themeNames="tertiary"> <items> <actionItem id="logoutMenuItem" ref="logoutAction"/> </items> </userMenu> -
non-checkable- 移除显示对勾的区域
<userMenu> <items> <viewItem id="profileMenuItem" text="msg://profileMenuItem.text" icon="USER" viewId="ProfileView"/> <viewItem id="settingsMenuItem" text="msg://settingsMenuItem.text" icon="COG" viewClass="com.company.onboarding.view.settings.SettingsView"/> <actionItem id="themeMenuItem"> <action id="themeMenuItem" type="userMenu_themeSwitch"/> </actionItem> <separator/> <actionItem id="logoutMenuItem" ref="logoutAction"/> </items> </userMenu> <userMenu> <items> <viewItem id="profileMenuItem" text="msg://profileMenuItem.text" icon="USER" viewId="ProfileView" themeNames="non-checkable"/> <viewItem id="settingsMenuItem" text="msg://settingsMenuItem.text" icon="COG" viewClass="com.company.onboarding.view.settings.SettingsView" themeNames="non-checkable"/> <actionItem id="themeMenuItem" themeNames="non-checkable"> <action id="themeMenuItem" type="userMenu_themeSwitch"/> </actionItem> <separator/> <actionItem id="logoutMenuItem" ref="logoutAction" themeNames="non-checkable"/> </items> </userMenu>
non-checkable 样式可以在 userMenu 和子菜单级别单独设置。
如果子菜单有可以勾选的菜单,最好在一级菜单中单独定义 non-checkable 样式。
|
渲染器
框架为按钮和可选的菜单 header 内容的自定义功能提供饿了灵活的方案。
按钮渲染器
可以通过 setButtonRenderer() 方法或 @Install 注解设置按钮渲染器。
@Autowired
private UiComponents uiComponents;
@Autowired
private FileStorage fileStorage;
@Install(to = "userMenu", subject = "buttonRenderer")
private Component userMenuButtonRenderer(final UserDetails userDetails) {
if (!(userDetails instanceof User user)) {
return null;
}
String userName = generateUserName(user);
Avatar avatar = createAvatar(userName, user.getPicture());
Span name = uiComponents.create(Span.class);
name.setText(userName);
name.addClassName(LumoUtility.TextColor.BODY);
HorizontalLayout content = uiComponents.create(HorizontalLayout.class);
content.setAlignItems(FlexComponent.Alignment.CENTER);
content.add(avatar, name);
content.addClassNames( (1)
LumoUtility.Padding.Horizontal.MEDIUM,
LumoUtility.Padding.Vertical.SMALL);
return content;
}
private String generateUserName(User user) {
String userName = String.format("%s %s",
Strings.nullToEmpty(user.getFirstName()),
Strings.nullToEmpty(user.getLastName()))
.trim();
return userName.isEmpty() ? user.getUsername() : userName;
}
private Avatar createAvatar(String fullName, @Nullable FileRef fileRef) {
Avatar avatar = uiComponents.create(Avatar.class);
avatar.setName(fullName);
avatar.getElement().setAttribute("tabindex", "-1"); (2)
if (fileRef != null) {
StreamResource streamResource = new StreamResource(
fileRef.getFileName(),
() -> fileStorage.openStream(fileRef));
avatar.setImageResource(streamResource); (3)
}
return avatar;
}
| 1 | 默认 HorizontalLayout 的内边距太大,我们通过样式添加内边距。 |
| 2 | avatar 默认是可聚焦的,但没有提供禁用聚焦的 Java API。移除 tab 索引,只有 userMenu 组件可以接收焦点。 |
| 3 | avatar 通过存储在 User 实体的 picture 属性中的引用,从给定的 StreamResource 获取内容。 |
Header 渲染器
Header 渲染器用于定义菜单顶部的信息。可以通过 setHeaderRenderer() 方法或 @Install 注解设置 header 渲染器。
@Autowired
private UiComponents uiComponents;
@Autowired
private FileStorage fileStorage;
@Install(to = "userMenu", subject = "headerRenderer")
private Component userMenuHeaderRenderer(final UserDetails userDetails) {
if (!(userDetails instanceof User user)) {
return null;
}
String name = generateUserName(user);
Avatar avatar = createAvatar(name, user.getPicture());
avatar.addThemeVariants(AvatarVariant.LUMO_LARGE);
avatar.addClassName("user-menu-avatar");
Span text = uiComponents.create(Span.class);
text.setText(name);
text.setClassName("user-menu-text");
Div content = uiComponents.create(Div.class);
content.setClassName("user-menu-header-content"); (1)
content.add(avatar, text);
if (name.equals(user.getUsername())) {
text.addClassNames("user-menu-text-subtext");
} else {
Span subtext = uiComponents.create(Span.class);
subtext.setText(user.getUsername());
subtext.setClassName("user-menu-subtext");
content.add(subtext);
}
return content;
}
private String generateUserName(User user) {
String userName = String.format("%s %s",
Strings.nullToEmpty(user.getFirstName()),
Strings.nullToEmpty(user.getLastName()))
.trim();
return userName.isEmpty() ? user.getUsername() : userName;
}
private Avatar createAvatar(String fullName, @Nullable FileRef fileRef) {
Avatar avatar = uiComponents.create(Avatar.class);
avatar.setName(fullName);
avatar.getElement().setAttribute("tabindex", "-1"); (2)
if (fileRef != null) {
StreamResource streamResource = new StreamResource(
fileRef.getFileName(),
() -> fileStorage.openStream(fileRef));
avatar.setImageResource(streamResource); (3)
}
return avatar;
}
| 1 | 组件位置通过样式定义。 |
| 2 | avatar 默认是可聚焦的,但没有提供禁用聚焦的 Java API。移除 tab 索引,只有 userMenu 组件可以接收焦点。 |
| 3 | avatar 通过存储在 User 实体的 picture 属性中的引用,从给定的 StreamResource 获取其内容。 |
.user-menu-header-content {
display: grid;
grid-template: "avatar text"
"avatar subtext";
grid-template-columns: auto 1fr;
column-gap: var(--lumo-space-s);
width: 100%;
box-sizing: border-box;
color: var(--lumo-body-text-color);
padding: var(--lumo-space-xs) var(--lumo-space-l) var(--lumo-space-xs) var(--lumo-space-s);
}
.user-menu-header-content > .user-menu-avatar {
grid-area: avatar;
align-self: center;
}
.user-menu-header-content > .user-menu-text {
grid-area: text;
color: var(--lumo-body-text-color);
font-weight: 700;
font-size: var(--lumo-font-size-m);
}
.user-menu-header-content > .user-menu-text-subtext {
grid-row: text / subtext;
}
.user-menu-header-content > .user-menu-text {
align-self: center;
text-align: start;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
.user-menu-header-content > .user-menu-subtext {
grid-area: subtext;
align-self: center;
text-align: start;
color: var(--lumo-secondary-text-color);
font-size: var(--lumo-font-size-xs);
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
XML 属性
通用属性 对所有组件都是一样的配置。
下面是 userMenu 的特殊属性:
名称 |
描述 |
默认值 |
|---|---|---|
如果 |
|
事件和处理器
通用事件和处理器 对所有组件都是一样的配置。
下面是 userMenu 的特殊事件和处理器:
|
在 Jmix Studio 生成处理器桩代码时,可以使用 Jmix UI 组件面板的 Handlers 标签页或者视图类顶部面板的 Generate Handler 添加,也可以通过 Code → Generate 菜单(Alt+Insert / Cmd+N)生成。 |
名称 |
描述 |
|---|---|
|
|
设置基于当前用户信息渲染按钮的 |
|
设置基于当前用户信息渲染 header 的 |