Фрагменты

Существует два основных подхода в использовании фрагментов.
Первый способ основан на замещении родительского контейнера. Создаётся стандартная разметка и в том месте, где будут использоваться фрагменты, размещается контейнер, например, FrameLayout. В коде контейнер замещается фрагментом. При использовании подобного сценария в разметке не используется тег fragment, так как его нельзя менять динамически. Также вам придётся обновлять ActionBar, если он зависит от фрагмента. Здесь показан такой пример.
Второй подход является наиболее гибким и в целом предпочтительным способом использования фрагментов. Активность проверяет в каком режиме (свои размеры) он запущен и использует разную разметку из ресурсов. Графически это выглядит следующим образом.


Основные классы
Сами фрагменты наследуются от androidx.fragment.app.Fragment. Существует подклассы фрагментов: ListFragment, DialogFragment, PreferenceFragment, WebViewFragment и др. Не исключено, что число классов будет увеличиваться, например, появился ещё один класс MapFragment.
Для взаимодействия между фрагментами используется класс android.app.FragmentManager — специальный менеджер по фрагментам.
Как в любом офисе, спецманагер не делает работу своими руками, а использует помощников. Например, для транзакций (добавление, удаление, замена) используется класс-помощник android.app.FragmentTransaction.
Для сравнения приведу названия классов из библиотеки совместимости:
- android.support.v4.app.FragmentActivity
- android.support.v4.app.Fragment
- android.support.v4.app.FragmentManager
- android.support.v4.app.FragmentTransaction
Как видите, разница в одном классе, который я привёл первым. Он используется вместо стандартного Activity, чтобы система поняла, что придётся работать с фрагментами. На данный момент студия создаёт проект на основе ActionBarActivity, который является подклассом FragmentActivity.
В одном приложении нельзя использовать новые фрагменты и фрагменты из библиотеки совместимости.
В 2018 году Гугл объявила фрагменты из пакета androd.app устаревшими. Заменяйте везде на версию из библиотеки совместимости. В 2020 году уже используют пакет androidx.fragment.app.
В версии Support Library 27.1.0 появились новые методы requireActivity() и requireContext(), которые пригодятся при написании кода, когда требуется наличие активности и нужно избежать ошибки на null.
Общий алгоритм работы с фрагментами будет следующим:
У каждого фрагмента должен быть свой класс. Класс наследуется от класса Fragment или схожих классов, о которых говорилось выше. Это похоже на создание новой активности или нового компонента.
Также, как в активности, вы создаёте различные методы типа onCreate() и т.д. Если фрагмент имеет разметку, то используется метод onCreateView() — считайте его аналогом метода setContentView(), в котором вы подключали разметку активности. При этом метод onCreateView() возвращает объект View, который является корневым элементом разметки фрагмента.
Разметку для фрагмента можно создать программно или декларативно через XML.
Создание разметки для фрагмента ничем не отличается от создания разметки для активности. Вот отрывок кода из метода onCreateView():
Глядя на этот код, вы должные понять, что фрагмент использует разметку из файла res/layout/first_fragment.xml, которая содержит кнопку с идентификатором android:id=»@+id/button_first». Здесь также прослеживается сходство с подключением компонентов в активности. Обратите внимание, что перед методом findViewById() используется view, так как этот метод относится к компоненту, а не к активности, как мы обычно делали в программах, когда просто опускали имя активности. Т.е. в нашем случае мы ищем ссылку на кнопку не среди разметки активности, а внутри разметки самого фрагмента.
Нужно помнить, что в методе inflate() последний параметр должен иметь значение false в большинстве случаев.
FragmentManager
Класс FragmentManager имеет два метода, позволяющих найти фрагмент, который связан с активностью:
findFragmentById(int id) Находит фрагмент по идентификатору findFragmentByTag(String tag) Находит фрагмент по заданному тегу
Методы транзакции
Мы уже использовали некоторые методы класса FragmentTransaction. Познакомимся с ними поближе
add() Добавляет фрагмент к активности remove() Удаляет фрагмент из активности replace() Заменяет один фрагмент на другой hide() Прячет фрагмент (делает невидимым на экране) show() Выводит скрытый фрагмент на экран detach() (API 13) Отсоединяет фрагмент от графического интерфейса, но экземпляр класса сохраняется attach() (API 13) Присоединяет фрагмент, который был отсоединён методом detach()
Методы remove(), replace(), detach(), attach() не применимы к статичным фрагментам.
Перед началом транзакции нужно получить экземпляр FragmentTransaction через метод FragmentManager.beginTransaction(). Далее вызываются различные методы для управления фрагментами.
В конце любой транзакции, которая может состоять из цепочки вышеперечисленных методов, следует вызвать метод commit().
Аргументы фрагмента
Фрагменты должны сохранять свою модульность и не должны общаться друг с другом напрямую. Если один фрагмент хочет докопаться до другого, он должен сообщить об этом своему менеджеру активности, а он уже передаст просьбу другому фрагменту. И наоборот. Это сделано специально для того, чтобы было понятно, что менеджер тут главный и он не зря зарплату получает. Есть три основных способа общения фрагмента с активностью.
- Активность может создать фрагмент и установить аргументы для него
- Активность может вызвать методы экземпляра фрагмента
- Фрагмент может реализовать интерфейс, который будет использован в активности в виде слушателя
Фрагмент должен иметь только один пустой конструктор без аргументов. Но можно создать статический newInstance с аргументами через метод setArguments().
Доступ к аргументам можно получить в методе onCreate() фрагмента:
Динамически загружаем фрагмент в активность.
Если активность должна выполнить какую-то операцию в фрагменте, то самый простой способ — задать нужный метод в фрагменте и вызвать данный метод через экземпляр фрагмента.
Вызываем метод в активности:
Если фрагмент должен сообщить о своих действиях активности, то следует реализовать интерфейс.
Управление стеком фрагментов
Фрагменты, как и активности, могут управляться кнопкой Back. Вы можете добавить несколько фрагментов, а потом через кнопку Back вернуться к первому фрагменту. Если в стеке не останется ни одного фрагмента, то следующее нажатие кнопки закроет активность.
Чтобы добавить транзакцию в стек, вызовите метод FragmentTransaction.addToBackStack(String) перед завершением транзакции (commit). Строковый аргумент — опциональное имя для идентификации стека или null. Класс FragmentManager имеет метод popBackStack(), возвращающий предыдущее состояние стека по этому имени.
Если вы вызовете метод addToBackStack() при удалении или замещении фрагмента, то будут вызваны методы фрагмента onPause(), onStop(), onDestroyView().
Когда пользователь нажимает на кнопку возврата, то вызываются методы фрагмента onCreateView(), onActivityCreated(), onStart() и onResume().
Рассмотрим пример реагирования на кнопку Back в фрагменте без использования стека. Активность имеет метод onBackPressed(), который реагирует на нажатие кнопки. Мы можем в этом методе сослаться на нужный фрагмент и вызвать метод фрагмента.
Теперь в классе фрагмента прописываем метод с нужным кодом.
Более желательным вариантом является использование интерфейсов. В некоторых примерах с фрагментами такой приём используется.
Интеграция Action Bar/Options Menu
Фрагменты могут добавлять свои элементы в панель действий или меню активности. Сначала вы должны вызвать метод Fragment.setHasOptionsMenu() в методе фрагмента onCreate(). Затем нужно задать настройки для методов фрагмента onCreateOptionsMenu() и onOptionsItemSelected(), а также при необходимости для методов onPrepareOptionsMenu(), onOptionsMenuClosed(), onDestroyOptionsMenu(). Работа методов фрагмента ничем не отличается от аналогичных методов для активности.
В активности, которая содержит фрагмент, данные методы автоматически сработают.
Если активность содержит собственные элементы панели действий или меню, то следует позаботиться, чтобы они не мешали вызовам методам фрагментов.
Код для активности:
Код для фрагмента:
Связь между фрагментом и активностью
Экземпляр фрагмента связан с активностью. Активность может вызывать методы фрагмента через ссылку на объект фрагмента. Доступ к фрагменту можно получить через методы findFragmentById() или findFragmentByTag().
1.2: Fragment lifecycle and communications
Like an Activity , a Fragment has its own lifecycle. Understanding the relationship between Activity and Fragment lifecycles helps you design fragments that can save and restore variables and communicate with activities.
An Activity that hosts a Fragment can send information to that Fragment , and receive information from that Fragment . This chapter describes the mechanisms for passing data and how to manage the Fragment lifecycle within an Activity .
Understanding the Fragment lifecycle
Using a Fragment lifecycle is a lot like using an Activity lifecycle (see The Activity Lifecycle for details). Within the Fragment lifecycle callback methods, you can declare how your Fragment behaves when it is in a certain state, such as active, paused, or stopped.
Fragment lifecycle states
The Fragment is added by an Activity (which acts as the host of the Fragment ). Once added, the Fragment goes through three states, as shown in the figure below:

- Active (or resumed)
- Paused
- Stopped
How the Activity state affects the Fragment
Because a Fragment is always hosted by an Activity , the Fragment lifecycle is directly affected by the host Activity lifecycle. For example, when the Activity is paused, so are all Fragments in it, and when the Activity is destroyed, so are all Fragments .
Each lifecycle callback for the Activity results in a similar callback for each Fragment , as shown in the following table. For example, when the Activity receives onPause() , it triggers a Fragment onPause() for each Fragment in the Activity .
| Activity State | Fragment Callbacks Triggered | Fragment Lifecycle |
| Created | onAttach(), onCreate(), onCreateView(), onActivityCreated() | Fragment is added and its layout is inflated. |
| Started | onStart() | Fragment is active and visible. |
| Resumed | onResume() | Fragment is active and ready for user interaction. |
| Paused | onPause() | Fragment is paused because the Activity is paused. |
| Stopped | onStop() | Fragment is stopped and no longer visible. |
| Destroyed | onDestroyView(), onDestroy(), onDetach() | Fragment is destroyed. |
As with an Activity , you can save the variable assignments in a Fragment . Because data in a Fragment is usually relevant to the Activity that hosts it, your Activity code can use a callback to retrieve data from the Fragment , and then restore that data when recreating the Fragment . You learn more about communicating between an Activity and a Fragment later in this chapter.
Tip: For more information about the Activity lifecycle and saving state, see The Activity Lifecycle.
Using Fragment lifecycle callbacks
As you would with Activity lifecycle methods, you can override Fragment lifecycle methods to perform important tasks when the Fragment is in certain states. Most apps should implement at least the following methods for every Fragment :
-
: Initialize essential components and variables of the Fragment in this callback. The system calls this method when the Fragment is created. Anything initialized in onCreate() is preserved if the Fragment is paused and resumed. : Inflate the XML layout for the Fragment in this callback. The system calls this method to draw the Fragment UI for the first time. As a result, the Fragment is visible in the Activity . To draw a UI for your Fragment , you must return the root View of your Fragment layout. Return null if the Fragment does not have a UI.
onPause() : Save any data and states that need to survive beyond the destruction of the Fragment . The system calls this method if any of the following occurs:
- The user navigates backward.
- The Fragment is replaced or removed, or another operation is modifying the Fragment .
- The host Activity is paused.
A paused Fragment is still alive (all state and member information is retained by the system), but it will be destroyed if the Activity is destroyed. If the user presses the Back button and the Fragment is returned from the back stack, the lifecycle resumes with the onCreateView() callback.
The Fragment class has other useful lifecycle callbacks:
-
: Called by the Activity to resume a Fragment that is visible to the user and actively running. : Called when a Fragment is first attached to a host Activity . Use this method to check if the Activity has implemented the required listener callback for the Fragment (if a listener interface was defined in the Fragment ). After this method, onCreate() is called. : Called when the Activity onCreate() method has returned. Use it to do final initialization, such as retrieving views or restoring state. It is also useful for a Fragment that uses setRetainInstance() to retain its instance, as this callback tells the Fragment when it is fully associated with the new Activity instance. The onActivityCreated() method is called after onCreateView() and before onViewStateRestored() . : Called when the View previously created by onCreateView() has been detached from the Fragment . This call can occur if the host Activity has stopped, or the Activity has removed the Fragment . Use it to perform some action, such as logging a message, when the Fragment is no longer visible. The next time the Fragment needs to be displayed, a new View is created. The onDestroyView() method is called after onStop() and before onDestroy() .
For a complete description of all Fragment lifecycle callbacks, see Fragment.
Using Fragment methods and the Activity context
An Activity can use methods in a Fragment by first acquiring a reference to the Fragment . Likewise, a Fragment can get a reference to its hosting Activity to access resources, such as a View .
Using the Activity context in a Fragment
When a Fragment is in the active or resumed state, it can get a reference to its hosting Activity instance using getActivity() . It can also perform tasks such as finding a View in the Activity layout:
Note that if you call getActivity() when the Fragment is not attached to an Activity , getActivity() returns null .
Using the Fragment methods in the host Activity
Likewise, your Activity can call methods in the Fragment by acquiring a reference to the Fragment from FragmentManager , using findFragmentById() . For example, to call the getSomeData() method in the Fragment , acquire a reference first:
Adding the Fragment to the back stack
A significant difference between an Activity and a Fragment is how activities and fragments use their respective back stacks, so that the user can navigate back with the Back button (as discussed in Tasks and Back Stack).
- For an Activity , the system automatically maintains a back stack of activities.
- For a Fragment , the hosting Activity maintains a back stack, and you have to explicitly add a Fragment to that back stack by calling addToBackStack() during any transaction that adds the Fragment .
Keep in mind that when your app replaces or removes a Fragment , it's often appropriate to allow the user to navigate backward and "undo" the change. To allow the user to navigate backward through Fragment transactions, call addToBackStack() before you commit the FragmentTransaction :
Tip: The addToBackStack() method takes an optional string parameter that specifies a unique name for the transaction. Specify null because the name isn't needed unless you plan to perform advanced Fragment operations using the FragmentManager.BackStackEntry interface.
When removing a Fragment , remember that the hosting Activity maintains a back stack for the Fragment (if you add the transaction to it, as described above). However, the Fragment is not destroyed. If the user navigates back to restore the Fragment , it restarts.
Communicating between a Fragment and an Activity
The Activity that hosts a Fragment can send information to that Fragment , and receive information from that Fragment , as described in "Sending data from a fragment to its host activity" in this chapter.

However, a Fragment can't communicate directly with another Fragment . All Fragment -to- Fragment communication is done through the Activity that hosts them. One Fragment communicates with the Activity , and the Activity communicates to the other Fragment .
Sending data to a Fragment from an Activity
To send data to a Fragment from an Activity , set a Bundle and use the Fragment method setArguments(Bundle) to supply the construction arguments for the Fragment .
For example, if the user previously made a choice in the Fragment , and you want to send that choice back to the Fragment when starting it from the Activity , you would create the arguments Bundle and insert the String value of the choice into the Bundle mapping for the key ( "choice" ).
The best practice for initializing the data in a Fragment is to perform this initialization in the Fragment in a factory method. As you learned in the lesson on fragments, you can create the Fragment instance with a newinstance() method in the Fragment itself:
Then instantiate the Fragment in an Activity by calling the newInstance() method in the Fragment :
Tip: If you choose New > Fragment > Fragment Blank to add a Fragment to your Android Studio project, and you select the Include fragment factory methods option, Android Studio automatically adds a newinstance() method to the Fragment as a factory method to set arguments for the Fragment when the Fragment is called by the Activity .
For example, to open the Fragment with the user's previously selected choice, all you need to do in the Activity is to provide the preselected choice as an argument for the newInstance() method when instantiating the Fragment in the Activity :
The newInstance() factory method in the Fragment can use a Bundle and the setArguments(Bundle) method to set the arguments before returning the Fragment :
The following shows how you would use getSupportFragmentManager() to get an instance of FragmentManager , create the fragmentTransaction , acquire the Fragment using the layout resource, and then create the Bundle .
The SimpleFragment newInstance() method receives the value of the mRadioButtonChoice argument in choice :
Before you draw the View for the Fragment , retrieve the passed-in arguments from the Bundle . To do this, use getArguments() in the Fragment onCreate() or onCreateView() callback, and if mRadioButtonChoice is not NONE , check the radio button for the choice:
Sending data from a Fragment to its host Activity
To have a Fragment communicate to its host Activity , follow these steps in the Fragment :
- Define a listener interface, with one or more callback methods to communicate with the Activity .
- Override the onAttach() lifecycle method to make sure the host Activity implements the interface.
- Call the interface callback method to pass data as a parameter.
In the host Activity , follow these steps:
- Implement the interface defined in the Fragment . (All the Activity classes that use the Fragment have to implement the interface.)
- Implement the Fragment callback method(s) to retrieve the data.
The following is an example of defining the OnFragmentInteractionListener interface in the Fragment , including the onRadioButtonChoice() callback to communicate to the host Activity . The Activity must implement this interface. The onAttach() method gets a reference to the listener if the Activity implemented this interface; if not, this method throws an exception:
The following shows how the Fragment uses the onCheckedChanged() listener for checked radio buttons in the Fragment , and uses the onRadioButtonChoice() callback to provide data to the host Activity .
To use the Fragment callback method(s) to retrieve data, the Activity must implement the interface defined in the Fragment class:
The Activity can then use the onRadioButtonChoice() callback to get the data.
Some data in a Fragment is may be relevant to the Activity that hosts it. Your Activity code can use a callback to retrieve relevant data from the Fragment . The Activity can then send that data to the Fragment when recreating the Fragment .
How to close the current fragment by using Button like the back button?
I have try to close the current fragment by using Imagebutton.
I am in Fragment-A and it will turn to the Fragment-B when I click the button.
And when I click the button at Fragment-B , it will turn to the Fragment-C and close the Fragment-B.
If I click the back button at Fragment-C , it will back to the Fragment-A.
The code I have try is like the following
When I click the back button at fragment-B , it turn to the Fragment-C.
But when I click the back button on Fragment-C , it doesn’t back to the Fragment-A. It back to the empty background. If I want to back to Fragment-A , I have to click the back button once again.
Как закрыть текущий фрагмент с помощью кнопки, такой как кнопка «Назад»?
Я попытался закрыть текущий фрагмент с помощью Imagebutton.
Я нахожусь во фрагменте-A, и он превратится в Фрагмент-B, Когда я нажму кнопку.
и когда я нажму кнопку на фрагменте-B, он повернется к фрагменту-C и закроет фрагмент-B.
если я нажму кнопку «назад» на фрагменте-C, он вернется к фрагменту-A.
код я попробовать, как следующее
когда я нажимаю кнопку Назад при фрагменте-B он обращается к фрагменту-C.
но когда я нажимаю кнопку «назад» на фрагменте-C, он не возвращается к фрагменту-A. Он возвращается на пустой фон. Если я хочу вернуться к фрагменту-A, я должен снова нажать кнопку «Назад».
таким образом, кажется, не закрывает текущий фрагмент полным.
Как закончить текущий фрагмент, такой как кнопка «Назад» Android ?
11 ответов
из фрагмента A, чтобы перейти к B, замените A на B и используйте addToBackstack() до commit() .
Теперь из фрагмента B, чтобы перейти к C, сначала используйте popBackStackImmediate() , это вернет A. теперь замените A на C, как и первая транзакция.
изменить код getActivity().getFragmentManager().beginTransaction().remove(this).commit();