- Фрагменты
- Основные классы
- FragmentManager
- Методы транзакции
- Аргументы фрагмента
- Управление стеком фрагментов
- Интеграция Action Bar/Options Menu
- Связь между фрагментом и активностью
- How to Add a Fragment the Kotlin way
- 1. Eliminating beginTransaction() and commit()
- 2. Extension functions replaces ActivityUtil
- How to Pass Data Between Fragments in Android using Kotlin
- Creating Fragments
- Passing Data Between Fragments
Фрагменты
Существует два основных подхода в использовании фрагментов.
Первый способ основан на замещении родительского контейнера. Создаётся стандартная разметка и в том месте, где будут использоваться фрагменты, размещается контейнер, например, 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().
Источник
How to Add a Fragment the Kotlin way
Before going to the Kotlin way, let’s recap how we used to add a Fragment in Java.
To avoid repeating this boilerplate and error-prone code in every Activity , we would have written a static util method in a class named ActivityUtil .
And we call this util method from Activity like this:
That’s all, nothing interesting here. Now, let’s see how we can do this better in Kotlin in two steps.
1. Eliminating beginTransaction() and commit()
How many minutes, if not hours, have you wasted debugging your application only to find out that you had missed calling commit() at the end of your Fragment transaction?
To eliminate that boilerplate code, let’s write an Extension function to the FragmentManager class which accepts a Lambda with Receiver as its argument.
Quick intro to the Kotlin features we are about to use:
Extension function is a way to add new functions (or properties) to an existing class even if the class is from a library or SDK. Inside the function we can access the public functions and properties of the class without any qualifiers as though this function is inside the class itself. (Note: Technically it’s not modifying the existing class but creating a static functions under the hood)
Higher Order Function is a function that takes functions as parameters, or returns a function. We can pass a function or return a function from a function like data.
Lambda with Receiver (Function Literals with Receiver) is the combination of above two — an Higher order function that takes an Extension function to a Receiver as its parameter. So in the lambda expression we pass as the argument, we can access the functions and properties of the Receiver as though the lambda function is inside the Receiver itself.
This is the Extension function to the FragmentManager which accepts a Lambda with Receiver as its argument, whereas the FragmentTransaction is the Receiver.
Here the parameter func is an Extension function to the FragmentTransaction and it has zero parameters and returns Unit. We invoke that function after calling the beginTransaction() and end the transaction by calling commit() .
Now to add a Fragment, we can call like this from any Activity:
Note that inside the lambda we can call the functions of the FragmentTransaction — like add or remove — without any additional qualifiers because this is an Extension function to the FragmentTransaction itself.
Using the above Extension function, we don’t have to call beginTransaction() and commit() every time we add or replace a Fragment now. We can even call multiple operations inside the inTransaction block and everything runs inside a Fragment transaction:
Update on 24th Aug, 2017: Dai suggested in the comments that we can improve the inTransaction function even more by making the lambda expression to return the FragmentTransaction :
2. Extension functions replaces ActivityUtil
Next, let’s see how the Extension functions to the AppCompatActivity is better than the Util class ActivityUtil.
We can write the following Extension functions to the AppCompatActivity — addFragment and replaceFragment :
Since these are Extension functions to the AppCompatActivity itself, we can access the supportFragmentManager directly inside the function.
Using the above extension functions, now we can add or replace a Fragment from any Activity in a single line, without any additional qualifiers:
There is no extra qualifier to call these functions and so this looks as though the Android SDK itself provides these functions.
Using Kotlin, we have fixed the ugly Android API and created a concise, more readable, and less error-prone code to add a Fragment.
This is not the end; these — Extension function, Higher order function, and Lambda with Receiver — are the building blocks of Kotlin to create a nice DSL for your project.
Leave your thoughts in the comments or reach me on Twitter .
Источник
How to Pass Data Between Fragments in Android using Kotlin
UPDATE [April 17th, 2021]: Switched from Kotlinx synthetic to View Binding. If you haven’t implement View Binding in your project yet, check this tutorial.
Passing data from a Fragment to another Fragment it’s not the same as passing data from one Activity to another. You need first to pass the data to Activity and then to the second Fragment.
If you have created your fragments and you just want to know how to pass data from one Fragment to another, skip the following step.
Creating Fragments
In this example, we have a fragment that contains an EditText, where we type a text and a Button that sends the text to the 2nd fragment and shows it in a TextView.
First, let’s go and create the layout of the fragment with the EditText and the Button (fragment_1.xml)
Next, let’s do the same for the fragment with the TextView (fragment_2.xml)
Go and add an id with a name ‘content_id’ on parent layout in the activity_main.xml file like the image below:
Now, let’s create the Kotlin class for our first fragment (Fragment1.kt) and second fragment (Fragment2.kt) like that:
Passing Data Between Fragments
When we want to pass data between fragments, we are doing it with the help of the Activity which contains these fragments.
In our example, the Activity is the MainActivity.kt.
So we are going to use the MainActivity to pass the data from the first to the second fragment using an interface.
In our example we call it Communicator:
Inside the interface there’s a method passDataCom where we pass the input text from the EditText from our first fragment.
Now, add Communicator at the top of the MainActivity and implement the methods by pressing Alt+Enter(Windows) or Command+Enter(MacOS), then Enter and OK on the new window.
Next, let’s go to Fragment1.kt and initialize our Communicator and call the method with the text from the EditText as a parameter:
Now it’s time to go back to our MainActivity and pass the value from Fragment1 to the Fragment2.
To do that, we’re taking the value and we’re adding it to a Bundle.
Then we pass the bundle data as arguments to the Fragment2
And at the end we display the new fragment by replacing the Fragment1 with the Fragment2 in the MainActivity like that:
In the code above, we gave a key name “input_txt” for our text. This will help us to retrieve the value from our 2nd fragment.
After we have sent the value from the 1st fragment, it’s time to retrieve it from our 2nd fragment and display it on the screen.
Go to our Fragment2 and paste the following code:
Here we’re getting the value with the key name, we put earlier, and we display it on a TextView.
If you have any questions, please feel free to leave a comment below
Источник