如何从 BackStack 恢复现有 Fragment [英] How to resume existing Fragment from BackStack

查看:20
本文介绍了如何从 BackStack 恢复现有 Fragment的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am learning how to use fragments. I have three instances of Fragment that are initialized at the top of the class. I am adding the fragment to an activity like this:

声明和初始化:

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();

这些片段工作正常.每个片段都附加到活动上,并且毫无问题地保存到后台堆栈中.

These snippets are working properly. Every fragment is attached to the activity, and is saved to the back stack without any problem.

所以当我启动 ACB 时,堆栈看起来像这样:

So when I launch A, C, and then B, the stack looks like this:

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

当我按下返回"按钮时,B 被破坏,C 被恢复.

And when I press the 'back' button, B is destroyed and C is resumed.

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

But, when I launch fragment A a second time, instead of resuming from back stack, it is added at the top of the back stack

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

但我想恢复 A 并销毁它上面的所有片段(如果有的话).实际上,我只是喜欢默认的返回堆栈行为.

But I want to resume A and destroy all fragments on top of it (if any). Actually, I just like the default back stack behavior.

我该如何实现?

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

Expected: (A should be resumed and top fragments should be destroyed)

| |
| |
| |
|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,然后按'back',B被删除,A 恢复.并再次按返回"应退出应用程序.但它显示一个空白窗口,我必须第三次按回才能关闭它.

The problem is, when I launch A and then B, then press 'back', B is removed and A is resumed. and pressing 'back' a second time should exit the app. But it is showing a blank window and I have to press back a third time to close it.

此外,当我启动 A,然后 B,然后 C,然后 B 再次...

Also, when I launch A, then B, then C, then B again...

预期:

| |
| |
|B|
|A|
___

实际:

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

我应该使用任何自定义来覆盖 onBackPressed() 还是我遗漏了什么?

Should I override onBackPressed() with any customization or am I missing something?

推荐答案

阅读文档,有一种方法可以根据事务名称或提交提供的 id 弹出返回堆栈.使用名称​​可能更容易,因为它不应该需要跟踪可能发生变化的数字并加强独特的返回堆栈条目"逻辑.

Reading the documentation, there is a way to pop the back stack based on either the transaction name or the id provided by commit. Using the name may be easier since it shouldn't require keeping track of a number that may change and reinforces the "unique back stack entry" logic.

由于您只需要每个 Fragment 一个返回堆栈条目,将返回状态命名为 Fragment 的类名(通过 getClass().getName()).然后在替换 Fragment 时,使用 popBackStackImmediate() 方法.如果它返回true,则表示在返回堆栈中存在Fragment 的实例.如果不是,则实际执行Fragment替换逻辑.

Since you want only one back stack entry per Fragment, make the back state name the Fragment's class name (via getClass().getName()). Then when replacing a Fragment, use the popBackStackImmediate() method. If it returns true, it means there is an instance of the Fragment in the back stack. If not, actually execute the Fragment replacement logic.

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();
  }
}

<小时>

编辑

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

The problem is - when i launch A and then B, then press back button, B is removed and A is resumed. and pressing again back button should exit the app. But it is showing a blank window and need another press to close it.

这是因为 FragmentTransaction 被添加到返回堆栈中,以确保我们可以稍后弹出顶部的片段.如果返回堆栈仅包含 1 个 Fragment

This is because the FragmentTransaction is being added to the back stack to ensure that we can pop the fragments on top later. A quick fix for this is overriding onBackPressed() and finishing the Activity if the back stack contains only 1 Fragment

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

关于重复的返回堆栈条目,如果片段尚未弹出,则替换片段的条件语句与我的原始代码片段明显不同.您所做的是添加到后退堆栈,而不管后退堆栈是否被弹出.

Regarding the duplicate back stack entries, your conditional statement that replaces the fragment if it hasn't been popped is clearly different than what my original code snippet's. What you are doing is adding to the back stack regardless of whether or not the back stack was popped.

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

Something like this should be closer to what you want:

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();
  } 
}

条件有所改变,因为在可见时选择相同的片段也会导致重复条目.

The conditional was changed a bit since selecting the same fragment while it was visible also caused duplicate entries.

实施:

我强烈建议不要像在代码中那样将更新后的 replaceFragment() 方法分开.所有的逻辑都包含在这个方法中,移动部件可能会导致问题.

I highly suggest not taking the the updated replaceFragment() method apart like you did in your code. All the logic is contained in this method and moving parts around may cause problems.

这意味着您应该将更新后的 replaceFragment() 方法复制到您的类中,然后更改

This means you should copy the updated replaceFragment() method into your class then change

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);

<小时>

编辑 #2

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

To update the drawer when the back stack changes, make a method that accepts in a Fragment and compares the class names. If anything matches, change the title and selection. Also add an OnBackStackChangedListener and have it call your update method if there is a valid Fragment.

比如在Activity的onCreate()中,添加

For example, in the Activity's 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.

Now, whenever the back stack changes, the title and checked position will reflect the visible Fragment.

这篇关于如何从 BackStack 恢复现有 Fragment的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆