Android studio fragment кнопка назад

Android studio 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, чтобы потерять фокус на спине.

Я надеюсь, что кто-то найдет это полезным. Удачного кодирования.

Источник

Fragment (Фрагменты). Часть вторая

В предыдущем примере два фрагмента были полностью независимы друг от друга. Но в реальности такое не встречается. Фрагменты должны как-то общаться между собой. Поэтому пора переходить к следующей части — как взаимодействовать с фрагментами.

Чтобы было легче перестроиться на новую технологию, начнём издалека и создадим сначала следующую программу. Набросаем на экран несколько кнопок и других компонентов. Я по возможности оставляю старый вариант, чтобы не переписывать всю статью. Вы можете некоторые части кода менять на более современные аналоги, например, использовать ConstraintLayout.

Думаю, вам уже не составит труда написать код для кнопок, чтобы в нижней части экрана менялась картинка и текстовое содержание про каждого кота. Но я этого делать пока не буду.

Заворачиваем в фрагменты

Фрагмент, как и активность, состоит из разметки и класса. Сначала займёмся разметкой.

Логически экран можно разделить на две части — верхняя неизменяемая часть с кнопками и нижняя часть с текстовым блоком и контейнером для картинки, которая изменяет свой вид в зависимости от нажатой кнопки.

Читайте также:  Батарея лучше у айфона или андроида

Создадим две отдельные разметки и скопируем нужные части из общей разметки в разметки для фрагментов. Делаем щелчок правой кнопкой мыши на папке res/layout и выбираем New | Layout Resource File.

Создаём новый файл fragment1.xml и размещаем верхнюю часть кода:

Также поступаем со вторым фрагментом — создаём новый файл fragment2.xml и в него копируем код из нижней части кода.

В обоих случаях мы вставляли код в корневой контейнер LinearLayout. Но если у вас была бы более сложная разметка с использованием контейнерных элементов, то вы могли бы копировать сразу готовый кусок кода без необходимости оборачивать его корневым элементом, как в нашем случае.

Пока мы создали разметки для будущих фрагментов. Теперь нужно создать отдельные классы для двух фрагментов. Для начала укажем, что наш класс должен наследоваться от класса Fragment. Не копируйте, а пишите код самостоятельно. Я создаю классы вручную с нуля, можно также воспользоваться готовым шаблоном Fragment (Blank), которым пользовались в первой части.

Следите, чтобы импортировался класс androidx.fragment.app.Fragment, а не устаревшие классы.

Самостоятельно создайте класс для второго фрагмента Fragment2 по такому же принципу.

Настало время подключить разметки к фрагментам. В активностях мы подключали разметку в методе onCreate() через метод setContentView(). В фрагментах метод onCreate() служит для других задач. А для подключения разметки используется отдельный метод onCreateView().

Чтобы долго не искать нужный нам метод, просто вводите на клавиатуре первую и заглавные буквы метода — ocv . Такой комбинации соответствует только один метод, который нам и нужен. Нажимаем кнопку OK и в код фрагмента будет вставлен следующий шаблон:

У метода используются три параметра. В первом параметре используется объект класса LayoutInflater, который позволяет построить нужный макет, считывая информацию из указанного XML-файла. Удалим строчку, которая возвращает результат и напишем свой вариант.

В Java-варианте код разбит на две части. Сначала мы получаем объект View, а затем уже его возвращаем в методе (не обязательно).

Скопируйте код метода onCreateView() и вставьте его в код класса Fragment2, не забыв указать разметку R.layout.fragment2.

Остальные два параметра container, false используются в связке и указывают на возможность подключения фрагментов в активность через контейнер динамически. Мы обойдёмся без динамики, а создадим собственные блоки для фрагментов, поэтому у нас используется значение false.

Возвращаемся к главной разметке активности. Смело удаляем все элементы с экрана, чтобы остался только корневой элемент LinearLayout.

В старых версиях студии на панели инструментов был готовый компонент .

Сейчас компонент убрали, поэтому напишем код вручную в режиме Code.

Обратите внимание на атрибут tools:layout=»@layout/fragmentX» у тегов fragment, они помогут отобразить содержимое фрагментов в режиме дизайна.

Если сейчас запустим приложение, то тоже никаких изменений не увидим. Зачем тогда потратили столько времени на создание фрагментов? Непонятно.

А, я понял. Можно теперь писать в резюме про свои умения: использую фрагменты.

Однако, продолжим. Если повернуть устройство в альбомную ориентацию, то программа будет выглядеть не слишком красиво.

Мы знаем, что можно создать отдельную папку res/layout-land (перечитайте урок Ориентация) и разместить там разметку для такого случая.

Скопируем файл activity_main.xml и вставим его в новую папку.

Скорее всего вы не видите созданную папку, но она есть! Переключитесь в режим Project и заново откройте структуру проекта, найдя нужную папку. В дальнейшем оставайтесь в этом режиме для данного урока.

Изменим разметку фрагмента для альбомной ориентации.

Не забывайте в имени фрагментов использовать свои названия пакетов. Совсем другое дело. Теперь в альбомной ориентации приложение выглядит намного лучше.

Но это мы могли сделать и без фрагментов. Зачем же они нужны? Пока версия с лишней строчкой в резюме остаётся основной — чтобы работодатель уважал за прогрессивный стиль.

Хотя небольшое удобство есть. Благодаря модульности, мы поменяли разметку только у фрагментов, а то, что было внутри фрагментов (кнопки, текстовые блоки и т.д.), мы не трогали.

Как уже говорилось, фрагменты были придуманы для того, чтобы обеспечить быстрое написание приложения под разные типы экранов — для смартфонов и планшетов. Часто бывает так, что на смартфоне на первом экране находится список, а когда пользователь нажимает на отдельный элемент списка, то запускается отдельная активность. А на планшете можно уместить список и дополнительные данные на одном экране, как можно увидеть на нашем последнем примере с альбомной ориентацией.

Давайте подключим поддержку планшетов. Создадим новую папку layout-sw600dp и скопируем в него файл из папки layout-land. Идентификатор sw600 говорит о минимальной ширине 600dp, что соответствует 7-дюймовым планшетам в альбомной ориентации. Существуют и другие варианты для планшетов с большими размерами.

Тут возникает небольшая проблема — если нам понадобится что-то изменить в разметке для альбомной ориентации, то придётся редактировать файлы во всех папках. Но есть выход из этой ситуации — использовать псевдонимы.

Мы можем создать одну копию разметки и указать, чтобы её использовали все нужные размеры устройств.

Делаем следующее. В папке layout-land переименовываем файл activity_main.xml в activity_main_wide.xml (Refactor | Rename) и перемещаем файл в папку layout. Пустую папку layout-land можно удалить.

Теперь создайте новую папку res/values-land. В созданной папке создаём новый файл refs.xml (имя не имеет значения, но так принято).

Этот файл говорит, что в альбомной ориентации вместо ресурса activity_main следует подключать ресурс layout/activity_main_wide. Можете запустить приложение и убедиться, что ничего не изменилось.

Если у вас будет поддержка альбомных ориентаций для разных размеров планшетов, то просто копируйте файл refs.xml в папки типа values-720dp_land и др.

Читайте также:  Heroes 3 полную версию для андроид

Теперь вы можете вносить изменения в одном файле activity_main_wide.xml, а не по отдельности в каждом файле.

В первой части мы узнали, что для создания фрагмента необходимо создать разметку, затем новый класс и в методе onCreateView() указать разметку. Затем в разметке активности указать тег fragment и присвоить ему имя класса фрагмента.

Поговорим о важном моменте. Вы можете установить связь между двумя фрагментами напрямую, чтобы при нажатии кнопки в первом фрагменте менялось содержимое во втором фрагменте. Но это неправильный подход, так как теряется смысл модульности фрагментов. Фрагменты ничего не должны знать о существовании друг друга. Любой фрагмент существует только в активности и только активность через свой специальный менеджер фрагментов должна управлять ими. А сами фрагменты должны реализовать необходимые интерфейсы, которые активность будет использовать в своих целях.

В первом фрагменте имеются кнопки. Добавим обработчик нажатий кнопок (такой же код вы могли использовать в активности, всё знакомо):

Подключаем кнопки в методе onViewCreated. Код будет похож на код, который мы обычно используем в методе onCreate() у активности, только метод findViewById() будет относиться уже не к классу Activity (обычно, мы опускали это), а к корневому элементу разметки фрагмента, в нашем случае view/rootView. В Java-варианте используется старый пример до появления метода onViewCreated(). Раньше приходилось писать код в onCreateView().

Для начала просто выведем сообщение, что кнопка нажата.

Запустите пример и проверьте. Но у нас три кнопки. Надо написать код, который бы получал информацию о нажатой кнопке, чтобы активность могла использовать эту информацию и использовать её для управления вторым фрагментом. Для удобства создадим в классе Fragment1 отдельный метод, который на основании идентификатора кнопки создаст нужный индекс:

Каждой кнопке соответствует свой индекс от 1 до 3.

Фрагмент всегда может узнать, в какой активности он находится, через метод getActivity(). В методе makeText() мы уже воспользовались данным методом, так как в фрагментах нет метода getApplicationContext().

Перепишем код для щелчка кнопки, чтобы узнать индекс нажатой кнопки.

Теперь мы умеем определять индекс нажатой кнопки. Но пока эта информация доступна только самому фрагменту. Наша задача — передать эту информацию активности, которая затем передаст её другой активности.

Для этой цели используются интерфейсы.

Открываем код первого фрагмента Fragment1 и объявляем интерфейс с единственным методом до объявления самого класса Fragment1:

Интерфейс не определяет работу метода, а только даёт ему имя. Класс, который будет использовать данный интерфейс, должен придумать, что делать в методе с данным именем.

У нас интерфейс будет использовать класс активности.

Переходим в класс активности и добавляем интерфейс OnSelectedButtonListener, который следует реализовать.

Среда разработки поможет создать заготовку для необходимого метода:

В этом методе надо написать такой код, чтобы активность получила индекс нажатой кнопки и передала информацию другому фрагменту, которая должна выполнить свою работу.

Но сначала подготовим второй фрагмент к работе. Объявим ссылки на компоненты, которые есть в разметке второго фрагмента. А также загрузим массив строк из ресурсов, который будем использовать для описания котов. Не забывайте, что в Java-варианте используется устаревший код.

Массив задаём в ресурсах (файл res/values/strings.xml). Так как первый элемент массива идёт под индексом 0, то добавим нейтральный текст:

Подготовим метод, который будет менять содержимое фрагмента в зависимости от индекса нажатой кнопки:

Осталось только получить информацию от активности (не от фрагмента) об индексе.

Опять возвращаемся в активность и напишем код для пустого метода onButtonSelected(), который будет получать от первого фрагмента индекс нажатой кнопки и передавать его второму фрагменту:

Активность получает доступ к своим фрагментам через специальный менеджер фрагментов (коты называют его манагером). Менеджер есть у любой активности, поэтому мы его не создаём через конструкцию new FragmentManager, а получаем через метод getSupportFragmentManager().

Менеджер фрагментов держит в руках все нити управления над своими фрагментами. Найти нужный фрагмент можно по идентификатору через метод FragmentManager.findFragmentById(), который похож на метод findViewById() для получения идентификатора кнопки, метки и т.д. У менеджера есть ещё один метод для поиска фрагмента по тегу findFragmentByTag().

В созданной заготовке вызываем менеджер фрагментов, получаем ссылку на второй фрагмент через его идентификатор и вызываем его метод setDescription().

Во втором фрагменте у нас нет надобности создавать интерфейс, так как фрагменту не нужно ничего сообщать активности. Он исполняет пассивную роль и ему нужно только получить данные для работы.

Опять возвращаемся к классу первого фрагмента. Сейчас нажатие кнопки приводит к появлению всплывающего сообщения об индексе кнопки.

Но теперь в методе onClick() мы можем получить доступ к слушателю активности.

По цепочке мы передаём информацию от первого фрагмента в активность, а затем активность передаёт информацию во второй фрагмент.

Если посмотреть на код двух фрагментов, то увидим, что они полностью независимы и не обращаются ни конкретно к друг другу, ни к определённой активности. Принцип модульности соблюдён. Вы можете добавить любой из этих фрагментов в любую новую активность и при этом вам не придётся менять код в самих фрагментах. Весь необходимый функционал в фрагментах уже прописан.

Запустите проект и проверьте на работоспособность. Для данного случая мы пока не получили никаких преимуществ в использовании фрагментов. Но сейчас главное для вас — понять основные принципы создания и взаимодействия фрагментов.

Спустя несколько лет возможности фрагментов расширились и теперь передавать данные можно проще.

Источник

Оцените статью