Android фрагмент не отображается

Фрагменты

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

Первый способ основан на замещении родительского контейнера. Создаётся стандартная разметка и в том месте, где будут использоваться фрагменты, размещается контейнер, например, 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, так как этот метод относится к компоненту, а не к активности, как мы обычно делали в программах, когда просто опускали имя активности. Т.е. в нашем случае мы ищем ссылку на кнопку не среди разметки активности, а внутри разметки самого фрагмента.

Читайте также:  Где найти серийный номер airpods pro через андроид

Нужно помнить, что в методе 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(). Работа методов фрагмента ничем не отличается от аналогичных методов для активности.

Читайте также:  Swords and soldier android

В активности, которая содержит фрагмент, данные методы автоматически сработают.

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

Код для активности:

Код для фрагмента:

Связь между фрагментом и активностью

Экземпляр фрагмента связан с активностью. Активность может вызывать методы фрагмента через ссылку на объект фрагмента. Доступ к фрагменту можно получить через методы findFragmentById() или findFragmentByTag().

Источник

Android — ViewPager2 — заменяем фрагменты на лету (программно)

Вдруг вам надо листать фрагменты через ViewPager2 и при этом подменять их динамически. Например, чтобы уйти «глубже» — пользователь из фрагмента «Главные настройки» переходит во фрагмент «Выбор языка». При этом новый фрагмент должен отобразиться на месте главного фрагмента. А потом пользователь еще и захочет вернуться обратно.

список Fragment-ов (например, разделы анкеты или многостраничный раздел настроек)

Kotlin (Java), Android собственно

Задача

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

Есть три фрагмента. Первый — список любимых тарелок, второй — экран с сообщениями, третий — настройки.

Пользователь на первом экране выбирает тарелку -> меняем первый фрагмент на фрагмент «Информация о тарелке»

Пользователь листает свайпами или выбирает через TabLayout третий экран — «Настройки», выбирает раздел «Выбор языка» -> меняем третий фрагмент на фрагмент «Выбор языка»

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

Итак, пытаемся сдружить ArrayMap и ViewPager2.

Результат

Дисклеймер (не читать, вода)

Пишу, потому что найти хороших решений по этой задаче не смог, хотя вроде пытался усердно.

Код местами специально упрощен — не люблю, когда в статьях добавляют десятки строк, не касающихся решения описываемой задачи, только ради соблюдения всех правил. Так что: строковые ресурсы — вынести, вью модели для фрагментов — добавить, пару уровней абстракций — ну вы поняли

Весь проект — https://github.com/IDolgopolov/ViewPager2_FragmentReplacer

Иногда буду использовать слово «страницы» — имею ввиду позицию, на которой отображается фрагмент во ViewPager. Например, «Список любимых тарелок», «Экран сообщений» и «Настройки» — три страницы. Фрагмент «Выбор языка» заменяет третью страницу.

Решение

Сначала ничего интересного, просто для общего ознакомления

Верстка

Интерфейсы

Опишем функции, которые нам понадобятся для смены фрагментов

Все фрагменты, добавляемые во ViewPagerAdapter (MyViewPager2Adapter описан ниже), будут наследоваться от BaseFragment.

Определим в нем переменные для хранения:

pageId — уникальный номер страницы. Может быть любым числом, но главное — уникальным и большим, чем у вас позиций страниц (pageId > PAGE_COUNT — 1), иначе будут баги из-за метода getItemId()

pagePos — номер странице, на которой будет отображаться фрагмент во ViewPager (начиная с 0, естественно)

fragmentReplacer — ссылка на ViewPagerAdapter (MyViewPager2Adapter реализует FragmentReplacer)

Связь активити и ViewPager2

Адаптер для ViewPager2 — всё здесь

В этом классе будут реализован интерфейс FragmentReplacer и переопределены несколько классов FragmentStateAdapter. Это и позволит менять фрагменты на лету.

Весь код класса, который ниже разбираю подробно

В первую очередь переопределим четыре метода FragmentStateAdapter:

Теперь две функции, которые позволяют подменять фрагменты

Пример использования

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

Например, фрагмент, по умолчанию, отображаемый на первой странице:

Мысли в слух

Аккуратно, если фрагменты тяжелые, — надо задуматься об очищении mapOfFragment.

Читайте также:  Как установить предыдущую версию андроид авто

Возможно, стоит хранить Class вместо BaseFragment. Но тогда придется инициализировать их каждый раз в createFragment(). Меньше памяти, больше времени. Что думаете?

Источник

Фрагмент поверх другого фрагмента

Когда я показываю один фрагмент (который является полноэкранным с #77000000 фоном) поверх другого фрагмента (давайте назовем его основным), мой основной фрагмент по-прежнему реагирует на щелчки (мы можем нажать кнопку, даже если мы ее не видим).

Вопрос : как предотвратить нажатия на первый (основной) фрагмент?

РЕДАКТИРОВАТЬ

К сожалению, я не могу просто скрыть основной фрагмент, потому что я использую прозрачный фон для второго фрагмента (поэтому пользователь может видеть, что находится позади).

Установите для clickable свойства второго фрагмента значение true. Представление будет перехватывать событие, чтобы оно не передавалось основному фрагменту. Так что, если представление второго фрагмента является макетом, это будет код:

Решение довольно простое. Во втором фрагменте (который перекрывает наш основной фрагмент) нам просто нужно перехватить onTouch событие:

Просто добавьте clickable=»true» и focusable=»true» родительский макет

Если вы используете AndroidX , попробуйте это

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

Если вы хотите узнать больше вопросов о том, как решить проблемы с фрагментом, вы можете посмотреть мою библиотеку: https://github.com/JustKiddingBaby/FragmentRigger

Вы должны добавить android:focusable=»true» с android:clickable=»true»

Clickable означает, что на него можно нажать указательным устройством или коснуться сенсорного устройства.

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

Существует несколько решений, которые некоторые из нас внесли в этот поток, но я также хотел бы упомянуть еще одно решение. Если вам не нравится помещать кликабельный и фокусируемый, равный true для каждой корневой макета макета в XML, как я. Вы также можете положить его на свою базу, если у вас есть так же, как показано ниже;

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

Я надеюсь, что это поможет тем, кто ненавидит макет XML.

Приемлемый ответ будет «работать», но также приведет к снижению производительности (перерисовка, повторное измерение при изменении ориентации), так как фрагмент внизу все еще рисуется. Может быть, вам нужно просто найти фрагмент по тегу или идентификатору и установить видимость GONE или VISIBLE, когда вам нужно снова показать.

Такое решение предпочтительнее альтернативы hide() и show() методов FragmentTransaction использования анимации. Вы просто вызовите его из onTransitionStart() и onTransitionEnd() в Transition.TransitionListener .

Метод 1:

Вы можете добавить ко всем фрагментам макет

Метод 2: (программно)

Расширить весь фрагмент FragmentBase и т. Д. Затем добавьте этот код в FragmentBase

Что вы можете сделать, так это то, что вы можете сделать пустой щелчок для макета предыдущего фрагмента, используя свойство onClick для родительского макета этого основного фрагмента, и в действии вы можете создать функцию doNothing(View view) и ничего не писать в ней. Это сделает это за вас.

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

Добавление android:clickable=»true» не работает для меня. Это решение не работает на CoordinatorLayout, когда это родительский макет. Вот почему я сделал RelativeLayout в качестве родительского макета, добавил android:clickable=»true» к нему и поместил CoordinatorLayout в этот RelativeLayout.

У меня было несколько фрагментов с тем же XML.
Потратив часы я снял setPageTransformer и все заработало

Источник

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