屏幕旋转时Fragment重叠问题求助
问题描述
正在学习Fragment,开发一个双Fragment的简单App:一个Fragment显示国家名称列表,另一个展示对应国家信息。实现逻辑是在MainActivity的onCreate()中先添加列表Fragment,再通过ViewModel监听数据变化,当用户点击列表项时切换到信息Fragment。
相关代码如下:
MainActivity 的 onCreate 方法
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); addCountriesFragment(); viewModel = new ViewModelProvider(this).get(ListItemViewModel.class); viewModel.getListItemPicked().observe(this, state -> { addCountryDescriptionFragment(state); }); }
添加列表Fragment的方法
private void addCountriesFragment(){ fragmentTransaction = fragmentManager.beginTransaction(); CountriesFragment countriesFragment = new CountriesFragment(); fragmentTransaction.add(R.id.fragmentContainer, countriesFragment); fragmentTransaction.commit(); }
添加信息Fragment的方法
private void addCountryDescriptionFragment(int index){ fragmentTransaction = fragmentManager.beginTransaction(); CountryDescriptionFragment countryDescriptionFragment = new CountryDescriptionFragment(); Bundle args = new Bundle(); args.putInt("itemIndex", index); countryDescriptionFragment.setArguments(args); fragmentTransaction.replace(R.id.fragmentContainer, countryDescriptionFragment); fragmentTransaction.addToBackStack(null); fragmentTransaction.commit(); }
MainActivity 布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" android:background="@color/white" android:orientation="vertical"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/fragmentContainer" /> </LinearLayout>
遇到的问题:
- App启动后正常显示列表Fragment
- 点击列表项,列表Fragment被替换为信息Fragment,功能正常
- 竖屏切换横屏后,信息Fragment正常显示
- 点击返回键,列表Fragment和信息Fragment重叠显示;再次点击返回键,重叠消失
问题原因与解决方案
问题根源
- 重复添加列表Fragment:屏幕旋转时Activity会重建,
onCreate()会再次调用addCountriesFragment(),而Android系统会自动恢复之前已存在的Fragment(包括信息Fragment),导致容器内同时存在新添加的列表Fragment和恢复的信息Fragment。 - ViewModel观察者重复触发:ViewModel是跨配置变化的,旋转后观察者会再次收到之前的列表项选中事件,导致重复执行
replace操作并添加到回退栈,回退栈中存在多份相同操作。
解决方案
方案1:避免重建时重复添加列表Fragment
在onCreate()中判断savedInstanceState是否为null,仅在首次创建Activity时添加列表Fragment;重建时FragmentManager会自动恢复之前的Fragment,无需重复添加。
修改onCreate()代码:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 仅在首次创建时添加列表Fragment if (savedInstanceState == null) { addCountriesFragment(); } viewModel = new ViewModelProvider(this).get(ListItemViewModel.class); viewModel.getListItemPicked().observe(this, state -> { addCountryDescriptionFragment(state); }); }
方案2:防止观察者重复执行切换操作
在切换Fragment前判断当前容器内是否已经显示目标信息Fragment,避免重复执行replace操作。
修改addCountryDescriptionFragment():
private void addCountryDescriptionFragment(int index){ // 检查当前是否已经显示对应索引的信息Fragment CountryDescriptionFragment existingFragment = (CountryDescriptionFragment) fragmentManager.findFragmentById(R.id.fragmentContainer); if (existingFragment != null && existingFragment.getArguments().getInt("itemIndex") == index) { return; } fragmentTransaction = fragmentManager.beginTransaction(); CountryDescriptionFragment countryDescriptionFragment = new CountryDescriptionFragment(); Bundle args = new Bundle(); args.putInt("itemIndex", index); countryDescriptionFragment.setArguments(args); fragmentTransaction.replace(R.id.fragmentContainer, countryDescriptionFragment); fragmentTransaction.addToBackStack(null); fragmentTransaction.commit(); }
额外优化:给Fragment设置唯一Tag
在添加列表Fragment时设置Tag,后续可以更精准判断是否已存在:
private void addCountriesFragment(){ fragmentTransaction = fragmentManager.beginTransaction(); CountriesFragment countriesFragment = new CountriesFragment(); // 设置唯一标识Tag fragmentTransaction.add(R.id.fragmentContainer, countriesFragment, "COUNTRIES_LIST_TAG"); fragmentTransaction.commit(); }
对应修改onCreate()中的判断逻辑:
if (savedInstanceState == null && fragmentManager.findFragmentByTag("COUNTRIES_LIST_TAG") == null) { addCountriesFragment(); }
内容的提问来源于stack exchange,提问作者Aakash Dasgupta
相关产品推荐
相关产品推荐

