Android SupportFragmentManager,popbackstack&;屏幕旋转

Android SupportFragmentManager,popbackstack&;屏幕旋转,android,android-fragments,Android,Android Fragments,我有一个让我发疯的奇怪问题 场景: @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Checks the orientation of the screen for landscape and portrait if (newConfig.orientat

我有一个让我发疯的奇怪问题

场景:

 @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        // Checks the orientation of the screen for landscape and portrait
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {

        } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {

        }
    }
@Override
public boolean onSupportNavigateUp() {
    onBackPressed();
    return true;
}

@Override
public void onBackPressed() {
    super.onBackPressed();
}
@Override
public boolean onSupportNavigateUp() {
    onBackPressed();
    return true;
}

@Override
public void onBackPressed() {
    if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
            getSupportFragmentManager().popBackStack(getSupportFragmentManager()
                    .getBackStackEntryAt(0).getId(), 
                    FragmentManager.POP_BACK_STACK_INCLUSIVE));
    } else {
            super.onBackPressed();
    }
 }
我有
活动
A作为启动器
活动
活动
B可以通过
活动A中的
意图
启动

Activity A --> Activity B
活动B有一个初始的
片段
称之为
片段
C,它在
活动
B中实例化,如下所示:

if(getSupportFragmentManager().findFragmentByTag(FragmentC.TAG) == null) {
        getSupportFragmentManager()
                 .beginTransaction()
                 .add(R.id.container,
                  FragmentC.newInstance(null), // optional bundle
                  FragmentC.TAG)
                 .commit();
}
Fragment
C中,我在
ViewHolder上有一个
OnClickListener
RecyclerView
,它通过
Fragment
C调用宿主
活动
B。此时,我启动
FragmentTransaction
(在
Activity
B中)要替换当前的
Fragment
C,请将其称为
Fragment
D,我将此事务添加到backstack:

getSupportFragmentManager()
          .beginTransaction()
          .setCustomAnimations(
                  R.anim.fragment_slide_enter,
                  R.anim.fragment_slide_exit,
                  R.anim.fragment_slide_enter_pop,
                  R.anim.fragment_slide_exit_pop)
          .replace(R.id.container,
                        FragmentD.newInstance(bundle),
                        FragmentD.TAG)
          .addToBackStack(null)
          .commit();
因此,在这一点上,我有
活动
B和
Fragment
D,事务从
Fragment
C到
Fragment
D在后堆栈中,像这样处理后压:

@Override
public boolean onSupportNavigateUp() {
    onBackPressed();
    return true;
}

@Override
public void onBackPressed() {
    super.onBackPressed();
}
从开始
Activity
A到
Activity
B-->
Fragment
D再到后面,导航可以正确地向前和向后运行

问题:

 @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        // Checks the orientation of the screen for landscape and portrait
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {

        } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {

        }
    }
@Override
public boolean onSupportNavigateUp() {
    onBackPressed();
    return true;
}

@Override
public void onBackPressed() {
    super.onBackPressed();
}
@Override
public boolean onSupportNavigateUp() {
    onBackPressed();
    return true;
}

@Override
public void onBackPressed() {
    if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
            getSupportFragmentManager().popBackStack(getSupportFragmentManager()
                    .getBackStackEntryAt(0).getId(), 
                    FragmentManager.POP_BACK_STACK_INCLUSIVE));
    } else {
            super.onBackPressed();
    }
 }
但是,如果我在
Fragment
D中并旋转屏幕,然后
pop
返回到
Fragment
C(撤消事务),我会看到一个空白的
Fragment
。更奇怪的是,所有
Fragment
C的生命周期方法
OnCreate()
OnCreateView()
OnActivityCreated()
都被调用,并且我拥有有效的数据/对象(通过设置断点进行检查)

然而,奇怪的是,如果我在从
Fragment
C-->
Fragment
D执行
add
事务,显然这两个都会同时显示在彼此的顶部,我旋转屏幕,按下后退键并删除事务,那么没有问题,显然不是解决方案,只是观察

我已经搜索过了,但我读过和尝试过的一切都不起作用。我以前也实现过类似的东西,效果很好,但我在这里很挣扎-任何帮助都将不胜感激

编辑


如果应用程序进入暂停状态(单独运行,屏幕关闭),然后再次唤醒并继续运行,则片段将按其应有的方式显示。我在恢复时没有初始化,所以我不知道为什么会这样。

您的问题是由于屏幕旋转造成的

根据文档,每次设备配置更改时(如用户旋转屏幕时),您的活动都将被销毁并重新创建。当屏幕更改方向时,系统将销毁并重新创建前台活动,因为屏幕配置已更改,并且您的活动可能需要加载其他资源(如布局)

在这种情况下,活动B再次启动初始框架C,所有生命周期方法再次执行

解决方案

  • 您可以在纵向模式下限制您的活动
  • 您应该在片段中处理
    onConfigurationChanged()
    方法,并定义
    setRetainInstance(true))
  • 公共void setRetainInstance(布尔保留) 控制是否在活动重新创建期间保留片段实例(例如从配置更改)。这只能用于不在后堆栈中的片段。如果设置,则重新创建活动时片段生命周期将略有不同:

     @Override
        public void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
    
            // Checks the orientation of the screen for landscape and portrait
            if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
    
            } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
    
            }
        }
    
    @Override
    public boolean onSupportNavigateUp() {
        onBackPressed();
        return true;
    }
    
    @Override
    public void onBackPressed() {
        super.onBackPressed();
    }
    
    @Override
    public boolean onSupportNavigateUp() {
        onBackPressed();
        return true;
    }
    
    @Override
    public void onBackPressed() {
        if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
                getSupportFragmentManager().popBackStack(getSupportFragmentManager()
                        .getBackStackEntryAt(0).getId(), 
                        FragmentManager.POP_BACK_STACK_INCLUSIVE));
        } else {
                super.onBackPressed();
        }
     }
    

    我以前也遇到过同样的问题。我已经提出了满足我要求的解决方案 在活动B的menifest中使用以下代码

     android:configChanges="keyboardHidden|orientation|screenSize"
    

    上面的行不允许破坏您的活动,因此您没有碎片破坏问题

    问题已解决

    好的,谢谢你的建议,但我已经自己解决了这个问题

    解决方案是什么?很简单,我踢自己

    在弹出backbackback时,我从未将
    片段的
    getId()
    包含到“pop”中:

    之前的代码依赖于
    FragmentActivity
    onBackPressed()中弹出backbackbackback本身。事实上,文件和方法应注意以下事项:

    /**
     * Take care of popping the fragment back stack or finishing the activity
     * as appropriate.
     */
    @Override
    public void onBackPressed() {
         if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
                super.onBackPressed();
          }
    }
    
    但是,当发生配置更改并且您希望弹出前一事务的backbackback时,这似乎无法正常工作。代码解决方案是自己手动“弹出”后堆栈:

    之前:

     @Override
        public void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
    
            // Checks the orientation of the screen for landscape and portrait
            if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
    
            } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
    
            }
        }
    
    @Override
    public boolean onSupportNavigateUp() {
        onBackPressed();
        return true;
    }
    
    @Override
    public void onBackPressed() {
        super.onBackPressed();
    }
    
    @Override
    public boolean onSupportNavigateUp() {
        onBackPressed();
        return true;
    }
    
    @Override
    public void onBackPressed() {
        if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
                getSupportFragmentManager().popBackStack(getSupportFragmentManager()
                        .getBackStackEntryAt(0).getId(), 
                        FragmentManager.POP_BACK_STACK_INCLUSIVE));
        } else {
                super.onBackPressed();
        }
     }
    
    之后(工作解决方案):

     @Override
        public void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
    
            // Checks the orientation of the screen for landscape and portrait
            if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
    
            } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
    
            }
        }
    
    @Override
    public boolean onSupportNavigateUp() {
        onBackPressed();
        return true;
    }
    
    @Override
    public void onBackPressed() {
        super.onBackPressed();
    }
    
    @Override
    public boolean onSupportNavigateUp() {
        onBackPressed();
        return true;
    }
    
    @Override
    public void onBackPressed() {
        if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
                getSupportFragmentManager().popBackStack(getSupportFragmentManager()
                        .getBackStackEntryAt(0).getId(), 
                        FragmentManager.POP_BACK_STACK_INCLUSIVE));
        } else {
                super.onBackPressed();
        }
     }
    
    getId()
    的文档说明:

    /**
     * Return the unique identifier for the entry.  This is the only
     * representation of the entry that will persist across activity
     * instances.
     */
    public int getId();
    

    因此,基本上,在发生配置更改(托管
    活动
    已被销毁并重新创建)后,成功“弹出”backbackback的唯一方法是引用
    片段
    唯一的
    id

    单个
    片段
    并托管
    活动
    处理配置更改  适当-此解决方案不适用于我遇到的问题。单个
    片段
    和托管
    活动
    处理配置更改  对于活动,您可以在清单文件中定义Configchanges属性。对于fragment,您可以重写onConfigurationChanged()方法,就像我在解决方案中解释的那样,如果您面临problem@NishchalAndroid这是有道理的。我观察到,对于片段,一旦生命周期方法按原样调用,则在旋转屏幕时调用
    onCreate()
    onestroy()
    而不调用
    onResume()
    onPause()
    。记录生命周期方法并亲自查看@MarkKeen@Rolbin我已经检查过了(我提到了破po