在片段之间导航时保存状态
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;
我正在开发一个应用程序,我有一个带有 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;