Афоризм
Зачем мне талия? Я замужем теперь.
Наталья Резник
Последние статьи

 • Активности Android
Многоэкранные Android приложения
 • Fragment dynamic
Динамическая загрузка фрагментов в Android
 • Fragment lifecycle
Жизненный цикл Fragment'ов в Android
 • Fragment example
Пример Fragment'ов в Android
 • Data Binding
Описание и пример Data Binding
 • Пример MVVM
Пример использования MVVM в Android
 • Компонент TreeTable
Описание компонента TreeTable для Swing
 • Пример TreeTable
Пример использования TreeTable
 • Хранилища Android
Внутренние и внешние хранилища данных
 • Пример SQLite
Пример использования SQLite в Android
 • WebSocket
Описание и пример реализации WebSocket
 • Визуальные компоненты
Улучшен компонент выбора даты из календаря
 • Анимация jQuery
Описание и примеры анимации элементов DOM
 • APK-файл Android
Создание apk-файла для android устройств, .dex файлы
 • платформа JaBricks
Платформа OSGi-приложения JaBricks
Поддержка проекта

Если Вам сайт понравился и помог, то будем признательны за Ваш «посильный» вклад в его поддержку и развитие
 • Yandex.Деньги
  410013796724260

 • Webmoney
  R335386147728
  Z369087728698

Динамическая загрузка фрагментов

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

Система позволяет динамически добавлять, удалять и заменять фрагменты друг другом. Такие манипуляции фрагментами, с точки зрения программирования, подобны транзакциям SQL. При этом все эти манипуляции (транзакции) можно сохранять в стеке BackStack и кнопкой «Назад» (стрелка <) возвращаться назад.

Интерфейс примера

Динамическую загрузку фрагментов рассмотрим на примере. Чтобы было более наглядно, сразу же представлю скриншот примера. В верхней части активности размещаются флажок BackStack и 3 кнопки. Ниже кнопок размещается контейнер (FrameLayout) для фрагментов. Количество фрагментов соответствует количеству кнопок; интерфейсы фрагментов одинаковые (метка сверху и 2 кнопки внизу). Отличия фрагментов связаны с фоновыми цветами, которые сответствуют цвету связанных с ними кнопками активности. На представленном скриншоте в активность включен первый фрагмент после нажатия соответствующей кнопки.

Пример динамических фрагментов android

Логика примера

При нажатии на кнопку активности в контейнер загружается связанный с кнопкой фрагмент. Если в активность уже был загружен фрагмент на предыдущем шаге, то новый загружаемый фрагмент заменит предыдущий. При установленном флаге BackStack транзакция попадает в стек, которую можно будет откатить.

При нажатии на кнопку Закрыть соответствующий фрагмент будет выгружен из активности. При нажатии на кнопку Popup фрагмент заменяется на фрагмент загруженный в стек (back stack) на предыдущем шаге при установленном флаге BackStack. Назад можно вернуться также при нажатии соответствующей кнопки (стрелка <) смартфона, либо виртуального устройства (Назад) при работе примера в среде Android Studio.

Описание примера

Ниже на скриншоте представлен проект/модуль fragment_02, включающий :

  • активность MainActivity;
  • однотипные классы фрагментов Fragment1, Fragment2, Fragment2;
  • интерфейс onFragmentClose;
  • ресурсные файлы
    • интерфейсный шаблон активности activity_main.xml;
    • однотипные интерфейсные шаблоны фрагментов fragment1.xml, fragment2.xml, fragment3.xml;
    • ресурсный файл текстовых строк strings.xml.
    • ресурсный файл цветов colors.xml;
Пример динамических фрагментов android

Подключение библиотеки AndroidX

Как было отмечено в описании, чтобы иметь возможность использования всей функциональности фрагментов, необходимо подключить к модулю библиотеку Gradle Scripts/build.gradle. Для этого в секции dependencies внесем следующий код :

...
dependencies {
    implementation "androidx.fragment:fragment:1.4.1"
    ...
}

Ресурсный файл strings.xml

В ресурсном файле res/values/strings.xml определим набор строк, которые будут использоваться в заголовках примера, метках и кнопках.

Листинг strings.xml

<resources>
    <string name="app_name">Фрагменты. Динамика</string>
    <string name="btn_close">Закрыть</string>
    <string name="btn_popup">Popup</string>
    <string name="frm1">Фрагмент 1</string>
    <string name="frm2">Фрагмент 2</string>
    <string name="frm3">Фрагмент 3</string>
    <string name="check">BackStack</string>
</resources>

Ресурсный файл colors.xml

В ресурсном файле res/values/colors.xml определены цветовые константы для представления фоновых цветов кнопок и фрагментов.

Листинг colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
    <color name="magenta">#FFFF00FF</color>
    <color name="orange">#FFFFA500</color>
</resources>

Интерфейс OnFragmentClose

Интерфейс OnFragmentClose необходим для реализации методов закрытия или смены фрагментов. Активность, имплементирующая (implements) данный интерфейс, должна определить методы closeFragment и popupFragment. Метод closeFragment в качестве параметра получает реализацию фрагмента (класс Fragment). При нажатии на кнопку фрагмента Закрыть вызывается метод активности closeFragment, при нажатии на кнопку PopuppopupFragment.

Листинг интерфейса

package com.example.fragment_02;

import androidx.fragment.app.Fragment;

public interface OnFragmentClose {
    void closeFragment(final Fragment frg);
    void popupFragment();
}

Интерфейсный шаблон фрагмента fragment1.xml

В интерфейсные шаблоны фрагментов, отличающиеся только фоновыми цветами, включены метка и 2 кнопки. Цвет фона определен тегом android:background. Ниже представлен интерфейсный шаблон первого фрагмента (fragment1.xml).

Листинг fragment1.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/magenta"
    android:orientation="vertical">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/frm1"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <Button
        android:id="@+id/popup"
        android:text="@string/btn_popup"
        android:layout_width="120dp"
        android:layout_height="48dp"
        android:layout_marginEnd="144dp"
        android:layout_marginBottom="48dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />
    <Button
        android:id="@+id/button"
        android:text="@string/btn_close"
        android:layout_width="120dp"
        android:layout_height="48dp"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="48dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Класс фрагмента Fragment1.java

Классы фрагментов также, как и их шаблоны, имеют незначительные отличия, связанные с выбором ресурсного файла (R.layout.fragment1) и с вызовом метода активности, которому нужно передать ссылку на текущий фрагмент. Ниже представлен класс первого фрагмента (Fragment1.java).

В методе onAttach определяется eventListener, т.е. ссылка на родителя (активность). В методе onCreateView к кнопкам подключаются обработчики событий, в которых вызываются соответствующие методы активности.

Листинг Fragment1.java

package com.example.fragment_02;

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

import androidx.fragment.app.Fragment;
import androidx.appcompat.app.AppCompatActivity;

public class Fragment1 extends Fragment
{
    OnFragmentClose eventListener = null;
    //-----------------------------------------------------
    @Override
    public void onAttach(Context context)
    {
        super.onAttach(context);
        AppCompatActivity activity = null;
        if (context instanceof AppCompatActivity){
            activity = (AppCompatActivity) context;
        }
        try {
            eventListener = (OnFragmentClose) activity;
        } catch (ClassCastException e) {}
    }
    //-----------------------------------------------------
    @Override
    public View onCreateView(LayoutInflater inflater,
                             ViewGroup container,
                             Bundle savedInstanceState)
    {
        View view=inflater.inflate(R.layout.fragment1,null);

        Button btn1 = view.findViewById(R.id.popup );
        Button btn2 = view.findViewById(R.id.button);

        btn1.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v) {
                eventListener.popupFragment();
            }
        });
        btn2.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v) {
                eventListener.closeFragment(Fragment1.this);
            }
        });
        return view;
    }
}

В предыдущем примере статического и динамического включения фрагментов в активность описание интерфейса было определено непосредственно в классе одного из фрагментов.

Шаблон активности main_active.xml

Контент интерфейсного шаблона активности main_active.xml включает флажок BackStack (CheckBox), три кнопки Button и контейнер для фрагментов типа FrameLayout. Кнопки подключены к методу onClick активности.

Листинг main_active.xml


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <CheckBox
        android:id="@+id/checkBox"
        android:layout_width="91dp"
        android:layout_height="22dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="@string/check"
        android:textSize="12sp"
        android:textStyle="bold"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <Button
        android:id="@+id/btn1"
        android:layout_width="110dp"
        android:layout_height="48dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        android:text="@string/frm1"
        android:textSize="11sp"
        android:textAllCaps="false"
        android:onClick="onClick"
        android:backgroundTint="@color/magenta"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/checkBox" />
    <Button
        android:id="@+id/btn2"
        android:layout_width="110dp"
        android:layout_height="48dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="8dp"
        android:text="@string/frm2"
        android:textSize="11sp"
        android:textAllCaps="false"
        android:onClick="onClick"
        android:backgroundTint="@color/orange"
        app:layout_constraintStart_toEndOf="@+id/btn1"
        app:layout_constraintTop_toBottomOf="@+id/checkBox" />
    <Button
      android:id="@+id/btn3"
      android:layout_width="110dp"
      android:layout_height="48dp"
      android:layout_marginStart="16dp"
      android:layout_marginTop="8dp"
      android:text="@string/frm3"
      android:textSize="11sp"
      android:textAllCaps="false"
      android:onClick="onClick"
      android:backgroundTint="@color/teal_200"
      app:layout_constraintStart_toEndOf="@+id/btn2"
      app:layout_constraintTop_toBottomOf="@+id/checkBox" />
    <FrameLayout
      android:id="@+id/frgContainer"
      android:layout_width="403dp"
      android:layout_height="486dp"
      android:layout_marginStart="32dp"
      android:layout_marginTop="24dp"
      android:layout_marginEnd="32dp"
      android:layout_marginBottom="24dp"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf=
	                        "@+id/btn2"></FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Класс активности MainActivity.java

Класс активности MainActivity включает следующие методы :

МетодОписание
onCreate Метод жизненного цикла активности.
В методе создаются интерфейс, фрагменты и определяется флаг использования стека chkStack.
onClick Обработчик нажатий кнопок.
В обработчике используется флаг flag, фиксирующий загрузку фрагмента в контейнер активности. Если в контейнере ещё нет фрагмента, то выполняется его загрузка методом add. При загруженном в контейнер фрагменте выполняется его замена методом replace.
closeFragment Реализованный метод интерфейса OnFragmentClose.
В методе определяется идентификатор фрагмента idx, стартует начало транзакции (beginTransaction), удаляется из контейнера активности фрагмент и транзакция завершается commit'ом.
popupFragment Реализованный метод интерфейса OnFragmentClose.
В методе стартует начало транакции (beginTransaction), возвращается из стека предыдущий фрагмент (popBackStackImmediate) и транзакция завершается commit'ом.

Листинг MainActivity.java

package com.example.fragment_02;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;

import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity
                          implements OnFragmentClose
{
    private  Fragment1           frg1;
    private  Fragment2           frg2;
    private  Fragment3           frg3;
    private  CheckBox            chkStack;

    private  FragmentManager     fm;
    private  FragmentTransaction ft;

    private  boolean             flag = false;
    //-----------------------------------------------------
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        frg1 = new Fragment1();
        frg2 = new Fragment2();
        frg3 = new Fragment3();

        chkStack = (CheckBox)findViewById(R.id.checkBox);
    }
    //-----------------------------------------------------
    public void onClick(View v)
    {
        fm = getSupportFragmentManager();
        ft = fm.beginTransaction();
        switch (v.getId()) {
            case R.id.btn1:
              if (!flag) {
                  ft.add(R.id.frgContainer, frg1);
                  flag = true;
              } else
                  ft.replace(R.id.frgContainer,frg1);
              break;
          case R.id.btn2:
              if (!flag) {
                  ft.add(R.id.frgContainer, frg2);
                  flag = true;
              } else
                  ft.replace(R.id.frgContainer,frg2);
              break;
            case R.id.btn3:
              if (!flag) {
                  ft.add(R.id.frgContainer, frg3);
                  flag = true;
              } else
                  ft.replace(R.id.frgContainer,frg3);
              break;
            default:
                break;
        }
        if (chkStack.isChecked())
            ft.addToBackStack(null);
        ft.commit();
    }
    //-----------------------------------------------------
    @Override
    public void closeFragment(Fragment frg)
    {
        int idx = (frg.getId() == frg1.getId()) ? 1 :
                  (frg.getId() == frg2.getId()) ? 2 :
                  (frg.getId() == frg3.getId()) ? 3 : -1;

        fm = getSupportFragmentManager();
        ft = fm.beginTransaction();

        switch (idx) {
            case 1:
                ft.remove(frg1);
                flag = false;
                break;
            case 2:
                ft.remove(frg2);
                flag = false;
                break;
            case 3:
                ft.remove(frg3);
                flag = false;
                break;
            default:
                break;
        }
        ft.commit();
    }
    //-----------------------------------------------------
    @Override
    public void popupFragment()
    {
        fm = getSupportFragmentManager();
        ft = fm.beginTransaction();

        fm.popBackStackImmediate();

        ft.commit();
    }
}

Старт примера

Сразе же необходимо отметить, что при разработке примера основной задачей ставилась проверка работоспособности классов и методов динамического размещения и удаления фрагментов, а также возвращения фрагментов (транзакции) из стеков. Поэтому рассмотрим 3 подхода к выполнению данного примера.

Размещение и удаление фрагмента
Для проверки данной логики необходимо поочередно загружать фрагменты в контейнер активности и после этого удалять их из активности. Загрузка фрагментов выполняется кнопками активности. Удаление фрагментов из контейнера активности выполняется нажатием на кнопки Закрыть.

Размещение и замена фрагмента
Для проверки логики замещения одного фрагмента другим необходимо поочередно нажимать на кнопки активности.

Восстановление фрагмента из стека
Чтобы использовать концепцию возврата фрагмента (транзакции) из стека необходимо в первую очередь установить флажок BackStack и поочередно нажать кнопки активности. После того, как в контейнер будет загружен последний фрагмент нажатием на кнопки фрагментов Popup можно возвращаться в предыдущее состояние.

  Рейтинг@Mail.ru