Фрагменты

Фрагмент - это структурный элемент пользовательского интерфейса, который можно использовать в качестве компонента экранов или других фрагментов. Фрагмент является композитным компонентом и обладает следующими дополнительными возможностями:

  • Содержимое фрагмента можно определять в формате XML.

  • Элемент XML fragment позволяет декларативно включать фрагменты в экраны и другие фрагменты.

  • Класс фрагмента (называемый также контроллер) поддерживает инжекцию бинов Spring и UI-компонентов фрагмента, а также аннотированные методы в качестве обработчиков.

  • Когда фрагмент полностью инициализирован, отправляется событие ReadyEvent.

  • В XML фрагмента можно определить действия и компоненты данных (контейнеры и загрузчики). Компоненты данных можно пометить как provided для их получения из экрана-владельца.

  • Студия предлагает шаблон Blank fragment для генерации фрагментов, отображает их в окне инструментов Jmix и позволяет использовать визуальный дизайнер так же, как для экранов.

Фрагменты в настоящее время не поддерживают фасеты.

Содержимое фрагмента

Аннотация @FragmentDescriptor определяет строковое значение, которое представляет собой путь к файлу XML, используемому для инициализации фрагмента. Если значение содержит только имя файла (и не начинается с /), то предполагается, что файл находится в пакете класса фрагмента.

Элементы, доступные в XML-дескрипторе:

  • content - обязательный элемент, содержащий макета фрагмент (аналогично элементу layout экрана). Поскольку корневым компонентом фрагмента может быть любой компонент, у content нет атрибутов и он не представляет какой-либо UI-компонент.

  • actions - необязательный элемент действий фрагмента (аналогичный элементу actions экрана). Если для действия задано сочетание клавиш, оно привязывается к корневому компоненту фрагмента. Другими словами, сочетание клавиш может быть активировано только в том случае, если фокус находится внутри фрагмента.

  • data — необязательный элемент данных фрагмента (аналогичный элементу data экрана). Фрагмент может определить свои собственные контейнеры данных и загрузчики, или получить их по идентификатору из включающего экрана или фрагмента, как описано в разделе Использование компонентов данных.

Пример XML-дескриптора фрагмента:

customer-list-fragment.xml
<fragment xmlns="http://jmix.io/schema/flowui/fragment">
    <data>
        <collection id="customersDc"
                    class="com.company.onboarding.entity.Customer">
            <fetchPlan extends="_base">
                <property name="city" fetchPlan="_base"/>
            </fetchPlan>
            <loader id="customersDl" readOnly="true">
                <query>
                    <![CDATA[select e from Customer e]]>
                </query>
            </loader>
        </collection>
    </data>
    <content>
        <vbox id="root" padding="false">
            <genericFilter id="genericFilter"
                           dataLoader="customersDl">
                <properties include=".*"/>
            </genericFilter>
            <hbox id="buttonsPanel" classNames="buttons-panel">
                <button id="createBtn" action="customersDataGrid.create"/>
                <button id="editBtn" action="customersDataGrid.edit"/>
                <button id="removeBtn" action="customersDataGrid.remove"/>
                <simplePagination id="pagination" dataLoader="customersDl"/>
            </hbox>
            <dataGrid id="customersDataGrid"
                      width="100%"
                      minHeight="20em"
                      dataContainer="customersDc"
                      columnReorderingAllowed="true">
                <actions>
                    <action id="create" type="list_create"/>
                    <action id="edit" type="list_edit"/>
                    <action id="remove" type="list_remove"/>
                </actions>
                <columns resizable="true">
                    <column property="city"/>
                    <column property="level"/>
                    <column property="age"/>
                    <column property="martialStatus"/>
                    <column property="hobby"/>
                    <column property="firstName"/>
                    <column property="lastName"/>
                    <column property="email"/>
                    <column property="rewardPoints"/>
                </columns>
            </dataGrid>
        </vbox>
    </content>
</fragment>

API фрагментов

  • getFragmentData() - возвращает объект FragmentData, определяющий методы взаимодействия с компонентами данных фрагмента.

  • getFragmentActions() - возвращает объект FragmentActions, определяющий методы взаимодействия с действиями фрагмента.

  • getParentController() - возвращает родительский объект FragmentOwner. Это может быть View или Fragment.

  • findInnerComponent() / getInnerComponent() - возвращают внутренний компонент с указанным идентификатором. Эти методы ищут только среди компонентов, добавленных через XML-дескриптор.

События фрагментов

  • ReadyEvent - событие, которое посылается после создания фрагмента и всех его декларативно определенных внутренних компонентов, и полной их инициализации. В обработчике этого события можно выполнить окончательную настройку фрагмента и его внутренних компонентов. Например:

    CustomerListFragment.java
    @FragmentDescriptor("customer-list-fragment.xml")
    public class CustomerListFragment extends Fragment<VerticalLayout> {
    
        @Subscribe
        public void onReady(final ReadyEvent event) {
            getFragmentData().loadAll(); (1)
        }
    }
    1 Вызывает метод load() всех загрузчиков фрагмента, включая предоставленные.

Автоматическое связывание

Аналогично экранам, фрагменты поддерживают инжекцию компонентов, определенных в XML, и вызов аннотированных методов обработчика:

@ViewComponent
public JmixButton button; (1)

@ViewComponent
public CollectionContainer<Customer> collectionDc; (2)

@Subscribe
public void onReady(ReadyEvent event) { (3)
    // ...
}

@Subscribe(value = "button", subject = "clickListener")
public void onButtonClick(ClickEvent<JmixButton> event) { (4)
    // ...
}

@Install(to = "collectionDl", target = Target.DATA_LOADER)
public List<Customer> collectionDlLoadDelegate(LoadContext<Customer> loadContext) { (5)
    return loadCustomers(loadContext);
}

@Supply(to = "dataGrid.name", subject = "renderer")
public Renderer<Customer> dataGridNameRenderer() { (6)
    return createRenderer();
}
1 Инжектирует UI-компонент.
2 Инжектирует контейнер данных.
3 Подписывается на событие ReadyEvent фрагмента.
4 Подписывается на событие ClickEvent кнопки.
5 Устанавливает делегат загрузки.
6 Предоставляет Renderer для таблицы данных.

В дополнение к этому, можно подписаться на события включающего экрана, определив target = Target.HOST_CONTROLLER в аннотации @Subscribe:

@Subscribe(target = Target.HOST_CONTROLLER)
public void onHostInit(View.InitEvent event) {
    // ...
}

@Subscribe(target = Target.HOST_CONTROLLER)
public void onHostBeforeShow(View.BeforeShowEvent event) {
    // ...
}

@Subscribe(target = Target.HOST_CONTROLLER)
public void onHostReady(View.ReadyEvent event) {
    // ...
}