Fragment addToBackStack() 和 popBackStackImmediate() 不起作用

27

我目前正在构建一个Android应用程序(14 <= SDK <= 21),使用一个ActionBarActivity和更多的Fragments(例如ListFragmentMapFragment),它们在单个FrameLayout视图中进行切换。

ActionBarActivity会自动替换/提交片段A。当用户点击按钮时,托管Activity会替换/提交一个新的不同片段B。我的目标是让用户在按下后退按钮时立即返回到片段A。

现在是一些代码。

MainActivity

public class MainActivity extends ActionBarActivity implements StopFragment.OnFragmentInteractionListener,
    StopItemFragment.OnFragmentInteractionListener {
...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        fragmentManager = getFragmentManager();
        fragmentManager.enableDebugLogging(true);
        ...
        if (fragmentManager.findFragmentById(R.id.content_frame) == null) {
            StopItemFragment list = StopItemFragment.newInstance(null); //A - extends ListFragment
            fragmentManager.beginTransaction()
                .replace(R.id.content_frame, list)
                .addToBackStack(null)
                .commit();
        }
        ...

        @Override
        public void onFragmentInteraction(String id) {
        selectItem(Integer.parseInt(id));
        }


       private void selectItem(int position) {
       StopFragment fragment = StopFragment.newInstance(null, null); //B - extends Fragment
       ...
       fragmentManager.beginTransaction()
            .replace(R.id.content_frame, fragment)
            .commit();

       ...
       }
}


问题

即使调用了addToBackStack(),但当我在片段B时,我无法返回到片段A。MainActivity直接关闭。 尽管我尝试自己管理返回栈,但没有成功。我可以看到片段已经在堆栈中,但如果我调用popBackStackImmediate(),片段A会弹出,并且片段事务不会执行。(第一次按下返回键没反应,第二次才关闭活动)

我还附上了FragmentManager的logcat日志:
http://pastebin.com/hFLHprL8

6个回答

72

对于那些仍在寻找解决方案的人。

在主Activity类(它托管碎片)中,只需覆盖onBackPressed()方法即可。

@Override
public void onBackPressed() {
    if (getFragmentManager().getBackStackEntryCount() > 0 ){
        getFragmentManager().popBackStack();
    } else {
        super.onBackPressed();
    }
}

在 Fragment 中没有 onBackPressed() 方法,而这个方法只适用于 activity。因此,当我们按下返回键时,会显示 activity 的默认行为,即

你要么回到上一个 activity(如果有的话),要么退出应用程序。

现在我们需要重写此方法,告诉 activity 当我们按下返回键时,如果后退栈中有任何片段,请将它们弹出(这就是 addToBackStack() 起作用的时候)。否则按照默认行为操作。


2
谢谢。我想知道为什么安卓文档没有提到这个重要的事情?或者是我错过了它... - Ryde

6

尝试这样做(注意对于fragmentA是添加而不是替换,对于fragmentB使用addToBackStack()

StopItemFragment list = StopItemFragment.newInstance(null); //A - extends ListFragment
        fragmentManager.beginTransaction()
            .add(R.id.content_frame, list)
            .commit();

并且

StopFragment fragment = StopFragment.newInstance(null, null); //B - extends Fragment
   ...
   fragmentManager.beginTransaction()
        .replace(R.id.content_frame, fragment)
        .addToBackStack("FragmentB")
        .commit();

1
这样我将片段B添加到堆栈上,而不是A。来自“FragmentTransaction”文档: addToBackStack():将此事务添加到后退堆栈中。这意味着该事务将在提交后被记住,并在稍后从堆栈中弹出时撤消其操作。 - Emanuele

5

我需要手动调用FragmentManagerbeginTransaction()commit()
通过重写onBackPressed()方法解决:

@Override
public void onBackPressed() {
    ...
    if (fragmentManager.getBackStackEntryCount() > 1){
        fragmentManager.popBackStackImmediate();
        fragmentManager.beginTransaction().commit();
    } else {
        //handle finish
        finish(); // Closes app
    }
}

popBackStackImmediatepopBackStack慢,为了避免性能问题,请尽可能使用popBackStack - Faisal Naseer

1
如果您在使用addToBackStack()和popBackStack()时遇到困难,那么可以简单地使用:
FragmentTransaction ft =getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, new HomeFragment(), "Home");
ft.commit();`

在您的Activity中,在OnBackPressed()方法中通过标签找到fragment,然后执行您需要的操作。
Fragment home = getSupportFragmentManager().findFragmentByTag("Home");

if (home instanceof HomeFragment && home.isVisible()) {
    // do you stuff
}

更多信息请参见 https://github.com/DattaHujare/NavigationDrawer。我从未使用 addToBackStack() 来处理碎片。

0

popBackStack()popBackStackImmediatly()无法正常工作的原因是您没有将要弹出的片段添加到后退栈中。为了解决这个问题,您需要在添加/替换片段时调用addToBackStack()方法,将其添加到后退栈中。


0

首先,我想对突出显示的[Nick]的答案表示衷心感谢(声望不足无法在下面发表评论),因为它帮助我知道了Android文档中没有提到的一些内容。

其次,现在是2019年,在尝试寻找Androidx偏好设置的类似方法时,我很长时间都找不到任何东西,直到我同时查看了这个问题和Kotlin官方示例代码,这个问题引导我找到了一个线索,而示例代码的方式实际上是我的解决方案,在我的情况下非常有效,只需要微小的重写即可,希望它也能帮助那些正在寻找官方解决方案的人们。

@Override
public boolean onSupportNavigateUp() {

    if (getSupportFragmentManager().popBackStackImmediate()){
        return true;
    }

    return super.onSupportNavigateUp();
}

Java中的上述代码(屏幕截图)

P.S. 这是我在stacksoverflow上的第一个回答,使用了至少5年时间 ^^


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接