Как удалить fragment android

Обновлено: 18.05.2024

У меня проблема с удалением определенного фрагмента из заднего стека. Мой сценарий такой: Фрагмент-1 заменяется на Фрагмент-2, а затем Фрагмент-2 заменяется на Фрагмент-3.

Заказ звонка; Фрагмент-1 -> Фрагмент-2 -> Фрагмент-3.

Fragment-1. Это означает, что я хочу удалить Fragment-2 из заднего стека.

Как это сделать?

В backstack у вас нет Fragment s, но FragmentTransaction s. Когда вы popBackStack() , транзакция применяется снова, но в обратном направлении. Это означает, что (при условии, что вы addToBackStackTrace(null) каждый раз) в вашем backstack у вас есть

Если вы не добавите вторую транзакцию в бэкстек, то ваш бэкстек будет просто

и, таким образом, нажатие кнопки «назад» приведет к выполнению 2->1 , что приведет к ошибке из-за отсутствия фрагмента 2 (вы находитесь во фрагменте 3).
Самое простое решение - вставить backstack перед тем, как перейти от 2 к 3

У меня был очень похожий сценарий с вашим, мое решение просто проверяло количество транзакций backStack, которые у меня были.

Если транзакции, где больше, чем 0, то я бы просто поп это сразу так что он будет пропускать при нажатии назад.

Это будет успешно:

A -> B (нажата спина) -> назад к A

A -> B -> C (нажата спина) -> назад к A

Единственным недостатком, который я вижу, является то, что есть быстрая вспышка, где фрагмент A отображается перед переходом к фрагменту C.

Если вы добавляете/запускаете все три фрагмента в одном действии, вместо метода add() для FragmentTransaction для отображения Fragment3, используйте метод replace() для FragmentTransaction (замените Fragment2 на Fragment3). Метод replace удаляет текущий фрагмент из backstack перед добавлением нового фрагмента. Если вы запускаете Fragment3 из другого действия, и, следовательно, вы не можете/не хотите использовать replace() , удалите Fragment2 из backstack перед началом нового действия (которое добавляет фрагмент3):

Код для Фрагмент A -> Фрагмент B :

Добавить ФрагментAin BackStack Фрагментов

Код для Фрагмент B -> Фрагмент C :

Не добавлять ФрагментBin BackStack Фрагментов

Это будет работать следующим образом: A -> B -> C и пока возвращаем C-> A , как вы и предполагали.

Надеюсь, это поможет вам.

Вы добавляете в состояние back из FragmentTransaction и удаляете из backstack, используя методы FragmentManager pop:

ИЛИ ЖЕ

Просто вы можете пропустить фрагмент от добавления в стек фрагментов, поэтому, когда вы вернетесь из Fragment-3 , он вернется к Fragment-1

Обновлено:

Чтобы отключить анимацию, вам нужно переопределить метод onCreateAnimation .

Кот из фрагментов

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

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

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

Fragment

Fragment

Основные классы

Сами фрагменты наследуются от 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().

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

- добавлять первый фрагмент
- удалять первый фрагмент
- заменять первый фрагмент вторым фрагментом
- переключать режим сохранения в BackStack операций с фрагментами

Project name: P1051_FragmentDynamic
Build Target: Android 4.1
Application name: FragmentDynamic
Package name: ru.startandroid.develop.p1051fragmentdynamic
Create Activity: MainActivity

В strings.xml добавим строки:

Создаем фрагменты. Как мы помним из прошлого урока, для этого нам нужны будут layout-файлы и классы, наследующие android.app.Fragment

fragment1.xml:

fragment2.xml:

Fragment1.java:

Fragment2.java:

Все почти аналогично прошлому уроку, только убрали вызовы кучи lifecycle методов с логами.

Рисуем основное Activity.

Три кнопки для добавления, удаления и замены фрагментов. Чекбокс для включения использования BackStack. И FrameLayout – это контейнер, в котором будет происходить вся работа с фрагментами. Он должен быть типа ViewGroup. А элементы Fragment, которые мы использовали на прошлом уроке для размещения фрагментов, нам не нужны для динамической работы.

MainActivity.java:

В onCreate создаем пару фрагментов и находим чекбокс.

В onClick мы получаем менеджер фрагментов с помощью метода getFragmentManager. Этот объект является основным для работы с фрагментами. Далее, чтобы добавить/удалить/заменить фрагмент, нам необходимо использовать транзакции. Они аналогичны транзакциям в БД, где мы открываем транзакцию, производим операции с БД, выполняем commit. Здесь мы открываем транзакцию, производим операции с фрагментами (добавляем, удаляем, заменяем), выполняем commit.

Итак, мы получили FragmentManager и открыли транзакцию методом beginTransaction. Далее определяем, какая кнопка была нажата:

если Add, то вызываем метод add, в который передаем id контейнера (тот самый FrameLayout из main.xml) и объект фрагмента. В итоге, в контейнер будет помещен Fragment1

если Remove, то вызываем метод remove, в который передаем объект фрагмента, который хотим убрать. В итоге, фрагмент удалится с экрана.

если Replace, то вызываем метод replace, в который передаем id контейнера и объект фрагмента. В итоге, из контейнера удалится его текущий фрагмент (если он там есть) и добавится фрагмент, указанный нами.

Далее проверяем чекбокс. Если он включен, то добавляем транзакцию в BackStack. Для этого используем метод addToBackStack. На вход можно подать строку-тэг. Я передаю null.

Ну и вызываем commit, транзакция завершена.

Давайте смотреть, что получилось. Все сохраняем, запускаем приложение.



появился первый фрагмент.

Жмем Remove


Еще раз добавим первый фрагмент – жмем Add. И жмем Replace


первый фрагмент заменился вторым.

Снова запускаем приложение и включаем чекбокс add to Back Stack


Т.е. все достаточно просто и понятно. Скажу еще про пару интересных моментов.

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

Когда мы удаляем фрагмент и не добавляем транзакцию в BackStack, то фрагмент уничтожается. Если же транзакция добавляется в BackStack, то, при удалении, фрагмент не уничтожается (onDestroy не вызывается), а останавливается (onStop).

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

На следующем уроке:

- рассмотрим взаимодействие между Activity и ее фрагментами

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

- новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Я пытаюсь динамически добавлять и удалять фрагменты из ViewPager, добавляя работы без каких-либо проблем, но удаление работает не так, как ожидалось.

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

Я также попытался использовать FragmentStatePagerAdapter или вернуть POSITION_NONE в методе getItemPosition адаптера.

что я делаю не так?

решения Лаута было недостаточно, чтобы заставить вещи работать на меня, так как существующие фрагменты не разрушались. Мотивировано ответ, я обнаружил, что решение состоит в том, чтобы переопределить getItemId(int position) метод FragmentPagerAdapter чтобы дать новый уникальный идентификатор всякий раз, когда произошло изменение ожидаемого положения фрагмента.

Исходный Код:

теперь, например, если вы удалите одну вкладку или внесете некоторые изменения в заказ, вы должны звоните notifyChangeInPosition(1) перед вызовом notifyDataSetChanged() , что гарантирует, что все фрагменты будут воссозданы.

почему это решение работает

переопределение getItemPosition():

, когда notifyDataSetChanged() вызывается, адаптер вызывает notifyChanged() метод ViewPager к которому он прикреплен. The ViewPager затем проверяет значение, возвращаемое адаптера getItemPosition() для каждого элемента, удалением тех элементов, которые возвращают POSITION_NONE (см. источник код), а затем размножаться.

переопределение getItemId():

это необходимо для предотвращения перезагрузки старого фрагмента адаптером, когда ViewPager - это размножаться. Вы можете легко понять, почему это работает, посмотрев на исходный код для instantiateItem() в FragmentPagerAdapter .

мое рабочее решение для удаления фрагмента страницы из view pager

и когда мне нужно удалить некоторую страницу по индексу, я делаю это

вот моя инициализация адаптера

фрагмент должен быть уже удален, но проблема заключалась в сохранении состояния viewpager

ничего не работало, но это решило проблему !

у меня была идея просто скопировать исходный код android.support.v4.app.FragmentPagerAdpater в пользовательский класс с именем CustumFragmentPagerAdapter . Это дало мне возможность изменить instantiateItem(. ) Так что каждый раз, когда он вызывается, он удаляет / уничтожает текущий прикрепленный фрагмент, прежде чем он добавляет новый фрагмент, полученный от getItem() метод.

просто изменить instantiateItem(. ) следующим образом:

вы можете объединить оба для лучшего:

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

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

поэтому я думаю, что вам лучше использовать arraylist weakReference для сохранения всех созданных вами фрагментов. И когда вы добавляете или удаляете какую-то страницу, вы можете получить правильную позицию из своей собственной список ArrayList.

согласно комментариям, getItemPosition вызывается, когда представление узла пытается определить, изменилось ли положение элемента. А возвращаемое значение означает его новую позицию.

но этого недостаточно. Нам еще предстоит сделать важный шаг. В исходном коде FragmentStatePagerAdapter есть массив с именем "mFragments" кэширует фрагменты, которые не уничтожаются. И в функции instantiateItem.

это возвращает кэшированный фрагмент непосредственно, когда он обнаруживает, что кэшированный фрагмент не равен null. Так что проблема есть. Из примера давайте удалим одну страницу в позиции 2, Во-первых, мы удалим этот фрагмент из нашего собственного ссылочного arraylist. таким образом, в getItemPosition он вернет POSITION_NONE для этого фрагмента, а затем этот фрагмент будет уничтожен и удален из "mFragments".

теперь фрагмент в позиции 3 будет в позиции 2. И instantiatedItem с параметром позиция 3 будет быть призванным. В это время третий элемент в "mFramgents" не является нулевым, поэтому он будет возвращен напрямую. Но на самом деле он вернул фрагмент в позиции 2. Поэтому, когда мы перейдем на страницу 3, мы найдем там пустую страницу.

чтобы обойти эту проблему. Мой совет заключается в том, что вы можете скопировать исходный код FragmentStatePagerAdapter в свой собственный проект, и когда вы добавляете или удаляете операции, вы должны добавлять и удалять элементы в arraylist "mFragments".

вещи будет проще, если вы просто используете PagerAdapter вместо FragmentStatePagerAdapter. удача.

добавить или удалить фрагмент в viewpager динамически.
Вызов setupViewPager (viewPager) при запуске действия. Для загрузки другого фрагмента вызовите setupViewPagerCustom (viewPager). например, при нажатии кнопки вызова: setupViewPagerCustom (viewPager);

у меня были некоторые проблемы с FragmentStatePagerAdapter . После удаления элемента:

  • был еще один элемент, используемый для позиции (элемент, который не принадлежал позиции, но к позиции рядом с ней)
  • или какой-то фрагмент не был загружен (на этой странице был виден только пустой фон)

после множества экспериментов я придумал следующее решение.

это решение использует взлом только в случае удаления элемента. В противном случае (например, при добавлении элемента) он сохраняет чистоту и производительность исходного кода.

конечно, снаружи адаптера вы вызываете только addItem / removeItem, нет необходимости вызывать notifyDataSetChanged().

попробуйте это решение. Я использовал databinding для привязки представления. Вы можете использовать общую функцию " findViewById ()".

Я добавил функцию "clearFragments", и я использовал эту функцию для очистки адаптера перед установкой новых фрагментов. Это вызывает правильные действия удаления фрагментов. Мой класс pagerAdapter:

У меня проблема с удалением определенного фрагмента из заднего стека. Мой сценарий такой: Фрагмент-1 заменяется на Фрагмент-2, а затем Фрагмент-2 заменяется на Фрагмент-3.

Заказ звонка; Фрагмент-1 -> Фрагмент-2 -> Фрагмент-3.

Fragment-1. Это означает, что я хочу удалить Fragment-2 из заднего стека.

Как это сделать?

В backstack у вас нет Fragment s, но FragmentTransaction s. Когда вы popBackStack() , транзакция применяется снова, но в обратном направлении. Это означает, что (при условии, что вы addToBackStackTrace(null) каждый раз) в вашем backstack у вас есть

Если вы не добавите вторую транзакцию в бэкстек, то ваш бэкстек будет просто

и, таким образом, нажатие кнопки «назад» приведет к выполнению 2->1 , что приведет к ошибке из-за отсутствия фрагмента 2 (вы находитесь во фрагменте 3).
Самое простое решение - вставить backstack перед тем, как перейти от 2 к 3

У меня был очень похожий сценарий с вашим, мое решение просто проверяло количество транзакций backStack, которые у меня были.

Если транзакции, где больше, чем 0, то я бы просто поп это сразу так что он будет пропускать при нажатии назад.

Это будет успешно:

A -> B (нажата спина) -> назад к A

A -> B -> C (нажата спина) -> назад к A

Единственным недостатком, который я вижу, является то, что есть быстрая вспышка, где фрагмент A отображается перед переходом к фрагменту C.

Если вы добавляете/запускаете все три фрагмента в одном действии, вместо метода add() для FragmentTransaction для отображения Fragment3, используйте метод replace() для FragmentTransaction (замените Fragment2 на Fragment3). Метод replace удаляет текущий фрагмент из backstack перед добавлением нового фрагмента. Если вы запускаете Fragment3 из другого действия, и, следовательно, вы не можете/не хотите использовать replace() , удалите Fragment2 из backstack перед началом нового действия (которое добавляет фрагмент3):

Код для Фрагмент A -> Фрагмент B :

Добавить ФрагментAin BackStack Фрагментов

Код для Фрагмент B -> Фрагмент C :

Не добавлять ФрагментBin BackStack Фрагментов

Это будет работать следующим образом: A -> B -> C и пока возвращаем C-> A , как вы и предполагали.

Надеюсь, это поможет вам.

Вы добавляете в состояние back из FragmentTransaction и удаляете из backstack, используя методы FragmentManager pop:

ИЛИ ЖЕ

Просто вы можете пропустить фрагмент от добавления в стек фрагментов, поэтому, когда вы вернетесь из Fragment-3 , он вернется к Fragment-1

Обновлено:

Чтобы отключить анимацию, вам нужно переопределить метод onCreateAnimation .

Читайте также: