使用Android Navigation Component如何避免点击底部导航时地图页刷新
问题核心原因
Jetpack Navigation 组件默认的底部导航绑定逻辑,切换页签时会销毁前一个页签的Fragment实例,下次切回时会重新执行onCreateView、onViewCreated生命周期,所以地图会被重新加载。
适配现有NavGraph的解决方案
你当前已经拆分了三个子导航图,最合适的方案是多NavHostFragment独立存储+hide/show切换,无需修改现有子页面导航逻辑,也能保留每个页签的独立回退栈,同时避免地图页重建:
1. 修改主页布局
把activity_main.xml中原先的单个NavHostFragment替换为普通的Fragment容器:
<FrameLayout android:id="@+id/nav_container" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottomNavigationView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" app:menu="@menu/bottom_nav_menu" />
2. 初始化三个独立的NavHostFragment
在MainActivity的onCreate中提前初始化三个导航图对应的NavHostFragment,全部添加到容器后默认显示首页导航:
// 全局变量 private NavHostFragment currentNavHost; private NavHostFragment homeNavHost; private NavHostFragment dashboardNavHost; private NavHostFragment menuNavHost; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigationView); // 初始化三个导航宿主 homeNavHost = NavHostFragment.create(R.navigation.home); dashboardNavHost = NavHostFragment.create(R.navigation.dashboard); menuNavHost = NavHostFragment.create(R.navigation.menu); // 全部添加到容器,默认显示首页,隐藏其他 getSupportFragmentManager().beginTransaction() .add(R.id.nav_container, homeNavHost, "home") .add(R.id.nav_container, dashboardNavHost, "dashboard") .hide(dashboardNavHost) .add(R.id.nav_container, menuNavHost, "menu") .hide(menuNavHost) .commit(); currentNavHost = homeNavHost; // 绑定ActionBar到当前显示的导航宿主 setupActionBarWithNavController(this, currentNavHost.getNavController()); }
3. 重写底部导航切换逻辑
切换时仅做hide和show操作,不会销毁任何NavHost实例,对应页签下的Fragment栈完全保留:
bottomNavigationView.setOnItemSelectedListener(item -> { NavHostFragment targetNavHost = null; if (item.getItemId() == R.id.home) { targetNavHost = homeNavHost; } else if (item.getItemId() == R.id.dashboard) { targetNavHost = dashboardNavHost; } else if (item.getItemId() == R.id.menu) { targetNavHost = menuNavHost; } if (targetNavHost == null || targetNavHost == currentNavHost) { return false; } // 切换导航宿主 getSupportFragmentManager().beginTransaction() .hide(currentNavHost) .show(targetNavHost) .commit(); currentNavHost = targetNavHost; // 更新ActionBar绑定 setupActionBarWithNavController(this, currentNavHost.getNavController()); return true; });
4. 地图页额外缓存优化
在HomeFragment中增加View缓存,避免即使宿主不销毁,也不会重复inflate布局:
public class HomeFragment extends Fragment { private View rootView; private SupportMapFragment mapFragment; private GoogleMap googleMap; private OnMapReadyCallback callback = new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap map) { googleMap = map; LatLng sydney = new LatLng(-34, 151); googleMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney")); googleMap.moveCamera(CameraUpdateFactory.newLatLng(sydney)); } }; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // 已初始化过View直接返回,避免重复加载 if (rootView != null) { // 移除原有父布局绑定,避免重复添加报错 if (rootView.getParent() != null) { ((ViewGroup) rootView.getParent()).removeView(rootView); } return rootView; } rootView = inflater.inflate(R.layout.fragment_home, container, false); return rootView; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { // 地图已初始化过直接跳过 if (googleMap != null) { return; } super.onViewCreated(view, savedInstanceState); mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map); if (mapFragment != null) { mapFragment.getMapAsync(callback); } } }
原有Transaction方案修复
你之前的Transaction方案问题出在每次切换都执行remove操作,把Fragment实例销毁了,把remove换成hide、add操作换成仅第一次添加,后续切换仅做hide和show,也能实现不销毁地图页的效果。
内容的提问来源于stack exchange,提问作者Jessica Katerina
相关产品推荐
相关产品推荐

