You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

屏幕旋转时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重叠显示;再次点击返回键,重叠消失

问题原因与解决方案

问题根源

  1. 重复添加列表Fragment:屏幕旋转时Activity会重建,onCreate()会再次调用addCountriesFragment(),而Android系统会自动恢复之前已存在的Fragment(包括信息Fragment),导致容器内同时存在新添加的列表Fragment和恢复的信息Fragment。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 21:51:20