Android横竖屏切换时如何避免同一个Fragment创建两个实例
问题根因
- 屏幕旋转触发Activity重建时,系统会自动恢复所有之前添加到FragmentManager中的Fragment实例,包括之前由ViewPager2创建的FragmentB
- 原有代码存在两处重复创建FragmentB的逻辑:
- 横屏场景下手动调用
new FragmentB()加载到右侧FrameLayout - ViewPager2的FragmentStateAdapter的
createFragment方法每次position=1时都返回新的FragmentB实例
- 横屏场景下手动调用
- 额外笔误:原有
onCreateView中第二个条件判断依然是isLandScape(),属于逻辑错误。
修复方案(复用FragmentB实例,无重复创建)
步骤1:修改MasterFragment逻辑,统一管理FragmentB实例
public class MasterFragment extends Fragment { NewPagerAdapter mSectionsPagerAdapter; ViewPager2 mViewPager; boolean mSplitView; // 新增:全局持有唯一的FragmentB实例 private FragmentB mFragmentB; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 优先从FragmentManager中恢复已存在的FragmentB,不存在才新建 Fragment existingB = getChildFragmentManager().findFragmentByTag(FragmentB.TAG); if (existingB instanceof FragmentB) { mFragmentB = (FragmentB) existingB; } else { mFragmentB = new FragmentB(); } } public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.masterfragment, container, false); mSplitView = isLandScape(); // 先把FragmentB从原有容器中移除,避免重复添加报错 if (mFragmentB.isAdded()) { getChildFragmentManager().beginTransaction().remove(mFragmentB).commitNow(); } if (mSplitView) { // 横屏:把FragmentB添加到右侧FrameLayout getChildFragmentManager().beginTransaction().add(R.id.container, mFragmentB, FragmentB.TAG).commitNow(); } mViewPager = view.findViewById(R.id.pager); mSectionsPagerAdapter = new NewPagerAdapter(getChildFragmentManager(), getLifecycle()); mViewPager.setOffscreenPageLimit(2); // 不需要7,2足够覆盖两个页面 mViewPager.setAdapter(mSectionsPagerAdapter); // 竖屏场景下如果之前在FragmentB页面,切换后保持显示 if (!mSplitView && savedInstanceState != null) { int lastPage = savedInstanceState.getInt("last_pager_position", 0); mViewPager.setCurrentItem(lastPage); } return view; } // 新增:保存ViewPager当前位置,横竖屏切换后恢复 @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); if (mViewPager != null) { outState.putInt("last_pager_position", mViewPager.getCurrentItem()); } } public boolean isLandScape() { int orientation = getResources().getConfiguration().orientation; return orientation == Configuration.ORIENTATION_LANDSCAPE; } public boolean backOnePage() { if(mViewPager == null || mSplitView) return false; int page = mViewPager.getCurrentItem(); if(page > 0) { mViewPager.setCurrentItem(page - 1); return true; } return false; } public void viewFragmentB() { if(!mSplitView) mViewPager.setCurrentItem(1); } public class NewPagerAdapter extends FragmentStateAdapter { public NewPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) { super(fragmentManager, lifecycle); } @NonNull @Override public Fragment createFragment(int position) { if(position == 0) return new FragmentA(); // 直接返回全局唯一的FragmentB实例,不新建 return mFragmentB; } @Override public int getItemCount() { return mSplitView ? 1 : 2; } } }
步骤2:删除原有冗余逻辑
原有onCreateView里的第二个else if (isLandScape())逻辑完全冗余且错误,直接删除即可,因为横竖屏对应的layout文件本身已经做了区分,不需要动态移除View。
验证效果
修改后旋转屏幕,FragmentB的COUNTER值只会在首次创建时+1,旋转时不会重复增加,不会出现同时两个实例的情况,地图页面状态也能完全复用不需要重新加载。
内容的提问来源于stack exchange,提问作者user3115340
相关产品推荐
相关产品推荐

