如何在选项卡中维护片段状态而不使用后退堆栈?

12

我试图在onSaveInstanceState中保存片段状态,但当我返回片段时,它总是重新加载,而不是从上次状态开始。

我研究了onCreateViewonActivityCreated,发现它们始终将onSaveInstanceState设为null。

public void navigateFragment(String tag, Fragment fragment,
        boolean shouldAdd) {

    FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction ft = manager.beginTransaction();


    if (shouldAdd)
        mStacks.get(tag).push(fragment); // push fragment on stack

    ft.replace(android.R.id.tabcontent, fragment);

    if (shouldAdd)
        ft.addToBackStack(tag);

    ft.commit();

    }

由于在标签页中,backstack是无用的,所以我无法使用它。非常感谢您的任何帮助。


你可以尝试这些示例,https://github.com/curioustechizen/blog-nested-fragments-backstack - Achin
我想保存数据,以便在从上一个屏幕重新加载片段时显示相同的数据。@The Original Android - moDev
你尝试过在onPause中将状态保存到sharedPreferences吗?这样当fragment调用onCreate时,你就可以重新加载状态了。 - Nicholas Ng
@droid_dev,你用过FragmentTabHost吗? - Sowmia Sundararajan
@droid_dev,你提到你有选项卡。你能说一下你是如何实现选项卡的吗?(使用按钮来实现选项卡吗?) - Sowmia Sundararajan
显示剩余7条评论
7个回答

6
在这种情况下,您需要自己管理片段的状态。我不知道您的代码如何工作,所以我能做的唯一的事情就是给您一些提示。
您需要实现的第一件事是保存片段的状态。假设所有片段都有唯一的ID。在这种情况下,您需要创建一个映射来保存所有状态:
private final Map<String, Fragment.SavedState> mFragmentStates = new HashMap<>();

private void saveFragmentState(String id, Fragment fragment) {
    Fragment.SavedState fragmentState = 
            getSupportFragmentManager().saveFragmentInstanceState(fragment);
    mFragmentStates.put(id, fragmentState);
}

在删除一个片段之前,您需要调用此方法。然后我们需要恢复片段的状态,这就是如何做到的:

private void restoreFragmentState(String id, Fragment fragment) {
    Fragment.SavedState fragmentState = mFragmentStates.remove(id);
    if (fragmentState != null) {
        fragment.setInitialSavedState(savedState);
    }
}

在将一个片段添加到事务之前,您需要调用此方法。

提供的代码应该可以正常工作,但为了在活动重新创建时正确地工作,我们需要正确保存和恢复mFragmentStates

private static final String KEY_FRAGMENT_STATES = "fragment_states";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    /* Your code ... */

    if (savedInstanceState != null) {
        Bundle fragmentStates =
                savedInstanceState.getParcelable(KEY_FRAGMENT_STATES);
        if (fragmentStates != null) {
            for (String id : fragmentStates.keySet()) {
                Fragment.SavedState fragmentState =
                        fragmentStates.getParcelable(id);
                mFragmentStates.put(id, fragmentState);
            }
        }
    }
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    /* Your code ... */

    Bundle fragmentStates = new Bundle(mFragmentStates.size());
    for (Map.Entry<String, Fragment.SavedState> entry : mFragmentStates.entrySet()) {
        fragmentStates.put(entry.getKey(), entry.getValue());
    }
    outState.putParcelable(KEY_FRAGMENT_STATES, fragmentStates);
}

此外,您可以查看FragmentStatePagerAdapter 类。它使用相同的方法来管理 ViewPager 的碎片状态。

更新:因此,您的代码应该类似于以下内容:

private Fragment mCurrentFragment;

public void navigateFragment(String tag, Fragment fragment,
        boolean shouldAdd) {
    FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction transaction = manager.beginTransaction();

    if (shouldAdd) {
        mStacks.get(tag).push(fragment); // push fragment on stack
    }

    if (mCurrentFragment != null) {
        saveFragmentState(mCurrentFragment.getClass().getName(), mCurrentFragment);
    }

    mCurrentFragment = fragment;
    restoreFragmentState(fragment.getClass().getName(), fragment);
    transaction.replace(android.R.id.tabcontent, fragment);

    if (shouldAdd) {
        // You shouldn't use back-stack when managing fragment states by yourself.
        transaction.addToBackStack(tag);
    }

    transaction.commit();
}

在这个例子中,我使用片段的类名作为id,所以所有片段必须有不同的类。但是你可以使用任何其他唯一的值作为id。还有另一件重要的事情我必须提到,那就是当你自己管理片段状态时,不应该使用后退栈。后退栈执行类似的状态管理,你很可能会遇到冲突。

1
FragmentStatePagerAdapterViewPager的适配器。它创建和管理一个FragmentTransaction,不适用于自定义片段管理场景。此外,我希望这个答案可以用于教育目的。 - Michael
@Michael 上面的代码之前运行得很好,但现在它崩溃了。看起来错误与片段恢复有关。你能帮忙吗? - moDev
@Michael,你在哪个聊天室可以联系到? - moDev
@droid_dev 其实我也不知道为什么会发生这种情况。我认为解决这个问题的最好方法是提交一个包含此堆栈跟踪和您的代码的新问题。 - Michael
@Michael 谢谢您的查看。我已经解决了问题。我尝试恢复选定的片段。如果我取消注释这些行,则可以工作。您能帮忙解决如何仅恢复/保存选定的片段吗? - moDev
显示剩余17条评论

0

0

我认为你应该使用共享首选项的概念,这比使用本地文件或数据库更快,编码肯定更容易。一个好的Android网页@ 存储选项。我将从网页中提取一些示例代码,并进行一些更改以适应你的需求。

首先,在onCreate()覆盖方法中获取保存在首选项中的数据:

@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);

   SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
   boolean silent = settings.getBoolean("silentMode", false);
...
}

接下来,让我们在片段停止或退出之前保存数据,出于各种原因。通过重写onDetach方法来实现这一点,而不是重写网页的onStop()方法。
@Override
public void onDetach() {
   super.onDetach();
   ...
   SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
   SharedPreferences.Editor editor = settings.edit();
   editor.putBoolean("silentMode", mSilentMode);

   // Commit the edits!
   editor.commit();
}

祝你好运,玩得开心!


0

你正在进行替换片段,这就是为什么片段被销毁的原因。你可以尝试以下方法。在单个FragmentTransaction中,我正在做两件事情,即添加一个新的片段和隐藏现有的片段。

假设我们正在将片段B添加到片段A之上。

    FragmentTransaction ft = fragmentManager.beginTransaction();
    ft.add(android.R.id.tabcontent, fragmentB, tagFragmentB)
            .hide(fragmentManager.findFragmentByTag(tagFragmentA))
            .addToBackStack(tag)
            .commit();

当你进行回退操作时,它将删除片段B并显示与添加B之前相同状态的片段A

请注意,这里的tagFragmentAtagFragmentB是分别用于添加片段AB的标签


0

我不清楚你使用了哪种方式来实现选项卡。 我从中猜测,

ft.replace(android.R.id.tabcontent, fragment);

你可能已经实现了FragmentTabHost。

编写一个自定义的FragmentTabhost并进行覆盖。

 @Override
public void onTabChanged(String tabId) {
    FragmentTransaction t = getSupportFragmentManager().beginTransaction();
    if (tabId.equals(“Tab1”)) {
        TabFragment1 fragment1 = null;
        if (getSupportFragmentManager().findFragmentByTag(“Tab1”) == null) {
            fragment1 = new TabFragment1();
        } else {
            fragment1 = (TabFragment1) getSupportFragmentManager().findFragmentByTag("Tab1");
        }
        t.replace(R.id.realContent, fragment1, "Tab1").addToBackStack(null).commit();
    }
}

更新:

  1. 确保在屏幕旋转时不会重新创建Activity,如果是这样,请设置setRetainInstance(true),这样即使Activity在屏幕旋转时重新创建,片段也将被保留。

  2. 为片段中的任何视图提供ID。这对于Android系统维护状态非常重要。


0
      clearBackStackEntry();
                        rl.setVisibility(View.GONE);

                        getSupportFragmentManager().beginTransaction()
                                .replace(FRAGMENT_CONTAINER, new HomeScreen())
                                .addToBackStack(null).commit();

     private void clearBackStackEntry() {
            int count = getSupportFragmentManager().getBackStackEntryCount();
            if (count > 0) {
                getSupportFragmentManager().popBackStack(null,
                        FragmentManager.POP_BACK_STACK_INCLUSIVE);
            }
        }



TRY THIS AND One more for fragment also try this: but use support.v4.app.Fragment, May be it will help you


Fragment fr = new main();
                android.support.v4.app.FragmentTransaction fragmentTransaction = getFragmentManager()
                        .beginTransaction();
                fragmentTransaction.replace(R.id.fragment_place, fr);
                fragmentTransaction.commit();
                // getActivity().finish();

0

处理这些事情非常简单。我可以给你一个示例来处理我们添加的 Fr 代理的后退按钮。

我已经声明了一个片段堆栈,并将所有片段推入其中,例如:

public static Stack<Fragment> fragmentStack;

创建一个像这样的方法:
    public static void replaceFragementsClick(Fragment fragementObj,     Bundle bundleObj, String title){
        try {
            FragmentManager fragmentManager = ((FragmentActivity)     mContext).getSupportFragmentManager();
            if (fragementObj != null) {

            fragementObj.setArguments(bundleObj);
                fragmentManager.beginTransaction().replace(R.id.frame_container,     fragementObj).commit();
            } 

            DashBoardActivity.fragmentStack.push(fragementObj);


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

也试试这个:

    public static void replaceFragementsClickBack(Fragment fragementObj, Bundle bundleObj, String title){
        try {
            FragmentManager fragmentManager = ((FragmentActivity) mContext).getSupportFragmentManager();
            if (fragementObj != null) {

            fragementObj.setArguments(bundleObj);
                fragmentManager.beginTransaction().replace(R.id.frame_container,     fragementObj).commit();

                DashBoardActivity.fragmentStack.pop();
            } 
        } catch (Exception e) {
            e.printStackTrace();
        }
}

在您添加的基本活动中,重写backpressed方法,如下所示:
@Override
    public void onBackPressed() {
            /**
             * Do Current Fragment Pop
             * */           
            fragmentStack.pop();            

            if(fragmentStack.size() >0){

                Bundle bunldeObj = new Bundle();
                //******Exit from Current Fragment
               Fragment fragment = fragmentStack.pop(); 
//                  fragmentStack.push(fragment);
                    if(fragment instanceof PhotosFragment){
                    bunldeObj.putString("position", "4");               
                    replaceFragementsClick(fragment,bunldeObj,"Photos");
                }else if(fragment instanceof PhotoDetatilFragment){
                    bunldeObj.putString("position", "4");
                    replaceFragementsClick(fragment,bunldeObj,"Photos");
                }else if(fragment instanceof PhotoFullViewFragment){
                    bunldeObj.putString("position", "4");
                    replaceFragementsClick(fragment,bunldeObj,"Photos");
                }else if(fragment instanceof HomeFragment){
                    bunldeObj.putString("position", "4");
                    replaceFragementsClick(fragment,bunldeObj,"Home");
                }else if(fragment instanceof VideosFragment){
                    bunldeObj.putString("position", "4");
                    replaceFragementsClick(fragment,bunldeObj,"Videos");
                }else if(fragment instanceof VideoDetailFragment){
                    bunldeObj.putString("position", "4");
                    replaceFragementsClick(fragment,bunldeObj,"Videos");
                }else if(fragment instanceof VideoViewFragment){
                    bunldeObj.putString("position", "4");
                    replaceFragementsClick(fragment,bunldeObj,"Videos");
                }else if(fragment instanceof MusicFragment){
                    bunldeObj.putString("position", "4");
                    replaceFragementsClick(fragment,bunldeObj,"Music");
                }else if(fragment instanceof MusicListFragment){
                    bunldeObj.putString("position", "4");
                    replaceFragementsClick(fragment,bunldeObj,"Music");
                }else if(fragment instanceof InstallAppsFragment){
                    bunldeObj.putString("position", "4");
                    replaceFragementsClick(fragment,bunldeObj,"Apps");
                }else if(fragment instanceof MessageFragment){
                    bunldeObj.putString("position", "4");
                        replaceFragementsClick(fragment,bunldeObj,"Messages");
                }else if(fragment instanceof MessageDetailFragment){
                    bunldeObj.putString("position", "4");
                    replaceFragementsClick(fragment,bunldeObj,"Messages");
                }else if(fragment instanceof LocateDeviceFragment){
                    bunldeObj.putString("position", "4");
                    replaceFragementsClick(fragment,bunldeObj,"Locate     Device");
                }else if(fragment instanceof FilesFragmentBottomBar){
                    bunldeObj.putString("position", "4");
                    replaceFragementsClick(fragment,bunldeObj,"Files");
                }else if(fragment instanceof AppsFragment){
                    bunldeObj.putString("position", "4");
                    replaceFragementsClick(fragment,bunldeObj,"Apps");  



            }else {
                super.onBackPressed();

                Intent intent = new     Intent(DashBoardActivity.this,ConnectDeviceActivity.class);
                startActivity(intent);
                finish();
        }
    }

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