如何从 BackStack 恢复现有 Fragment

2023-12-30

我正在学习如何使用片段。我有三个实例Fragment在类的顶部初始化。我将片段添加到这样的活动中:

声明和初始化:

Fragment A = new AFragment();
Fragment B = new BFragment();
Fragment C = new CFragment();

替换/添加:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, A);
ft.addToBackStack(null);
ft.commit();

这些片段工作正常。每个片段都附加到活动中,并毫无问题地保存到返回堆栈中。

所以当我启动时A, C, 进而B,堆栈看起来像这样:

| |
|B|
|C|
|A|
___

当我按下“后退”按钮时B被摧毁并且C已恢复。

但是,当我启动片段时A第二次,不是从返回堆栈恢复,而是添加到返回堆栈的顶部

| |
|A|
|C|
|A|
___

但我想重新开始A并销毁其上的所有碎片(如果有)。实际上,我只是喜欢默认的返回堆栈行为。

我该如何实现这个目标?

预期的: (A应恢复并销毁顶部片段)

| |
| |
| |
|A|
___

编辑:(A--C建议)

这是我的尝试代码:

private void selectItem(int position) {
        Fragment problemSearch = null, problemStatistics = null;
        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();
        String backStateName = null;
        Fragment fragmentName = null;
        boolean fragmentPopped = false;
        switch (position) {
        case 0:
            fragmentName = profile;
            break;
        case 1:
            fragmentName = submissionStatistics;
            break;
        case 2:
            fragmentName = solvedProblemLevel;
            break;
        case 3:
            fragmentName = latestSubmissions;
            break;
        case 4:
            fragmentName = CPExercise;
            break;
        case 5:
            Bundle bundle = new Bundle();
            bundle.putInt("problem_no", problemNo);
            problemSearch = new ProblemWebView();
            problemSearch.setArguments(bundle);
            fragmentName = problemSearch;
            break;
        case 6:
            fragmentName = rankList;
            break;
        case 7:
            fragmentName = liveSubmissions;
            break;
        case 8:
            Bundle bundles = new Bundle();
            bundles.putInt("problem_no", problemNo);
            problemStatistics = new ProblemStatistics();
            problemStatistics.setArguments(bundles);
            fragmentName = problemStatistics;
        default:
            break;
        }
        backStateName = fragmentName.getClass().getName();
        fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
        if (!fragmentPopped) {
            ft.replace(R.id.content_frame, fragmentName);
        }
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        ft.addToBackStack(backStateName);
        ft.commit();

        // I am using drawer layout
        mDrawerList.setItemChecked(position, true);
        setTitle(title[position]);
        mDrawerLayout.closeDrawer(mDrawerList);
    }

问题是,当我启动时A进而B,然后按“返回”,B被删除并且A已恢复。第二次按“返回”应退出应用程序。但它显示一个空白窗口,我必须第三次按回才能关闭它。

另外,当我启动时A, then B, then C, then B再次...

预期的:

| |
| |
|B|
|A|
___

Actual:

| |
|B|
|B|
|A|
___

我应该覆盖吗onBackPressed()有任何定制还是我错过了什么?


正在阅读文档 http://developer.android.com/reference/android/support/v4/app/FragmentManager.html,有一种方法可以根据事务名称或提交提供的 id 弹出返回堆栈。使用名称may更容易,因为它不需要跟踪可能更改的数字并强化“唯一的返回堆栈条目”逻辑。

因为您只需要一个返回堆栈条目Fragment,将后台状态名称命名为 Fragment 的类名称(通过getClass().getName())。那么当更换一个Fragment, 使用popBackStackImmediate() http://developer.android.com/reference/android/support/v4/app/FragmentManager.html#popBackStackImmediate%28java.lang.String,%20int%29方法。如果返回true,则说明后台栈中有该Fragment的实例。如果没有,则实际执行Fragment替换逻辑。

private void replaceFragment (Fragment fragment){
  String backStateName = fragment.getClass().getName();

  FragmentManager manager = getSupportFragmentManager();
  boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);

  if (!fragmentPopped){ //fragment not in back stack, create it.
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.content_frame, fragment);
    ft.addToBackStack(backStateName);
    ft.commit();
  }
}

EDIT

问题是 - 当我启动 A 然后启动 B,然后按后退按钮 B 被移除并恢复A。再次按后退按钮应该 退出应用程序。但它显示一个空白窗口,需要再按一次 关闭它。

这是因为FragmentTransaction被添加到返回堆栈中,以确保我们稍后可以将片段弹出到顶部。对此的快速解决方案是压倒性的onBackPressed()如果返回堆栈仅包含 1,则结束 ActivityFragment

@Override
public void onBackPressed(){
  if (getSupportFragmentManager().getBackStackEntryCount() == 1){
    finish();
  }
  else {
    super.onBackPressed();
  }
}

关于重复的返回堆栈条目,如果片段尚未弹出,则替换片段的条件语句显然是不同的比我原来的代码片段的。您所做的就是添加到返回堆栈,无论返回堆栈是否被弹出。

像这样的东西应该更接近你想要的:

private void replaceFragment (Fragment fragment){
  String backStateName =  fragment.getClass().getName();
  String fragmentTag = backStateName;

  FragmentManager manager = getSupportFragmentManager();
  boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);

  if (!fragmentPopped && manager.findFragmentByTag(fragmentTag) == null){ //fragment not in back stack, create it.
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.content_frame, fragment, fragmentTag);
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    ft.addToBackStack(backStateName);
    ft.commit();
  } 
}

条件发生了一些变化,因为在可见时选择相同的片段也会导致重复的条目。

执行:

我强烈建议不要采用更新的replaceFragment()方法分开,就像您在代码中所做的那样。所有逻辑都包含在该方法中,移动部件可能会导致问题。

这意味着您应该复制更新的replaceFragment()方法进入你的类然后改变

backStateName = fragmentName.getClass().getName();
fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped) {
            ft.replace(R.id.content_frame, fragmentName);
}
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();

所以这很简单

replaceFragment (fragmentName);

EDIT #2

要在返回堆栈更改时更新抽屉,请创建一个接受 Fragment 并比较类名的方法。如果有任何匹配,请更改标题和选择。还添加一个OnBackStackChangedListener如果有有效的片段,让它调用您的更新方法。

例如,在活动的onCreate(), add

getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {

  @Override
  public void onBackStackChanged() {
    Fragment f = getSupportFragmentManager().findFragmentById(R.id.content_frame);
    if (f != null){
      updateTitleAndDrawer (f);
    }

  }
});

还有另一种方法:

private void updateTitleAndDrawer (Fragment fragment){
  String fragClassName = fragment.getClass().getName();

  if (fragClassName.equals(A.class.getName())){
    setTitle ("A");
    //set selected item position, etc
  }
  else if (fragClassName.equals(B.class.getName())){
    setTitle ("B");
    //set selected item position, etc
  }
  else if (fragClassName.equals(C.class.getName())){
    setTitle ("C");
    //set selected item position, etc
  }
}

现在,每当返回堆栈发生变化时,标题和选中的位置将反映可见的Fragment.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何从 BackStack 恢复现有 Fragment 的相关文章

随机推荐