应用程序之后重叠隐藏的碎片被杀死并恢复

我通过隐藏最后一个片段并添加一个新片段(见下面的代码)在片段之间切换 – 将它添加到后备栈 。 这样,用户可以快速切换片段,而无需重新加载片段数据。

这个效果很好,直到应用程序被杀死 (情景:用户使用其他几个应用程序,我的应用程序正在被持续和杀死)。

当用户打开应用程序时,它正在恢复,并显示所有的片段 – 相互重叠

问题:恢复的碎片如何以隐藏状态恢复? 也许我错过了一些国旗? 某处? 也许有更好的解决scheme来快速切换片段(不重新加载数据)?

添加片段的示例代码 – 在某处单击时用不同的片段调用几次:

FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); fragmentTransaction.hide(lastFragment); fragmentTransaction.add(newFragment); fragmentTransaction.addToBackStack(null); fragmentTransaction.commit(); lastFragment = newFragment; 

希望有人find更好的解决scheme。 在接受我的解决scheme之前,我会等待一个:

一般来说,我使用生成的标签来查找未隐藏的片段并隐藏它们。

具体来说,我为每个片段(StackEntry)生成一个唯一的标签,并将这些标签堆叠起来。 我坚持堆栈在bundel和加载它时,应用程序恢复,以便继续使用它。 然后,我使用标签列表查找所有未隐藏的片段并隐藏它们 – 除了最后一个。

下面是示例代码:

 public class FragmentActivity extends Activity { private static final String FRAGMENT_STACK_KEY = "FRAGMENT_STACK_KEY"; private Stack<StackEntry> fragmentsStack = new Stack<StackEntry>(); public FragmentActivity() { } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.content_frame); if (savedInstanceState == null) { // Init for the first time - not restore // ... } else { Serializable serializable = savedInstanceState.getSerializable(FRAGMENT_STACK_KEY); if (serializable != null) { // Workaround Android bug. // See: http://stackoverflow.com/questions/13982192/when-using-an-android-bundle-why-does-a-serialised-stack-deserialise-as-an-arra // And: https://code.google.com/p/android/issues/detail?id=3847 @SuppressWarnings("unchecked") List<StackEntry> arrayList = (List<StackEntry>) serializable; fragmentsStack = new Stack<StackEntry>(); fragmentsStack.addAll(arrayList); } // Hide all the restored fragments instead of the last one if (fragmentsStack.size() > 1) { FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); for (int i = 0; i < fragmentsStack.size()-1; i++) { String fragTag = fragmentsStack.get(i).getFragTag(); Fragment fragment = getFragmentManager().findFragmentByTag(fragTag); fragmentTransaction.hide(fragment); } fragmentTransaction.commit(); } } getFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() { @Override public void onBackStackChanged() { Fragment lastFragment = getLastFragment(); if (lastFragment.isHidden()) { FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); fragmentTransaction.show(lastFragment); fragmentTransaction.commit(); } } }); } private Fragment getLastFragment() { if (fragmentsStack.isEmpty()) return null; String fragTag = fragmentsStack.peek().getFragTag(); Fragment fragment = getFragmentManager().findFragmentByTag(fragTag); return fragment; } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putSerializable(FRAGMENT_STACK_KEY, fragmentsStack); } @Override public void onBackPressed() { if (!fragmentsStack.isEmpty()) { fragmentsStack.pop(); } } public void switchContent(Fragment fragment) { FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); Fragment lastFragment = getLastFragment(); if (lastFragment != null) { fragmentTransaction.hide(lastFragment); } String fragTag; if (fragment.isAdded()) { fragmentTransaction.show(fragment); fragTag = fragment.getTag(); } else { fragTag = Long.toString(System.currentTimeMillis()); fragmentTransaction.add(R.id.content_frame, fragment, fragTag); } if (!isFirstFragment()) { // Add to backstack only the first content fragment and not the state before (that has nothing) fragmentTransaction.addToBackStack(null); } fragmentTransaction.commit(); fragmentsStack.push(new StackEntry(fragTag)); } public boolean isFirstFragment() { return fragmentsStack.size() == 0; } private static class StackEntry implements Serializable { private static final long serialVersionUID = -6162805540320628024L; private String fragTag = null; public StackEntry(String fragTag) { super(); this.fragTag = fragTag; } public String getFragTag() { return fragTag; } } public static class Intent extends android.content.Intent { public Intent(Context packageContext) { super(packageContext, FragmentActivity.class); } } } 

我也有这个问题,这里有一个可能的解决scheme:每个片段保存自己的状态是否隐藏,然后隐藏自己的onCreate。

 @Override public void onSaveInstanceState(Bundle bundle) { super.onSaveInstanceState(bundle); if (this.isHidden()) { bundle.putBoolean("hidden", true); } } @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); if (bundle != null) { if (bundle.getBoolean("hidden", false)) { getFragmentManager() .beginTransaction() .hide(this) .commit(); } } } 

最后我find了解决这个问题最简单的方法:将content_frame从FramLayout更改为LinearLayout。

我有同样的问题,并通过设置setRetainInstance(true);来解决它setRetainInstance(true); 在每个片段的onCreate()方法中。

我有同样的问题。 不幸的是唯一不错的解决scheme是切换到使用fragmentTransaction.replace而不是fragmentTransaction.hide并添加。

当时很吸引人,但我真的很高兴我做到了。 它迫使我想想savedInstanceState并正确处理它。 你提到在导航回来的片段重新加载。 我有完全相同的问题,迫使我正确处理savedInstanceState 。 那里有两个案子。

  1. 如果活动没有被破坏,那么唯一需要重新创build的就是视图(通过onCreateView ),其他的东西仍然在内存中,所以这只是一个把适配器挂在视图上的问题而已。

  2. 如果活动被破坏,我需要重新创build视图和适配器。 为了最小化加载时间,我将所需的数据存储到savedInstanceState重新创build适配器

然而,你的抱怨是有效的,我不知道为什么Android不支持使用正在使用添加和隐藏的片段的已销毁的活动的正确隐藏状态回来。

既然你提到过你不介意把片段从零开始重新加载,为什么不使用intent并重新开始主要的片段活动呢?

我面临着像你刚才提到的那个问题,那些碎片是相互重叠的。 我查看了所有的stackoverflow,发现只有这个线程讨论这个特定的问题。 我尝试了沃尔特提供的解决scheme,但没有按预期工作。

下面的解决方法至less对我来说是有效的,所以如果有人结束了这种情况,那就分享一下

在父片段的onSaveInstanceState中,我设置了一个标记以确保在该包中保存了某些内容。

 public void onSaveInstanceState(Bundle outState) { // TODO Auto-generated method stub super.onSaveInstanceState(outState); outState.putString(TAG, "Get ready to be terminated"); }; 

并且在onCreate中,当保存的实例状态不为null时,可以指定要使用Intent加载的类,

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.layout); if(savedInstanceState != null) { Intent myIntent = new Intent(this, your.class); // Closing the parent fragment finish(); this.startActivity(myIntent); } else { // your main code ........... ........... }; }; 

这将确保您的片段从头开始重新创build。

在我的情况下,当用户打开我的应用程序,我有一个login屏幕,如果他们已经“login”,他们将被redirect到碎片屏幕之前。

在杀人过程中,一旦应用程序到达前台,我将用户重新引导到login页面,从那里开始,我已经存在的代码负责将用户重新引导回新创build的碎片屏幕。

注意:你需要确保你的子片段在onCreateView没有松散的结束。

我遇到了同样的问题,我认为这是Android框架的bug。这是问题所在 。

但是我的方式会为你工作,我们应该重写onSaveInstanceState(Bundle outState)方法,将我们的自定义数据保存到outState ,但不要调用super.onSaveInstanceState(outState);

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ...... if (savedInstanceState != null) { mCustomVariable = savedInstanceState.getInt("variable", 0); } } @Override protected void onSaveInstanceState(Bundle outState) { //super.onSaveInstanceState(outState); outState.putInt("variable", mCustomVariable); }