多次点击BottomNavigationView项导致Fragment重复加载问题求助
嘿,我帮你分析下这个Fragment重复加载的问题哈!你遇到的核心问题有两个:
问题根源
- 每次点击都创建新Fragment实例:你在每个导航项的case里都
new了一个全新的Fragment对象,比如每次点Home都new HomeFragment(),这就导致每次点击都会触发Fragment的创建、初始化流程,自然会重复加载。 - FragmentTransaction执行顺序错误:你的代码里先调用了
ft.commit(),之后才执行ft.replace()——但commit()调用后这个事务就已经提交完成了,后续的replace操作根本不会生效,反而会引发一些逻辑混乱。
解决方案一:通过Tag复用已存在的Fragment
这种方式会先查找是否已经有对应的Fragment实例存在,存在就直接复用,不存在再创建新的,能有效避免重复加载:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BottomNavigationView bottomNav = findViewById(R.id.bottom_navigation); bottomNav.setOnNavigationItemSelectedListener(navListener); // 初始化默认显示的HomeFragment if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .replace(R.id.fragment_container, new HomeFragment(), "HOME_FRAGMENT") .commit(); setTitle("Playing11"); } } private BottomNavigationView.OnNavigationItemSelectedListener navListener = new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { FragmentManager fm = getSupportFragmentManager(); Fragment selectedFragment = null; String fragmentTag = ""; switch (item.getItemId()) { case R.id.navigation_home: setTitle("Playing11"); fragmentTag = "HOME_FRAGMENT"; selectedFragment = fm.findFragmentByTag(fragmentTag); if (selectedFragment == null) { selectedFragment = new HomeFragment(); } break; case R.id.navigation_dashboard: setTitle("Dashboard"); fragmentTag = "DASHBOARD_FRAGMENT"; selectedFragment = fm.findFragmentByTag(fragmentTag); if (selectedFragment == null) { selectedFragment = new HomeFragment1(); } break; case R.id.navigation_notifications: setTitle("Safe11"); fragmentTag = "NOTIFICATIONS_FRAGMENT"; selectedFragment = fm.findFragmentByTag(fragmentTag); if (selectedFragment == null) { selectedFragment = new HomeFragment2(); } break; case R.id.navigation_notification: setTitle("More"); fragmentTag = "MORE_FRAGMENT"; selectedFragment = fm.findFragmentByTag(fragmentTag); if (selectedFragment == null) { selectedFragment = new HomeFragment3(); } break; } // 执行Fragment替换并提交事务 if (selectedFragment != null) { fm.beginTransaction() .replace(R.id.fragment_container, selectedFragment, fragmentTag) .commit(); } return true; } };
解决方案二:使用ViewPager+FragmentPagerAdapter(更推荐)
这种方式利用ViewPager的缓存机制,Fragment会被缓存起来,切换时不会重复创建,只会执行生命周期的onResume等方法,体验更流畅:
第一步:修改布局,添加ViewPager
把原来的fragment_container替换成ViewPager(或者保留容器,在里面嵌套ViewPager):
<!-- activity_main.xml --> <androidx.viewpager.widget.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/bottom_navigation"/>
第二步:Activity代码实现
private ViewPager viewPager; private BottomNavigationView bottomNav; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewPager = findViewById(R.id.view_pager); bottomNav = findViewById(R.id.bottom_navigation); // 配置ViewPager的适配器 ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); adapter.addFragment(new HomeFragment(), "Playing11"); adapter.addFragment(new HomeFragment1(), "Dashboard"); adapter.addFragment(new HomeFragment2(), "Safe11"); adapter.addFragment(new HomeFragment3(), "More"); viewPager.setAdapter(adapter); // ViewPager切换时同步BottomNavigationView的选中状态 viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {} @Override public void onPageSelected(int position) { switch (position) { case 0: bottomNav.setSelectedItemId(R.id.navigation_home); setTitle("Playing11"); break; case 1: bottomNav.setSelectedItemId(R.id.navigation_dashboard); setTitle("Dashboard"); break; case 2: bottomNav.setSelectedItemId(R.id.navigation_notifications); setTitle("Safe11"); break; case 3: bottomNav.setSelectedItemId(R.id.navigation_notification); setTitle("More"); break; } } @Override public void onPageScrollStateChanged(int state) {} }); // BottomNavigationView点击时切换ViewPager的页面 bottomNav.setOnNavigationItemSelectedListener(item -> { switch (item.getItemId()) { case R.id.navigation_home: viewPager.setCurrentItem(0); return true; case R.id.navigation_dashboard: viewPager.setCurrentItem(1); return true; case R.id.navigation_notifications: viewPager.setCurrentItem(2); return true; case R.id.navigation_notification: viewPager.setCurrentItem(3); return true; } return false; }); } // 自定义ViewPager适配器 private static class ViewPagerAdapter extends FragmentPagerAdapter { private final List<Fragment> fragmentList = new ArrayList<>(); private final List<String> titleList = new ArrayList<>(); public ViewPagerAdapter(FragmentManager fm) { super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); } public void addFragment(Fragment fragment, String title) { fragmentList.add(fragment); titleList.add(title); } @Override public Fragment getItem(int position) { return fragmentList.get(position); } @Override public int getCount() { return fragmentList.size(); } }
补充说明
- 方案一适合简单的场景,代码改动小;
- 方案二更适合复杂的Fragment场景,缓存机制能提升切换流畅度,也能避免很多生命周期相关的问题。
内容的提问来源于stack exchange,提问作者Sánthösh Kúmár
相关产品推荐
相关产品推荐

