使用 Fragments
在本章节,介绍如何定义和使用 界面 fragments。参阅 ScreenFragment Events 了解如何处理 fragment 的生命周期事件。
声明式使用
假设我们有用来输入地址的 fragment:
@UiController("sample_AddressFragment")
@UiDescriptor("address-fragment.xml")
public class AddressFragment extends ScreenFragment {
}
<fragment xmlns="http://jmix.io/schema/ui/fragment">
<layout>
<textField id="cityField" caption="City"/>
<textField id="zipField" caption="Zip"/>
</layout>
</fragment>
然后我们可以在其他界面使用 fragment
元素来包含此 fragment,fragment
元素需要有指向 fragment id 的 screen
属性,fragment id 在其 @UiController
注解设置:
<window xmlns="http://jmix.io/schema/ui/window"
caption="msg://hostScreen.caption">
<layout>
<groupBox id="addressBox" caption="Address">
<fragment screen="sample_AddressFragment"/>
</groupBox>
</layout>
</window>
fragment
元素可以添加在界面任意的 UI 容器中,也包含最外层的 layout
元素。
编程式使用
同一个 fragment 也可以通过编程的方式添加到界面,需要在 InitEvent 或 AfterInitEvent 事件处理器中添加:
<window xmlns="http://jmix.io/schema/ui/window"
caption="msg://hostScreen.caption">
<layout>
<groupBox id="addressBox" caption="Address"/>
</layout>
</window>
@UiController("sample_HostScreen")
@UiDescriptor("host-screen.xml")
public class HostScreen extends Screen {
@Autowired
private Fragments fragments; (1)
@Autowired
private GroupBoxLayout addressBox;
@Subscribe
private void onInit(InitEvent event) {
AddressFragment addressFragment = fragments.create(this, AddressFragment.class); (2)
addressBox.add(addressFragment.getFragment()); (3)
}
}
1 | 注入 Fragments bean,用来实例化界面 fragment。 |
2 | 用类创建 fragment 控制器 |
3 | 从控制器中获取 Fragment 可视化组件,并添加到 UI 容器中。 |
如果 fragment 有参数,可以在将 fragment 添加到界面之前通过公共 setter 方法设置。之后,fragment 控制器的 InitEvent 和 AfterInitEvent 处理方法里面可以访问到这些参数。
|
传递参数
Fragment 控制器可以有公共的 setter 方法用来接收参数,与打开界面时传参一样。示例:
@UiController("sample_AddressFragment")
@UiDescriptor("address-fragment.xml")
public class AddressFragment extends ScreenFragment {
@Autowired
private TextField<String> zipcodeField;
private String zipcode;
public void setZipcode(String zipcode) { (1)
this.zipcode = zipcode;
}
public void setAddressContainer(InstanceContainer<Address> dataContainer) { (1)
//
}
@Subscribe
public void onInit(InitEvent event) {
zipcodeField.setInputPrompt(zipcode); (2)
}
}
1 | Setter 方法,支持为界面 fragment 传递参数。 |
2 | 参数使用示例。 |
如果 fragment 是以编程式打开,需要显式调用 setters:
@Autowired
private Fragments fragments;
@Autowired
private GroupBoxLayout addressBox;
@Subscribe
private void onInit(InitEvent event) {
AddressFragment addressFragment = fragments.create(this, AddressFragment.class);
addressFragment.setZipcode("2779001"); (1)
addressBox.add(addressFragment.getFragment());
}
1 | - 在将片段添加到界面之前先传递参数。 |
如果 fragment 是通过 XML 以声明的方式添加到界面,使用 properties
元素来传递参数,示例:
<fragment id="addressFragment" screen="sample_AddressFragment">
<properties>
<property name="zipcode" value="2779001"/> (1)
<property name="addressContainer" ref="addressDc"/> (2)
</properties>
</fragment>
1 | 传递一个字符串参数给 setZipcode() 方法。 |
2 | 传递一个数据容器给 setAddressContainer() 方法。 |
使用 value
属性设置值,用 ref
属性指定界面组件的标识符。setters 必须使用合适类型的参数。
如需在 Jmix Studio 中添加参数,可以在界面 XML 或者 Component Hierarchy 面板中选择 |
使用数据组件
界面 fragment 可以有自己的数据容器和数据加载器,通过 XML 元素 data
定义。同时,框架会为界面及其所有 fragments 创建 DataContext 的单例。因此,所有加载的实体都合并到同一数据上下文,并在父界面提交的时候一起保存。
下面的例子中,会使用界面 fragment 自己的数据容器和加载器。
假设在 fragment 中有 Country
实体,我们希望使用下拉列表展示可选的国家而不用文本控件来展示。可以跟普通界面一样,在 fragment 的 XML 描述中定义数据组件。
<fragment xmlns="http://jmix.io/schema/ui/fragment">
<data>
<collection id="countriesDc" class="ui.ex1.entity.Country">
<fetchPlan extends="_base"/>
<loader id="countriesDl">
<query>
<![CDATA[select e from uiex1_Country e]]>
</query>
</loader>
</collection>
</data>
<facets>
<dataLoadCoordinator auto="true"/>
</facets>
<layout>
<entityComboBox id="countryField" caption="Country" optionsContainer="countriesDc"/>
<textField id="zipField" caption="Zip"/>
</layout>
</fragment>
当父界面打开时,数据会加载到 fragment。
共用数据容器
下一个例子展示了如何在 fragment 中使用父界面的数据容器。
<window xmlns="http://jmix.io/schema/ui/window"
caption="msg://hostScreen.caption">
<data>
<instance id="addressDc"
class="ui.ex1.entity.Address"> (1)
<fetchPlan extends="_base"/>
<loader/>
</instance>
</data>
<facets>
<dataLoadCoordinator auto="true"/>
</facets>
<layout>
<groupBox id="addressBox" caption="Address">
<fragment screen="sample_AddressFragment"/>
</groupBox>
</layout>
</window>
1 | 下面的 fragment 将使用的数据容器 |
<fragment xmlns="http://jmix.io/schema/ui/fragment">
<data>
<instance id="addressDc" class="ui.ex1.entity.Address"
provided="true"/> (1)
<collection id="countriesDc" class="ui.ex1.entity.Country" provided="true"/>
</data>
<layout>
<entityComboBox id="countryField" caption="Country" optionsContainer="countriesDc"
dataContainer="addressDc" property="country"/> (2)
<textField id="streetField"
property="street"/>
<textField id="zipcodeField"
property="zipcode"/>
</layout>
</fragment>
1 | provided="true" 表示使用同样 id 的容器必须存在于父界面或者外部嵌套的 fragment 中。 |
2 | UI 组件连接到提供的数据容器 |
在包含 provided="true"
属性的 XML 元素中,除了 id
之外其他所有的属性都会被忽略,但是也可以加上,以便提供设计思路。
Fragment 中也可以定义由父界面提供的加载器。外部提供的加载器 id 必须与原 id 一致,并使用 provided="true"
属性。示例:
<loader id="addressDl" provided="true"/>