Короче говоря, проблема:
У меня есть MainActivity, на котором расположены BottomNavigationView и FrameLayout. BottomNavigationView имеет 5 вкладок, и при нажатии на вкладку я добавляю фрагмент на этот FrameLayout. Но из какого-то фрагмента мне нужно открыть другой фрагмент. Из этого другого фрагмента мне нужно открыть другой. Каждый раз, когда мне нужно показать фрагмент, я уведомляю MainActivity из фрагмента, что ему нужно добавить еще один. Каждый фрагмент проверяет, реализует ли его активность интерфейс. И это раздражает. Итак, если у меня есть 100 фрагментов, MainActivity реализует слишком много интерфейсов. Это приводит к шаблонному коду. Итак, как правильно перемещаться между фрагментами, если их много?
Подробнее о проблеме:
Пожалуйста, сначала прочтите задачу в коротком разделе.
Как я уже сказал, у меня есть BottomNavigationView с 5 вкладками. Назовем фрагменты, отвечающие за каждую вкладку, как FragmentA, FragmentB, FragmentC, FragmentD, FragmentE. Я действительно знаю, как показывать эти фрагменты при нажатии вкладки. Я просто заменяю / добавляю эти фрагменты в действии. Но подождите, а что, если вы хотите перейти с FragmentA на FragmentF? После этого с FragmentF на FragmentG? Вот как я справляюсь с этой проблемой: из FragmentF или FragmentG я уведомляю MainActivity, что хочу изменить фрагмент. Но как они общаются с MainActivity? Для этого у меня есть интерфейсы внутри каждого фрагмента. MainActivity реализует эти интерфейсы. И вот проблема. MainActivity реализует слишком много интерфейсов, что приводит к шаблонному коду. Итак, как лучше всего перемещаться по фрагментам? Я даже не трогаю, что еще надо обрабатывать нажатия кнопок назад :)
Вот как выглядит мой код:
MainActivity, реализующий интерфейсы для изменения фрагментов при необходимости:
class MainActivity : AppCompatActivity(), DashboardFragment.OnFragmentInteractionListener,
PaymentFragment.BigCategoryChosenListener, PaymentSubcategoryFragment.ItemClickedListener, PayServiceFragment.OnPayServiceListener, ContactListFragment.ContactTapListener, P2PFragment.P2PNotifier
Вот, например, мой метод onAttach для PaymentFragment:
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof BigCategoryChosenListener) {
listener = (BigCategoryChosenListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement BigCategoryChosenListener");
}
}
И с помощью этого listener я уведомляю об изменении фрагмента. И фрагмент в каждом я должен так сделать. Я не думаю, что это лучшая практика. Итак, все в порядке или есть способ лучше?
@GennadiiSaprykin Спасибо за ответ. К сожалению, я вас не очень хорошо понял. Что вы имеете в виду под небольшим количеством фрагментов? У меня есть BottomNavigationView с 5 вкладками, и с каждой вкладки я могу переходить к другим фрагментам. Вы имеете в виду, что мне нужны фрагменты внутри фрагментов?
5 вкладок, каждая имеет один соответствующий фрагмент содержимого. Даже если они разные, это всего лишь один интерфейс, работающий с таким методом, как onTabClicked(int tab). Все ли эти 5 фрагментов доступны для навигации? Можете ли вы как-то обобщить логику при навигации? Пожалуйста, опишите подробнее, какой контент вы показываете и что происходит, когда вы перемещаетесь внутрь. Спасибо.
@abay - На мой взгляд, исходя из вашего описания, у вас может быть определенный набор фрагментов, скажем, для 5 вкладок 5 фрагментов, и вы можете заменять их, когда захотите. Для управления навигацией между ними есть навигатор, для этого будут вспомогательные методы экранной навигации. Как replaceFragment. Надеюсь это поможет.
@GennadiiSaprykin Обновленный вопрос с более подробной информацией
@Abhi Пожалуйста, посмотрите, чтобы обновить вопрос
abay - Хорошо, спасибо за подробное описание. Если мне нужно решить этот сценарий, я не буду беспокоить Mainactivity для взаимодействия. Учитывая, что все ваше представление фрагментов находится на уровне глубины 1. Вы можете использовать вспомогательный класс типа ScreenManager с Context (ActivityContext) в качестве параметра для вспомогательного метода replaceFragment. Надеюсь, я прояснил это. Также ваша обратная навигация по стеку будет работать нормально.
@abay спасибо! Итак, если проблема в том, что в MainActivity слишком много кода, вы можете сделать то, что сказал @Abhi - переместить его в какой-нибудь вспомогательный класс, такой как Navigator, просто для улучшения читабельности. Если у вас действительно много фрагментов, вам все равно придется где-то писать эту логику, будь то активность или навигатор, на самом деле не имеет значения.
Единственный способ уменьшить объем кода - это создать какое-то сопоставление между фрагментами и создать общий метод для замены фрагментов, который определит, какой фрагмент заполнять при навигации вниз, в зависимости от этого сопоставления. Но это сильно увеличит сложность и, скорее всего, того не стоит. Я думаю, что это нормально - иметь много кода, если он выполняет одну конкретную задачу, навигацию, и написан в одном месте, а не распределен по базе кода. Я думаю, что простой перенос кода во вспомогательный класс - лучший вариант.
@GennadiiSaprykin Еще раз спасибо. Давай попробуем в последний раз. Я обновляю вопросы, вставляя код. Может быть, это поможет вам глубже понять проблему и дать другие советы.
@Abhi Я обновляю вопросы, добавляя код. Что вы думаете об этом коде? Это нормально?
Хорошо, что вам нужно, это что-то вроде этого в действии, где вы инициализируете свой BottomNavigationView.
bottomNavigationView.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_1://Handle menu click -
//Call Navigator helper to replace Fragment to Fragment A
break;
case R.id.menu_2:
//Call Navigator helper to replace Fragment to Fragment B
break;
case R.id.menu_3:
//Call Navigator helper to replace Fragment to Fragment C
break;
}
return true;
}
});
Но замена фрагмента означает уничтожение предыдущего фрагмента. Это означает, что когда вы нажимаете назад, предыдущий фрагмент воссоздает и выполняет ненужные операции, такие как HTTP-запросы.
Ну, это зависит от того, что вы там делаете. Как правило, при правильном подходе к программированию HTTP-запрос будет запускаться от презентатора (MVP), а не от фрагмента, и фрагмент будет показывать данные, которые получены с сервера и кэшированы. Это также зависит от того, когда (когда preseneter задействован) выбираются данные. Также скажите, что когда целевой целевой фрагмент уже находится в backstack FragmentManager, вы можете напрямую перейти к целевому фрагменту.
Ваш подход правильный, но действительно ли у вас есть 100 различных типов фрагментов? Вы уверены, что они не могут быть представлены несколькими фрагментами, которые просто обновляют свое содержание? Кажется маловероятным, что вам действительно нужно столько ...