简体   繁体   English

如何在选项卡中保持没有backstack的片段状态?

[英]How to maintain fragment state without backstack in tab?

I am trying to save fragment state in onSaveInstanceState but when i go back to fragment, it always reloaded again instead of starting from last state. 我试图在onSaveInstanceState保存片段状态但是当我回到片段时,它总是再次重新加载而不是从上一个状态开始。

I looked into onCreateView and onActivityCreated and it always have onSaveInstanceState as null. 我查看了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();

    }

As i am unable to use backstack because in tabs back stack is not useful. 因为我无法使用backstack因为在标签中返回堆栈没有用。 Any help would be highly appreciated. 任何帮助将受到高度赞赏。

In this case you have to manage fragments' states by yourself. 在这种情况下,您必须自己管理片段的状态。 I don't know exactly how your code works so the only thing I can do is to give you some hints. 我不确切知道你的代码是如何工作的,所以我唯一能做的就是给你一些提示。

The first thing you need to implement is saving fragment's state. 您需要实现的第一件事是保存片段的状态。 Let's assume that all fragments have unique ids. 我们假设所有片段都有唯一的ID。 In this case you need to create a map that will keep all the states: 在这种情况下,您需要创建一个将保留所有状态的地图:

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

You need to call this method for a fragment that you're going to remove. 您需要为要删除的片段调用此方法。 Then we need to restore fragment's state and that's how we can do it: 然后我们需要恢复片段的状态,这就是我们如何做到这一点:

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

This method you need to call before adding a fragment to a transaction. 在将片段添加到事务之前需要调用此方法。

The code provided should work fine but to make it work correctly on activity recreation we need to save and restore mFragmentStates properly: 提供的代码应该可以正常工作,但要使它在活动重新创建时正常工作,我们需要正确保存和恢复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);
}

Also you can take a look at FragmentStatePagerAdapter class. 您还可以查看FragmentStatePagerAdapter类。 It uses the same approach for managing states of ViewPager 's fragments. 它使用相同的方法来管理ViewPager片段的状态。

UPDATE : And so your code should end up looking something like this: 更新 :所以你的代码应该看起来像这样:

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

In this example I use fragment's class name as an id so all the fragment must have different classes. 在这个例子中,我使用片段的类名作为id,因此所有片段必须具有不同的类。 But you can use any other unique value as an id. 但您可以使用任何其他唯一值作为ID。 And another important thing I have to mention is that you shouldn't use back-stack when managing fragment states by yourself. 另外一件重要的事情是,在自己管理片段状态时,不应该使用反向堆栈。 Back-stack performs similar state management and you will likely have conflicts. Back-stack执行类似的状态管理,您可能会遇到冲突。

I think you should use the idea of Shared Preferences , this is faster than using local files or database, and definitely easier to code. 我认为你应该使用共享首选项的想法,这比使用本地文件或数据库更快,并且更容易编码。 A good Android webpage @ Storage Options . 一个很好的Android网页@ 存储选项 I'll put some sample code from the webpage and change a few to fit your needs. 我将从网页上添加一些示例代码并更改一些以满足您的需求。

First, get the data saved from preferences in onCreate() override method: 首先,在onCreate()覆盖方法中获取从首选项中保存的数据:

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

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

Next let's save data before the fragment stops or exits, for various reasons. 接下来让我们在片段停止或退出之前保存数据,原因有多种。 Achieve this by override onDetach method, the webpage override onStop() instead. 通过覆盖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();
}

Good luck, have fun! 祝你好运,玩得开心!

You are doing replace fragment, thats why fragment is getting destroyed. 你正在做替换片段,这就是为什么片段被破坏了。 You can try below. 你可以尝试下面。 Here i am doing two things in single FragmentTransaction , i am adding a new fragment & hiding the existing fragment. 在这里,我在单个FragmentTransaction中做两件事,我正在添加一个新片段并隐藏现有片段。

Lets say we are adding fragment B on top of Fragment A 比方说,我们正在片段A的顶部添加B片段

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

Once you do back press, it will remove the fragment B & show the fragment A with same state(before you added fragment B ) 一旦你按下它,它将删除片段B显示具有相同状态的片段A (在添加片段B之前)

Please note here tagFragmentA & tagFragmentB are tags with which fragments A & B are added respectively 请注意, tagFragmentAtagFragmentB是分别添加片段AB的标签

I am not clear of way kind of vie you are using for implementing Tabs. 我不清楚你用来实现Tabs的方式。 I guessed from, 我猜,

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

that you might have implemented FragmentTabHost. 你可能已经实现了FragmentTabHost。

write a customFragmentTabhost and override 编写customFragmentTabhost并覆盖

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

UPDATE: 更新:

  1. Ensure Activity is not recreated on orientation, If so setRetainInstance(true), so that even if activity is recreated on orientation change, the fragments will be retained. 确保不在方向上重新创建活动,如果是setRetainInstance(true),那么即使在方向更改时重新创建活动,也会保留碎片。

  2. Do give id for any views in the fragment. 请为片段中的任何视图提供id。 It is important for Android system to maintain state. Android系统维护状态非常重要。

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

declare 宣布

setRetainInstance(true) 

In your fragment onCreate(). 在你的片段onCreate()中。 to recreate previous state 重建以前的状态
And link will helpful for understanding for how setRetainInstance work. 链接将有助于理解setRetainInstance的工作方式。

Understanding Fragment's setRetainInstance(boolean) 理解Fragment的setRetainInstance(boolean)

Why use Fragment#setRetainInstance(boolean)? 为什么要使用Fragment #setRetainInstance(boolean)?

Thanks :) 谢谢 :)

it is very simple to handle these things. 处理这些事情非常简单。 I can give you the sample to handle the back press on Fr agents which we added. 我可以给你样品来处理我们添加的Fr代理商的背压。

I have declared a fragment stack and push all the fragments in it like; 我已经声明了一个片段堆栈并将所有片段推入其中;

public static Stack<Fragment> fragmentStack;

make a method like this: 制作一个这样的方法:

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

Try this one also: 试试这个:

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

In the base activity where you have added, override the backpressed like: 在您添加的基本活动中,覆盖背景,如:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM