- Android override onbackpressed in fragment
- Комментарии RSS по email OK
- Как реализовать onBackPressed () во фрагментах?
- JAVA SOLUTION
- КОТЛИН РЕШЕНИЕ
- 1 — Создать интерфейс
- 2 — Подготовьте свою деятельность
- 3 — Внедрите в свой целевой фрагмент
- BackPress handling in Android Fragments, The old and the new!
- Demonstrating the old way of implementing this and the new one-liner!
- The old:
- The new:
- Фрагменты
- Основные классы
- FragmentManager
- Методы транзакции
- Аргументы фрагмента
- Управление стеком фрагментов
- Интеграция Action Bar/Options Menu
- Связь между фрагментом и активностью
Android override onbackpressed in fragment
28 октября 2014
В Android-приложениях иногда требуется особым образом обработать нажатие кнопки back. Если у вас не используются фрагменты, всё просто. Перекрываем метод onBackPressed у Activity и делаем что нам нужно. Если же используются фрагменты и по нажатию back необходимо что-то поменять в фрагменте, обработку хочется сделать именно в нём.
Посмотрев ответы на эту тему на StackOverflow я был несколько удивлён. Предлагается либо ненадёжный способ через OnKeyListener , либо жёсткий хардкод. Попробуем сделать это более красиво и удобно.
Начнём с интерфейса для фрагментов. Готового в фреймворке нет, сделаем свой:
Далее перекроем метод onBackPressed в нашем FragmentActivity :
Вытаскиваем все фрагменты, которые у нас есть. Ищем первый попавшийся, который реализует наш интерфейс OnBackPressedListener . Тут можно было придумать что-то, чтобы работать с несколькими обработчиками, но чаще всего он один. Если есть фрагмент, который реализует OnBackPressedListener , вызываем его единственный метод. Если нет — обрабатываем back как обычно.
Ну и, наконец, сам фрагмент:
Плюс данного подхода в том, что можно, например, отнаследовать все наши activity от MyActivity и использовать OnBackPressedListener без каких-либо изменений в коде MyActivity .
Комментарии RSS по email OK
Александр, смотрю ты уже на Android перешел. Мобильная разработка всех поглощает? Или это просто хобби 🙂
Я не ограничиваю себя какой-то одной технологией или языком. В случае андройда это не хобби. Коммерческий проект.
Согласен. Я то в моб. разработке дальше ionicframework(cordova) не пошел пока.
Спасибо за ваш вариант. На основе вашего у меня родилось следующее решение:
Я новечек важно ваше мнение. Конструкция вроде работает. Но могут ли быть с ней проблемы?
Могут. Как минимум, стоит учитывать, что использоваться может более одного фрагмента в одном activity.
В ListActivity это выглядит примерно так:
А как такое сделать в ListFragment и обычном Activity?
Sam, а почему вы пишете «андроЙд»? Или вы с такой же ошибкой пишите слова «плазмоЙд», «гиперболоЙд», «стероЙд» и «планетоЙд»? Домашнее задание: попробуйте произнести вслух эти слова так, как они написаны — с буквой «Й». Постарайтесь при этом не завязать язык в узел.
Капитан О., описочка вышла. Кстати, произнести с «й» вполне получается и при этом ничего в узел не завязывается. Это сочетание звуков вполне характерно для английского.
Спасибо за статью! Очень наглядный пример как ловить onBackPressed в фрагментах)
Источник
Как реализовать onBackPressed () во фрагментах?
Есть ли способ, которым мы можем реализовать onBackPressed() в Android Fragment аналогично тому, как мы реализуем в Android Activity?
Так как Фрагмент жизненного цикла не имеет onBackPressed() . Есть ли другой альтернативный способ перегрузки onBackPressed() фрагментов Android 3.0?
Я решил таким образом переопределить onBackPressed в Activity. Все FragmentTransaction это , addToBackStack прежде чем совершить:
На мой взгляд, лучшее решение:
JAVA SOLUTION
Создать простой интерфейс:
И в вашей деятельности
Наконец в вашем фрагменте:
КОТЛИН РЕШЕНИЕ
1 — Создать интерфейс
2 — Подготовьте свою деятельность
3 — Внедрите в свой целевой фрагмент
Согласно @HaMMeRed ответом здесь является псевдокод, как он должен работать. Допустим, называется ваша основная деятельность BaseActivity с дочерними фрагментами (как в примере с SlidingMenu lib). Вот шаги:
Сначала нам нужно создать интерфейс и класс, который реализует его интерфейс, чтобы иметь универсальный метод
Создать интерфейс класса OnBackPressedListener
Создать класс, который реализует навыки OnBackPressedListener
С этого момента мы будем работать над нашим кодом BaseActivity и его фрагментами.
Создайте частного слушателя на вершине своего класса BaseActivity
создать метод для установки слушателя в BaseActivity
в переопределении onBackPressed реализовать что-то подобное
в вашем фрагменте onCreateView вы должны добавить наш слушатель
Вуаля, теперь, когда ты щелкаешь обратно фрагментом, ты должен поймать свой метод на спине.
Если вам нужна такая функциональность, вам нужно переопределить ее в своей деятельности, а затем добавить YourBackPressed интерфейс для всех ваших фрагментов, который вы вызываете для соответствующего фрагмента при каждом нажатии кнопки «Назад».
Изменить: я хотел бы добавить свой предыдущий ответ.
Если бы я делал это сегодня, я бы использовал трансляцию или, возможно, заказанную трансляцию, если бы ожидал, что другие панели будут обновляться в унисон на панель основного / основного контента.
LocalBroadcastManager в библиотеке поддержки может помочь с этим, и вы просто отправляете трансляцию onBackPressed и подписываетесь на интересующие вас фрагменты. Я думаю, что Messaging является более изолированной реализацией и будет лучше масштабироваться, поэтому сейчас это будет моя официальная рекомендация по внедрению. Просто используйте Intent действие в качестве фильтра для вашего сообщения. отправьте только что созданное ACTION_BACK_PRESSED , отправьте его из своей деятельности и прослушайте в соответствующих фрагментах.
Ничто из этого не легко реализовать и не будет функционировать оптимальным образом.
У фрагментов есть вызов метода onDetach, который сделает эту работу.
ЭТО ДЕЛАЕТ РАБОТУ.
Если вы используете androidx.appcompat:appcompat:1.1.0 или выше, вы можете добавить OnBackPressedCallback к своему фрагменту, как показано ниже
Просто добавьте, addToBackStack пока вы переходите между фрагментами, как показано ниже:
если вы пишете addToBackStack(null) , он будет обрабатывать его сам, но если вы дадите тег, вы должны обработать его вручную.
Поскольку этому вопросу и некоторым ответам уже более пяти лет, позвольте мне поделиться своим решением. Это продолжение и модернизация в ответ от @oyenigun
ОБНОВИТЬ: В нижней части этой статьи я добавил альтернативную реализацию, использующую абстрактное расширение Fragment, которое вообще не будет включать Activity, что было бы полезно для любого с более сложной иерархией фрагментов, включающей вложенные фрагменты, которые требуют различного обратного поведения.
Мне нужно было реализовать это, потому что некоторые из фрагментов, которые я использую, имеют меньшие представления, которые я хотел бы отклонить с помощью кнопки «Назад», такие как небольшие информационные представления, которые появляются, и т. Д., Но это хорошо для всех, кому нужно переопределить поведение кнопка возврата внутри фрагментов.
Сначала определите интерфейс
Этот интерфейс, который я называю Backable (я сторонник соглашений об именах), имеет единственный метод, onBackPressed() который должен возвращать boolean значение. Нам нужно ввести логическое значение, потому что нам нужно будет знать, «нажата» ли кнопка «назад», «поглотила» ли она событие назад. Возврат true означает, что он есть, и никаких дальнейших действий не требуется, в противном случае он false говорит, что обратное действие по умолчанию все еще должно иметь место. Этот интерфейс должен быть его собственным файлом (желательно в отдельном названном пакете interfaces ). Помните, что разделение ваших классов на пакеты — это хорошая практика.
Во-вторых, найдите верхний фрагмент
Я создал метод, который возвращает последний Fragment объект в заднем стеке. Я использую теги . если вы используете ID, внесите необходимые изменения. У меня есть этот статический метод в служебном классе, который имеет дело с состояниями навигации и т. Д., Но, конечно, поместите его там, где он вам больше подходит. Для назидания я поместил свой класс в класс NavUtils .
Удостоверьтесь, что число обратных стеков больше 0, иначе ArrayOutOfBoundsException может быть выброшено во время выполнения. Если оно не больше 0, вернуть ноль. Мы проверим нулевое значение позже .
В-третьих, реализовать в фрагменте
Реализуйте Backable интерфейс в любом фрагменте, где вам нужно переопределить поведение кнопки «Назад». Добавьте метод реализации.
В onBackPressed() переопределении поместите любую логику, которая вам нужна. Если вы хотите, чтобы кнопка «Назад» не вставляла задний стек (поведение по умолчанию), верните true , чтобы событие «Назад» было поглощено. В противном случае верните false.
Наконец, в вашей деятельности .
Переопределите onBackPressed() метод и добавьте к нему следующую логику:
Мы получаем текущий фрагмент в заднем стеке, затем делаем нулевую проверку и определяем, реализует ли он наш Backable интерфейс. Если это так, определите, было ли событие поглощено. Если это так, мы закончили onBackPressed() и можем вернуться. В противном случае рассматривайте это как обычное нажатие назад и вызывайте метод super.
Второй вариант — не привлекать активность
Иногда вы не хотите, чтобы Activity обрабатывал это вообще, и вам нужно обрабатывать это непосредственно внутри фрагмента. Но кто сказал, что у вас не может быть фрагментов с API для обратной печати? Просто расширьте свой фрагмент до нового класса.
Создайте абстрактный класс, который расширяет Fragment и реализует View.OnKeyListner интерфейс .
Как вы можете видеть, любой фрагмент, который расширяется, BackableFragment будет автоматически захватывать обратные клики, используя View.OnKeyListener интерфейс. Просто вызовите абстрактный onBackButtonPressed() метод из реализованного onKey() метода, используя стандартную логику, чтобы распознать нажатие кнопки назад. Если вам нужно зарегистрировать нажатия клавиш, отличные от кнопки «Назад», просто вызовите super метод при переопределении onKey() вашего фрагмента, иначе вы переопределите поведение в абстракции.
Прост в использовании, просто расширьте и внедрите:
Поскольку onBackButtonPressed() метод в суперклассе является абстрактным, после расширения вы должны реализовать его onBackButtonPressed() . Он возвращается, void потому что ему просто нужно выполнить действие внутри класса фрагмента, и ему не нужно ретранслировать поглощение прессы обратно в Activity. Убедитесь, что вы вызываете onBackPressed() метод Activity, если все, что вы делаете с кнопкой «назад», не требует обработки, в противном случае кнопка «Назад» будет отключена . и вам это не нужно!
Предостережения Как вы можете видеть, это устанавливает ключевой слушатель на корневой вид фрагмента, и нам нужно сфокусировать его. Если в вашем фрагменте, который расширяет этот класс (или в других внутренних фрагментах или представлениях, имеющих одинаковые значения), есть тексты редактирования (или любые другие представления для кражи фокуса), вам придется обрабатывать это отдельно. Есть хорошая статья о расширении EditText, чтобы потерять фокус на спине.
Я надеюсь, что кто-то найдет это полезным. Удачного кодирования.
Источник
BackPress handling in Android Fragments, The old and the new!
Demonstrating the old way of implementing this and the new one-liner!
One of the nice things that recently was introduced in the Android world in devSummit19 was the new way of handling back presses in the fragments which always was a bit of pain to implement. Previously if a fragment let’s say a SearchFragment needed to respond to back press events and close the SearchView, we had to go through all the steps as below:
The old:
1-We should’ve had an interface as:
2- All the fragments willing to intercept the BackPress event had to implement the interface above which caused them having the onBackPressed() function call. Now the fragment can respond to BackPress events and do something and based on if the event was consumed or not they can return true or false.
3-Now it’s the time to connect the dots and override the Activity’s OnBackPressed() function to implement the logic, We needed to get the current fragment from “ fragmentManager” and examine if it was of type BackPressHandler and if so we should’ve executed the onBackPressed() function on that particular fragment and checked the response, If it was returning false, meaning that they were not interested in consuming the event then the next move was to call super.onBackPressed() or do specific logic in the activity, but if it returned true then it means that the fragment has consumed the event and we should skip executing the logic in Activity’s onBackPressed(), Let’s see how it is done in Action:
and If you’re wondering what’s inside getTopFragment extension function:
The new:
As you can see it was a lot of work just for a simple thing to do, Now with the new way of doing this we only need to add the code below on the Fragment’s onCreate() method:
From now on all the back press events will be captured by handleOnBackPressed() method and your fragments will have the opportunity to react to Back Press events. but what about the times that we’re not sure that we need to consume the back press event? The times that we just need to check some condition and if they apply then do some action? In those cases we can set the isEnabled variable to false and call the onBackPressed() method on activity again manually:
Источник
Фрагменты
Существует два основных подхода в использовании фрагментов.
Первый способ основан на замещении родительского контейнера. Создаётся стандартная разметка и в том месте, где будут использоваться фрагменты, размещается контейнер, например, 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().
Источник