3.5.1.4. 使用界面 Fragments
在本章节,介绍如何定义和使用界面 fragments。
- 声明式使用 fragment
-
假设我们有用来输入地址的 fragment:
AddressFragment.java@UiController("demo_AddressFragment") @UiDescriptor("address-fragment.xml") public class AddressFragment extends ScreenFragment { }
address-fragment.xml<fragment xmlns="http://schemas.haulmont.com/cuba/screen/fragment.xsd"> <layout> <textField id="cityField" caption="City"/> <textField id="zipField" caption="Zip"/> </layout> </fragment>
然后我们可以在其它界面使用
fragment
元素来包含此 fragment,fragment
元素需要有指向 fragment id 的screen
属性,fragment id 在其@UiController
注解设置:host-screen.xml<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd" caption="Some Screen"> <layout> <groupBox id="addressBox" caption="Address"> <fragment screen="demo_AddressFragment"/> </groupBox> </layout> </window>
fragment
元素可以添加在界面任意的 UI 容器中,也包含最顶上的layout
元素。
- 编程式使用 fragment
-
同一个 fragment也可以通过编程的方式添加到界面,需要在 InitEvent 或 AfterInitEvent 事件处理器中添加:
host-screen.xml<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd" caption="Some Screen"> <layout> <groupBox id="addressBox" caption="Address"/> </layout> </window>
HostScreen.java@UiController("demo_HostScreen") @UiDescriptor("host-screen.xml") public class HostScreen extends Screen { @Inject private Fragments fragments; (1) @Inject private GroupBoxLayout addressBox; @Subscribe private void onInit(InitEvent event) { AddressFragment addressFragment = fragments.create(this, AddressFragment.class); (2) addressFragment.init(); (3) addressBox.add(addressFragment.getFragment()); (4) } }
1 - 注入 Fragments
bean,用来实例化界面 fragment2 - 用 class 创建 fragment 控制器 3 - 初始化界面 fragment 4 - 从控制器获取 Fragment
可视化组件,并添加到 UI 容器别忘了在用编程的方式创建 fragment 之后需要使用
init()
方法进行初始化。如果 fragment 带有参数,可以在调用init()
之前使用 public setters 进行设置。之后,fragment 控制器的InitEvent
和AfterInitEvent
处理方法里面可以访问到这些参数。
- 界面 fragment 中的数据组件
-
界面 fragment 可以有自己的数据容器和数据加载器,通过 XML 元素
data
定义。同时,框架会为界面及其所有 fragments 创建DataContext的单例。因此,所有加载的实体都合并到同一数据上下文,并在父界面提交的时候一起保存更改。下面的例子中,会使用界面 fragment 自己的数据容器和加载器。
假设在 fragment 中有
City
实体,我们希望使用下拉列表框展示可选的城市而不仅仅用文本控件来展示。可以跟普通界面一样,在 fragment 的 XML 描述中定义数据组件。address-fragment.xml<fragment xmlns="http://schemas.haulmont.com/cuba/screen/fragment.xsd"> <data> <collection id="citiesDc" class="com.company.demo.entity.City" view="_base"> <loader id="citiesLd"> <query><![CDATA[select e from demo_City e ]]></query> </loader> </collection> </data> <layout> <lookupField id="cityField" caption="City" optionsContainer="citiesDc"/> <textField id="zipField" caption="Zip"/> </layout> </fragment>
如果需要在父界面打开时加载 fragment 的数据,需要订阅父界面事件:
AddressFragment.java@UiController("demo_AddressFragment") @UiDescriptor("address-fragment.xml") public class AddressFragment extends ScreenFragment { @Inject private CollectionLoader<City> citiesLd; @Subscribe(target = Target.PARENT_CONTROLLER) (1) private void onBeforeShowHost(Screen.BeforeShowEvent event) { citiesLd.load(); } }
1 - 订阅父界面的 BeforeShowEvent 事件 @LoadDataBeforeShow
对界面 fragments 无效。
- 使用已有的数据容器
-
下一个例子展示了如何在 fragment 中使用父界面的数据容器。
host-screen.xml<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd" caption="Some Screen"> <data> <instance id="addressDc" class="com.company.demo.entity.Address"/> (1) </data> <layout> <groupBox id="addressBox" caption="Address"> <fragment screen="demo_AddressFragment"/> </groupBox> </layout> </window>
1 - fragment 将在下面使用的数据容器 address-fragment.xml<fragment xmlns="http://schemas.haulmont.com/cuba/screen/fragment.xsd"> <data> <instance id="addressDc" class="com.company.demo.entity.Address" provided="true"/> (1) <collection id="citiesDc" class="com.company.demo.entity.City" view="_base"> <loader id="citiesLd"> <query><![CDATA[select e from demo_City e]]></query> </loader> </collection> </data> <layout> <lookupField id="cityField" caption="City" optionsContainer="citiesDc" dataContainer="addressDc" property="city"/> (2) <textField id="zipField" caption="Zip" dataContainer="addressDc" property="zip"/> </layout> </fragment>
1 - provided="true"
表示使用同样 id 的容器必须存在于父界面或者嵌套的 fragment 中,也就是说必须在该 fragment 外部提供2 - UI 组件连接到提供的数据容器 在包含
provided="true"
属性的 XML 元素中,除了id
之外其它所有的属性都会被忽略,但是也可以加上,以便提供设计思路。
- 订阅父界面事件
-
在 fragment 控制器中,可以订阅父界面的事件,需要在注解中为
target
属性指定PARENT_CONTROLLER
值,示例:@Subscribe(target = Target.PARENT_CONTROLLER) private void onBeforeShowHost(Screen.BeforeShowEvent event) { // }
这个方法可以处理任何事件,包括实体编辑界面发送的 InitEntityEvent 事件。