在片段之间导航时保​​存状态

Save state when navigating between fragments

我正在开发一个应用程序,我有一个带有 NavigationDrawer 的菜单,用于在片段之间导航。在其中一个片段中,我调用了后端,然后将结果保存在列表中。当我导航到另一个片段并返回时,结果消失了,但我想暂时保存列表的内容。 我想使用 onSaveInstanceState(),但似乎从未调用过该方法。 我还查看了 return 时数据是否仍在字段中片段,但事实并非如此。我想我在 FragmentManager 上做错了什么,但我不确定。

这是用于碎片交易的方法:

private void openFragment(Class fragmentClass) {
    Fragment fragment;
    try {
        fragment = (Fragment) fragmentClass.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
        return;
    }
    contentFrame.removeAllViews();
    FragmentManager fragmentManager = getSupportFragmentManager();
    fragmentManager.beginTransaction().replace(R.id.contentFrame,fragment).commit();
}

我使用 switch case 来确定片段的 class 并将其发送到此方法。

我可能会想出一个 hacky-snappy 的方法来解决这个问题,但我想在没有太多 hacky-snappy 代码的情况下解决这个问题。

我希望有人知道如何解决这个问题。提前致谢。

编辑:

这是我的片段class:

public class LGSFragment extends Fragment {

    @BindView(R.id.rvLGS)
    RecyclerView rvLGS;

    private List<LGS> lgsList;
    private LGSAdapter adapter;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        //I debugged here and all fields were null at this point
        View view = inflater.inflate(R.layout.fragment_lgs,container,false);
        ButterKnife.bind(this, view);
        lgsList = new ArrayList<>();
        LinearLayoutManager manager = new LinearLayoutManager(getContext());
        rvLGS.setLayoutManager(manager);
        adapter = new LGSAdapter(lgsList);
        rvLGS.setAdapter(adapter);
        getDatabaseLGSs();
        return view;
    }

    /**
     * Method to load in the LGSs from the database
     */
    private void getDatabaseLGSs() {
        String collection = getString(R.string.db_lgs);
        FireStoreUtils.getAllDocumentsConverted(collection, LGS.class, new OperationCompletedListener() {
            @Override
            public void onOperationComplete(Result result, Object... data) {
                if (result == Result.SUCCESS) {
                    lgsList.clear();
                    List<LGS> newLGSs = (List<LGS>) data[0];
                    List<String> ids = (List<String>) data[1];
                    int i = 0;
                    for (LGS lgs : newLGSs) {
                        lgs.setId(ids.get(i));
                        lgsList.add(lgs);
                        i++;
                    }
                    adapter.notifyDataSetChanged();
                }
            }
        });
    }


    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
    }
}

onSaveInstanceState 未被调用,因为 没有理由 ,当您在片段之间导航时,较旧的片段直到 OS 需要他们使用的 space(低内存)。

首先创建一个后台栈来保存片段或者在fragmentTransaction结束时调用addtoBackStack然后将列表初始化和数据请求移动到onCreate所以它只在片段时调用已创建:

    lgsList = new ArrayList<>();
    getDatabaseLGSs();

之后,每次您返回片段时,都会使用可用数据重新创建视图。

更新: 您可以将片段添加到 backstack,然后使用相应的 tag 检索它,而不是自己保留引用。这让 fragmentManager 自己管理缓存。第二次访问片段时,它不会被重新创建:

@Override
public void onNavigationDrawerItemSelected(@NonNull MenuItem item) {
    if (item.isChecked())
        return;

    item.setChecked(true);
    setTitle(item.getTitle());

    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    Fragment currentlyShown = fragmentManager.findFragmentByTag(currentlyShownTag);

    Fragment dest;
    switch (item.getItemId()){
        case R.id.nav_lgs:
            dest = fragmentManager.findFragmentByTag(LGSFragment.class.getName());
            if (dest == null) {
                Log.d("TRANSACTION", "instanciating new fragment");
                dest = new LGSFragment();
                currentlyShownTag = LGSFragment.class.getName();
                transaction.add(R.id.contentFrame, dest, LGSFragment.class.getName());
            }
            break;
            ...


    }

    if(currentlyShown != null)
        transaction.hide(currentlyShown);

    transaction.show(dest);
    transaction.commit();
    drawerLayout.closeDrawers();
    return true;
}

编辑: 虽然这个解决方案工作正常,但这个解决方案使用了一些不好的做法,我建议改用公认的解决方案。

我已经在 Keivan Esbati and denvercoder9 的帮助下解决了这个问题(谢谢!)

因为我只有 4 个片段,所以我在 MainActivity 中为每个片段保留了一个实例,我还有一个变量来跟踪当前片段。每次打开片段时,我都会使用 FragmentManager 并在事务中调用 .hide() 来隐藏当前片段。然后,如果 Fragment 是一个新的 Fragment,我在交易中调用 .add(),否则我在交易中调用 .show

onNavigationItemSelected() 方法的代码(当用户选择菜单中的项目时触发):

public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    if (!item.isChecked()) {
        item.setChecked(true);
        setTitle(item.getTitle());
        switch (item.getItemId()) {
            case R.id.nav_lgs: {
                if (lgsFragment == null) {
                    lgsFragment = new LGSFragment();
                    openFragment(lgsFragment, FragmentTag.LGS.toString());
                } else {
                    openFragment(lgsFragment, "");
                }
                currentFragmentTag = FragmentTag.LGS;
                break;
            }
            case R.id.nav_users: {
                if (userFragment == null) {
                    userFragment = new UserFragment();
                    openFragment(userFragment, FragmentTag.USERS.toString());
                } else {
                    openFragment(userFragment, "");
                }
                currentFragmentTag = FragmentTag.USERS;
                break;
            }
            case R.id.nav_profile: {
                if (profileFragment == null) {
                    profileFragment = new ProfileFragment();
                    openFragment(profileFragment, FragmentTag.PROFILE.toString());
                } else {
                    openFragment(profileFragment, "");
                }
                currentFragmentTag = FragmentTag.PROFILE;
                break;
            }
            case R.id.nav_my_lgs: {
                if (myLGSFragment == null) {
                    myLGSFragment = new MyLGSFragment();
                    openFragment(myLGSFragment, FragmentTag.MY_LGS.toString());
                } else {
                    openFragment(myLGSFragment, "");
                }
                currentFragmentTag = FragmentTag.MY_LGS;
                break;
            }
            default: {
                if (lgsFragment == null) {
                    lgsFragment = new LGSFragment();
                    openFragment(lgsFragment, FragmentTag.LGS.toString());
                } else {
                    openFragment(lgsFragment, "");
                }
                currentFragmentTag = FragmentTag.LGS;
                break;
            }
        }
    }
    drawerLayout.closeDrawers();
    return true;
}

上面使用的openFragment()方法:

private void openFragment(Fragment fragment, String tag) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    if (currentFragmentTag != FragmentTag.NO_FRAGMENT) {
        fragmentManager.beginTransaction().hide(fragmentManager.findFragmentByTag(currentFragmentTag.toString())).commit();
    }
    if (!tag.equals("")) {
        fragmentManager.beginTransaction().add(R.id.contentFrame,fragment,tag).commit();
    } else {
        fragmentManager.beginTransaction().show(fragment).commit();
    }
}

onCreate()中设置:

currentFragmentTag = FragmentTag.NO_FRAGMENT;
if (lgsFragment == null) {
    lgsFragment = new LGSFragment();
    openFragment(lgsFragment, FragmentTag.LGS.toString());
} else {
    openFragment(lgsFragment, "");
}
currentFragmentTag = FragmentTag.LGS;