【问题标题】:Save state when navigating between fragments在片段之间导航时保​​存状态
【发布时间】:2018-03-16 09:25:35
【问题描述】:

我正在开发一个应用程序,并且我有一个带有 NavigationDrawer 的菜单,用于在片段之间导航。在其中一个片段中,我调用了后端,然后将结果保存在列表中。当我导航到另一个片段并返回时,结果消失了,但我想暂时保存列表的内容。 我想使用onSaveInstanceState(),但该方法似乎从未被调用过。当我返回片段时,我还查看了数据是否仍在字段中,但那也不是t 的情况。我认为我在 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 来确定 Fragment 的类并将其发送到此方法。

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

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

编辑:

这是我的片段类:

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

【问题讨论】:

    标签: java android android-fragments onsaveinstancestate


    【解决方案1】:

    onSaveInstanceState 未被调用,因为没有理由,当您在片段之间导航时,旧片段不会被破坏,直到操作系统需要它们使用的空间(低内存)。

    首先创建一个回栈来保存片段,或者只是在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;
    }
    

    【讨论】:

      【解决方案2】:

      编辑: 尽管此解决方案运行良好,但此解决方案使用了一些不好的做法,我建议改用公认的解决方案。

      Keivan Esbatidenvercoder9 的帮助下我已经解决了这个问题(谢谢!)

      由于我只有 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;
      

      【讨论】:

      • 您应该发布代码的onMenuItemClick(或您正在执行片段切换的任何功能)部分作为示例并接受此答案。将来会帮助别人!
      • 您可以通过使用标签(字符串).add()ing 片段来进一步改进此代码。并通过fragmentmanager.findFragmentByTag(tag) 获取片段。您已经在使用标签来跟踪 currentFragment。此外,要隐藏当前片段,您可以执行 fragment = manager.findFragmentById(currentFragmentId); 然后隐藏片段。通过这样做,您不必编写 switch 语句来检查不同类型的片段标签。想象一下,如果导航抽屉中有 10 个片段。那么管理你当前的代码就相当困难了
      • 很抱歉打断你,但保留对片段的引用不是一个好习惯,在活动重新创建(配置更改、内存不足等)的情况下,所有引用都将失效。
      • @KeivanEsbati 此刻我并没有真正看到其他方式
      • 我更新了我的答案,你能检查一下它是否适合你吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-07
      • 2021-08-05
      • 1970-01-01
      • 1970-01-01
      • 2019-10-05
      • 2020-02-27
      • 1970-01-01
      相关资源
      最近更新 更多