- Доступ к View внутри фрагмента
- Доступ к View внутри динамического фрагмента из активности
- res/layout/fragmentlayout.xml
- Передача данных между фрагментами
- res/layout/redfragment.xml
- res/layout/bluefragment.xml
- Android Fragment Result Listener
- Как это работает?
- Как это выглядит в коде?
- Передача данных
- Получение данных
- Parent Fragment Manger
- Тестирование
- Передача данных
- Получение данных
- Вывод
- Полный список
- Доступ к фрагменту из Activity
- Доступ к Activity из фрагмента
- Обработка в Activity события из фрагмента
Доступ к View внутри фрагмента
Так как существует два способа использования фрагментов в активности, то взаимодействие между компонентами, которые находятся внутри фрагментов немного отличается.
Доступ к View внутри динамического фрагмента из активности
Рассмотрим пример доступа к компоненту, который находится внутри динамического фрагмента из активности.
Фрагмент может иметь собственную разметку и содержать различные компоненты View: TextView, EditText и т.д. Напрямую из активности обратиться к нужному компоненту и поменять, например, текст в TextView не получится. А как же достучаться до нужного компонента? Рассмотрим простой пример.
Создадим класс фрагмента MyFragment.java:
В методе onCreateView() мы указали ресурс разметки R.layout.fragmentlayout. Давайте создадим разметку для фрагмента.
res/layout/fragmentlayout.xml
В данной разметке нас интересует вторая текстовая метка с идентификатором fragmenttext.
Создадим разметку для основной активности:
Последняя компоновка LinearLayout с идентификатором myfragment является контейнером для фрагмента, который будет его замещать. Напишем код для главной активности:
При запуске программы мы получаем экземпляр класса FragmentTransaction и добавляем фрагмент на экран вместо LinearLayout. Теперь текстовая метка фрагмента доступна для изменения — получаем ссылку на нужный компонент и устанавливаем требуемый текст.
Передача данных между фрагментами
Когда мы заменяем контейнер своим фрагментом, то он становится частью активности и получаем доступ к компонентам стандартным способом, как в примере выше. Если мы используем фрагменты как самостоятельные элементы, то доступ к компонентам происходит немного по-другому. Так как фрагменты не существуют сами по себе, а только внутри активности, то сначала нужно получить доступ к родительской активности через метод getActivity(), а затем уже можно получить доступ к нужному компоненту из фрагмента:
Создадим разметки для двух фрагментов. В одном разместим текстовое поле, а во втором кнопку и текстовую метку, в которой будем выводить текст из текстового поля первого фрагмента:
res/layout/redfragment.xml
res/layout/bluefragment.xml
В код первого фрагмента добавим только наполнение из разметки:
В класс второго фрагмента добавим код для чтения данных из первого фрагмента. Сделаем это в методе onStart():
Мы получаем ссылки на все компоненты и обрабатываем щелчок мыши. В разметку активности добавьте два созданных фрагмента и запустите пример. Введите какой-нибудь текст в текстовом поле (напоминаю, что он относится к первому фрагменту). Нажмите на кнопку, которая относится ко второму фрагменту. В текстовой метке второго фрагмента появится введённым вами текст.
Источник
Android Fragment Result Listener
В Android передача данных между фрагментами может осуществляться разными способами: передача через родительскую Activity, используя ViewModel или даже Fragments API. Fragment Target API с недавних пор получил статус Deprecated и вместо него Google рекомендует использовать Fragment result API.
Что такое Fragment result API? Это новый инструмент от Google который позволяет передавать данные между фрагментами по ключу. Для этого используется FragmentManager, который в свою очередь реализует интерфейс FragmentResultOwner. FragmentResultOwner выступает в качестве центрального хранилища для данных, которые мы передаем между фрагментами.
Как это работает?
Как упоминалось выше, наш FragmentManager реализует интерфейс FragmentResultOwner, который хранит в себе ConcurrentHashMap . Эта HashMap хранит наши Bundle-ы по строковому ключу. Как только один из фрагментов подписывается (или уже подписан) то он получает результат по тому самому ключу.
Что важно знать:
- Если какой-либо фрагмент подписывается на результат методом setResultFragmentListener() после того, как отправляющий фрагмент вызовет setFragmentResult() , то он немедленно получит результат
- Каждую связку “Key + Result (Bundle)“ фрагмент получает только 1 раз
- Фрагменты которые находятся в бек стеке получат результат только после того как перейдут в состояние STARTED
- После того как фрагмент перейдет в состояние DESTROYED мы больше не сможем подписываться на ResultListener
Как это выглядит в коде?
Передача данных
Для передачи данных в другой фрагмент нам необходимо вызвать метод:
В параметры метода мы кладем ключ, который и будет нашим идентификатором для получения данных и сам Bundle. Этот Bundle будет содержать в себе передаваемые данные.
Получение данных
Для получения данных через FragmentManager мы регистрируем наш FragmentResultListener и задаем ключ по которому мы будем получать данные. Тот самый ключ который мы указывали в методе FragmentManager.setFragmentResult()
Здесь мы видим 2 аргумента: key: String и bundle: Bundle.
Первый — это тот самый ключ, по которому мы передаем сюда данные. Второй — Bundle, в котором лежат переданные данные.
Parent Fragment Manger
Выбор FragmentManager-а для передачи данных между фрагментами зависит от принимающего фрагмента:
- Если оба фрагмента находятся в одном и том же FragmentManager (например оба фрагмента находятся в Activity), то мы должны использовать родительский FragmentManager, который хранит в себе Activity
- Если у нас один фрагмент вложен в другой фрагмент, то для передачи данных мы используем childFragmentManager (он же родительский фрагмент для принимающего фрагмента)
Важно понимать, что наш FragmentResultListener должен находиться в общем для двух фрагментов FragmentManager-е.
Тестирование
Для тестирования отправки/получения данных через FragmentResultListener, мы можем использовать FragmentScenario API, который предоставляет нам все преимущества тестирования фрагментов в изоляции.
Передача данных
Как мы можем протестировать, что наш фрагмент корректно отправляет данные через родительский FragmentManager? Для этого нам необходимо внутри теста отправить результат и проверить, что наш FragmentResultListener получил корректные данные:
Получение данных
Для проверки корректности получения данных мы можем симулировать отправку данных, используя родительский FragmentManager. Если в отправляющем фрагменте корректно установлен FragmentResultListener мы должны получить корректные данные проверяя сам листенер или последствие их получения.
Вывод
В данный момент FragmentResultListener находится в альфе, а это значит что возможно еще будут изменения со стороны Google. Но уже сейчас видно, что это достаточно крутой инструмент, для передачи данных между фрагментами, не создавая дополнительных интерфейсов и классов. Единственным нюансом остается, пожалуй то, что не совсем понятно, как и где лучше хранить ключи где, но это не кажется таким уж большим минусом.
Для того чтоб получить возможность использовать FragmentResultListener нам нужно подключить в зависимостях версию фрагментов 1.3.0-alpha04 или новее:
Источник
Полный список
— рассмотрим взаимодействие между Activity и ее фрагментами
После размещения фрагмента, хотелось бы начать с ним взаимодействовать. Т.е. размещать View-компоненты и работать с ними, обращаться к фрагментам из Activity и наоборот. Попробуем это реализовать.
Для чистоты эксперимента будем работать с двумя фрагментами: статическим и динамическим.
Project name: P1061_FragmentActivity
Build Target: Android 4.1
Application name: FragmentActivity
Package name: ru.startandroid.develop.p1061fragmentactivity
Create Activity: MainActivity
В strings.xml добавим строки:
Создаем layout и классы для двух фрагментов.
У фрагмента нет привычного для нас метода findViewById для поиска компонентов с экрана. Поэтому вызываем этот метод для View, которое будет содержимым фрагмента. В методе onCreateView мы создаем View и сразу же находим в нем кнопку и ставим ей обработчик. Затем отдаем View системе.
Все аналогично Fragment1.
Настраиваем основное Activity.
Кнопка, компонент fragment, в который помещен Fragment1, и контейнер FrameLayout, в который потом поместим Fragment2.
Обратите внимание на атрибут tools:layout. В нем указан layout-файл, и мы можем на этапе разработки видеть, как будет выглядеть статический фрагмент, когда приложение будет запущено.
Для этого надо нажать правой кнопкой на компоненте fragment, и через пункт Fragment Layout указать нужный layout.
Здесь мы просто добавляем Fragment2 в контейнер.
Все сохраняем, запускаем приложение.
Жмем кнопку Log в первом фрагменте и смотрим лог:
Button click in Fragment1
Жмем Log во втором фрагменте:
Button click in Fragment2
Все ок. Компоненты в фрагментах нашлись и обработчики среагировали на нажатия.
Атрибут onClick, который мы привыкли использовать для кнопки, здесь не прокатит. Указанный в этом атрибуте метод, будет вызван в Activity, а не в фрагменте.
Доступ к фрагменту из Activity
Разберемся, как получить доступ к фрагменту из Activity. Для этого у FragmentManager есть метод findFragmentById, который на вход принимает id компонента fragment (если фрагмент статический) или id контейнера (если динамический).
У нас в main.xml есть кнопка btnFind, вызывающая метод onClick при нажатии. Дорисуем в MainActivity.java метод onClick:
Используем метод findFragmentById. В первом случае на вход передаем id компонента fragment, т.к. Fragment1 у нас размещен именно так. При поиске Fragment2 указываем id контейнера, в который этот фрагмент был помещен. В результате метод findFragmentById возвращает нам объект Fragment.
Далее мы получаем доступ к его View с помощью метода getView, находим в нем TextView и меняем текст.
Все сохраняем, запускаем. Жмем кнопку Find
Тексты в фрагментах обновились. Тем самым из Activity мы достучались до фрагментов и их компонентов.
На всякий случай проговорю одну вещь из разряда «Спасибо кэп!». Если посмотреть на код MainActivity, то можно заметить, что работая с frag2 в методе onCreate и с frag2 в методе onClick мы работаем с текущим фрагментом Fragment2. Это так и есть. Оба frag2 в итоге будут ссылаться на один объект. Так что, если вы динамически добавили фрагмент, то у вас уже есть ссылка на него, и искать его через findFragmentById вам уже не надо.
Доступ к Activity из фрагмента
Теперь попробуем из фрагмента поработать с Activity. Для этого фрагмент имеет метод getActivity.
Давайте перепишем обработчик кнопки в первом фрагменте. Будем менять текст кнопки btnFind.
Получаем Activity методом getActivity, ищем в нем кнопку и меняем текст.
Сохраняем, запускаем. Жмем кнопку в первом фрагменте:
Работает. Из фрагмента мы поменяли компонент Activity.
Обработка в Activity события из фрагмента
Рассмотрим механизм, который описан в хелпе: фрагмент генерирует некое событие и ставит Activity обработчиком.
Например, в Activity есть два фрагмента. Первый – список заголовков статей. Второй – отображает содержимое статьи, выбранной в первом. Мы нажимаем на заголовок статьи в первом фрагменте и получаем содержимое во втором. В этом случае, цель первого фрагмента – передать в Activity информацию о том, что выбран заголовок. А Activity дальше уже сама решает, что делать с этой информацией. Если, например, приложение запущено на планшете в горизонтальной ориентации, то можно отобразить содержимое статьи во втором фрагменте. Если же приложение запущено на смартфоне, то экран маловат для двух фрагментов и надо запускать отдельное Activity со вторым фрагментом, чтобы отобразить статью.
Фишка тут в том, что первому фрагменту неинтересны все эти терзания Activity. Фрагмент – обособленный модуль. Его дело — проинформировать, что выбрана статья такая-то. Ему не надо искать второй фрагмент и работать с ним – это дело Activity.
Тут немного отвлекусь на небольшое лирическое отступление. Модульность, вообще, — очень важная и полезная штука. И ее надо использовать для универсальности, удобности и легкости в понимании работы своих приложений. Но уникальных рецептов, как правильно все организовать, конечно, нет. Каждый делает по-своему. Именно по этим причинам я в своих уроках даю чисто технические вещи про отдельные компоненты и не рассказываю, как организовывать и писать целое приложение. Иначе, форум бы уже ломился от сообщений, что я все делаю не так и надо по-другому, и каждый бы излагал свое видение. И была бы куча споров, где одна сторона говорит, что крокодил зеленый, а другая сторона говорит, что он нифига не зеленый, а длинный ))
Вернемся к уроку. Фрагмент должен сообщить в Activity, что выбрана статья. Для этого он будет вызывать некий метод в Activity. И как нам сообщает хелп, лучший способ тут – это использовать интерфейс, который мы опишем в фрагменте и который затем будет реализован в Activity. Схема известная и распространенная. Давайте реализуем. В нашем приложении никаких статей нет, поэтому будем просто передавать произвольную строку из второго фрагмента в Activity. А Activity уже будет отображать эту строку в первом фрагменте.
Описываем интерфейс onSomeEventListener. В нем метод someEvent, который на вход получает строку. Этот интерфейс будет реализовывать Activity.
В методе onAttach мы на вход получаем Activity, к которому присоединен фрагмент. Мы пытаемся привести это Activity к типу интерфейса onSomeEventListener, чтобы можно было вызывать метод someEvent и передать туда строку. Теперь someEventListener ссылается на Activity.
Далее, в onCreateView, в обработчике кнопки мы вызываем метод someEvent и передаем туда текст. Этот метод будет отработан в Activity.
Теперь меняем Activity.
Дописываем интерфейс onSomeEventListener к описанию класса.
onCreate без изменений.
Реализуем метод someEvent. Просто ищем первый фрагмент и вставляем туда текст.
Все сохраняем и запускаем. Жмем кнопку во втором фрагменте:
Второй фрагмент передал через интерфейс строку в Activity, а оно нашло первый фрагмент и отобразило там эту строку.
На следующем уроке:
— размещаем элементы в ActionBar
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник