410013796724260
• Webmoney
R335386147728
Z369087728698
Вернуться к «Разработчикам Android»
Динамическая загрузка фрагментовВ одном из размещенных на сайте примеров было показано, как можно статически размещать фрагмент в шаблоне активности, а также затрагивался вопрос динамической загрузки фрагмента в интерфейс активности. В данной статье более подробно рассмотрим вопрос динамической загрузки фрагментов. Система позволяет динамически добавлять, удалять и заменять фрагменты друг другом. Такие манипуляции фрагментами, с точки зрения программирования, подобны транзакциям SQL. При этом все эти манипуляции (транзакции) можно сохранять в стеке BackStack и кнопкой «Назад» (стрелка <) возвращаться назад. Интерфейс примераДинамическую загрузку фрагментов рассмотрим на примере. Чтобы было более наглядно, сразу же представлю скриншот примера. В верхней части активности размещаются флажок BackStack и 3 кнопки. Ниже кнопок размещается контейнер (FrameLayout) для фрагментов. Количество фрагментов соответствует количеству кнопок; интерфейсы фрагментов одинаковые (метка сверху и 2 кнопки внизу). Отличия фрагментов связаны с фоновыми цветами, которые сответствуют цвету связанных с ними кнопками активности. На представленном скриншоте в активность включен первый фрагмент после нажатия соответствующей кнопки. ![]() Логика примераПри нажатии на кнопку активности в контейнер загружается связанный с кнопкой фрагмент. Если в активность уже был загружен фрагмент на предыдущем шаге, то новый загружаемый фрагмент заменит предыдущий. При установленном флаге BackStack транзакция попадает в стек, которую можно будет откатить. При нажатии на кнопку Закрыть соответствующий фрагмент будет выгружен из активности. При нажатии на кнопку Popup фрагмент заменяется на фрагмент загруженный в стек (back stack) на предыдущем шаге при установленном флаге BackStack. Назад можно вернуться также при нажатии соответствующей кнопки (стрелка <) смартфона, либо виртуального устройства (Назад) при работе примера в среде Android Studio. Описание примераНиже на скриншоте представлен проект/модуль fragment_02, включающий :
![]() Подключение библиотеки 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, при нажатии на кнопку Popup — popupFragment. Листинг интерфейса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 включает следующие методы :
Листинг 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 подхода к выполнению данного примера. Размещение и удаление фрагмента Размещение и замена фрагмента Восстановление фрагмента из стека |