BottomNavigationView-как избежать воссоздания фрагментов и повторного их использования
Я хотел бы сделать нижнюю панель навигации в моем проекте. Каждый вид имеет свой собственный фрагмент. Проблема заключается в том, что каждый раз, когда я нажимаю на кнопку, чтобы изменить вид, например, из недавних в избранное, он создает новый фрагмент с совершенно новыми состояниями(например, положение прокрутки, текст изменен, что бы ни содержал мой фрагмент). Я знаю, что в официальной документации Android было написано, что нижняя панель навигации должна сбросить состояния задачи, но я думаю, что это слишком неудобно для пользователи.
Я хотел бы иметь подобную функциональность, как instagram, что вы меняете из ленты, чтобы исследовать и обратно, чтобы кормить положение прокрутки изображение кэширует все, что остается сохраненным. Я попробовал почти каждый способ решить эту проблему единственное, что сработало, - это установить visibility GONE и установить visibility VISIBLE в соответствии с ситуацией, но я понимаю, что это не правильный путь, должен быть лучший способ сделать это, и я не говорю о ручном сохранении необходимых экземпляров. Я следовал почти каждому учебнику о нижних фрагментах nav, но самое интересное, что никто не заинтересован в том, чтобы использовать его, не вызывая новый каждый раз.
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, FirstFragment.newInstance());
fragmentTransaction.commit();
bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.menu_dialer:
fragment = FirstFragment.newInstance();
break;
case R.id.menu_email:
fragment = SecondFragment.newInstance();
break;
case R.id.menu_map:
fragment = ThirdFragment.newInstance();
break;
}
if (fragment != null) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, fragment);
fragmentTransaction.commit();
}
return true;
}
});
Я также попробовал решения onAttach и Deattach, но снова безуспешно.
Может быть, это связано с тем, что я использую канареечную версию или что-то не так в моих зависимостях от gradle?

НОВЫЕ ОБНОВЛЕНИЯ:
public class MainActivity extends AppCompatActivity {
private TextView mTextMessage;
private static final String TAG_FRAGMENT_ONE = "fragment_one";
private static final String TAG_FRAGMENT_TWO = "fragment_two";
private FragmentManager fragmentManager;
private Fragment currentFragment;
String TAG = "babken";
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
Fragment fragment = null;
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FragmentFirst.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
break;
case R.id.navigation_dashboard:
fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_TWO);
if (fragment == null) {
fragment = FragmentSecond.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_TWO);
break;
}
return true;
}
};
private void replaceFragment(@NonNull Fragment fragment, @NonNull String tag) {
if (!fragment.equals(currentFragment)) {
fragmentManager
.beginTransaction()
.replace(R.id.armen, fragment, tag)
.commit();
currentFragment = fragment;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FragmentFirst.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
}
}
7 ответов:
Я бы не стал хранить экземпляры фрагментов глобально. Вместо этого добавьте тег к фрагменту при их создании
getSupportFragmentManager() .beginTransaction() .replace(R.id.container, new PlaceholderFragment(), TAG_PLACEHOLDER) .commit();Тогда вы всегда можете получить его следующим образом:
Fragment fragment = getSupportFragmentManager().findFragmentByTag(TAG_PLACEHOLDER); if (fragment == null) { fragment = new PlaceholderFragment(); } getSupportFragmentManager() .beginTransaction() .replace(R.id.container, fragment, TAG_PLACEHOLDER) .commit();Обновление: я обновил свой ответ и предоставил полное решение:
private static final String TAG_FRAGMENT_ONE = "fragment_one"; private static final String TAG_FRAGMENT_TWO = "fragment_two"; private static final String TAG_FRAGMENT_THREE = "fragment_three"; private FragmentManager fragmentManager; private Fragment currentFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // instantiate the fragment manager fragmentManager = getSupportFragmentManager(); Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE); if (fragment == null) { fragment = FirstFragment.newInstance(); } replaceFragment(fragment, TAG_FRAGMENT_ONE); bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation); bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { Fragment fragment = null; switch (item.getItemId()) { case R.id.menu_dialer: // I'm aware that this code can be optimized by a method which accepts a class definition and returns the proper fragment Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE); if (fragment == null) { fragment = FirstFragment.newInstance(); } replaceFragment(fragment, TAG_FRAGMENT_ONE); break; case R.id.menu_email: Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_TWO); if (fragment == null) { fragment = SecondFragment.newInstance(); } replaceFragment(fragment, TAG_FRAGMENT_TWO); break; case R.id.menu_map: Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_THREE); if (fragment == null) { fragment = ThirdFragment.newInstance(); } replaceFragment(fragment, TAG_FRAGMENT_THREE); break; } return true; } }); } private void replaceFragment(@NonNull Fragment fragment, @NonNull String tag) { if (!fragment.equals(currentFragment)) { fragmentManager .beginTransaction() .replace(R.id.frameLayout, fragment, tag) .commit(); currentFragment = fragment; } }Дополнительная информация: Если вы хотите быть уверены, что состояния фрагментов не изменяются, и если вы также хотите иметь возможность прокручивать фрагменты, вы должны рассмотреть возможность использования ViewPager с FragmentStatePagerAdapter и изменять текущий фрагмент в адаптере с каждым событием click
Как насчет этого. Вы объявляете фрагменты в классе.
Fragment firstFragment,secondFragment,thirdFragment;Тогда в случае коммутатора Вы можете закодировать так:
switch (item.getItemId()) { case R.id.menu_dialer: if(firstFragment != null) { fragment = firstFragment; }else{ fragment = FirstFragment.newInstance(); } break; case R.id.menu_email: // the same ... break; case R.id.menu_map: //the same ... break; }
Создайте три фрагмента как члены класса и повторно используйте их.
public class MainActivity extends AppCompatActivity { private final Fragment mFirstFragment = new FirstFragment(); private final Fragment mSecondFragment = new SecondFragment(); private final Fragment mThirdFragment = new ThirdFragment(); @Override protected void onCreate(Bundle savedInstanceState) { ...... ...... FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.frameLayout, mFirstFragment); fragmentTransaction.commit(); bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { if (bottomNavigationView.getSelectedItemId() != item.getItemId()) { switch (item.getItemId()) { R.id.menu_dialer: replaceFragment(mFirstFragment); break; case R.id.menu_email: replaceFragment(mSecondFragment); break; case R.id.menu_map: replaceFragment(mThirdFragment); break; } } return true; } }); } private void replaceFragment(Fragment fragment) { FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.frameLayout, fragment); fragmentTransaction.commit(); } }
Все предыдущие ответы используют
fragmentTransaction.replace(...). Это заменит текущий фрагмент, уничтожив его (что вызывает проблему). Поэтому все эти решения на самом деле не будут работать.Это самое близкое, что я мог бы получить в качестве решения этой проблемы:
private void selectContentFragment(Fragment fragmentToSelect) { FragmentTransaction fragmentTransaction = this.getSupportFragmentManager().beginTransaction(); if (this.getSupportFragmentManager().getFragments().contains(fragmentToSelect)) { // Iterate through all cached fragments. for (Fragment cachedFragment : this.getSupportFragmentManager().getFragments()) { if (cachedFragment != fragmentToSelect) { // Hide the fragments that are not the one being selected. fragmentTransaction.hide(cachedFragment); } } // Show the fragment that we want to be selected. fragmentTransaction.show(fragmentToSelect); } else { // The fragment to be selected does not (yet) exist in the fragment manager, add it. fragmentTransaction.add(R.id.fragment_container, fragmentToSelect); } fragmentTransaction.commit(); }Чтобы сделать эту работу, вы должны отслеживать фрагменты в массиве (или в отдельных переменных) в вашей деятельности. Я для справки предварительно скопировал все фрагменты в разреженном виде.
У меня была похожая проблема, но этот код решил мою проблему.
public class MainActivity extends AppCompatActivity { final Fragment fragment1 = new HomeFragment(); final Fragment fragment2 = new DashboardFragment(); final Fragment fragment3 = new NotificationsFragment(); final FragmentManager fm = getSupportFragmentManager(); Fragment active = fragment1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation); navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener); fm.beginTransaction().add(R.id.main_container, fragment3, "3").hide(fragment3).commit(); fm.beginTransaction().add(R.id.main_container, fragment2, "2").hide(fragment2).commit(); fm.beginTransaction().add(R.id.main_container,fragment1, "1").commit(); } private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.navigation_home: fm.beginTransaction().hide(active).show(fragment1).commit(); active = fragment1; return true; case R.id.navigation_dashboard: fm.beginTransaction().hide(active).show(fragment2).commit(); active = fragment2; return true; case R.id.navigation_notifications: fm.beginTransaction().hide(active).show(fragment3).commit(); active = fragment3; return true; } return false; } };Надеюсь, это поможет.
Источник: BottomNavigationView с фрагментами (без восстановления фрагментов).
Можно использовать методы attach() и detach ():
private FirstFragment firstFragment = FirstFragment.newInstance(); private SecondFragment secondFragment= SecondFragment.newInstance(); private ThirdFragment thirdFragment = ThirdFragment.newInstance(); navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.menu_dialer: changeFragment(firstFragment, "firstFragment"); return true; case R.id.menu_email: changeFragment(secondFragment, "secondFragment"); return true; case R.id.menu_map: changeFragment(thirdFragment, "thirdFragment"); return true; } return false; } }); public void changeFragment(Fragment fragment, String tagFragmentName) { FragmentManager mFragmentManager = getSupportFragmentManager(); FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); Fragment currentFragment = mFragmentManager.getPrimaryNavigationFragment(); if (currentFragment != null) { fragmentTransaction.detach(currentFragment); } Fragment fragmentTemp = mFragmentManager.findFragmentByTag(tagFragmentName); if (fragmentTemp == null) { fragmentTemp = fragment; fragmentTransaction.add(R.id.content, fragmentTemp, tagFragmentName); } else { fragmentTransaction.attach(fragmentTemp); } fragmentTransaction.setPrimaryNavigationFragment(fragmentTemp); fragmentTransaction.setReorderingAllowed(true); fragmentTransaction.commitNowAllowingStateLoss(); }
Таким образом, я исследовал это в течение длительного времени и выяснил, что нет никакого способа повторно использовать фрагмент с автоматически сохраненными состояниями, вы должны сохранить необходимые состояния вручную, а затем восстановить их всякий раз, когда создается новый фрагмент, но что касается положения прокрутки, это слишком сложно, и даже в некоторых случаях невозможно сохранить состояние положения прокрутки(например, recycler view). Поэтому я использовал концепцию под названием видимость когда я нажимаю на кнопку этот фрагмент становится видимым другие скрывают автоматически.



Comments