Ограничение доступа
Ролевой механизм
Загрузка модулей410013796724260
|
Вернуться к «Разработчикам 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 подхода к выполнению данного примера. Размещение и удаление фрагмента Размещение и замена фрагмента Восстановление фрагмента из стека |
