根据 Activity 和 Fragment 生命周期(http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle和http://developer.android.com/guide/components/fragments.html#Lifecycle) ,在活动/片段更改之间保持状态的最可靠方法是使用默认 API 来保存和恢复状态:
当 Activity/Fragment 被关闭时(由于屏幕旋转等配置更改或因为用户请求转到另一个 Activity/Fragment),您可以将其状态保存在 Bundle 对象中。在创建它时,您可以恢复其保存的状态,从而重新创建一个与用户离开的实例完全相同的新实例 - 因此用户感觉没有任何变化。这不取决于您正在使用的活动/片段的特定子类。
我已经实现了你想要的东西:在我的例子中,一个片段包含一个带有按钮的菜单,每个按钮将用户引导到另一个片段,其中包含一个带有“后退”按钮的子菜单。因此,如果用户从菜单转到子菜单 1,然后返回菜单,然后返回子菜单 2,然后返回菜单,最后再次返回子菜单 1,我希望子菜单 1 看起来就像用户第一次离开它一样.
为此,我创建了:
1)通过我的活动interface
定义我的子菜单类型,implemented
以便它们可以在我的子菜单之间更改
2)一个主通用class
,我所有的子菜单都会extend
有一个Bundle
对象来存储它们的状态
3)在我的活动中,我有一组Bundle
能够存储每个子菜单的一个实例(因为我只对恢复最后一个状态感兴趣,所以我不需要多个)
接口(项目1):
public interface SubmenusManager {
public static enum Submenus {
ROOTMENU,
SUBMENU1,
SUBMENU2;
private static final int size = Submenus.values().length;
public static int size() {
return size;
}
public static int getId(Submenus test) {
switch(test) {
case SUBMENU1:
return 1;
case SUBMENU2:
return 2;
case ROOTMENU:
default:
return 0;
}
}
}
public void cloneCurrentSubmenuState(Parcelable toOverwrite);
public Bundle getLastStoredSubmenuState(Submenus submenu);
public void setCurrentSubmenuTo(Submenus submenu);
}
泛型类(第 2 项):
public class MenuFragment extends Fragment {
private Bundle menuData = new Bundle();
public static String RESTORE_MAIN_OBJECT = "restore_main";
public Bundle getMenuData() {
return menuData;
}
public Bundle cloneMenuData() {
return new Bundle(menuData);
}
public void setMenuData(Bundle menuData) {
this.menuData = menuData;
}
}
其中一项活动(第 3 项):
public class ExampleAct extends FragmentActivity implements SubmenusManager {
/**
* instance variables
*/
private MenuFragment mMenu;
private Bundle [] menuData; // the Array of Bundles!
private static final String CONTAINER = "parcelable_container";
private static final String SUBMENU = "saved_submenu";
private Submenus curSubmenu = Submenus.ROOTMENU; // the default state is the ROOTMENU
private boolean restoreLastSavedState = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) { // first time creating this activity
menuData = new Bundle[Submenus.size()];
} else { // this activity has a saved state from before
// restore all the data from all the submenus
menuData = (Bundle[]) savedInstanceState.getParcelableArray(CONTAINER);
// restore the info about which is the current active submenu
curSubmenu = (Submenus) savedInstanceState.getSerializable(SUBMENU);
}
buildMenuFragment(true);
//(...) stuff
}
private void buildMenuFragment(boolean restoreState) {
// (re)builds fragment inside menu.
// restoreState flags whether activity should look for
// saved state data and restore it
restoreLastSavedState = restoreState;
switch(curSubmenu) {
// Eclipse warns you about which are the constants in your enum
case ROOTMENU:
mMenu = new FragmentRootMenu();
break;
case SUBMENU1:
mMenu = new FragmentSubmenu1();
break;
case SUBMENU2:
mMenu = new FragmentSubmenu2();
break;
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.menu_frame, mMenu)
.commit();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(SUBMENU, curSubmenu);
cloneCurrentSubmenuState(mMenu.getMenuData().
getParcelable(MenuFragment.RESTORE_MAIN_OBJECT));
outState.putParcelableArray(CONTAINER, menuData);
// (...) stuff
}
@Override
public void cloneCurrentSubmenuState(Parcelable toOverwrite) {
if (menuData == null) menuData = new Bundle[Submenus.size()];
if (toOverwrite != null)
mMenu.getMenuData().putParcelable(MenuFragment.RESTORE_MAIN_OBJECT, toOverwrite);
menuData[Submenus.getId(curSubmenu)] = mMenu.cloneMenuData();
}
@Override
public Bundle getLastStoredSubmenuState(Submenus forThisSubmenu) {
return
(menuData == null || !restoreLastSavedState) ? new Bundle() : menuData[Submenus.getId(forThisSubmenu)];
}
@Override
public void setCurrentSubmenuTo(Submenus toThisSubmenu) {
if (mMenu != null) {
cloneCurrentSubmenuState(mMenu.getMenuData().
getParcelable(MenuFragment.RESTORE_MAIN_OBJECT));
}
curSubmenu = toThisSubmenu;
buildMenuFragment(true);
}
子菜单之一(第 2 项的扩展):
public class FragmentSubmenu1 extends MenuFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_submenu1, null);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
init();
}
public void init() {
// (...) stuff
MyParcelableObject tmp = null; // MyParcelableObject is a class
// that implements Parcelable and stores
// relevant info to rebuild this menu
// from a saved state
SubmenusManager m = (SubmenusManager) getActivity(); // remember activity implements SubmenusManager
Bundle bnd = m.getLastStoredSubmenuState(SubmenusManager.Submenus.SUBMENU1);
if (bnd != null) tmp = bnd.getParcelable(MenuFragment.RESTORE_MAIN_OBJECT);
if (tmp == null) {
tmp = new MyParcelableObject();
tmp.buildFromScratch(); // initializes with default data
}
// back button
Button backToMainMenu = (Button) getView().findViewById(R.id.submenu1_back);
backToMainMenu.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
((SubmenusManager) getActivity()).
setCurrentSubmenuTo(SubmenusManager.Submenus.ROOTMENU);
}
});
// (...) stuff
}
}
根菜单(项目 2 的扩展):
public class FragmentRootMenu extends MenuFragment {
View myView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
myView = inflater.inflate(R.layout.fragment_rootmenu, null);
return myView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
init();
}
public void init() {
Button btnSubmenu1 = (Button) myView.findViewById(R.id.btn_call_submenu1);
btnSubmenu1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
((SubmenusManager) getActivity()).
setCurrentSubmenuTo(SubmenusManager.Submenus.SUBMENU1);
}
});
Button btnSubmenu2 = (Button) myView.findViewById(R.id.btn_call_submenu2);
btnSubmenu2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
((SubmenusManager) getActivity()).
setCurrentSubmenuTo(SubmenusManager.Submenus.SUBMENU2);
}
});
}
}
为了在活动之间工作,您需要做的就是将存储所有片段的最后状态(在我的情况下是Bundle [] menuData
)的对象传递给通过其调用的活动Intent
;你会像我ExampleAct
在它的onCreate()
. 如果使用数组有问题,您还可以将其包装Bundle []
在自定义 Parcelable 对象中(与我的示例非常相似MyParcelableObject
;在那个里面我有类似的东西)。HashMap
这里如何在 Activity 之间传递 Parcelable:
如何使用 Intents 将对象从一个 Android Activity 发送到另一个?