我通过隐藏最后一个片段和添加一个新的片段来切换片段(参见下面的代码)-将其添加到back-stack以及。这样,用户就可以在片段之间快速切换,而无需重新加载片段数据。
在应用程序被杀死(场景:用户使用其他几个应用程序,我的应用程序被持久化并被杀死)之前,这个工作很好。
当用户打开应用程序时,它正在被还原并且显示所有的片段- 重叠。
问题:恢复的片段如何恢复到隐藏状态?也许我错过了一些标志?的地方吗?也许有一个更好的解决方案在片段之间快速切换(不重新加载数据)?
添加片段的示例代码-在单击某个地方时使用不同的片段调用多次:
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.hide(lastFragment);
fragmentTransaction.add(newFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
lastFragment = newFragment;
希望有人能找到更好的解决方案。在我接受我的解决方案之前,我会等一个:
一般来说,我使用生成的标记来查找未隐藏的片段并隐藏它们。详细地说,我为每个片段(StackEntry)生成一个唯一的标签,并在片段本身堆叠时堆叠标签。我将堆栈保存在bundle中,并在应用恢复时加载它,以便继续使用它。然后我使用标签列表找到所有未隐藏的片段并隐藏它们-除了最后一个。
下面是示例代码: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);
}
}
}
我也有这个问题,这里有一个可能的解决方案:让每个片段保存自己的状态关于它是否被隐藏,然后隐藏自己在它的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();
}
}
}
最后我找到了解决这个问题的最简单的方法:将content_frame从FramLayout改为LinearLayout
我遇到了同样的问题,并通过在每个片段的onCreate()
方法中设置setRetainInstance(true);
来解决。
我遇到了完全相同的问题。不幸的是,唯一好的解决方案是切换到使用fragmentTransaction.replace
代替fragmentTransaction.hide
,并添加。
当时很糟糕,但我很高兴我做了。它迫使我思考savedInstanceState
,并妥善处理它。你提到,在返回导航时,片段被重新加载。我有完全相同的问题,迫使我正确处理savedInstanceState
。这里有两种情况
-
如果活动没有被销毁,唯一需要重新创建的是视图(通过
onCreateView
),其他一切仍然在内存中,所以它只是一个问题,连接适配器的视图和你完成。 -
如果活动被销毁,我需要重新创建视图和适配器。为了尽量减少加载时间,我将重新创建适配器所需的数据存储在
savedInstanceState
中。
你的抱怨是有效的,然而,我不知道为什么Android不支持回来与正确的隐藏状态从一个销毁的活动与碎片,使用添加和隐藏。
既然你提到你不介意从头开始重新加载自己的片段,为什么不使用意图并重新开始主片段活动?
我遇到了和你提到的一样的问题,片段彼此重叠。我看了整个stackoverflow,发现只有这个线程讨论了这个特定的问题。我尝试了Walt提供的解决方案,但是没有达到预期的效果。
下面的解决方法至少对我来说是有效的,所以分享一下,以防有人遇到这种情况
在父片段的onSaveInstanceState处,我设置了一个标记来确保bundle中保存了一些东西。
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
...........
...........
};
};
这将确保您的片段是从头重新创建的。
在我的案例中,当用户打开我的应用程序时,我有一个登录屏幕,如果他们已经"登录",他们将被重定向到片段屏幕。
在kill过程中,一旦应用进入前台,我就会将用户重定向到登录页面,从那里开始,我已经存在的代码会再次将用户重定向到新创建的片段屏幕。
注意:你需要确保你的子片段没有松散的结束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);
}