Screen
的打开与传值Fragments
的打开与传值Screen
与Fragments
的父子、子子、子父之间的传值
- 一些组件的使用
Table
使用自定义容器DataGrid
使用 KV 属性容器
- 一些布局
- 数据容器的创建和使用
- KV属性容器的创建和使用
- 数据加载器的使用
- 使用
Timer
定时触发事件
Actions
的定义和使用
Dialog
的使用
Notification
的使用
暂无
- 自定义主题
- 使用其他字体库中的图标
暂无
Screen
的打开与传值Fragments
的打开与传值Screen
与 Fragments
的父子、子子、子父之间的传值Table
使用自定义容器DataGrid
使用 KV 属性容器Timer
定时触发事件Actions
的定义和使用Dialog
的使用Notification
的使用暂无
暂无
https://www.cuba-platform.com/discuss/t/menucollapseglyph/11552/14
添加 全局翻译文件 messages.properties 以下内容
sideMenuCollapse = Collapse Menu
sideMenuExpand = Expand Menu
menuCollapseGlyph = «
menuExpandGlyph = »
打开 Screen
共有两种方式可以实现,用 screens
接口 或者 ScreenBuilders Bean
,可以参考官方介绍 https://doc.cuba-platform.cn/manual-7.2-chs/opening_screens.html
我更倾向于使用后者多一些,主要的一个原因就是比较灵活一些,提供的方法比较多。
这两者区别的一点是:screens
不能选择打开方式,而 screenBuilders
可以
可以选择打开方式 NEW_TAB, THIS_TAB, DIALOG, NEW_WINDOW, ROOT
主要有以下文件被创建:
// Controller
ChildScreen.java
// 界面
child-screen.xml
// 国际化等翻译文件
messages.properties
messages_zh_CN.properties
// Controller.java
package com.ason.testing.web.screens.test_screen;
import com.haulmont.cuba.gui.components.Label;
import com.haulmont.cuba.gui.screen.Screen;
import com.haulmont.cuba.gui.screen.UiController;
import com.haulmont.cuba.gui.screen.UiDescriptor;
import javax.inject.Inject;
@UiController("testing_ChildScreen")
@UiDescriptor("child-screen.xml")
public class ChildScreen extends Screen {
@Inject
private Label<String> label_message; // 界面组件
public void setMessage(String message) {
label_message.setValue(message); // 界面组件设置值
}
}
// child-screen.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
caption="msg://caption"
messagesPack="com.ason.testing.web.screens.test_screen">
<layout>
<vbox spacing="true">
<hbox spacing="true">
<label id="label_child" value="msg://label_child"/>
<label id="label_message"/> <!--显示 message 信息-->
</hbox>
<button id="btn_close" caption="msg://btn_close"/>
</vbox>
</layout>
</window>
@Subscribe("btn_open_child")
public void onBtn_open_childClick(Button.ClickEvent event) {
ChildScreen childScreen = screens.create(ChildScreen.class);
childScreen.setMessage("Hello World! I am created by Screens");
childScreen.addAfterCloseListener(e -> {
// TODO: after close child screen
});
screens.show(childScreen);
}
优缺点如下:
ChildScree.java
上面添加 @DialogMode(forceDialog = true, width="80%", height="80%", resizable=true)
@Subscribe("btn_open_child")
public void onBtn_open_childClick(Button.ClickEvent event) {
ChildScreen childScreen = screenBuilders.screen(this)
.withScreenClass(ChildScreen.class)
.withOpenMode(OpenMode.DIALOG) // 可以选择打开方式 NEW_TAB, THIS_TAB, DIALOG, NEW_WINDOW, ROOT
.withAfterCloseListener(e -> {
// TODO: after close child screen
})
.build();
childScreen.setMessage("Hello World! I am created by ScreenBuilders");
childScreen.show();
}
优缺点如下:
ChildScreen.java
中定义,更加灵活以上
加载 Fragments
同样有两种方式,一种是直接在 xml
文件中写,一种是纯java
代码创建
我比较推荐的是在 xml
中写,在界面文件中比较直观,好管理
有以下文件被创建:
// Controller
userFragment.java
// fragment 界面
user-fragment.xml
// 国际化等翻译文件
messages.properties
messages_zh_CN.properties
有以下文件被创建:
// Controller
FragmentCollectionScreen.java
// fragment 界面
fragment-collection-screen.xml
// 国际化等翻译文件
messages.properties
messages_zh_CN.properties
UserFragment 代码
// UserFragment.java
package com.ason.testing.web.screens.test_fragment.user;
import com.haulmont.cuba.gui.model.InstanceLoader;
import com.haulmont.cuba.gui.screen.*;
import com.haulmont.cuba.security.entity.User;
import javax.inject.Inject;
import java.util.UUID;
@UiController("testing_UserFragment")
@UiDescriptor("user-fragment.xml")
public class UserFragment extends ScreenFragment {
//此处定义为 public 是因为要在 Java 代码中直接使用,如果使用方式一,可以定义为 private
@Inject
public InstanceLoader<User> userDl;
private UUID userId;
// 获取
public void setUserId(UUID userId) {
this.userId = userId;
}
private String fromWhere;
public void setFromWhere(String fromWhere) {
this.fromWhere = fromWhere;
}
@Subscribe(target = Target.PARENT_CONTROLLER)
public void onAfterShow(Screen.AfterShowEvent event) {
System.out.println(fromWhere);
userDl.setParameter("userId", userId);
userDl.load();
}
}
<!-- user-fragment.xml -->
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<fragment xmlns="http://schemas.haulmont.com/cuba/screen/fragment.xsd">
<data>
<instance id="userDc"
class="com.haulmont.cuba.security.entity.User"
view="user.browse">
<loader id="userDl">
<query>
<![CDATA[select e from sec$user where e.id = :userId]]>
</query>
</loader>
</instance>
</data>
<layout>
<groupBox spacing="true" collapsable="true" caption="msg://group_user">
<hbox width="100%" spacing="true">
<hbox spacing="true">
<label value="msg://label_user_name"/>
<label id="value_user_name" dataContainer="userDc" property="name"/>
</hbox>
<hbox spacing="true" align="TOP_RIGHT">
<label value="msg://label_email"/>
<label id="value_email" dataContainer="userDc" property="email"/>
</hbox>
</hbox>
</groupBox>
</layout>
</fragment>
FragmentCollectionScreen 代码
package com.ason.testing.web.screens.test_fragment;
import com.ason.testing.web.screens.test_fragment.user.UserFragment;
import com.haulmont.cuba.gui.screen.Screen;
import com.haulmont.cuba.gui.screen.Subscribe;
import com.haulmont.cuba.gui.screen.UiController;
import com.haulmont.cuba.gui.screen.UiDescriptor;
import com.haulmont.cuba.security.global.UserSession;
import javax.inject.Inject;
@UiController("testing_FragmentCollectionScreen")
@UiDescriptor("fragment-collection-screen.xml")
public class FragmentCollectionScreen extends Screen {
@Inject
private UserSession userSession;
@Inject
private UserFragment fragment_user;
@Subscribe
public void onAfterShow(AfterShowEvent event) {
// fragment_user.setUserId(userSession.getId()); // 使用 Java 代码传值
// 使用 Java 代码创建
UserFragment userFragment = fragments.create(this, UserFragment.class);
userFragment.getFragment().setId("main_with_java");
userFragment.setUserId(userSession.getUser().getId());
userFragment.setFromWhere("main_with_java");
userFragment.userDl.setParameter("userId", userSession.getUser().getId());
userFragment.userDl.load();
vbox.add(userFragment.getFragment());
}
}
<!-- fragment-collection-screen.xml -->
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
caption="msg://caption"
messagesPack="com.ason.testing.web.screens.test_fragment">
<layout>
<vbox id="vbox">
<fragment id="fragment_user" screen="testing_UserFragment">
<properties>
<property name="fromWhere" value="main" />
</properties>
</fragment>
</vbox>
</layout>
</window>
如上代码所示,直接在 fragment-collection-screen.xml
里面添加 fragment
组件即可
传值方式有两种,一种是直接在 properies
中 定义 property
,传递静态值。
Tips
:此处需要注意的一点是:fromWhere
(或 xx 属性) 必须要有相对应的 setFromWhere
方法在 Fragement
中,否则会报错
<fragment id="fragment_user" screen="testing_UserFragment">
<properties>
<property name="fromWhere" value="main" />
</properties>
</fragment>
另一种也可以在 FragmentCollectionScreen.java
中,直接使用 Java
代码传动态值
@Inject
private UserSession userSession;
@Inject
private UserFragment fragment_user;
fragment_user.setUserId(userSession.getId()); // 传值
优缺点:
xml
中传递// 使用 Java 代码创建
UserFragment userFragment = fragments.create(this, UserFragment.class);
userFragment.getFragment().setId("main_with_java");
userFragment.setUserId(userSession.getUser().getId());
userFragment.setFromWhere("main_with_java");
// 直接加载数据,否则不会显示内容
userFragment.userDl.setParameter("userId", userSession.getUser().getId());
userFragment.userDl.load();
vbox.add(userFragment.getFragment());
优缺点:
Fragment
以上
有两种方式,不过要看 Fragment
是怎么加载的
<fragment id="fragment_user" screen="testing_UserFragment">
<properties>
<property name="fromWhere" value="main" />
</properties>
</fragment>
fragment_user.setUserId(userSession.getUser().getId()); // 传值
或者
// 使用 Java 代码创建
UserFragment userFragment = fragments.create(this, UserFragment.class);
userFragment.getFragment().setId("main_with_java");
userFragment.setUserId(userSession.getUser().getId()); // 传递变量
userFragment.setFromWhere("main_with_java");
...
使用 getHostScreen
或者 getHostController()
来访问父级界面,然后通过定义在父级界面中的 public
变量来访问
FragmentCollectionScreen hostScreen = (FragmentCollectionScreen)getHostScreen();
hostScreen.setCaption("Set by UserFragment");
使用 getHostScreen
或者 getHostController()
来访问父级界面,然后通过定义在父级界面中的 public
变量来访问 fragment
在主界面定义 fragment
为 public
@Inject
public UserFragment fragment_user;
在 other fragment
中刷新 UserFragment
FragmentCollectionScreen hostScreen = (FragmentCollectionScreen)getHostScreen();
hostScreen.fragment_user.userDl.setParameter("userId", userSession.getUser().getId());
hostScreen.fragment_user.userDl.load();
在程序开发过程中,经常会遇到多页签的情况,这里简单记录一下
Tips
: 不能直接使用 Tab.setCaption
改变 Caption
,使用 TabSheet.getTab("tab").setCaption
@Named("tabsheet.tab_active")
private VBoxLayout tab_active;
@Named("tabsheet.tab_unactive")
private VBoxLayout tab_unactive;
@Inject
private TabSheet tabsheet;
@Subscribe
public void onAfterShow(AfterShowEvent event) {
// 这样改没有用
// tab_active.setCaption("Active_Change_by_Tab");
// tab_unactive.setCaption("Unactive_Change_by_Tab");
tabsheet.getTab("tab_active").setCaption("Active_Change_by_TabSheet");
tabsheet.getTab("tab_unactive").setCaption("Unactive_Change_by_TabSheet");
}
在开发时,会遇到没有实体或者实体属性不定或者需要动态更改属性的时候,普通的集合容器(Collection
)已经没法满足,这时候就可以使用键值对容器(KeyValueCollection
)。
这里介绍一种完全动态 KeyValueCollection
显示在 Table
里的方式。
上传完文件之后需要先预览一下数据信息,确认无误之后,再提交数据库验证
接下来要做的是使用后端返回的 List<T>
数据,进行 Table
的渲染
主要思路:动态生成表格,然后添加到界面上进行显示
使用 ValueGroupDatasourceImpl
存储 KeyValueCollection
数据,然后设置到表格上
Step1:处理数据
使用 addTable
方法,将后端传递过来到数据,转换成 KeyValueCollection
形式,用来在 Table
里面显示
Step2:生成表格
使用 componentsFactory
,动态创建 Table
,然后将数据设置到 Table
里面。
主要有以下文件被创建:
// Controller
UploadReviewScreen.java
// 界面
upload-review-screen.xml
// 国际化等翻译文件
messages.properties
messages_zh_CN.properties
// UploadReviewScreen.java
package com.ason.testing.web.screens.kvCollection;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaPropertyPath;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.gui.components.Table;
import com.haulmont.cuba.gui.components.VBoxLayout;
import com.haulmont.cuba.gui.data.DsBuilder;
import com.haulmont.cuba.gui.data.impl.ValueGroupDatasourceImpl;
import com.haulmont.cuba.gui.screen.Screen;
import com.haulmont.cuba.gui.screen.Subscribe;
import com.haulmont.cuba.gui.screen.UiController;
import com.haulmont.cuba.gui.screen.UiDescriptor;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import javax.inject.Inject;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@UiController("testing_UploadReviewScreen")
@UiDescriptor("upload-review-screen.xml")
public class UploadReviewScreen extends Screen {
final static String packPath = "com.ason.testing.web.screens.kvCollection";
@Inject
private ComponentsFactory componentsFactory;
@Inject
private Messages messages;
@Inject
private VBoxLayout vbox;
private final static List<String> props = new ArrayList<>(
Arrays.asList("id", "name", "email", "address", "birthday", "errorMsg")
);
private final static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
// 测试数据, 后端返回的
private List<ImportUser> fakeUserList = new ArrayList<>();
@Subscribe
public void onAfterInit(AfterInitEvent event) {
fakeUserList = generateUserList(10);
addTable(fakeUserList, vbox, props);
}
/**
* 动态添加数据表格
*
* @param items 返回的数据项
* @param vBox 添加的位置
* @param allProps 属性
* @param <GenericT> 类型
*/
private <GenericT> void addTable(List<GenericT> items, VBoxLayout vBox, List<String> allProps) {
ValueGroupDatasourceImpl ds = DsBuilder.create().buildValuesGroupDatasource();
for (String prop : allProps) {
ds.addProperty(prop);
}
for (GenericT item : items) {
KeyValueEntity kvEntity = new KeyValueEntity();
Field[] fields = ImportUser.class.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
field.setAccessible(true);
try {
Object val = field.get(item);
if (val instanceof Date) {
kvEntity.setValue(field.getName(), formatter.format(val));
} else {
kvEntity.setValue(field.getName(), val != null ? val.toString() : null); // 防止 toString 报错
}
} catch (IllegalAccessException e) {
System.out.println(e);
}
}
ds.addItem(kvEntity);
}
generateTable(ds, vBox, allProps);
}
/**
* 绘制表格
*
* @param ds 数据源
* @param vBox 要添加在哪个box
* @param allProps 所有属性
*/
private void generateTable(ValueGroupDatasourceImpl ds, VBoxLayout vBox, List<String> allProps) {
vBox.removeAll();
Table table = componentsFactory.createComponent(Table.class);
table.setColumnHeaderVisible(true);
table.setSortable(false);
// 添加列
MetaClass metaClass = ds.getMetaClass();
for (String prop : allProps) {
MetaPropertyPath propMpp = metaClass.getPropertyPath(prop);
Table.Column propColumn;
if (propMpp != null) {
propColumn = new Table.Column(propMpp, messages.getMessage(packPath, "ImportUser." + prop));
propColumn.setType(propMpp.getRangeJavaClass());
} else {
// 此种情况是应对在 ImportUser 中没有定义,但是在 props 里面定义了的情况, 可以酌情使用
propColumn = new Table.Column(prop, messages.getMessage(packPath, "ImportUser." + prop));
propColumn.setType(String.class);
}
table.addColumn(propColumn);
}
table.setWidth("100%");
table.setHeight("100%");
table.setDatasource(ds);
vBox.add(table);
}
/**
* 模拟后端数据返回
*
* @param count
* @return
*/
private List<ImportUser> generateUserList(int count) {
List<ImportUser> result = new ArrayList<>();
for (int i = 0; i < count; i++) {
ImportUser importUser = new ImportUser(i, "Name" + i, System.currentTimeMillis() + "@ason.com", "Add" + i, new Date());
result.add(importUser);
}
return result;
}
}
/**
* 后端定义的数据结构
*/
class ImportUser implements Serializable {
private long id;
private String name;
private String email;
private String address;
private Date birthday;
ImportUser(long id, String name, String email, String address, Date birthday) {
this.id = id;
this.name = name;
this.email = email;
this.address = address;
this.birthday = birthday;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
caption="msg://caption"
messagesPack="com.ason.testing.web.screens.kvCollection">
<layout>
<vbox id="vbox" spacing="true"/>
</layout>
</window>
在 DataGrid
中使用 KeyValueCollection
也是比较常见的,最简单的也是一些上传之类的数据,需要先校验,然后再确认上传。
主要思路:创建一个 KeyValueCollection
,然后给 kvCollection
设置 items
, 然后由 DataGrid
来渲染
Step1:先在 xml
中定义一个键值对容器 kvCollection
,然后填写 properties
id(UUID), name(string), extension(string), size(long), createDate(Date)
Step2:将 kvCollection
设置到 dataGrid
上到 dataContainer
属性上
Step3:使用 Java
代码,动态设置 kvCollction
的 items
kvCollction.setItems(Collection<KeyValueEntity> T)
// 这个代表更改 item,如果是新增等 item, 也不能使用 setItem
kvCollection.setItem();
// 这个代表添加 items
kvCollection.setItems();
参看下一篇 8. upload file preview and remove functions 实现上传文件并预览、删除功能